diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..6afd357c2 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +.git* export-ignore +/CMakeLists.txt export-subst \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..2a68f647b --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/build +/tags \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 000000000..1db93abc2 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,115 @@ +cmake_minimum_required(VERSION 2.8.6) + +set(VERSION "0.1") +# $Format:Packaged from commit %H%nset(COMMIT %h)%nset(REFS "%d")$ + +set_property(GLOBAL PROPERTY USE_FOLDERS ON) +set(CMAKE_CONFIGURATION_TYPES "Debug;Release") +enable_testing() + +include_directories(src contrib/epee/include "${CMAKE_BINARY_DIR}/version") + +set(STATIC ${MSVC} CACHE BOOL "Link libraries statically") + +if(MSVC) + add_definitions("/bigobj /MP /W3 /GS- /D_CRT_SECURE_NO_WARNINGS /wd4996 /wd4345 /D_WIN32_WINNT=0x0600 /DWIN32_LEAN_AND_MEAN /DGTEST_HAS_TR1_TUPLE=0 /FIinline_c.h /D__SSE4_1__") + # set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /Dinline=__inline") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /STACK:10485760") + if(STATIC) + foreach(VAR CMAKE_C_FLAGS_DEBUG CMAKE_CXX_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE CMAKE_CXX_FLAGS_RELEASE) + string(REPLACE "/MD" "/MT" ${VAR} "${${VAR}}") + endforeach() + endif() + include_directories(SYSTEM src/platform/msc) +else() + set(ARCH native CACHE STRING "CPU to build for: -march value or default") + if("${ARCH}" STREQUAL "default") + set(ARCH_FLAG "") + else() + set(ARCH_FLAG "-march=${ARCH}") + endif() + set(WARNINGS "-Wall -Wextra -Wpointer-arith -Wundef -Wvla -Wwrite-strings -Werror -Wno-error=extra -Wno-error=deprecated-declarations -Wno-error=sign-compare -Wno-error=strict-aliasing -Wno-error=type-limits -Wno-unused-parameter -Wno-error=unused-variable -Wno-error=undef -Wno-error=uninitialized") + if(CMAKE_C_COMPILER_ID STREQUAL "Clang") + set(WARNINGS "${WARNINGS} -Wno-error=mismatched-tags -Wno-error=null-conversion -Wno-overloaded-shift-op-parentheses -Wno-error=shift-count-overflow -Wno-error=tautological-constant-out-of-range-compare -Wno-error=unused-private-field -Wno-error=unneeded-internal-declaration") + else() + set(WARNINGS "${WARNINGS} -Wlogical-op -Wno-error=maybe-uninitialized") + endif() + if(MINGW) + set(WARNINGS "${WARNINGS} -Wno-error=unused-value") + set(MINGW_FLAG "-DWIN32_LEAN_AND_MEAN") + include_directories(SYSTEM src/platform/mingw) + else() + set(MINGW_FLAG "") + endif() + set(C_WARNINGS "-Waggregate-return -Wnested-externs -Wold-style-definition -Wstrict-prototypes") + set(CXX_WARNINGS "-Wno-reorder -Wno-missing-field-initializers") + try_compile(STATIC_ASSERT_RES "${CMAKE_CURRENT_BINARY_DIR}/static-assert" "${CMAKE_CURRENT_SOURCE_DIR}/utils/test-static-assert.c" COMPILE_DEFINITIONS "-std=c11") + if(STATIC_ASSERT_RES) + set(STATIC_ASSERT_FLAG "") + else() + set(STATIC_ASSERT_FLAG "-Dstatic_assert=_Static_assert") + endif() + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c11 -D_GNU_SOURCE ${MINGW_FLAG} ${STATIC_ASSERT_FLAG} ${WARNINGS} ${C_WARNINGS} ${ARCH_FLAG}") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -D_GNU_SOURCE ${MINGW_FLAG} ${WARNINGS} ${CXX_WARNINGS} ${ARCH_FLAG}") + if(CMAKE_C_COMPILER_ID STREQUAL "GNU" AND NOT (CMAKE_C_COMPILER_VERSION VERSION_LESS 4.8)) + set(DEBUG_FLAGS "-g3 -Og") + else() + set(DEBUG_FLAGS "-g3 -O0") + endif() + set(RELEASE_FLAGS "-Ofast -DNDEBUG -Wno-unused-variable -flto") + #if(CMAKE_C_COMPILER_ID STREQUAL "GNU" AND NOT MINGW) + # set(RELEASE_FLAGS "${RELEASE_FLAGS} -fno-fat-lto-objects") + #endif() + set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} ${DEBUG_FLAGS}") + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${DEBUG_FLAGS}") + set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${RELEASE_FLAGS}") + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${RELEASE_FLAGS}") + if(STATIC) + set(CMAKE_FIND_LIBRARY_SUFFIXES ".a") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static") + function(add_executable name) + _add_executable("${name}" ${ARGN}) + set_target_properties("${name}" PROPERTIES LINK_SEARCH_START_STATIC ON LINK_SEARCH_END_STATIC ON) + endfunction() + endif() +endif() + +if(STATIC) + set(Boost_USE_STATIC_LIBS ON) + set(Boost_USE_STATIC_RUNTIME ON) +endif() +find_package(Boost 1.53 REQUIRED COMPONENTS system filesystem thread date_time chrono regex serialization atomic program_options) +if((${Boost_MAJOR_VERSION} EQUAL 1) AND (${Boost_MINOR_VERSION} EQUAL 54)) + message(SEND_ERROR "Boost version 1.54 is unsupported, more details are available here http://goo.gl/RrCFmA") +endif() +include_directories(SYSTEM ${Boost_INCLUDE_DIRS}) +if(MINGW) + set(Boost_LIBRARIES "${Boost_LIBRARIES};ws2_32;mswsock") +endif() + +set(COMMIT_ID_IN_VERSION ON CACHE BOOL "Include commit ID in version") +file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/version") +if (NOT COMMIT_ID_IN_VERSION) + set(VERSION "${VERSION}-unknown") + configure_file("src/version.h.in" "version/version.h") + add_custom_target(version ALL) +elseif(DEFINED COMMIT) + string(REPLACE "." "\\." VERSION_RE "${VERSION}") + if(NOT REFS MATCHES "(\\(|, )tag: v${VERSION_RE}(\\)|, )") + set(VERSION "${VERSION}-g${COMMIT}") + endif() + configure_file("src/version.h.in" "version/version.h") + add_custom_target(version ALL) +else() + find_package(Git QUIET) + if(Git_FOUND) + add_custom_target(version ALL "${CMAKE_COMMAND}" "-D" "VERSION=${VERSION}" "-D" "GIT=${GIT_EXECUTABLE}" "-D" "TO=${CMAKE_BINARY_DIR}/version/version.h" "-P" "src/version.cmake" WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}") + else() + set(VERSION "${VERSION}-unknown") + configure_file("src/version.h.in" "version/version.h") + add_custom_target(version ALL) + endif() +endif() + +add_subdirectory(src) +add_subdirectory(tests) diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 1ab189cc5..000000000 --- a/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2013 Antonio Juarez - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 000000000..451319b08 --- /dev/null +++ b/Makefile @@ -0,0 +1,33 @@ +all: all-release + +cmake-debug: + mkdir -p build/debug + cd build/debug && cmake -D CMAKE_BUILD_TYPE=Debug ../.. + +build-debug: cmake-debug + cd build/debug && $(MAKE) + +test-debug: build-debug + cd build/debug && $(MAKE) test + +all-debug: build-debug + +cmake-release: + mkdir -p build/release + cd build/release && cmake -D CMAKE_BUILD_TYPE=Release ../.. + +build-release: cmake-release + cd build/release && $(MAKE) + +test-release: build-release + cd build/release && $(MAKE) test + +all-release: build-release + +clean: + rm -rf build + +tags: + ctags -R --sort=1 --c++-kinds=+p --fields=+iaS --extra=+q --language-force=C++ src contrib tests/gtest + +.PHONY: all cmake-debug build-debug test-debug all-debug cmake-release build-release test-release all-release clean tags diff --git a/README b/README new file mode 100644 index 000000000..cced0c2c9 --- /dev/null +++ b/README @@ -0,0 +1,32 @@ +-= Building Cybernote =- + +On *nix: + +Dependencies: GCC 4.7 or later, CMake 2.8.6 or later, and Boost 1.53 or later (except 1.54, more details here: http://goo.gl/RrCFmA). +You may download them from: +http://gcc.gnu.org/ +http://www.cmake.org/ +http://www.boost.org/ +Alternatively, it may be possible to install them using a package manager. + +To build, change to a directory where this file is located, and run `make'. The resulting executables can be found in build/release/src. + +Advanced options: +Parallel build: run `make -j' instead of `make'. +Debug build: run `make build-debug'. +Test suite: run `make test-release' to run tests in addition to building. Running `make test-debug' will do the same to the debug version. +Building with Clang: it may be possible to use Clang instead of GCC, but this may not work everywhere. To build, run `export CC=clang CXX=clang++' before running `make'. + +On Windows: +Dependencies: MSVC 2012 or later, CMake 2.8.6 or later, and Boost 1.53 or later. You may download them from: +http://www.microsoft.com/ +http://www.cmake.org/ +http://www.boost.org/ + +To build, change to a directory where this file is located, and run this commands: +mkdir build +cd build +cmake -G "Visual Studio 11 Win64" .. + +And then do Build. +Good luck! \ No newline at end of file diff --git a/README.md b/README.md deleted file mode 100644 index 7527baef7..000000000 --- a/README.md +++ /dev/null @@ -1,4 +0,0 @@ -bytecoin -======== - -Fresh and juicy cryptocurrency. diff --git a/contrib/epee/LICENSE.txt b/contrib/epee/LICENSE.txt new file mode 100644 index 000000000..4a6b529e5 --- /dev/null +++ b/contrib/epee/LICENSE.txt @@ -0,0 +1,25 @@ +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 Andrey N. Sabelnikov 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. \ No newline at end of file diff --git a/contrib/epee/README.md b/contrib/epee/README.md new file mode 100644 index 000000000..a69884f57 --- /dev/null +++ b/contrib/epee/README.md @@ -0,0 +1 @@ +epee - is a small library of helpers, wrappers, tools and and so on, used to make my life easier. \ No newline at end of file diff --git a/contrib/epee/demo/.gitignore b/contrib/epee/demo/.gitignore new file mode 100644 index 000000000..d9b4f015d --- /dev/null +++ b/contrib/epee/demo/.gitignore @@ -0,0 +1 @@ +/build/* diff --git a/contrib/epee/demo/CMakeLists.txt b/contrib/epee/demo/CMakeLists.txt new file mode 100644 index 000000000..b4ac2cc8b --- /dev/null +++ b/contrib/epee/demo/CMakeLists.txt @@ -0,0 +1,49 @@ +cmake_minimum_required(VERSION 2.8) +set(Boost_USE_MULTITHREADED ON) +#set(Boost_DEBUG 1) +find_package(Boost COMPONENTS system filesystem thread date_time chrono regex ) + +include_directories( ${Boost_INCLUDE_DIRS} ) + + +IF (MSVC) + add_definitions( "/W3 /D_CRT_SECURE_NO_WARNINGS /wd4996 /wd4345 /nologo /D_WIN32_WINNT=0x0600 /DWIN32_LEAN_AND_MEAN /bigobj" ) +ELSE() + # set stuff for other systems + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -Wno-reorder -D_GNU_SOURCE") +ENDIF() + + +include_directories(.) +include_directories(../include) +include_directories(iface) + + +# Add folders to filters +file(GLOB_RECURSE LEVIN_GENERAL_SECTION RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/demo_levin_server/*.h + ${CMAKE_CURRENT_SOURCE_DIR}/demo_levin_server/*.inl + ${CMAKE_CURRENT_SOURCE_DIR}/demo_levin_server/*.cpp) + +file(GLOB_RECURSE HTTP_GENERAL_SECTION RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/demo_http_server/*.h + ${CMAKE_CURRENT_SOURCE_DIR}/demo_http_server/*.inl + ${CMAKE_CURRENT_SOURCE_DIR}/demo_http_server/*.cpp) + + + +source_group(general FILES ${LEVIN_GENERAL_SECTION} FILES ${HTTP_GENERAL_SECTION}) +#source_group(general FILES ${HTTP_GENERAL_SECTION}) + +add_executable(demo_http_server ${HTTP_GENERAL_SECTION} ) +add_executable(demo_levin_server ${LEVIN_GENERAL_SECTION} ) + +target_link_libraries( demo_http_server ${Boost_LIBRARIES} ) +target_link_libraries( demo_levin_server ${Boost_LIBRARIES} ) + +IF (NOT WIN32) + target_link_libraries (demo_http_server rt) + target_link_libraries (demo_levin_server rt) +ENDIF() + + diff --git a/contrib/epee/demo/README.txt b/contrib/epee/demo/README.txt new file mode 100644 index 000000000..e69de29bb diff --git a/contrib/epee/demo/demo_http_server/demo_http_server.cpp b/contrib/epee/demo/demo_http_server/demo_http_server.cpp new file mode 100644 index 000000000..f614da6c5 --- /dev/null +++ b/contrib/epee/demo/demo_http_server/demo_http_server.cpp @@ -0,0 +1,217 @@ +// 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 "stdafx.h" +#include "console_handler.h" +#include "demo_http_server.h" +#include "net/http_client.h" +#include "storages/http_abstract_invoke.h" + + +template +bool communicate(const std::string url, t_request& req, t_response& rsp, const std::string& ip, const std::string& port, bool use_json, bool use_jrpc = false) +{ + epee::net_utils::http::http_simple_client http_client; + bool r = http_client.connect(ip, port, 1000); + CHECK_AND_ASSERT_MES(r, false, "failed to connect"); + if(use_json) + { + if(use_jrpc) + { + epee::json_rpc::request req_t = AUTO_VAL_INIT(req_t); + req_t.params = req; + req_t.id = "10"; + req_t.method = "command_example_1"; + req_t.version = "1.1"; + epee::json_rpc::response resp_t = AUTO_VAL_INIT(resp_t); + if(!epee::net_utils::invoke_http_json_remote_command2("/request_json_rpc", req_t, resp_t, http_client)) + { + return false; + } + rsp = resp_t.result; + return true; + }else + return epee::net_utils::invoke_http_json_remote_command2(url, req, rsp, http_client); + } + else + return epee::net_utils::invoke_http_bin_remote_command2(url, req, rsp, http_client); +} + + +int main(int argc, char* argv[]) +{ + TRY_ENTRY(); + string_tools::set_module_name_and_folder(argv[0]); + + //set up logging options + log_space::get_set_log_detalisation_level(true, LOG_LEVEL_2); + log_space::log_singletone::add_logger(LOGGER_CONSOLE, NULL, NULL); + log_space::log_singletone::add_logger(LOGGER_FILE, + log_space::log_singletone::get_default_log_file().c_str(), + log_space::log_singletone::get_default_log_folder().c_str()); + + + + LOG_PRINT("Demo server starting ...", LOG_LEVEL_0); + + + demo::demo_http_server srv; + + start_default_console(&srv); + + std::string bind_param = "0.0.0.0"; + std::string port = "83"; + + if(!srv.init(port, bind_param)) + { + LOG_ERROR("Failed to initialize srv!"); + return 1; + } + + //log loop + srv.run(); + size_t count = 0; + while (!srv.is_stop()) + { + + demo::COMMAND_EXAMPLE_1::request req; + req.sub = demo::get_test_data(); + demo::COMMAND_EXAMPLE_1::response rsp; + bool r = false; + if(count%2) + {//invoke json + r = communicate("/request_api_json_1", req, rsp, "127.0.0.1", port, true, true); + }else{ + r = communicate("/request_api_bin_1", req, rsp, "127.0.0.1", port, false); + } + CHECK_AND_ASSERT_MES(r, false, "failed to invoke http request"); + CHECK_AND_ASSERT_MES(rsp.m_success, false, "wrong response"); + CHECK_AND_ASSERT_MES(rsp.subs.size()==1, false, "wrong response"); + CHECK_AND_ASSERT_MES(rsp.subs.front() == demo::get_test_data(), false, "wrong response"); + //misc_utils::sleep_no_w(1000); + ++count; + } + bool r = srv.wait_stop(); + CHECK_AND_ASSERT_MES(r, 1, "failed to wait server stop"); + srv.deinit(); + + LOG_PRINT("Demo server stoped.", LOG_LEVEL_0); + return 1; + + CATCH_ENTRY_L0("main", 1); +} + +/************************************************************************/ +/* */ +/************************************************************************/ +namespace demo +{ + bool demo_http_server::init(const std::string& bind_port, const std::string& bind_ip) + { + + + //set self as callback handler + m_net_server.get_config_object().m_phandler = this; + + //here set folder for hosting reqests + m_net_server.get_config_object().m_folder = ""; + + LOG_PRINT_L0("Binding on " << bind_ip << ":" << bind_port); + return m_net_server.init_server(bind_port, bind_ip); + } + + bool demo_http_server::run() + { + m_stop = false; + //here you can set worker threads count + int thrds_count = 4; + + //go to loop + LOG_PRINT("Run net_service loop( " << thrds_count << " threads)...", LOG_LEVEL_0); + if(!m_net_server.run_server(thrds_count, false)) + { + LOG_ERROR("Failed to run net tcp server!"); + } + + return true; + } + + bool demo_http_server::deinit() + { + return m_net_server.deinit_server(); + } + + bool demo_http_server::send_stop_signal() + { + m_stop = true; + m_net_server.send_stop_signal(); + return true; + } + + bool demo_http_server::on_requestr_uri_1(const net_utils::http::http_request_info& query_info, + net_utils::http::http_response_info& response, + const net_utils::connection_context_base& m_conn_context) + { + return true; + } + + + bool demo_http_server::on_requestr_uri_2(const net_utils::http::http_request_info& query_info, + net_utils::http::http_response_info& response, + const net_utils::connection_context_base& m_conn_context) + { + return true; + } + + + bool demo_http_server::on_hosting_request( const net_utils::http::http_request_info& query_info, + net_utils::http::http_response_info& response, + const net_utils::connection_context_base& m_conn_context) + { + //read file from filesystem here + return true; + } + + bool demo_http_server::on_request_api_1(const COMMAND_EXAMPLE_1::request& req, COMMAND_EXAMPLE_1::response& res) + { + CHECK_AND_ASSERT_MES(req.sub == demo::get_test_data(), false, "wrong request"); + res.m_success = true; + res.subs.push_back(req.sub); + return true; + } + + bool demo_http_server::on_request_api_1_with_error(const COMMAND_EXAMPLE_1::request& req, COMMAND_EXAMPLE_1::response& res, epee::json_rpc::error& error_resp) + { + error_resp.code = 232432; + error_resp.message = "bla bla bla"; + return false; + } + + bool demo_http_server::on_request_api_2(const COMMAND_EXAMPLE_2::request& req, COMMAND_EXAMPLE_2::response& res) + { + return true; + } +} diff --git a/contrib/epee/demo/demo_http_server/demo_http_server.h b/contrib/epee/demo/demo_http_server/demo_http_server.h new file mode 100644 index 000000000..bb8f9bffa --- /dev/null +++ b/contrib/epee/demo/demo_http_server/demo_http_server.h @@ -0,0 +1,101 @@ +// 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. +// + + + +#pragma once +#include +#include + +#include "net/http_server_cp2.h" +#include "transport_defs.h" +#include "net/http_server_handlers_map2.h" + +using namespace epee; + +namespace demo +{ + + class demo_http_server: public net_utils::http::i_http_server_handler + { + public: + demo_http_server():m_stop(false){} + bool run(); + bool init(const std::string& bind_port = "11112", const std::string& bind_ip = "0.0.0.0"); + bool deinit(); + bool send_stop_signal(); + bool is_stop(){return m_stop;} + bool wait_stop(){return m_net_server.timed_wait_server_stop(100000);} + private: + + + CHAIN_HTTP_TO_MAP2(); //forward http requests to uri map + + BEGIN_URI_MAP2() + MAP_URI2("/requestr_uri_1", on_requestr_uri_1) + MAP_URI2("/requestr_uri_2", on_requestr_uri_1) + //MAP_URI_AUTO_XML2("/request_api_xml_1", on_request_api_1, COMMAND_EXAMPLE_1) + //MAP_URI_AUTO_XML2("/request_api_xml_2", on_request_api_2, COMMAND_EXAMPLE_2) + MAP_URI_AUTO_JON2("/request_api_json_1", on_request_api_1, COMMAND_EXAMPLE_1) + MAP_URI_AUTO_JON2("/request_api_json_2", on_request_api_2, COMMAND_EXAMPLE_2) + MAP_URI_AUTO_BIN2("/request_api_bin_1", on_request_api_1, COMMAND_EXAMPLE_1) + MAP_URI_AUTO_BIN2("/request_api_bin_2", on_request_api_2, COMMAND_EXAMPLE_2) + BEGIN_JSON_RPC_MAP("/request_json_rpc") + MAP_JON_RPC("command_example_1", on_request_api_1, COMMAND_EXAMPLE_1) + MAP_JON_RPC("command_example_2", on_request_api_2, COMMAND_EXAMPLE_2) + MAP_JON_RPC_WE("command_example_1_we", on_request_api_1_with_error, COMMAND_EXAMPLE_1) + END_JSON_RPC_MAP() + CHAIN_URI_MAP2(on_hosting_request) + END_URI_MAP2() + + + + bool on_requestr_uri_1(const net_utils::http::http_request_info& query_info, + net_utils::http::http_response_info& response, + const net_utils::connection_context_base& m_conn_context); + + + bool on_requestr_uri_2(const net_utils::http::http_request_info& query_info, + net_utils::http::http_response_info& response, + const net_utils::connection_context_base& m_conn_context); + + + + + bool on_hosting_request( const net_utils::http::http_request_info& query_info, + net_utils::http::http_response_info& response, + const net_utils::connection_context_base& m_conn_context); + + bool on_request_api_1(const COMMAND_EXAMPLE_1::request& req, COMMAND_EXAMPLE_1::response& res); + bool on_request_api_2(const COMMAND_EXAMPLE_2::request& req, COMMAND_EXAMPLE_2::response& res); + + bool on_request_api_1_with_error(const COMMAND_EXAMPLE_1::request& req, COMMAND_EXAMPLE_1::response& res, epee::json_rpc::error& error_resp); + + net_utils::boosted_http_server_custum_handling m_net_server; + std::atomic m_stop; + }; +} + diff --git a/contrib/epee/demo/demo_http_server/stdafx.cpp b/contrib/epee/demo/demo_http_server/stdafx.cpp new file mode 100644 index 000000000..ecec24657 --- /dev/null +++ b/contrib/epee/demo/demo_http_server/stdafx.cpp @@ -0,0 +1,8 @@ +// stdafx.cpp : source file that includes just the standard includes +// demo_http_server.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + +// TODO: reference any additional headers you need in STDAFX.H +// and not in this file diff --git a/contrib/epee/demo/demo_http_server/stdafx.h b/contrib/epee/demo/demo_http_server/stdafx.h new file mode 100644 index 000000000..e28883202 --- /dev/null +++ b/contrib/epee/demo/demo_http_server/stdafx.h @@ -0,0 +1,40 @@ +// 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. +// + + +#pragma once + +#include "targetver.h" + + +#include + + +#define BOOST_FILESYSTEM_VERSION 3 +#define ENABLE_RELEASE_LOGGING +#include "misc_log_ex.h" + + diff --git a/contrib/epee/demo/demo_http_server/targetver.h b/contrib/epee/demo/demo_http_server/targetver.h new file mode 100644 index 000000000..6fe8eb79e --- /dev/null +++ b/contrib/epee/demo/demo_http_server/targetver.h @@ -0,0 +1,13 @@ +#pragma once + +// The following macros define the minimum required platform. The minimum required platform +// is the earliest version of Windows, Internet Explorer etc. that has the necessary features to run +// your application. The macros work by enabling all features available on platform versions up to and +// including the version specified. + +// Modify the following defines if you have to target a platform prior to the ones specified below. +// Refer to MSDN for the latest info on corresponding values for different platforms. +#ifndef _WIN32_WINNT // Specifies that the minimum required platform is Windows Vista. +#define _WIN32_WINNT 0x0600 // Change this to the appropriate value to target other versions of Windows. +#endif + diff --git a/contrib/epee/demo/demo_levin_server/demo_levin_server.cpp b/contrib/epee/demo/demo_levin_server/demo_levin_server.cpp new file mode 100644 index 000000000..215b1dd48 --- /dev/null +++ b/contrib/epee/demo/demo_levin_server/demo_levin_server.cpp @@ -0,0 +1,200 @@ +// 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 "stdafx.h" +#include "demo_levin_server.h" +#include "console_handler.h" + + +template +bool communicate(net_utils::boosted_levin_async_server& transport, int id, t_request& req, const std::string& ip, const std::string& port, bool use_async) +{ + if(use_async) + { + //IMPORTANT: do not pass local parameters from stack by reference! connect_async returns immediately, and callback will call in any thread later + transport.connect_async(ip, port, 10000, [&transport, id, req, ip, port](net_utils::connection_context_base& ctx, const boost::system::error_code& ec_) + { + if(!!ec_) + { + LOG_ERROR("Failed to connect to " << ip << ":" << port); + }else + {//connected ok! + + epee::net_utils::async_invoke_remote_command2(ctx.m_connection_id, id, req, transport.get_config_object(), [&transport, ip, port](int res_code, demo::COMMAND_EXAMPLE_1::response& rsp, net_utils::connection_context_base& ctx) + { + if(res_code < 0) + { + LOG_ERROR("Failed to invoke to " << ip << ":" << port); + }else + {//invoked ok + CHECK_AND_ASSERT_MES(rsp.m_success, false, "wrong response"); + CHECK_AND_ASSERT_MES(rsp.subs.size()==1, false, "wrong response"); + CHECK_AND_ASSERT_MES(rsp.subs.front() == demo::get_test_data(), false, "wrong response"); + LOG_PRINT_GREEN("Client COMMAND_EXAMPLE_1 async invoked ok", LOG_LEVEL_0); + } + transport.get_config_object().close(ctx.m_connection_id); + return true; + }); + LOG_PRINT_GREEN("Client COMMAND_EXAMPLE_1 async invoke requested", LOG_LEVEL_0); + } + }); + }else + { + net_utils::connection_context_base ctx = AUTO_VAL_INIT(ctx); + bool r = transport.connect(ip, port, 10000, ctx); + CHECK_AND_ASSERT_MES(r, false, "failed to connect to " << ip << ":" << port); + demo::COMMAND_EXAMPLE_1::response rsp = AUTO_VAL_INIT(rsp); + LOG_PRINT_GREEN("Client COMMAND_EXAMPLE_1 sync invoke requested", LOG_LEVEL_0); + r = epee::net_utils::invoke_remote_command2(ctx.m_connection_id, id, req, rsp, transport.get_config_object()); + CHECK_AND_ASSERT_MES(r, false, "failed to invoke levin request"); + CHECK_AND_ASSERT_MES(rsp.m_success, false, "wrong response"); + CHECK_AND_ASSERT_MES(rsp.subs.size()==1, false, "wrong response"); + CHECK_AND_ASSERT_MES(rsp.subs.front() == demo::get_test_data(), false, "wrong response"); + transport.get_config_object().close(ctx.m_connection_id); + LOG_PRINT_GREEN("Client COMMAND_EXAMPLE_1 sync invoked ok", LOG_LEVEL_0); + } + return true; +} + + +int main(int argc, char* argv[]) +{ + TRY_ENTRY(); + string_tools::set_module_name_and_folder(argv[0]); + + //set up logging options + log_space::get_set_log_detalisation_level(true, LOG_LEVEL_2); + log_space::log_singletone::add_logger(LOGGER_CONSOLE, NULL, NULL); + log_space::log_singletone::add_logger(LOGGER_FILE, + log_space::log_singletone::get_default_log_file().c_str(), + log_space::log_singletone::get_default_log_folder().c_str()); + + + + LOG_PRINT("Demo server starting ...", LOG_LEVEL_0); + + + demo::demo_levin_server srv; + + start_default_console(&srv); + + std::string bind_param = "0.0.0.0"; + std::string port = "12345"; + + if(!srv.init(port, bind_param)) + { + LOG_ERROR("Failed to initialize srv!"); + return 1; + } + + srv.run(); + + size_t c = 1; + while (!srv.is_stop()) + { + + demo::COMMAND_EXAMPLE_1::request req; + req.sub = demo::get_test_data(); + bool r = communicate(srv.get_server(), demo::COMMAND_EXAMPLE_1::ID, req, "127.0.0.1", port, (c%2 == 0)); + misc_utils::sleep_no_w(1000); + ++c; + } + bool r = srv.wait_stop(); + CHECK_AND_ASSERT_MES(r, 1, "failed to wait server stop"); + + + srv.deinit(); + + LOG_PRINT("Demo server stoped.", LOG_LEVEL_0); + return 1; + + CATCH_ENTRY_L0("main", 1); +} + +/************************************************************************/ +/* */ +/************************************************************************/ +namespace demo +{ + bool demo_levin_server::init(const std::string& bind_port, const std::string& bind_ip) + { + m_net_server.get_config_object().m_pcommands_handler = this; + LOG_PRINT_L0("Binding on " << bind_ip << ":" << bind_port); + return m_net_server.init_server(bind_port, bind_ip); + } + + bool demo_levin_server::run() + { + m_stop = false; + //here you can set worker threads count + int thrds_count = 4; + m_net_server.get_config_object().m_invoke_timeout = 10000; + m_net_server.get_config_object().m_pcommands_handler = this; + + //go to loop + LOG_PRINT("Run net_service loop( " << thrds_count << " threads)...", LOG_LEVEL_0); + if(!m_net_server.run_server(thrds_count, false)) + { + LOG_ERROR("Failed to run net tcp server!"); + } + + LOG_PRINT("net_service loop stopped.", LOG_LEVEL_0); + return true; + } + + bool demo_levin_server::deinit() + { + return m_net_server.deinit_server(); + } + + bool demo_levin_server::send_stop_signal() + { + m_net_server.send_stop_signal(); + return true; + } + int demo_levin_server::handle_command_1(int command, COMMAND_EXAMPLE_1::request& arg, COMMAND_EXAMPLE_1::response& rsp, const net_utils::connection_context_base& context) + { + CHECK_AND_ASSERT_MES(arg.sub == demo::get_test_data(), false, "wrong request"); + rsp.m_success = true; + rsp.subs.push_back(arg.sub); + LOG_PRINT_GREEN("Server COMMAND_EXAMPLE_1 ok", LOG_LEVEL_0); + return 1; + } + int demo_levin_server::handle_command_2(int command, COMMAND_EXAMPLE_2::request& arg, COMMAND_EXAMPLE_2::response& rsp, const net_utils::connection_context_base& context) + { + return 1; + } + int demo_levin_server::handle_notify_1(int command, COMMAND_EXAMPLE_1::request& arg, const net_utils::connection_context_base& context) + { + return 1; + } + int demo_levin_server::handle_notify_2(int command, COMMAND_EXAMPLE_2::request& arg, const net_utils::connection_context_base& context) + { + return 1; + } +} diff --git a/contrib/epee/demo/demo_levin_server/demo_levin_server.h b/contrib/epee/demo/demo_levin_server/demo_levin_server.h new file mode 100644 index 000000000..5a6f68f2d --- /dev/null +++ b/contrib/epee/demo/demo_levin_server/demo_levin_server.h @@ -0,0 +1,76 @@ +// 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. +// + + + +#pragma once +#include +#include + +#include "net/levin_server_cp2.h" +#include "transport_defs.h" +#include "storages/levin_abstract_invoke2.h" + +using namespace epee; + +namespace demo +{ + + class demo_levin_server: public levin::levin_commands_handler<> + { + public: + bool run(); + bool init(const std::string& bind_port = "11112", const std::string& bind_ip = "0.0.0.0"); + bool deinit(); + bool send_stop_signal(); + bool is_stop(){return m_stop;} + bool wait_stop(){return m_net_server.timed_wait_server_stop(100000);} + net_utils::boosted_levin_async_server& get_server(){return m_net_server;} + private: + + + CHAIN_LEVIN_INVOKE_MAP(); //move levin_commands_handler interface invoke(...) callbacks into invoke map + CHAIN_LEVIN_NOTIFY_STUB(); //move levin_commands_handler interface notify(...) callbacks into nothing + + BEGIN_INVOKE_MAP2(demo_levin_server) + HANDLE_INVOKE_T2(COMMAND_EXAMPLE_1, &demo_levin_server::handle_command_1) + HANDLE_INVOKE_T2(COMMAND_EXAMPLE_2, &demo_levin_server::handle_command_2) + HANDLE_NOTIFY_T2(COMMAND_EXAMPLE_1, &demo_levin_server::handle_notify_1) + HANDLE_NOTIFY_T2(COMMAND_EXAMPLE_2, &demo_levin_server::handle_notify_2) + END_INVOKE_MAP2() + + //----------------- commands handlers ---------------------------------------------- + int handle_command_1(int command, COMMAND_EXAMPLE_1::request& arg, COMMAND_EXAMPLE_1::response& rsp, const net_utils::connection_context_base& context); + int handle_command_2(int command, COMMAND_EXAMPLE_2::request& arg, COMMAND_EXAMPLE_2::response& rsp, const net_utils::connection_context_base& context); + int handle_notify_1(int command, COMMAND_EXAMPLE_1::request& arg, const net_utils::connection_context_base& context); + int handle_notify_2(int command, COMMAND_EXAMPLE_2::request& arg, const net_utils::connection_context_base& context); + //---------------------------------------------------------------------------------- + net_utils::boosted_levin_async_server m_net_server; + std::atomic m_stop; + + }; +} + diff --git a/contrib/epee/demo/demo_levin_server/stdafx.cpp b/contrib/epee/demo/demo_levin_server/stdafx.cpp new file mode 100644 index 000000000..d6ea1c6f2 --- /dev/null +++ b/contrib/epee/demo/demo_levin_server/stdafx.cpp @@ -0,0 +1,30 @@ +// 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 "stdafx.h" + diff --git a/contrib/epee/demo/demo_levin_server/stdafx.h b/contrib/epee/demo/demo_levin_server/stdafx.h new file mode 100644 index 000000000..f69d5922b --- /dev/null +++ b/contrib/epee/demo/demo_levin_server/stdafx.h @@ -0,0 +1,41 @@ +// 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. +// + + +#pragma once + +#include "targetver.h" + + +#include + + +#define BOOST_FILESYSTEM_VERSION 3 +#define ENABLE_RELEASE_LOGGING +#include "log_opt_defs.h" +#include "misc_log_ex.h" + + diff --git a/contrib/epee/demo/demo_levin_server/targetver.h b/contrib/epee/demo/demo_levin_server/targetver.h new file mode 100644 index 000000000..6fe8eb79e --- /dev/null +++ b/contrib/epee/demo/demo_levin_server/targetver.h @@ -0,0 +1,13 @@ +#pragma once + +// The following macros define the minimum required platform. The minimum required platform +// is the earliest version of Windows, Internet Explorer etc. that has the necessary features to run +// your application. The macros work by enabling all features available on platform versions up to and +// including the version specified. + +// Modify the following defines if you have to target a platform prior to the ones specified below. +// Refer to MSDN for the latest info on corresponding values for different platforms. +#ifndef _WIN32_WINNT // Specifies that the minimum required platform is Windows Vista. +#define _WIN32_WINNT 0x0600 // Change this to the appropriate value to target other versions of Windows. +#endif + diff --git a/contrib/epee/demo/generate_gcc.sh b/contrib/epee/demo/generate_gcc.sh new file mode 100644 index 000000000..fcd0a8a7e --- /dev/null +++ b/contrib/epee/demo/generate_gcc.sh @@ -0,0 +1,4 @@ +mkdir build +cd build +cmake .. +#cmake -DBOOST_ROOT=/usr/local/proj/boost_1_49_0 -DBOOST_LIBRARYDIR=/usr/local/proj/boost_1_49_0/stage/lib .. diff --git a/contrib/epee/demo/generate_vc_proj.bat b/contrib/epee/demo/generate_vc_proj.bat new file mode 100644 index 000000000..111405981 --- /dev/null +++ b/contrib/epee/demo/generate_vc_proj.bat @@ -0,0 +1,7 @@ +mkdir build + +cd build + +cmake "-DBoost_USE_STATIC_LIBS=TRUE" -G "Visual Studio 11 Win64" .. +cd .. +pause \ No newline at end of file diff --git a/contrib/epee/demo/iface/transport_defs.h b/contrib/epee/demo/iface/transport_defs.h new file mode 100644 index 000000000..97f6caadd --- /dev/null +++ b/contrib/epee/demo/iface/transport_defs.h @@ -0,0 +1,213 @@ +#pragma once + +#include "serialization/keyvalue_serialization.h" + + +namespace demo +{ + + struct some_test_subdata + { + std::string m_str; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(m_str) + END_KV_SERIALIZE_MAP() + }; + + struct some_test_data + { + std::string m_str; + uint64_t m_uint64; + uint32_t m_uint32; + uint16_t m_uint16; + uint8_t m_uint8; + int64_t m_int64; + int32_t m_int32; + int16_t m_int16; + int8_t m_int8; + double m_double; + bool m_bool; + std::list m_list_of_str; + std::list m_list_of_uint64_t; + std::list m_list_of_uint32_t; + std::list m_list_of_uint16_t; + std::list m_list_of_uint8_t; + std::list m_list_of_int64_t; + std::list m_list_of_int32_t; + std::list m_list_of_int16_t; + std::list m_list_of_int8_t; + std::list m_list_of_double; + std::list m_list_of_bool; + some_test_subdata m_subobj; + std::list m_list_of_self; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(m_str) + KV_SERIALIZE(m_uint64) + KV_SERIALIZE(m_uint32) + KV_SERIALIZE(m_uint16) + KV_SERIALIZE(m_uint8) + KV_SERIALIZE(m_int64) + KV_SERIALIZE(m_int32) + KV_SERIALIZE(m_int16) + KV_SERIALIZE(m_int8) + KV_SERIALIZE(m_double) + KV_SERIALIZE(m_bool) + KV_SERIALIZE(m_subobj) + KV_SERIALIZE(m_list_of_str) + KV_SERIALIZE(m_list_of_uint64_t) + KV_SERIALIZE(m_list_of_uint32_t) + KV_SERIALIZE(m_list_of_uint16_t) + KV_SERIALIZE(m_list_of_uint8_t) + KV_SERIALIZE(m_list_of_int64_t) + KV_SERIALIZE(m_list_of_int32_t) + KV_SERIALIZE(m_list_of_int16_t) + KV_SERIALIZE(m_list_of_int8_t) + KV_SERIALIZE(m_list_of_double) + KV_SERIALIZE(m_list_of_bool) + KV_SERIALIZE(m_list_of_self) + END_KV_SERIALIZE_MAP() + }; + + /************************************************************************/ + /* */ + /************************************************************************/ + struct COMMAND_EXAMPLE_1 + { + const static int ID = 1000; + + struct request + { + std::string example_string_data; + some_test_data sub; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(example_string_data) + KV_SERIALIZE(sub) + END_KV_SERIALIZE_MAP() + }; + + + struct response + { + bool m_success; + std::list subs; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(m_success) + KV_SERIALIZE(subs) + END_KV_SERIALIZE_MAP() + }; + }; + + + + struct COMMAND_EXAMPLE_2 + { + const static int ID = 1001; + + struct request + { + std::string example_string_data2; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(example_string_data2) + END_KV_SERIALIZE_MAP() + }; + + struct response + { + bool m_success; + + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(m_success) + END_KV_SERIALIZE_MAP() + }; + }; + + + //------------------------------------------------------------------------------------- + //------------------------------------------------------------------------------------- + //in debug purpose + bool operator != (const some_test_subdata& a, const some_test_subdata& b) + { + return b.m_str != a.m_str; + } + + bool operator == (const some_test_data& a, const some_test_data& b) + { + if( b.m_str != a.m_str + || b.m_uint64 != a.m_uint64 + || b.m_uint32 != a.m_uint32 + || b.m_uint16 != a.m_uint16 + || b.m_uint8 != a.m_uint8 + || b.m_int64 != a.m_int64 + || b.m_int32 != a.m_int32 + || b.m_int16 != a.m_int16 + || b.m_int8 != a.m_int8 + || b.m_double != a.m_double + || b.m_bool != a.m_bool + || b.m_list_of_str != a.m_list_of_str + || b.m_list_of_uint64_t != a.m_list_of_uint64_t + || b.m_list_of_uint32_t != a.m_list_of_uint32_t + || b.m_list_of_uint16_t != a.m_list_of_uint16_t + || b.m_list_of_uint8_t != a.m_list_of_uint8_t + || b.m_list_of_int64_t != a.m_list_of_int64_t + || b.m_list_of_int32_t != a.m_list_of_int32_t + || b.m_list_of_int16_t != a.m_list_of_int16_t + || b.m_list_of_int8_t != a.m_list_of_int8_t + || b.m_list_of_double != a.m_list_of_double + || b.m_list_of_bool != a.m_list_of_bool + || b.m_subobj != a.m_subobj + || b.m_list_of_self != a.m_list_of_self + ) + return false; + return true; + } + + inline some_test_data get_test_data() + { + some_test_data s; + s.m_str = "zuzuzuzuzuz"; + s.m_uint64 = 111111111111111; + s.m_uint32 = 2222222; + s.m_uint16 = 2222; + s.m_uint8 = 22; + s.m_int64 = -111111111111111; + s.m_int32 = -2222222; + s.m_int16 = -2222; + s.m_int8 = -24; + s.m_double = 0.11111; + s.m_bool = true; + s.m_list_of_str.push_back("1112121"); + s.m_list_of_uint64_t.push_back(1111111111); + s.m_list_of_uint64_t.push_back(2222222222); + s.m_list_of_uint32_t.push_back(1111111); + s.m_list_of_uint32_t.push_back(2222222); + s.m_list_of_uint16_t.push_back(1111); + s.m_list_of_uint16_t.push_back(2222); + s.m_list_of_uint8_t.push_back(11); + s.m_list_of_uint8_t.push_back(22); + + + s.m_list_of_int64_t.push_back(-1111111111); + s.m_list_of_int64_t.push_back(-222222222); + s.m_list_of_int32_t.push_back(-1111111); + s.m_list_of_int32_t.push_back(-2222222); + s.m_list_of_int16_t.push_back(-1111); + s.m_list_of_int16_t.push_back(-2222); + s.m_list_of_int8_t.push_back(-11); + s.m_list_of_int8_t.push_back(-22); + + s.m_list_of_double.push_back(0.11111); + s.m_list_of_double.push_back(0.22222); + s.m_list_of_bool.push_back(true); + s.m_list_of_bool.push_back(false); + + s.m_subobj.m_str = "subszzzzzzzz"; + s.m_list_of_self.push_back(s); + return s; + } +} \ No newline at end of file diff --git a/contrib/epee/include/ado_db_helper.h b/contrib/epee/include/ado_db_helper.h new file mode 100644 index 000000000..ed4e5b30f --- /dev/null +++ b/contrib/epee/include/ado_db_helper.h @@ -0,0 +1,1095 @@ +// 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. +// + + +#ifndef _DB_ADO_HELPER_H_ +#define _DB_ADO_HELPER_H_ + +#include +#include +#include "string_coding.h" +#include "math_helper.h" +#include "file_io_utils.h" +#include "global_stream_operators.h" + + + +#define BEGIN_TRY_SECTION() try { + +#define CATCH_TRY_SECTION(ret_val) CATCH_TRY_SECTION_MESS(ret_val, "") + +#define CATCH_TRY_SECTION_MESS(ret_val, mess_where) }\ + catch(const std::exception&ex)\ + {\ + LOG_PRINT_J("DB_ERROR: " << ex.what(), LOG_LEVEL_0);\ + return ret_val;\ + }\ + catch(const _com_error& comm_err)\ + {\ + const TCHAR* pstr = comm_err.Description();\ + std::string descr = string_encoding::convert_to_ansii(pstr?pstr:TEXT(""));\ + const TCHAR* pmessage = comm_err.ErrorMessage();\ + pstr = comm_err.Source();\ + std::string source = string_encoding::convert_to_ansii(pstr?pstr:TEXT(""));\ + LOG_PRINT_J("COM_ERROR " << mess_where << ":\n\tDescriprion:" << descr << ", \n\t Message: " << string_encoding::convert_to_ansii(pmessage) << "\n\t Source: " << source, LOG_LEVEL_0);\ + return ret_val;\ + }\ + catch(...)\ + {\ + LOG_PRINT_J("..._ERROR: Unknown error.", LOG_LEVEL_0);\ + return ret_val;\ + }\ + +namespace epee +{ +namespace ado_db_helper +{ + + struct profile_entry + { + profile_entry():m_call_count(0), m_max_time(0), m_min_time(0) + {} + //std::string m_sql; + math_helper::average m_avrg; + size_t m_call_count; + DWORD m_max_time; + DWORD m_min_time; + }; + + class profiler_manager + { + public: + typedef std::map sqls_map; + profiler_manager(){} + + static bool sort_by_timing(const sqls_map::iterator& a, const sqls_map::iterator& b) + { + return a->second.m_avrg.get_avg() > b->second.m_avrg.get_avg(); + } + + bool flush_log(const std::string& path) + { + CRITICAL_REGION_BEGIN(m_sqls_lock); + std::stringstream strm; + strm << "SQL PROFILE:\r\nStatements: " << m_sqls.size() << "\r\n"; + std::list m_sorted_by_time_sqls; + for(std::map::iterator it = m_sqls.begin();it!=m_sqls.end();it++) + m_sorted_by_time_sqls.push_back(it); + + m_sorted_by_time_sqls.sort(sort_by_timing); + + for(std::list::iterator it = m_sorted_by_time_sqls.begin();it!=m_sorted_by_time_sqls.end();it++) + { + strm << "---------------------------------------------------------------------------------------------------------\r\nSQL: " << (*it)->first << "\r\n"; + strm << "\tavrg: " << (*it)->second.m_avrg.get_avg() << "\r\n\tmax: " << (*it)->second.m_max_time << "\r\n\tmin: " << (*it)->second.m_min_time << "\r\n\tcount: " << (*it)->second.m_call_count << "\r\n"; + } + + return file_io_utils::save_string_to_file(path.c_str(), strm.str()); + CRITICAL_REGION_END(); + } + + bool push_entry(const std::string sql, DWORD time) + { + CRITICAL_REGION_BEGIN(m_sqls_lock); + profile_entry& entry_ref = m_sqls[sql]; + entry_ref.m_avrg.push(time); + entry_ref.m_call_count++; + if(time > entry_ref.m_max_time) entry_ref.m_max_time = time; + if(time < entry_ref.m_min_time || entry_ref.m_min_time == 0) entry_ref.m_min_time = time; + CRITICAL_REGION_END(); + return true; + } + + bool get_entry_avarege(const std::string sql, DWORD& time) + { + CRITICAL_REGION_BEGIN(m_sqls_lock); + sqls_map::iterator it = m_sqls.find(sql); + if(it==m_sqls.end()) + return false; + + time = static_cast(it->second.m_avrg.get_avg()); + CRITICAL_REGION_END(); + return true; + } + + private: + + sqls_map m_sqls; + critical_section m_sqls_lock; + }; +inline + profiler_manager* get_set_profiler(bool need_to_set = false, profiler_manager** pprofiler = NULL) + { + static profiler_manager* pmanager = NULL; + if(need_to_set) + pmanager = *pprofiler; + //else + // *pprofiler = pmanager; + + return pmanager; + } +inline + bool init() // INIT and DEINIT are NOT THREAD SAFE operations, CALL it BEFOR u start using this wrapper. + { + profiler_manager* pmanager = new profiler_manager(); + get_set_profiler(true, &pmanager); + return true; + } +inline + bool deinit() + { + profiler_manager* pmanager = get_set_profiler(); + //get_set_profiler(false, &pmanager); + if(pmanager) + delete pmanager; + return true; + } + inline bool push_timing(const std::string sql, DWORD time) + { + profiler_manager* pmanager = get_set_profiler(); + //get_set_profiler(false, &pmanager); + if(pmanager) + return pmanager->push_entry(sql, time); + return true; + } + + inline bool flush_profiler(const std::string path) + { + profiler_manager* pmanager = get_set_profiler(); + //get_set_profiler(false, &pmanager); + if(pmanager) + return pmanager->flush_log(path); + return true; + } + + class timing_guard + { + DWORD m_start_time; + std::string m_sql; + + public: + timing_guard(const std::string& sql) + { + m_start_time = ::GetTickCount(); + m_sql = sql; + } + + ~timing_guard() + { + DWORD timing = ::GetTickCount() - m_start_time; + push_timing(m_sql, timing); + } + }; +#define PROFILE_SQL(sql) timing_guard local_timing(sql) + + + typedef std::vector > table; + + inline bool add_parametr(ADODB::_CommandPtr cmd, const std::string& parametr) + { + _variant_t param(parametr.c_str()); + ADODB::ADO_LONGPTR size = sizeof(parametr); + ADODB::_ParameterPtr param_obj = cmd->CreateParameter("", ADODB::adVarChar, ADODB::adParamInput, static_cast(parametr.size()+1), param); + cmd->Parameters->Append(param_obj); + return true; + } + + inline bool add_parametr(ADODB::_CommandPtr cmd, const std::wstring& parametr) + { + _variant_t param(parametr.c_str()); + ADODB::ADO_LONGPTR size = sizeof(parametr); + ADODB::_ParameterPtr param_obj = cmd->CreateParameter("", ADODB::adVarWChar, ADODB::adParamInput, static_cast(parametr.size()+2), param); + cmd->Parameters->Append(param_obj); + return true; + } + + inline bool add_parametr(ADODB::_CommandPtr cmd, const __int64 parametr) + { + _variant_t param(parametr); + ADODB::ADO_LONGPTR size = static_cast(sizeof(parametr)); + ADODB::_ParameterPtr param_obj = cmd->CreateParameter("parametr", ADODB::adBigInt, ADODB::adParamInput, static_cast(size), param); + cmd->Parameters->Append(param_obj); + return true; + } + + inline bool add_parametr(ADODB::_CommandPtr cmd, const unsigned __int64 parametr) + { + _variant_t param(parametr); + ADODB::ADO_LONGPTR size = static_cast(sizeof(parametr)); + ADODB::_ParameterPtr param_obj = cmd->CreateParameter("parametr", ADODB::adUnsignedBigInt, ADODB::adParamInput, static_cast(size), param); + cmd->Parameters->Append(param_obj); + return true; + } + + + inline bool add_parametr(ADODB::_CommandPtr cmd, const int parametr) + { + _variant_t param(parametr); + ADODB::ADO_LONGPTR size = static_cast(sizeof(parametr)); + ADODB::_ParameterPtr param_obj = cmd->CreateParameter("parametr", ADODB::adInteger, ADODB::adParamInput, static_cast(size), param); + cmd->Parameters->Append(param_obj); + return true; + } + + inline bool add_parametr(ADODB::_CommandPtr cmd, const unsigned int parametr) + { + _variant_t param(parametr); + ADODB::ADO_LONGPTR size = static_cast(sizeof(parametr)); + ADODB::_ParameterPtr param_obj = cmd->CreateParameter("parametr", ADODB::adUnsignedInt, ADODB::adParamInput, static_cast(size), param); + cmd->Parameters->Append(param_obj); + return true; + } + + inline bool add_parametr(ADODB::_CommandPtr cmd, float parametr) + { + _variant_t param; + param.ChangeType(VT_R4); + param.fltVal = parametr; + ADODB::_ParameterPtr param_obj = cmd->CreateParameter("parametr", ADODB::adSingle, ADODB::adParamInput, static_cast(sizeof(float)), param); + cmd->Parameters->Append(param_obj); + return true; + } + + inline bool add_parametr(ADODB::_CommandPtr cmd, bool parametr) + { + _variant_t param; + param = parametr; + ADODB::_ParameterPtr param_obj = cmd->CreateParameter("parametr", ADODB::adBoolean, ADODB::adParamInput, sizeof(parametr), param); + cmd->Parameters->Append(param_obj); + return true; + } + + + inline bool add_parametr(ADODB::_CommandPtr cmd, _variant_t parametr) + { + ADODB::_ParameterPtr param_obj = cmd->CreateParameter("parametr", ADODB::adDBTimeStamp, ADODB::adParamInput, sizeof(parametr), parametr); + cmd->Parameters->Append(param_obj); + return true; + } + + + inline bool add_parametr_as_double(ADODB::_CommandPtr cmd, const DATE parametr) + { + _variant_t param; + param.ChangeType(VT_R8); + param.dblVal = parametr; + ADODB::_ParameterPtr param_obj = cmd->CreateParameter("parametr", ADODB::adDouble, ADODB::adParamInput, sizeof(float), param); + cmd->Parameters->Append(param_obj); + return true; + } + + template + inline bool add_parametr(ADODB::_CommandPtr cmd, const std::list params) + { + for(std::list::const_iterator it = params.begin(); it!=params.end(); it++) + if(!add_parametr(cmd, *it)) + return false; + return true; + } + + /* + inline bool add_parametr(ADODB::_CommandPtr cmd, const size_t parametr) + { + _variant_t param; + param.ChangeType(VT_I4); + param.intVal = parametr; + ADODB::_ParameterPtr param_obj = cmd->CreateParameter("parametr", ADODB::adInteger, ADODB::adParamInput, sizeof(parametr), param); + cmd->Parameters->Append(param_obj); + return true; + }*/ + + + inline bool add_parametr(ADODB::_CommandPtr cmd, const DATE parametr) + { + /*_variant_t param; + param.ChangeType(VT_R8); + param.dblVal = parametr; + ADODB::_ParameterPtr param_obj = cmd->CreateParameter("parametr", ADODB::adDouble, ADODB::adParamInput, sizeof(float), param); + cmd->Parameters->Append(param_obj);*/ + + _variant_t param; + param.ChangeType(VT_DATE); + param.date = parametr; + ADODB::_ParameterPtr param_obj = cmd->CreateParameter("parametr", ADODB::adDBDate, ADODB::adParamInput, sizeof(parametr), param); + cmd->Parameters->Append(param_obj); + + return true; + } + + + inline bool execute_helper(ADODB::_CommandPtr cmd, _variant_t* pcount_processed = NULL) + { + //BEGIN_TRY_SECTION(); + + cmd->Execute(pcount_processed, NULL, ADODB::adExecuteNoRecords); + + + //CATCH_TRY_SECTION(false); + + return true; + } + + + inline bool select_helper(ADODB::_CommandPtr cmd, table& result_vector) + { + result_vector.clear(); + //BEGIN_TRY_SECTION(); + + ADODB::_RecordsetPtr precordset = cmd->Execute(NULL, NULL, NULL); + if(!precordset) + { + LOG_ERROR("DB_ERROR: cmd->Execute returned NULL!!!"); + return false; + } + + //if(precordset->EndOfFile == EOF) + //{ + // return true; + //} + /*try + { + if(precordset->MoveFirst()!= S_OK) + { + LOG_ERROR("DB_ERROR: Filed to move first!!!"); + return false; + } + } + catch (...) + { + return true; + }*/ + + size_t current_record_index = 0; + while(precordset->EndOfFile != EOF) + { + result_vector.push_back(table::value_type()); + size_t fields_count = precordset->Fields->Count; + result_vector[current_record_index].resize(fields_count); + for(size_t current_field_index = 0; current_field_index < fields_count; current_field_index++) + { + _variant_t var; + var.ChangeType(VT_I2); + var.intVal = static_cast(current_field_index); + result_vector[current_record_index][current_field_index] = precordset->Fields->GetItem(var)->Value; + } + precordset->MoveNext(); + current_record_index++; + } + //CATCH_TRY_SECTION(false); + return true; + } + + + template + struct adapter_zero + { + + }; + + template + struct adapter_single + { + TParam1 tparam1; + }; + template + struct adapter_double + { + TParam1 tparam1; + TParam2 tparam2; + }; + + + template + struct adapter_triple + { + TParam1 tparam1; + TParam2 tparam2; + TParam3 tparam3; + }; + + template + struct adapter_quad + { + TParam1 tparam1; + TParam2 tparam2; + TParam3 tparam3; + TParam4 tparam4; + }; + + template + struct adapter_quanto + { + TParam1 tparam1; + TParam2 tparam2; + TParam3 tparam3; + TParam4 tparam4; + TParam5 tparam5; + }; + + template + struct adapter_sixto + { + TParam1 tparam1; + TParam2 tparam2; + TParam3 tparam3; + TParam4 tparam4; + TParam5 tparam5; + TParam6 tparam6; + }; + + template + struct adapter_sevento + { + TParam1 tparam1; + TParam2 tparam2; + TParam3 tparam3; + TParam4 tparam4; + TParam5 tparam5; + TParam6 tparam6; + TParam7 tparam7; + }; + + template + struct adapter_nine + { + TParam1 tparam1; + TParam2 tparam2; + TParam3 tparam3; + TParam4 tparam4; + TParam5 tparam5; + TParam6 tparam6; + TParam7 tparam7; + TParam8 tparam8; + TParam9 tparam9; + }; + + template + bool add_parametrs_multi(ADODB::_CommandPtr cmd, const adapter_zero& params) + { + return true; + } + + template + bool add_parametrs_multi(ADODB::_CommandPtr cmd, const adapter_single& params) + { + return add_parametr(cmd, params.tparam1); + } + + template + bool add_parametrs_multi(ADODB::_CommandPtr cmd, const adapter_double& params) + { + if(!add_parametr(cmd, params.tparam1)) return false; + return add_parametr(cmd, params.tparam2); + } + + template + bool add_parametrs_multi(ADODB::_CommandPtr cmd, const adapter_triple& params) + { + if(!add_parametr(cmd, params.tparam1)) return false; + if(!add_parametr(cmd, params.tparam2)) return false; + return add_parametr(cmd, params.tparam3); + } + + template + bool add_parametrs_multi(ADODB::_CommandPtr cmd, const adapter_quad& params) + { + if(!add_parametr(cmd, params.tparam1)) return false; + if(!add_parametr(cmd, params.tparam2)) return false; + if(!add_parametr(cmd, params.tparam3)) return false; + return add_parametr(cmd, params.tparam4); + } + + template + bool add_parametrs_multi(ADODB::_CommandPtr cmd, const adapter_quanto& params) + { + if(!add_parametr(cmd, params.tparam1)) return false; + if(!add_parametr(cmd, params.tparam2)) return false; + if(!add_parametr(cmd, params.tparam3)) return false; + if(!add_parametr(cmd, params.tparam4)) return false; + return add_parametr(cmd, params.tparam5); + } + + template + bool add_parametrs_multi(ADODB::_CommandPtr cmd, const adapter_sixto& params) + { + if(!add_parametr(cmd, params.tparam1)) return false; + if(!add_parametr(cmd, params.tparam2)) return false; + if(!add_parametr(cmd, params.tparam3)) return false; + if(!add_parametr(cmd, params.tparam4)) return false; + if(!add_parametr(cmd, params.tparam5)) return false; + return add_parametr(cmd, params.tparam6); + } + + template + bool add_parametrs_multi(ADODB::_CommandPtr cmd, const adapter_sevento& params) + { + if(!add_parametr(cmd, params.tparam1)) return false; + if(!add_parametr(cmd, params.tparam2)) return false; + if(!add_parametr(cmd, params.tparam3)) return false; + if(!add_parametr(cmd, params.tparam4)) return false; + if(!add_parametr(cmd, params.tparam5)) return false; + if(!add_parametr(cmd, params.tparam6)) return false; + return add_parametr(cmd, params.tparam7); + } + + template + bool add_parametrs_multi(ADODB::_CommandPtr cmd, const adapter_nine& params) + { + if(!add_parametr(cmd, params.tparam1)) return false; + if(!add_parametr(cmd, params.tparam2)) return false; + if(!add_parametr(cmd, params.tparam3)) return false; + if(!add_parametr(cmd, params.tparam4)) return false; + if(!add_parametr(cmd, params.tparam5)) return false; + if(!add_parametr(cmd, params.tparam6)) return false; + if(!add_parametr(cmd, params.tparam7)) return false; + if(!add_parametr(cmd, params.tparam8)) return false; + return add_parametr(cmd, params.tparam9); + } + + template + std::string print_parameters_multi(const adapter_sevento& params) + { + std::stringstream strm; + strm << params.tparam1 << ", " << params.tparam2 << ", " << params.tparam3 << ", " << params.tparam4 << ", " << params.tparam5 << ", " << params.tparam6 << ", " << params.tparam7; + return strm.str(); + } + + template + std::string print_parameters_multi(const adapter_nine& params) + { + std::stringstream strm; + strm << params.tparam1 << ", " << params.tparam2 << ", " << params.tparam3 << ", " << params.tparam4 << ", " << params.tparam5 << ", " << params.tparam6 << ", " << params.tparam7 << ", " << params.tparam8 << ", " << params.tparam9; + return strm.str(); + } + + template + std::string print_parameters_multi(const adapter_sixto& params) + { + std::stringstream strm; + strm << params.tparam1 << ", " << params.tparam2 << ", " << params.tparam3 << ", " << params.tparam4 << ", " << params.tparam5 << ", " << params.tparam6; + return strm.str(); + } + + template + std::string print_parameters_multi(const adapter_quanto& params) + { + std::stringstream strm; + strm << params.tparam1 << ", " << params.tparam2 << ", " << params.tparam3 << ", " << params.tparam4 << ", " << params.tparam5; + return strm.str(); + } + + + template + std::string print_parameters_multi(const adapter_quad& params) + { + std::stringstream strm; + strm << params.tparam1 << ", " << params.tparam2 << ", " << params.tparam3 << ", " << params.tparam4; + return strm.str(); + } + + template + std::string print_parameters_multi(const adapter_triple& params) + { + std::stringstream strm; + strm << params.tparam1 << ", " << params.tparam2 << ", " << params.tparam3; + return strm.str(); + } + + template + std::string get_str_param(const TParam& prm) + { + std::stringstream strm; + strm << prm; + return strm.str(); + } + + template + std::string get_str_param(const std::list& prm_lst) + { + std::stringstream strm; + for(std::list::const_iterator it = prm_lst.begin();it!=prm_lst.end();it++) + strm << get_str_param(*it) << ", "; + return strm.str(); + } + + + template + std::string print_parameters_multi(const adapter_double& params) + { + std::stringstream strm; + strm << get_str_param(params.tparam1) << ", " << get_str_param(params.tparam2); + return strm.str(); + } + + template + std::string print_parameters_multi(const adapter_single& params) + { + std::stringstream strm; + strm << get_str_param(params.tparam1); + return strm.str(); + } + + template + std::string print_parameters_multi(const adapter_zero& params) + { + std::stringstream strm; + strm << "(no parametrs)"; + return strm.str(); + } + + + template + bool execute_helper_multiparam(ADODB::_ConnectionPtr pconnection, const std::string& sql_statment, const TParams& parametrs, _variant_t* pcount_processed = NULL) + { + PROFILE_SQL(sql_statment); + bool res = false; + BEGIN_TRY_SECTION(); + + ADODB::_CommandPtr cmd; + cmd.CreateInstance(__uuidof(ADODB::Command)); + cmd->CommandText = _bstr_t(sql_statment.c_str()); + + if(!add_parametrs_multi(cmd, parametrs)) + return false; + + cmd->ActiveConnection = pconnection; + res = execute_helper(cmd, pcount_processed); + + CATCH_TRY_SECTION_MESS(false, "while statment: " << sql_statment << " [params]: " << print_parameters_multi(parametrs)); + return res; + } + + + template + inline + bool select_helper_multiparam(ADODB::_ConnectionPtr pconnection, const std::string& sql_statment, const TParams& parametrs, table& result_vector) + { + PROFILE_SQL(sql_statment); + bool res = false; + BEGIN_TRY_SECTION(); + ADODB::_CommandPtr cmd; + cmd.CreateInstance(__uuidof(ADODB::Command)); + cmd->CommandText = _bstr_t(sql_statment.c_str()); + + + if(!add_parametrs_multi(cmd, parametrs)) + return false; + + cmd->ActiveConnection = pconnection; + res = select_helper(cmd, result_vector); + CATCH_TRY_SECTION_MESS(false, "while statment: " << sql_statment << " [params]: " << print_parameters_multi(parametrs)); + return res; + } + + + template + inline + bool select_helper_param_container(ADODB::_ConnectionPtr pconnection, const std::string& sql_statment, const TParams& parametrs, table& result_vector) + { + PROFILE_SQL(sql_statment); + bool res = false; + BEGIN_TRY_SECTION(); + ADODB::_CommandPtr cmd; + cmd.CreateInstance(__uuidof(ADODB::Command)); + cmd->CommandText = _bstr_t(sql_statment.c_str()); + + + for(TParams::const_iterator it = parametrs.begin(); it!=parametrs.end(); it++) + { + add_parametr(cmd, *it); + } + + cmd->ActiveConnection = pconnection; + res = select_helper(cmd, result_vector); + + CATCH_TRY_SECTION(false); + return res; + } + + + inline + bool execute_helper(ADODB::_ConnectionPtr pconnection, const std::string& sql_statment, _variant_t* pvt = NULL) + { + adapter_zero params; + return execute_helper_multiparam(pconnection, sql_statment, params, pvt); + } + + template + bool execute_helper(ADODB::_ConnectionPtr pconnection, const std::string& sql_statment, const TParam& parametr) + { + adapter_single params; + params.tparam1 = parametr; + return execute_helper_multiparam(pconnection, sql_statment, params); + } + + + template + bool execute_helper(ADODB::_ConnectionPtr pconnection, const std::string& sql_statment, const TParam1& parametr1, const TParam2& parametr2) + { + adapter_double params; + params.tparam1 = parametr1; + params.tparam2 = parametr2; + return execute_helper_multiparam(pconnection, sql_statment, params); + + } + + template + bool execute_helper(ADODB::_ConnectionPtr pconnection, const std::string& sql_statment, const TParam1& parametr1, const TParam2& parametr2, const TParam3& parametr3) + { + adapter_triple params; + params.tparam1 = parametr1; + params.tparam2 = parametr2; + params.tparam3 = parametr3; + return execute_helper_multiparam(pconnection, sql_statment, params); + } + + template + bool execute_helper(ADODB::_ConnectionPtr pconnection, const std::string& sql_statment, const TParam1& parametr1, const TParam2& parametr2, const TParam3& parametr3, const TParam4& parametr4) + { + adapter_quad params; + params.tparam1 = parametr1; + params.tparam2 = parametr2; + params.tparam3 = parametr3; + params.tparam4 = parametr4; + return execute_helper_multiparam(pconnection, sql_statment, params); + } + + template + bool execute_helper(ADODB::_ConnectionPtr pconnection, const std::string& sql_statment, const TParam1& parametr1, const TParam2& parametr2, const TParam3& parametr3, const TParam4& parametr4, const TParam5& parametr5) + { + adapter_quanto params; + params.tparam1 = parametr1; + params.tparam2 = parametr2; + params.tparam3 = parametr3; + params.tparam4 = parametr4; + params.tparam5 = parametr5; + return execute_helper_multiparam(pconnection, sql_statment, params); + } + + template + bool execute_helper(ADODB::_ConnectionPtr pconnection, const std::string& sql_statment, const TParam1& parametr1, const TParam2& parametr2, const TParam3& parametr3, const TParam4& parametr4, const TParam5& parametr5, const TParam6& parametr6) + { + adapter_sixto params; + params.tparam1 = parametr1; + params.tparam2 = parametr2; + params.tparam3 = parametr3; + params.tparam4 = parametr4; + params.tparam5 = parametr5; + params.tparam6 = parametr6; + return execute_helper_multiparam(pconnection, sql_statment, params); + } + + + template + bool execute_helper(ADODB::_ConnectionPtr pconnection, const std::string& sql_statment, const TParam1& parametr1, const TParam2& parametr2, const TParam3& parametr3, const TParam4& parametr4, const TParam5& parametr5, const TParam6& parametr6, const TParam7& parametr7) + { + adapter_sevento params; + params.tparam1 = parametr1; + params.tparam2 = parametr2; + params.tparam3 = parametr3; + params.tparam4 = parametr4; + params.tparam5 = parametr5; + params.tparam6 = parametr6; + params.tparam7 = parametr7; + return execute_helper_multiparam(pconnection, sql_statment, params); + } + + inline + bool select_helper(ADODB::_ConnectionPtr pconnection, const std::string& sql_statment, table& result_vector) + { + adapter_zero params; + return select_helper_multiparam(pconnection, sql_statment, params, result_vector); + } + + + template + bool select_helper(ADODB::_ConnectionPtr pconnection, const std::string& sql_statment, const TParam& parametr, table& result_vector) + { + adapter_single params; + params.tparam1 = parametr; + return select_helper_multiparam(pconnection, sql_statment, params, result_vector); + } + + template + bool select_helper(ADODB::_ConnectionPtr pconnection, const std::string& sql_statment, const TParam1 parametr1, const TParam2 parametr2, table& result_vector) + { + adapter_double params; + params.tparam1 = parametr1; + params.tparam2 = parametr2; + return select_helper_multiparam(pconnection, sql_statment, params, result_vector); + + } + + template + bool select_helper(ADODB::_ConnectionPtr pconnection, const std::string& sql_statment, const TParam1 parametr1, const TParam2 parametr2, const TParam3 parametr3, table& result_vector) + { + adapter_triple params; + params.tparam1 = parametr1; + params.tparam2 = parametr2; + params.tparam3 = parametr3; + return select_helper_multiparam(pconnection, sql_statment, params, result_vector); + + } + + template + bool select_helper(ADODB::_ConnectionPtr pconnection, const std::string& sql_statment, const TParam1 parametr1, const TParam2 parametr2, const TParam3 parametr3, const TParam4 parametr4, table& result_vector) + { + adapter_quad params; + params.tparam1 = parametr1; + params.tparam2 = parametr2; + params.tparam3 = parametr3; + params.tparam4 = parametr4; + return select_helper_multiparam(pconnection, sql_statment, params, result_vector); + } + + template + bool select_helper(ADODB::_ConnectionPtr pconnection, const std::string& sql_statment, const TParam1 parametr1, const TParam2 parametr2, const TParam3 parametr3, const TParam4 parametr4, const TParam5 parametr5, table& result_vector) + { + adapter_quanto params; + params.tparam1 = parametr1; + params.tparam2 = parametr2; + params.tparam3 = parametr3; + params.tparam4 = parametr4; + params.tparam5 = parametr5; + return select_helper_multiparam(pconnection, sql_statment, params, result_vector); + } + + + template + bool select_helper(ADODB::_ConnectionPtr pconnection, const std::string& sql_statment, const TParam1 parametr1, const TParam2 parametr2, const TParam3 parametr3, const TParam4 parametr4, const TParam5 parametr5, const TParam6 parametr6, table& result_vector) + { + adapter_sixto params; + params.tparam1 = parametr1; + params.tparam2 = parametr2; + params.tparam3 = parametr3; + params.tparam4 = parametr4; + params.tparam5 = parametr5; + params.tparam6 = parametr6; + return select_helper_multiparam(pconnection, sql_statment, params, result_vector); + } + + template + bool select_helper(ADODB::_ConnectionPtr pconnection, const std::string& sql_statment, const TParam1 parametr1, const TParam2 parametr2, const TParam3 parametr3, const TParam4 parametr4, const TParam5 parametr5, const TParam6 parametr6, const TParam7 parametr7, table& result_vector) + { + adapter_sevento params; + params.tparam1 = parametr1; + params.tparam2 = parametr2; + params.tparam3 = parametr3; + params.tparam4 = parametr4; + params.tparam5 = parametr5; + params.tparam6 = parametr6; + params.tparam7 = parametr7; + return select_helper_multiparam(pconnection, sql_statment, params, result_vector); + } + + template + bool select_helper(ADODB::_ConnectionPtr pconnection, const std::string& sql_statment, const TParam1 parametr1, const TParam2 parametr2, const TParam3 parametr3, const TParam4 parametr4, const TParam5 parametr5, const TParam6 parametr6, const TParam7 parametr7,const TParam8 parametr8,const TParam9 parametr9, table& result_vector) + { + adapter_nine params; + params.tparam1 = parametr1; + params.tparam2 = parametr2; + params.tparam3 = parametr3; + params.tparam4 = parametr4; + params.tparam5 = parametr5; + params.tparam6 = parametr6; + params.tparam7 = parametr7; + params.tparam8 = parametr8; + params.tparam9 = parametr9; + return select_helper_multiparam(pconnection, sql_statment, params, result_vector); + } + + + + + /************************************************************************/ + /* */ + /************************************************************************/ + + class per_thread_connection_pool + { + public: + bool init(const std::string& connection_string, const std::string& login, const std::string& pass) + { + m_connection_string = connection_string; + m_login = login; + m_password = pass; + if(!get_db_connection().GetInterfacePtr()) + return false; + + return true; + } + + ADODB::_ConnectionPtr& get_db_connection() + { + + //soci::session + + m_db_connections_lock.lock(); + boost::shared_ptr& conn_ptr = m_db_connections[::GetCurrentThreadId()]; + m_db_connections_lock.unlock(); + if(!conn_ptr.get()) + { + conn_ptr.reset(new ADODB::_ConnectionPtr()); + ADODB::_ConnectionPtr& conn = *conn_ptr.get(); + //init new connection + + BEGIN_TRY_SECTION(); + //_bstr_t str = _bstr_t("Provider=SQLOLEDB;Data Source=SRV1;Integrated Security=SSPI;Initial Catalog=dispatcher;"); + + if(S_OK != conn.CreateInstance(__uuidof(ADODB::Connection))) + { + LOG_ERROR("Failed to Create, instance, was CoInitialize called ???!"); + return conn; + } + + HRESULT res = conn->Open(_bstr_t(m_connection_string.c_str()), _bstr_t(m_login.c_str()), _bstr_t(m_password.c_str()), NULL); + if(res != S_OK) + { + LOG_ERROR("Failed to connect do DB, connection str:" << m_connection_string); + return conn; + } + CATCH_TRY_SECTION_MESS(conn, "while creating another connection"); + LOG_PRINT("New DB Connection added for threadid=" << ::GetCurrentThreadId(), LOG_LEVEL_0); + ado_db_helper::execute_helper(conn, "set enable_seqscan=false;"); + return conn; + } + + return *conn_ptr.get(); + } + + //---------------------------------------------------------------------------------------------- + bool check_status() + { + ADODB::_ConnectionPtr& rconn = get_db_connection(); + if(!ado_db_helper::execute_helper(rconn, "SET CLIENT_ENCODING TO 'SQL_ASCII'")) + { + + try{ + HRESULT res = rconn->Close(); + } + catch(...) + { + + }; + BEGIN_TRY_SECTION(); + + HRESULT res = rconn->Open(_bstr_t(m_connection_string.c_str()), _bstr_t(m_login.c_str()), _bstr_t(m_password.c_str()), NULL); + if(res != S_OK) + { + LOG_PRINT("Failed to restore connection to local AI DB", LOG_LEVEL_1); + return false; + } + CATCH_TRY_SECTION(false); + } + + return true; + } + + protected: + private: + std::map > m_db_connections; + critical_section m_db_connections_lock; + std::string m_connection_string; + std::string m_login; + std::string m_password; + }; + + + template + bool find_or_add_t(const std::string& sql_select_statment, const std::string& sql_insert_statment, OUT default_id_type& id, OUT bool& new_object_added, TParam1 parametr_1, t_conn& c) + { + ado_db_helper::adapter_single params; + params.tparam1 = parametr_1; + return find_or_add_t_multiparametred(sql_select_statment, sql_insert_statment, id, new_object_added, params, c); + } + + + template + bool find_or_add_t(const std::string& sql_select_statment, const std::string& sql_insert_statment, OUT default_id_type& id, OUT bool& new_object_added, TParam1 parametr_1, TParam2 parametr_2, t_conn& c) + { + ado_db_helper::adapter_double params; + params.tparam1 = parametr_1; + params.tparam2 = parametr_2; + return find_or_add_t_multiparametred(sql_select_statment, sql_insert_statment, id, new_object_added, params, c); + } + + + template + bool find_or_add_t(const std::string& sql_select_statment, const std::string& sql_insert_statment, OUT default_id_type& id, OUT bool& new_object_added, TParam1 parametr_1, TParam2 parametr_2, TParam3 parametr_3, t_conn& c) + { + ado_db_helper::adapter_triple params; + params.tparam1 = parametr_1; + params.tparam2 = parametr_2; + params.tparam3 = parametr_3; + return find_or_add_t_multiparametred(sql_select_statment, sql_insert_statment, id, new_object_added, params, c); + } + + template + bool find_or_add_t(const std::string& sql_select_statment, const std::string& sql_insert_statment, OUT default_id_type& id, OUT bool& new_object_added, TParam1 parametr_1, TParam2 parametr_2, TParam3 parametr_3, TParam4 parametr_4, t_conn& c) + { + ado_db_helper::adapter_quad params; + params.tparam1 = parametr_1; + params.tparam2 = parametr_2; + params.tparam3 = parametr_3; + params.tparam4 = parametr_4; + return find_or_add_t_multiparametred(sql_select_statment, sql_insert_statment, id, new_object_added, params, c); + } + + template + bool find_or_add_t_multiparametred(const std::string& sql_select_statment, const std::string& sql_insert_statment, OUT default_id_type& id, OUT bool& new_object_added, TParams params, t_conn& c) + { + + //CHECK_CONNECTION(false); + + new_object_added = false; + ado_db_helper::table result_table; + + bool res = select_helper_multiparam(c.get_db_connection(), sql_select_statment, params, result_table); + if(!result_table.size()) + { + res = select_helper_multiparam(c.get_db_connection(), sql_insert_statment, params, result_table); + if(!res || !result_table.size()) + { + //last time try to select + res = select_helper_multiparam(c.get_db_connection(), sql_select_statment, params, result_table); + CHECK_AND_ASSERT_MES(res, false, "Failed to execute statment: " << sql_select_statment); + CHECK_AND_ASSERT_MES(result_table.size(), false, "No records returned from statment: " << sql_select_statment); + }else + { + new_object_added = true; + } + } + + BEGIN_TRY_SECTION() + id = result_table[0][0]; + CATCH_TRY_SECTION_MESS(false, "while converting returned value [find_or_add_t_multiparametred()]"); + + return true; + } + +} +} +#endif //!_DB_HELPER_H_ diff --git a/contrib/epee/include/console_handler.h b/contrib/epee/include/console_handler.h new file mode 100644 index 000000000..238abc80c --- /dev/null +++ b/contrib/epee/include/console_handler.h @@ -0,0 +1,310 @@ +// 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. +// + + + +#pragma once + +namespace epee +{ + + + + + template + bool empty_commands_handler(t_server* psrv, const std::string& command) + { + return true; + } + + + template + bool default_console_handler(t_server* psrv, chain_handler ch_handler, const std::string usage = "") + { + TRY_ENTRY(); + bool continue_handle = true; + while(continue_handle) + { + char command_buff[400] = {0}; + std::string command; + std::cin.getline(command_buff, 399); + if(std::cin.eof() || std::cin.fail()) + { + LOG_PRINT("std::cin.eof() or std::cin.fail(), stopping...", LOG_LEVEL_0); + continue_handle = false; + break; + } + command = command_buff; + + if(!command.compare("exit") || !command.compare("q") ) + { + psrv->send_stop_signal(); + continue_handle = false; + }else if ( !command.compare(0, 7, "set_log")) + { + //parse set_log command + if(command.size() != 9) + { + std::cout << "wrong syntax: " << command << std::endl << "use set_log n" << std::endl; + continue; + } + int n = 0; + if(!string_tools::get_xtype_from_string(n, command.substr(8, 1))) + { + std::cout << "wrong syntax: " << command << std::endl << "use set_log n" << std::endl; + continue; + } + log_space::get_set_log_detalisation_level(true, n); + LOG_PRINT_L0("New log level set " << n); + } + else if(ch_handler(psrv, command)) + continue; + else + { + std::cout << "unknown command: " << command << std::endl; + std::cout << usage; + } + } + return true; + CATCH_ENTRY_L0("console_handler", false); + } + + template + bool default_console_handler2(chain_handler ch_handler, const std::string usage) + { + TRY_ENTRY(); + bool continue_handle = true; + while(continue_handle) + { + char command_buff[400] = {0}; + std::string command; + std::cin.getline(command_buff, 399); + if(std::cin.eof() || std::cin.fail()) + { + + LOG_PRINT("std::cin.eof() or std::cin.fail(), stopping...", LOG_LEVEL_0); + continue_handle = false; + break; + } + command = command_buff; + + if(!command.compare("exit") || !command.compare("q") ) + { + continue_handle = false; + }else if ( !command.compare(0, 7, "set_log")) + { + //parse set_log command + if(command.size() != 9) + { + std::cout << "wrong syntax: " << command << std::endl << "use set_log n" << std::endl; + continue; + } + int n = 0; + if(!string_tools::get_xtype_from_string(n, command.substr(8, 1))) + { + std::cout << "wrong syntax: " << command << std::endl << "use set_log n" << std::endl; + continue; + } + log_space::get_set_log_detalisation_level(true, n); + LOG_PRINT_L0("New log level set " << n); + } + else if(ch_handler(command)) + continue; + else + { + std::cout << "unknown command: " << command << std::endl; + std::cout << usage; + } + } + return true; + CATCH_ENTRY_L0("console_handler", false); + } + + + + + template + bool start_default_console(t_server* ptsrv, t_handler handlr, const std::string& usage = "") + { + boost::thread( boost::bind(default_console_handler, ptsrv, handlr, usage) ); + return true; + } + + template + bool start_default_console(t_server* ptsrv, const std::string& usage = "") + { + return start_default_console(ptsrv, empty_commands_handler, usage); + } + + template + bool no_srv_param_adapter(t_server* ptsrv, const std::string& cmd, t_handler handlr) + { + return handlr(cmd); + } + + template + bool run_default_console_handler_no_srv_param(t_server* ptsrv, t_handler handlr, const std::string& usage = "") + { + return default_console_handler(ptsrv, boost::bind(no_srv_param_adapter, _1, _2, handlr), usage); + } + + template + bool start_default_console_handler_no_srv_param(t_server* ptsrv, t_handler handlr, const std::string& usage = "") + { + boost::thread( boost::bind(run_default_console_handler_no_srv_param, ptsrv, handlr, usage) ); + return true; + } + + /*template + bool f(int i, a l) + { + return true; + }*/ + /* + template + bool default_console_handler2(chain_handler ch_handler, const std::string usage) + */ + + + /*template + bool start_default_console2(t_handler handlr, const std::string& usage = "") + { + //std::string usage_local = usage; + boost::thread( boost::bind(default_console_handler2, handlr, usage) ); + //boost::function p__ = boost::bind(f, 1, handlr); + //boost::function p__ = boost::bind(default_console_handler2, handlr, usage); + //boost::thread tr(p__); + return true; + }*/ + + /************************************************************************/ + /* */ + /************************************************************************/ + class console_handlers_binder + { + typedef boost::function &)> console_command_handler; + typedef std::map > command_handlers_map; + std::unique_ptr m_console_thread; + command_handlers_map m_command_handlers; + public: + std::string get_usage() + { + std::stringstream ss; + size_t max_command_len = 0; + for(auto& x:m_command_handlers) + if(x.first.size() > max_command_len) + max_command_len = x.first.size(); + + for(auto& x:m_command_handlers) + { + ss.width(max_command_len + 3); + ss << std::left << x.first << x.second.second << ENDL; + } + return ss.str(); + } + void set_handler(const std::string& cmd, const console_command_handler& hndlr, const std::string& usage = "") + { + command_handlers_map::mapped_type & vt = m_command_handlers[cmd]; + vt.first = hndlr; + vt.second = usage; + } + bool process_command_vec(const std::vector& cmd) + { + if(!cmd.size()) + return false; + auto it = m_command_handlers.find(cmd.front()); + if(it == m_command_handlers.end()) + return false; + std::vector cmd_local(cmd.begin()+1, cmd.end()); + return it->second.first(cmd_local); + } + + bool process_command_str(const std::string& cmd) + { + std::vector cmd_v; + boost::split(cmd_v,cmd,boost::is_any_of(" "), boost::token_compress_on); + return process_command_vec(cmd_v); + } + + /*template + bool start_handling(t_srv& srv, const std::string& usage_string = "") + { + start_default_console_handler_no_srv_param(&srv, boost::bind(&console_handlers_binder::process_command_str, this, _1)); + return true; + }*/ + + bool start_handling(const std::string& usage_string = "") + { + m_console_thread.reset(new boost::thread(boost::bind(&console_handlers_binder::run_handling, this, usage_string) )); + return true; + } + + bool stop_handling() + { + if(m_console_thread.get()) + m_console_thread->interrupt(); + return true; + } + + + bool run_handling(const std::string usage_string) + { + return default_console_handler2(boost::bind(&console_handlers_binder::process_command_str, this, _1), usage_string); + } + + /*template + bool run_handling(t_srv& srv, const std::string& usage_string) + { + return run_default_console_handler_no_srv_param(&srv, boost::bind(&console_handlers_binder::process_command_str, this, _1), usage_string); + }*/ + + }; + + /* work around because of broken boost bind */ + template + class srv_console_handlers_binder: public console_handlers_binder + { + bool process_command_str(t_server* /*psrv*/, const std::string& cmd) + { + return console_handlers_binder::process_command_str(cmd); + } + public: + bool start_handling(t_server* psrv, const std::string& usage_string = "") + { + boost::thread(boost::bind(&srv_console_handlers_binder::run_handling, this, psrv, usage_string) ); + return true; + } + + bool run_handling(t_server* psrv, const std::string usage_string) + { + return default_console_handler(psrv, boost::bind(&srv_console_handlers_binder::process_command_str, this, _1, _2), usage_string); + } + }; + + +} + + diff --git a/contrib/epee/include/file_io_utils.h b/contrib/epee/include/file_io_utils.h new file mode 100644 index 000000000..7e8521838 --- /dev/null +++ b/contrib/epee/include/file_io_utils.h @@ -0,0 +1,455 @@ +// 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. +// + + +#ifndef _FILE_IO_UTILS_H_ +#define _FILE_IO_UTILS_H_ + + +//#include +//#include + +#include +#include + + +#ifndef MAKE64 + #define MAKE64(low,high) ((__int64)(((DWORD)(low)) | ((__int64)((DWORD)(high))) << 32)) +#endif + +#ifdef WINDOWS_PLATFORM +#include +#include +#include +#include + +#endif + + + +namespace epee +{ +namespace file_io_utils +{ +#ifdef WINDOWS_PLATFORM + + inline + std::string get_temp_file_name_a() + { + std::string str_result; + char sz_temp[MAX_PATH*2] = {0}; + if(!::GetTempPathA( sizeof( sz_temp ), sz_temp )) + return str_result; + + char sz_temp_file[MAX_PATH*2] = {0}; + if(!::GetTempFileNameA( sz_temp, "mail", 0, sz_temp_file)) + return str_result; + sz_temp_file[sizeof(sz_temp_file)-1] = 0; //be happy! + str_result = sz_temp_file; + return str_result; + } + + +#ifdef BOOST_LEXICAL_CAST_INCLUDED + inline + bool get_not_used_filename(const std::string& folder, OUT std::string& result_name) + { + DWORD folder_attr = ::GetFileAttributesA(folder.c_str()); + if(folder_attr == INVALID_FILE_ATTRIBUTES) + return false; + if(!(folder_attr&FILE_ATTRIBUTE_DIRECTORY)) + return false; + + + std::string base_name = folder + "\\tmp"; + std::string tmp_name; + bool name_found = false; + int current_index = 0; + tmp_name = base_name + boost::lexical_cast(current_index) + ".tmp"; + while(!name_found) + { + if(INVALID_FILE_ATTRIBUTES == ::GetFileAttributesA(tmp_name.c_str())) + name_found = true; + else + { + current_index++; + tmp_name = base_name + boost::lexical_cast(current_index) + ".tmp"; + } + } + result_name = tmp_name; + return true; + } +#endif + + inline + std::string get_temp_folder_a() + { + std::string str_result; + char sz_temp[MAX_PATH*2] = {0}; + if(!::GetTempPathA( sizeof( sz_temp ), sz_temp )) + return str_result; + sz_temp[(sizeof(sz_temp)/sizeof(sz_temp[0])) -1] = 0; + str_result = sz_temp; + return str_result; + } + + std::string convert_from_device_path_to_standart(const std::string& path) + { + + + STRSAFE_LPSTR pszFilename = (STRSAFE_LPSTR)path.c_str(); + + // Translate path with device name to drive letters. + char szTemp[4000] = {0}; + + if (::GetLogicalDriveStringsA(sizeof(szTemp)-1, szTemp)) + { + char szName[MAX_PATH]; + char szDrive[3] = " :"; + BOOL bFound = FALSE; + char* p = szTemp; + + do + { + // Copy the drive letter to the template string + *szDrive = *p; + + // Look up each device name + if (::QueryDosDeviceA(szDrive, szName, sizeof(szName))) + { + UINT uNameLen = strlen(szName); + + if (uNameLen < MAX_PATH) + { + bFound = _mbsnbicmp((const unsigned char*)pszFilename, (const unsigned char*)szName, + uNameLen) == 0; + + if (bFound) + { + // Reconstruct pszFilename using szTempFile + // Replace device path with DOS path + char szTempFile[MAX_PATH] = {0}; + StringCchPrintfA(szTempFile, + MAX_PATH, + "%s%s", + szDrive, + pszFilename+uNameLen); + return szTempFile; + //::StringCchCopyNA(pszFilename, MAX_PATH+1, szTempFile, strlen(szTempFile)); + } + } + } + + // Go to the next NULL character. + while (*p++); + } while (!bFound && *p); // end of string + } + + return ""; + } + + inline + std::string get_process_path_by_pid(DWORD pid) + { + std::string res; + + HANDLE hprocess = 0; + if( hprocess = ::OpenProcess( PROCESS_QUERY_INFORMATION|PROCESS_VM_READ, FALSE, pid) ) + { + char buff[MAX_PATH]= {0}; + if(!::GetModuleFileNameExA( hprocess, 0, buff, MAX_PATH - 1 )) + res = "Unknown_b"; + else + { + buff[MAX_PATH - 1]=0; //be happy! + res = buff; + std::string::size_type a = res.rfind( '\\' ); + if ( a != std::string::npos ) + res.erase( 0, a+1); + + } + ::CloseHandle( hprocess ); + }else + res = "Unknown_a"; + + return res; + } + + + + + + inline + std::wstring get_temp_file_name_w() + { + std::wstring str_result; + wchar_t sz_temp[MAX_PATH*2] = {0}; + if(!::GetTempPathW( sizeof(sz_temp)/sizeof(sz_temp[0]), sz_temp )) + return str_result; + + wchar_t sz_temp_file[MAX_PATH+1] = {0}; + if(!::GetTempFileNameW( sz_temp, L"mail", 0, sz_temp_file)) + return str_result; + + sz_temp_file[(sizeof(sz_temp_file)/sizeof(sz_temp_file[0]))-1] = 0; //be happy! + str_result = sz_temp_file; + return str_result; + } +#endif + inline + bool is_file_exist(const std::string& path) + { + boost::filesystem::path p(path); + return boost::filesystem::exists(p); + } + + /* + inline + bool save_string_to_handle(HANDLE hfile, const std::string& str) + { + + + + if( INVALID_HANDLE_VALUE != hfile ) + { + DWORD dw; + if( !::WriteFile( hfile, str.data(), (DWORD) str.size(), &dw, NULL) ) + { + int err_code = GetLastError(); + //LOG_PRINT("Failed to write to file handle: " << hfile<< " Last error code:" << err_code << " : " << log_space::get_win32_err_descr(err_code), LOG_LEVEL_2); + return false; + } + ::CloseHandle(hfile); + return true; + }else + { + //LOG_WIN32_ERROR(::GetLastError()); + return false; + } + + return false; + }*/ + + + + inline + bool save_string_to_file(const std::string& path_to_file, const std::string& str) + { + + try + { + std::ofstream fstream; + fstream.exceptions(std::ifstream::failbit | std::ifstream::badbit); + fstream.open(path_to_file, std::ios_base::binary | std::ios_base::out | std::ios_base::trunc); + fstream << str; + fstream.close(); + return true; + } + + catch(...) + { + return false; + } + } + + /* + inline + bool load_form_handle(HANDLE hfile, std::string& str) + { + if( INVALID_HANDLE_VALUE != hfile ) + { + bool res = true; + DWORD dw = 0; + DWORD fsize = ::GetFileSize(hfile, &dw); + if(fsize > 300000000) + { + ::CloseHandle(hfile); + return false; + } + if(fsize) + { + str.resize(fsize); + if(!::ReadFile( hfile, (LPVOID)str.data(), (DWORD)str.size(), &dw, NULL)) + res = false; + } + ::CloseHandle(hfile); + return res; + } + return false; + } + */ + inline + bool get_file_time(const std::string& path_to_file, OUT time_t& ft) + { + boost::system::error_code ec; + ft = boost::filesystem::last_write_time(boost::filesystem::path(path_to_file), ec); + if(!ec) + return true; + else + return false; + } + + inline + bool set_file_time(const std::string& path_to_file, const time_t& ft) + { + boost::system::error_code ec; + boost::filesystem::last_write_time(boost::filesystem::path(path_to_file), ft, ec); + if(!ec) + return true; + else + return false; + } + + + inline + bool load_file_to_string(const std::string& path_to_file, std::string& target_str) + { + try + { + std::ifstream fstream; + fstream.exceptions(std::ifstream::failbit | std::ifstream::badbit); + fstream.open(path_to_file, std::ios_base::binary | std::ios_base::in | std::ios::ate); + + std::ifstream::pos_type file_size = fstream.tellg(); + + if(file_size > 1000000000) + return false;//don't go crazy + size_t file_size_t = static_cast(file_size); + + target_str.resize(file_size_t); + + fstream.seekg (0, std::ios::beg); + fstream.read((char*)target_str.data(), target_str.size()); + fstream.close(); + return true; + } + + catch(...) + { + return false; + } + } + + inline + bool append_string_to_file(const std::string& path_to_file, const std::string& str) + { + try + { + std::ofstream fstream; + fstream.exceptions(std::ifstream::failbit | std::ifstream::badbit); + fstream.open(path_to_file.c_str(), std::ios_base::binary | std::ios_base::out | std::ios_base::app); + fstream << str; + fstream.close(); + return true; + } + + catch(...) + { + return false; + } + } + + /* + bool remove_dir_and_subirs(const char* path_to_dir); + + inline + bool clean_dir(const char* path_to_dir) + { + if(!path_to_dir) + return false; + + std::string folder = path_to_dir; + WIN32_FIND_DATAA find_data = {0}; + HANDLE hfind = ::FindFirstFileA((folder + "\\*.*").c_str(), &find_data); + if(INVALID_HANDLE_VALUE == hfind) + return false; + do{ + if(!strcmp("..", find_data.cFileName) || (!strcmp(".", find_data.cFileName))) + continue; + + if(find_data.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY) + { + if(!remove_dir_and_subirs((folder + "\\" + find_data.cFileName).c_str())) + return false; + }else + { + if(!::DeleteFileA((folder + "\\" + find_data.cFileName).c_str())) + return false; + } + + + }while(::FindNextFileA(hfind, &find_data)); + ::FindClose(hfind); + + return true; + } + */ +#ifdef WINDOWS_PLATFORM + inline bool get_folder_content(const std::string& path, std::list& OUT target_list) + { + WIN32_FIND_DATAA find_data = {0}; + HANDLE hfind = ::FindFirstFileA((path + "\\*.*").c_str(), &find_data); + if(INVALID_HANDLE_VALUE == hfind) + return false; + do{ + if(!strcmp("..", find_data.cFileName) || (!strcmp(".", find_data.cFileName))) + continue; + + target_list.push_back(find_data); + + }while(::FindNextFileA(hfind, &find_data)); + ::FindClose(hfind); + + return true; + } +#endif + inline bool get_folder_content(const std::string& path, std::list& OUT target_list, bool only_files = false) + { + try + { + + boost::filesystem::directory_iterator end_itr; // default construction yields past-the-end + for ( boost::filesystem::directory_iterator itr( path ); itr != end_itr; ++itr ) + { + if ( only_files && boost::filesystem::is_directory(itr->status()) ) + { + continue; + } + target_list.push_back(itr->path().filename().string()); + } + + } + + catch(...) + { + return false; + } + return true; + } +} +} + +#endif //_FILE_IO_UTILS_H_ diff --git a/contrib/epee/include/global_stream_operators.h b/contrib/epee/include/global_stream_operators.h new file mode 100644 index 000000000..6fbdbc2ed --- /dev/null +++ b/contrib/epee/include/global_stream_operators.h @@ -0,0 +1,35 @@ +// 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. +// + + +#pragma once + +std::stringstream& operator<<(std::stringstream& out, const std::wstring& ws) +{ + std::string as = string_encoding::convert_to_ansii(ws); + out << as; + return out; +} diff --git a/contrib/epee/include/gzip_encoding.h b/contrib/epee/include/gzip_encoding.h new file mode 100644 index 000000000..2be51e77d --- /dev/null +++ b/contrib/epee/include/gzip_encoding.h @@ -0,0 +1,227 @@ +// 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. +// + + + + +#ifndef _GZIP_ENCODING_H_ +#define _GZIP_ENCODING_H_ +#include "net/http_client_base.h" +#include "zlib/zlib.h" +//#include "http.h" + + +namespace epee +{ +namespace net_utils +{ + + + + class content_encoding_gzip: public i_sub_handler + { + public: + /*! \brief + * Function content_encoding_gzip : Constructor + * + */ + inline + content_encoding_gzip(i_target_handler* powner_filter, bool is_deflate_mode = false):m_powner_filter(powner_filter), + m_is_stream_ended(false), + m_is_deflate_mode(is_deflate_mode), + m_is_first_update_in(true) + { + memset(&m_zstream_in, 0, sizeof(m_zstream_in)); + memset(&m_zstream_out, 0, sizeof(m_zstream_out)); + int ret = 0; + if(is_deflate_mode) + { + ret = inflateInit(&m_zstream_in); + ret = deflateInit(&m_zstream_out, Z_DEFAULT_COMPRESSION); + }else + { + ret = inflateInit2(&m_zstream_in, 0x1F); + ret = deflateInit2(&m_zstream_out, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 0x1F, 8, Z_DEFAULT_STRATEGY); + } + } + /*! \brief + * Function content_encoding_gzip : Destructor + * + */ + inline + ~content_encoding_gzip() + { + inflateEnd(& m_zstream_in ); + deflateEnd(& m_zstream_out ); + } + /*! \brief + * Function update_in : Entry point for income data + * + */ + inline + virtual bool update_in( std::string& piece_of_transfer) + { + + bool is_first_time_here = m_is_first_update_in; + m_is_first_update_in = false; + + if(m_pre_decode.size()) + m_pre_decode += piece_of_transfer; + else + m_pre_decode.swap(piece_of_transfer); + piece_of_transfer.clear(); + + std::string decode_summary_buff; + + size_t ungzip_size = m_pre_decode.size() * 0x30; + std::string current_decode_buff(ungzip_size, 'X'); + + //Here the cycle is introduced where we unpack the buffer, the cycle is required + //because of the case where if after unpacking the data will exceed the awaited size, we will not halt with error + bool continue_unpacking = true; + bool first_step = true; + while(m_pre_decode.size() && continue_unpacking) + { + + //fill buffers + m_zstream_in.next_in = (Bytef*)m_pre_decode.data(); + m_zstream_in.avail_in = (uInt)m_pre_decode.size(); + m_zstream_in.next_out = (Bytef*)current_decode_buff.data(); + m_zstream_in.avail_out = (uInt)ungzip_size; + + int flag = Z_SYNC_FLUSH; + int ret = inflate(&m_zstream_in, flag); + CHECK_AND_ASSERT_MES(ret>=0 || m_zstream_in.avail_out ||m_is_deflate_mode, false, "content_encoding_gzip::update_in() Failed to inflate. err = " << ret); + + if(Z_STREAM_END == ret) + m_is_stream_ended = true; + else if(Z_DATA_ERROR == ret && is_first_time_here && m_is_deflate_mode&& first_step) + { + // some servers (notably Apache with mod_deflate) don't generate zlib headers + // insert a dummy header and try again + static char dummy_head[2] = + { + 0x8 + 0x7 * 0x10, + (((0x8 + 0x7 * 0x10) * 0x100 + 30) / 31 * 31) & 0xFF, + }; + inflateReset(&m_zstream_in); + m_zstream_in.next_in = (Bytef*) dummy_head; + m_zstream_in.avail_in = sizeof(dummy_head); + + ret = inflate(&m_zstream_in, Z_NO_FLUSH); + if (ret != Z_OK) + { + LOCAL_ASSERT(0); + m_pre_decode.swap(piece_of_transfer); + return false; + } + m_zstream_in.next_in = (Bytef*)m_pre_decode.data(); + m_zstream_in.avail_in = (uInt)m_pre_decode.size(); + + ret = inflate(&m_zstream_in, Z_NO_FLUSH); + if (ret != Z_OK) + { + LOCAL_ASSERT(0); + m_pre_decode.swap(piece_of_transfer); + return false; + } + } + + + //leave only unpacked part in the output buffer to start with it the next time + m_pre_decode.erase(0, m_pre_decode.size()-m_zstream_in.avail_in); + //if decoder gave nothing to return, then everything is ahead, now simply break + if(ungzip_size == m_zstream_in.avail_out) + break; + + //decode_buff currently stores data parts that were unpacked, fix this size + current_decode_buff.resize(ungzip_size - m_zstream_in.avail_out); + if(decode_summary_buff.size()) + decode_summary_buff += current_decode_buff; + else + current_decode_buff.swap(decode_summary_buff); + + current_decode_buff.resize(ungzip_size); + first_step = false; + } + + //Process these data if required + bool res = true; + + res = m_powner_filter->handle_target_data(decode_summary_buff); + + return true; + + } + /*! \brief + * Function stop : Entry point for stop signal and flushing cached data buffer. + * + */ + inline + virtual void stop(std::string& OUT collect_remains) + { + } + protected: + private: + /*! \brief + * Pointer to parent HTTP-parser + */ + i_target_handler* m_powner_filter; + /*! \brief + * ZLIB object for income stream + */ + z_stream m_zstream_in; + /*! \brief + * ZLIB object for outcome stream + */ + z_stream m_zstream_out; + /*! \brief + * Data that could not be unpacked immediately, left to wait for the next packet of data + */ + std::string m_pre_decode; + /*! \brief + * The data are accumulated for a package in the buffer to send the web client + */ + std::string m_pre_encode; + /*! \brief + * Signals that stream looks like ended + */ + bool m_is_stream_ended; + /*! \brief + * If this flag is set, income data is in HTTP-deflate mode + */ + bool m_is_deflate_mode; + /*! \brief + * Marks that it is a first data packet + */ + bool m_is_first_update_in; + }; +} +} + + + +#endif //_GZIP_ENCODING_H_ diff --git a/contrib/epee/include/hmac-md5.h b/contrib/epee/include/hmac-md5.h new file mode 100644 index 000000000..2a4e0d401 --- /dev/null +++ b/contrib/epee/include/hmac-md5.h @@ -0,0 +1,93 @@ +/* + * libEtPan! -- a mail stuff library + * + * Copyright (C) 2001, 2005 - DINH Viet Hoa + * 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 libEtPan! project 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 AUTHORS 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 AUTHORS 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. + */ + +/* hmac-md5.h -- HMAC_MD5 functions + */ + +/* + * $Id: hmac-md5.h,v 1.1.1.1 2005/03/18 20:17:28 zautrix Exp $ + */ + +#ifndef HMAC_MD5_H +#define HMAC_MD5_H 1 + +namespace md5 +{ + + + +#define HMAC_MD5_SIZE 16 + + /* intermediate MD5 context */ + typedef struct HMAC_MD5_CTX_s { + MD5_CTX ictx, octx; + } HMAC_MD5_CTX; + + /* intermediate HMAC state + * values stored in network byte order (Big Endian) + */ + typedef struct HMAC_MD5_STATE_s { + UINT4 istate[4]; + UINT4 ostate[4]; + } HMAC_MD5_STATE; + + /* One step hmac computation + * + * digest may be same as text or key + */ + void hmac_md5(const unsigned char *text, int text_len, + const unsigned char *key, int key_len, + unsigned char digest[HMAC_MD5_SIZE]); + + /* create context from key + */ + void hmac_md5_init(HMAC_MD5_CTX *hmac, + const unsigned char *key, int key_len); + + /* precalculate intermediate state from key + */ + void hmac_md5_precalc(HMAC_MD5_STATE *hmac, + const unsigned char *key, int key_len); + + /* initialize context from intermediate state + */ + void hmac_md5_import(HMAC_MD5_CTX *hmac, HMAC_MD5_STATE *state); + +#define hmac_md5_update(hmac, text, text_len) MD5Update(&(hmac)->ictx, (text), (text_len)) + + /* finish hmac from intermediate result. Intermediate result is zeroed. + */ + void hmac_md5_final(unsigned char digest[HMAC_MD5_SIZE], + HMAC_MD5_CTX *hmac); + +} + +#endif /* HMAC_MD5_H */ diff --git a/contrib/epee/include/include_base_utils.h b/contrib/epee/include/include_base_utils.h new file mode 100644 index 000000000..8412a0083 --- /dev/null +++ b/contrib/epee/include/include_base_utils.h @@ -0,0 +1,34 @@ +// 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. +// + +#pragma once + +#define BOOST_FILESYSTEM_VERSION 3 +#define ENABLE_RELEASE_LOGGING + +#include "misc_log_ex.h" + + diff --git a/contrib/epee/include/math_helper.h b/contrib/epee/include/math_helper.h new file mode 100644 index 000000000..44efd4682 --- /dev/null +++ b/contrib/epee/include/math_helper.h @@ -0,0 +1,272 @@ +// 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. +// + + + + +#pragma once + + +#include +#include +#include +#include +#include + +#include "misc_os_dependent.h" + +namespace epee +{ +namespace math_helper +{ + + template + class average + { + public: + + average() + { + m_base = default_base; + m_last_avg_val = 0; + } + + bool set_base() + { + CRITICAL_REGION_LOCAL(m_lock); + + m_base = default_base; + if(m_list.size() > m_base) + m_list.resize(m_base); + + return true; + } + + typedef val value_type; + + void push(const value_type& vl) + { + CRITICAL_REGION_LOCAL(m_lock); + +//#ifndef DEBUG_STUB + m_list.push_back(vl); + if(m_list.size() > m_base ) + m_list.pop_front(); +//#endif + } + + double update(const value_type& vl) + { + CRITICAL_REGION_LOCAL(m_lock); +//#ifndef DEBUG_STUB + push(vl); +//#endif + + return get_avg(); + } + + double get_avg() + { + CRITICAL_REGION_LOCAL(m_lock); + + value_type vl = std::accumulate(m_list.begin(), m_list.end(), value_type(0)); + if(m_list.size()) + return m_last_avg_val = (double)(vl/m_list.size()); + + return m_last_avg_val = (double)vl; + } + + value_type get_last_val() + { + CRITICAL_REGION_LOCAL(m_lock); + if(m_list.size()) + return m_list.back(); + + return 0; + } + + private: + unsigned int m_base; + double m_last_avg_val; + std::list m_list; + critical_section m_lock; + }; + + +#ifdef WINDOWS_PLATFORM + + /************************************************************************/ + /* */ + /************************************************************************/ + class timing_guard_base + { + public: + virtual ~timing_guard_base(){}; + }; + + template + class timing_guard: public timing_guard_base + { + public: + timing_guard(T& avrg):m_avrg(avrg) + { + m_start_ticks = ::GetTickCount(); + } + + ~timing_guard() + { + m_avrg.push(::GetTickCount()-m_start_ticks); + } + + private: + T& m_avrg; + DWORD m_start_ticks; + }; + + template + timing_guard_base* create_timing_guard(t_timing& timing){return new timing_guard(timing);} + +#define BEGIN_TIMING_ZONE(timing_var) { boost::shared_ptr local_timing_guard_ptr(math_helper::create_timing_guard(timing_var)); +#define END_TIMING_ZONE() } +#endif + +//#ifdef WINDOWS_PLATFORM_EX + template + class speed + { + public: + + speed() + { + m_time_window = default_time_window; + m_last_speed_value = 0; + } + bool chick() + { +#ifndef DEBUG_STUB + boost::uint64_t ticks = misc_utils::get_tick_count(); + CRITICAL_REGION_BEGIN(m_lock); + m_chicks.push_back(ticks); + CRITICAL_REGION_END(); + //flush(ticks); +#endif + return true; + } + + bool chick(size_t count) + { + for(size_t s = 0; s != count; s++) + chick(); + + return true; + } + + + size_t get_speed() + { + flush(misc_utils::get_tick_count()); + return m_last_speed_value = m_chicks.size(); + } + private: + + bool flush(boost::uint64_t ticks) + { + CRITICAL_REGION_BEGIN(m_lock); + std::list::iterator it = m_chicks.begin(); + while(it != m_chicks.end()) + { + if(*it + m_time_window < ticks) + m_chicks.erase(it++); + else + break; + } + CRITICAL_REGION_END(); + return true; + } + + std::list m_chicks; + boost::uint64_t m_time_window; + size_t m_last_speed_value; + critical_section m_lock; + }; +//#endif + + template + void randomize_list(tlist& t_list) + { + for(typename tlist::iterator it = t_list.begin();it!=t_list.end();it++) + { + size_t offset = rand()%t_list.size(); + typename tlist::iterator it_2 = t_list.begin(); + for(size_t local_offset = 0;local_offset!=offset;local_offset++) + it_2++; + if(it_2 == it) + continue; + std::swap(*it_2, *it); + } + + } +PRAGMA_WARNING_PUSH +PRAGMA_GCC("GCC diagnostic ignored \"-Wstrict-aliasing\"") + inline + uint64_t generated_random_uint64() + { + boost::uuids::uuid id___ = boost::uuids::random_generator()(); + return *reinterpret_cast(&id___.data[0]); //(*reinterpret_cast(&id___.data[0]) ^ *reinterpret_cast(&id___.data[8])); + } +PRAGMA_WARNING_POP + template + class once_a_time_seconds + { + public: + once_a_time_seconds():m_interval(default_interval) + { + m_last_worked_time = 0; + if(!start_immediate) + time(&m_last_worked_time); + } + + template + bool do_call(functor_t functr) + { + time_t current_time = 0; + time(¤t_time); + + if(current_time - m_last_worked_time > m_interval) + { + bool res = functr(); + time(&m_last_worked_time); + return res; + } + return true; + } + + private: + time_t m_last_worked_time; + time_t m_interval; + }; +} +} \ No newline at end of file diff --git a/contrib/epee/include/md5_l.h b/contrib/epee/include/md5_l.h new file mode 100644 index 000000000..fe4c67db6 --- /dev/null +++ b/contrib/epee/include/md5_l.h @@ -0,0 +1,97 @@ +/* + * libEtPan! -- a mail stuff library + * + * Copyright (C) 2001, 2005 - DINH Viet Hoa + * 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 libEtPan! project 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 AUTHORS 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 AUTHORS 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. + */ + +/* + * $Id: md5.h,v 1.1.1.1 2005/03/18 20:17:27 zautrix Exp $ + */ + +/* MD5.H - header file for MD5C.C + */ + +/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All +rights reserved. + +License to copy and use this software is granted provided that it +is identified as the "RSA Data Security, Inc. MD5 Message-Digest +Algorithm" in all material mentioning or referencing this software +or this function. + +License is also granted to make and use derivative works provided +that such works are identified as "derived from the RSA Data +Security, Inc. MD5 Message-Digest Algorithm" in all material +mentioning or referencing the derived work. + +RSA Data Security, Inc. makes no representations concerning either +the merchantability of this software or the suitability of this +software for any particular purpose. It is provided "as is" +without express or implied warranty of any kind. +These notices must be retained in any copies of any part of this +documentation and/or software. + */ +#ifndef MD5_H +#define MD5_H + + +#include "md5global.h" + +namespace md5 +{ + /* MD5 context. */ + typedef struct { + UINT4 state[4]; /* state (ABCD) */ + UINT4 count[2]; /* number of bits, modulo 2^64 (lsb first) */ + unsigned char buffer[64]; /* input buffer */ + } MD5_CTX; + + static void MD5Init(MD5_CTX * context); + static void MD5Update( MD5_CTX *context, const unsigned char *input, unsigned int inputLen ); + static void MD5Final ( unsigned char digest[16], MD5_CTX *context ); + static void hmac_md5(const unsigned char* text, int text_len, const unsigned char* key, int key_len, unsigned char *digest); + + + inline bool md5( unsigned char *input, int ilen, unsigned char output[16] ) + { + MD5_CTX ctx; + + MD5Init( &ctx ); + MD5Update( &ctx, input, ilen ); + MD5Final( output, &ctx); + + memset( &ctx, 0, sizeof( MD5_CTX) ); + return true; + } + + +} + +#include "md5_l.inl" + +#endif diff --git a/contrib/epee/include/md5_l.inl b/contrib/epee/include/md5_l.inl new file mode 100644 index 000000000..c3da1a3b0 --- /dev/null +++ b/contrib/epee/include/md5_l.inl @@ -0,0 +1,563 @@ +/* +* libEtPan! -- a mail stuff library +* +* Copyright (C) 2001, 2005 - DINH Viet Hoa +* 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 libEtPan! project 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 AUTHORS 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 AUTHORS 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. +*/ + +/* +* $Id: md5.c,v 1.1.1.1 2005/03/18 20:17:27 zautrix Exp $ +*/ + +/* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm +*/ + +/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All +rights reserved. + +License to copy and use this software is granted provided that it +is identified as the "RSA Data Security, Inc. MD5 Message-Digest +Algorithm" in all material mentioning or referencing this software +or this function. + +License is also granted to make and use derivative works provided +that such works are identified as "derived from the RSA Data +Security, Inc. MD5 Message-Digest Algorithm" in all material +mentioning or referencing the derived work. + +RSA Data Security, Inc. makes no representations concerning either +the merchantability of this software or the suitability of this +software for any particular purpose. It is provided "as is" +without express or implied warranty of any kind. + +These notices must be retained in any copies of any part of this +documentation and/or software. +*/ + +/* do i need all of this just for htonl()? damn. */ +//#include +//#include +//#include +//#include + + + +#include "md5global.h" +#include "md5_l.h" +#include "hmac-md5.h" + +namespace md5 +{ + /* Constants for MD5Transform routine. + */ + +#define S11 7 +#define S12 12 +#define S13 17 +#define S14 22 +#define S21 5 +#define S22 9 +#define S23 14 +#define S24 20 +#define S31 4 +#define S32 11 +#define S33 16 +#define S34 23 +#define S41 6 +#define S42 10 +#define S43 15 +#define S44 21 + + /* + static void MD5Transform PROTO_LIST ((UINT4 [4], unsigned char [64])); + static void Encode PROTO_LIST + ((unsigned char *, UINT4 *, unsigned int)); + static void Decode PROTO_LIST + ((UINT4 *, unsigned char *, unsigned int)); + static void MD5_memcpy PROTO_LIST ((POINTER, POINTER, unsigned int)); + static void MD5_memset PROTO_LIST ((POINTER, int, unsigned int)); + */ + + static void MD5_memcpy (POINTER output, POINTER input, unsigned int len) + { + unsigned int i; + + for (i = 0; i < len; i++) + output[i] = input[i]; + } + + /* Note: Replace "for loop" with standard memset if possible. + */ + + static void MD5_memset (POINTER output, int value, unsigned int len) + { + unsigned int i; + + for (i = 0; i < len; i++) + ((char *)output)[i] = (char)value; + } + + static void MD5Transform (UINT4 state[4], unsigned char block[64]); + + static unsigned char* PADDING() + { + static unsigned char local_PADDING[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + + return local_PADDING; + + } + + + + /* F, G, H and I are basic MD5 functions. + + */ +#ifdef I + /* This might be defined via NANA */ +#undef I +#endif + +#define MD5_M_F(x, y, z) (((x) & (y)) | ((~x) & (z))) +#define MD5_M_G(x, y, z) (((x) & (z)) | ((y) & (~z))) +#define MD5_M_H(x, y, z) ((x) ^ (y) ^ (z)) +#define MD5_M_I(x, y, z) ((y) ^ ((x) | (~z))) + + /* ROTATE_LEFT rotates x left n bits. + + */ + +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) + + /* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. + Rotation is separate from addition to prevent recomputation. + */ + +#define FF(a, b, c, d, x, s, ac) { (a) += MD5_M_F ((b), (c), (d)) + (x) + (UINT4)(ac); (a) = ROTATE_LEFT ((a), (s)); (a) += (b); } +#define GG(a, b, c, d, x, s, ac) { (a) += MD5_M_G ((b), (c), (d)) + (x) + (UINT4)(ac); (a) = ROTATE_LEFT ((a), (s)); (a) += (b); } +#define HH(a, b, c, d, x, s, ac) { (a) += MD5_M_H ((b), (c), (d)) + (x) + (UINT4)(ac); (a) = ROTATE_LEFT ((a), (s)); (a) += (b); } +#define II(a, b, c, d, x, s, ac) { (a) += MD5_M_I ((b), (c), (d)) + (x) + (UINT4)(ac); (a) = ROTATE_LEFT ((a), (s)); (a) += (b); } + + /* MD5 initialization. Begins an MD5 operation, writing a new context. + */ + + static void MD5Init(MD5_CTX * context) + { + context->count[0] = context->count[1] = 0; + + /* Load magic initialization constants. + + */ + context->state[0] = 0x67452301; + context->state[1] = 0xefcdab89; + context->state[2] = 0x98badcfe; + context->state[3] = 0x10325476; + } + + /* MD5 block update operation. Continues an MD5 message-digest + operation, processing another message block, and updating the context. + */ + + static void MD5Update( MD5_CTX *context, const unsigned char *input, unsigned int inputLen ) + { + unsigned int i, index, partLen; + + /* Compute number of bytes mod 64 */ + index = (unsigned int)((context->count[0] >> 3) & 0x3F); + + /* Update number of bits */ + if ((context->count[0] += ((UINT4)inputLen << 3)) + < ((UINT4)inputLen << 3)) + context->count[1]++; + context->count[1] += ((UINT4)inputLen >> 29); + + partLen = 64 - index; + + /* Transform as many times as possible. + + */ + if (inputLen >= partLen) + { + MD5_memcpy( (POINTER)&context->buffer[index], (POINTER)input, partLen ); + MD5Transform( context->state, context->buffer ); + + for (i = partLen; i + 63 < inputLen; i += 64) + MD5Transform (context->state, (unsigned char*)&input[i]); + + index = 0; + } + else + i = 0; + + /* Buffer remaining input */ + MD5_memcpy( (POINTER)&context->buffer[index], (POINTER)&input[i], inputLen-i ); + + } + + /* Encodes input (UINT4) into output (unsigned char). Assumes len is + a multiple of 4. + + */ + + static void Encode (unsigned char *output, UINT4 *input, unsigned int len) + { + unsigned int i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) { + output[j] = (unsigned char)(input[i] & 0xff); + output[j+1] = (unsigned char)((input[i] >> 8) & 0xff); + output[j+2] = (unsigned char)((input[i] >> 16) & 0xff); + output[j+3] = (unsigned char)((input[i] >> 24) & 0xff); + } + } + + /* Decodes input (unsigned char) into output (UINT4). Assumes len is + a multiple of 4. + + */ + + static void Decode (UINT4 *output, unsigned char *input, unsigned int len) + { + unsigned int i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) + output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) | (((UINT4)input[j+2]) << 16) + | (((UINT4)input[j+3]) << 24); + } + + /* MD5 finalization. Ends an MD5 message-digest operation, writing the + the message digest and zeroizing the context. + + */ + + static void MD5Final ( unsigned char digest[16], MD5_CTX *context ) + { + unsigned char bits[8]; + unsigned int index, padLen; + + /* Save number of bits */ + Encode (bits, context->count, 8); + + /* Pad out to 56 mod 64. + + */ + index = (unsigned int)((context->count[0] >> 3) & 0x3f); + padLen = (index < 56) ? (56 - index) : (120 - index); + MD5Update (context, PADDING(), padLen); + + /* Append length (before padding) */ + MD5Update (context, bits, 8); + + /* Store state in digest */ + Encode (digest, context->state, 16); + + /* Zeroize sensitive information. + + */ + MD5_memset ((POINTER)context, 0, sizeof (*context)); + } + + /* MD5 basic transformation. Transforms state based on block. + + */ + + static void MD5Transform (UINT4 state[4], unsigned char block[64]) + { + UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16]; + + Decode (x, block, 64); + + /* Round 1 */ + FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */ + FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */ + FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */ + FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */ + FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */ + FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */ + FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */ + FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */ + FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */ + FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */ + FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ + FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ + FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ + FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ + FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ + FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ + + /* Round 2 */ + GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */ + GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */ + GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ + GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */ + GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */ + GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */ + GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ + GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */ + GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */ + GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ + GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */ + GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */ + GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ + GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */ + GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */ + GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ + + /* Round 3 */ + HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */ + HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */ + HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ + HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ + HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */ + HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */ + HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */ + HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ + HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ + HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */ + HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */ + HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */ + HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */ + HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ + HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ + HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */ + + /* Round 4 */ + II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */ + II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */ + II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ + II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */ + II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ + II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */ + II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ + II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */ + II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */ + II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ + II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */ + II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ + II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */ + II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ + II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */ + II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */ + + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + + /* Zeroize sensitive information. + */ + MD5_memset ((POINTER)x, 0, sizeof (x)); + } + + /* Note: Replace "for loop" with standard memcpy if possible. + + */ + inline + void hmac_md5_init(HMAC_MD5_CTX *hmac, + const unsigned char *key, + int key_len) + { + unsigned char k_ipad[65]; /* inner padding - + * key XORd with ipad + */ + unsigned char k_opad[65]; /* outer padding - + * key XORd with opad + */ + unsigned char tk[16]; + int i; + /* if key is longer than 64 bytes reset it to key=MD5(key) */ + if (key_len > 64) { + + MD5_CTX tctx; + + MD5Init(&tctx); + MD5Update(&tctx, key, key_len); + MD5Final(tk, &tctx); + + key = tk; + key_len = 16; + } + + /* + * the HMAC_MD5 transform looks like: + * + * MD5(K XOR opad, MD5(K XOR ipad, text)) + * + * where K is an n byte key + * ipad is the byte 0x36 repeated 64 times + * opad is the byte 0x5c repeated 64 times + * and text is the data being protected + */ + + /* start out by storing key in pads */ + MD5_memset(k_ipad, '\0', sizeof k_ipad); + MD5_memset(k_opad, '\0', sizeof k_opad); + MD5_memcpy( k_ipad, (POINTER)key, key_len); + MD5_memcpy( k_opad, (POINTER)key, key_len); + + /* XOR key with ipad and opad values */ + for (i=0; i<64; i++) { + k_ipad[i] ^= 0x36; + k_opad[i] ^= 0x5c; + } + + MD5Init(&hmac->ictx); /* init inner context */ + MD5Update(&hmac->ictx, k_ipad, 64); /* apply inner pad */ + + MD5Init(&hmac->octx); /* init outer context */ + MD5Update(&hmac->octx, k_opad, 64); /* apply outer pad */ + + /* scrub the pads and key context (if used) */ + MD5_memset( (POINTER)&k_ipad, 0, sizeof(k_ipad)); + MD5_memset( (POINTER)&k_opad, 0, sizeof(k_opad)); + MD5_memset( (POINTER)&tk, 0, sizeof(tk)); + + /* and we're done. */ + } + + /* The precalc and import routines here rely on the fact that we pad + * the key out to 64 bytes and use that to initialize the md5 + * contexts, and that updating an md5 context with 64 bytes of data + * leaves nothing left over; all of the interesting state is contained + * in the state field, and none of it is left over in the count and + * buffer fields. So all we have to do is save the state field; we + * can zero the others when we reload it. Which is why the decision + * was made to pad the key out to 64 bytes in the first place. */ + inline + void hmac_md5_precalc(HMAC_MD5_STATE *state, + const unsigned char *key, + int key_len) + { + HMAC_MD5_CTX hmac; + unsigned lupe; + + hmac_md5_init(&hmac, key, key_len); + for (lupe = 0; lupe < 4; lupe++) { + state->istate[lupe] = htonl(hmac.ictx.state[lupe]); + state->ostate[lupe] = htonl(hmac.octx.state[lupe]); + } + MD5_memset( (POINTER)&hmac, 0, sizeof(hmac)); + } + + + inline + void hmac_md5_import(HMAC_MD5_CTX *hmac, + HMAC_MD5_STATE *state) + { + unsigned lupe; + MD5_memset( (POINTER)hmac, 0, sizeof(HMAC_MD5_CTX)); + for (lupe = 0; lupe < 4; lupe++) { + hmac->ictx.state[lupe] = ntohl(state->istate[lupe]); + hmac->octx.state[lupe] = ntohl(state->ostate[lupe]); + } + /* Init the counts to account for our having applied + * 64 bytes of key; this works out to 0x200 (64 << 3; see + * MD5Update above...) */ + hmac->ictx.count[0] = hmac->octx.count[0] = 0x200; + } + + inline + void hmac_md5_final(unsigned char digest[HMAC_MD5_SIZE], + HMAC_MD5_CTX *hmac) + { + MD5Final(digest, &hmac->ictx); /* Finalize inner md5 */ + MD5Update(&hmac->octx, digest, 16); /* Update outer ctx */ + MD5Final(digest, &hmac->octx); /* Finalize outer md5 */ + } + + + void hmac_md5(const unsigned char* text, int text_len, const unsigned char* key, int key_len, unsigned char *digest) + { + MD5_CTX context; + + unsigned char k_ipad[65]; /* inner padding - + * key XORd with ipad + */ + unsigned char k_opad[65]; /* outer padding - + * key XORd with opad + */ + unsigned char tk[16]; + int i; + /* if key is longer than 64 bytes reset it to key=MD5(key) */ + if (key_len > 64) { + + MD5_CTX tctx; + + MD5Init(&tctx); + MD5Update(&tctx, key, key_len); + MD5Final(tk, &tctx); + + key = tk; + key_len = 16; + } + + /* + * the HMAC_MD5 transform looks like: + * + * MD5(K XOR opad, MD5(K XOR ipad, text)) + * + * where K is an n byte key + * ipad is the byte 0x36 repeated 64 times + * opad is the byte 0x5c repeated 64 times + * and text is the data being protected + */ + + /* start out by storing key in pads */ + MD5_memset(k_ipad, '\0', sizeof k_ipad); + MD5_memset(k_opad, '\0', sizeof k_opad); + MD5_memcpy( k_ipad, (POINTER)key, key_len); + MD5_memcpy( k_opad, (POINTER)key, key_len); + + /* XOR key with ipad and opad values */ + for (i=0; i<64; i++) { + k_ipad[i] ^= 0x36; + k_opad[i] ^= 0x5c; + } + /* + * perform inner MD5 + */ + + MD5Init(&context); /* init context for 1st + * pass */ + MD5Update(&context, k_ipad, 64); /* start with inner pad */ + MD5Update(&context, text, text_len); /* then text of datagram */ + MD5Final(digest, &context); /* finish up 1st pass */ + + /* + * perform outer MD5 + */ + MD5Init(&context); /* init context for 2nd + * pass */ + MD5Update(&context, k_opad, 64); /* start with outer pad */ + MD5Update(&context, digest, 16); /* then results of 1st + * hash */ + MD5Final(digest, &context); /* finish up 2nd pass */ + + } +} \ No newline at end of file diff --git a/contrib/epee/include/md5global.h b/contrib/epee/include/md5global.h new file mode 100644 index 000000000..afc229019 --- /dev/null +++ b/contrib/epee/include/md5global.h @@ -0,0 +1,77 @@ +/* + * libEtPan! -- a mail stuff library + * + * Copyright (C) 2001, 2005 - DINH Viet Hoa + * 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 libEtPan! project 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 AUTHORS 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 AUTHORS 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. + */ + +/* + * $Id: md5global.h,v 1.1.1.1 2005/03/18 20:17:28 zautrix Exp $ + */ + +/* GLOBAL.H - RSAREF types and constants + */ + +#ifndef MD5GLOBAL_H +#define MD5GLOBAL_H + +namespace md5 +{ + + + /* PROTOTYPES should be set to one if and only if the compiler supports + function argument prototyping. + The following makes PROTOTYPES default to 0 if it has not already + been defined with C compiler flags. + */ +#ifndef PROTOTYPES +#define PROTOTYPES 0 +#endif + + /* POINTER defines a generic pointer type */ + typedef unsigned char *POINTER; + + /* UINT2 defines a two byte word */ + typedef unsigned short int UINT2; + + /* UINT4 defines a four byte word */ + //typedef unsigned long int UINT4; + typedef unsigned int UINT4; + + /* PROTO_LIST is defined depending on how PROTOTYPES is defined above. + If using PROTOTYPES, then PROTO_LIST returns the list, otherwise it + returns an empty list. + */ +#if PROTOTYPES +#define PROTO_LIST(list) list +#else +#define PROTO_LIST(list) () +#endif + +} + +#endif diff --git a/contrib/epee/include/misc_language.h b/contrib/epee/include/misc_language.h new file mode 100644 index 000000000..d5157365c --- /dev/null +++ b/contrib/epee/include/misc_language.h @@ -0,0 +1,162 @@ +// 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. +// + + + +#pragma once + +#include +#include +#include +namespace epee +{ +#define STD_TRY_BEGIN() try { + +#define STD_TRY_CATCH(where_, ret_val) \ + } \ + catch (const std::exception &e) \ + { \ + LOG_ERROR("EXCEPTION: " << where_ << ", mes: "<< e.what()); \ + return ret_val; \ + } \ + catch (...) \ + { \ + LOG_ERROR("EXCEPTION: " << where_ ); \ + return ret_val; \ + } + + + +#define AUTO_VAL_INIT(v) boost::value_initialized() + +namespace misc_utils +{ + template + t_type get_max_t_val(t_type t) + { + return (std::numeric_limits::max)(); + } + + + template + t_iterator move_it_forward(t_iterator it, size_t count) + { + while(count--) + it++; + return it; + } + + template + t_iterator move_it_backward(t_iterator it, size_t count) + { + while(count--) + it--; + return it; + } + + + // TEMPLATE STRUCT less + template + struct less_as_pod + : public std::binary_function<_Ty, _Ty, bool> + { // functor for operator< + bool operator()(const _Ty& _Left, const _Ty& _Right) const + { // apply operator< to operands + return memcmp(&_Left, &_Right, sizeof(_Left)) < 0; + } + }; + + template + bool is_less_as_pod(const _Ty& _Left, const _Ty& _Right) + { // apply operator< to operands + return memcmp(&_Left, &_Right, sizeof(_Left)) < 0; + } + + + inline + bool sleep_no_w(long ms ) + { + boost::this_thread::sleep( + boost::get_system_time() + + boost::posix_time::milliseconds( std::max(ms,0) ) ); + + return true; + } + + template + type_vec_type median(std::vector &v) + { + if(v.empty()) + return boost::value_initialized(); + if(v.size() == 1) + return v[0]; + + size_t n = (v.size()) / 2; + std::sort(v.begin(), v.end()); + //nth_element(v.begin(), v.begin()+n-1, v.end()); + if(v.size()%2) + {//1, 3, 5... + return v[n]; + }else + {//2, 4, 6... + return (v[n-1] + v[n])/2; + } + + } + + /************************************************************************/ + /* */ + /************************************************************************/ + + struct call_befor_die_base + { + virtual ~call_befor_die_base(){} + }; + + typedef boost::shared_ptr auto_scope_leave_caller; + + + template + struct call_befor_die: public call_befor_die_base + { + t_scope_leave_handler m_func; + call_befor_die(t_scope_leave_handler f):m_func(f) + {} + ~call_befor_die() + { + m_func(); + } + }; + + template + auto_scope_leave_caller create_scope_leave_handler(t_scope_leave_handler f) + { + auto_scope_leave_caller slc(new call_befor_die(f)); + return slc; + } + +} +} diff --git a/contrib/epee/include/misc_log_ex.h b/contrib/epee/include/misc_log_ex.h new file mode 100644 index 000000000..446d4bd3b --- /dev/null +++ b/contrib/epee/include/misc_log_ex.h @@ -0,0 +1,1426 @@ +// 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. +// + + +#ifndef _MISC_LOG_EX_H_ +#define _MISC_LOG_EX_H_ + +//#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "static_initializer.h" +#include "string_tools.h" +#include "time_helper.h" +#include "misc_os_dependent.h" + + +#include "syncobj.h" + + + + +#define LOG_LEVEL_SILENT -1 +#define LOG_LEVEL_0 0 +#define LOG_LEVEL_1 1 +#define LOG_LEVEL_2 2 +#define LOG_LEVEL_3 3 +#define LOG_LEVEL_4 4 +#define LOG_LEVEL_MIN LOG_LEVEL_SILENT +#define LOG_LEVEL_MAX LOG_LEVEL_4 + + + + +#define LOGGER_NULL 0 +#define LOGGER_FILE 1 +#define LOGGER_DEBUGGER 2 +#define LOGGER_CONSOLE 3 +#define LOGGER_DUMP 4 + + +#ifndef LOCAL_ASSERT +#include +#if (defined _MSC_VER) +#define LOCAL_ASSERT(expr) {if(epee::debug::get_set_enable_assert()){_ASSERTE(expr);}} +#else +#define LOCAL_ASSERT(expr) +#endif + +#endif + +namespace epee +{ +namespace debug +{ + inline bool get_set_enable_assert(bool set = false, bool v = false) + { + static bool e = true; + if(set) + e = v; + return e; + } +} +namespace log_space +{ + class logger; + class log_message; + class log_singletone; + + /************************************************************************/ + /* */ + /************************************************************************/ + enum console_colors + { + console_color_default, + console_color_white, + console_color_red, + console_color_green, + console_color_blue, + console_color_cyan, + console_color_magenta, + console_color_yellow + }; + + + struct ibase_log_stream + { + ibase_log_stream(){} + virtual ~ibase_log_stream(){} + virtual bool out_buffer( const char* buffer, int buffer_len , int log_level, int color, const char* plog_name = NULL)=0; + virtual int get_type(){return 0;} + + virtual bool set_max_logfile_size(boost::uint64_t max_size){return true;}; + virtual bool set_log_rotate_cmd(const std::string& cmd){return true;}; + }; + + /************************************************************************/ + /* */ + /************************************************************************/ + /*struct ibase_log_value + { + public: + virtual void debug_out( std::stringstream* p_stream)const = 0; + };*/ + + /************************************************************************/ + /* */ + /************************************************************************/ + /*class log_message: public std::stringstream + { + public: + log_message(const log_message& lm): std::stringstream(), std::stringstream::basic_ios() + {} + log_message(){} + + template + log_message& operator<< (T t) + { + std::stringstream* pstrstr = this; + (*pstrstr) << t; + + return *this; + } + }; + inline + log_space::log_message& operator<<(log_space::log_message& sstream, const ibase_log_value& log_val) + { + log_val.debug_out(&sstream); + return sstream; + } + */ + /************************************************************************/ + /* */ + /************************************************************************/ + struct delete_ptr + { + template + void operator() (P p) + { + delete p.first; + } + }; + + /************************************************************************/ + /* */ + /************************************************************************/ + //------------------------------------------------------------------------ +#define max_dbg_str_len 80 +#ifdef _MSC_VER + class debug_output_stream: public ibase_log_stream + { + virtual bool out_buffer( const char* buffer, int buffer_len , int log_level, int color, const char* plog_name = NULL) + { + for ( int i = 0; i < buffer_len; i = i + max_dbg_str_len ) + { + std::string s( buffer + i, buffer_len- i < max_dbg_str_len ? + buffer_len - i : max_dbg_str_len ); + + ::OutputDebugStringA( s.c_str() ); + } + return true; + } + + }; +#endif + + inline void set_console_color(int color, bool bright) + { + switch(color) + { + case console_color_default: + { +#ifdef WIN32 + HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); + SetConsoleTextAttribute(h_stdout, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE| (bright ? FOREGROUND_INTENSITY:0)); +#else + if(bright) + std::cout << "\033[1;37m"; + else + std::cout << "\033[0m"; +#endif + } + break; + case console_color_white: + { +#ifdef WIN32 + HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); + SetConsoleTextAttribute(h_stdout, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | (bright ? FOREGROUND_INTENSITY:0)); +#else + if(bright) + std::cout << "\033[1;37m"; + else + std::cout << "\033[0;37m"; +#endif + } + break; + case console_color_red: + { +#ifdef WIN32 + HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); + SetConsoleTextAttribute(h_stdout, FOREGROUND_RED | (bright ? FOREGROUND_INTENSITY:0)); +#else + if(bright) + std::cout << "\033[1;31m"; + else + std::cout << "\033[0;31m"; +#endif + } + break; + case console_color_green: + { +#ifdef WIN32 + HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); + SetConsoleTextAttribute(h_stdout, FOREGROUND_GREEN | (bright ? FOREGROUND_INTENSITY:0)); +#else + if(bright) + std::cout << "\033[1;32m"; + else + std::cout << "\033[0;32m"; +#endif + } + break; + + case console_color_blue: + { +#ifdef WIN32 + HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); + SetConsoleTextAttribute(h_stdout, FOREGROUND_BLUE | FOREGROUND_INTENSITY);//(bright ? FOREGROUND_INTENSITY:0)); +#else + if(bright) + std::cout << "\033[1;34m"; + else + std::cout << "\033[0;34m"; +#endif + } + break; + + case console_color_cyan: + { +#ifdef WIN32 + HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); + SetConsoleTextAttribute(h_stdout, FOREGROUND_GREEN | FOREGROUND_BLUE | (bright ? FOREGROUND_INTENSITY:0)); +#else + if(bright) + std::cout << "\033[1;36m"; + else + std::cout << "\033[0;36m"; +#endif + } + break; + + case console_color_magenta: + { +#ifdef WIN32 + HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); + SetConsoleTextAttribute(h_stdout, FOREGROUND_BLUE | FOREGROUND_RED | (bright ? FOREGROUND_INTENSITY:0)); +#else + if(bright) + std::cout << "\033[1;35m"; + else + std::cout << "\033[0;35m"; +#endif + } + break; + + case console_color_yellow: + { +#ifdef WIN32 + HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); + SetConsoleTextAttribute(h_stdout, FOREGROUND_RED | FOREGROUND_GREEN | (bright ? FOREGROUND_INTENSITY:0)); +#else + if(bright) + std::cout << "\033[1;33m"; + else + std::cout << "\033[0;33m"; +#endif + } + break; + + } + } + + inline void reset_console_color() { +#ifdef WIN32 + HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); + SetConsoleTextAttribute(h_stdout, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); +#else + std::cout << "\033[0m"; +#endif + } + + class console_output_stream: public ibase_log_stream + { +#ifdef _MSC_VER + bool m_have_to_kill_console; +#endif + + public: + console_output_stream() + { +#ifdef _MSC_VER + + if(!::GetStdHandle(STD_OUTPUT_HANDLE)) + m_have_to_kill_console = true; + else + m_have_to_kill_console = false; + + ::AllocConsole(); +#endif + } + + ~console_output_stream() + { +#ifdef _MSC_VER + if(m_have_to_kill_console) + ::FreeConsole(); +#endif + } + int get_type(){return LOGGER_CONSOLE;} + + + + virtual bool out_buffer( const char* buffer, int buffer_len , int log_level, int color, const char* plog_name = NULL) + { + if(plog_name) + return true; //skip alternative logs from console + + set_console_color(color, log_level < 1); + +#ifdef _MSC_VER + const char* ptarget_buf = NULL; + char* pallocated_buf = NULL; + + // + int i = 0; + for(; i < buffer_len; i++) + if(buffer[i] == '\a') break; + if(i == buffer_len) + ptarget_buf = buffer; + else + { + pallocated_buf = new char[buffer_len]; + ptarget_buf = pallocated_buf; + for(i = 0; i < buffer_len; i++) + { + if(buffer[i] == '\a') + pallocated_buf[i] = '^'; + else + pallocated_buf[i] = buffer[i]; + } + } + + //boost::uint32_t b = 0; + //::WriteConsoleA(::GetStdHandle(STD_OUTPUT_HANDLE), ptarget_buf, buffer_len, (DWORD*)&b, 0); + std::cout << ptarget_buf; + if(pallocated_buf) delete [] pallocated_buf; +#else + std::string buf(buffer, buffer_len); + for(size_t i = 0; i!= buf.size(); i++) + { + if(buf[i] == 7 || buf[i] == -107) + buf[i] = '^'; + } + + std::cout << buf; +#endif + reset_console_color(); + return true; + } + + + }; + + inline bool rotate_log_file(const char* pfile_path) + { +#ifdef _MSC_VER + if(!pfile_path) + return false; + + std::string file_path = pfile_path; + std::string::size_type a = file_path .rfind('.'); + if ( a != std::string::npos ) + file_path .erase( a, file_path .size()); + + ::DeleteFileA( (file_path + ".0").c_str() ); + ::MoveFileA( (file_path + ".log").c_str(), (file_path + ".0").c_str() ); +#else + return false;//not implemented yet +#endif + return true; + } + + + + + //--------------------------------------------------------------------------// + class file_output_stream : public ibase_log_stream + { + public: + typedef std::map named_log_streams; + + file_output_stream( std::string default_log_file_name, std::string log_path ) + { + m_default_log_filename = default_log_file_name; + m_max_logfile_size = 0; + m_default_log_path = log_path; + m_pdefault_file_stream = add_new_stream_and_open(default_log_file_name.c_str()); + } + + ~file_output_stream() + { + for(named_log_streams::iterator it = m_log_file_names.begin(); it!=m_log_file_names.end(); it++) + { + if ( it->second->is_open() ) + { + it->second->flush(); + it->second->close(); + } + delete it->second; + } + } + private: + named_log_streams m_log_file_names; + std::string m_default_log_path; + std::ofstream* m_pdefault_file_stream; + std::string m_log_rotate_cmd; + std::string m_default_log_filename; + boost::uint64_t m_max_logfile_size; + + + std::ofstream* add_new_stream_and_open(const char* pstream_name) + { + //log_space::rotate_log_file((m_default_log_path + "\\" + pstream_name).c_str()); + + std::ofstream* pstream = (m_log_file_names[pstream_name] = new std::ofstream); + std::string target_path = m_default_log_path + "/" + pstream_name; + pstream->open( target_path.c_str(), std::ios_base::out | std::ios::app /*ios_base::trunc */); + if(pstream->fail()) + return NULL; + return pstream; + } + + bool set_max_logfile_size(boost::uint64_t max_size) + { + m_max_logfile_size = max_size; + return true; + } + + bool set_log_rotate_cmd(const std::string& cmd) + { + m_log_rotate_cmd = cmd; + return true; + } + + + + virtual bool out_buffer( const char* buffer, int buffer_len, int log_level, int color, const char* plog_name = NULL ) + { + std::ofstream* m_target_file_stream = m_pdefault_file_stream; + if(plog_name) + { //find named stream + named_log_streams::iterator it = m_log_file_names.find(plog_name); + if(it == m_log_file_names.end()) + m_target_file_stream = add_new_stream_and_open(plog_name); + else + m_target_file_stream = it->second; + } + if(!m_target_file_stream || !m_target_file_stream->is_open()) + return false;//TODO: add assert here + + m_target_file_stream->write(buffer, buffer_len ); + m_target_file_stream->flush(); + + if(m_max_logfile_size) + { + std::ofstream::pos_type pt = m_target_file_stream->tellp(); + boost::uint64_t current_sz = pt; + if(current_sz > m_max_logfile_size) + { + std::cout << "current_sz= " << current_sz << " m_max_logfile_size= " << m_max_logfile_size << std::endl; + std::string log_file_name; + if(!plog_name) + log_file_name = m_default_log_filename; + else + log_file_name = plog_name; + + m_target_file_stream->close(); + std::string new_log_file_name = log_file_name; + + time_t tm = 0; + time(&tm); + + int err_count = 0; + boost::system::error_code ec; + do + { + new_log_file_name = string_tools::cut_off_extension(log_file_name); + if(err_count) + new_log_file_name += misc_utils::get_time_str_v2(tm) + "(" + boost::lexical_cast(err_count) + ")" + ".log"; + else + new_log_file_name += misc_utils::get_time_str_v2(tm) + ".log"; + + err_count++; + }while(boost::filesystem::exists(m_default_log_path + "/" + new_log_file_name, ec)); + + std::string new_log_file_path = m_default_log_path + "/" + new_log_file_name; + boost::filesystem::rename(m_default_log_path + "/" + log_file_name, new_log_file_path, ec); + if(ec) + { + std::cout << "Filed to rename, ec = " << ec.message() << std::endl; + } + + if(m_log_rotate_cmd.size()) + { + + std::string m_log_rotate_cmd_local_copy = m_log_rotate_cmd; + //boost::replace_all(m_log_rotate_cmd, "[*SOURCE*]", new_log_file_path); + boost::replace_all(m_log_rotate_cmd_local_copy, "[*TARGET*]", new_log_file_path); + + misc_utils::call_sys_cmd(m_log_rotate_cmd_local_copy); + } + + m_target_file_stream->open( (m_default_log_path + "/" + log_file_name).c_str(), std::ios_base::out | std::ios::app /*ios_base::trunc */); + if(m_target_file_stream->fail()) + return false; + } + } + + return true; + } + int get_type(){return LOGGER_FILE;} + }; + /************************************************************************/ + /* */ + /************************************************************************/ + class log_stream_splitter + { + public: + typedef std::list > streams_container; + + log_stream_splitter(){} + ~log_stream_splitter() + { + //free pointers + std::for_each(m_log_streams.begin(), m_log_streams.end(), delete_ptr()); + } + + bool set_max_logfile_size(boost::uint64_t max_size) + { + for(streams_container::iterator it = m_log_streams.begin(); it!=m_log_streams.end();it++) + it->first->set_max_logfile_size(max_size); + return true; + } + + bool set_log_rotate_cmd(const std::string& cmd) + { + for(streams_container::iterator it = m_log_streams.begin(); it!=m_log_streams.end();it++) + it->first->set_log_rotate_cmd(cmd); + return true; + } + + bool do_log_message(const std::string& rlog_mes, int log_level, int color, const char* plog_name = NULL) + { + std::string str_mess = rlog_mes; + size_t str_len = str_mess.size(); + const char* pstr = str_mess.c_str(); + for(streams_container::iterator it = m_log_streams.begin(); it!=m_log_streams.end();it++) + if(it->second >= log_level) + it->first->out_buffer(pstr, (int)str_len, log_level, color, plog_name); + return true; + } + + bool add_logger( int type, const char* pdefault_file_name, const char* pdefault_log_folder, int log_level_limit = LOG_LEVEL_4 ) + { + ibase_log_stream* ls = NULL; + + switch( type ) + { + case LOGGER_FILE: + ls = new file_output_stream( pdefault_file_name, pdefault_log_folder ); + break; + + case LOGGER_DEBUGGER: +#ifdef _MSC_VER + ls = new debug_output_stream( ); +#else + return false;//not implemented yet +#endif + break; + case LOGGER_CONSOLE: + ls = new console_output_stream( ); + break; + } + + if ( ls ) { + m_log_streams.push_back(streams_container::value_type(ls, log_level_limit)); + return true; + } + return ls ? true:false; + } + bool add_logger( ibase_log_stream* pstream, int log_level_limit = LOG_LEVEL_4 ) + { + m_log_streams.push_back(streams_container::value_type(pstream, log_level_limit) ); + return true; + } + + bool remove_logger(int type) + { + streams_container::iterator it = m_log_streams.begin(); + for(;it!=m_log_streams.end(); it++) + { + if(it->first->get_type() == type) + { + delete it->first; + m_log_streams.erase(it); + return true; + } + } + return false; + + } + + protected: + private: + + streams_container m_log_streams; + }; + + /************************************************************************/ + /* */ + /************************************************************************/ + inline int get_set_log_detalisation_level(bool is_need_set = false, int log_level_to_set = LOG_LEVEL_1); + inline int get_set_time_level(bool is_need_set = false, int time_log_level = LOG_LEVEL_0); + inline bool get_set_need_thread_id(bool is_need_set = false, bool is_need_val = false); + inline bool get_set_need_proc_name(bool is_need_set = false, bool is_need_val = false); + + + inline std::string get_daytime_string2() + { + boost::posix_time::ptime p = boost::posix_time::microsec_clock::local_time(); + return misc_utils::get_time_str_v3(p); + } + inline std::string get_day_time_string() + { + return get_daytime_string2(); + //time_t tm = 0; + //time(&tm); + //return misc_utils::get_time_str(tm); + } + + inline std::string get_time_string() + { + return get_daytime_string2(); + + } +#ifdef _MSC_VER + inline std::string get_time_string_adv(SYSTEMTIME* pst = NULL) + { + SYSTEMTIME st = {0}; + if(!pst) + { + pst = &st; + GetSystemTime(&st); + } + std::stringstream str_str; + str_str.fill('0'); + str_str << std::setw(2) << pst->wHour << "_" + << std::setw(2) << pst->wMinute << "_" + << std::setw(2) << pst->wSecond << "_" + << std::setw(3) << pst->wMilliseconds; + return str_str.str(); + } +#endif + + + + + + class logger + { + public: + friend class log_singletone; + + logger() + { + CRITICAL_REGION_BEGIN(m_critical_sec); + init(); + CRITICAL_REGION_END(); + } + ~logger() + { + } + + bool set_max_logfile_size(boost::uint64_t max_size) + { + CRITICAL_REGION_BEGIN(m_critical_sec); + m_log_target.set_max_logfile_size(max_size); + CRITICAL_REGION_END(); + return true; + } + + bool set_log_rotate_cmd(const std::string& cmd) + { + CRITICAL_REGION_BEGIN(m_critical_sec); + m_log_target.set_log_rotate_cmd(cmd); + CRITICAL_REGION_END(); + return true; + } + + bool take_away_journal(std::list& journal) + { + CRITICAL_REGION_BEGIN(m_critical_sec); + m_journal.swap(journal); + CRITICAL_REGION_END(); + return true; + } + + bool do_log_message(const std::string& rlog_mes, int log_level, int color, bool add_to_journal = false, const char* plog_name = NULL) + { + CRITICAL_REGION_BEGIN(m_critical_sec); + m_log_target.do_log_message(rlog_mes, log_level, color, plog_name); + if(add_to_journal) + m_journal.push_back(rlog_mes); + + return true; + CRITICAL_REGION_END(); + } + + bool add_logger( int type, const char* pdefault_file_name, const char* pdefault_log_folder , int log_level_limit = LOG_LEVEL_4) + { + CRITICAL_REGION_BEGIN(m_critical_sec); + return m_log_target.add_logger( type, pdefault_file_name, pdefault_log_folder, log_level_limit); + CRITICAL_REGION_END(); + } + bool add_logger( ibase_log_stream* pstream, int log_level_limit = LOG_LEVEL_4) + { + CRITICAL_REGION_BEGIN(m_critical_sec); + return m_log_target.add_logger(pstream, log_level_limit); + CRITICAL_REGION_END(); + } + + bool remove_logger(int type) + { + CRITICAL_REGION_BEGIN(m_critical_sec); + return m_log_target.remove_logger(type); + CRITICAL_REGION_END(); + } + + + bool set_thread_prefix(const std::string& prefix) + { + CRITICAL_REGION_BEGIN(m_critical_sec); + m_thr_prefix_strings[misc_utils::get_thread_string_id()] = prefix; + CRITICAL_REGION_END(); + return true; + } + + + std::string get_default_log_file() + { + return m_default_log_file; + } + + std::string get_default_log_folder() + { + return m_default_log_folder; + } + + protected: + private: + bool init() + { + // + + m_process_name = string_tools::get_current_module_name(); + + init_log_path_by_default(); + + //init default set of loggers + init_default_loggers(); + + std::stringstream ss; + ss << get_time_string() << " Init logging. Level=" << get_set_log_detalisation_level() + << " Log path=" << m_default_log_folder << std::endl; + this->do_log_message(ss.str(), console_color_white, LOG_LEVEL_0); + return true; + } + bool init_default_loggers() + { + //TODO: + return true; + } + + bool init_log_path_by_default() + { + //load process name + m_default_log_folder = string_tools::get_current_module_folder(); + + m_default_log_file = m_process_name; + std::string::size_type a = m_default_log_file.rfind('.'); + if ( a != std::string::npos ) + m_default_log_file.erase( a, m_default_log_file.size()); + m_default_log_file += ".log"; + + return true; + } + + log_stream_splitter m_log_target; + + std::string m_default_log_folder; + std::string m_default_log_file; + std::string m_process_name; + std::map m_thr_prefix_strings; + std::list m_journal; + critical_section m_critical_sec; + }; + /************************************************************************/ + /* */ + /************************************************************************/ + class log_singletone + { + public: + friend class initializer; + friend class logger; + static int get_log_detalisation_level() + { + get_or_create_instance();//to initialize logger, if it not initialized + return get_set_log_detalisation_level(); + } + + static bool is_filter_error(int error_code) + { + return false; + } + + + static bool do_log_message(const std::string& rlog_mes, int log_level, int color, bool keep_in_journal, const char* plog_name = NULL) + { + logger* plogger = get_or_create_instance(); + bool res = false; + if(plogger) + res = plogger->do_log_message(rlog_mes, log_level, color, keep_in_journal, plog_name); + else + { //globally uninitialized, create new logger for each call of do_log_message() and then delete it + plogger = new logger(); + //TODO: some extra initialization + res = plogger->do_log_message(rlog_mes, log_level, color, keep_in_journal, plog_name); + delete plogger; + plogger = NULL; + } + return res; + } + + static bool take_away_journal(std::list& journal) + { + logger* plogger = get_or_create_instance(); + bool res = false; + if(plogger) + res = plogger->take_away_journal(journal); + + return res; + } + + static bool set_max_logfile_size(boost::uint64_t file_size) + { + logger* plogger = get_or_create_instance(); + if(!plogger) return false; + return plogger->set_max_logfile_size(file_size); + } + + + static bool set_log_rotate_cmd(const std::string& cmd) + { + logger* plogger = get_or_create_instance(); + if(!plogger) return false; + return plogger->set_log_rotate_cmd(cmd); + } + + + static bool add_logger( int type, const char* pdefault_file_name, const char* pdefault_log_folder, int log_level_limit = LOG_LEVEL_4) + { + logger* plogger = get_or_create_instance(); + if(!plogger) return false; + return plogger->add_logger(type, pdefault_file_name, pdefault_log_folder, log_level_limit); + } + + static std::string get_default_log_file() + { + logger* plogger = get_or_create_instance(); + if(plogger) + return plogger->get_default_log_file(); + + return ""; + } + + static std::string get_default_log_folder() + { + logger* plogger = get_or_create_instance(); + if(plogger) + return plogger->get_default_log_folder(); + + return ""; + } + + static bool add_logger( ibase_log_stream* pstream, int log_level_limit = LOG_LEVEL_4 ) + { + logger* plogger = get_or_create_instance(); + if(!plogger) return false; + return plogger->add_logger(pstream, log_level_limit); + } + + + static bool remove_logger( int type ) + { + logger* plogger = get_or_create_instance(); + if(!plogger) return false; + return plogger->remove_logger(type); + } +PUSH_WARNINGS +DISABLE_GCC_WARNING(maybe-uninitialized) + static int get_set_log_detalisation_level(bool is_need_set = false, int log_level_to_set = LOG_LEVEL_1) + { + static int log_detalisation_level = LOG_LEVEL_1; + if(is_need_set) + log_detalisation_level = log_level_to_set; + return log_detalisation_level; + } +POP_WARNINGS + static int get_set_time_level(bool is_need_set = false, int time_log_level = LOG_LEVEL_0) + { + static int val_time_log_level = LOG_LEVEL_0; + if(is_need_set) + val_time_log_level = time_log_level; + + return val_time_log_level; + } + + static int get_set_process_level(bool is_need_set = false, int process_log_level = LOG_LEVEL_0) + { + static int val_process_log_level = LOG_LEVEL_0; + if(is_need_set) + val_process_log_level = process_log_level; + + return val_process_log_level; + } + + /*static int get_set_tid_level(bool is_need_set = false, int tid_log_level = LOG_LEVEL_0) + { + static int val_tid_log_level = LOG_LEVEL_0; + if(is_need_set) + val_tid_log_level = tid_log_level; + + return val_tid_log_level; + }*/ + + static bool get_set_need_thread_id(bool is_need_set = false, bool is_need_val = false) + { + static bool is_need = false; + if(is_need_set) + is_need = is_need_val; + + return is_need; + } + + static bool get_set_need_proc_name(bool is_need_set = false, bool is_need_val = false) + { + static bool is_need = true; + if(is_need_set) + is_need = is_need_val; + + return is_need; + } + static boost::uint64_t get_set_err_count(bool is_need_set = false, boost::uint64_t err_val = false) + { + static boost::uint64_t err_count = 0; + if(is_need_set) + err_count = err_val; + + return err_count; + } + + +#ifdef _MSC_VER + + + static void SetThreadName( DWORD dwThreadID, const char* threadName) + { +#define MS_VC_EXCEPTION 0x406D1388 + +#pragma pack(push,8) + typedef struct tagTHREADNAME_INFO + { + DWORD dwType; // Must be 0x1000. + LPCSTR szName; // Pointer to name (in user addr space). + DWORD dwThreadID; // Thread ID (-1=caller thread). + DWORD dwFlags; // Reserved for future use, must be zero. + } THREADNAME_INFO; +#pragma pack(pop) + + + + Sleep(10); + THREADNAME_INFO info; + info.dwType = 0x1000; + info.szName = (char*)threadName; + info.dwThreadID = dwThreadID; + info.dwFlags = 0; + + __try + { + RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info ); + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + } + } +#endif + + static bool set_thread_log_prefix(const std::string& prefix) + { +#ifdef _MSC_VER + SetThreadName(-1, prefix.c_str()); +#endif + + + logger* plogger = get_or_create_instance(); + if(!plogger) return false; + return plogger->set_thread_prefix(prefix); + } + + + static std::string get_prefix_entry() + { + std::stringstream str_prefix; + //write time entry + if ( get_set_time_level() <= get_set_log_detalisation_level() ) + str_prefix << get_day_time_string() << " "; + + //write process info + logger* plogger = get_or_create_instance(); + //bool res = false; + if(!plogger) + { //globally uninitialized, create new logger for each call of get_prefix_entry() and then delete it + plogger = new logger(); + } + + //if ( get_set_need_proc_name() && get_set_process_level() <= get_set_log_detalisation_level() ) + // str_prefix << "[" << plogger->m_process_name << " (id=" << GetCurrentProcessId() << ")] "; +//#ifdef _MSC_VER_EX + if ( get_set_need_thread_id() /*&& get_set_tid_level() <= get_set_log_detalisation_level()*/ ) + str_prefix << "tid:" << misc_utils::get_thread_string_id() << " "; +//#endif + + if(plogger->m_thr_prefix_strings.size()) + { + CRITICAL_REGION_LOCAL(plogger->m_critical_sec); + std::string thr_str = misc_utils::get_thread_string_id(); + std::map::iterator it = plogger->m_thr_prefix_strings.find(thr_str); + if(it!=plogger->m_thr_prefix_strings.end()) + { + str_prefix << it->second; + } + } + + + if(get_set_is_uninitialized()) + delete plogger; + + return str_prefix.str(); + } + + private: + log_singletone(){}//restric to create an instance + //static initializer m_log_initializer;//must be in one .cpp file (for example main.cpp) via DEFINE_LOGGING macro + + static bool init() + { + return true;/*do nothing here*/ + } + static bool un_init() + { + //delete object + logger* plogger = get_set_instance_internal(); + if(plogger) delete plogger; + //set uninitialized + get_set_is_uninitialized(true, true); + get_set_instance_internal(true, NULL); + return true; + } + + static logger* get_or_create_instance() + { + logger* plogger = get_set_instance_internal(); + if(!plogger) + if(!get_set_is_uninitialized()) + get_set_instance_internal(true, plogger = new logger); + + return plogger; + } + + static logger* get_set_instance_internal(bool is_need_set = false, logger* pnew_logger_val = NULL) + { + static logger* val_plogger = NULL; + + if(is_need_set) + val_plogger = pnew_logger_val; + + return val_plogger; + } + + static bool get_set_is_uninitialized(bool is_need_set = false, bool is_uninitialized = false) + { + static bool val_is_uninitialized = false; + + if(is_need_set) + val_is_uninitialized = is_uninitialized; + + return val_is_uninitialized; + } + //static int get_set_error_filter(bool is_need_set = false) + }; + + const static initializer log_initializer; + /************************************************************************/ + /* */ +// /************************************************************************/ +// class log_array_value +// { +// int num; +// log_message& m_ref_log_mes; +// +// public: +// +// log_array_value( log_message& log_mes ) : num(0), m_ref_log_mes(log_mes) {} +// +// void operator ( )( ibase_log_value *val ) { +// m_ref_log_mes << "\n[" << num++ << "] "/* << val*/; } +// +// +// template +// void operator ()(T &value ) +// { +// m_ref_log_mes << "\n[" << num++ << "] " << value; +// } +// }; + + class log_frame + { + std::string m_name; + int m_level; + const char* m_plog_name; + public: + + log_frame(const std::string& name, int dlevel = LOG_LEVEL_2 , const char* plog_name = NULL) + { +#ifdef _MSC_VER + int lasterr=::GetLastError(); +#endif + m_plog_name = plog_name; + if ( dlevel <= log_singletone::get_log_detalisation_level() ) + { + m_name = name; + std::stringstream ss; + ss << log_space::log_singletone::get_prefix_entry() << "-->>" << m_name << std::endl; + log_singletone::do_log_message(ss.str(), dlevel, console_color_default, false, m_plog_name); + } + m_level = dlevel; +#ifdef _MSC_VER + ::SetLastError(lasterr); +#endif + } + ~log_frame() + { +#ifdef _MSC_VER + int lasterr=::GetLastError(); +#endif + + if (m_level <= log_singletone::get_log_detalisation_level() ) + { + std::stringstream ss; + ss << log_space::log_singletone::get_prefix_entry() << "<<--" << m_name << std::endl; + log_singletone::do_log_message(ss.str(), m_level, console_color_default, false,m_plog_name); + } +#ifdef _MSC_VER + ::SetLastError(lasterr); +#endif + } + }; + + inline int get_set_time_level(bool is_need_set, int time_log_level) + { + return log_singletone::get_set_time_level(is_need_set, time_log_level); + } + inline int get_set_log_detalisation_level(bool is_need_set, int log_level_to_set) + { + return log_singletone::get_set_log_detalisation_level(is_need_set, log_level_to_set); + } + inline std::string get_prefix_entry() + { + return log_singletone::get_prefix_entry(); + } + inline bool get_set_need_thread_id(bool is_need_set, bool is_need_val) + { + return log_singletone::get_set_need_thread_id(is_need_set, is_need_val); + } + inline bool get_set_need_proc_name(bool is_need_set, bool is_need_val ) + { + return log_singletone::get_set_need_proc_name(is_need_set, is_need_val); + } + + inline std::string get_win32_err_descr(int err_no) + { +#ifdef _MSC_VER + LPVOID lpMsgBuf; + + FormatMessageA( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + err_no, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (char*) &lpMsgBuf, + 0, NULL ); + + std::string fix_sys_message = "(null)"; + if(lpMsgBuf) fix_sys_message = (char*)lpMsgBuf; + std::string::size_type a; + if ( (a = fix_sys_message.rfind( '\n' )) != std::string::npos ) + fix_sys_message.erase(a); + if ( (a = fix_sys_message.rfind( '\r' )) != std::string::npos ) + fix_sys_message.erase(a); + + LocalFree(lpMsgBuf); + return fix_sys_message; +#else + return "Not implemented yet"; +#endif + } + + inline bool getwin32_err_text(std::stringstream& ref_message, int error_no) + { + ref_message << "win32 error:" << get_win32_err_descr(error_no); + return true; + } +} +#if defined(_DEBUG) || defined(__GNUC__) + #define ENABLE_LOGGING_INTERNAL +#endif + +#if defined(ENABLE_RELEASE_LOGGING) + #define ENABLE_LOGGING_INTERNAL +#endif + + +#if defined(ENABLE_LOGGING_INTERNAL) + +#define LOG_PRINT_NO_PREFIX2(log_name, x, y) {if ( y <= epee::log_space::log_singletone::get_log_detalisation_level() )\ + {std::stringstream ss________; ss________ << x << std::endl; epee::log_space::log_singletone::do_log_message(ss________.str() , y, epee::log_space::console_color_default, false, log_name);}} + +#define LOG_PRINT_NO_PREFIX_NO_POSTFIX2(log_name, x, y) {if ( y <= epee::log_space::log_singletone::get_log_detalisation_level() )\ + {std::stringstream ss________; ss________ << x; epee::log_space::log_singletone::do_log_message(ss________.str(), y, epee::log_space::console_color_default, false, log_name);}} + + +#define LOG_PRINT_NO_POSTFIX2(log_name, x, y) {if ( y <= epee::log_space::log_singletone::get_log_detalisation_level() )\ + {std::stringstream ss________; ss________ << epee::log_space::log_singletone::get_prefix_entry() << x; epee::log_space::log_singletone::do_log_message(ss________.str(), y, epee::log_space::console_color_default, false, log_name);}} + + +#define LOG_PRINT2(log_name, x, y) {if ( y <= epee::log_space::log_singletone::get_log_detalisation_level() )\ + {std::stringstream ss________; ss________ << epee::log_space::log_singletone::get_prefix_entry() << x << std::endl;epee::log_space::log_singletone::do_log_message(ss________.str(), y, epee::log_space::console_color_default, false, log_name);}} + +#define LOG_PRINT_COLOR2(log_name, x, y, color) {if ( y <= epee::log_space::log_singletone::get_log_detalisation_level() )\ + {std::stringstream ss________; ss________ << epee::log_space::log_singletone::get_prefix_entry() << x << std::endl;epee::log_space::log_singletone::do_log_message(ss________.str(), y, color, false, log_name);}} + + +#define LOG_PRINT2_JORNAL(log_name, x, y) {if ( y <= epee::log_space::log_singletone::get_log_detalisation_level() )\ + {std::stringstream ss________; ss________ << epee::log_space::log_singletone::get_prefix_entry() << x << std::endl;epee::log_space::log_singletone::do_log_message(ss________.str(), y, epee::log_space::console_color_default, true, log_name);}} + + +#define LOG_ERROR2(log_name, x) { \ + std::stringstream ss________; ss________ << epee::log_space::log_singletone::get_prefix_entry() << "ERROR " << __FILE__ << ":" << __LINE__ << " " << x << std::endl; epee::log_space::log_singletone::do_log_message(ss________.str(), LOG_LEVEL_0, epee::log_space::console_color_red, true, log_name);LOCAL_ASSERT(0); epee::log_space::log_singletone::get_set_err_count(true, epee::log_space::log_singletone::get_set_err_count()+1);} + +#define LOG_FRAME2(log_name, x, y) epee::log_space::log_frame frame(x, y, log_name) + +#else + + +#define LOG_PRINT_NO_PREFIX2(log_name, x, y) + +#define LOG_PRINT_NO_PREFIX_NO_POSTFIX2(log_name, x, y) + +#define LOG_PRINT_NO_POSTFIX2(log_name, x, y) + +#define LOG_PRINT_COLOR2(log_name, x, y, color) + +#define LOG_PRINT2_JORNAL(log_name, x, y) + +#define LOG_PRINT2(log_name, x, y) + +#define LOG_ERROR2(log_name, x) + + +#define LOG_FRAME2(log_name, x, y) + + +#endif + + +#ifndef LOG_DEFAULT_TARGET + #define LOG_DEFAULT_TARGET NULL +#endif + + +#define LOG_PRINT_NO_POSTFIX(mess, level) LOG_PRINT_NO_POSTFIX2(LOG_DEFAULT_TARGET, mess, level) +#define LOG_PRINT_NO_PREFIX(mess, level) LOG_PRINT_NO_PREFIX2(LOG_DEFAULT_TARGET, mess, level) +#define LOG_PRINT_NO_PREFIX_NO_POSTFIX(mess, level) LOG_PRINT_NO_PREFIX_NO_POSTFIX2(LOG_DEFAULT_TARGET, mess, level) +#define LOG_PRINT(mess, level) LOG_PRINT2(LOG_DEFAULT_TARGET, mess, level) + +#define LOG_PRINT_COLOR(mess, level, color) LOG_PRINT_COLOR2(LOG_DEFAULT_TARGET, mess, level, color) +#define LOG_PRINT_RED(mess, level) LOG_PRINT_COLOR2(LOG_DEFAULT_TARGET, mess, level, epee::log_space::console_color_red) +#define LOG_PRINT_GREEN(mess, level) LOG_PRINT_COLOR2(LOG_DEFAULT_TARGET, mess, level, epee::log_space::console_color_green) +#define LOG_PRINT_BLUE(mess, level) LOG_PRINT_COLOR2(LOG_DEFAULT_TARGET, mess, level, epee::log_space::console_color_blue) + +#define LOG_PRINT_RED_L0(mess) LOG_PRINT_COLOR2(LOG_DEFAULT_TARGET, mess, LOG_LEVEL_0, epee::log_space::console_color_red) + +#define LOG_PRINT_L0(mess) LOG_PRINT(mess, LOG_LEVEL_0) +#define LOG_PRINT_L1(mess) LOG_PRINT(mess, LOG_LEVEL_1) +#define LOG_PRINT_L2(mess) LOG_PRINT(mess, LOG_LEVEL_2) +#define LOG_PRINT_L3(mess) LOG_PRINT(mess, LOG_LEVEL_3) +#define LOG_PRINT_L4(mess) LOG_PRINT(mess, LOG_LEVEL_4) +#define LOG_PRINT_J(mess, level) LOG_PRINT2_JORNAL(LOG_DEFAULT_TARGET, mess, level) + +#define LOG_ERROR(mess) LOG_ERROR2(LOG_DEFAULT_TARGET, mess) +#define LOG_FRAME(mess, level) LOG_FRAME2(LOG_DEFAULT_TARGET, mess, level) +#define LOG_VALUE(mess, level) LOG_VALUE2(LOG_DEFAULT_TARGET, mess, level) +#define LOG_ARRAY(mess, level) LOG_ARRAY2(LOG_DEFAULT_TARGET, mess, level) +//#define LOGWIN_PLATFORM_ERROR(err_no) LOGWINDWOS_PLATFORM_ERROR2(LOG_DEFAULT_TARGET, err_no) +#define LOG_SOCKET_ERROR(err_no) LOG_SOCKET_ERROR2(LOG_DEFAULT_TARGET, err_no) +//#define LOGWIN_PLATFORM_ERROR_UNCRITICAL(mess) LOGWINDWOS_PLATFORM_ERROR_UNCRITICAL2(LOG_DEFAULT_TARGET, mess) + +#define ENDL std::endl + +#define TRY_ENTRY() try { +#define CATCH_ENTRY(location, return_val) } \ + catch(const std::exception& ex) \ +{ \ + (void)(ex); \ + LOG_ERROR("Exception at [" << location << "], what=" << ex.what()); \ + return return_val; \ +}\ + catch(...)\ +{\ + LOG_ERROR("Exception at [" << location << "], generic exception \"...\"");\ + return return_val; \ +} + +#define CATCH_ENTRY_L0(lacation, return_val) CATCH_ENTRY(lacation, return_val) +#define CATCH_ENTRY_L1(lacation, return_val) CATCH_ENTRY(lacation, return_val) +#define CATCH_ENTRY_L2(lacation, return_val) CATCH_ENTRY(lacation, return_val) +#define CATCH_ENTRY_L3(lacation, return_val) CATCH_ENTRY(lacation, return_val) +#define CATCH_ENTRY_L4(lacation, return_val) CATCH_ENTRY(lacation, return_val) + + +#define ASSERT_MES_AND_THROW(message) {LOG_ERROR(message); std::stringstream ss; ss << message; throw std::runtime_error(ss.str());} +#define CHECK_AND_ASSERT_THROW_MES(expr, message) {if(!(expr)) ASSERT_MES_AND_THROW(message);} + + +#ifndef CHECK_AND_ASSERT +#define CHECK_AND_ASSERT(expr, fail_ret_val) do{if(!(expr)){LOCAL_ASSERT(expr); return fail_ret_val;};}while(0) +#endif + +#define NOTHING + +#ifndef CHECK_AND_ASSERT_MES +#define CHECK_AND_ASSERT_MES(expr, fail_ret_val, message) do{if(!(expr)) {LOG_ERROR(message); return fail_ret_val;};}while(0) +#endif + +#ifndef CHECK_AND_NO_ASSERT_MES +#define CHECK_AND_NO_ASSERT_MES(expr, fail_ret_val, message) do{if(!(expr)) {LOG_PRINT_L0(message); /*LOCAL_ASSERT(expr);*/ return fail_ret_val;};}while(0) +#endif + + +#ifndef CHECK_AND_ASSERT_MES_NO_RET +#define CHECK_AND_ASSERT_MES_NO_RET(expr, message) do{if(!(expr)) {LOG_ERROR(message); return;};}while(0) +#endif + + +#ifndef CHECK_AND_ASSERT_MES2 +#define CHECK_AND_ASSERT_MES2(expr, message) do{if(!(expr)) {LOG_ERROR(message); };}while(0) +#endif + +} +#endif //_MISC_LOG_EX_H_ diff --git a/contrib/epee/include/misc_os_dependent.h b/contrib/epee/include/misc_os_dependent.h new file mode 100644 index 000000000..0850c7c07 --- /dev/null +++ b/contrib/epee/include/misc_os_dependent.h @@ -0,0 +1,108 @@ +// 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. +// +#ifdef WIN32 + #ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN + #endif + + //#ifdef _WIN32_WINNT + // #undef _WIN32_WINNT + // #define _WIN32_WINNT 0x0600 + //#endif + + +#include +#endif + +#ifdef __MACH__ +#include +#include +#endif + +#pragma once +namespace epee +{ +namespace misc_utils +{ + + inline boost::uint64_t get_tick_count() + { +#if defined(_MSC_VER) + return ::GetTickCount64(); +#elif defined(__MACH__) + clock_serv_t cclock; + mach_timespec_t mts; + + host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock); + clock_get_time(cclock, &mts); + mach_port_deallocate(mach_task_self(), cclock); + + return (mts.tv_sec * 1000) + (mts.tv_nsec/1000000); +#else + struct timespec ts; + if(clock_gettime(CLOCK_MONOTONIC, &ts) != 0) { + return 0; + } + return (ts.tv_sec * 1000) + (ts.tv_nsec/1000000); +#endif + } + + + inline int call_sys_cmd(const std::string& cmd) + { + std::cout << "# " << cmd << std::endl; + + FILE * fp ; + //char tstCommand[] ="ls *"; + char path[1000] = {0}; +#if !defined(__GNUC__) + fp = _popen(cmd.c_str(), "r"); +#else + fp = popen(cmd.c_str(), "r"); +#endif + while ( fgets( path, 1000, fp ) != NULL ) + std::cout << path; + +#if !defined(__GNUC__) + _pclose(fp); +#else + pclose(fp); +#endif + return 0; + + } + + + inline std::string get_thread_string_id() + { +#if defined(_MSC_VER) + return boost::lexical_cast(GetCurrentThreadId()); +#elif defined(__GNUC__) + return boost::lexical_cast(pthread_self()); +#endif + } +} +} diff --git a/contrib/epee/include/net/abstract_tcp_server.h b/contrib/epee/include/net/abstract_tcp_server.h new file mode 100644 index 000000000..c74444c8e --- /dev/null +++ b/contrib/epee/include/net/abstract_tcp_server.h @@ -0,0 +1,316 @@ +// 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. +// + + + +#ifndef _ABSTRACT_TCP_SERVER_H_ +#define _ABSTRACT_TCP_SERVER_H_ + +#include +#include +#include +#include "winobj.h" +//#include "threads_helper.h" +#include "net_utils_base.h" + +#pragma comment(lib, "Ws2_32.lib") + +namespace epee +{ +namespace net_utils +{ + /************************************************************************/ + /* */ + /************************************************************************/ + class soket_sender: public i_service_endpoint + { + public: + soket_sender(SOCKET sock):m_sock(sock){} + private: + virtual bool handle_send(const void* ptr, size_t cb) + { + if(cb != send(m_sock, (char*)ptr, (int)cb, 0)) + { + int sock_err = WSAGetLastError(); + LOG_ERROR("soket_sender: Failed to send " << cb << " bytes, Error=" << sock_err); + return false; + } + return true; + + } + + SOCKET m_sock; + }; + + + + /************************************************************************/ + /* */ + /************************************************************************/ + template + class abstract_tcp_server + { + public: + abstract_tcp_server(); + + bool init_server(int port_no); + bool deinit_server(); + bool run_server(); + bool send_stop_signal(); + + typename THandler::config_type& get_config_object(){return m_config;} + + private: + bool invoke_connection(SOCKET hnew_sock, long ip_from, int post_from); + static unsigned __stdcall ConnectionHandlerProc(void* lpParameter); + + class thread_context; + typedef std::list connections_container; + typedef typename connections_container::iterator connections_iterator; + + struct thread_context + { + HANDLE m_htread; + SOCKET m_socket; + abstract_tcp_server* powner; + connection_context m_context; + typename connections_iterator m_self_it; + }; + + SOCKET m_listen_socket; + int m_port; + bool m_initialized; + volatile LONG m_stop_server; + volatile LONG m_threads_count; + typename THandler::config_type m_config; + connections_container m_connections; + critical_section m_connections_lock; + }; + + template + unsigned __stdcall abstract_tcp_server::ConnectionHandlerProc(void* lpParameter) + { + + thread_context* pthread_context = (thread_context*)lpParameter; + if(!pthread_context) + return 0; + abstract_tcp_server* pthis = pthread_context->powner; + + ::InterlockedIncrement(&pthis->m_threads_count); + + ::CoInitialize(NULL); + + + LOG_PRINT("Handler thread STARTED with socket=" << pthread_context->m_socket, LOG_LEVEL_2); + int res = 0; + + soket_sender sndr(pthread_context->m_socket); + THandler srv(&sndr, pthread_context->powner->m_config, pthread_context->m_context); + + + srv.after_init_connection(); + + char buff[1000] = {0}; + std::string ansver; + while ( (res = recv(pthread_context->m_socket, (char*)buff, 1000, 0)) > 0) + { + LOG_PRINT("Data in, " << res << " bytes", LOG_LEVEL_3); + if(!srv.handle_recv(buff, res)) + break; + } + shutdown(pthread_context->m_socket, SD_BOTH); + closesocket(pthread_context->m_socket); + + abstract_tcp_server* powner = pthread_context->powner; + LOG_PRINT("Handler thread with socket=" << pthread_context->m_socket << " STOPPED", LOG_LEVEL_2); + powner->m_connections_lock.lock(); + ::CloseHandle(pthread_context->m_htread); + pthread_context->powner->m_connections.erase(pthread_context->m_self_it); + powner->m_connections_lock.unlock(); + CoUninitialize(); + ::InterlockedDecrement(&pthis->m_threads_count); + return 1; + } + //---------------------------------------------------------------------------------------- + template + abstract_tcp_server::abstract_tcp_server():m_listen_socket(INVALID_SOCKET), + m_initialized(false), + m_stop_server(0), m_port(0), m_threads_count(0) + { + + } + + //---------------------------------------------------------------------------------------- + template + bool abstract_tcp_server::init_server(int port_no) + { + m_port = port_no; + WSADATA wsad = {0}; + int err = ::WSAStartup(MAKEWORD(2,2), &wsad); + if ( err != 0 || LOBYTE( wsad.wVersion ) != 2 || HIBYTE( wsad.wVersion ) != 2 ) + { + LOG_ERROR("Could not find a usable WinSock DLL, err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\""); + return false; + } + + m_initialized = true; + + m_listen_socket = ::WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, 0); + if(INVALID_SOCKET == m_listen_socket) + { + err = ::WSAGetLastError(); + LOG_ERROR("Failed to create socket, err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\""); + return false; + } + + int opt = 1; + setsockopt (m_listen_socket, SOL_SOCKET,SO_REUSEADDR, reinterpret_cast(&opt), sizeof(int)); + + sockaddr_in adr = {0}; + adr.sin_family = AF_INET; + adr.sin_addr.s_addr = htonl(INADDR_ANY); + adr.sin_port = (u_short)htons(port_no); + + err = bind(m_listen_socket, (const sockaddr*)&adr, sizeof(adr )); + if(SOCKET_ERROR == err ) + { + err = ::WSAGetLastError(); + LOG_PRINT("Failed to Bind, err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\"", LOG_LEVEL_2); + deinit_server(); + return false; + } + + ::InterlockedExchange(&m_stop_server, 0); + + return true; + } + //---------------------------------------------------------------------------------------- + template + bool abstract_tcp_server::deinit_server() + { + + if(!m_initialized) + return true; + + if(INVALID_SOCKET != m_listen_socket) + { + shutdown(m_listen_socket, SD_BOTH); + int res = closesocket(m_listen_socket); + if(SOCKET_ERROR == res) + { + int err = ::WSAGetLastError(); + LOG_ERROR("Failed to closesocket(), err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\""); + } + m_listen_socket = INVALID_SOCKET; + } + + int res = ::WSACleanup(); + if(SOCKET_ERROR == res) + { + int err = ::WSAGetLastError(); + LOG_ERROR("Failed to WSACleanup(), err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\""); + } + m_initialized = false; + + return true; + } + //---------------------------------------------------------------------------------------- + template + bool abstract_tcp_server::send_stop_signal() + { + InterlockedExchange(&m_stop_server, 1); + return true; + } + //---------------------------------------------------------------------------------------- + template + bool abstract_tcp_server::run_server() + { + int err = listen(m_listen_socket, 10000); + if(SOCKET_ERROR == err ) + { + err = ::WSAGetLastError(); + LOG_ERROR("Failed to listen, err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\""); + return false; + } + + LOG_PRINT("Listening port "<< m_port << "...." , LOG_LEVEL_2); + + while(!m_stop_server) + { + sockaddr_in adr_from = {0}; + int adr_len = sizeof(adr_from); + fd_set read_fs = {0}; + read_fs.fd_count = 1; + read_fs.fd_array[0] = m_listen_socket; + TIMEVAL tv = {0}; + tv.tv_usec = 100; + int select_res = select(0, &read_fs, NULL, NULL, &tv); + if(!select_res) + continue; + SOCKET new_sock = WSAAccept(m_listen_socket, (sockaddr *)&adr_from, &adr_len, NULL, NULL); + LOG_PRINT("Accepted connection on socket=" << new_sock, LOG_LEVEL_2); + invoke_connection(new_sock, adr_from.sin_addr.s_addr, adr_from.sin_port); + } + + deinit_server(); + +#define ABSTR_TCP_SRV_WAIT_COUNT_MAX 5000 +#define ABSTR_TCP_SRV_WAIT_COUNT_INTERVAL 1000 + + int wait_count = 0; + + while(m_threads_count && wait_count*1000 < ABSTR_TCP_SRV_WAIT_COUNT_MAX) + { + ::Sleep(ABSTR_TCP_SRV_WAIT_COUNT_INTERVAL); + wait_count++; + } + LOG_PRINT("abstract_tcp_server exit with wait count=" << wait_count*ABSTR_TCP_SRV_WAIT_COUNT_INTERVAL << "(max=" << ABSTR_TCP_SRV_WAIT_COUNT_MAX <<")", LOG_LEVEL_0); + + return true; + } + //---------------------------------------------------------------------------------------- + template + bool abstract_tcp_server::invoke_connection(SOCKET hnew_sock, long ip_from, int post_from) + { + m_connections_lock.lock(); + m_connections.push_back(thread_context()); + m_connections_lock.unlock(); + m_connections.back().m_socket = hnew_sock; + m_connections.back().powner = this; + m_connections.back().m_self_it = --m_connections.end(); + m_connections.back().m_context.m_remote_ip = ip_from; + m_connections.back().m_context.m_remote_port = post_from; + m_connections.back().m_htread = threads_helper::create_thread(ConnectionHandlerProc, &m_connections.back()); + + return true; + } + //---------------------------------------------------------------------------------------- + + //---------------------------------------------------------------------------------------- + //---------------------------------------------------------------------------------------- +} +} +#endif //_ABSTRACT_TCP_SERVER_H_ diff --git a/contrib/epee/include/net/abstract_tcp_server2.h b/contrib/epee/include/net/abstract_tcp_server2.h new file mode 100644 index 000000000..d49b8f864 --- /dev/null +++ b/contrib/epee/include/net/abstract_tcp_server2.h @@ -0,0 +1,276 @@ +// 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. +// + + + +#ifndef _ABSTRACT_TCP_SERVER2_H_ +#define _ABSTRACT_TCP_SERVER2_H_ + + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include "net_utils_base.h" +#include "syncobj.h" + + +#define ABSTRACT_SERVER_SEND_QUE_MAX_COUNT 100 + +namespace epee +{ +namespace net_utils +{ + + struct i_connection_filter + { + virtual bool is_remote_ip_allowed(boost::uint32_t adress)=0; + protected: + virtual ~i_connection_filter(){} + }; + + /************************************************************************/ + /* */ + /************************************************************************/ + /// Represents a single connection from a client. + template + class connection + : public boost::enable_shared_from_this >, + private boost::noncopyable, + public i_service_endpoint + { + public: + typedef typename t_protocol_handler::connection_context t_connection_context; + /// Construct a connection with the given io_service. + explicit connection(boost::asio::io_service& io_service, + typename t_protocol_handler::config_type& config, volatile boost::uint32_t& sock_count, i_connection_filter * &pfilter); + + virtual ~connection(); + /// Get the socket associated with the connection. + boost::asio::ip::tcp::socket& socket(); + + /// Start the first asynchronous operation for the connection. + bool start(bool is_income, bool is_multithreaded); + + void get_context(t_connection_context& context_){context_ = context;} + + void call_back_starter(); + private: + //----------------- i_service_endpoint --------------------- + virtual bool do_send(const void* ptr, size_t cb); + virtual bool close(); + virtual bool call_run_once_service_io(); + virtual bool request_callback(); + virtual boost::asio::io_service& get_io_service(); + virtual bool add_ref(); + virtual bool release(); + //------------------------------------------------------ + boost::shared_ptr > safe_shared_from_this(); + bool shutdown(); + /// Handle completion of a read operation. + void handle_read(const boost::system::error_code& e, + std::size_t bytes_transferred); + + /// Handle completion of a write operation. + void handle_write(const boost::system::error_code& e, size_t cb); + + /// Strand to ensure the connection's handlers are not called concurrently. + boost::asio::io_service::strand strand_; + + /// Socket for the connection. + boost::asio::ip::tcp::socket socket_; + + /// Buffer for incoming data. + boost::array buffer_; + + t_connection_context context; + volatile boost::uint32_t m_want_close_connection; + std::atomic m_was_shutdown; + critical_section m_send_que_lock; + std::list m_send_que; + volatile boost::uint32_t& m_ref_sockets_count; + i_connection_filter* &m_pfilter; + volatile bool m_is_multithreaded; + + //this should be the last one, because it could be wait on destructor, while other activities possible on other threads + t_protocol_handler m_protocol_handler; + //typename t_protocol_handler::config_type m_dummy_config; + std::list > > m_self_refs; // add_ref/release support + critical_section m_self_refs_lock; + }; + + + /************************************************************************/ + /* */ + /************************************************************************/ + template + class boosted_tcp_server + : private boost::noncopyable + { + public: + typedef boost::shared_ptr > connection_ptr; + typedef typename t_protocol_handler::connection_context t_connection_context; + /// Construct the server to listen on the specified TCP address and port, and + /// serve up files from the given directory. + boosted_tcp_server(); + explicit boosted_tcp_server(boost::asio::io_service& external_io_service); + ~boosted_tcp_server(); + + bool init_server(uint32_t port, const std::string address = "0.0.0.0"); + bool init_server(const std::string port, const std::string& address = "0.0.0.0"); + + /// Run the server's io_service loop. + bool run_server(size_t threads_count, bool wait = true); + + /// wait for service workers stop + bool timed_wait_server_stop(boost::uint64_t wait_mseconds); + + /// Stop the server. + void send_stop_signal(); + + bool is_stop_signal_sent(); + + void set_threads_prefix(const std::string& prefix_name); + + bool deinit_server(){return true;} + + size_t get_threads_count(){return m_threads_count;} + + void set_connection_filter(i_connection_filter* pfilter); + + bool connect(const std::string& adr, const std::string& port, boost::uint32_t conn_timeot, t_connection_context& cn, const std::string& bind_ip = "0.0.0.0"); + template + bool connect_async(const std::string& adr, const std::string& port, boost::uint32_t conn_timeot, t_callback cb, const std::string& bind_ip = "0.0.0.0"); + + typename t_protocol_handler::config_type& get_config_object(){return m_config;} + + int get_binded_port(){return m_port;} + + boost::asio::io_service& get_io_service(){return io_service_;} + + struct idle_callback_conext_base + { + virtual ~idle_callback_conext_base(){} + + virtual bool call_handler(){return true;} + + idle_callback_conext_base(boost::asio::io_service& io_serice): + m_timer(io_serice) + {} + boost::asio::deadline_timer m_timer; + boost::uint64_t m_period; + }; + + template + struct idle_callback_conext: public idle_callback_conext_base + { + idle_callback_conext(boost::asio::io_service& io_serice, t_handler& h, boost::uint64_t period): + idle_callback_conext_base(io_serice), + m_handler(h) + {this->m_period = period;} + + t_handler m_handler; + virtual bool call_handler() + { + return m_handler(); + } + }; + + template + bool add_idle_handler(t_handler t_callback, boost::uint64_t timeout_ms) + { + boost::shared_ptr ptr(new idle_callback_conext(io_service_, t_callback, timeout_ms)); + //needed call handler here ?... + ptr->m_timer.expires_from_now(boost::posix_time::milliseconds(ptr->m_period)); + ptr->m_timer.async_wait(boost::bind(&boosted_tcp_server::global_timer_handler, this, ptr)); + return true; + } + + bool global_timer_handler(/*const boost::system::error_code& err, */boost::shared_ptr ptr) + { + //if handler return false - he don't want to be called anymore + if(!ptr->call_handler()) + return true; + ptr->m_timer.expires_from_now(boost::posix_time::milliseconds(ptr->m_period)); + ptr->m_timer.async_wait(boost::bind(&boosted_tcp_server::global_timer_handler, this, ptr)); + return true; + } + + template + bool async_call(t_handler t_callback) + { + io_service_.post(t_callback); + return true; + } + + protected: + typename t_protocol_handler::config_type m_config; + + private: + /// Run the server's io_service loop. + bool worker_thread(); + /// Handle completion of an asynchronous accept operation. + void handle_accept(const boost::system::error_code& e); + + bool is_thread_worker(); + + /// The io_service used to perform asynchronous operations. + std::unique_ptr m_io_service_local_instance; + boost::asio::io_service& io_service_; + + /// Acceptor used to listen for incoming connections. + boost::asio::ip::tcp::acceptor acceptor_; + + /// The next connection to be accepted. + connection_ptr new_connection_; + std::atomic m_stop_signal_sent; + uint32_t m_port; + volatile boost::uint32_t m_sockets_count; + std::string m_address; + std::string m_thread_name_prefix; + size_t m_threads_count; + i_connection_filter* m_pfilter; + std::vector > m_threads; + boost::thread::id m_main_thread_id; + critical_section m_threads_lock; + volatile uint32_t m_thread_index; + }; +} +} + +#include "abstract_tcp_server2.inl" + +#endif \ No newline at end of file diff --git a/contrib/epee/include/net/abstract_tcp_server2.inl b/contrib/epee/include/net/abstract_tcp_server2.inl new file mode 100644 index 000000000..1d6a1662f --- /dev/null +++ b/contrib/epee/include/net/abstract_tcp_server2.inl @@ -0,0 +1,811 @@ +// 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_utils_base.h" +#include +#include +#include +#include +#include +#include +#include +#include "misc_language.h" +#include "pragma_comp_defs.h" + +PRAGMA_WARNING_PUSH +namespace epee +{ +namespace net_utils +{ + /************************************************************************/ + /* */ + /************************************************************************/ +PRAGMA_WARNING_DISABLE_VS(4355) + + template + connection::connection(boost::asio::io_service& io_service, + typename t_protocol_handler::config_type& config, volatile boost::uint32_t& sock_count, i_connection_filter* &pfilter) + : strand_(io_service), + socket_(io_service), + //context(typename boost::value_initialized()) + m_protocol_handler(this, config, context), + m_want_close_connection(0), + m_was_shutdown(0), + m_ref_sockets_count(sock_count), + m_pfilter(pfilter) + { + boost::interprocess::ipcdetail::atomic_inc32(&m_ref_sockets_count); + } +PRAGMA_WARNING_DISABLE_VS(4355) + //--------------------------------------------------------------------------------- + template + connection::~connection() + { + if(!m_was_shutdown) + { + LOG_PRINT_L3("[sock " << socket_.native_handle() << "] Socket destroyed without shutdown."); + shutdown(); + } + + LOG_PRINT_L3("[sock " << socket_.native_handle() << "] Socket destroyed"); + boost::interprocess::ipcdetail::atomic_dec32(&m_ref_sockets_count); + } + //--------------------------------------------------------------------------------- + template + boost::asio::ip::tcp::socket& connection::socket() + { + return socket_; + } + //--------------------------------------------------------------------------------- + template + boost::shared_ptr > connection::safe_shared_from_this() + { + try + { + return connection::shared_from_this(); + } + catch (const boost::bad_weak_ptr&) + { + // It happens when the connection is being deleted + return boost::shared_ptr >(); + } + } + //--------------------------------------------------------------------------------- + template + bool connection::start(bool is_income, bool is_multithreaded) + { + TRY_ENTRY(); + + // Use safe_shared_from_this, because of this is public method and it can be called on the object being deleted + auto self = safe_shared_from_this(); + if(!self) + return false; + + m_is_multithreaded = is_multithreaded; + + boost::system::error_code ec; + auto remote_ep = socket_.remote_endpoint(ec); + CHECK_AND_NO_ASSERT_MES(!ec, false, "Failed to get remote endpoint: " << ec.message() << ':' << ec.value()); + + auto local_ep = socket_.local_endpoint(ec); + CHECK_AND_NO_ASSERT_MES(!ec, false, "Failed to get local endpoint: " << ec.message() << ':' << ec.value()); + + context = boost::value_initialized(); + long ip_ = boost::asio::detail::socket_ops::host_to_network_long(remote_ep.address().to_v4().to_ulong()); + + context.set_details(boost::uuids::random_generator()(), ip_, remote_ep.port(), is_income); + LOG_PRINT_L3("[sock " << socket_.native_handle() << "] new connection from " << print_connection_context_short(context) << + " to " << local_ep.address().to_string() << ':' << local_ep.port() << + ", total sockets objects " << m_ref_sockets_count); + + if(m_pfilter && !m_pfilter->is_remote_ip_allowed(context.m_remote_ip)) + { + LOG_PRINT_L2("[sock " << socket_.native_handle() << "] ip denied " << string_tools::get_ip_string_from_int32(context.m_remote_ip) << ", shutdowning connection"); + close(); + return false; + } + + m_protocol_handler.after_init_connection(); + + socket_.async_read_some(boost::asio::buffer(buffer_), + strand_.wrap( + boost::bind(&connection::handle_read, self, + boost::asio::placeholders::error, + boost::asio::placeholders::bytes_transferred))); + + return true; + + CATCH_ENTRY_L0("connection::start()", false); + } + //--------------------------------------------------------------------------------- + template + bool connection::request_callback() + { + TRY_ENTRY(); + LOG_PRINT_L2("[" << print_connection_context_short(context) << "] request_callback"); + // Use safe_shared_from_this, because of this is public method and it can be called on the object being deleted + auto self = safe_shared_from_this(); + if(!self) + return false; + + strand_.post(boost::bind(&connection::call_back_starter, self)); + CATCH_ENTRY_L0("connection::request_callback()", false); + return true; + } + //--------------------------------------------------------------------------------- + template + boost::asio::io_service& connection::get_io_service() + { + return socket_.get_io_service(); + } + //--------------------------------------------------------------------------------- + template + bool connection::add_ref() + { + TRY_ENTRY(); + LOG_PRINT_L4("[sock " << socket_.native_handle() << "] add_ref"); + CRITICAL_REGION_LOCAL(m_self_refs_lock); + + // Use safe_shared_from_this, because of this is public method and it can be called on the object being deleted + auto self = safe_shared_from_this(); + if(!self) + return false; + if(m_was_shutdown) + return false; + m_self_refs.push_back(self); + return true; + CATCH_ENTRY_L0("connection::add_ref()", false); + } + //--------------------------------------------------------------------------------- + template + bool connection::release() + { + TRY_ENTRY(); + boost::shared_ptr > back_connection_copy; + LOG_PRINT_L4("[sock " << socket_.native_handle() << "] release"); + CRITICAL_REGION_BEGIN(m_self_refs_lock); + CHECK_AND_ASSERT_MES(m_self_refs.size(), false, "[sock " << socket_.native_handle() << "] m_self_refs empty at connection::release() call"); + //erasing from container without additional copy can cause start deleting object, including m_self_refs + back_connection_copy = m_self_refs.back(); + m_self_refs.pop_back(); + CRITICAL_REGION_END(); + return true; + CATCH_ENTRY_L0("connection::release()", false); + } + //--------------------------------------------------------------------------------- + template + void connection::call_back_starter() + { + TRY_ENTRY(); + LOG_PRINT_L2("[" << print_connection_context_short(context) << "] fired_callback"); + m_protocol_handler.handle_qued_callback(); + CATCH_ENTRY_L0("connection::call_back_starter()", void()); + } + //--------------------------------------------------------------------------------- + template + void connection::handle_read(const boost::system::error_code& e, + std::size_t bytes_transferred) + { + TRY_ENTRY(); + LOG_PRINT_L4("[sock " << socket_.native_handle() << "] Async read calledback."); + + if (!e) + { + LOG_PRINT("[sock " << socket_.native_handle() << "] RECV " << bytes_transferred, LOG_LEVEL_4); + bool recv_res = m_protocol_handler.handle_recv(buffer_.data(), bytes_transferred); + if(!recv_res) + { + LOG_PRINT("[sock " << socket_.native_handle() << "] protocol_want_close", LOG_LEVEL_4); + + //some error in protocol, protocol handler ask to close connection + boost::interprocess::ipcdetail::atomic_write32(&m_want_close_connection, 1); + bool do_shutdown = false; + CRITICAL_REGION_BEGIN(m_send_que_lock); + if(!m_send_que.size()) + do_shutdown = true; + CRITICAL_REGION_END(); + if(do_shutdown) + shutdown(); + }else + { + socket_.async_read_some(boost::asio::buffer(buffer_), + strand_.wrap( + boost::bind(&connection::handle_read, connection::shared_from_this(), + boost::asio::placeholders::error, + boost::asio::placeholders::bytes_transferred))); + LOG_PRINT_L4("[sock " << socket_.native_handle() << "]Async read requested."); + } + }else + { + LOG_PRINT_L3("[sock " << socket_.native_handle() << "] Some not success at read: " << e.message() << ':' << e.value()); + if(e.value() != 2) + { + LOG_PRINT_L3("[sock " << socket_.native_handle() << "] Some problems at read: " << e.message() << ':' << e.value()); + shutdown(); + } + } + // If an error occurs then no new asynchronous operations are started. This + // means that all shared_ptr references to the connection object will + // disappear and the object will be destroyed automatically after this + // handler returns. The connection class's destructor closes the socket. + CATCH_ENTRY_L0("connection::handle_read", void()); + } + //--------------------------------------------------------------------------------- + template + bool connection::call_run_once_service_io() + { + TRY_ENTRY(); + if(!m_is_multithreaded) + { + //single thread model, we can wait in blocked call + size_t cnt = socket_.get_io_service().run_one(); + if(!cnt)//service is going to quit + return false; + }else + { + //multi thread model, we can't(!) wait in blocked call + //so we make non blocking call and releasing CPU by calling sleep(0); + //if no handlers were called + //TODO: Maybe we need to have have critical section + event + callback to upper protocol to + //ask it inside(!) critical region if we still able to go in event wait... + size_t cnt = socket_.get_io_service().poll_one(); + if(!cnt) + misc_utils::sleep_no_w(0); + } + + return true; + CATCH_ENTRY_L0("connection::call_run_once_service_io", false); + } + //--------------------------------------------------------------------------------- + template + bool connection::do_send(const void* ptr, size_t cb) + { + TRY_ENTRY(); + // Use safe_shared_from_this, because of this is public method and it can be called on the object being deleted + auto self = safe_shared_from_this(); + if(!self) + return false; + if(m_was_shutdown) + return false; + + LOG_PRINT("[sock " << socket_.native_handle() << "] SEND " << cb, LOG_LEVEL_4); + //some data should be wrote to stream + //request complete + + epee::critical_region_t send_guard(m_send_que_lock); + if(m_send_que.size() > ABSTRACT_SERVER_SEND_QUE_MAX_COUNT) + { + send_guard.unlock(); + LOG_ERROR("send que size is more than ABSTRACT_SERVER_SEND_QUE_MAX_COUNT(" << ABSTRACT_SERVER_SEND_QUE_MAX_COUNT << "), shutting down connection"); + close(); + return false; + } + + m_send_que.resize(m_send_que.size()+1); + m_send_que.back().assign((const char*)ptr, cb); + + if(m_send_que.size() > 1) + { + //active operation should be in progress, nothing to do, just wait last operation callback + }else + { + //no active operation + if(m_send_que.size()!=1) + { + LOG_ERROR("Looks like no active operations, but send que size != 1!!"); + return false; + } + + boost::asio::async_write(socket_, boost::asio::buffer(m_send_que.front().data(), m_send_que.front().size()), + //strand_.wrap( + boost::bind(&connection::handle_write, self, _1, _2) + //) + ); + + LOG_PRINT_L4("[sock " << socket_.native_handle() << "] Async send requested " << m_send_que.front().size()); + } + + return true; + + CATCH_ENTRY_L0("connection::do_send", false); + } + //--------------------------------------------------------------------------------- + template + bool connection::shutdown() + { + // Initiate graceful connection closure. + boost::system::error_code ignored_ec; + socket_.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignored_ec); + m_was_shutdown = true; + m_protocol_handler.release_protocol(); + return true; + } + //--------------------------------------------------------------------------------- + template + bool connection::close() + { + TRY_ENTRY(); + LOG_PRINT_L4("[sock " << socket_.native_handle() << "] Que Shutdown called."); + size_t send_que_size = 0; + CRITICAL_REGION_BEGIN(m_send_que_lock); + send_que_size = m_send_que.size(); + CRITICAL_REGION_END(); + boost::interprocess::ipcdetail::atomic_write32(&m_want_close_connection, 1); + if(!send_que_size) + { + shutdown(); + } + + return true; + CATCH_ENTRY_L0("connection::close", false); + } + //--------------------------------------------------------------------------------- + template + void connection::handle_write(const boost::system::error_code& e, size_t cb) + { + TRY_ENTRY(); + LOG_PRINT_L4("[sock " << socket_.native_handle() << "] Async send calledback " << cb); + + if (e) + { + LOG_PRINT_L0("[sock " << socket_.native_handle() << "] Some problems at write: " << e.message() << ':' << e.value()); + shutdown(); + return; + } + + bool do_shutdown = false; + CRITICAL_REGION_BEGIN(m_send_que_lock); + if(m_send_que.empty()) + { + LOG_ERROR("[sock " << socket_.native_handle() << "] m_send_que.size() == 0 at handle_write!"); + return; + } + + m_send_que.pop_front(); + if(m_send_que.empty()) + { + if(boost::interprocess::ipcdetail::atomic_read32(&m_want_close_connection)) + { + do_shutdown = true; + } + }else + { + //have more data to send + boost::asio::async_write(socket_, boost::asio::buffer(m_send_que.front().data(), m_send_que.front().size()), + //strand_.wrap( + boost::bind(&connection::handle_write, connection::shared_from_this(), _1, _2)); + //); + } + CRITICAL_REGION_END(); + + if(do_shutdown) + { + shutdown(); + } + CATCH_ENTRY_L0("connection::handle_write", void()); + } + /************************************************************************/ + /* */ + /************************************************************************/ + template + boosted_tcp_server::boosted_tcp_server(): + m_io_service_local_instance(new boost::asio::io_service()), + io_service_(*m_io_service_local_instance.get()), + acceptor_(io_service_), + new_connection_(new connection(io_service_, m_config, m_sockets_count, m_pfilter)), + m_stop_signal_sent(false), m_port(0), m_sockets_count(0), m_threads_count(0), m_pfilter(NULL), m_thread_index(0) + { + m_thread_name_prefix = "NET"; + } + + template + boosted_tcp_server::boosted_tcp_server(boost::asio::io_service& extarnal_io_service): + io_service_(extarnal_io_service), + acceptor_(io_service_), + new_connection_(new connection(io_service_, m_config, m_sockets_count, m_pfilter)), + m_stop_signal_sent(false), m_port(0), m_sockets_count(0), m_threads_count(0), m_pfilter(NULL), m_thread_index(0) + { + m_thread_name_prefix = "NET"; + } + //--------------------------------------------------------------------------------- + template + boosted_tcp_server::~boosted_tcp_server() + { + this->send_stop_signal(); + timed_wait_server_stop(10000); + } + //--------------------------------------------------------------------------------- + template + bool boosted_tcp_server::init_server(uint32_t port, const std::string address) + { + TRY_ENTRY(); + m_stop_signal_sent = false; + m_port = port; + m_address = address; + // Open the acceptor with the option to reuse the address (i.e. SO_REUSEADDR). + boost::asio::ip::tcp::resolver resolver(io_service_); + boost::asio::ip::tcp::resolver::query query(address, boost::lexical_cast(port)); + boost::asio::ip::tcp::endpoint endpoint = *resolver.resolve(query); + acceptor_.open(endpoint.protocol()); + acceptor_.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true)); + acceptor_.bind(endpoint); + acceptor_.listen(); + boost::asio::ip::tcp::endpoint binded_endpoint = acceptor_.local_endpoint(); + m_port = binded_endpoint.port(); + acceptor_.async_accept(new_connection_->socket(), + boost::bind(&boosted_tcp_server::handle_accept, this, + boost::asio::placeholders::error)); + + return true; + CATCH_ENTRY_L0("boosted_tcp_server::init_server", false); + } + //----------------------------------------------------------------------------- +PUSH_WARNINGS +DISABLE_GCC_WARNING(maybe-uninitialized) + template + bool boosted_tcp_server::init_server(const std::string port, const std::string& address) + { + uint32_t p = 0; + + if (port.size() && !string_tools::get_xtype_from_string(p, port)) { + return false; + LOG_ERROR("Failed to convert port no = port"); + } + return this->init_server(p, address); + } +POP_WARNINGS + //--------------------------------------------------------------------------------- + template + bool boosted_tcp_server::worker_thread() + { + TRY_ENTRY(); + uint32_t local_thr_index = boost::interprocess::ipcdetail::atomic_inc32(&m_thread_index); + std::string thread_name = std::string("[") + m_thread_name_prefix; + thread_name += boost::to_string(local_thr_index) + "]"; + log_space::log_singletone::set_thread_log_prefix(thread_name); + while(!m_stop_signal_sent) + { + try + { + io_service_.run(); + } + catch(const std::exception& ex) + { + LOG_ERROR("Exception at server worker thread, what=" << ex.what()); + } + catch(...) + { + LOG_ERROR("Exception at server worker thread, unknown execption"); + } + } + LOG_PRINT_L4("Worker thread finished"); + return true; + CATCH_ENTRY_L0("boosted_tcp_server::worker_thread", false); + } + //--------------------------------------------------------------------------------- + template + void boosted_tcp_server::set_threads_prefix(const std::string& prefix_name) + { + m_thread_name_prefix = prefix_name; + } + //--------------------------------------------------------------------------------- + template + void boosted_tcp_server::set_connection_filter(i_connection_filter* pfilter) + { + m_pfilter = pfilter; + } + //--------------------------------------------------------------------------------- + template + bool boosted_tcp_server::run_server(size_t threads_count, bool wait) + { + TRY_ENTRY(); + m_threads_count = threads_count; + m_main_thread_id = boost::this_thread::get_id(); + log_space::log_singletone::set_thread_log_prefix("[SRV_MAIN]"); + while(!m_stop_signal_sent) + { + + // Create a pool of threads to run all of the io_services. + CRITICAL_REGION_BEGIN(m_threads_lock); + for (std::size_t i = 0; i < threads_count; ++i) + { + boost::shared_ptr thread(new boost::thread( + boost::bind(&boosted_tcp_server::worker_thread, this))); + m_threads.push_back(thread); + } + CRITICAL_REGION_END(); + // Wait for all threads in the pool to exit. + if(wait) + { + for (std::size_t i = 0; i < m_threads.size(); ++i) + m_threads[i]->join(); + m_threads.clear(); + + }else + { + return true; + } + + if(wait && !m_stop_signal_sent) + { + //some problems with the listening socket ?.. + LOG_PRINT_L0("Net service stopped without stop request, restarting..."); + if(!this->init_server(m_port, m_address)) + { + LOG_PRINT_L0("Reiniting service failed, exit."); + return false; + }else + { + LOG_PRINT_L0("Reiniting OK."); + } + } + } + return true; + CATCH_ENTRY_L0("boosted_tcp_server::run_server", false); + } + //--------------------------------------------------------------------------------- + template + bool boosted_tcp_server::is_thread_worker() + { + TRY_ENTRY(); + CRITICAL_REGION_LOCAL(m_threads_lock); + BOOST_FOREACH(boost::shared_ptr& thp, m_threads) + { + if(thp->get_id() == boost::this_thread::get_id()) + return true; + } + if(m_threads_count == 1 && boost::this_thread::get_id() == m_main_thread_id) + return true; + return false; + CATCH_ENTRY_L0("boosted_tcp_server::is_thread_worker", false); + } + //--------------------------------------------------------------------------------- + template + bool boosted_tcp_server::timed_wait_server_stop(boost::uint64_t wait_mseconds) + { + TRY_ENTRY(); + boost::chrono::milliseconds ms(wait_mseconds); + for (std::size_t i = 0; i < m_threads.size(); ++i) + { + if(m_threads[i]->joinable() && !m_threads[i]->try_join_for(ms)) + { + LOG_PRINT_L0("Interrupting thread " << m_threads[i]->native_handle()); + m_threads[i]->interrupt(); + } + } + return true; + CATCH_ENTRY_L0("boosted_tcp_server::timed_wait_server_stop", false); + } + //--------------------------------------------------------------------------------- + template + void boosted_tcp_server::send_stop_signal() + { + m_stop_signal_sent = true; + TRY_ENTRY(); + io_service_.stop(); + CATCH_ENTRY_L0("boosted_tcp_server::send_stop_signal()", void()); + } + //--------------------------------------------------------------------------------- + template + bool boosted_tcp_server::is_stop_signal_sent() + { + return m_stop_signal_sent; + } + //--------------------------------------------------------------------------------- + template + void boosted_tcp_server::handle_accept(const boost::system::error_code& e) + { + TRY_ENTRY(); + if (!e) + { + connection_ptr conn(std::move(new_connection_)); + + new_connection_.reset(new connection(io_service_, m_config, m_sockets_count, m_pfilter)); + acceptor_.async_accept(new_connection_->socket(), + boost::bind(&boosted_tcp_server::handle_accept, this, + boost::asio::placeholders::error)); + + bool r = conn->start(true, 1 < m_threads_count); + if (!r) + LOG_ERROR("[sock " << conn->socket().native_handle() << "] Failed to start connection, connections_count = " << m_sockets_count); + }else + { + LOG_ERROR("Some problems at accept: " << e.message() << ", connections_count = " << m_sockets_count); + } + CATCH_ENTRY_L0("boosted_tcp_server::handle_accept", void()); + } + //--------------------------------------------------------------------------------- + template + bool boosted_tcp_server::connect(const std::string& adr, const std::string& port, boost::uint32_t conn_timeout, t_connection_context& conn_context, const std::string& bind_ip) + { + TRY_ENTRY(); + + connection_ptr new_connection_l(new connection(io_service_, m_config, m_sockets_count, m_pfilter) ); + boost::asio::ip::tcp::socket& sock_ = new_connection_l->socket(); + + ////////////////////////////////////////////////////////////////////////// + boost::asio::ip::tcp::resolver resolver(io_service_); + boost::asio::ip::tcp::resolver::query query(boost::asio::ip::tcp::v4(), adr, port); + boost::asio::ip::tcp::resolver::iterator iterator = resolver.resolve(query); + boost::asio::ip::tcp::resolver::iterator end; + if(iterator == end) + { + LOG_ERROR("Failed to resolve " << adr); + return false; + } + ////////////////////////////////////////////////////////////////////////// + + + //boost::asio::ip::tcp::endpoint remote_endpoint(boost::asio::ip::address::from_string(addr.c_str()), port); + boost::asio::ip::tcp::endpoint remote_endpoint(*iterator); + + sock_.open(remote_endpoint.protocol()); + if(bind_ip != "0.0.0.0" && bind_ip != "0" && bind_ip != "" ) + { + boost::asio::ip::tcp::endpoint local_endpoint(boost::asio::ip::address::from_string(adr.c_str()), 0); + sock_.bind(local_endpoint); + } + + /* + NOTICE: be careful to make sync connection from event handler: in case if all threads suddenly do sync connect, there will be no thread to dispatch events from io service. + */ + + boost::system::error_code ec = boost::asio::error::would_block; + + //have another free thread(s), work in wait mode, without event handling + struct local_async_context + { + boost::system::error_code ec; + boost::mutex connect_mut; + boost::condition_variable cond; + }; + + boost::shared_ptr local_shared_context(new local_async_context()); + local_shared_context->ec = boost::asio::error::would_block; + boost::unique_lock lock(local_shared_context->connect_mut); + auto connect_callback = [](boost::system::error_code ec_, boost::shared_ptr shared_context) + { + shared_context->connect_mut.lock(); shared_context->ec = ec_; shared_context->connect_mut.unlock(); shared_context->cond.notify_one(); + }; + + sock_.async_connect(remote_endpoint, boost::bind(connect_callback, _1, local_shared_context)); + while(local_shared_context->ec == boost::asio::error::would_block) + { + bool r = local_shared_context->cond.timed_wait(lock, boost::get_system_time() + boost::posix_time::milliseconds(conn_timeout)); + if(local_shared_context->ec == boost::asio::error::would_block && !r) + { + //timeout + sock_.close(); + LOG_PRINT_L3("Failed to connect to " << adr << ":" << port << ", because of timeout (" << conn_timeout << ")"); + return false; + } + } + ec = local_shared_context->ec; + + if (ec || !sock_.is_open()) + { + LOG_PRINT("Some problems at connect, message: " << ec.message(), LOG_LEVEL_3); + return false; + } + + LOG_PRINT_L3("Connected success to " << adr << ':' << port); + + bool r = new_connection_l->start(false, 1 < m_threads_count); + if (r) + { + new_connection_l->get_context(conn_context); + //new_connection_l.reset(new connection(io_service_, m_config, m_sockets_count, m_pfilter)); + } + else + { + LOG_ERROR("[sock " << new_connection_->socket().native_handle() << "] Failed to start connection, connections_count = " << m_sockets_count); + } + + return r; + + CATCH_ENTRY_L0("boosted_tcp_server::connect", false); + } + //--------------------------------------------------------------------------------- + template template + bool boosted_tcp_server::connect_async(const std::string& adr, const std::string& port, boost::uint32_t conn_timeout, t_callback cb, const std::string& bind_ip) + { + TRY_ENTRY(); + connection_ptr new_connection_l(new connection(io_service_, m_config, m_sockets_count, m_pfilter) ); + boost::asio::ip::tcp::socket& sock_ = new_connection_l->socket(); + + ////////////////////////////////////////////////////////////////////////// + boost::asio::ip::tcp::resolver resolver(io_service_); + boost::asio::ip::tcp::resolver::query query(boost::asio::ip::tcp::v4(), adr, port); + boost::asio::ip::tcp::resolver::iterator iterator = resolver.resolve(query); + boost::asio::ip::tcp::resolver::iterator end; + if(iterator == end) + { + LOG_ERROR("Failed to resolve " << adr); + return false; + } + ////////////////////////////////////////////////////////////////////////// + boost::asio::ip::tcp::endpoint remote_endpoint(*iterator); + + sock_.open(remote_endpoint.protocol()); + if(bind_ip != "0.0.0.0" && bind_ip != "0" && bind_ip != "" ) + { + boost::asio::ip::tcp::endpoint local_endpoint(boost::asio::ip::address::from_string(adr.c_str()), 0); + sock_.bind(local_endpoint); + } + + boost::shared_ptr sh_deadline(new boost::asio::deadline_timer(io_service_)); + //start deadline + sh_deadline->expires_from_now(boost::posix_time::milliseconds(conn_timeout)); + sh_deadline->async_wait([=](const boost::system::error_code& error) + { + if(error != boost::asio::error::operation_aborted) + { + LOG_PRINT_L3("Failed to connect to " << adr << ':' << port << ", because of timeout (" << conn_timeout << ")"); + new_connection_l->socket().close(); + } + }); + //start async connect + sock_.async_connect(remote_endpoint, [=](const boost::system::error_code& ec_) + { + t_connection_context conn_context = AUTO_VAL_INIT(conn_context); + boost::system::error_code ignored_ec; + boost::asio::ip::tcp::socket::endpoint_type lep = new_connection_l->socket().local_endpoint(ignored_ec); + if(!ec_) + {//success + if(!sh_deadline->cancel()) + { + cb(conn_context, boost::asio::error::operation_aborted);//this mean that deadline timer already queued callback with cancel operation, rare situation + }else + { + LOG_PRINT_L3("[sock " << new_connection_l->socket().native_handle() << "] Connected success to " << adr << ':' << port << + " from " << lep.address().to_string() << ':' << lep.port()); + bool r = new_connection_l->start(false, 1 < m_threads_count); + if (r) + { + new_connection_l->get_context(conn_context); + cb(conn_context, ec_); + } + else + { + LOG_PRINT_L3("[sock " << new_connection_l->socket().native_handle() << "] Failed to start connection to " << adr << ':' << port); + cb(conn_context, boost::asio::error::fault); + } + } + }else + { + LOG_PRINT_L3("[sock " << new_connection_l->socket().native_handle() << "] Failed to connect to " << adr << ':' << port << + " from " << lep.address().to_string() << ':' << lep.port() << ": " << ec_.message() << ':' << ec_.value()); + cb(conn_context, ec_); + } + }); + return true; + CATCH_ENTRY_L0("boosted_tcp_server::connect_async", false); + } +} +} +PRAGMA_WARNING_POP diff --git a/contrib/epee/include/net/abstract_tcp_server_cp.h b/contrib/epee/include/net/abstract_tcp_server_cp.h new file mode 100644 index 000000000..b6410e120 --- /dev/null +++ b/contrib/epee/include/net/abstract_tcp_server_cp.h @@ -0,0 +1,233 @@ +// 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. +// + + +#ifndef _LEVIN_CP_SERVER_H_ +#define _LEVIN_CP_SERVER_H_ + +#include +#include +#include +#include +#include + +#include "misc_log_ex.h" +//#include "threads_helper.h" +#include "syncobj.h" +#define ENABLE_PROFILING +#include "profile_tools.h" +#include "net_utils_base.h" +#include "pragma_comp_defs.h" + +#define LEVIN_DEFAULT_DATA_BUFF_SIZE 2000 + +namespace epee +{ +namespace net_utils +{ + + template + class cp_server_impl//: public abstract_handler + { + public: + cp_server_impl(/*abstract_handler* phandler = NULL*/); + virtual ~cp_server_impl(); + + bool init_server(int port_no); + bool deinit_server(); + bool run_server(int threads_count = 0); + bool send_stop_signal(); + bool is_stop_signal(); + virtual bool on_net_idle(){return true;} + size_t get_active_connections_num(); + typename TProtocol::config_type& get_config_object(){return m_config;} + private: + enum overlapped_operation_type + { + op_type_recv, + op_type_send, + op_type_stop + }; + + struct io_data_base + { + OVERLAPPED m_overlapped; + WSABUF DataBuf; + overlapped_operation_type m_op_type; + DWORD TotalBuffBytes; + volatile LONG m_is_in_use; + char Buffer[1]; + }; + +PRAGMA_WARNING_PUSH +PRAGMA_WARNING_DISABLE_VS(4355) + template + struct connection: public net_utils::i_service_endpoint + { + connection(typename TProtocol::config_type& ref_config):m_sock(INVALID_SOCKET), m_tprotocol_handler(this, ref_config, context), m_psend_data(NULL), m_precv_data(NULL), m_asked_to_shutdown(0), m_connection_shutwoned(0) + { + } + + //connection():m_sock(INVALID_SOCKET), m_tprotocol_handler(this, m_dummy_config, context), m_psend_data(NULL), m_precv_data(NULL), m_asked_to_shutdown(0), m_connection_shutwoned(0) + //{ + //} + + connection& operator=(const connection& obj) + { + return *this; + } + + bool init_buffers() + { + m_psend_data = (io_data_base*)new char[sizeof(io_data_base) + LEVIN_DEFAULT_DATA_BUFF_SIZE-1]; + m_psend_data->TotalBuffBytes = LEVIN_DEFAULT_DATA_BUFF_SIZE; + m_precv_data = (io_data_base*)new char[sizeof(io_data_base) + LEVIN_DEFAULT_DATA_BUFF_SIZE-1]; + m_precv_data->TotalBuffBytes = LEVIN_DEFAULT_DATA_BUFF_SIZE; + return true; + } + + bool query_shutdown() + { + if(!::InterlockedCompareExchange(&m_asked_to_shutdown, 1, 0)) + { + m_psend_data->m_op_type = op_type_stop; + ::PostQueuedCompletionStatus(m_completion_port, 0, (ULONG_PTR)this, &m_psend_data->m_overlapped); + } + return true; + } + + //bool set_config(typename TProtocol::config_type& config) + //{ + // this->~connection(); + // new(this) connection(config); + // return true; + //} + ~connection() + { + if(m_psend_data) + delete m_psend_data; + + if(m_precv_data) + delete m_precv_data; + } + virtual bool handle_send(const void* ptr, size_t cb) + { + PROFILE_FUNC("[handle_send]"); + if(m_psend_data->TotalBuffBytes < cb) + resize_send_buff((DWORD)cb); + + ZeroMemory(&m_psend_data->m_overlapped, sizeof(OVERLAPPED)); + m_psend_data->DataBuf.len = (u_long)cb;//m_psend_data->TotalBuffBytes; + m_psend_data->DataBuf.buf = m_psend_data->Buffer; + memcpy(m_psend_data->DataBuf.buf, ptr, cb); + m_psend_data->m_op_type = op_type_send; + InterlockedExchange(&m_psend_data->m_is_in_use, 1); + DWORD bytes_sent = 0; + DWORD flags = 0; + int res = 0; + { + PROFILE_FUNC("[handle_send] ::WSASend"); + res = ::WSASend(m_sock, &(m_psend_data->DataBuf), 1, &bytes_sent, flags, &(m_psend_data->m_overlapped), NULL); + } + + if(res == SOCKET_ERROR ) + { + int err = ::WSAGetLastError(); + if(WSA_IO_PENDING == err ) + return true; + } + LOG_ERROR("BIG FAIL: WSASend error code not correct, res=" << res << " last_err=" << err); + ::InterlockedExchange(&m_psend_data->m_is_in_use, 0); + query_shutdown(); + //closesocket(m_psend_data); + return false; + }else if(0 == res) + { + ::InterlockedExchange(&m_psend_data->m_is_in_use, 0); + if(!bytes_sent || bytes_sent != cb) + { + int err = ::WSAGetLastError(); + LOG_ERROR("BIG FAIL: WSASend immediatly complete? but bad results, res=" << res << " last_err=" << err); + query_shutdown(); + return false; + }else + { + return true; + } + } + + return true; + } + bool resize_send_buff(DWORD new_size) + { + if(m_psend_data->TotalBuffBytes >= new_size) + return true; + + delete m_psend_data; + m_psend_data = (io_data_base*)new char[sizeof(io_data_base) + new_size-1]; + m_psend_data->TotalBuffBytes = new_size; + LOG_PRINT("Connection buffer resized up to " << new_size, LOG_LEVEL_3); + return true; + } + + + SOCKET m_sock; + net_utils::connection_context_base context; + TProtocol m_tprotocol_handler; + typename TProtocol::config_type m_dummy_config; + io_data_base* m_precv_data; + io_data_base* m_psend_data; + HANDLE m_completion_port; + volatile LONG m_asked_to_shutdown; + volatile LONG m_connection_shutwoned; + }; +PRAGMA_WARNING_POP + + bool worker_thread_member(); + static unsigned CALLBACK worker_thread(void* param); + + bool add_new_connection(SOCKET new_sock, long ip_from, int port_from); + bool shutdown_connection(connection* pconn); + + + typedef std::map > > connections_container; + SOCKET m_listen_socket; + HANDLE m_completion_port; + connections_container m_connections; + critical_section m_connections_lock; + int m_port; + volatile LONG m_stop; + //abstract_handler* m_phandler; + bool m_initialized; + volatile LONG m_worker_thread_counter; + typename TProtocol::config_type m_config; + }; +} +} +#include "abstract_tcp_server_cp.inl" + + +#endif //_LEVIN_SERVER_H_ diff --git a/contrib/epee/include/net/abstract_tcp_server_cp.inl b/contrib/epee/include/net/abstract_tcp_server_cp.inl new file mode 100644 index 000000000..5673c50be --- /dev/null +++ b/contrib/epee/include/net/abstract_tcp_server_cp.inl @@ -0,0 +1,605 @@ +// 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. +// + + +#pragma comment(lib, "Ws2_32.lib") + +namespace epee +{ +namespace net_utils +{ +template +cp_server_impl::cp_server_impl(): + m_port(0), m_stop(false), + m_worker_thread_counter(0), m_listen_socket(INVALID_SOCKET) +{ +} +//------------------------------------------------------------- +template +cp_server_impl::~cp_server_impl() +{ + deinit_server(); +} +//------------------------------------------------------------- +template +bool cp_server_impl::init_server(int port_no) +{ + m_port = port_no; + + WSADATA wsad = {0}; + int err = ::WSAStartup(MAKEWORD(2,2), &wsad); + if ( err != 0 || LOBYTE( wsad.wVersion ) != 2 || HIBYTE( wsad.wVersion ) != 2 ) + { + LOG_ERROR("Could not find a usable WinSock DLL, err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\""); + return false; + } + + m_initialized = true; + + m_listen_socket = ::WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED); + if(INVALID_SOCKET == m_listen_socket) + { + err = ::WSAGetLastError(); + LOG_ERROR("Failed to create socket, err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\""); + return false; + } + + + int opt = 1; + err = setsockopt (m_listen_socket, SOL_SOCKET,SO_REUSEADDR, reinterpret_cast(&opt), sizeof(int)); + if(SOCKET_ERROR == err ) + { + err = ::WSAGetLastError(); + LOG_PRINT("Failed to setsockopt(SO_REUSEADDR), err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\"", LOG_LEVEL_1); + deinit_server(); + return false; + } + + + sockaddr_in adr = {0}; + adr.sin_family = AF_INET; + adr.sin_addr.s_addr = htonl(INADDR_ANY); + adr.sin_port = (u_short)htons(m_port); + + //binding + err = bind(m_listen_socket, (const sockaddr*)&adr, sizeof(adr )); + if(SOCKET_ERROR == err ) + { + err = ::WSAGetLastError(); + LOG_PRINT("Failed to Bind, err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\"", LOG_LEVEL_1); + deinit_server(); + return false; + } + + + m_completion_port = ::CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0); + if(INVALID_HANDLE_VALUE == m_completion_port) + { + err = ::WSAGetLastError(); + LOG_PRINT("Failed to CreateIoCompletionPort, err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\"", LOG_LEVEL_1); + deinit_server(); + return false; + } + + + return true; +} +//------------------------------------------------------------- + +//------------------------------------------------------------- +static int CALLBACK CPConditionFunc( + IN LPWSABUF lpCallerId, + IN LPWSABUF lpCallerData, + IN OUT LPQOS lpSQOS, + IN OUT LPQOS lpGQOS, + IN LPWSABUF lpCalleeId, + OUT LPWSABUF lpCalleeData, + OUT GROUP FAR *g, + IN DWORD_PTR dwCallbackData + ) +{ + + /*cp_server_impl* pthis = (cp_server_impl*)dwCallbackData; + if(!pthis) + return CF_REJECT;*/ + /*if(pthis->get_active_connections_num()>=FD_SETSIZE-1) + { + LOG_PRINT("Maximum connections count overfull.", LOG_LEVEL_2); + return CF_REJECT; + }*/ + + return CF_ACCEPT; +} +//------------------------------------------------------------- +template +size_t cp_server_impl::get_active_connections_num() +{ + return m_connections.size(); +} +//------------------------------------------------------------- +template +unsigned CALLBACK cp_server_impl::worker_thread(void* param) +{ + if(!param) + return 0; + + cp_server_impl* pthis = (cp_server_impl*)param; + pthis->worker_thread_member(); + return 1; +} +//------------------------------------------------------------- +template +bool cp_server_impl::worker_thread_member() +{ + LOG_PRINT("Worker thread STARTED", LOG_LEVEL_1); + bool stop_handling = false; + while(!stop_handling) + { + PROFILE_FUNC("[worker_thread]Worker Loop"); + DWORD bytes_transfered = 0; + connection* pconnection = 0; + io_data_base* pio_data = 0; + + { + PROFILE_FUNC("[worker_thread]GetQueuedCompletionStatus"); + BOOL res = ::GetQueuedCompletionStatus (m_completion_port, &bytes_transfered , (PULONG_PTR)&pconnection, (LPOVERLAPPED *)&pio_data, INFINITE); + if (res == 0) + { + // check return code for error + int err = GetLastError(); + LOG_PRINT("GetQueuedCompletionStatus failed with error " << err << " " << log_space::get_win32_err_descr(err), LOG_LEVEL_1); + + if(pio_data) + ::InterlockedExchange(&pio_data->m_is_in_use, 0); + + + continue; + } + } + + if(pio_data) + ::InterlockedExchange(&pio_data->m_is_in_use, 0); + + + + if(!bytes_transfered && !pconnection && !pio_data) + { + //signal to stop + break; + } + if(!pconnection || !pio_data) + { + LOG_PRINT("BIG FAIL: pconnection or pio_data is empty: pconnection=" << pconnection << " pio_data=" << pio_data, LOG_LEVEL_0); + break; + } + + + + if(::InterlockedCompareExchange(&pconnection->m_connection_shutwoned, 0, 0)) + { + LOG_ERROR("InterlockedCompareExchange(&pconnection->m_connection_shutwoned, 0, 0)"); + //DebugBreak(); + } + + if(pio_data->m_op_type == op_type_stop) + { + if(!pconnection) + { + LOG_ERROR("op_type=op_type_stop, but pconnection is empty!!!"); + continue; + } + shutdown_connection(pconnection); + continue;// + } + else if(pio_data->m_op_type == op_type_send) + { + continue; + //do nothing, just queuing request + }else if(pio_data->m_op_type == op_type_recv) + { + PROFILE_FUNC("[worker_thread]m_tprotocol_handler.handle_recv"); + if(bytes_transfered) + { + bool res = pconnection->m_tprotocol_handler.handle_recv(pio_data->Buffer, bytes_transfered); + if(!res) + pconnection->query_shutdown(); + } + else + { + pconnection->query_shutdown(); + continue; + } + + } + + //preparing new request, + + { + PROFILE_FUNC("[worker_thread]RECV Request small loop"); + int res = 0; + while(true) + { + LOG_PRINT("Prepearing data for WSARecv....", LOG_LEVEL_3); + ZeroMemory(&pio_data->m_overlapped, sizeof(OVERLAPPED)); + pio_data->DataBuf.len = pio_data->TotalBuffBytes; + pio_data->DataBuf.buf = pio_data->Buffer; + pio_data->m_op_type = op_type_recv; + //calling WSARecv() and go to completion waiting + DWORD bytes_recvd = 0; + DWORD flags = 0; + + LOG_PRINT("Calling WSARecv....", LOG_LEVEL_3); + ::InterlockedExchange(&pio_data->m_is_in_use, 1); + res = WSARecv(pconnection->m_sock, &(pio_data->DataBuf), 1, &bytes_recvd , &flags, &(pio_data->m_overlapped), NULL); + if(res == SOCKET_ERROR ) + { + int err = ::WSAGetLastError(); + if(WSA_IO_PENDING == err ) + {//go pending, ok + LOG_PRINT("WSARecv return WSA_IO_PENDING", LOG_LEVEL_3); + break; + } + LOG_ERROR("BIG FAIL: WSARecv error code not correct, res=" << res << " last_err=" << err); + ::InterlockedExchange(&pio_data->m_is_in_use, 0); + pconnection->query_shutdown(); + break; + } + break; + /*else if(0 == res) + { + if(!bytes_recvd) + { + ::InterlockedExchange(&pio_data->m_is_in_use, 0); + LOG_PRINT("WSARecv return 0, bytes_recvd=0, graceful close.", LOG_LEVEL_3); + int err = ::WSAGetLastError(); + //LOG_ERROR("BIG FAIL: WSARecv error code not correct, res=" << res << " last_err=" << err); + //pconnection->query_shutdown(); + break; + }else + { + LOG_PRINT("WSARecv return immediatily 0, bytes_recvd=" << bytes_recvd, LOG_LEVEL_3); + //pconnection->m_tprotocol_handler.handle_recv(pio_data->Buffer, bytes_recvd); + } + }*/ + } + } + } + + + LOG_PRINT("Worker thread STOPED", LOG_LEVEL_1); + ::InterlockedDecrement(&m_worker_thread_counter); + return true; +} +//------------------------------------------------------------- +template +bool cp_server_impl::shutdown_connection(connection* pconn) +{ + PROFILE_FUNC("[shutdown_connection]"); + + if(!pconn) + { + LOG_ERROR("Attempt to remove null pptr connection!"); + return false; + } + else + { + LOG_PRINT("Shutting down connection ("<< pconn << ")", LOG_LEVEL_3); + } + m_connections_lock.lock(); + connections_container::iterator it = m_connections.find(pconn->m_sock); + m_connections_lock.unlock(); + if(it == m_connections.end()) + { + LOG_ERROR("Failed to find closing socket=" << pconn->m_sock); + return false; + } + SOCKET sock = it->second->m_sock; + { + PROFILE_FUNC("[shutdown_connection] shutdown, close"); + ::shutdown(it->second->m_sock, SD_SEND ); + } + size_t close_sock_wait_count = 0; + { + LOG_PRINT("Entered to 'in_use wait zone'", LOG_LEVEL_3); + PROFILE_FUNC("[shutdown_connection] wait for in_use"); + while(::InterlockedCompareExchange(&it->second->m_precv_data->m_is_in_use, 1, 1)) + { + + Sleep(100); + close_sock_wait_count++; + } + LOG_PRINT("First step to 'in_use wait zone'", LOG_LEVEL_3); + + + while(::InterlockedCompareExchange(&it->second->m_psend_data->m_is_in_use, 1, 1)) + { + Sleep(100); + close_sock_wait_count++; + } + LOG_PRINT("Leaved 'in_use wait zone'", LOG_LEVEL_3); + } + + ::closesocket(it->second->m_sock); + + ::InterlockedExchange(&it->second->m_connection_shutwoned, 1); + m_connections_lock.lock(); + m_connections.erase(it); + m_connections_lock.unlock(); + LOG_PRINT("Socked " << sock << " closed, wait_count=" << close_sock_wait_count, LOG_LEVEL_2); + return true; +} +//------------------------------------------------------------- +template +bool cp_server_impl::run_server(int threads_count = 0) +{ + int err = listen(m_listen_socket, 100); + if(SOCKET_ERROR == err ) + { + err = ::WSAGetLastError(); + LOG_ERROR("Failed to listen, err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\""); + return false; + } + + if(!threads_count) + { + SYSTEM_INFO si = {0}; + ::GetSystemInfo(&si); + threads_count = si.dwNumberOfProcessors + 2; + } + for(int i = 0; i != threads_count; i++) + { + boost::thread(boost::bind(&cp_server_impl::worker_thread_member, this)); + //HANDLE h_thread = threads_helper::create_thread(worker_thread, this); + InterlockedIncrement(&m_worker_thread_counter); + //::CloseHandle(h_thread); + } + + LOG_PRINT("Numbers of worker threads started: " << threads_count, LOG_LEVEL_1); + + m_stop = false; + while(!m_stop) + { + PROFILE_FUNC("[run_server] main_loop"); + TIMEVAL tv = {0}; + tv.tv_sec = 0; + tv.tv_usec = 100; + fd_set sock_set; + sock_set.fd_count = 1; + sock_set.fd_array[0] = m_listen_socket; + int select_res = 0; + { + PROFILE_FUNC("[run_server] select"); + select_res = select(0, &sock_set, &sock_set, NULL, &tv); + } + + if(SOCKET_ERROR == select_res) + { + err = ::WSAGetLastError(); + LOG_ERROR("Failed to select, err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\""); + return false; + } + if(!select_res) + { + on_net_idle(); + continue; + } + else + { + sockaddr_in adr_from = {0}; + int adr_len = sizeof(adr_from); + SOCKET new_sock = INVALID_SOCKET; + { + PROFILE_FUNC("[run_server] WSAAccept"); + new_sock = ::WSAAccept(m_listen_socket, (sockaddr *)&adr_from, &adr_len, CPConditionFunc, (DWORD_PTR)this); + } + + if(INVALID_SOCKET == new_sock) + { + if(m_stop) + break; + int err = ::WSAGetLastError(); + LOG_PRINT("Failed to WSAAccept, err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\"", LOG_LEVEL_2); + continue; + } + LOG_PRINT("Accepted connection (new socket=" << new_sock << ")", LOG_LEVEL_2); + { + PROFILE_FUNC("[run_server] Add new connection"); + add_new_connection(new_sock, adr_from.sin_addr.s_addr, adr_from.sin_port); + } + + } + + } + LOG_PRINT("Closing connections("<< m_connections.size() << ") and waiting...", LOG_LEVEL_2); + m_connections_lock.lock(); + for(connections_container::iterator it = m_connections.begin(); it != m_connections.end(); it++) + { + ::shutdown(it->second->m_sock, SD_BOTH); + ::closesocket(it->second->m_sock); + } + m_connections_lock.unlock(); + size_t wait_count = 0; + while(m_connections.size() && wait_count < 100) + { + ::Sleep(100); + wait_count++; + } + LOG_PRINT("Connections closed OK (wait_count=" << wait_count << ")", LOG_LEVEL_2); + + + LOG_PRINT("Stopping worker threads("<< m_worker_thread_counter << ").", LOG_LEVEL_2); + for(int i = 0; i > ptr; + ptr.reset(new connection(m_config)); + + connection& conn = *ptr.get(); + m_connections[new_sock] = ptr; + LOG_PRINT("Add new connection zone: leaving lock", LOG_LEVEL_3); + m_connections_lock.unlock(); + conn.init_buffers(); + conn.m_sock = new_sock; + conn.context.m_remote_ip = ip_from; + conn.context.m_remote_port = port_from; + conn.m_completion_port = m_completion_port; + { + PROFILE_FUNC("[add_new_connection] CreateIoCompletionPort"); + ::CreateIoCompletionPort((HANDLE)new_sock, m_completion_port, (ULONG_PTR)&conn, 0); + } + + //if(NULL == ::CreateIoCompletionPort((HANDLE)new_sock, m_completion_port, (ULONG_PTR)&conn, 0)) + //{ + // int err = ::GetLastError(); + // LOG_PRINT("Failed to CreateIoCompletionPort(associate socket and completion port), err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\"", LOG_LEVEL_2); + // return false; + //} + + conn.m_tprotocol_handler.after_init_connection(); + { + PROFILE_FUNC("[add_new_connection] starting loop"); + int res = 0; + while(true)//res!=SOCKET_ERROR) + { + PROFILE_FUNC("[add_new_connection] in loop time"); + conn.m_precv_data->TotalBuffBytes = LEVIN_DEFAULT_DATA_BUFF_SIZE; + ZeroMemory(&conn.m_precv_data->m_overlapped, sizeof(OVERLAPPED)); + conn.m_precv_data->DataBuf.len = conn.m_precv_data->TotalBuffBytes; + conn.m_precv_data->DataBuf.buf = conn.m_precv_data->Buffer; + conn.m_precv_data->m_op_type = op_type_recv; + InterlockedExchange(&conn.m_precv_data->m_is_in_use, 1); + DWORD bytes_recvd = 0; + DWORD flags = 0; + + ::InterlockedExchange(&conn.m_precv_data->m_is_in_use, 1); + { + PROFILE_FUNC("[add_new_connection] ::WSARecv"); + res = ::WSARecv(conn.m_sock, &(conn.m_precv_data->DataBuf), 1, &bytes_recvd , &flags, &(conn.m_precv_data->m_overlapped), NULL); + } + if(res == SOCKET_ERROR ) + { + int err = ::WSAGetLastError(); + if(WSA_IO_PENDING == err ) + { + break; + } + LOG_ERROR("BIG FAIL: WSARecv error code not correct, res=" << res << " last_err=" << err << " " << log_space::get_win32_err_descr(err)); + ::InterlockedExchange(&conn.m_precv_data->m_is_in_use, 0); + conn.query_shutdown(); + //shutdown_connection(&conn); + break; + } + + + break; + /*else if(0 == res) + { + if(!bytes_recvd) + { + PROFILE_FUNC("[add_new_connection] shutdown_connection"); + ::InterlockedExchange(&conn.m_precv_data->m_is_in_use, 0); + conn.query_shutdown(); + //shutdown_connection(&conn); + break; + }else + { + PROFILE_FUNC("[add_new_connection] handle_recv"); + } + }*/ + } + } + + + + return true; +} +//------------------------------------------------------------- +template +bool cp_server_impl::deinit_server() +{ + if(!m_initialized) + return true; + + if(INVALID_SOCKET != m_listen_socket) + { + shutdown(m_listen_socket, SD_BOTH); + int res = closesocket(m_listen_socket); + if(SOCKET_ERROR == res) + { + int err = ::WSAGetLastError(); + LOG_ERROR("Failed to closesocket(), err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\""); + } + m_listen_socket = INVALID_SOCKET; + } + + int res = ::WSACleanup(); + if(SOCKET_ERROR == res) + { + int err = ::WSAGetLastError(); + LOG_ERROR("Failed to WSACleanup(), err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\""); + } + m_initialized = false; + + return true; +} + +//------------------------------------------------------------- +template +bool cp_server_impl::send_stop_signal() +{ + ::InterlockedExchange(&m_stop, 1); + return true; +} +//------------------------------------------------------------- +template +bool cp_server_impl::is_stop_signal() +{ + return m_stop?true:false; +} +//------------------------------------------------------------- +} +} \ No newline at end of file diff --git a/contrib/epee/include/net/http_base.h b/contrib/epee/include/net/http_base.h new file mode 100644 index 000000000..6de537a4c --- /dev/null +++ b/contrib/epee/include/net/http_base.h @@ -0,0 +1,184 @@ +// 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. +// + + + +#pragma once +#include +#include + +#include "string_tools.h" +namespace epee +{ +namespace net_utils +{ + namespace http + { + + enum http_method{ + http_method_get, + http_method_post, + http_method_put, + http_method_head, + http_method_etc, + 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 > fields_list; + + inline + 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; + } + + + inline + 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(); + } + + + + struct http_header_info + { + std::string m_connection; //"Connection:" + std::string m_referer; //"Referer:" + std::string m_content_length; //"Content-Length:" + std::string m_content_type; //"Content-Type:" + std::string m_transfer_encoding;//"Transfer-Encoding:" + std::string m_content_encoding; //"Content-Encoding:" + std::string m_host; //"Host:" + std::string m_cookie; //"Cookie:" + fields_list m_etc_fields; + + void clear() + { + m_connection.clear(); + m_referer.clear(); + m_content_length.clear(); + m_content_type.clear(); + m_transfer_encoding.clear(); + m_content_encoding.clear(); + m_host.clear(); + m_cookie.clear(); + m_etc_fields.clear(); + } + }; + + struct uri_content + { + std::string m_path; + std::string m_query; + std::string m_fragment; + std::list > m_query_params; + }; + + struct url_content + { + std::string schema; + std::string host; + std::string uri; + boost::uint64_t port; + uri_content m_uri_content; + }; + + + struct http_request_info + { + http_request_info():m_http_method(http_method_unknown), + m_http_ver_hi(0), + m_http_ver_lo(0), + m_have_to_block(false) + {} + + http_method m_http_method; + std::string m_URI; + std::string m_http_method_str; + std::string m_full_request_str; + std::string m_replace_html; + std::string m_request_head; + int m_http_ver_hi; + int m_http_ver_lo; + bool m_have_to_block; + http_header_info m_header_info; + uri_content m_uri_content; + size_t m_full_request_buf_size; + std::string m_body; + + void clear() + { + this->~http_request_info(); + new(this) http_request_info(); + } + }; + + + struct http_response_info + { + int m_response_code; + std::string m_response_comment; + fields_list m_additional_fields; + std::string m_body; + std::string m_mime_tipe; + http_header_info m_header_info; + int m_http_ver_hi;// OUT paramter only + int m_http_ver_lo;// OUT paramter only + + void clear() + { + this->~http_response_info(); + new(this) http_response_info(); + } + }; + } +} +} \ No newline at end of file diff --git a/contrib/epee/include/net/http_client.h b/contrib/epee/include/net/http_client.h new file mode 100644 index 000000000..4a88c06ef --- /dev/null +++ b/contrib/epee/include/net/http_client.h @@ -0,0 +1,875 @@ +// 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. +// + + + +#pragma once +#include +#include +#include +//#include +#include +#include +#include + +#include "net_helper.h" +#include "http_client_base.h" + +#ifdef HTTP_ENABLE_GZIP +#include "gzip_encoding.h" +#endif + +#include "string_tools.h" +#include "reg_exp_definer.h" +#include "http_base.h" +#include "to_nonconst_iterator.h" +#include "net_parse_helpers.h" + +//#include "shlwapi.h" + +//#pragma comment(lib, "shlwapi.lib") + +extern epee::critical_section gregexp_lock; + + +namespace epee +{ +namespace net_utils +{ + +using namespace std; + + /*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(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(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_; + };*/ + + + + + //--------------------------------------------------------------------------- + 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'}; + return hexVals; + } + + static inline const char* get_unsave_chars() + { + //static char unsave_chars[] = "\"<>%\\^[]`+$,@:;/!#?=&"; + static char unsave_chars[] = "\"<>%\\^[]`+$,@:;!#&"; + return unsave_chars; + } + + static inline bool is_unsafe(unsigned char compare_char) + { + if(compare_char <= 32 || compare_char >= 123) + return true; + + const char* punsave = get_unsave_chars(); + + for(int ichar_pos = 0; 0!=punsave[ichar_pos] ;ichar_pos++) + if(compare_char == punsave[ichar_pos]) + return true; + + return false; + } + + static inline + std::string dec_to_hex(char num, int radix) + { + int temp=0; + std::string csTmp; + int num_char; + + num_char = (int) num; + if (num_char < 0) + num_char = 256 + num_char; + + while (num_char >= radix) + { + temp = num_char % radix; + num_char = (int)floor((float)num_char / (float)radix); + csTmp = get_hex_vals()[temp]; + } + + csTmp += get_hex_vals()[num_char]; + + if(csTmp.size() < 2) + { + csTmp += '0'; + } + + std::reverse(csTmp.begin(), csTmp.end()); + //_mbsrev((unsigned char*)csTmp.data()); + + return csTmp; + } + + static inline std::string convert(char val) + { + std::string csRet; + csRet += "%"; + csRet += dec_to_hex(val, 16); + return csRet; + } + static inline std::string conver_to_url_format(const std::string& uri) + { + + std::string result; + + for(size_t i = 0; i!= uri.size(); i++) + { + if(is_unsafe(uri[i])) + result += convert(uri[i]); + else + result += uri[i]; + + } + + return result; + } + + static inline std::string convert_to_url_format_force_all(const std::string& uri) + { + + std::string result; + + for(size_t i = 0; i!= uri.size(); i++) + { + result += convert(uri[i]); + } + + return result; + } + + + + + + namespace http + { + + class http_simple_client: public i_target_handler + { + public: + + + private: + enum reciev_machine_state + { + reciev_machine_state_header, + reciev_machine_state_body_content_len, + reciev_machine_state_body_connection_close, + reciev_machine_state_body_chunked, + reciev_machine_state_done, + reciev_machine_state_error + }; + + + + enum chunked_state{ + http_chunked_state_chunk_head, + http_chunked_state_chunk_body, + http_chunked_state_done, + http_chunked_state_undefined + }; + + + blocked_mode_client m_net_client; + std::string m_host_buff; + std::string m_port; + unsigned int m_timeout; + std::string m_header_cache; + 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 m_pcontent_encoding_handler; + reciev_machine_state m_state; + chunked_state m_chunked_state; + std::string m_chunked_cache; + critical_section m_lock; + + public: + void set_host_name(const std::string& name) + { + CRITICAL_REGION_LOCAL(m_lock); + m_host_buff = name; + } + bool connect(const std::string& host, int port, unsigned int timeout) + { + return connect(host, std::to_string(port), timeout); + } + bool connect(const std::string& host, const std::string& port, unsigned int timeout) + { + CRITICAL_REGION_LOCAL(m_lock); + m_host_buff = host; + m_port = port; + m_timeout = timeout; + + return m_net_client.connect(host, port, timeout, timeout); + } + //--------------------------------------------------------------------------- + bool disconnect() + { + CRITICAL_REGION_LOCAL(m_lock); + return m_net_client.disconnect(); + } + //--------------------------------------------------------------------------- + bool is_connected() + { + CRITICAL_REGION_LOCAL(m_lock); + return m_net_client.is_connected(); + } + //--------------------------------------------------------------------------- + virtual bool handle_target_data(std::string& piece_of_transfer) + { + CRITICAL_REGION_LOCAL(m_lock); + m_response_info.m_body += piece_of_transfer; + piece_of_transfer.clear(); + return true; + } + //--------------------------------------------------------------------------- + inline + bool invoke_get(const std::string& uri, const std::string& body = std::string(), const http_response_info** ppresponse_info = NULL, const fields_list& additional_params = fields_list()) + { + CRITICAL_REGION_LOCAL(m_lock); + return invoke(uri, "GET", body, ppresponse_info, additional_params); + } + + //--------------------------------------------------------------------------- + inline bool invoke(const std::string& uri, const std::string& method, const std::string& body, const http_response_info** ppresponse_info = NULL, const fields_list& additional_params = fields_list()) + { + CRITICAL_REGION_LOCAL(m_lock); + if(!is_connected()) + { + LOG_PRINT("Reconnecting...", LOG_LEVEL_3); + if(!connect(m_host_buff, m_port, m_timeout)) + { + LOG_PRINT("Failed to connect to " << m_host_buff << ":" << m_port, LOG_LEVEL_3); + return false; + } + } + m_response_info.clear(); + std::string req_buff = method + " "; + req_buff += uri + " HTTP/1.1\r\n" + + "Host: "+ m_host_buff +"\r\n" + "Content-Length: " + boost::lexical_cast(body.size()) + "\r\n"; + + + //handle "additional_params" + for(fields_list::const_iterator it = additional_params.begin(); it!=additional_params.end(); it++) + req_buff += it->first + ": " + it->second + "\r\n"; + req_buff += "\r\n"; + //-- + + bool res = m_net_client.send(req_buff); + CHECK_AND_ASSERT_MES(res, false, "HTTP_CLIENT: Failed to SEND"); + if(body.size()) + res = m_net_client.send(body); + CHECK_AND_ASSERT_MES(res, false, "HTTP_CLIENT: Failed to SEND"); + + if(ppresponse_info) + *ppresponse_info = &m_response_info; + + m_state = reciev_machine_state_header; + return handle_reciev(); + } + //--------------------------------------------------------------------------- + inline bool invoke_post(const std::string& uri, const std::string& body, const http_response_info** ppresponse_info = NULL, const fields_list& additional_params = fields_list()) + { + CRITICAL_REGION_LOCAL(m_lock); + return invoke(uri, "POST", body, ppresponse_info, additional_params); + } + private: + //--------------------------------------------------------------------------- + inline bool handle_reciev() + { + CRITICAL_REGION_LOCAL(m_lock); + bool keep_handling = true; + bool need_more_data = true; + std::string recv_buffer; + while(keep_handling) + { + if(need_more_data) + { + if(!m_net_client.recv(recv_buffer)) + { + LOG_PRINT("Unexpected reciec fail", LOG_LEVEL_3); + m_state = reciev_machine_state_error; + } + if(!recv_buffer.size()) + { + //connection is going to be closed + if(reciev_machine_state_body_connection_close != m_state) + { + m_state = reciev_machine_state_error; + } + } + need_more_data = false; + } + switch(m_state) + { + case reciev_machine_state_header: + keep_handling = handle_header(recv_buffer, need_more_data); + break; + case reciev_machine_state_body_content_len: + keep_handling = handle_body_content_len(recv_buffer, need_more_data); + break; + case reciev_machine_state_body_connection_close: + keep_handling = handle_body_connection_close(recv_buffer, need_more_data); + break; + case reciev_machine_state_body_chunked: + keep_handling = handle_body_body_chunked(recv_buffer, need_more_data); + break; + case reciev_machine_state_done: + keep_handling = false; + break; + case reciev_machine_state_error: + keep_handling = false; + break; + } + + } + m_header_cache.clear(); + if(m_state != reciev_machine_state_error) + { + if(m_response_info.m_header_info.m_connection.size() && !string_tools::compare_no_case("close", m_response_info.m_header_info.m_connection)) + disconnect(); + + return true; + } + else + { + LOG_PRINT_L3("Returning false because of wrong state machine. state: " << m_state); + return false; + } + } + //--------------------------------------------------------------------------- + inline + bool handle_header(std::string& recv_buff, bool& need_more_data) + { + + CRITICAL_REGION_LOCAL(m_lock); + if(!recv_buff.size()) + { + LOG_ERROR("Connection closed at handle_header"); + m_state = reciev_machine_state_error; + return false; + } + + m_header_cache += recv_buff; + recv_buff.clear(); + std::string::size_type pos = m_header_cache.find("\r\n\r\n"); + if(pos != std::string::npos) + { + recv_buff.assign(m_header_cache.begin()+pos+4, m_header_cache.end()); + m_header_cache.erase(m_header_cache.begin()+pos+4, m_header_cache.end()); + + analize_cached_header_and_invoke_state(); + m_header_cache.clear(); + if(!recv_buff.size() && (m_state != reciev_machine_state_error && m_state != reciev_machine_state_done)) + need_more_data = true; + + return true; + }else + need_more_data = true; + return true; + } + //--------------------------------------------------------------------------- + inline + bool handle_body_content_len(std::string& recv_buff, bool& need_more_data) + { + CRITICAL_REGION_LOCAL(m_lock); + if(!recv_buff.size()) + { + LOG_PRINT("Warning: Content-Len mode, but connection unexpectedly closed", LOG_LEVEL_3); + m_state = reciev_machine_state_done; + return true; + } + CHECK_AND_ASSERT_MES(m_len_in_remain >= recv_buff.size(), false, "m_len_in_remain >= recv_buff.size()"); + m_len_in_remain -= recv_buff.size(); + m_pcontent_encoding_handler->update_in(recv_buff); + + if(m_len_in_remain == 0) + m_state = reciev_machine_state_done; + else + need_more_data = true; + + return true; + } + //--------------------------------------------------------------------------- + inline + bool handle_body_connection_close(std::string& recv_buff, bool& need_more_data) + { + CRITICAL_REGION_LOCAL(m_lock); + if(!recv_buff.size()) + { + m_state = reciev_machine_state_done; + return true; + } + need_more_data = true; + m_pcontent_encoding_handler->update_in(recv_buff); + + + return true; + } + //--------------------------------------------------------------------------- + inline bool is_hex_symbol(char ch) + { + + if( (ch >= '0' && ch <='9')||(ch >= 'A' && ch <='F')||(ch >= 'a' && ch <='f')) + return true; + else + return false; + } + //--------------------------------------------------------------------------- + inline + bool get_len_from_chunk_head(const std::string &chunk_head, size_t& result_size) + { + std::stringstream str_stream; + str_stream << std::hex; + if(!(str_stream << chunk_head && str_stream >> result_size)) + return false; + + return true; + } + //--------------------------------------------------------------------------- + inline + bool get_chunk_head(std::string& buff, size_t& chunk_size, bool& is_matched) + { + is_matched = false; + size_t offset = 0; + for(std::string::iterator it = buff.begin(); it!= buff.end(); it++, offset++) + { + if(!is_hex_symbol(*it)) + { + if(*it == '\r' || *it == ' ' ) + { + offset--; + continue; + } + else if(*it == '\n') + { + std::string chunk_head = buff.substr(0, offset); + if(!get_len_from_chunk_head(chunk_head, chunk_size)) + return false; + + if(0 == chunk_size) + { + //Here is a small confusion + //In breif - if the chunk is the last one we need to get terminating sequence + //along with the cipher, generally in the "ddd\r\n\r\n" form + + for(it++;it != buff.end(); it++) + { + if('\r' == *it) + continue; + else if('\n' == *it) + break; + else + { + LOG_ERROR("http_stream_filter: Wrong last chunk terminator"); + return false; + } + } + + if(it == buff.end()) + return true; + } + + buff.erase(buff.begin(), ++it); + + is_matched = true; + return true; + } + else + return false; + } + } + + return true; + } + //--------------------------------------------------------------------------- + inline + bool handle_body_body_chunked(std::string& recv_buff, bool& need_more_data) + { + CRITICAL_REGION_LOCAL(m_lock); + if(!recv_buff.size()) + { + LOG_PRINT("Warning: CHUNKED mode, but connection unexpectedly closed", LOG_LEVEL_3); + m_state = reciev_machine_state_done; + return true; + } + m_chunked_cache += recv_buff; + recv_buff.clear(); + bool is_matched = false; + + while(true) + { + if(!m_chunked_cache.size()) + { + need_more_data = true; + break; + } + + switch(m_chunked_state) + { + case http_chunked_state_chunk_head: + if(m_chunked_cache[0] == '\n' || m_chunked_cache[0] == '\r') + { + //optimize a bit + if(m_chunked_cache[0] == '\r' && m_chunked_cache.size()>1 && m_chunked_cache[1] == '\n') + m_chunked_cache.erase(0, 2); + else + m_chunked_cache.erase(0, 1); + break; + } + if(!get_chunk_head(m_chunked_cache, m_len_in_remain, is_matched)) + { + LOG_ERROR("http_stream_filter::handle_chunked(*) Failed to get length from chunked head:" << m_chunked_cache); + m_state = reciev_machine_state_error; + return false; + } + + if(!is_matched) + { + need_more_data = true; + return true; + }else + { + m_chunked_state = http_chunked_state_chunk_body; + if(m_len_in_remain == 0) + {//last chunk, let stop the stream and fix the chunk queue. + m_state = reciev_machine_state_done; + return true; + } + m_chunked_state = http_chunked_state_chunk_body; + break; + } + break; + case http_chunked_state_chunk_body: + { + std::string chunk_body; + if(m_len_in_remain >= m_chunked_cache.size()) + { + m_len_in_remain -= m_chunked_cache.size(); + chunk_body.swap(m_chunked_cache); + }else + { + chunk_body.assign(m_chunked_cache, 0, m_len_in_remain); + m_chunked_cache.erase(0, m_len_in_remain); + m_len_in_remain = 0; + } + + m_pcontent_encoding_handler->update_in(chunk_body); + + if(!m_len_in_remain) + m_chunked_state = http_chunked_state_chunk_head; + } + break; + case http_chunked_state_done: + m_state = reciev_machine_state_done; + return true; + case http_chunked_state_undefined: + default: + LOG_ERROR("http_stream_filter::handle_chunked(): Wrong state" << m_chunked_state); + return false; + } + } + + return true; + } + //--------------------------------------------------------------------------- + inline + bool parse_header(http_header_info& body_info, const std::string& m_cache_to_process) + { + LOG_FRAME("http_stream_filter::parse_cached_header(*)", LOG_LEVEL_4); + + STATIC_REGEXP_EXPR_1(rexp_mach_field, + "\n?((Connection)|(Referer)|(Content-Length)|(Content-Type)|(Transfer-Encoding)|(Content-Encoding)|(Host)|(Cookie)" + // 12 3 4 5 6 7 8 9 + "|([\\w-]+?)) ?: ?((.*?)(\r?\n))[^\t ]", + //10 1112 13 + boost::regex::icase | boost::regex::normal); + + boost::smatch result; + std::string::const_iterator it_current_bound = m_cache_to_process.begin(); + std::string::const_iterator it_end_bound = m_cache_to_process.end(); + + + + //lookup all fields and fill well-known fields + while( boost::regex_search( it_current_bound, it_end_bound, result, rexp_mach_field, boost::match_default) && result[0].matched) + { + const size_t field_val = 12; + //const size_t field_etc_name = 10; + + int i = 2; //start position = 2 + if(result[i++].matched)//"Connection" + body_info.m_connection = result[field_val]; + else if(result[i++].matched)//"Referrer" + body_info.m_referer = result[field_val]; + else if(result[i++].matched)//"Content-Length" + body_info.m_content_length = result[field_val]; + else if(result[i++].matched)//"Content-Type" + body_info.m_content_type = result[field_val]; + else if(result[i++].matched)//"Transfer-Encoding" + body_info.m_transfer_encoding = result[field_val]; + else if(result[i++].matched)//"Content-Encoding" + body_info.m_content_encoding = result[field_val]; + else if(result[i++].matched)//"Host" + { body_info.m_host = result[field_val]; + string_tools::trim(body_info.m_host); + } + else if(result[i++].matched)//"Cookie" + body_info.m_cookie = result[field_val]; + else if(result[i++].matched)//e.t.c (HAVE TO BE MATCHED!) + {;} + else + {CHECK_AND_ASSERT_MES(false, false, "http_stream_filter::parse_cached_header() not matched last entry in:"<(result[1]); + m_response_info.m_http_ver_lo = boost::lexical_cast(result[2]); + m_response_info.m_response_code = boost::lexical_cast(result[3]); + + m_header_cache.erase(to_nonsonst_iterator(m_header_cache, result[0].first), to_nonsonst_iterator(m_header_cache, result[0].second)); + return true; + }else + { + LOG_ERROR("http_stream_filter::handle_invoke_reply_line(): Failed to match first response line:" << m_header_cache); + return false; + } + + } + inline + bool set_reply_content_encoder() + { + STATIC_REGEXP_EXPR_1(rexp_match_gzip, "^.*?((gzip)|(deflate))", boost::regex::icase | boost::regex::normal); + boost::smatch result; // 12 3 + if(boost::regex_search( m_response_info.m_header_info.m_content_encoding, result, rexp_match_gzip, boost::match_default) && result[0].matched) + { +#ifdef HTTP_ENABLE_GZIP + m_pcontent_encoding_handler.reset(new content_encoding_gzip(this, result[3].matched)); +#else + m_pcontent_encoding_handler.reset(new do_nothing_sub_handler(this)); + LOG_ERROR("GZIP encoding not supported in this build, please add zlib to your project and define HTTP_ENABLE_GZIP"); + return false; +#endif + } + else + { + m_pcontent_encoding_handler.reset(new do_nothing_sub_handler(this)); + } + + return true; + } + inline + bool analize_cached_header_and_invoke_state() + { + m_response_info.clear(); + analize_first_response_line(); + std::string fake_str; //gcc error workaround + + bool res = parse_header(m_response_info.m_header_info, m_header_cache); + CHECK_AND_ASSERT_MES(res, false, "http_stream_filter::analize_cached_reply_header_and_invoke_state(): failed to anilize reply header: " << m_header_cache); + + set_reply_content_encoder(); + + m_len_in_summary = 0; + bool content_len_valid = false; + if(m_response_info.m_header_info.m_content_length.size()) + content_len_valid = string_tools::get_xtype_from_string(m_len_in_summary, m_response_info.m_header_info.m_content_length); + + + + if(!m_len_in_summary && ((m_response_info.m_response_code>=100&&m_response_info.m_response_code<200) + || 204 == m_response_info.m_response_code + || 304 == m_response_info.m_response_code) ) + {//There will be no response body, server will display the local page with error + m_state = reciev_machine_state_done; + return true; + }else if(m_response_info.m_header_info.m_transfer_encoding.size()) + { + string_tools::trim(m_response_info.m_header_info.m_transfer_encoding); + if(string_tools::compare_no_case(m_response_info.m_header_info.m_transfer_encoding, "chunked")) + { + LOG_ERROR("Wrong Transfer-Encoding:" << m_response_info.m_header_info.m_transfer_encoding); + m_state = reciev_machine_state_error; + return false; + } + m_state = reciev_machine_state_body_chunked; + m_chunked_state = http_chunked_state_chunk_head; + return true; + } + else if(!m_response_info.m_header_info.m_content_length.empty()) + { + //In the response header the length was specified + if(!content_len_valid) + { + LOG_ERROR("http_stream_filter::analize_cached_reply_header_and_invoke_state(): Failed to get_len_from_content_lenght();, m_query_info.m_content_length="<(u_c.port), timeout); + CHECK_AND_ASSERT_MES(res, false, "failed to connect " << u_c.host << ":" << u_c.port); + } + + return tr.invoke(u_c.uri, method, body, ppresponse_info, additional_params); + } + + } +} +} \ No newline at end of file diff --git a/contrib/epee/include/net/http_client_abstract_invoke.h b/contrib/epee/include/net/http_client_abstract_invoke.h new file mode 100644 index 000000000..425a355ee --- /dev/null +++ b/contrib/epee/include/net/http_client_abstract_invoke.h @@ -0,0 +1,98 @@ + +// 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. +// + +#pragma once +#include "storages/serializeble_struct_helper.h" + +namespace epee +{ + namespace net_utils + { + namespace http + { + template + bool invoke_http_json_remote_command(const std::string& url, TArg& out_struct, TResult& result_struct, TTransport& transport, unsigned int timeout = 5000, const std::string& method = "GET") + { + std::string req_param; + StorageNamed::InMemStorageSpace::json::store_t_to_json(out_struct, req_param); + + const http_response_info* pri = NULL; + if(!invoke_request(url, transport, timeout, &pri, method, req_param)) + { + LOG_PRINT_L1("Failed to invoke http request to " << url); + return false; + } + + if(!pri->m_response_code) + { + LOG_PRINT_L1("Failed to invoke http request to " << url << ", internal error (null response ptr)"); + return false; + } + + if(pri->m_response_code != 200) + { + LOG_PRINT_L1("Failed to invoke http request to " << url << ", wrong response code: " << pri->m_response_code); + return false; + } + + return StorageNamed::InMemStorageSpace::json::load_t_from_json(result_struct, pri->m_body); + } + + + + template + bool invoke_http_bin_remote_command(const std::string& url, TArg& out_struct, TResult& result_struct, TTransport& transport, unsigned int timeout = 5000, const std::string& method = "GET") + { + std::string req_param; + epee::StorageNamed::save_struct_as_storage_to_buff(out_struct, req_param); + + const http_response_info* pri = NULL; + if(!invoke_request(url, transport, timeout, &pri, method, req_param)) + { + LOG_PRINT_L1("Failed to invoke http request to " << url); + return false; + } + + if(!pri->m_response_code) + { + LOG_PRINT_L1("Failed to invoke http request to " << url << ", internal error (null response ptr)"); + return false; + } + + if(pri->m_response_code != 200) + { + LOG_PRINT_L1("Failed to invoke http request to " << url << ", wrong response code: " << pri->m_response_code); + return false; + } + + return epee::StorageNamed::load_struct_from_storage_buff(result_struct, pri->m_body); + } + + + } + } +} diff --git a/contrib/epee/include/net/http_client_base.h b/contrib/epee/include/net/http_client_base.h new file mode 100644 index 000000000..571e27f73 --- /dev/null +++ b/contrib/epee/include/net/http_client_base.h @@ -0,0 +1,73 @@ +// 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. +// + +#pragma once + +namespace epee +{ + namespace net_utils + { + struct i_sub_handler + { + virtual ~i_sub_handler(){} + + virtual bool update_in( std::string& piece_of_transfer)=0; + virtual void stop(std::string& OUT collect_remains)=0; + virtual bool update_and_stop(std::string& OUT collect_remains, bool& is_changed) + { + is_changed = true; + bool res = this->update_in(collect_remains); + if(res) + this->stop(collect_remains); + return res; + } + }; + + + struct i_target_handler + { + virtual ~i_target_handler(){} + virtual bool handle_target_data( std::string& piece_of_transfer)=0; + }; + + + class do_nothing_sub_handler: public i_sub_handler + { + public: + do_nothing_sub_handler(i_target_handler* powner_filter):m_powner_filter(powner_filter) + {} + virtual bool update_in( std::string& piece_of_transfer) + { + return m_powner_filter->handle_target_data(piece_of_transfer); + } + virtual void stop(std::string& OUT collect_remains) + { + + } + i_target_handler* m_powner_filter; + }; + } +} \ No newline at end of file diff --git a/contrib/epee/include/net/http_client_via_api_helper.h b/contrib/epee/include/net/http_client_via_api_helper.h new file mode 100644 index 000000000..45a70993b --- /dev/null +++ b/contrib/epee/include/net/http_client_via_api_helper.h @@ -0,0 +1,177 @@ +// 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. +// + + + + +#pragma once +#include +#include +#pragma comment(lib, "Wininet.lib") + +namespace epee +{ +namespace net_utils +{ + inline + bool http_ssl_invoke(const std::string& url, const std::string usr, const std::string psw, std::string& http_response_body, bool use_post = false) + { + bool final_res = false; + + ATL::CUrl url_obj; + BOOL crack_rss = url_obj.CrackUrl(string_encoding::convert_to_t >(url).c_str()); + + HINTERNET hinet = ::InternetOpenA(SHARED_JOBSCOMMON_HTTP_AGENT, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0); + if(!hinet) + { + int err = ::GetLastError(); + LOG_PRINT("Failed to call InternetOpenA, \nError: " << err << " " << log_space::get_win32_err_descr(err), LOG_LEVEL_0); + return false; + } + + DWORD dwFlags = 0; + DWORD dwBuffLen = sizeof(dwFlags); + + if(usr.size()) + { + dwFlags |= INTERNET_FLAG_IGNORE_CERT_CN_INVALID|INTERNET_FLAG_IGNORE_CERT_DATE_INVALID| + INTERNET_FLAG_PRAGMA_NOCACHE | SECURITY_FLAG_IGNORE_UNKNOWN_CA|INTERNET_FLAG_SECURE; + }else + { + dwFlags |= INTERNET_FLAG_PRAGMA_NOCACHE; + } + + + int port = url_obj.GetPortNumber(); + BOOL res = FALSE; + + HINTERNET hsession = ::InternetConnectA(hinet, string_encoding::convert_to_ansii(url_obj.GetHostName()).c_str(), port/*INTERNET_DEFAULT_HTTPS_PORT*/, usr.c_str(), psw.c_str(), INTERNET_SERVICE_HTTP, dwFlags, NULL); + if(hsession) + { + const std::string uri = string_encoding::convert_to_ansii(url_obj.GetUrlPath()) + string_encoding::convert_to_ansii(url_obj.GetExtraInfo()); + + HINTERNET hrequest = ::HttpOpenRequestA(hsession, use_post?"POST":NULL, uri.c_str(), NULL, NULL,NULL, dwFlags, NULL); + if(hrequest) + { + while(true) + { + res = ::HttpSendRequestA(hrequest, NULL, 0, NULL, 0); + if(!res) + { + //ERROR_INTERNET_INVALID_CA 45 + //ERROR_INTERNET_INVALID_URL (INTERNET_ERROR_BASE + 5) + int err = ::GetLastError(); + LOG_PRINT("Failed to call HttpSendRequestA, \nError: " << log_space::get_win32_err_descr(err), LOG_LEVEL_0); + break; + } + + DWORD code = 0; + DWORD buf_len = sizeof(code); + DWORD index = 0; + res = ::HttpQueryInfo(hrequest, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE, &code, &buf_len, &index); + if(!res) + { + //ERROR_INTERNET_INVALID_CA 45 + //ERROR_INTERNET_INVALID_URL (INTERNET_ERROR_BASE + 5) + int err = ::GetLastError(); + LOG_PRINT("Failed to call HttpQueryInfo, \nError: " << log_space::get_win32_err_descr(err), LOG_LEVEL_0); + break; + } + if(code < 200 || code > 299) + { + LOG_PRINT("Wrong server response, HttpQueryInfo returned statuse code" << code , LOG_LEVEL_0); + break; + } + + + char buff[100000] = {0}; + DWORD readed = 0; + while(true) + { + res = ::InternetReadFile(hrequest, buff, sizeof(buff), &readed); + if(!res) + { + int err = ::GetLastError(); + LOG_PRINT("Failed to call InternetReadFile, \nError: " << log_space::get_win32_err_descr(err), LOG_LEVEL_0); + break; + } + if(readed) + { + http_response_body.append(buff, readed); + } + else + break; + } + + if(!res) + break; + + + //we success + final_res = true; + + res = ::InternetCloseHandle(hrequest); + if(!res) + { + int err = ::GetLastError(); + LOG_PRINT("Failed to call InternetCloseHandle, \nError: " << log_space::get_win32_err_descr(err), LOG_LEVEL_0); + } + + break; + } + } + else + { + //ERROR_INTERNET_INVALID_CA + int err = ::GetLastError(); + LOG_PRINT("Failed to call InternetOpenUrlA, \nError: " << log_space::get_win32_err_descr(err), LOG_LEVEL_0); + return false; + } + + res = ::InternetCloseHandle(hsession); + if(!res) + { + int err = ::GetLastError(); + LOG_PRINT("Failed to call InternetCloseHandle, \nError: " << log_space::get_win32_err_descr(err), LOG_LEVEL_0); + } + }else + { + int err = ::GetLastError(); + LOG_PRINT("Failed to call InternetConnectA(" << string_encoding::convert_to_ansii(url_obj.GetHostName()) << ", port " << port << " \nError: " << log_space::get_win32_err_descr(err), LOG_LEVEL_0); + } + + + + res = ::InternetCloseHandle(hinet); + if(!res) + { + int err = ::GetLastError(); + LOG_PRINT("Failed to call InternetCloseHandle, \nError: " << log_space::get_win32_err_descr(err), LOG_LEVEL_0); + } + return final_res; + } +} +} \ No newline at end of file diff --git a/contrib/epee/include/net/http_protocol_handler.h b/contrib/epee/include/net/http_protocol_handler.h new file mode 100644 index 000000000..4aebcf2aa --- /dev/null +++ b/contrib/epee/include/net/http_protocol_handler.h @@ -0,0 +1,209 @@ +// 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. +// + + + + +#ifndef _HTTP_SERVER_H_ +#define _HTTP_SERVER_H_ + +#include +#include "net_utils_base.h" +#include "to_nonconst_iterator.h" +#include "http_base.h" + +namespace epee +{ +namespace net_utils +{ + namespace http + { + + + /************************************************************************/ + /* */ + /************************************************************************/ + struct http_server_config + { + std::string m_folder; + critical_section m_lock; + }; + + /************************************************************************/ + /* */ + /************************************************************************/ + class simple_http_connection_handler + { + public: + typedef net_utils::connection_context_base connection_context; + typedef http_server_config config_type; + + simple_http_connection_handler(i_service_endpoint* psnd_hndlr, config_type& config); + virtual ~simple_http_connection_handler(){} + + bool release_protocol() + { + return true; + } + + virtual bool thread_init() + { + return true; + } + + virtual bool thread_deinit() + { + return true; + } + bool after_init_connection() + { + return true; + } + virtual bool handle_recv(const void* ptr, size_t cb); + virtual bool handle_request(const http::http_request_info& query_info, http_response_info& response); + + + //temporary here + //bool parse_uri(const std::string uri, uri_content& content); + + private: + enum machine_state{ + http_state_retriving_comand_line, + http_state_retriving_header, + http_state_retriving_body, + http_state_connection_close, + http_state_error + }; + + enum body_transfer_type{ + http_body_transfer_chunked, + http_body_transfer_measure,//mean "Content-Length" valid + http_body_transfer_chunked_instead_measure, + http_body_transfer_connection_close, + http_body_transfer_multipart, + http_body_transfer_undefined + }; + + bool handle_buff_in(std::string& buf); + + bool analize_cached_request_header_and_invoke_state(size_t pos); + + bool handle_invoke_query_line(); + bool parse_cached_header(http_header_info& body_info, const std::string& m_cache_to_process, size_t pos); + std::string::size_type match_end_of_header(const std::string& buf); + bool get_len_from_content_lenght(const std::string& str, size_t& len); + bool handle_retriving_query_body(); + bool handle_query_measure(); + bool set_ready_state(); + bool slash_to_back_slash(std::string& str); + std::string get_file_mime_tipe(const std::string& path); + std::string get_response_header(const http_response_info& response); + + //major function + inline bool handle_request_and_send_response(const http::http_request_info& query_info); + + + std::string get_not_found_response_body(const std::string& URI); + + std::string m_root_path; + std::string m_cache; + machine_state m_state; + body_transfer_type m_body_transfer_type; + bool m_is_stop_handling; + http::http_request_info m_query_info; + size_t m_len_summary, m_len_remain; + config_type& m_config; + bool m_want_close; + protected: + i_service_endpoint* m_psnd_hndlr; + }; + + + struct i_http_server_handler + { + virtual ~i_http_server_handler(){} + virtual bool handle_http_request(const http_request_info& query_info, http_response_info& response, const net_utils::connection_context_base& m_conn_context)=0; + virtual bool init_server_thread(){return true;} + virtual bool deinit_server_thread(){return true;} + }; + + + struct custum_handler_config: public http_server_config + { + i_http_server_handler* m_phandler; + }; + + /************************************************************************/ + /* */ + /************************************************************************/ + class http_custom_handler: public simple_http_connection_handler + { + public: + typedef custum_handler_config config_type; + + http_custom_handler(i_service_endpoint* psnd_hndlr, config_type& config, const net_utils::connection_context_base& conn_context):simple_http_connection_handler(psnd_hndlr, config), + m_config(config), + m_conn_context(conn_context) + {} + inline bool handle_request(const http_request_info& query_info, http_response_info& response) + { + CHECK_AND_ASSERT_MES(m_config.m_phandler, false, "m_config.m_phandler is NULL!!!!"); + //fill with default values + response.m_mime_tipe = "text/plain"; + response.m_response_code = 200; + response.m_response_comment = "OK"; + response.m_body.clear(); + return m_config.m_phandler->handle_http_request(query_info, response, m_conn_context); + } + + virtual bool thread_init() + { + return m_config.m_phandler->init_server_thread();; + } + + virtual bool thread_deinit() + { + return m_config.m_phandler->deinit_server_thread(); + } + void handle_qued_callback() + {} + bool after_init_connection() + { + return true; + } + + private: + //simple_http_connection_handler::config_type m_stub_config; + config_type& m_config; + const net_utils::connection_context_base& m_conn_context; + }; + } +} +} + +#include "http_protocol_handler.inl" + +#endif //_HTTP_SERVER_H_ diff --git a/contrib/epee/include/net/http_protocol_handler.inl b/contrib/epee/include/net/http_protocol_handler.inl new file mode 100644 index 000000000..810c46db9 --- /dev/null +++ b/contrib/epee/include/net/http_protocol_handler.inl @@ -0,0 +1,664 @@ +// 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 +#include +#include "http_protocol_handler.h" +#include "reg_exp_definer.h" +#include "string_tools.h" +#include "file_io_utils.h" +#include "net_parse_helpers.h" + +#define HTTP_MAX_URI_LEN 9000 +#define HTTP_MAX_HEADER_LEN 100000 + +namespace epee +{ +namespace net_utils +{ + namespace http + { + + struct multipart_entry + { + std::list > m_etc_header_fields; + std::string m_content_disposition; + std::string m_content_type; + std::string m_body; + }; + + inline + bool match_boundary(const std::string& content_type, std::string& boundary) + { + STATIC_REGEXP_EXPR_1(rexp_match_boundary, "boundary=(.*?)(($)|([;\\s,]))", boost::regex::icase | boost::regex::normal); + // 1 + boost::smatch result; + if(boost::regex_search(content_type, result, rexp_match_boundary, boost::match_default) && result[0].matched) + { + boundary = result[1]; + return true; + } + + return false; + } + + inline + bool parse_header(std::string::const_iterator it_begin, std::string::const_iterator it_end, multipart_entry& entry) + { + STATIC_REGEXP_EXPR_1(rexp_mach_field, + "\n?((Content-Disposition)|(Content-Type)" + // 12 3 + "|([\\w-]+?)) ?: ?((.*?)(\r?\n))[^\t ]", + //4 56 7 + boost::regex::icase | boost::regex::normal); + + boost::smatch result; + std::string::const_iterator it_current_bound = it_begin; + std::string::const_iterator it_end_bound = it_end; + + //lookup all fields and fill well-known fields + while( boost::regex_search( it_current_bound, it_end_bound, result, rexp_mach_field, boost::match_default) && result[0].matched) + { + const size_t field_val = 6; + const size_t field_etc_name = 4; + + int i = 2; //start position = 2 + if(result[i++].matched)//"Content-Disposition" + entry.m_content_disposition = result[field_val]; + else if(result[i++].matched)//"Content-Type" + entry.m_content_type = result[field_val]; + else if(result[i++].matched)//e.t.c (HAVE TO BE MATCHED!) + entry.m_etc_header_fields.push_back(std::pair(result[field_etc_name], result[field_val])); + else + { + LOG_ERROR("simple_http_connection_handler::parse_header() not matched last entry in:"<& out_values) + { + //bool res = file_io_utils::load_file_to_string("C:\\public\\multupart_data", body); + + std::string boundary; + if(!match_boundary(content_type, boundary)) + { + LOG_PRINT("Failed to match boundary in content type: " << content_type, LOG_LEVEL_0); + return false; + } + + boundary+="\r\n"; + bool is_stop = false; + bool first_step = true; + + std::string::const_iterator it_begin = body.begin(); + std::string::const_iterator it_end; + while(!is_stop) + { + std::string::size_type pos = body.find(boundary, std::distance(body.begin(), it_begin)); + + if(std::string::npos == pos) + { + is_stop = true; + boundary.erase(boundary.size()-2, 2); + boundary+= "--"; + pos = body.find(boundary, std::distance(body.begin(), it_begin)); + if(std::string::npos == pos) + { + LOG_PRINT("Error: Filed to match closing multipart tag", LOG_LEVEL_0); + it_end = body.end(); + }else + { + it_end = body.begin() + pos; + } + }else + it_end = body.begin() + pos; + + + if(first_step && !is_stop) + { + first_step = false; + it_begin = it_end + boundary.size(); + std::string temp = "\r\n--"; + boundary = temp + boundary; + continue; + } + + out_values.push_back(multipart_entry()); + if(!handle_part_of_multipart(it_begin, it_end, out_values.back())) + { + LOG_PRINT("Failed to handle_part_of_multipart", LOG_LEVEL_0); + return false; + } + + it_begin = it_end + boundary.size(); + } + + return true; + } + + + + + //-------------------------------------------------------------------------------------------- + inline + simple_http_connection_handler::simple_http_connection_handler(i_service_endpoint* psnd_hndlr, config_type& config): + m_state(http_state_retriving_comand_line), + m_body_transfer_type(http_body_transfer_undefined), + m_is_stop_handling(false), + m_len_summary(0), + m_len_remain(0), + m_config(config), + m_want_close(false), + m_psnd_hndlr(psnd_hndlr) + { + + } + //-------------------------------------------------------------------------------------------- + inline bool simple_http_connection_handler::set_ready_state() + { + m_is_stop_handling = false; + m_state = http_state_retriving_comand_line; + m_body_transfer_type = http_body_transfer_undefined; + m_query_info.clear(); + m_len_summary = 0; + return true; + } + //-------------------------------------------------------------------------------------------- + inline bool simple_http_connection_handler::handle_recv(const void* ptr, size_t cb) + { + std::string buf((const char*)ptr, cb); + //LOG_PRINT_L0("HTTP_RECV: " << ptr << "\r\n" << buf); + //file_io_utils::save_string_to_file(string_tools::get_current_module_folder() + "/" + boost::lexical_cast(ptr), std::string((const char*)ptr, cb)); + + bool res = handle_buff_in(buf); + if(m_want_close/*m_state == http_state_connection_close || m_state == http_state_error*/) + return false; + return res; + } + //-------------------------------------------------------------------------------------------- + inline bool simple_http_connection_handler::handle_buff_in(std::string& buf) + { + + if(m_cache.size()) + m_cache += buf; + else + m_cache.swap(buf); + + m_is_stop_handling = false; + while(!m_is_stop_handling) + { + switch(m_state) + { + case http_state_retriving_comand_line: + //The HTTP protocol does not place any a priori limit on the length of a URI. (c)RFC2616 + //but we forebly restirct it len to HTTP_MAX_URI_LEN to make it more safely + if(!m_cache.size()) + break; + + //check_and_handle_fake_response(); + if((m_cache[0] == '\r' || m_cache[0] == '\n')) + { + //some times it could be that before query line cold be few line breaks + //so we have to be calm without panic with assers + m_cache.erase(0, 1); + break; + } + + if(std::string::npos != m_cache.find('\n', 0)) + handle_invoke_query_line(); + else + { + m_is_stop_handling = true; + if(m_cache.size() > HTTP_MAX_URI_LEN) + { + LOG_ERROR("simple_http_connection_handler::handle_buff_out: Too long URI line"); + m_state = http_state_error; + return false; + } + } + break; + case http_state_retriving_header: + { + std::string::size_type pos = match_end_of_header(m_cache); + if(std::string::npos == pos) + { + m_is_stop_handling = true; + if(m_cache.size() > HTTP_MAX_HEADER_LEN) + { + LOG_ERROR("simple_http_connection_handler::handle_buff_in: Too long header area"); + m_state = http_state_error; + return false; + } + break; + } + analize_cached_request_header_and_invoke_state(pos); + break; + } + case http_state_retriving_body: + return handle_retriving_query_body(); + case http_state_connection_close: + return false; + default: + LOG_ERROR("simple_http_connection_handler::handle_char_out: Wrong state: " << m_state); + return false; + case http_state_error: + LOG_ERROR("simple_http_connection_handler::handle_char_out: Error state!!!"); + return false; + } + + if(!m_cache.size()) + m_is_stop_handling = true; + } + + return true; + } + //-------------------------------------------------------------------------------------------- + inline bool analize_http_method(const boost::smatch& result, http::http_method& method, int& http_ver_major, int& http_ver_minor) + { + CHECK_AND_ASSERT_MES(result[0].matched, false, "simple_http_connection_handler::analize_http_method() assert failed..."); + http_ver_major = boost::lexical_cast(result[11]); + http_ver_minor = boost::lexical_cast(result[12]); + if(result[4].matched) + method = http::http_method_get; + else if(result[5].matched) + method = http::http_method_head; + else if(result[6].matched) + method = http::http_method_post; + else if(result[7].matched) + method = http::http_method_put; + else + method = http::http_method_etc; + + return true; + } + + //-------------------------------------------------------------------------------------------- + inline bool simple_http_connection_handler::handle_invoke_query_line() + { + LOG_FRAME("simple_http_connection_handler::handle_recognize_protocol_out(*)", LOG_LEVEL_3); + + STATIC_REGEXP_EXPR_1(rexp_match_command_line, "^(((OPTIONS)|(GET)|(HEAD)|(POST)|(PUT)|(DELETE)|(TRACE)) (\\S+) HTTP/(\\d+).(\\d+))\r?\n", boost::regex::icase | boost::regex::normal); + // 123 4 5 6 7 8 9 10 11 12 + //size_t match_len = 0; + boost::smatch result; + if(boost::regex_search(m_cache, result, rexp_match_command_line, boost::match_default) && result[0].matched) + { + analize_http_method(result, m_query_info.m_http_method, m_query_info.m_http_ver_hi, m_query_info.m_http_ver_hi); + m_query_info.m_URI = result[10]; + parse_uri(m_query_info.m_URI, m_query_info.m_uri_content); + m_query_info.m_http_method_str = result[2]; + m_query_info.m_full_request_str = result[0]; + + m_cache.erase(m_cache.begin(), to_nonsonst_iterator(m_cache, result[0].second)); + + m_state = http_state_retriving_header; + + return true; + }else + { + m_state = http_state_error; + LOG_ERROR("simple_http_connection_handler::handle_invoke_query_line(): Failed to match first line: " << m_cache); + return false; + } + + return false; + } + //-------------------------------------------------------------------------------------------- + inline std::string::size_type simple_http_connection_handler::match_end_of_header(const std::string& buf) + { + + //Here we returning head size, including terminating sequence (\r\n\r\n or \n\n) + std::string::size_type res = buf.find("\r\n\r\n"); + if(std::string::npos != res) + return res+4; + res = buf.find("\n\n"); + if(std::string::npos != res) + return res+2; + return res; + } + //-------------------------------------------------------------------------------------------- + inline bool simple_http_connection_handler::analize_cached_request_header_and_invoke_state(size_t pos) + { + //LOG_PRINT_L4("HTTP HEAD:\r\n" << m_cache.substr(0, pos)); + + LOG_FRAME("simple_http_connection_handler::analize_cached_request_header_and_invoke_state(*)", LOG_LEVEL_3); + + m_query_info.m_full_request_buf_size = pos; + m_query_info.m_request_head.assign(m_cache.begin(), m_cache.begin()+pos); + + if(!parse_cached_header(m_query_info.m_header_info, m_cache, pos)) + { + LOG_ERROR("simple_http_connection_handler::analize_cached_request_header_and_invoke_state(): failed to anilize request header: " << m_cache); + m_state = http_state_error; + } + + m_cache.erase(0, pos); + + std::string req_command_str = m_query_info.m_full_request_str; + //if we have POST or PUT command, it is very possible tha we will get body + //but now, we suppose than we have body only in case of we have "ContentLength" + if(m_query_info.m_header_info.m_content_length.size()) + { + m_state = http_state_retriving_body; + m_body_transfer_type = http_body_transfer_measure; + if(!get_len_from_content_lenght(m_query_info.m_header_info.m_content_length, m_len_summary)) + { + LOG_ERROR("simple_http_connection_handler::analize_cached_request_header_and_invoke_state(): Failed to get_len_from_content_lenght();, m_query_info.m_content_length="<= m_cache.size()) + { + m_len_remain -= m_cache.size(); + m_query_info.m_body += m_cache; + m_cache.clear(); + }else + { + m_query_info.m_body.append(m_cache.begin(), m_cache.begin() + m_len_remain); + m_cache.erase(0, m_len_remain); + m_len_remain = 0; + } + + if(!m_len_remain) + { + if(handle_request_and_send_response(m_query_info)) + set_ready_state(); + else + m_state = http_state_error; + } + return true; + } + //-------------------------------------------------------------------------------------------- + inline bool simple_http_connection_handler::parse_cached_header(http_header_info& body_info, const std::string& m_cache_to_process, size_t pos) + { + LOG_FRAME("http_stream_filter::parse_cached_header(*)", LOG_LEVEL_3); + + STATIC_REGEXP_EXPR_1(rexp_mach_field, + "\n?((Connection)|(Referer)|(Content-Length)|(Content-Type)|(Transfer-Encoding)|(Content-Encoding)|(Host)|(Cookie)" + // 12 3 4 5 6 7 8 9 + "|([\\w-]+?)) ?: ?((.*?)(\r?\n))[^\t ]", + //10 1112 13 + boost::regex::icase | boost::regex::normal); + + boost::smatch result; + std::string::const_iterator it_current_bound = m_cache_to_process.begin(); + std::string::const_iterator it_end_bound = m_cache_to_process.begin()+pos; + + body_info.clear(); + + //lookup all fields and fill well-known fields + while( boost::regex_search( it_current_bound, it_end_bound, result, rexp_mach_field, boost::match_default) && result[0].matched) + { + const size_t field_val = 12; + const size_t field_etc_name = 10; + + int i = 2; //start position = 2 + if(result[i++].matched)//"Connection" + body_info.m_connection = result[field_val]; + else if(result[i++].matched)//"Referer" + body_info.m_referer = result[field_val]; + else if(result[i++].matched)//"Content-Length" + body_info.m_content_length = result[field_val]; + else if(result[i++].matched)//"Content-Type" + body_info.m_content_type = result[field_val]; + else if(result[i++].matched)//"Transfer-Encoding" + body_info.m_transfer_encoding = result[field_val]; + else if(result[i++].matched)//"Content-Encoding" + body_info.m_content_encoding = result[field_val]; + else if(result[i++].matched)//"Host" + body_info.m_host = result[field_val]; + else if(result[i++].matched)//"Cookie" + body_info.m_cookie = result[field_val]; + else if(result[i++].matched)//e.t.c (HAVE TO BE MATCHED!) + body_info.m_etc_fields.push_back(std::pair(result[field_etc_name], result[field_val])); + else + { + LOG_ERROR("simple_http_connection_handler::parse_cached_header() not matched last entry in:"<(result[0]); + return true; + } + //----------------------------------------------------------------------------------- + inline bool simple_http_connection_handler::handle_request_and_send_response(const http::http_request_info& query_info) + { + http_response_info response; + bool res = handle_request(query_info, response); + //CHECK_AND_ASSERT_MES(res, res, "handle_request(query_info, response) returned false" ); + + std::string response_data = get_response_header(response); + + //LOG_PRINT_L0("HTTP_SEND: << \r\n" << response_data + response.m_body); + LOG_PRINT_L3("HTTP_RESPONSE_HEAD: << \r\n" << response_data); + + m_psnd_hndlr->do_send((void*)response_data.data(), response_data.size()); + if(response.m_body.size()) + m_psnd_hndlr->do_send((void*)response.m_body.data(), response.m_body.size()); + return res; + } + //----------------------------------------------------------------------------------- + inline bool simple_http_connection_handler::handle_request(const http::http_request_info& query_info, http_response_info& response) + { + + std::string uri_to_path = query_info.m_uri_content.m_path; + if("/" == uri_to_path) + uri_to_path = "/index.html"; + + //slash_to_back_slash(uri_to_path); + m_config.m_lock.lock(); + std::string destination_file_path = m_config.m_folder + uri_to_path; + m_config.m_lock.unlock(); + if(!file_io_utils::load_file_to_string(destination_file_path.c_str(), response.m_body)) + { + LOG_PRINT("URI \""<< query_info.m_full_request_str.substr(0, query_info.m_full_request_str.size()-2) << "\" [" << destination_file_path << "] Not Found (404 )" , LOG_LEVEL_1); + response.m_body = get_not_found_response_body(query_info.m_URI); + response.m_response_code = 404; + response.m_response_comment = "Not found"; + response.m_mime_tipe = "text/html"; + return true; + } + + LOG_PRINT(" -->> " << query_info.m_full_request_str << "\r\n<<--OK" , LOG_LEVEL_3); + response.m_response_code = 200; + response.m_response_comment = "OK"; + response.m_mime_tipe = get_file_mime_tipe(uri_to_path); + + + return true; + } + //----------------------------------------------------------------------------------- + inline std::string simple_http_connection_handler::get_response_header(const http_response_info& response) + { + std::string buf = "HTTP/1.1 "; + buf += boost::lexical_cast(response.m_response_code) + " " + response.m_response_comment + "\r\n" + + "Server: Siski v0.1\r\n" + "Content-Length: "; + buf += boost::lexical_cast(response.m_body.size()) + "\r\n"; + buf += "Content-Type: "; + buf += response.m_mime_tipe + "\r\n"; + + buf += "Last-Modified: "; + time_t tm; + time(&tm); + buf += misc_utils::get_internet_time_str(tm) + "\r\n"; + buf += "Accept-Ranges: bytes\r\n"; + //Wed, 01 Dec 2010 03:27:41 GMT" + + string_tools::trim(m_query_info.m_header_info.m_connection); + if(m_query_info.m_header_info.m_connection.size()) + { + if(!string_tools::compare_no_case("close", m_query_info.m_header_info.m_connection)) + { + //closing connection after sending + buf += "Connection: close\r\n"; + m_state = http_state_connection_close; + m_want_close = true; + } + } + //add additional fields, if it is + for(fields_list::const_iterator it = response.m_additional_fields.begin(); it!=response.m_additional_fields.end(); it++) + buf += it->first + ":" + it->second + "\r\n"; + + buf+="\r\n"; + + return buf; + } + //----------------------------------------------------------------------------------- + inline std::string simple_http_connection_handler::get_file_mime_tipe(const std::string& path) + { + std::string result; + std::string ext = string_tools::get_extension(path); + if(!string_tools::compare_no_case(ext, "gif")) + result = "image/gif"; + else if(!string_tools::compare_no_case(ext, "jpg")) + result = "image/jpeg"; + else if(!string_tools::compare_no_case(ext, "html")) + result = "text/html"; + else if(!string_tools::compare_no_case(ext, "htm")) + result = "text/html"; + else if(!string_tools::compare_no_case(ext, "js")) + result = "application/x-javascript"; + else if(!string_tools::compare_no_case(ext, "css")) + result = "text/css"; + else if(!string_tools::compare_no_case(ext, "xml")) + result = "application/xml"; + else if(!string_tools::compare_no_case(ext, "svg")) + result = "image/svg+xml"; + + + return result; + } + //----------------------------------------------------------------------------------- + inline std::string simple_http_connection_handler::get_not_found_response_body(const std::string& URI) + { + std::string body = + "\r\n" + "\r\n" + "404 Not Found\r\n" + "\r\n" + "

Not Found

\r\n" + "

The requested URL \r\n"; + body += URI; + body += "was not found on this server.

\r\n" + "\r\n"; + + return body; + } + //-------------------------------------------------------------------------------------------- + inline bool simple_http_connection_handler::slash_to_back_slash(std::string& str) + { + for(std::string::iterator it = str.begin(); it!=str.end(); it++) + if('/' == *it) + *it = '\\'; + return true; + } + } +} +} + +//-------------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------------------- \ No newline at end of file diff --git a/contrib/epee/include/net/http_server_cp.h b/contrib/epee/include/net/http_server_cp.h new file mode 100644 index 000000000..bbb167f9f --- /dev/null +++ b/contrib/epee/include/net/http_server_cp.h @@ -0,0 +1,48 @@ +// 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. +// + + + + +#ifndef _HTTP_SERVER_CP_H_ +#define _HTTP_SERVER_CP_H_ + +#include "abstract_tcp_server_cp.h" +#include "http_server.h" +namespace epee +{ +namespace net_utils +{ + typedef cp_server_impl cp_http_server_file_system; + typedef cp_server_impl cp_http_server_custum_handling; +} +} + + + +#endif + + diff --git a/contrib/epee/include/net/http_server_cp2.h b/contrib/epee/include/net/http_server_cp2.h new file mode 100644 index 000000000..dd76d06f8 --- /dev/null +++ b/contrib/epee/include/net/http_server_cp2.h @@ -0,0 +1,47 @@ +// 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. +// + + + + +#ifndef _HTTP_SERVER_CP2_H_ +#define _HTTP_SERVER_CP2_H_ + +#include "abstract_tcp_server2.h" +#include "http_protocol_handler.h" +namespace epee +{ +namespace net_utils +{ + typedef boosted_tcp_server boosted_http_server_file_system; + typedef boosted_tcp_server boosted_http_server_custum_handling; +} +} + + +#endif + + diff --git a/contrib/epee/include/net/http_server_handlers_map2.h b/contrib/epee/include/net/http_server_handlers_map2.h new file mode 100644 index 000000000..7a8bdd4ad --- /dev/null +++ b/contrib/epee/include/net/http_server_handlers_map2.h @@ -0,0 +1,260 @@ +// 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. +// + + +#pragma once +#include "serialization/keyvalue_serialization.h" +#include "storages/portable_storage_template_helper.h" +#include "http_base.h" + + +#define CHAIN_HTTP_TO_MAP2() bool handle_http_request(const epee::net_utils::http::http_request_info& query_info, \ + epee::net_utils::http::http_response_info& response, \ + const epee::net_utils::connection_context_base& m_conn_context) \ +{\ + LOG_PRINT_L2("HTTP [" << epee::string_tools::get_ip_string_from_int32(m_conn_context.m_remote_ip ) << "] " << query_info.m_http_method_str << " " << query_info.m_URI); \ + response.m_response_code = 200; \ + response.m_response_comment = "Ok"; \ + if(!handle_http_request_map(query_info, response, m_conn_context)) \ + {response.m_response_code = 404;response.m_response_comment = "Not found";} \ + return true; \ +} + + +#define BEGIN_URI_MAP2() bool handle_http_request_map(const epee::net_utils::http::http_request_info& query_info, \ + epee::net_utils::http::http_response_info& response_info, \ + const epee::net_utils::connection_context_base& m_conn_context) { \ + bool handled = false; \ + if(false) return true; //just a stub to have "else if" + +#define MAP_URI2(pattern, callback) else if(std::string::npos != query_info.m_URI.find(pattern)) return callback(query_info, response_info, m_conn_context); + +#define MAP_URI_AUTO_XML2(s_pattern, callback_f, command_type) //TODO: don't think i ever again will use xml - ambiguous and "overtagged" format + +#define MAP_URI_AUTO_JON2(s_pattern, callback_f, command_type) \ + else if(query_info.m_URI == s_pattern) \ + { \ + handled = true; \ + boost::uint64_t ticks = misc_utils::get_tick_count(); \ + boost::value_initialized req; \ + bool parse_res = epee::serialization::load_t_from_json(static_cast(req), query_info.m_body); \ + CHECK_AND_ASSERT_MES(parse_res, false, "Failed to parse json: \r\n" << query_info.m_body); \ + boost::uint64_t ticks1 = epee::misc_utils::get_tick_count(); \ + boost::value_initialized resp;\ + if(!callback_f(static_cast(req), static_cast(resp))) \ + { \ + LOG_ERROR("Failed to " << #callback_f << "()"); \ + response_info.m_response_code = 500; \ + response_info.m_response_comment = "Internal Server Error"; \ + return true; \ + } \ + boost::uint64_t ticks2 = epee::misc_utils::get_tick_count(); \ + epee::serialization::store_t_to_json(static_cast(resp), response_info.m_body); \ + boost::uint64_t ticks3 = epee::misc_utils::get_tick_count(); \ + response_info.m_mime_tipe = "application/json"; \ + response_info.m_header_info.m_content_type = " application/json"; \ + LOG_PRINT( s_pattern << " processed with " << ticks1-ticks << "/"<< ticks2-ticks1 << "/" << ticks3-ticks2 << "ms", LOG_LEVEL_2); \ + } + +#define MAP_URI_AUTO_BIN2(s_pattern, callback_f, command_type) \ + else if(query_info.m_URI == s_pattern) \ + { \ + handled = true; \ + boost::uint64_t ticks = misc_utils::get_tick_count(); \ + boost::value_initialized req; \ + bool parse_res = epee::serialization::load_t_from_binary(static_cast(req), query_info.m_body); \ + CHECK_AND_ASSERT_MES(parse_res, false, "Failed to parse bin body data, body size=" << query_info.m_body.size()); \ + boost::uint64_t ticks1 = misc_utils::get_tick_count(); \ + boost::value_initialized resp;\ + if(!callback_f(static_cast(req), static_cast(resp))) \ + { \ + LOG_ERROR("Failed to " << #callback_f << "()"); \ + response_info.m_response_code = 500; \ + response_info.m_response_comment = "Internal Server Error"; \ + return true; \ + } \ + boost::uint64_t ticks2 = misc_utils::get_tick_count(); \ + epee::serialization::store_t_to_binary(static_cast(resp), response_info.m_body); \ + boost::uint64_t ticks3 = epee::misc_utils::get_tick_count(); \ + response_info.m_mime_tipe = " application/octet-stream"; \ + response_info.m_header_info.m_content_type = " application/octet-stream"; \ + LOG_PRINT( s_pattern << "() processed with " << ticks1-ticks << "/"<< ticks2-ticks1 << "/" << ticks3-ticks2 << "ms", LOG_LEVEL_2); \ + } + +#define CHAIN_URI_MAP2(callback) else {callback(query_info, response_info, m_conn_context);handled = true;} + +#define END_URI_MAP2() return handled;} + + + + +namespace epee +{ + namespace json_rpc + { + template + struct request + { + std::string version; + std::string method; + std::string id; + t_param params; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(version) + KV_SERIALIZE(method) + KV_SERIALIZE(id) + KV_SERIALIZE(params) + END_KV_SERIALIZE_MAP() + }; + + struct error + { + int64_t code; + std::string message; + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(code) + KV_SERIALIZE(message) + END_KV_SERIALIZE_MAP() + }; + + struct dummy_error + { + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + + template + struct response + { + t_param result; + t_error error; + std::string id; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(result) + KV_SERIALIZE(error) + KV_SERIALIZE(id) + END_KV_SERIALIZE_MAP() + }; + + typedef response error_response; + } +} + + + + +#define BEGIN_JSON_RPC_MAP(uri) else if(query_info.m_URI == uri) \ + { \ + boost::uint64_t ticks = epee::misc_utils::get_tick_count(); \ + epee::serialization::portable_storage ps; \ + if(!ps.load_from_json(query_info.m_body)) \ + { \ + boost::value_initialized rsp; \ + static_cast(rsp).error.code = -32700; \ + static_cast(rsp).error.message = "Parse error"; \ + epee::serialization::store_t_to_json(static_cast(rsp), response_info.m_body); \ + return true; \ + } \ + std::string callback_name; \ + if(!ps.get_value("method", callback_name, nullptr)) \ + { \ + epee::json_rpc::error_response rsp; \ + rsp.error.code = -32600; \ + rsp.error.message = "Invalid Request"; \ + epee::serialization::store_t_to_json(static_cast(rsp), response_info.m_body); \ + return true; \ + } \ + if(false) return true; //just a stub to have "else if" + +#define MAP_JON_RPC_WE(method_name, callback_f, command_type) \ + else if(callback_name == method_name) \ +{ \ + handled = true; \ + boost::value_initialized > req_; \ + epee::json_rpc::request& req = static_cast&>(req_);\ + req.load(ps); \ + boost::uint64_t ticks1 = epee::misc_utils::get_tick_count(); \ + boost::value_initialized > resp_; \ + epee::json_rpc::response& resp = static_cast &>(resp_); \ + resp.id = req.id; \ + epee::json_rpc::error_response fail_resp = AUTO_VAL_INIT(fail_resp); \ + fail_resp.id = req.id; \ + if(!callback_f(req.params, resp.result, fail_resp.error)) \ +{ \ + epee::serialization::store_t_to_json(static_cast(fail_resp), response_info.m_body); \ + return true; \ +} \ + boost::uint64_t ticks2 = epee::misc_utils::get_tick_count(); \ + epee::serialization::store_t_to_json(resp, response_info.m_body); \ + boost::uint64_t ticks3 = epee::misc_utils::get_tick_count(); \ + response_info.m_mime_tipe = "application/json"; \ + response_info.m_header_info.m_content_type = " application/json"; \ + LOG_PRINT( query_info.m_URI << "[" << method_name << "] processed with " << ticks1-ticks << "/"<< ticks2-ticks1 << "/" << ticks3-ticks2 << "ms", LOG_LEVEL_2); \ + return true;\ +} + + +#define MAP_JON_RPC(method_name, callback_f, command_type) \ + else if(callback_name == method_name) \ +{ \ + handled = true; \ + boost::value_initialized > req_; \ + epee::json_rpc::request& req = static_cast&>(req_);\ + req.load(ps); \ + boost::uint64_t ticks1 = epee::misc_utils::get_tick_count(); \ + boost::value_initialized > resp_; \ + epee::json_rpc::response& resp = static_cast &>(resp_); \ + resp.id = req.id; \ + if(!callback_f(req.params, resp.result)) \ +{ \ + epee::json_rpc::error_response fail_resp = AUTO_VAL_INIT(fail_resp); \ + fail_resp.id = req.id; \ + fail_resp.error.code = -32603; \ + fail_resp.error.message = "Internal error"; \ + epee::serialization::store_t_to_json(static_cast(fail_resp), response_info.m_body); \ + return true; \ +} \ + boost::uint64_t ticks2 = epee::misc_utils::get_tick_count(); \ + epee::serialization::store_t_to_json(resp, response_info.m_body); \ + boost::uint64_t ticks3 = epee::misc_utils::get_tick_count(); \ + response_info.m_mime_tipe = "application/json"; \ + response_info.m_header_info.m_content_type = " application/json"; \ + LOG_PRINT( query_info.m_URI << "[" << method_name << "] processed with " << ticks1-ticks << "/"<< ticks2-ticks1 << "/" << ticks3-ticks2 << "ms", LOG_LEVEL_2); \ + return true;\ +} + + +#define END_JSON_RPC_MAP() \ + epee::json_rpc::error_response rsp; \ + rsp.error.code = -32601; \ + rsp.error.message = "Method not found"; \ + epee::serialization::store_t_to_json(static_cast(rsp), response_info.m_body); \ + return true; \ + } + + diff --git a/contrib/epee/include/net/http_server_impl_base.h b/contrib/epee/include/net/http_server_impl_base.h new file mode 100644 index 000000000..f81b4f601 --- /dev/null +++ b/contrib/epee/include/net/http_server_impl_base.h @@ -0,0 +1,112 @@ +// 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. +// + + + + +#pragma once + + +#include +#include + +#include "net/http_server_cp2.h" +#include "net/http_server_handlers_map2.h" + +namespace epee +{ + + template + class http_server_impl_base: public net_utils::http::i_http_server_handler + { + + public: + http_server_impl_base() + : m_net_server() + {} + + explicit http_server_impl_base(boost::asio::io_service& external_io_service) + : m_net_server(external_io_service) + {} + + bool init(const std::string& bind_port = "0", const std::string& bind_ip = "0.0.0.0") + { + + //set self as callback handler + m_net_server.get_config_object().m_phandler = static_cast(this); + + //here set folder for hosting reqests + m_net_server.get_config_object().m_folder = ""; + + LOG_PRINT_L0("Binding on " << bind_ip << ":" << bind_port); + bool res = m_net_server.init_server(bind_port, bind_ip); + if(!res) + { + LOG_ERROR("Failed to bind server"); + return false; + } + return true; + } + + bool run(size_t threads_count, bool wait = true) + { + //go to loop + LOG_PRINT("Run net_service loop( " << threads_count << " threads)...", LOG_LEVEL_0); + if(!m_net_server.run_server(threads_count, wait)) + { + LOG_ERROR("Failed to run net tcp server!"); + } + + if(wait) + LOG_PRINT("net_service loop stopped.", LOG_LEVEL_0); + return true; + } + + bool deinit() + { + return m_net_server.deinit_server(); + } + + bool timed_wait_server_stop(uint64_t ms) + { + return m_net_server.timed_wait_server_stop(ms); + } + + bool send_stop_signal() + { + m_net_server.send_stop_signal(); + return true; + } + + int get_binded_port() + { + return m_net_server.get_binded_port(); + } + + protected: + net_utils::boosted_http_server_custum_handling m_net_server; + }; +} \ No newline at end of file diff --git a/contrib/epee/include/net/http_server_thread_per_connect.h b/contrib/epee/include/net/http_server_thread_per_connect.h new file mode 100644 index 000000000..bec43b726 --- /dev/null +++ b/contrib/epee/include/net/http_server_thread_per_connect.h @@ -0,0 +1,48 @@ +// 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. +// + + + +#ifndef _HTTP_SERVER_CP_H_ +#define _HTTP_SERVER_CP_H_ + +#include "abstract_tcp_server.h" +#include "http_server.h" + +namespace epee +{ +namespace net_utils +{ + typedef abstract_tcp_server mt_http_server_file_system; + typedef abstract_tcp_server mt_http_server_custum_handling; + +} +} + + +#endif + + diff --git a/contrib/epee/include/net/levin_base.h b/contrib/epee/include/net/levin_base.h new file mode 100644 index 000000000..503a9e5df --- /dev/null +++ b/contrib/epee/include/net/levin_base.h @@ -0,0 +1,125 @@ +// 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. +// + + + +#ifndef _LEVIN_BASE_H_ +#define _LEVIN_BASE_H_ + +#include "net_utils_base.h" + +#define LEVIN_SIGNATURE 0x0101010101012101LL //Bender's nightmare + +namespace epee +{ +namespace levin +{ +#pragma pack(push) +#pragma pack(1) + struct bucket_head + { + boost::uint64_t m_signature; + boost::uint64_t m_cb; + bool m_have_to_return_data; + boost::uint32_t m_command; + boost::int32_t m_return_code; + boost::uint32_t m_reservedA; //probably some flags in future + boost::uint32_t m_reservedB; //probably some check sum in future + }; +#pragma pack(pop) + + +#pragma pack(push) +#pragma pack(1) + struct bucket_head2 + { + boost::uint64_t m_signature; + boost::uint64_t m_cb; + bool m_have_to_return_data; + boost::uint32_t m_command; + boost::int32_t m_return_code; + boost::uint32_t m_flags; + boost::uint32_t m_protocol_version; + }; +#pragma pack(pop) + + +#define LEVIN_DEFAULT_TIMEOUT_PRECONFIGURED 0 +#define LEVIN_DEFAULT_MAX_PACKET_SIZE 100000000 //100MB by default + +#define LEVIN_PACKET_REQUEST 0x00000001 +#define LEVIN_PACKET_RESPONSE 0x00000002 + + +#define LEVIN_PROTOCOL_VER_0 0 +#define LEVIN_PROTOCOL_VER_1 1 + + template + struct levin_commands_handler + { + virtual int invoke(int command, const std::string& in_buff, std::string& buff_out, t_connection_context& context)=0; + virtual int notify(int command, const std::string& in_buff, t_connection_context& context)=0; + virtual void callback(t_connection_context& context){}; + + virtual void on_connection_new(t_connection_context& context){}; + virtual void on_connection_close(t_connection_context& context){}; + + }; + +#define LEVIN_OK 0 +#define LEVIN_ERROR_CONNECTION -1 +#define LEVIN_ERROR_CONNECTION_NOT_FOUND -2 +#define LEVIN_ERROR_CONNECTION_DESTROYED -3 +#define LEVIN_ERROR_CONNECTION_TIMEDOUT -4 +#define LEVIN_ERROR_CONNECTION_NO_DUPLEX_PROTOCOL -5 +#define LEVIN_ERROR_CONNECTION_HANDLER_NOT_DEFINED -6 +#define LEVIN_ERROR_FORMAT -7 + +#define DESCRIBE_RET_CODE(code) case code: return #code; + inline + const char* get_err_descr(int err) + { + switch(err) + { + DESCRIBE_RET_CODE(LEVIN_OK); + DESCRIBE_RET_CODE(LEVIN_ERROR_CONNECTION); + DESCRIBE_RET_CODE(LEVIN_ERROR_CONNECTION_NOT_FOUND); + DESCRIBE_RET_CODE(LEVIN_ERROR_CONNECTION_DESTROYED); + DESCRIBE_RET_CODE(LEVIN_ERROR_CONNECTION_TIMEDOUT); + DESCRIBE_RET_CODE(LEVIN_ERROR_CONNECTION_NO_DUPLEX_PROTOCOL); + DESCRIBE_RET_CODE(LEVIN_ERROR_CONNECTION_HANDLER_NOT_DEFINED); + DESCRIBE_RET_CODE(LEVIN_ERROR_FORMAT); + default: + return "unknown code"; + } + } + + +} +} + + +#endif //_LEVIN_BASE_H_ diff --git a/contrib/epee/include/net/levin_client.h b/contrib/epee/include/net/levin_client.h new file mode 100644 index 000000000..335f6ba02 --- /dev/null +++ b/contrib/epee/include/net/levin_client.h @@ -0,0 +1,89 @@ +// 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. +// + + + + + +#ifndef _LEVIN_CLIENT_H_ +#define _LEVIN_CLIENT_H_ + +#include "net_helper.h" +#include "levin_base.h" + + +#ifndef MAKE_IP +#define MAKE_IP( a1, a2, a3, a4 ) (a1|(a2<<8)|(a3<<16)|(a4<<24)) +#endif + +namespace epee +{ +namespace levin +{ + /************************************************************************/ + /* */ + /************************************************************************/ + class levin_client_impl + { + public: + levin_client_impl(); + virtual ~levin_client_impl(); + + bool connect(u_long ip, int port, unsigned int timeout, const std::string& bind_ip = "0.0.0.0"); + bool connect(const std::string& addr, int port, unsigned int timeout, const std::string& bind_ip = "0.0.0.0"); + bool is_connected(); + bool disconnect(); + + virtual int invoke(int command, const std::string& in_buff, std::string& buff_out); + virtual int notify(int command, const std::string& in_buff); + + protected: + net_utils::blocked_mode_client m_transport; + }; + + + /************************************************************************/ + /* */ + /************************************************************************/ + class levin_client_impl2: public levin_client_impl + { + public: + + int invoke(int command, const std::string& in_buff, std::string& buff_out); + int notify(int command, const std::string& in_buff); + }; + +} +namespace net_utils +{ + typedef levin::levin_client_impl levin_client; + typedef levin::levin_client_impl2 levin_client2; +} +} + +#include "levin_client.inl" + +#endif //_LEVIN_CLIENT_H_ diff --git a/contrib/epee/include/net/levin_client.inl b/contrib/epee/include/net/levin_client.inl new file mode 100644 index 000000000..ae159da6e --- /dev/null +++ b/contrib/epee/include/net/levin_client.inl @@ -0,0 +1,194 @@ +// 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 "string_tools.h" +namespace epee +{ +namespace levin +{ +inline +bool levin_client_impl::connect(u_long ip, int port, unsigned int timeout, const std::string& bind_ip) +{ + return m_transport.connect(string_tools::get_ip_string_from_int32(ip), port, timeout, timeout, bind_ip); +} +//------------------------------------------------------------------------------ +inline + bool levin_client_impl::connect(const std::string& addr, int port, unsigned int timeout, const std::string& bind_ip) +{ + return m_transport.connect(addr, port, timeout, timeout, bind_ip); +} +//------------------------------------------------------------------------------ +inline +bool levin_client_impl::is_connected() +{ + return m_transport.is_connected(); +} +//------------------------------------------------------------------------------ +inline +bool levin_client_impl::disconnect() +{ + return m_transport.disconnect(); +} +//------------------------------------------------------------------------------ +inline +levin_client_impl::levin_client_impl() +{ +} +//------------------------------------------------------------------------------ +inline +levin_client_impl::~levin_client_impl() +{ + disconnect(); +} +//------------------------------------------------------------------------------ +inline +int levin_client_impl::invoke(int command, const std::string& in_buff, std::string& buff_out) +{ + if(!is_connected()) + return -1; + + bucket_head head = {0}; + head.m_signature = LEVIN_SIGNATURE; + head.m_cb = in_buff.size(); + head.m_have_to_return_data = true; + head.m_command = command; + if(!m_transport.send(&head, sizeof(head))) + return -1; + + if(!m_transport.send(in_buff)) + return -1; + + std::string local_buff; + if(!m_transport.recv_n(local_buff, sizeof(bucket_head))) + return -1; + + head = *(bucket_head*)local_buff.data(); + + + if(head.m_signature!=LEVIN_SIGNATURE) + { + LOG_PRINT_L0("Signature missmatch in response"); + return -1; + } + + if(!m_transport.recv_n(buff_out, head.m_cb)) + return -1; + + return head.m_return_code; +} +//------------------------------------------------------------------------------ +inline +int levin_client_impl::notify(int command, const std::string& in_buff) +{ + if(!is_connected()) + return -1; + + bucket_head head = {0}; + head.m_signature = LEVIN_SIGNATURE; + head.m_cb = in_buff.size(); + head.m_have_to_return_data = false; + head.m_command = command; + + if(!m_transport.send((const char*)&head, sizeof(head))) + return -1; + + if(!m_transport.send(in_buff)) + return -1; + + return 1; +} + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +inline + int levin_client_impl2::invoke(int command, const std::string& in_buff, std::string& buff_out) +{ + if(!is_connected()) + return -1; + + bucket_head2 head = {0}; + head.m_signature = LEVIN_SIGNATURE; + head.m_cb = in_buff.size(); + head.m_have_to_return_data = true; + head.m_command = command; + head.m_protocol_version = LEVIN_PROTOCOL_VER_1; + head.m_flags = LEVIN_PACKET_REQUEST; + if(!m_transport.send(&head, sizeof(head))) + return -1; + + if(!m_transport.send(in_buff)) + return -1; + + std::string local_buff; + if(!m_transport.recv_n(local_buff, sizeof(bucket_head2))) + return -1; + + head = *(bucket_head2*)local_buff.data(); + + + if(head.m_signature!=LEVIN_SIGNATURE) + { + LOG_PRINT_L0("Signature missmatch in response"); + return -1; + } + + if(!m_transport.recv_n(buff_out, head.m_cb)) + return -1; + + return head.m_return_code; +} +//------------------------------------------------------------------------------ +inline + int levin_client_impl2::notify(int command, const std::string& in_buff) +{ + if(!is_connected()) + return -1; + + bucket_head2 head = {0}; + head.m_signature = LEVIN_SIGNATURE; + head.m_cb = in_buff.size(); + head.m_have_to_return_data = false; + head.m_command = command; + head.m_protocol_version = LEVIN_PROTOCOL_VER_1; + head.m_flags = LEVIN_PACKET_REQUEST; + + if(!m_transport.send((const char*)&head, sizeof(head))) + return -1; + + if(!m_transport.send(in_buff)) + return -1; + + return 1; +} + +} +} +//------------------------------------------------------------------------------ \ No newline at end of file diff --git a/contrib/epee/include/net/levin_client_async.h b/contrib/epee/include/net/levin_client_async.h new file mode 100644 index 000000000..b02fa7ee7 --- /dev/null +++ b/contrib/epee/include/net/levin_client_async.h @@ -0,0 +1,577 @@ +// 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. +// + + +#pragma once + +#include "" +#include "net_helper.h" +#include "levin_base.h" + + +namespace epee +{ +namespace levin +{ + + /************************************************************************ + * levin_client_async - probably it is not really fast implementation, + * each handler thread could make up to 30 ms latency. + * But, handling events in reader thread will cause dead locks in + * case of recursive call (call invoke() to the same connection + * on reader thread on remote invoke() handler) + ***********************************************************************/ + + + class levin_client_async + { + levin_commands_handler* m_pcommands_handler; + volatile boost::uint32_t m_is_stop; + volatile boost::uint32_t m_threads_count; + ::critical_section m_send_lock; + + std::string m_local_invoke_buff; + ::critical_section m_local_invoke_buff_lock; + volatile int m_invoke_res; + + volatile boost::uint32_t m_invoke_data_ready; + volatile boost::uint32_t m_invoke_is_active; + + boost::mutex m_invoke_event; + boost::condition_variable m_invoke_cond; + size_t m_timeout; + + ::critical_section m_recieved_packets_lock; + struct packet_entry + { + bucket_head m_hd; + std::string m_body; + boost::uint32_t m_connection_index; + }; + std::list m_recieved_packets; + /* + m_current_connection_index needed when some connection was broken and reconnected - in this + case we could have some received packets in que, which shoud not be handled + */ + volatile boost::uint32_t m_current_connection_index; + ::critical_section m_invoke_lock; + ::critical_section m_reciev_packet_lock; + ::critical_section m_connection_lock; + net_utils::blocked_mode_client m_transport; + public: + levin_client_async():m_pcommands_handler(NULL), m_is_stop(0), m_threads_count(0), m_invoke_data_ready(0), m_invoke_is_active(0) + {} + levin_client_async(const levin_client_async& /*v*/):m_pcommands_handler(NULL), m_is_stop(0), m_threads_count(0), m_invoke_data_ready(0), m_invoke_is_active(0) + {} + ~levin_client_async() + { + boost::interprocess::ipcdetail::atomic_write32(&m_is_stop, 1); + disconnect(); + + + while(boost::interprocess::ipcdetail::atomic_read32(&m_threads_count)) + ::Sleep(100); + } + + void set_handler(levin_commands_handler* phandler) + { + m_pcommands_handler = phandler; + } + + bool connect(boost::uint32_t ip, boost::uint32_t port, boost::uint32_t timeout) + { + loop_call_guard(); + critical_region cr(m_connection_lock); + + m_timeout = timeout; + bool res = false; + CRITICAL_REGION_BEGIN(m_reciev_packet_lock); + CRITICAL_REGION_BEGIN(m_send_lock); + res = levin_client_impl::connect(ip, port, timeout); + boost::interprocess::ipcdetail::atomic_inc32(&m_current_connection_index); + CRITICAL_REGION_END(); + CRITICAL_REGION_END(); + if(res && !boost::interprocess::ipcdetail::atomic_read32(&m_threads_count) ) + { + //boost::interprocess::ipcdetail::atomic_write32(&m_is_stop, 0);//m_is_stop = false; + boost::thread( boost::bind(&levin_duplex_client::reciever_thread, this) ); + boost::thread( boost::bind(&levin_duplex_client::handler_thread, this) ); + boost::thread( boost::bind(&levin_duplex_client::handler_thread, this) ); + } + + return res; + } + bool is_connected() + { + loop_call_guard(); + critical_region cr(m_cs); + return levin_client_impl::is_connected(); + } + + inline + bool check_connection() + { + loop_call_guard(); + critical_region cr(m_cs); + + if(!is_connected()) + { + if( !reconnect() ) + { + LOG_ERROR("Reconnect Failed. Failed to invoke() becouse not connected!"); + return false; + } + } + return true; + } + + //------------------------------------------------------------------------------ + inline + bool recv_n(SOCKET s, char* pbuff, size_t cb) + { + while(cb) + { + int res = ::recv(m_socket, pbuff, (int)cb, 0); + + if(SOCKET_ERROR == res) + { + if(!m_connected) + return false; + + int err = ::WSAGetLastError(); + LOG_ERROR("Failed to recv(), err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\""); + disconnect(); + //reconnect(); + return false; + }else if(res == 0) + { + disconnect(); + //reconnect(); + return false; + } + LOG_PRINT_L4("[" << m_socket <<"] RECV " << res); + cb -= res; + pbuff += res; + } + + return true; + } + + //------------------------------------------------------------------------------ + inline + bool recv_n(SOCKET s, std::string& buff) + { + size_t cb_remain = buff.size(); + char* m_current_ptr = (char*)buff.data(); + return recv_n(s, m_current_ptr, cb_remain); + } + + bool disconnect() + { + //boost::interprocess::ipcdetail::atomic_write32(&m_is_stop, 1);//m_is_stop = true; + loop_call_guard(); + critical_region cr(m_cs); + levin_client_impl::disconnect(); + + CRITICAL_REGION_BEGIN(m_local_invoke_buff_lock); + m_local_invoke_buff.clear(); + m_invoke_res = LEVIN_ERROR_CONNECTION_DESTROYED; + CRITICAL_REGION_END(); + boost::interprocess::ipcdetail::atomic_write32(&m_invoke_data_ready, 1); //m_invoke_data_ready = true; + m_invoke_cond.notify_all(); + return true; + } + + void loop_call_guard() + { + + } + + void on_leave_invoke() + { + boost::interprocess::ipcdetail::atomic_write32(&m_invoke_is_active, 0); + } + + int invoke(const GUID& target, int command, const std::string& in_buff, std::string& buff_out) + { + + critical_region cr_invoke(m_invoke_lock); + + boost::interprocess::ipcdetail::atomic_write32(&m_invoke_is_active, 1); + boost::interprocess::ipcdetail::atomic_write32(&m_invoke_data_ready, 0); + misc_utils::destr_ptr hdlr = misc_utils::add_exit_scope_handler(boost::bind(&levin_duplex_client::on_leave_invoke, this)); + + loop_call_guard(); + + if(!check_connection()) + return LEVIN_ERROR_CONNECTION_DESTROYED; + + + bucket_head head = {0}; + head.m_signature = LEVIN_SIGNATURE; + head.m_cb = in_buff.size(); + head.m_have_to_return_data = true; + head.m_id = target; +#ifdef TRACE_LEVIN_PACKETS_BY_GUIDS + ::UuidCreate(&head.m_id); +#endif + head.m_command = command; + head.m_protocol_version = LEVIN_PROTOCOL_VER_1; + head.m_flags = LEVIN_PACKET_REQUEST; + LOG_PRINT("[" << m_socket <<"] Sending invoke data", LOG_LEVEL_4); + + CRITICAL_REGION_BEGIN(m_send_lock); + LOG_PRINT_L4("[" << m_socket <<"] SEND " << sizeof(head)); + int res = ::send(m_socket, (const char*)&head, sizeof(head), 0); + if(SOCKET_ERROR == res) + { + int err = ::WSAGetLastError(); + LOG_ERROR("Failed to send(), err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\""); + disconnect(); + return LEVIN_ERROR_CONNECTION_DESTROYED; + } + LOG_PRINT_L4("[" << m_socket <<"] SEND " << (int)in_buff.size()); + res = ::send(m_socket, in_buff.data(), (int)in_buff.size(), 0); + if(SOCKET_ERROR == res) + { + int err = ::WSAGetLastError(); + LOG_ERROR("Failed to send(), err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\""); + disconnect(); + return LEVIN_ERROR_CONNECTION_DESTROYED; + } + CRITICAL_REGION_END(); + LOG_PRINT_L4("LEVIN_PACKET_SENT. [len=" << head.m_cb << ", flags=" << head.m_flags << ", is_cmd=" << head.m_have_to_return_data <<", cmd_id = " << head.m_command << ", pr_v=" << head.m_protocol_version << ", uid=" << string_tools::get_str_from_guid_a(head.m_id) << "]"); + + //hard coded timeout in 10 minutes for maximum invoke period. if it happens, it could mean only some real troubles. + boost::system_time timeout = boost::get_system_time()+ boost::posix_time::milliseconds(100); + size_t timeout_count = 0; + boost::unique_lock lock(m_invoke_event); + + while(!boost::interprocess::ipcdetail::atomic_read32(&m_invoke_data_ready)) + { + if(!m_invoke_cond.timed_wait(lock, timeout)) + { + if(timeout_count < 10) + { + //workaround to avoid freezing at timed_wait called after notify_all. + timeout = boost::get_system_time()+ boost::posix_time::milliseconds(100); + ++timeout_count; + continue; + }else if(timeout_count == 10) + { + //workaround to avoid freezing at timed_wait called after notify_all. + timeout = boost::get_system_time()+ boost::posix_time::minutes(10); + ++timeout_count; + continue; + }else + { + LOG_PRINT("[" << m_socket <<"] Timeout on waiting invoke result. ", LOG_LEVEL_0); + //disconnect(); + return LEVIN_ERROR_CONNECTION_TIMEDOUT; + } + } + } + + + CRITICAL_REGION_BEGIN(m_local_invoke_buff_lock); + buff_out.swap(m_local_invoke_buff); + m_local_invoke_buff.clear(); + CRITICAL_REGION_END(); + return m_invoke_res; + } + + int notify(const GUID& target, int command, const std::string& in_buff) + { + if(!check_connection()) + return LEVIN_ERROR_CONNECTION_DESTROYED; + + bucket_head head = {0}; + head.m_signature = LEVIN_SIGNATURE; + head.m_cb = in_buff.size(); + head.m_have_to_return_data = false; + head.m_id = target; +#ifdef TRACE_LEVIN_PACKETS_BY_GUIDS + ::UuidCreate(&head.m_id); +#endif + head.m_command = command; + head.m_protocol_version = LEVIN_PROTOCOL_VER_1; + head.m_flags = LEVIN_PACKET_REQUEST; + CRITICAL_REGION_BEGIN(m_send_lock); + LOG_PRINT_L4("[" << m_socket <<"] SEND " << sizeof(head)); + int res = ::send(m_socket, (const char*)&head, sizeof(head), 0); + if(SOCKET_ERROR == res) + { + int err = ::WSAGetLastError(); + LOG_ERROR("Failed to send(), err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\""); + disconnect(); + return LEVIN_ERROR_CONNECTION_DESTROYED; + } + LOG_PRINT_L4("[" << m_socket <<"] SEND " << (int)in_buff.size()); + res = ::send(m_socket, in_buff.data(), (int)in_buff.size(), 0); + if(SOCKET_ERROR == res) + { + int err = ::WSAGetLastError(); + LOG_ERROR("Failed to send(), err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\""); + disconnect(); + return LEVIN_ERROR_CONNECTION_DESTROYED; + } + CRITICAL_REGION_END(); + LOG_PRINT_L4("LEVIN_PACKET_SENT. [len=" << head.m_cb << ", flags=" << head.m_flags << ", is_cmd=" << head.m_have_to_return_data <<", cmd_id = " << head.m_command << ", pr_v=" << head.m_protocol_version << ", uid=" << string_tools::get_str_from_guid_a(head.m_id) << "]"); + + return 1; + } + + + private: + bool have_some_data(SOCKET sock, int interval = 1) + { + fd_set fds; + FD_ZERO(&fds); + FD_SET(sock, &fds); + + fd_set fdse; + FD_ZERO(&fdse); + FD_SET(sock, &fdse); + + + timeval tv; + tv.tv_sec = interval; + tv.tv_usec = 0; + + int sel_res = select(0, &fds, 0, &fdse, &tv); + if(0 == sel_res) + return false; + else if(sel_res == SOCKET_ERROR) + { + if(m_is_stop) + return false; + int err_code = ::WSAGetLastError(); + LOG_ERROR("Filed to call select, err code = " << err_code); + disconnect(); + }else + { + if(fds.fd_array[0]) + {//some read operations was performed + return true; + }else if(fdse.fd_array[0]) + {//some error was at the socket + return true; + } + } + return false; + } + + + bool reciev_and_process_incoming_data() + { + bucket_head head = {0}; + boost::uint32_t conn_index = 0; + bool is_request = false; + std::string local_buff; + CRITICAL_REGION_BEGIN(m_reciev_packet_lock);//to protect from socket reconnect between head and body + + if(!recv_n(m_socket, (char*)&head, sizeof(head))) + { + if(m_is_stop) + return false; + LOG_ERROR("Failed to recv_n"); + return false; + } + + conn_index = boost::interprocess::ipcdetail::atomic_read32(&m_current_connection_index); + + if(head.m_signature!=LEVIN_SIGNATURE) + { + LOG_ERROR("Signature missmatch in response"); + return false; + } + + is_request = (head.m_protocol_version == LEVIN_PROTOCOL_VER_1 && head.m_flags&LEVIN_PACKET_REQUEST); + + + local_buff.resize((size_t)head.m_cb); + if(!recv_n(m_socket, local_buff)) + { + if(m_is_stop) + return false; + LOG_ERROR("Filed to reciev"); + return false; + } + CRITICAL_REGION_END(); + + LOG_PRINT_L4("LEVIN_PACKET_RECIEVED. [len=" << head.m_cb << ", flags=" << head.m_flags << ", is_cmd=" << head.m_have_to_return_data <<", cmd_id = " << head.m_command << ", pr_v=" << head.m_protocol_version << ", uid=" << string_tools::get_str_from_guid_a(head.m_id) << "]"); + + if(is_request) + { + CRITICAL_REGION_BEGIN(m_recieved_packets_lock); + m_recieved_packets.resize(m_recieved_packets.size() + 1); + m_recieved_packets.back().m_hd = head; + m_recieved_packets.back().m_body.swap(local_buff); + m_recieved_packets.back().m_connection_index = conn_index; + CRITICAL_REGION_END(); + /* + + */ + }else + {//this is some response + + CRITICAL_REGION_BEGIN(m_local_invoke_buff_lock); + m_local_invoke_buff.swap(local_buff); + m_invoke_res = head.m_return_code; + CRITICAL_REGION_END(); + boost::interprocess::ipcdetail::atomic_write32(&m_invoke_data_ready, 1); //m_invoke_data_ready = true; + m_invoke_cond.notify_all(); + + } + return true; + } + + bool reciever_thread() + { + LOG_PRINT_L3("[" << m_socket <<"] Socket reciever thread started.[m_threads_count=" << m_threads_count << "]"); + log_space::log_singletone::set_thread_log_prefix("RECIEVER_WORKER"); + boost::interprocess::ipcdetail::atomic_inc32(&m_threads_count); + + while(!m_is_stop) + { + if(!m_connected) + { + Sleep(100); + continue; + } + + if(have_some_data(m_socket, 1)) + { + if(!reciev_and_process_incoming_data()) + { + if(m_is_stop) + { + break;//boost::interprocess::ipcdetail::atomic_dec32(&m_threads_count); + //return true; + } + LOG_ERROR("Failed to reciev_and_process_incoming_data. shutting down"); + //boost::interprocess::ipcdetail::atomic_dec32(&m_threads_count); + //disconnect_no_wait(); + //break; + } + } + } + + boost::interprocess::ipcdetail::atomic_dec32(&m_threads_count); + LOG_PRINT_L3("[" << m_socket <<"] Socket reciever thread stopped.[m_threads_count=" << m_threads_count << "]"); + return true; + } + + bool process_recieved_packet(bucket_head& head, const std::string& local_buff, boost::uint32_t conn_index) + { + + net_utils::connection_context_base conn_context; + conn_context.m_remote_ip = m_ip; + conn_context.m_remote_port = m_port; + if(head.m_have_to_return_data) + { + std::string return_buff; + if(m_pcommands_handler) + head.m_return_code = m_pcommands_handler->invoke(head.m_id, head.m_command, local_buff, return_buff, conn_context); + else + head.m_return_code = LEVIN_ERROR_CONNECTION_HANDLER_NOT_DEFINED; + + + + head.m_cb = return_buff.size(); + head.m_have_to_return_data = false; + head.m_protocol_version = LEVIN_PROTOCOL_VER_1; + head.m_flags = LEVIN_PACKET_RESPONSE; + + std::string send_buff((const char*)&head, sizeof(head)); + send_buff += return_buff; + CRITICAL_REGION_BEGIN(m_send_lock); + if(conn_index != boost::interprocess::ipcdetail::atomic_read32(&m_current_connection_index)) + {//there was reconnect, send response back is not allowed + return true; + } + int res = ::send(m_socket, (const char*)send_buff.data(), send_buff.size(), 0); + if(res == SOCKET_ERROR) + { + int err_code = ::WSAGetLastError(); + LOG_ERROR("Failed to send, err = " << err_code); + return false; + } + CRITICAL_REGION_END(); + LOG_PRINT_L4("LEVIN_PACKET_SENT. [len=" << head.m_cb << ", flags=" << head.m_flags << ", is_cmd=" << head.m_have_to_return_data <<", cmd_id = " << head.m_command << ", pr_v=" << head.m_protocol_version << ", uid=" << string_tools::get_str_from_guid_a(head.m_id) << "]"); + + } + else + { + if(m_pcommands_handler) + m_pcommands_handler->notify(head.m_id, head.m_command, local_buff, conn_context); + } + + return true; + } + + bool handler_thread() + { + LOG_PRINT_L3("[" << m_socket <<"] Socket handler thread started.[m_threads_count=" << m_threads_count << "]"); + log_space::log_singletone::set_thread_log_prefix("HANDLER_WORKER"); + boost::interprocess::ipcdetail::atomic_inc32(&m_threads_count); + + while(!m_is_stop) + { + bool have_some_work = false; + std::string local_buff; + bucket_head bh = {0}; + boost::uint32_t conn_index = 0; + + CRITICAL_REGION_BEGIN(m_recieved_packets_lock); + if(m_recieved_packets.size()) + { + bh = m_recieved_packets.begin()->m_hd; + conn_index = m_recieved_packets.begin()->m_connection_index; + local_buff.swap(m_recieved_packets.begin()->m_body); + have_some_work = true; + m_recieved_packets.pop_front(); + } + CRITICAL_REGION_END(); + + if(have_some_work) + { + process_recieved_packet(bh, local_buff, conn_index); + }else + { + //Idle when no work + Sleep(30); + } + } + + boost::interprocess::ipcdetail::atomic_dec32(&m_threads_count); + LOG_PRINT_L3("[" << m_socket <<"] Socket handler thread stopped.[m_threads_count=" << m_threads_count << "]"); + return true; + } + }; + +} +} \ No newline at end of file diff --git a/contrib/epee/include/net/levin_client_async.inl b/contrib/epee/include/net/levin_client_async.inl new file mode 100644 index 000000000..e69de29bb diff --git a/contrib/epee/include/net/levin_helper.h b/contrib/epee/include/net/levin_helper.h new file mode 100644 index 000000000..a8406103c --- /dev/null +++ b/contrib/epee/include/net/levin_helper.h @@ -0,0 +1,137 @@ +// 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. +// + + + +#pragma once + +#include "levin_base.h" +#include "serializeble_struct_helper.h" + +namespace epee +{ +namespace levin +{ + template + bool pack_struct_to_levin_message(const t_struct& t, std::string& buff, int command_id) + { + buff.resize(sizeof(levin::bucket_head)); + levin::bucket_head& head = *(levin::bucket_head*)(&buff[0]); + head.m_signature = LEVIN_SIGNATURE; + head.m_cb = 0; + head.m_have_to_return_data = true; + head.m_command = command_id; + head.m_return_code = 1; + head.m_reservedA = rand(); //probably some flags in future + head.m_reservedB = rand(); //probably some check summ in future + + std::string buff_strg; + if(!StorageNamed::save_struct_as_storage_to_buff_t(t, buff_strg)) + return false; + + head.m_cb = buff_strg.size(); + buff.append(buff_strg); + return true; + } + + + bool pack_data_to_levin_message(const std::string& data, std::string& buff, int command_id) + { + buff.resize(sizeof(levin::bucket_head)); + levin::bucket_head& head = *(levin::bucket_head*)(&buff[0]); + head.m_signature = LEVIN_SIGNATURE; + head.m_cb = 0; + head.m_have_to_return_data = true; + head.m_command = command_id; + head.m_return_code = 1; + head.m_reservedA = rand(); //probably some flags in future + head.m_reservedB = rand(); //probably some check summ in future + + head.m_cb = data.size(); + buff.append(data); + return true; + } + + bool load_levin_data_from_levin_message(std::string& levin_data, const std::string& buff, int& command) + { + if(buff.size() < sizeof(levin::bucket_head) ) + { + LOG_PRINT_L3("size of buff(" << buff.size() << ") is too small, at load_struct_from_levin_message"); + return false; + } + + levin::bucket_head& head = *(levin::bucket_head*)(&buff[0]); + if(head.m_signature != LEVIN_SIGNATURE) + { + LOG_PRINT_L3("Failed to read signature in levin message, at load_struct_from_levin_message"); + return false; + } + if(head.m_cb != buff.size()-sizeof(levin::bucket_head)) + { + LOG_PRINT_L3("sizes missmatch, at load_struct_from_levin_message"); + return false; + } + + //std::string buff_strg; + levin_data.assign(&buff[sizeof(levin::bucket_head)], buff.size()-sizeof(levin::bucket_head)); + command = head.m_command; + return true; + } + + template + bool load_struct_from_levin_message(t_struct& t, const std::string& buff, int& command) + { + if(buff.size() < sizeof(levin::bucket_head) ) + { + LOG_ERROR("size of buff(" << buff.size() << ") is too small, at load_struct_from_levin_message"); + return false; + } + + levin::bucket_head& head = *(levin::bucket_head*)(&buff[0]); + if(head.m_signature != LEVIN_SIGNATURE) + { + LOG_ERROR("Failed to read signature in levin message, at load_struct_from_levin_message"); + return false; + } + if(head.m_cb != buff.size()-sizeof(levin::bucket_head)) + { + LOG_ERROR("sizes missmatch, at load_struct_from_levin_message"); + return false; + } + + std::string buff_strg; + buff_strg.assign(&buff[sizeof(levin::bucket_head)], buff.size()-sizeof(levin::bucket_head)); + + if(!StorageNamed::load_struct_from_storage_buff_t(t, buff_strg)) + { + LOG_ERROR("Failed to read storage, at load_struct_from_levin_message"); + return false; + } + command = head.m_command; + return true; + } +} +} \ No newline at end of file diff --git a/contrib/epee/include/net/levin_protocol_handler.h b/contrib/epee/include/net/levin_protocol_handler.h new file mode 100644 index 000000000..adc6e95d5 --- /dev/null +++ b/contrib/epee/include/net/levin_protocol_handler.h @@ -0,0 +1,178 @@ +// 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. +// + + + +#ifndef _LEVIN_PROTOCOL_HANDLER_H_ +#define _LEVIN_PROTOCOL_HANDLER_H_ + +#include +#include "levin_base.h" + +namespace epee +{ +namespace levin +{ + template + struct protocl_handler_config + { + levin_commands_handler* m_pcommands_handler; + }; + + template + class protocol_handler + { + public: + typedef t_connection_context connection_context; + typedef protocl_handler_config config_type; + + protocol_handler(net_utils::i_service_endpoint* psnd_hndlr, config_type& config, t_connection_context& conn_context); + virtual ~protocol_handler(){} + + virtual bool handle_recv(const void* ptr, size_t cb); + + bool after_init_connection(){return true;} + private: + enum connection_data_state + { + conn_state_reading_head, + conn_state_reading_body + }; + + + config_type& m_config; + t_connection_context& m_conn_context; + net_utils::i_service_endpoint* m_psnd_hndlr; + std::string m_cach_in_buffer; + connection_data_state m_state; + bucket_head m_current_head; + }; + + template + protocol_handler::protocol_handler(net_utils::i_service_endpoint* psnd_hndlr, config_type& config, t_connection_context& conn_context): + m_config(config), + m_conn_context(conn_context), + m_psnd_hndlr(psnd_hndlr), + m_state(conn_state_reading_head), + m_current_head(bucket_head()) + {} + + template + bool protocol_handler::handle_recv(const void* ptr, size_t cb) + { + if(!m_config.m_pcommands_handler) + { + LOG_ERROR("Command handler not set!"); + return false; + } + m_cach_in_buffer.append((const char*)ptr, cb); + + bool is_continue = true; + while(is_continue) + { + switch(m_state) + { + case conn_state_reading_head: + if(m_cach_in_buffer.size() < sizeof(bucket_head)) + { + if(m_cach_in_buffer.size() >= sizeof(boost::uint64_t) && *((boost::uint64_t*)m_cach_in_buffer.data()) != LEVIN_SIGNATURE) + { + LOG_ERROR("Signature missmatch on accepted connection"); + return false; + } + is_continue = false; + break; + } + { + bucket_head* phead = (bucket_head*)m_cach_in_buffer.data(); + if(LEVIN_SIGNATURE != phead->m_signature) + { + LOG_ERROR("Signature missmatch on accepted connection"); + return false; + } + m_current_head = *phead; + } + m_cach_in_buffer.erase(0, sizeof(bucket_head)); + m_state = conn_state_reading_body; + break; + case conn_state_reading_body: + if(m_cach_in_buffer.size() < m_current_head.m_cb) + { + is_continue = false; + break; + } + { + std::string buff_to_invoke; + if(m_cach_in_buffer.size() == m_current_head.m_cb) + buff_to_invoke.swap(m_cach_in_buffer); + else + { + buff_to_invoke.assign(m_cach_in_buffer, 0, (std::string::size_type)m_current_head.m_cb); + m_cach_in_buffer.erase(0, (std::string::size_type)m_current_head.m_cb); + } + + + if(m_current_head.m_have_to_return_data) + { + std::string return_buff; + m_current_head.m_return_code = m_config.m_pcommands_handler->invoke(m_current_head.m_command, buff_to_invoke, return_buff, m_conn_context); + m_current_head.m_cb = return_buff.size(); + m_current_head.m_have_to_return_data = false; + std::string send_buff((const char*)&m_current_head, sizeof(m_current_head)); + send_buff += return_buff; + + if(!m_psnd_hndlr->do_send(send_buff.data(), send_buff.size())) + return false; + + } + else + m_config.m_pcommands_handler->notify(m_current_head.m_command, buff_to_invoke, m_conn_context); + } + m_state = conn_state_reading_head; + break; + default: + LOG_ERROR("Undefined state in levin_server_impl::connection_handler, m_state=" << m_state); + return false; + } + } + + return true; + } + + + + + + + +} +} + + + + +#endif //_LEVIN_PROTOCOL_HANDLER_H_ + diff --git a/contrib/epee/include/net/levin_protocol_handler_async.h b/contrib/epee/include/net/levin_protocol_handler_async.h new file mode 100644 index 000000000..dc4f41146 --- /dev/null +++ b/contrib/epee/include/net/levin_protocol_handler_async.h @@ -0,0 +1,778 @@ +// 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. +// + +#pragma once +#include +#include +#include + +#include + +#include "levin_base.h" +#include "misc_language.h" + + +namespace epee +{ +namespace levin +{ + +/************************************************************************/ +/* */ +/************************************************************************/ +template +class async_protocol_handler; + +template +class async_protocol_handler_config +{ + typedef std::map* > connections_map; + critical_section m_connects_lock; + connections_map m_connects; + + void add_connection(async_protocol_handler* pc); + void del_connection(async_protocol_handler* pc); + + async_protocol_handler* find_connection(boost::uuids::uuid connection_id) const; + int find_and_lock_connection(boost::uuids::uuid connection_id, async_protocol_handler*& aph); + + friend class async_protocol_handler; + +public: + typedef t_connection_context connection_context; + levin_commands_handler* m_pcommands_handler; + boost::uint64_t m_max_packet_size; + boost::uint64_t m_invoke_timeout; + + int invoke(int command, const std::string& in_buff, std::string& buff_out, boost::uuids::uuid connection_id); + template + int invoke_async(int command, const std::string& in_buff, boost::uuids::uuid connection_id, callback_t cb, size_t timeout = LEVIN_DEFAULT_TIMEOUT_PRECONFIGURED); + + int notify(int command, const std::string& in_buff, boost::uuids::uuid connection_id); + bool close(boost::uuids::uuid connection_id); + bool update_connection_context(const t_connection_context& contxt); + bool request_callback(boost::uuids::uuid connection_id); + template + bool foreach_connection(callback_t cb); + size_t get_connections_count(); + + async_protocol_handler_config():m_pcommands_handler(NULL), m_max_packet_size(LEVIN_DEFAULT_MAX_PACKET_SIZE) + {} +}; + + +/************************************************************************/ +/* */ +/************************************************************************/ +template +class async_protocol_handler +{ +public: + typedef t_connection_context connection_context; + typedef async_protocol_handler_config config_type; + + enum stream_state + { + stream_state_head, + stream_state_body + }; + + std::atomic m_deletion_initiated; + std::atomic m_protocol_released; + volatile uint32_t m_invoke_buf_ready; + + volatile int m_invoke_result_code; + + critical_section m_local_inv_buff_lock; + std::string m_local_inv_buff; + + critical_section m_send_lock; + critical_section m_call_lock; + + volatile uint32_t m_wait_count; + volatile uint32_t m_close_called; + bucket_head2 m_current_head; + net_utils::i_service_endpoint* m_pservice_endpoint; + config_type& m_config; + t_connection_context& m_connection_context; + + std::string m_cache_in_buffer; + stream_state m_state; + + boost::int32_t m_oponent_protocol_ver; + bool m_connection_initialized; + + struct invoke_response_handler_base + { + virtual bool handle(int res, const std::string& buff, connection_context& context)=0; + virtual bool is_timer_started() const=0; + virtual void cancel()=0; + virtual bool cancel_timer()=0; + }; + template + struct anvoke_handler: invoke_response_handler_base + { + anvoke_handler(const callback_t& cb, uint64_t timeout, async_protocol_handler& con, int command) + :m_cb(cb), m_con(con), m_timer(con.m_pservice_endpoint->get_io_service()), m_timer_started(false), + m_cancel_timer_called(false), m_timer_cancelled(false), m_command(command) + { + if(m_con.start_outer_call()) + { + m_timer.expires_from_now(boost::posix_time::milliseconds(timeout)); + m_timer.async_wait([&con, command, cb](const boost::system::error_code& ec) + { + if(ec == boost::asio::error::operation_aborted) + return; + LOG_PRINT_CC(con.get_context_ref(), "Timeout on invoke operation happened, command: " << command, LOG_LEVEL_2); + std::string fake; + cb(LEVIN_ERROR_CONNECTION_TIMEDOUT, fake, con.get_context_ref()); + con.close(); + con.finish_outer_call(); + }); + m_timer_started = true; + } + } + virtual ~anvoke_handler() + {} + callback_t m_cb; + async_protocol_handler& m_con; + boost::asio::deadline_timer m_timer; + bool m_timer_started; + bool m_cancel_timer_called; + bool m_timer_cancelled; + int m_command; + virtual bool handle(int res, const std::string& buff, typename async_protocol_handler::connection_context& context) + { + if(!cancel_timer()) + return false; + m_cb(res, buff, context); + m_con.finish_outer_call(); + return true; + } + virtual bool is_timer_started() const + { + return m_timer_started; + } + virtual void cancel() + { + if(cancel_timer()) + { + std::string fake; + m_cb(LEVIN_ERROR_CONNECTION_DESTROYED, fake, m_con.get_context_ref()); + m_con.finish_outer_call(); + } + } + virtual bool cancel_timer() + { + if(!m_cancel_timer_called) + { + m_cancel_timer_called = true; + boost::system::error_code ignored_ec; + m_timer_cancelled = 1 == m_timer.cancel(ignored_ec); + } + return m_timer_cancelled; + } + }; + critical_section m_invoke_response_handlers_lock; + std::list > m_invoke_response_handlers; + + template + bool add_invoke_response_handler(callback_t cb, uint64_t timeout, async_protocol_handler& con, int command) + { + CRITICAL_REGION_LOCAL(m_invoke_response_handlers_lock); + boost::shared_ptr handler(boost::make_shared>(cb, timeout, con, command)); + m_invoke_response_handlers.push_back(handler); + return handler->is_timer_started(); + } + template friend struct anvoke_handler; +public: + async_protocol_handler(net_utils::i_service_endpoint* psnd_hndlr, + config_type& config, + t_connection_context& conn_context): + m_current_head(bucket_head2()), + m_pservice_endpoint(psnd_hndlr), + m_config(config), + m_connection_context(conn_context), + m_state(stream_state_head) + { + m_close_called = 0; + m_deletion_initiated = false; + m_protocol_released = false; + m_wait_count = 0; + m_oponent_protocol_ver = 0; + m_connection_initialized = false; + } + virtual ~async_protocol_handler() + { + m_deletion_initiated = true; + if(m_connection_initialized) + { + m_config.del_connection(this); + } + + for (size_t i = 0; i < 60 * 1000 / 100 && 0 != boost::interprocess::ipcdetail::atomic_read32(&m_wait_count); ++i) + { + misc_utils::sleep_no_w(100); + } + CHECK_AND_ASSERT_MES_NO_RET(0 == boost::interprocess::ipcdetail::atomic_read32(&m_wait_count), "Failed to wait for operation completion. m_wait_count = " << m_wait_count); + + LOG_PRINT_CC(m_connection_context, "~async_protocol_handler()", LOG_LEVEL_4); + } + + bool start_outer_call() + { + LOG_PRINT_CC_L4(m_connection_context, "[levin_protocol] -->> start_outer_call"); + if(!m_pservice_endpoint->add_ref()) + { + LOG_PRINT_CC_RED(m_connection_context, "[levin_protocol] -->> start_outer_call failed", LOG_LEVEL_4); + return false; + } + boost::interprocess::ipcdetail::atomic_inc32(&m_wait_count); + return true; + } + bool finish_outer_call() + { + LOG_PRINT_CC_L4(m_connection_context, "[levin_protocol] <<-- finish_outer_call"); + boost::interprocess::ipcdetail::atomic_dec32(&m_wait_count); + m_pservice_endpoint->release(); + return true; + } + + bool release_protocol() + { + decltype(m_invoke_response_handlers) local_invoke_response_handlers; + CRITICAL_REGION_BEGIN(m_invoke_response_handlers_lock); + local_invoke_response_handlers.swap(m_invoke_response_handlers); + m_protocol_released = true; + CRITICAL_REGION_END(); + + // Never call callback inside critical section, that can cause deadlock. Callback can be called when + // invoke_response_handler_base is cancelled + std::for_each(local_invoke_response_handlers.begin(), local_invoke_response_handlers.end(), [](const boost::shared_ptr& pinv_resp_hndlr) { + pinv_resp_hndlr->cancel(); + }); + + return true; + } + + bool close() + { + boost::interprocess::ipcdetail::atomic_inc32(&m_close_called); + + m_pservice_endpoint->close(); + return true; + } + + void update_connection_context(const connection_context& contxt) + { + m_connection_context = contxt; + } + + void request_callback() + { + misc_utils::auto_scope_leave_caller scope_exit_handler = misc_utils::create_scope_leave_handler( + boost::bind(&async_protocol_handler::finish_outer_call, this)); + + m_pservice_endpoint->request_callback(); + } + + void handle_qued_callback() + { + m_config.m_pcommands_handler->callback(m_connection_context); + } + + virtual bool handle_recv(const void* ptr, size_t cb) + { + if(boost::interprocess::ipcdetail::atomic_read32(&m_close_called)) + return false; //closing connections + + if(!m_config.m_pcommands_handler) + { + LOG_ERROR_CC(m_connection_context, "Commands handler not set!"); + return false; + } + + if(m_cache_in_buffer.size() + cb > m_config.m_max_packet_size) + { + LOG_ERROR_CC(m_connection_context, "Maximum packet size exceed!, m_max_packet_size = " << m_config.m_max_packet_size + << ", packet received " << m_cache_in_buffer.size() + cb + << ", connection will be closed."); + return false; + } + + m_cache_in_buffer.append((const char*)ptr, cb); + + bool is_continue = true; + while(is_continue) + { + switch(m_state) + { + case stream_state_body: + if(m_cache_in_buffer.size() < m_current_head.m_cb) + { + is_continue = false; + break; + } + { + std::string buff_to_invoke; + if(m_cache_in_buffer.size() == m_current_head.m_cb) + buff_to_invoke.swap(m_cache_in_buffer); + else + { + buff_to_invoke.assign(m_cache_in_buffer, 0, (std::string::size_type)m_current_head.m_cb); + m_cache_in_buffer.erase(0, (std::string::size_type)m_current_head.m_cb); + } + + bool is_response = (m_oponent_protocol_ver == LEVIN_PROTOCOL_VER_1 && m_current_head.m_flags&LEVIN_PACKET_RESPONSE); + + LOG_PRINT_CC_L4(m_connection_context, "LEVIN_PACKET_RECIEVED. [len=" << m_current_head.m_cb + << ", flags" << m_current_head.m_flags + << ", r?=" << m_current_head.m_have_to_return_data + <<", cmd = " << m_current_head.m_command + << ", v=" << m_current_head.m_protocol_version); + + if(is_response) + {//response to some invoke + + epee::critical_region_t invoke_response_handlers_guard(m_invoke_response_handlers_lock); + if(!m_invoke_response_handlers.empty()) + {//async call scenario + boost::shared_ptr response_handler = m_invoke_response_handlers.front(); + bool timer_cancelled = response_handler->cancel_timer(); + // Don't pop handler, to avoid destroying it + if(timer_cancelled) + m_invoke_response_handlers.pop_front(); + invoke_response_handlers_guard.unlock(); + + if(timer_cancelled) + response_handler->handle(m_current_head.m_command, buff_to_invoke, m_connection_context); + } + else + { + invoke_response_handlers_guard.unlock(); + //use sync call scenario + if(!boost::interprocess::ipcdetail::atomic_read32(&m_wait_count) && !boost::interprocess::ipcdetail::atomic_read32(&m_close_called)) + { + LOG_ERROR_CC(m_connection_context, "no active invoke when response came, wtf?"); + return false; + }else + { + CRITICAL_REGION_BEGIN(m_local_inv_buff_lock); + buff_to_invoke.swap(m_local_inv_buff); + buff_to_invoke.clear(); + m_invoke_result_code = m_current_head.m_return_code; + CRITICAL_REGION_END(); + boost::interprocess::ipcdetail::atomic_write32(&m_invoke_buf_ready, 1); + } + } + }else + { + if(m_current_head.m_have_to_return_data) + { + std::string return_buff; + m_current_head.m_return_code = m_config.m_pcommands_handler->invoke( + m_current_head.m_command, + buff_to_invoke, + return_buff, + m_connection_context); + m_current_head.m_cb = return_buff.size(); + m_current_head.m_have_to_return_data = false; + m_current_head.m_protocol_version = LEVIN_PROTOCOL_VER_1; + m_current_head.m_flags = LEVIN_PACKET_RESPONSE; + std::string send_buff((const char*)&m_current_head, sizeof(m_current_head)); + send_buff += return_buff; + CRITICAL_REGION_BEGIN(m_send_lock); + if(!m_pservice_endpoint->do_send(send_buff.data(), send_buff.size())) + return false; + CRITICAL_REGION_END(); + LOG_PRINT_CC_L4(m_connection_context, "LEVIN_PACKET_SENT. [len=" << m_current_head.m_cb + << ", flags" << m_current_head.m_flags + << ", r?=" << m_current_head.m_have_to_return_data + <<", cmd = " << m_current_head.m_command + << ", ver=" << m_current_head.m_protocol_version); + } + else + m_config.m_pcommands_handler->notify(m_current_head.m_command, buff_to_invoke, m_connection_context); + } + } + m_state = stream_state_head; + break; + case stream_state_head: + { + if(m_cache_in_buffer.size() < sizeof(bucket_head2)) + { + if(m_cache_in_buffer.size() >= sizeof(boost::uint64_t) && *((boost::uint64_t*)m_cache_in_buffer.data()) != LEVIN_SIGNATURE) + { + LOG_ERROR_CC(m_connection_context, "Signature mismatch, connection will be closed"); + return false; + } + is_continue = false; + break; + } + + bucket_head2* phead = (bucket_head2*)m_cache_in_buffer.data(); + if(LEVIN_SIGNATURE != phead->m_signature) + { + LOG_ERROR_CC(m_connection_context, "Signature mismatch, connection will be closed"); + return false; + } + m_current_head = *phead; + + m_cache_in_buffer.erase(0, sizeof(bucket_head2)); + m_state = stream_state_body; + m_oponent_protocol_ver = m_current_head.m_protocol_version; + if(m_current_head.m_cb > m_config.m_max_packet_size) + { + LOG_ERROR_CC(m_connection_context, "Maximum packet size exceed!, m_max_packet_size = " << m_config.m_max_packet_size + << ", packet header received " << m_current_head.m_cb + << ", connection will be closed."); + return false; + } + } + break; + default: + LOG_ERROR_CC(m_connection_context, "Undefined state in levin_server_impl::connection_handler, m_state=" << m_state); + return false; + } + } + + return true; + } + + bool after_init_connection() + { + if (!m_connection_initialized) + { + m_connection_initialized = true; + m_config.add_connection(this); + } + return true; + } + + template + bool async_invoke(int command, const std::string& in_buff, callback_t cb, size_t timeout = LEVIN_DEFAULT_TIMEOUT_PRECONFIGURED) + { + misc_utils::auto_scope_leave_caller scope_exit_handler = misc_utils::create_scope_leave_handler( + boost::bind(&async_protocol_handler::finish_outer_call, this)); + + if(timeout == LEVIN_DEFAULT_TIMEOUT_PRECONFIGURED) + timeout = m_config.m_invoke_timeout; + + int err_code = LEVIN_OK; + do + { + if(m_deletion_initiated) + { + err_code = LEVIN_ERROR_CONNECTION_DESTROYED; + break; + } + + CRITICAL_REGION_LOCAL(m_call_lock); + + if(m_deletion_initiated) + { + err_code = LEVIN_ERROR_CONNECTION_DESTROYED; + break; + } + + bucket_head2 head = {0}; + head.m_signature = LEVIN_SIGNATURE; + head.m_cb = in_buff.size(); + head.m_have_to_return_data = true; + + head.m_flags = LEVIN_PACKET_REQUEST; + head.m_command = command; + head.m_protocol_version = LEVIN_PROTOCOL_VER_1; + + boost::interprocess::ipcdetail::atomic_write32(&m_invoke_buf_ready, 0); + CRITICAL_REGION_BEGIN(m_send_lock); + CRITICAL_REGION_LOCAL1(m_invoke_response_handlers_lock); + if(!m_pservice_endpoint->do_send(&head, sizeof(head))) + { + LOG_ERROR_CC(m_connection_context, "Failed to do_send"); + err_code = LEVIN_ERROR_CONNECTION; + break; + } + + if(!m_pservice_endpoint->do_send(in_buff.data(), (int)in_buff.size())) + { + LOG_ERROR_CC(m_connection_context, "Failed to do_send"); + err_code = LEVIN_ERROR_CONNECTION; + break; + } + + if(!add_invoke_response_handler(cb, timeout, *this, command)) + { + err_code = LEVIN_ERROR_CONNECTION_DESTROYED; + break; + } + CRITICAL_REGION_END(); + } while (false); + + if (LEVIN_OK != err_code) + { + std::string stub_buff; + // Never call callback inside critical section, that can cause deadlock + cb(err_code, stub_buff, m_connection_context); + return false; + } + + return true; + } + + int invoke(int command, const std::string& in_buff, std::string& buff_out) + { + misc_utils::auto_scope_leave_caller scope_exit_handler = misc_utils::create_scope_leave_handler( + boost::bind(&async_protocol_handler::finish_outer_call, this)); + + if(m_deletion_initiated) + return LEVIN_ERROR_CONNECTION_DESTROYED; + + CRITICAL_REGION_LOCAL(m_call_lock); + + if(m_deletion_initiated) + return LEVIN_ERROR_CONNECTION_DESTROYED; + + bucket_head2 head = {0}; + head.m_signature = LEVIN_SIGNATURE; + head.m_cb = in_buff.size(); + head.m_have_to_return_data = true; + + head.m_flags = LEVIN_PACKET_REQUEST; + head.m_command = command; + head.m_protocol_version = LEVIN_PROTOCOL_VER_1; + + boost::interprocess::ipcdetail::atomic_write32(&m_invoke_buf_ready, 0); + CRITICAL_REGION_BEGIN(m_send_lock); + if(!m_pservice_endpoint->do_send(&head, sizeof(head))) + { + LOG_ERROR_CC(m_connection_context, "Failed to do_send"); + return LEVIN_ERROR_CONNECTION; + } + + if(!m_pservice_endpoint->do_send(in_buff.data(), (int)in_buff.size())) + { + LOG_ERROR_CC(m_connection_context, "Failed to do_send"); + return LEVIN_ERROR_CONNECTION; + } + CRITICAL_REGION_END(); + + LOG_PRINT_CC_L4(m_connection_context, "LEVIN_PACKET_SENT. [len=" << head.m_cb + << ", f=" << head.m_flags + << ", r?=" << head.m_have_to_return_data + << ", cmd = " << head.m_command + << ", ver=" << head.m_protocol_version); + + uint64_t ticks_start = misc_utils::get_tick_count(); + + while(!boost::interprocess::ipcdetail::atomic_read32(&m_invoke_buf_ready) && !m_deletion_initiated && !m_protocol_released) + { + if(misc_utils::get_tick_count() - ticks_start > m_config.m_invoke_timeout) + { + LOG_PRINT_CC_L2(m_connection_context, "invoke timeout (" << m_config.m_invoke_timeout << "), closing connection "); + close(); + return LEVIN_ERROR_CONNECTION_TIMEDOUT; + } + if(!m_pservice_endpoint->call_run_once_service_io()) + return LEVIN_ERROR_CONNECTION_DESTROYED; + } + + if(m_deletion_initiated || m_protocol_released) + return LEVIN_ERROR_CONNECTION_DESTROYED; + + CRITICAL_REGION_BEGIN(m_local_inv_buff_lock); + buff_out.swap(m_local_inv_buff); + m_local_inv_buff.clear(); + CRITICAL_REGION_END(); + + return m_invoke_result_code; + } + + int notify(int command, const std::string& in_buff) + { + misc_utils::auto_scope_leave_caller scope_exit_handler = misc_utils::create_scope_leave_handler( + boost::bind(&async_protocol_handler::finish_outer_call, this)); + + if(m_deletion_initiated) + return LEVIN_ERROR_CONNECTION_DESTROYED; + + CRITICAL_REGION_LOCAL(m_call_lock); + + if(m_deletion_initiated) + return LEVIN_ERROR_CONNECTION_DESTROYED; + + bucket_head2 head = {0}; + head.m_signature = LEVIN_SIGNATURE; + head.m_have_to_return_data = false; + head.m_cb = in_buff.size(); + + head.m_command = command; + head.m_protocol_version = LEVIN_PROTOCOL_VER_1; + head.m_flags = LEVIN_PACKET_REQUEST; + CRITICAL_REGION_BEGIN(m_send_lock); + if(!m_pservice_endpoint->do_send(&head, sizeof(head))) + { + LOG_ERROR_CC(m_connection_context, "Failed to do_send()"); + return -1; + } + + if(!m_pservice_endpoint->do_send(in_buff.data(), (int)in_buff.size())) + { + LOG_ERROR("Failed to do_send()"); + return -1; + } + CRITICAL_REGION_END(); + LOG_PRINT_CC_L4(m_connection_context, "LEVIN_PACKET_SENT. [len=" << head.m_cb << + ", f=" << head.m_flags << + ", r?=" << head.m_have_to_return_data << + ", cmd = " << head.m_command << + ", ver=" << head.m_protocol_version); + + return 1; + } + //------------------------------------------------------------------------------------------ + boost::uuids::uuid get_connection_id() {return m_connection_context.m_connection_id;} + //------------------------------------------------------------------------------------------ + t_connection_context& get_context_ref() {return m_connection_context;} +}; +//------------------------------------------------------------------------------------------ +template +void async_protocol_handler_config::del_connection(async_protocol_handler* pconn) +{ + CRITICAL_REGION_BEGIN(m_connects_lock); + m_connects.erase(pconn->get_connection_id()); + CRITICAL_REGION_END(); + m_pcommands_handler->on_connection_close(pconn->m_connection_context); +} +//------------------------------------------------------------------------------------------ +template +void async_protocol_handler_config::add_connection(async_protocol_handler* pconn) +{ + CRITICAL_REGION_BEGIN(m_connects_lock); + m_connects[pconn->get_connection_id()] = pconn; + CRITICAL_REGION_END(); + m_pcommands_handler->on_connection_new(pconn->m_connection_context); +} +//------------------------------------------------------------------------------------------ +template +async_protocol_handler* async_protocol_handler_config::find_connection(boost::uuids::uuid connection_id) const +{ + auto it = m_connects.find(connection_id); + return it == m_connects.end() ? 0 : it->second; +} +//------------------------------------------------------------------------------------------ +template +int async_protocol_handler_config::find_and_lock_connection(boost::uuids::uuid connection_id, async_protocol_handler*& aph) +{ + CRITICAL_REGION_LOCAL(m_connects_lock); + aph = find_connection(connection_id); + if(0 == aph) + return LEVIN_ERROR_CONNECTION_NOT_FOUND; + if(!aph->start_outer_call()) + return LEVIN_ERROR_CONNECTION_DESTROYED; + return LEVIN_OK; +} +//------------------------------------------------------------------------------------------ +template +int async_protocol_handler_config::invoke(int command, const std::string& in_buff, std::string& buff_out, boost::uuids::uuid connection_id) +{ + async_protocol_handler* aph; + int r = find_and_lock_connection(connection_id, aph); + return LEVIN_OK == r ? aph->invoke(command, in_buff, buff_out) : r; +} +//------------------------------------------------------------------------------------------ +template template +int async_protocol_handler_config::invoke_async(int command, const std::string& in_buff, boost::uuids::uuid connection_id, callback_t cb, size_t timeout) +{ + async_protocol_handler* aph; + int r = find_and_lock_connection(connection_id, aph); + return LEVIN_OK == r ? aph->async_invoke(command, in_buff, cb, timeout) : r; +} +//------------------------------------------------------------------------------------------ +template template +bool async_protocol_handler_config::foreach_connection(callback_t cb) +{ + CRITICAL_REGION_LOCAL(m_connects_lock); + for(auto& c: m_connects) + { + async_protocol_handler* aph = c.second; + if(!cb(aph->get_context_ref())) + return false; + } + return true; +} +//------------------------------------------------------------------------------------------ +template +size_t async_protocol_handler_config::get_connections_count() +{ + CRITICAL_REGION_LOCAL(m_connects_lock); + return m_connects.size(); +} +//------------------------------------------------------------------------------------------ +template +int async_protocol_handler_config::notify(int command, const std::string& in_buff, boost::uuids::uuid connection_id) +{ + async_protocol_handler* aph; + int r = find_and_lock_connection(connection_id, aph); + return LEVIN_OK == r ? aph->notify(command, in_buff) : r; +} +//------------------------------------------------------------------------------------------ +template +bool async_protocol_handler_config::close(boost::uuids::uuid connection_id) +{ + CRITICAL_REGION_LOCAL(m_connects_lock); + async_protocol_handler* aph = find_connection(connection_id); + return 0 != aph ? aph->close() : false; +} +//------------------------------------------------------------------------------------------ +template +bool async_protocol_handler_config::update_connection_context(const t_connection_context& contxt) +{ + CRITICAL_REGION_LOCAL(m_connects_lock); + async_protocol_handler* aph = find_connection(contxt.m_connection_id); + if(0 == aph) + return false; + aph->update_connection_context(contxt); + return true; +} +//------------------------------------------------------------------------------------------ +template +bool async_protocol_handler_config::request_callback(boost::uuids::uuid connection_id) +{ + async_protocol_handler* aph; + int r = find_and_lock_connection(connection_id, aph); + if(LEVIN_OK == r) + { + aph->request_callback(); + return true; + } + else + { + return false; + } +} +} +} diff --git a/contrib/epee/include/net/levin_server_cp.h b/contrib/epee/include/net/levin_server_cp.h new file mode 100644 index 000000000..8ece35059 --- /dev/null +++ b/contrib/epee/include/net/levin_server_cp.h @@ -0,0 +1,47 @@ +// 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. +// + + + + +#ifndef _HTTP_SERVER_CP_H_ +#define _HTTP_SERVER_CP_H_ + +#include "abstract_tcp_server_cp.h" +#include "levin_protocol_handler.h" +namespace epee +{ +namespace net_utils +{ + typedef cp_server_impl cp_levin_server; +} +} + + + +#endif + + diff --git a/contrib/epee/include/net/levin_server_cp2.h b/contrib/epee/include/net/levin_server_cp2.h new file mode 100644 index 000000000..b29d49bf8 --- /dev/null +++ b/contrib/epee/include/net/levin_server_cp2.h @@ -0,0 +1,49 @@ +// 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. +// + + + +#ifndef _HTTP_SERVER_CP_H_ +#define _HTTP_SERVER_CP_H_ + +#include "abstract_tcp_server2.h" +#include "levin_protocol_handler.h" +#include "levin_protocol_handler_async.h" + +namespace epee +{ +namespace net_utils +{ + typedef boosted_tcp_server > boosted_levin_server; + typedef boosted_tcp_server > boosted_levin_async_server; +} +} + + + +#endif + + diff --git a/contrib/epee/include/net/local_ip.h b/contrib/epee/include/net/local_ip.h new file mode 100644 index 000000000..028ad73ef --- /dev/null +++ b/contrib/epee/include/net/local_ip.h @@ -0,0 +1,72 @@ +// 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. +// + + +#pragma once + +namespace epee +{ + namespace net_utils + { + inline + bool is_ip_local(boost::uint32_t ip) + { + /* + local ip area + 10.0.0.0 — 10.255.255.255 + 172.16.0.0 — 172.31.255.255 + 192.168.0.0 — 192.168.255.255 + */ + if( (ip | 0xffffff00) == 0xffffff0a) + return true; + + if( (ip | 0xffff0000) == 0xffffa8c0) + return true; + + if( (ip | 0xffffff00) == 0xffffffac) + { + uint32_t second_num = (ip << 8) & 0xff000000; + if(second_num >= 16 && second_num <= 31 ) + return true; + } + return false; + } + inline + bool is_ip_loopback(boost::uint32_t ip) + { + if( (ip | 0xffffff00) == 0xffffff7f) + return true; + //MAKE_IP + /* + loopback ip + 127.0.0.0 — 127.255.255.255 + */ + return false; + } + + } +} + diff --git a/contrib/epee/include/net/multiprotocols_server.h b/contrib/epee/include/net/multiprotocols_server.h new file mode 100644 index 000000000..4807a4421 --- /dev/null +++ b/contrib/epee/include/net/multiprotocols_server.h @@ -0,0 +1,47 @@ +// 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. +// + + + +#ifndef _MULTIPROTOCOLS_SERVER_H_ +#define _MULTIPROTOCOLS_SERVER_H_ + +//#include "abstract_tcp_server_cp.h" +#include "protocol_switcher.h" +#include "abstract_tcp_server2.h" + +namespace epee +{ +namespace net_utils +{ + //typedef cp_server_impl multiprotocol_server; + typedef boosted_tcp_server boosted_multiprotocol_server; +} +} + + +#endif //_MULTIPROTOCOLS_SERVER_H_ + diff --git a/contrib/epee/include/net/munin_connection_handler.h b/contrib/epee/include/net/munin_connection_handler.h new file mode 100644 index 000000000..8579339c5 --- /dev/null +++ b/contrib/epee/include/net/munin_connection_handler.h @@ -0,0 +1,376 @@ +// 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. +// + + + +#ifndef _MUNIN_CONNECTION_HANDLER_H_ +#define _MUNIN_CONNECTION_HANDLER_H_ + +#include +#include "net_utils_base.h" +#include "to_nonconst_iterator.h" +#include "http_base.h" +#include "reg_exp_definer.h" + +#define MUNIN_ARGS_DEFAULT(vertial_lable_str) "graph_args --base 1000 -l 0 --vertical-label " vertial_lable_str " \n" +#define MUNIN_ARGS_FORCE_AUPPER_LIMIT(vertial_lable_str, limit) "graph_args --base 1000 -l 0 --vertical-label " vertial_lable_str " --rigid --upper-limit " limit " \n" +#define MUNIN_TITLE(title_str) "graph_title " title_str "\n" +#define MUNIN_CATEGORY(category_str) "graph_category " category_str "\n" +#define MUNIN_INFO(info_str) "graph_info " info_str "\n" +#define MUNIN_ENTRY(var_name) #var_name".label " #var_name "\n" #var_name".info "#var_name".\n" +#define MUNIN_ENTRY_AREA(var_name) #var_name".label " #var_name "\n" #var_name".info "#var_name".\n" #var_name".draw AREASTACK\n" +#define MUNIN_ENTRY_ALIAS(var_name, alias) #var_name".label " #alias"\n" #var_name".info "#alias".\n" +#define BEGIN_MUNIN_SERVICE(servivece_name_str) if(servivece_name_str == pservice->m_service_name) { +#define END_MUNIN_SERVICE() } +#define MUNIN_SERVICE_PARAM(munin_var_name_str, variable) paramters_text += std::string() + munin_var_name_str ".value " + boost::lexical_cast(variable) + "\n" + + + + +namespace epee +{ +namespace net_utils +{ + namespace munin + { + + + /************************************************************************/ + /* */ + /************************************************************************/ + struct munin_service; + + struct munin_service_data_provider + { + virtual bool update_service_data(munin_service* pservice, std::string& paramters_text)=0; + }; + + struct munin_service + { + std::string m_service_name; + std::string m_service_config_string; + munin_service_data_provider* m_pdata_provider; + }; + + struct node_server_config + { + std::list m_services; + //TODO: + }; + + struct fake_send_handler: public i_service_endpoint + { + virtual bool do_send(const void* ptr, size_t cb) + { + m_cache += std::string((const char*)ptr, cb); + return true; + } + public: + + std::string m_cache; + }; + + /************************************************************************/ + /* */ + /************************************************************************/ + class munin_node_server_connection_handler + { + public: + typedef node_server_config config_type; + typedef connection_context_base connection_context; + + munin_node_server_connection_handler(i_service_endpoint* psnd_hndlr, config_type& config, const connection_context_base& context):m_psnd_hndlr(psnd_hndlr), + m_machine_state(http_state_retriving_comand_line), + m_config(config) + { + init(); + } + virtual ~munin_node_server_connection_handler() + { + + } + + bool release_protocol() + { + return true; + } + bool after_init_connection() + { + std::string hello_str = "# munin node at "; + hello_str += m_host_name + "\n"; + send_hook(hello_str); + return true; + } + + virtual bool thread_init() + { + return true; + } + + virtual bool thread_deinit() + { + return true; + } + + void handle_qued_callback() + { + + } + + virtual bool handle_recv(const void* ptr, size_t cb) + { + + const char* pbuff = (const char*)ptr; + std::string recvd_buff(pbuff, cb); + LOG_PRINT("munin_recv: \n" << recvd_buff, LOG_LEVEL_3); + + m_cache += recvd_buff; + + bool stop_handling = false; + while(!stop_handling) + { + switch(m_machine_state) + { + case http_state_retriving_comand_line: + { + + std::string::size_type fpos = m_cache.find('\n'); + if(std::string::npos != fpos ) + { + bool res = handle_command(m_cache); + if(!res) + return false; + m_cache.erase(0, fpos+1); + continue; + } + stop_handling = true; + } + break; + case http_state_error: + stop_handling = true; + return false; + default: + LOG_ERROR("Error in munin state machine! Unkonwon state=" << m_machine_state); + stop_handling = true; + m_machine_state = http_state_error; + return false; + } + + } + + return true; + } + + private: + + + bool init() + { + char hostname[64] = {0}; + int res = gethostname(hostname, 64); + hostname[63] = 0;//be happy + m_host_name = hostname; + return true; + } + bool handle_command(const std::string& command) + { + // list, nodes, config, fetch, version or quit + STATIC_REGEXP_EXPR_1(rexp_match_command_line, "^((list)|(nodes)|(config)|(fetch)|(version)|(quit))(\\s+(\\S+))?", boost::regex::icase | boost::regex::normal); + // 12 3 4 5 6 7 8 9 + size_t match_len = 0; + boost::smatch result; + if(boost::regex_search(command, result, rexp_match_command_line, boost::match_default) && result[0].matched) + { + if(result[2].matched) + {//list command + return handle_list_command(); + }else if(result[3].matched) + {//nodes command + return handle_nodes_command(); + }else if(result[4].matched) + {//config command + if(result[9].matched) + return handle_config_command(result[9]); + else + { + send_hook("Unknown service\n"); + } + }else if(result[5].matched) + {//fetch command + if(result[9].matched) + return handle_fetch_command(result[9]); + else + { + send_hook("Unknown service\n"); + } + }else if(result[6].matched) + {//version command + return handle_version_command(); + }else if(result[7].matched) + {//quit command + return handle_quit_command(); + } + else + return send_hook("Unknown command. Try list, nodes, config, fetch, version or quit\n"); + } + + return send_hook("Unknown command. Try list, nodes, config, fetch, version or quit\n");; + } + + bool handle_list_command() + { + std::string buff_to_send; + for(std::list::const_iterator it = m_config.m_services.begin(); it!=m_config.m_services.end();it++) + { + buff_to_send += it->m_service_name + " "; + } + buff_to_send+='\n'; + return send_hook(buff_to_send); + } + bool handle_nodes_command() + { + //supports only one node - host name + send_hook(m_host_name + "\n.\n"); + return true; + } + bool handle_config_command(const std::string& service_name) + { + munin_service* psrv = get_service_by_name(service_name); + if(!psrv) + return send_hook(std::string() + "Unknown service\n"); + + + return send_hook(psrv->m_service_config_string + ".\n"); + } + + bool handle_fetch_command(const std::string& service_name) + { + munin_service* psrv = get_service_by_name(service_name); + if(!psrv) + return send_hook(std::string() + "Unknown service\n"); + + std::string buff; + psrv->m_pdata_provider->update_service_data(psrv, buff); + + buff += ".\n"; + return send_hook(buff); + } + bool handle_version_command() + { + return send_hook("Munin node component by Andrey Sabelnikov\n"); + } + bool handle_quit_command() + { + return false; + } + + bool send_hook(const std::string& buff) + { + LOG_PRINT("munin_send: \n" << buff, LOG_LEVEL_3); + + if(m_psnd_hndlr) + return m_psnd_hndlr->do_send(buff.data(), buff.size()); + else + return false; + } + + + munin_service* get_service_by_name(const std::string& srv_name) + { + std::list::iterator it = m_config.m_services.begin(); + for(; it!=m_config.m_services.end(); it++) + if(it->m_service_name == srv_name) + break; + + if(it==m_config.m_services.end()) + return NULL; + + return &(*it); + } + + enum machine_state{ + http_state_retriving_comand_line, + http_state_error + }; + + + config_type& m_config; + machine_state m_machine_state; + std::string m_cache; + std::string m_host_name; + protected: + i_service_endpoint* m_psnd_hndlr; + }; + + + inline bool test_self() + { + /*WSADATA w; + ::WSAStartup(MAKEWORD(1, 1), &w); + node_server_config sc; + sc.m_services.push_back(munin_service()); + sc.m_services.back().m_service_name = "test_service"; + + sc.m_services.back().m_service_config_string = + "graph_args --base 1000 -l 0 --vertical-label N --upper-limit 329342976\n" + "graph_title REPORTS STATICTICS\n" + "graph_category bind\n" + "graph_info This graph shows how many reports came in fixed time period.\n" + "graph_order apps free swap\n" + "apps.label apps\n" + "apps.draw AREA\n" + "apps.info Memory used by user-space applications.\n" + "swap.label swap\n" + "swap.draw STACK\n" + "swap.info Swap space used.\n" + "free.label unused\n" + "free.draw STACK\n" + "free.info Wasted memory. Memory that is not used for anything at all.\n" + "committed.label committed\n" + "committed.draw LINE2\n" + "committed.warn 625410048\n" + "committed.info The amount of memory that would be used if all the memory that's been allocated were to be used.\n"; + + + sc.m_services.push_back(munin_service()); + sc.m_services.back().m_service_name = "test_service1"; + fake_send_handler fh; + munin_node_server_connection_handler mh(&fh, sc); + + std::string buff = "list\n"; + mh.handle_recv(buff.data(), buff.size()); + + + buff = "nodes\n"; + mh.handle_recv(buff.data(), buff.size()); +*/ + return true; + } + + } +} +} +#endif//!_MUNIN_CONNECTION_HANDLER_H_ \ No newline at end of file diff --git a/contrib/epee/include/net/munin_node_server.h b/contrib/epee/include/net/munin_node_server.h new file mode 100644 index 000000000..07637f550 --- /dev/null +++ b/contrib/epee/include/net/munin_node_server.h @@ -0,0 +1,49 @@ +// 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. +// + + + +#ifndef _MUNIN_NODE_SERVER_H_ +#define _MUNIN_NODE_SERVER_H_ + +#include +//#include "net_utils_base.h" +#include "munin_connection_handler.h" +//#include "abstract_tcp_server.h" +//#include "abstract_tcp_server_cp.h" +#include "abstract_tcp_server2.h" +namespace epee +{ +namespace net_utils +{ + namespace munin + { + typedef boosted_tcp_server munin_node_server; + //typedef cp_server_impl munin_node_cp_server; + } +} +} +#endif//!_MUNIN_NODE_SERVER_H_ \ No newline at end of file diff --git a/contrib/epee/include/net/net_helper.h b/contrib/epee/include/net/net_helper.h new file mode 100644 index 000000000..d2a4cfec3 --- /dev/null +++ b/contrib/epee/include/net/net_helper.h @@ -0,0 +1,683 @@ +// 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. +// + + + + +#pragma once + +//#include +//#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "net/net_utils_base.h" +#include "misc_language.h" +//#include "profile_tools.h" +#include "../string_tools.h" + +#ifndef MAKE_IP +#define MAKE_IP( a1, a2, a3, a4 ) (a1|(a2<<8)|(a3<<16)|(a4<<24)) +#endif + + +namespace epee +{ +namespace net_utils +{ + + class blocked_mode_client + { + + + struct handler_obj + { + handler_obj(boost::system::error_code& error, size_t& bytes_transferred):ref_error(error), ref_bytes_transferred(bytes_transferred) + {} + handler_obj(const handler_obj& other_obj):ref_error(other_obj.ref_error), ref_bytes_transferred(other_obj.ref_bytes_transferred) + {} + + boost::system::error_code& ref_error; + size_t& ref_bytes_transferred; + + void operator()(const boost::system::error_code& error, // Result of operation. + std::size_t bytes_transferred // Number of bytes read. + ) + { + ref_error = error; + ref_bytes_transferred = bytes_transferred; + } + }; + + public: + inline + blocked_mode_client():m_socket(m_io_service), + m_initialized(false), + m_connected(false), + m_deadline(m_io_service), + m_shutdowned(0) + { + + + m_initialized = true; + + + // No deadline is required until the first socket operation is started. We + // set the deadline to positive infinity so that the actor takes no action + // until a specific deadline is set. + m_deadline.expires_at(boost::posix_time::pos_infin); + + // Start the persistent actor that checks for deadline expiry. + check_deadline(); + + } + inline + ~blocked_mode_client() + { + //profile_tools::local_coast lc("~blocked_mode_client()", 3); + shutdown(); + } + + inline void set_recv_timeout(int reciev_timeout) + { + m_reciev_timeout = reciev_timeout; + } + + inline + bool connect(const std::string& addr, int port, unsigned int connect_timeout, unsigned int reciev_timeout, const std::string& bind_ip = "0.0.0.0") + { + return connect(addr, std::to_string(port), connect_timeout, reciev_timeout, bind_ip); + } + + inline + bool connect(const std::string& addr, const std::string& port, unsigned int connect_timeout, unsigned int reciev_timeout, const std::string& bind_ip = "0.0.0.0") + { + m_connect_timeout = connect_timeout; + m_reciev_timeout = reciev_timeout; + m_connected = false; + if(!m_reciev_timeout) + m_reciev_timeout = m_connect_timeout; + + try + { + m_socket.close(); + // Get a list of endpoints corresponding to the server name. + + + ////////////////////////////////////////////////////////////////////////// + + boost::asio::ip::tcp::resolver resolver(m_io_service); + boost::asio::ip::tcp::resolver::query query(boost::asio::ip::tcp::v4(), addr, port); + boost::asio::ip::tcp::resolver::iterator iterator = resolver.resolve(query); + boost::asio::ip::tcp::resolver::iterator end; + if(iterator == end) + { + LOG_ERROR("Failed to resolve " << addr); + return false; + } + + ////////////////////////////////////////////////////////////////////////// + + + //boost::asio::ip::tcp::endpoint remote_endpoint(boost::asio::ip::address::from_string(addr.c_str()), port); + boost::asio::ip::tcp::endpoint remote_endpoint(*iterator); + + + m_socket.open(remote_endpoint.protocol()); + if(bind_ip != "0.0.0.0" && bind_ip != "0" && bind_ip != "" ) + { + boost::asio::ip::tcp::endpoint local_endpoint(boost::asio::ip::address::from_string(addr.c_str()), 0); + m_socket.bind(local_endpoint); + } + + + m_deadline.expires_from_now(boost::posix_time::milliseconds(m_connect_timeout)); + + + boost::system::error_code ec = boost::asio::error::would_block; + + //m_socket.connect(remote_endpoint); + m_socket.async_connect(remote_endpoint, boost::lambda::var(ec) = boost::lambda::_1); + while (ec == boost::asio::error::would_block) + { + m_io_service.run_one(); + } + + if (!ec && m_socket.is_open()) + { + m_connected = true; + m_deadline.expires_at(boost::posix_time::pos_infin); + return true; + }else + { + LOG_PRINT("Some problems at connect, message: " << ec.message(), LOG_LEVEL_3); + return false; + } + + } + catch(const boost::system::system_error& er) + { + LOG_PRINT("Some problems at connect, message: " << er.what(), LOG_LEVEL_4); + return false; + } + catch(...) + { + LOG_PRINT("Some fatal problems.", LOG_LEVEL_4); + return false; + } + + return true; + } + + + inline + bool disconnect() + { + try + { + if(m_connected) + { + m_connected = false; + m_socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both); + + } + } + + catch(const boost::system::system_error& /*er*/) + { + //LOG_ERROR("Some problems at disconnect, message: " << er.what()); + return false; + } + catch(...) + { + //LOG_ERROR("Some fatal problems."); + return false; + } + return true; + } + + + inline + bool send(const std::string& buff) + { + + try + { + m_deadline.expires_from_now(boost::posix_time::milliseconds(m_reciev_timeout)); + + // Set up the variable that receives the result of the asynchronous + // operation. The error code is set to would_block to signal that the + // operation is incomplete. Asio guarantees that its asynchronous + // operations will never fail with would_block, so any other value in + // ec indicates completion. + boost::system::error_code ec = boost::asio::error::would_block; + + // Start the asynchronous operation itself. The boost::lambda function + // object is used as a callback and will update the ec variable when the + // operation completes. The blocking_udp_client.cpp example shows how you + // can use boost::bind rather than boost::lambda. + boost::asio::async_write(m_socket, boost::asio::buffer(buff), boost::lambda::var(ec) = boost::lambda::_1); + + // Block until the asynchronous operation has completed. + while (ec == boost::asio::error::would_block) + { + m_io_service.run_one(); + } + + if (ec) + { + LOG_PRINT_L3("Problems at write: " << ec.message()); + m_connected = false; + return false; + }else + { + m_deadline.expires_at(boost::posix_time::pos_infin); + } + } + + catch(const boost::system::system_error& er) + { + LOG_ERROR("Some problems at connect, message: " << er.what()); + return false; + } + catch(...) + { + LOG_ERROR("Some fatal problems."); + return false; + } + + return true; + } + + inline + bool send(const void* data, size_t sz) + { + try + { + /* + m_deadline.expires_from_now(boost::posix_time::milliseconds(m_reciev_timeout)); + + // Set up the variable that receives the result of the asynchronous + // operation. The error code is set to would_block to signal that the + // operation is incomplete. Asio guarantees that its asynchronous + // operations will never fail with would_block, so any other value in + // ec indicates completion. + boost::system::error_code ec = boost::asio::error::would_block; + + // Start the asynchronous operation itself. The boost::lambda function + // object is used as a callback and will update the ec variable when the + // operation completes. The blocking_udp_client.cpp example shows how you + // can use boost::bind rather than boost::lambda. + boost::asio::async_write(m_socket, boost::asio::buffer(data, sz), boost::lambda::var(ec) = boost::lambda::_1); + + // Block until the asynchronous operation has completed. + while (ec == boost::asio::error::would_block) + { + m_io_service.run_one(); + } + */ + boost::system::error_code ec; + + size_t writen = m_socket.write_some(boost::asio::buffer(data, sz), ec); + + + + if (!writen || ec) + { + LOG_PRINT_L3("Problems at write: " << ec.message()); + m_connected = false; + return false; + }else + { + m_deadline.expires_at(boost::posix_time::pos_infin); + } + } + + catch(const boost::system::system_error& er) + { + LOG_ERROR("Some problems at send, message: " << er.what()); + m_connected = false; + return false; + } + catch(...) + { + LOG_ERROR("Some fatal problems."); + return false; + } + + return true; + } + + bool is_connected() + { + return m_connected && m_socket.is_open(); + //TRY_ENTRY() + //return m_socket.is_open(); + //CATCH_ENTRY_L0("is_connected", false) + } + + inline + bool recv(std::string& buff) + { + + try + { + // Set a deadline for the asynchronous operation. Since this function uses + // a composed operation (async_read_until), the deadline applies to the + // entire operation, rather than individual reads from the socket. + m_deadline.expires_from_now(boost::posix_time::milliseconds(m_reciev_timeout)); + + // Set up the variable that receives the result of the asynchronous + // operation. The error code is set to would_block to signal that the + // operation is incomplete. Asio guarantees that its asynchronous + // operations will never fail with would_block, so any other value in + // ec indicates completion. + //boost::system::error_code ec = boost::asio::error::would_block; + + // Start the asynchronous operation itself. The boost::lambda function + // object is used as a callback and will update the ec variable when the + // operation completes. The blocking_udp_client.cpp example shows how you + // can use boost::bind rather than boost::lambda. + + boost::system::error_code ec = boost::asio::error::would_block; + size_t bytes_transfered = 0; + + handler_obj hndlr(ec, bytes_transfered); + + char local_buff[10000] = {0}; + //m_socket.async_read_some(boost::asio::buffer(local_buff, sizeof(local_buff)), hndlr); + boost::asio::async_read(m_socket, boost::asio::buffer(local_buff, sizeof(local_buff)), boost::asio::transfer_at_least(1), hndlr); + + // Block until the asynchronous operation has completed. + while (ec == boost::asio::error::would_block && !boost::interprocess::ipcdetail::atomic_read32(&m_shutdowned)) + { + m_io_service.run_one(); + } + + + if (ec) + { + LOG_PRINT_L4("READ ENDS: Connection err_code " << ec.value()); + if(ec == boost::asio::error::eof) + { + LOG_PRINT_L4("Connection err_code eof."); + //connection closed there, empty + return true; + } + + LOG_PRINT_L3("Problems at read: " << ec.message()); + m_connected = false; + return false; + }else + { + LOG_PRINT_L4("READ ENDS: Success. bytes_tr: " << bytes_transfered); + m_deadline.expires_at(boost::posix_time::pos_infin); + } + + /*if(!bytes_transfered) + return false;*/ + + buff.assign(local_buff, bytes_transfered); + return true; + } + + catch(const boost::system::system_error& er) + { + LOG_ERROR("Some problems at read, message: " << er.what()); + m_connected = false; + return false; + } + catch(...) + { + LOG_ERROR("Some fatal problems at read."); + return false; + } + + + + return false; + + } + + inline bool recv_n(std::string& buff, boost::int64_t sz) + { + + try + { + // Set a deadline for the asynchronous operation. Since this function uses + // a composed operation (async_read_until), the deadline applies to the + // entire operation, rather than individual reads from the socket. + m_deadline.expires_from_now(boost::posix_time::milliseconds(m_reciev_timeout)); + + // Set up the variable that receives the result of the asynchronous + // operation. The error code is set to would_block to signal that the + // operation is incomplete. Asio guarantees that its asynchronous + // operations will never fail with would_block, so any other value in + // ec indicates completion. + //boost::system::error_code ec = boost::asio::error::would_block; + + // Start the asynchronous operation itself. The boost::lambda function + // object is used as a callback and will update the ec variable when the + // operation completes. The blocking_udp_client.cpp example shows how you + // can use boost::bind rather than boost::lambda. + + buff.resize(static_cast(sz)); + boost::system::error_code ec = boost::asio::error::would_block; + size_t bytes_transfered = 0; + + + handler_obj hndlr(ec, bytes_transfered); + + //char local_buff[10000] = {0}; + boost::asio::async_read(m_socket, boost::asio::buffer((char*)buff.data(), buff.size()), boost::asio::transfer_at_least(buff.size()), hndlr); + + // Block until the asynchronous operation has completed. + while (ec == boost::asio::error::would_block && !boost::interprocess::ipcdetail::atomic_read32(&m_shutdowned)) + { + m_io_service.run_one(); + } + + if (ec) + { + LOG_PRINT_L3("Problems at read: " << ec.message()); + m_connected = false; + return false; + }else + { + m_deadline.expires_at(boost::posix_time::pos_infin); + } + + if(bytes_transfered != buff.size()) + { + LOG_ERROR("Transferred missmatch with transfer_at_least value: m_bytes_transferred=" << bytes_transfered << " at_least value=" << buff.size()); + return false; + } + + return true; + } + + catch(const boost::system::system_error& er) + { + LOG_ERROR("Some problems at read, message: " << er.what()); + m_connected = false; + return false; + } + catch(...) + { + LOG_ERROR("Some fatal problems at read."); + return false; + } + + + + return false; + } + + bool shutdown() + { + m_deadline.cancel(); + boost::system::error_code ignored_ec; + m_socket.cancel(ignored_ec); + m_socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignored_ec); + m_socket.close(ignored_ec); + boost::interprocess::ipcdetail::atomic_write32(&m_shutdowned, 1); + m_connected = false; + return true; + } + + void set_connected(bool connected) + { + m_connected = connected; + } + boost::asio::io_service& get_io_service() + { + return m_io_service; + } + + boost::asio::ip::tcp::socket& get_socket() + { + return m_socket; + } + + private: + + void check_deadline() + { + // Check whether the deadline has passed. We compare the deadline against + // the current time since a new asynchronous operation may have moved the + // deadline before this actor had a chance to run. + if (m_deadline.expires_at() <= boost::asio::deadline_timer::traits_type::now()) + { + // The deadline has passed. The socket is closed so that any outstanding + // asynchronous operations are cancelled. This allows the blocked + // connect(), read_line() or write_line() functions to return. + LOG_PRINT_L3("Timed out socket"); + m_connected = false; + m_socket.close(); + + // There is no longer an active deadline. The expiry is set to positive + // infinity so that the actor takes no action until a new deadline is set. + m_deadline.expires_at(boost::posix_time::pos_infin); + } + + // Put the actor back to sleep. + m_deadline.async_wait(boost::bind(&blocked_mode_client::check_deadline, this)); + } + + + + protected: + boost::asio::io_service m_io_service; + boost::asio::ip::tcp::socket m_socket; + int m_connect_timeout; + int m_reciev_timeout; + bool m_initialized; + bool m_connected; + boost::asio::deadline_timer m_deadline; + volatile boost::uint32_t m_shutdowned; + }; + + + /************************************************************************/ + /* */ + /************************************************************************/ + class async_blocked_mode_client: public blocked_mode_client + { + public: + async_blocked_mode_client():m_send_deadline(blocked_mode_client::m_io_service) + { + + // No deadline is required until the first socket operation is started. We + // set the deadline to positive infinity so that the actor takes no action + // until a specific deadline is set. + m_send_deadline.expires_at(boost::posix_time::pos_infin); + + // Start the persistent actor that checks for deadline expiry. + check_send_deadline(); + } + ~async_blocked_mode_client() + { + m_send_deadline.cancel(); + } + + bool shutdown() + { + blocked_mode_client::shutdown(); + m_send_deadline.cancel(); + return true; + } + + inline + bool send(const void* data, size_t sz) + { + try + { + /* + m_send_deadline.expires_from_now(boost::posix_time::milliseconds(m_reciev_timeout)); + + // Set up the variable that receives the result of the asynchronous + // operation. The error code is set to would_block to signal that the + // operation is incomplete. Asio guarantees that its asynchronous + // operations will never fail with would_block, so any other value in + // ec indicates completion. + boost::system::error_code ec = boost::asio::error::would_block; + + // Start the asynchronous operation itself. The boost::lambda function + // object is used as a callback and will update the ec variable when the + // operation completes. The blocking_udp_client.cpp example shows how you + // can use boost::bind rather than boost::lambda. + boost::asio::async_write(m_socket, boost::asio::buffer(data, sz), boost::lambda::var(ec) = boost::lambda::_1); + + // Block until the asynchronous operation has completed. + while(ec == boost::asio::error::would_block) + { + m_io_service.run_one(); + }*/ + + boost::system::error_code ec; + + size_t writen = m_socket.write_some(boost::asio::buffer(data, sz), ec); + + if (!writen || ec) + { + LOG_PRINT_L3("Problems at write: " << ec.message()); + return false; + }else + { + m_send_deadline.expires_at(boost::posix_time::pos_infin); + } + } + + catch(const boost::system::system_error& er) + { + LOG_ERROR("Some problems at connect, message: " << er.what()); + return false; + } + catch(...) + { + LOG_ERROR("Some fatal problems."); + return false; + } + + return true; + } + + + private: + + boost::asio::deadline_timer m_send_deadline; + + void check_send_deadline() + { + // Check whether the deadline has passed. We compare the deadline against + // the current time since a new asynchronous operation may have moved the + // deadline before this actor had a chance to run. + if (m_send_deadline.expires_at() <= boost::asio::deadline_timer::traits_type::now()) + { + // The deadline has passed. The socket is closed so that any outstanding + // asynchronous operations are cancelled. This allows the blocked + // connect(), read_line() or write_line() functions to return. + LOG_PRINT_L3("Timed out socket"); + m_socket.close(); + + // There is no longer an active deadline. The expiry is set to positive + // infinity so that the actor takes no action until a new deadline is set. + m_send_deadline.expires_at(boost::posix_time::pos_infin); + } + + // Put the actor back to sleep. + m_send_deadline.async_wait(boost::bind(&async_blocked_mode_client::check_send_deadline, this)); + } + }; +} +} diff --git a/contrib/epee/include/net/net_parse_helpers.h b/contrib/epee/include/net/net_parse_helpers.h new file mode 100644 index 000000000..16641a970 --- /dev/null +++ b/contrib/epee/include/net/net_parse_helpers.h @@ -0,0 +1,168 @@ +// 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. +// + + + + +#pragma once +#include "http_base.h" +#include "reg_exp_definer.h" + + +namespace epee +{ +namespace net_utils +{ + + inline bool parse_uri_query(const std::string& query, std::list >& params) + { + enum state + { + st_param_name, + st_param_val + }; + state st = st_param_name; + std::string::const_iterator start_it = query.begin(); + std::pair e; + for(std::string::const_iterator it = query.begin(); it != query.end(); it++) + { + switch(st) + { + case st_param_name: + if(*it == '=') + { + e.first.assign(start_it, it); + start_it = it;++start_it; + st = st_param_val; + } + break; + case st_param_val: + if(*it == '&') + { + e.second.assign(start_it, it); + start_it = it;++start_it; + params.push_back(e); + e.first.clear();e.second.clear(); + st = st_param_name; + } + break; + default: + LOG_ERROR("Unknown state " << (int)st); + return false; + } + } + if(st == st_param_name) + { + if(start_it != query.end()) + { + e.first.assign(start_it, query.end()); + params.push_back(e); + } + }else + { + if(start_it != query.end()) + e.second.assign(start_it, query.end()); + + if(e.first.size()) + params.push_back(e); + } + return true; + } + + inline + bool parse_uri(const std::string uri, http::uri_content& content) + { + + ///iframe_test.html?api_url=http://api.vk.com/api.php&api_id=3289090&api_settings=1&viewer_id=562964060&viewer_type=0&sid=0aad8d1c5713130f9ca0076f2b7b47e532877424961367d81e7fa92455f069be7e21bc3193cbd0be11895&secret=368ebbc0ef&access_token=668bc03f43981d883f73876ffff4aa8564254b359cc745dfa1b3cde7bdab2e94105d8f6d8250717569c0a7&user_id=0&group_id=0&is_app_user=1&auth_key=d2f7a895ca5ff3fdb2a2a8ae23fe679a&language=0&parent_language=0&ad_info=ElsdCQBaQlxiAQRdFUVUXiN2AVBzBx5pU1BXIgZUJlIEAWcgAUoLQg==&referrer=unknown&lc_name=9834b6a3&hash= + content.m_query_params.clear(); + STATIC_REGEXP_EXPR_1(rexp_match_uri, "^([^?#]*)(\\?([^#]*))?(#(.*))?", boost::regex::icase | boost::regex::normal); + + boost::smatch result; + if(!boost::regex_search(uri, result, rexp_match_uri, boost::match_default) && result[0].matched) + { + LOG_PRINT_L0("[PARSE URI] regex not matched for uri: " << uri); + content.m_path = uri; + return true; + } + if(result[1].matched) + { + content.m_path = result[1]; + } + if(result[3].matched) + { + content.m_query = result[3]; + } + if(result[5].matched) + { + content.m_fragment = result[5]; + } + if(content.m_query.size()) + { + parse_uri_query(content.m_query, content.m_query_params); + } + return true; + } + + + inline + bool parse_url(const std::string url_str, http::url_content& content) + { + + ///iframe_test.html?api_url=http://api.vk.com/api.php&api_id=3289090&api_settings=1&viewer_id=562964060&viewer_type=0&sid=0aad8d1c5713130f9ca0076f2b7b47e532877424961367d81e7fa92455f069be7e21bc3193cbd0be11895&secret=368ebbc0ef&access_token=668bc03f43981d883f73876ffff4aa8564254b359cc745dfa1b3cde7bdab2e94105d8f6d8250717569c0a7&user_id=0&group_id=0&is_app_user=1&auth_key=d2f7a895ca5ff3fdb2a2a8ae23fe679a&language=0&parent_language=0&ad_info=ElsdCQBaQlxiAQRdFUVUXiN2AVBzBx5pU1BXIgZUJlIEAWcgAUoLQg==&referrer=unknown&lc_name=9834b6a3&hash= + //STATIC_REGEXP_EXPR_1(rexp_match_uri, "^([^?#]*)(\\?([^#]*))?(#(.*))?", boost::regex::icase | boost::regex::normal); + STATIC_REGEXP_EXPR_1(rexp_match_uri, "^((.*?)://)?(([^/:]*)(:(\\d+))?)(.*)?", boost::regex::icase | boost::regex::normal); + // 12 34 5 6 7 + content.port = 0; + boost::smatch result; + if(!boost::regex_search(url_str, result, rexp_match_uri, boost::match_default) && result[0].matched) + { + LOG_PRINT_L0("[PARSE URI] regex not matched for uri: " << rexp_match_uri); + //content.m_path = uri; + return true; + } + if(result[2].matched) + { + content.schema = result[2]; + } + if(result[4].matched) + { + content.host = result[4]; + } + if(result[6].matched) + { + content.port = boost::lexical_cast(result[6]); + } + if(result[7].matched) + { + content.uri = result[7]; + return parse_uri(result[7], content.m_uri_content); + } + + return true; + } + +} +} \ No newline at end of file diff --git a/contrib/epee/include/net/net_utils_base.h b/contrib/epee/include/net/net_utils_base.h new file mode 100644 index 000000000..86797bb85 --- /dev/null +++ b/contrib/epee/include/net/net_utils_base.h @@ -0,0 +1,150 @@ +// 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. +// + + + +#ifndef _NET_UTILS_BASE_H_ +#define _NET_UTILS_BASE_H_ + +#include +#include "string_tools.h" + +#ifndef MAKE_IP +#define MAKE_IP( a1, a2, a3, a4 ) (a1|(a2<<8)|(a3<<16)|(a4<<24)) +#endif + + +namespace epee +{ +namespace net_utils +{ + /************************************************************************/ + /* */ + /************************************************************************/ + struct connection_context_base + { + const boost::uuids::uuid m_connection_id; + const boost::uint32_t m_remote_ip; + const boost::uint32_t m_remote_port; + const bool m_is_income; + + connection_context_base(boost::uuids::uuid connection_id, long remote_ip, int remote_port, bool is_income): + m_connection_id(connection_id), + m_remote_ip(remote_ip), + m_remote_port(remote_port), + m_is_income(is_income) + + + {} + + connection_context_base(): m_connection_id(), + m_remote_ip(0), + m_remote_port(0), + m_is_income(false) + {} + + connection_context_base& operator=(const connection_context_base& a) + { + set_details(a.m_connection_id, a.m_remote_ip, a.m_remote_port, a.m_is_income); + return *this; + } + + private: + template + friend class connection; + void set_details(boost::uuids::uuid connection_id, long remote_ip, int remote_port, bool is_income) + { + this->~connection_context_base(); + new(this) connection_context_base(connection_id, remote_ip, remote_port, is_income); + } + + }; + + /************************************************************************/ + /* */ + /************************************************************************/ + struct i_service_endpoint + { + virtual bool do_send(const void* ptr, size_t cb)=0; + virtual bool close()=0; + virtual bool call_run_once_service_io()=0; + virtual bool request_callback()=0; + virtual boost::asio::io_service& get_io_service()=0; + //protect from deletion connection object(with protocol instance) during external call "invoke" + virtual bool add_ref()=0; + virtual bool release()=0; + protected: + virtual ~i_service_endpoint(){} + }; + + + //some helpers + + + inline + std::string print_connection_context(const connection_context_base& ctx) + { + std::stringstream ss; + ss << epee::string_tools::get_ip_string_from_int32(ctx.m_remote_ip) << ":" << ctx.m_remote_port << " " << epee::string_tools::get_str_from_guid_a(ctx.m_connection_id) << (ctx.m_is_income ? " INC":" OUT"); + return ss.str(); + } + + inline + std::string print_connection_context_short(const connection_context_base& ctx) + { + std::stringstream ss; + ss << epee::string_tools::get_ip_string_from_int32(ctx.m_remote_ip) << ":" << ctx.m_remote_port << (ctx.m_is_income ? " INC":" OUT"); + return ss.str(); + } + +#define LOG_PRINT_CC(ct, message, log_level) LOG_PRINT("[" << epee::net_utils::print_connection_context_short(ct) << "]" << message, log_level) +#define LOG_PRINT_CC_GREEN(ct, message, log_level) LOG_PRINT_GREEN("[" << epee::net_utils::print_connection_context_short(ct) << "]" << message, log_level) +#define LOG_PRINT_CC_RED(ct, message, log_level) LOG_PRINT_RED("[" << epee::net_utils::print_connection_context_short(ct) << "]" << message, log_level) +#define LOG_PRINT_CC_BLUE(ct, message, log_level) LOG_PRINT_BLUE("[" << epee::net_utils::print_connection_context_short(ct) << "]" << message, log_level) +#define LOG_ERROR_CC(ct, message) LOG_ERROR("[" << epee::net_utils::print_connection_context_short(ct) << "]" << message) + +#define LOG_PRINT_CC_L0(ct, message) LOG_PRINT_L0("[" << epee::net_utils::print_connection_context_short(ct) << "]" << message) +#define LOG_PRINT_CC_L1(ct, message) LOG_PRINT_L1("[" << epee::net_utils::print_connection_context_short(ct) << "]" << message) +#define LOG_PRINT_CC_L2(ct, message) LOG_PRINT_L2("[" << epee::net_utils::print_connection_context_short(ct) << "]" << message) +#define LOG_PRINT_CC_L3(ct, message) LOG_PRINT_L3("[" << epee::net_utils::print_connection_context_short(ct) << "]" << message) +#define LOG_PRINT_CC_L4(ct, message) LOG_PRINT_L4("[" << epee::net_utils::print_connection_context_short(ct) << "]" << message) + +#define LOG_PRINT_CCONTEXT_L0(message) LOG_PRINT_CC_L0(context, message) +#define LOG_PRINT_CCONTEXT_L1(message) LOG_PRINT_CC_L1(context, message) +#define LOG_PRINT_CCONTEXT_L2(message) LOG_PRINT_CC_L2(context, message) +#define LOG_PRINT_CCONTEXT_L3(message) LOG_PRINT_CC_L3(context, message) +#define LOG_ERROR_CCONTEXT(message) LOG_ERROR_CC(context, message) + +#define LOG_PRINT_CCONTEXT_GREEN(message, log_level) LOG_PRINT_CC_GREEN(context, message, log_level) +#define LOG_PRINT_CCONTEXT_RED(message, log_level) LOG_PRINT_CC_RED(context, message, log_level) +#define LOG_PRINT_CCONTEXT_BLUE(message, log_level) LOG_PRINT_CC_BLUE(context, message, log_level) + +#define CHECK_AND_ASSERT_MES_CC(condition, return_val, err_message) CHECK_AND_ASSERT_MES(condition, return_val, "[" << epee::net_utils::print_connection_context_short(context) << "]" << err_message) + +} +} + +#endif //_NET_UTILS_BASE_H_ diff --git a/contrib/epee/include/net/protocol_switcher.h b/contrib/epee/include/net/protocol_switcher.h new file mode 100644 index 000000000..f9a6dbe6f --- /dev/null +++ b/contrib/epee/include/net/protocol_switcher.h @@ -0,0 +1,121 @@ +// 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. +// + + + +#ifndef _PROTOCOL_SWITCHER_H_ +#define _PROTOCOL_SWITCHER_H_ + +#include "levin_base.h" +#include "http_server.h" +#include "levin_protocol_handler.h" +//#include "abstract_tcp_server.h" + +namespace epee +{ +namespace net_utils +{ + struct protocl_switcher_config + { + http::http_custom_handler::config_type m_http_config; + levin::protocol_handler::config_type m_levin_config; + }; + + + struct i_protocol_handler + { + virtual bool handle_recv(const void* ptr, size_t cb)=0; + }; + + template + class t_protocol_handler: public i_protocol_handler + { + public: + typedef t t_type; + t_protocol_handler(i_service_endpoint* psnd_hndlr, typename t_type::config_type& config, const connection_context& conn_context):m_hadler(psnd_hndlr, config, conn_context) + {} + private: + bool handle_recv(const void* ptr, size_t cb) + { + return m_hadler.handle_recv(ptr, cb); + } + t_type m_hadler; + }; + + + class protocol_switcher + { + public: + typedef protocl_switcher_config config_type; + + protocol_switcher(net_utils::i_service_endpoint* psnd_hndlr, config_type& config, const net_utils::connection_context_base& conn_context); + virtual ~protocol_switcher(){} + + virtual bool handle_recv(const void* ptr, size_t cb); + + bool after_init_connection(){return true;} + private: + t_protocol_handler m_http_handler; + t_protocol_handler m_levin_handler; + i_protocol_handler* pcurrent_handler; + + std::string m_cached_buff; + }; + + protocol_switcher::protocol_switcher(net_utils::i_service_endpoint* psnd_hndlr, config_type& config, const net_utils::connection_context_base& conn_context):m_http_handler(psnd_hndlr, config.m_http_config, conn_context), m_levin_handler(psnd_hndlr, config.m_levin_config, conn_context), pcurrent_handler(NULL) + {} + + bool protocol_switcher::handle_recv(const void* ptr, size_t cb) + { + if(pcurrent_handler) + return pcurrent_handler->handle_recv(ptr, cb); + else + { + m_cached_buff.append((const char*)ptr, cb); + if(m_cached_buff.size() < sizeof(boost::uint64_t)) + return true; + + if(*((boost::uint64_t*)&m_cached_buff[0]) == LEVIN_SIGNATURE) + { + pcurrent_handler = &m_levin_handler; + return pcurrent_handler->handle_recv(m_cached_buff.data(), m_cached_buff.size()); + } + if(m_cached_buff.substr(0, 4) == "GET " || m_cached_buff.substr(0, 4) == "POST") + { + pcurrent_handler = &m_http_handler; + return pcurrent_handler->handle_recv(m_cached_buff.data(), m_cached_buff.size()); + }else + { + LOG_ERROR("Wrong protocol accepted on port..."); + return false; + } + } + + return true; + } +} +} +#endif //_PROTOCOL_SWITCHER_H_ \ No newline at end of file diff --git a/contrib/epee/include/net/rpc_method_name.h b/contrib/epee/include/net/rpc_method_name.h new file mode 100644 index 000000000..c226639c4 --- /dev/null +++ b/contrib/epee/include/net/rpc_method_name.h @@ -0,0 +1,31 @@ +// 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. +// + + +#pragma once + + +#define RPC_METHOD_NAME(name) static inline const char* methodname(){return name;} \ No newline at end of file diff --git a/contrib/epee/include/net/smtp.h b/contrib/epee/include/net/smtp.h new file mode 100644 index 000000000..d2e8598fd --- /dev/null +++ b/contrib/epee/include/net/smtp.h @@ -0,0 +1,181 @@ +// 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. +// + + + + +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace epee +{ +namespace net_utils +{ + namespace smtp + { + + using boost::asio::ip::tcp; + using namespace boost::archive::iterators; + typedef base64_from_binary > base64_text; + + /************************************************************************/ + /* */ + /************************************************************************/ + class smtp_client + { + public: + smtp_client(std::string pServer,unsigned int pPort,std::string pUser,std::string pPassword): + mServer(pServer),mPort(pPort),mUserName(pUser),mPassword(pPassword),mSocket(mIOService),mResolver(mIOService) + { + tcp::resolver::query qry(mServer,boost::lexical_cast( mPort )); + mResolver.async_resolve(qry,boost::bind(&smtp_client::handleResolve,this,boost::asio::placeholders::error, + boost::asio::placeholders::iterator)); + } + bool Send(std::string pFrom,std::string pTo,std::string pSubject,std::string pMessage) + { + mHasError = true; + mFrom=pFrom; + mTo=pTo; + mSubject=pSubject; + mMessage=pMessage; + mIOService.run(); + return !mHasError; + } + private: + std::string encodeBase64(std::string pData) + { + std::stringstream os; + size_t sz=pData.size(); + std::copy(base64_text(pData.c_str()),base64_text(pData.c_str()+sz),std::ostream_iterator(os)); + return os.str(); + } + void handleResolve(const boost::system::error_code& err,tcp::resolver::iterator endpoint_iterator) + { + if(!err) + { + tcp::endpoint endpoint=*endpoint_iterator; + mSocket.async_connect(endpoint, + boost::bind(&smtp_client::handleConnect,this,boost::asio::placeholders::error,++endpoint_iterator)); + } + else + { + mHasError=true; + mErrorMsg= err.message(); + } + } + void writeLine(std::string pData) + { + std::ostream req_strm(&mRequest); + req_strm << pData << "\r\n"; + boost::asio::write(mSocket,mRequest); + req_strm.clear(); + } + void readLine(std::string& pData) + { + boost::asio::streambuf response; + boost::asio::read_until(mSocket, response, "\r\n"); + std::istream response_stream(&response); + response_stream >> pData; + } + void handleConnect(const boost::system::error_code& err,tcp::resolver::iterator endpoint_iterator) + { + if (!err) + { + std::string read_buff; + // The connection was successful. Send the request. + std::ostream req_strm(&mRequest); + writeLine("EHLO "+mServer); + readLine(read_buff);//220 + writeLine("AUTH LOGIN"); + readLine(read_buff);// + writeLine(encodeBase64(mUserName)); + readLine(read_buff); + writeLine(encodeBase64(mPassword)); + readLine(read_buff); + writeLine( "MAIL FROM:<"+mFrom+">"); + writeLine( "RCPT TO:<"+mTo+">"); + writeLine( "DATA"); + writeLine( "SUBJECT:"+mSubject); + writeLine( "From:"+mFrom); + writeLine( "To:"+mTo); + writeLine( ""); + writeLine( mMessage ); + writeLine( "\r\n.\r\n"); + readLine(read_buff); + if(read_buff == "250") + mHasError = false; + writeLine( "QUIT"); + } + else + { + mHasError=true; + mErrorMsg= err.message(); + } + } + std::string mServer; + std::string mUserName; + std::string mPassword; + std::string mFrom; + std::string mTo; + std::string mSubject; + std::string mMessage; + unsigned int mPort; + boost::asio::io_service mIOService; + tcp::resolver mResolver; + tcp::socket mSocket; + boost::asio::streambuf mRequest; + boost::asio::streambuf mResponse; + bool mHasError; + std::string mErrorMsg; + }; + + + bool send_mail(const std::string& server, int port, const std::string& login, const std::string& pass, const std::string& from_email, /*"STIL CRAWLER",*/ + const std::string& maillist, const std::string& subject, const std::string& body) + { + STD_TRY_BEGIN(); + //smtp_client mailc("yoursmtpserver.com",25,"user@yourdomain.com","password"); + //mailc.Send("from@yourdomain.com","to@somewhere.com","subject","Hello from C++ SMTP Client!"); + smtp_client mailc(server,port,login,pass); + return mailc.Send(from_email,maillist,subject,body); + STD_TRY_CATCH("at send_mail", false); + } + + } +} +} + +//#include "smtp.inl" \ No newline at end of file diff --git a/contrib/epee/include/net/smtp.inl b/contrib/epee/include/net/smtp.inl new file mode 100644 index 000000000..d42c8b950 --- /dev/null +++ b/contrib/epee/include/net/smtp.inl @@ -0,0 +1,1569 @@ +// 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 "md5.h" + +namespace epee +{ +namespace net_utils +{ + namespace smtp + { + + + ////////////////////////////////////////////////////////////////////////// + inline char * convert_hex( unsigned char *in, int len ) + { + static char hex[] = "0123456789abcdef"; + char * out; + int i; + + out = (char *) malloc(len * 2 + 1); + if (out == NULL) + return NULL; + + for (i = 0; i < len; i++) { + out[i * 2] = hex[in[i] >> 4]; + out[i * 2 + 1] = hex[in[i] & 15]; + } + + out[i*2] = 0; + + return out; + } + + ////////////////////////////////////////////////////////////////////////// + inline char * hash_md5(const char * sec_key, const char * data, int len) + { + char key[65], digest[24]; + char * hash_hex; + + int sec_len, i; + + sec_len = strlen(sec_key); + + if (sec_len < 64) { + memcpy(key, sec_key, sec_len); + for (i = sec_len; i < 64; i++) { + key[i] = 0; + } + } else { + memcpy(key, sec_key, 64); + } + + md5::hmac_md5( (const unsigned char*)data, len, (const unsigned char*)key, 64, (unsigned char*)digest ); + hash_hex = convert_hex( (unsigned char*)digest, 16 ); + + return hash_hex; + } + ////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////// + inline CSMTPClient::CSMTPClient(void) + { + m_dwSupportedAuthModesCount = 0; + m_bConnected = FALSE; + m_hSocket = INVALID_SOCKET; + m_pErrorText = NULL; + + // Initialize WinSock + WORD wVer = MAKEWORD( 2, 2 ); + if ( WSAStartup( wVer, &m_wsaData ) != NO_ERROR ) + { + SetErrorText( "WSAStartup.", WSAGetLastError() ); + throw; + } + if ( LOBYTE( m_wsaData.wVersion ) != 2 || HIBYTE( m_wsaData.wVersion ) != 2 ) + { + SetErrorText( "Can't find a useable WinSock DLL." ); + WSACleanup(); + throw; + } + } + + ////////////////////////////////////////////////////////////////////////// + inline CSMTPClient::~CSMTPClient(void) + { + if ( m_pErrorText ) + { + free( m_pErrorText ); + m_pErrorText = NULL; + } + + if ( m_bConnected ) + ServerDisconnect(); + + // Cleanup + WSACleanup(); + } + + ////////////////////////////////////////////////////////////////////////// + inline void CSMTPClient::SetErrorText( LPCSTR szErrorText, DWORD dwErrorCode ) + { + if ( m_pErrorText ) + { + free( m_pErrorText ); + m_pErrorText = NULL; + } + + LPVOID lpMsgBuf = NULL; + if ( dwErrorCode ) + { + FormatMessageA( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + dwErrorCode, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPSTR) &lpMsgBuf, + 0, NULL ); + } + + if ( szErrorText && strlen( szErrorText ) ) + { + m_pErrorText = (LPBYTE)malloc( strlen( szErrorText ) + 1 ); + strcpy( (char*)m_pErrorText, szErrorText ); + + if ( lpMsgBuf ) + { + strcat( (char*)m_pErrorText, " " ); + strcpy( (char*)m_pErrorText, (char*)lpMsgBuf ); + + LocalFree( lpMsgBuf ); + } + } + } + + inline void CSMTPClient::SetErrorText( PBYTE szErrorText, DWORD dwErrorCode ) + { + SetErrorText( (LPCSTR)szErrorText, dwErrorCode ); + } + + ////////////////////////////////////////////////////////////////////////// + inline char* CSMTPClient::GetLastErrorText() + { + return (char*)m_pErrorText; + } + + ////////////////////////////////////////////////////////////////////////// + inline DWORD CSMTPClient::ReceiveData( SOCKET hSocket, PBYTE pReceiveBuffer, DWORD dwReceiveBufferSize ) + { + DWORD dwReceivedDataSize = 0; + + if ( hSocket != INVALID_SOCKET && pReceiveBuffer && dwReceiveBufferSize ) + { + int iReceived = 0; + int iLength = 0; + + iLength = recv( hSocket, (LPSTR)pReceiveBuffer + iReceived, dwReceiveBufferSize - iReceived, + NO_FLAGS ); + + if ( iLength != 0 && iLength != SOCKET_ERROR ) + iReceived += iLength; + + dwReceivedDataSize = iReceived; + + pReceiveBuffer[ iReceived ] = 0; + } + + return dwReceivedDataSize; + } + + inline ////////////////////////////////////////////////////////////////////////// + DWORD CSMTPClient::SendData( SOCKET hSocket, PBYTE pSendBuffer, DWORD dwSendBufferSize ) + { + DWORD dwSended = 0; + + if ( hSocket != INVALID_SOCKET && pSendBuffer && dwSendBufferSize ) + { + int iSended = 0; + int iLength = 0; + + while ( iLength != SOCKET_ERROR && dwSendBufferSize - iSended > 0 ) + { + iLength = send( hSocket, (LPSTR)pSendBuffer + iSended, dwSendBufferSize - iSended, + NO_FLAGS ); + + if ( iLength != 0 && iLength != SOCKET_ERROR ) + iSended += iLength; + } + + dwSended = iSended; + } + + //if ( dwSended ) + // printf( "C: %s", pSendBuffer ); + + return dwSended; + } + + ////////////////////////////////////////////////////////////////////////// + inline unsigned short CSMTPClient::GetResponseCode( LPBYTE pBuffer, DWORD dwBufferSize ) + { + unsigned short iCode = 0; + + if ( dwBufferSize >= 3 ) + { + CHAR szResponseCode[ 4 ] = { 0 }; + memcpy( szResponseCode, pBuffer, 3 ); + szResponseCode[ 3 ] = 0; + iCode = atoi( szResponseCode ); + } + + return iCode; + } + + ////////////////////////////////////////////////////////////////////////// + inline void CSMTPClient::ParseESMTPExtensions( LPBYTE pBuffer, DWORD dwBufferSize ) + { + const char *szSubstring = strstr( (const char*)pBuffer, "250-AUTH " ); + if ( !szSubstring ) + { + szSubstring = strstr( (const char*)pBuffer, "250 AUTH " ); + } + + if ( szSubstring ) + { + const char *szSubstringEnd = strstr( (const char*)szSubstring, "\r\n" ); + if ( szSubstringEnd ) + { + szSubstring += 9; + char szAuthMode[ 256 ] = { 0 }; + for ( ; szSubstring < szSubstringEnd + 1 ; szSubstring++ ) + { + if ( *szSubstring == ' ' || *szSubstring == '\r' ) + { + if ( _strcmpi( szAuthMode, SMTP_COMMAND_AUTH_PLAIN ) == 0 ) + { + m_aSupportedAuthModes[ m_dwSupportedAuthModesCount ] = AUTH_MODE_PLAIN; + m_dwSupportedAuthModesCount++; + } + else if ( _strcmpi( szAuthMode, SMTP_COMMAND_AUTH_LOGIN ) == 0 ) + { + m_aSupportedAuthModes[ m_dwSupportedAuthModesCount ] = AUTH_MODE_LOGIN; + m_dwSupportedAuthModesCount++; + } + else if ( _strcmpi( szAuthMode, SMTP_COMMAND_AUTH_CRAM_MD5 ) == 0 ) + { + m_aSupportedAuthModes[ m_dwSupportedAuthModesCount ] = AUTH_MODE_CRAM_MD5; + m_dwSupportedAuthModesCount++; + } + + szAuthMode[ 0 ] = 0; + + if ( m_dwSupportedAuthModesCount == MAX_AUTH_MODES_COUND ) + break; + } + else + { + szAuthMode[ strlen( szAuthMode ) + 1 ] = 0; + szAuthMode[ strlen( szAuthMode ) ] = *szSubstring; + } + } + } + } + } + + ////////////////////////////////////////////////////////////////////////// + inline BOOL CSMTPClient::ServerConnect( LPCSTR szServerAddress, const unsigned short iPortNumber ) + { + if ( m_bConnected ) + ServerDisconnect(); + + m_bConnected = FALSE; + m_hSocket = INVALID_SOCKET; + + m_hSocket = _connectServerSocket( szServerAddress, iPortNumber ); + + if ( m_hSocket != INVALID_SOCKET ) + { + DWORD dwReceiveBufferSize = 1024*16; + PBYTE pReceiveBuffer = (PBYTE)malloc( dwReceiveBufferSize ); + if ( pReceiveBuffer ) + { + // Connected. Wait server hello string. + DWORD iReceived = ReceiveData( m_hSocket, pReceiveBuffer, dwReceiveBufferSize ); + if ( iReceived ) + { + // Check 220 + int iResponseCode = GetResponseCode( pReceiveBuffer, iReceived ); + if ( iResponseCode != 220 ) + { + SetErrorText( pReceiveBuffer ); + free( pReceiveBuffer ); + ServerDisconnect(); + return FALSE; + } + } + else + { + SetErrorText( "ReceiveData error. ", WSAGetLastError() ); + free( pReceiveBuffer ); + ServerDisconnect(); + return FALSE; + } + + // EHLO / HELO + BYTE szHelloBuffer[ 256 ]; + sprintf( (char*)szHelloBuffer, "%s %s\r\n", (char*)SMTP_COMMAND_EHLO, (char*)szServerAddress ); + if ( SendData( m_hSocket, (PBYTE)szHelloBuffer, strlen( (const char*)szHelloBuffer ) ) == 0 ) + { + SetErrorText( "SendData error.", WSAGetLastError() ); + free( pReceiveBuffer ); + ServerDisconnect(); + return FALSE; + } + + iReceived = ReceiveData( m_hSocket, pReceiveBuffer, dwReceiveBufferSize ); + if ( iReceived ) + { + // Check 250 + int iResponseCode = GetResponseCode( pReceiveBuffer, iReceived ); + if ( iResponseCode == 500 ) + { + SetErrorText( pReceiveBuffer ); + + sprintf( (char*)szHelloBuffer, "%s %s\r\n", (char*)SMTP_COMMAND_HELO, (char*)szServerAddress ); + if ( SendData( m_hSocket, (PBYTE)szHelloBuffer, strlen( (const char*)szHelloBuffer ) ) == 0 ) + { + SetErrorText( "SendData error.", WSAGetLastError() ); + free( pReceiveBuffer ); + ServerDisconnect(); + return FALSE; + } + + iResponseCode = GetResponseCode( pReceiveBuffer, iReceived ); + if ( iResponseCode != 250 ) + { + SetErrorText( pReceiveBuffer ); + free( pReceiveBuffer ); + ServerDisconnect(); + return FALSE; + } + } + else if ( iResponseCode != 250 ) + { + SetErrorText( pReceiveBuffer ); + free( pReceiveBuffer ); + ServerDisconnect(); + return FALSE; + } + + // Parse AUTH supported modes + ParseESMTPExtensions( pReceiveBuffer, iReceived ); + } + else + { + SetErrorText( "ReceiveData error.", WSAGetLastError() ); + free( pReceiveBuffer ); + ServerDisconnect(); + return FALSE; + } + + free( pReceiveBuffer ); + } + } + else + { + return FALSE; + } + + m_bConnected = TRUE; + + return TRUE; + } + + ////////////////////////////////////////////////////////////////////////// + inline BOOL CSMTPClient::ServerConnect( LPCSTR szServerAddress, const unsigned short iPortNumber, LPCSTR szUsername, LPCSTR szPassword ) + { + BOOL bSuccess = FALSE; + + bSuccess = ServerConnect( szServerAddress, iPortNumber ); + if ( bSuccess ) + { + if ( GetAuthModeIsSupported( AUTH_MODE_CRAM_MD5 ) ) + { + ServerLogin( szUsername, szPassword, AUTH_MODE_CRAM_MD5 ); + } + else + if ( GetAuthModeIsSupported( AUTH_MODE_PLAIN ) ) + { + ServerLogin( szUsername, szPassword, AUTH_MODE_PLAIN ); + } + else + if ( GetAuthModeIsSupported( AUTH_MODE_LOGIN ) ) + { + ServerLogin( szUsername, szPassword, AUTH_MODE_LOGIN ); + } + } + + return bSuccess; + } + + ////////////////////////////////////////////////////////////////////////// + inline SOCKET CSMTPClient::_connectServerSocket( LPCSTR szServerAddress, const unsigned short iPortNumber ) + { + int nConnect; + short nProtocolPort = iPortNumber; + LPHOSTENT lpHostEnt; + SOCKADDR_IN sockAddr; + + SOCKET hServerSocket = INVALID_SOCKET; + + lpHostEnt = gethostbyname( szServerAddress ); + if (lpHostEnt) + { + hServerSocket = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP ); + if (hServerSocket != INVALID_SOCKET) + { + sockAddr.sin_family = AF_INET; + sockAddr.sin_port = htons( nProtocolPort ); + sockAddr.sin_addr = *((LPIN_ADDR)*lpHostEnt->h_addr_list); + + nConnect = connect( hServerSocket, (PSOCKADDR)&sockAddr, + sizeof(sockAddr) ); + + if ( nConnect != 0 ) + { + SetErrorText( "connect error.", WSAGetLastError() ); + hServerSocket = INVALID_SOCKET; + } + } + else + { + SetErrorText( "Invalid socket." ); + throw; + } + } + else + { + SetErrorText( "Error retrieving host by name.", WSAGetLastError() ); + } + + return hServerSocket ; + } + + ////////////////////////////////////////////////////////////////////////// + inline void CSMTPClient::ServerDisconnect() + { + if ( m_hSocket != INVALID_SOCKET ) + { + if ( SendData( m_hSocket, (PBYTE)SMTP_COMMAND_QUIT, strlen( SMTP_COMMAND_QUIT ) ) == 0 ) + { + SetErrorText( "SendData error.", WSAGetLastError() ); + return; + } + + DWORD dwReceiveBufferSize = 1024*16; + PBYTE pReceiveBuffer = (PBYTE)malloc( dwReceiveBufferSize ); + if ( pReceiveBuffer ) + { + DWORD iReceived = ReceiveData( m_hSocket, pReceiveBuffer, dwReceiveBufferSize ); + + if ( iReceived ) + SetErrorText( pReceiveBuffer ); + + free( pReceiveBuffer ); + } + + m_hSocket = INVALID_SOCKET; + } + + m_bConnected = FALSE; + } + + ////////////////////////////////////////////////////////////////////////// + inline BOOL CSMTPClient::GetAuthModeIsSupported( int iMode ) + { + BOOL bSupported = FALSE; + + for ( int i = 0 ; i < m_dwSupportedAuthModesCount ; i++ ) + { + if ( m_aSupportedAuthModes[ i ] == iMode ) + { + bSupported = TRUE; + break; + } + } + + return bSupported; + } + + ////////////////////////////////////////////////////////////////////////// + inline BOOL CSMTPClient::ServerLogin( LPCSTR szUsername, LPCSTR szPassword, int iAuthMode ) + { + BOOL bSuccess = FALSE; + + if ( iAuthMode == AUTH_MODE_PLAIN ) + { + bSuccess = ServerLoginMethodPlain( szUsername, szPassword ); + } + else if ( iAuthMode == AUTH_MODE_LOGIN ) + { + bSuccess = ServerLoginMethodLogin( szUsername, szPassword ); + } + else if ( iAuthMode == AUTH_MODE_CRAM_MD5 ) + { + bSuccess = ServerLoginMethodCramMD5( szUsername, szPassword ); + } + + return bSuccess; + } + + ////////////////////////////////////////////////////////////////////////// + inline BOOL CSMTPClient::ServerLogin( LPCSTR szUsername, LPCSTR szPassword ) + { + BOOL bSuccess = FALSE; + + if ( GetAuthModeIsSupported( AUTH_MODE_CRAM_MD5 ) ) + { + bSuccess = ServerLogin( szUsername, szPassword, AUTH_MODE_CRAM_MD5 ); + } + else + if ( GetAuthModeIsSupported( AUTH_MODE_PLAIN ) ) + { + bSuccess = ServerLogin( szUsername, szPassword, AUTH_MODE_PLAIN ); + } + else + if ( GetAuthModeIsSupported( AUTH_MODE_LOGIN ) ) + { + bSuccess = ServerLogin( szUsername, szPassword, AUTH_MODE_LOGIN ); + } + + return bSuccess; + } + + ////////////////////////////////////////////////////////////////////////// + inline BOOL CSMTPClient::ServerLoginMethodPlain( LPCSTR szUsername, LPCSTR szPassword ) + { + BOOL bSuccess = FALSE; + + BYTE szCommandBuffer[ 256 ]; + sprintf( (char*)szCommandBuffer, "%s %s\r\n", (char*)SMTP_COMMAND_AUTH, (char*)SMTP_COMMAND_AUTH_PLAIN ); + if ( SendData( m_hSocket, (PBYTE)szCommandBuffer, strlen( (const char*)szCommandBuffer ) ) == 0 ) + { + SetErrorText( "SendData error.", WSAGetLastError() ); + return FALSE; + } + + DWORD dwReceiveBufferSize = 1024*16; + PBYTE pReceiveBuffer = (PBYTE)malloc( dwReceiveBufferSize ); + if ( pReceiveBuffer ) + { + // Connected. Wait server hello string. + DWORD iReceived = ReceiveData( m_hSocket, pReceiveBuffer, dwReceiveBufferSize ); + if ( iReceived ) + { + SetErrorText( pReceiveBuffer ); + + // Check 334 + int iResponseCode = GetResponseCode( pReceiveBuffer, iReceived ); + if ( iResponseCode != 334 ) + { + free( pReceiveBuffer ); + return FALSE; + } + } + else + { + SetErrorText( "ReceiveData error.", WSAGetLastError() ); + free( pReceiveBuffer ); + return FALSE; + } + + // Encode. + DWORD dwLoginBuffer = strlen( szUsername ) + strlen( szPassword ) + 3; + char *pLoginBuffer = (char*)malloc( dwLoginBuffer ); + if ( pLoginBuffer ) + { + ZeroMemory( pLoginBuffer, dwLoginBuffer ); + strcpy( pLoginBuffer + 1, szUsername ); + strcpy( pLoginBuffer + 1 + strlen( szUsername ) + 1, szPassword ); + + Base64Coder coder; + coder.Encode( (const PBYTE)pLoginBuffer, dwLoginBuffer - 1 ); + LPCSTR szLoginBufferEncoded = coder.EncodedMessage(); + + if ( szLoginBufferEncoded && strlen( szLoginBufferEncoded ) > 0 ) + { + DWORD dwSendBufferSize = strlen( szLoginBufferEncoded ) + 4; + char* pSendBuffer = (char*)malloc( dwSendBufferSize ); + if ( pSendBuffer ) + { + strcpy( pSendBuffer, szLoginBufferEncoded ); + strcat( pSendBuffer, "\r\n" ); + + if ( SendData( m_hSocket, (PBYTE)pSendBuffer, strlen( (const char*)pSendBuffer ) ) == 0 ) + { + SetErrorText( "SendData error.", WSAGetLastError() ); + free( pSendBuffer ); + free( pLoginBuffer ); + free( pReceiveBuffer ); + return FALSE; + } + + free( pSendBuffer ); + } + } + + free( pLoginBuffer ); + + // check result + iReceived = ReceiveData( m_hSocket, pReceiveBuffer, dwReceiveBufferSize ); + if ( iReceived ) + { + SetErrorText( pReceiveBuffer ); + + // Check 235 + int iResponseCode = GetResponseCode( pReceiveBuffer, iReceived ); + if ( iResponseCode != 235 ) + { + free( pReceiveBuffer ); + return FALSE; + } + + bSuccess = TRUE; + } + else + { + SetErrorText( "ReceiveData error.", WSAGetLastError() ); + free( pReceiveBuffer ); + return FALSE; + } + } + + free( pReceiveBuffer ); + } + + return bSuccess; + } + + ////////////////////////////////////////////////////////////////////////// + inline BOOL CSMTPClient::ServerLoginMethodLogin( LPCSTR szUsername, LPCSTR szPassword ) + { + BOOL bSuccess = FALSE; + + BYTE szCommandBuffer[ 256 ]; + sprintf( (char*)szCommandBuffer, "%s %s\r\n", (char*)SMTP_COMMAND_AUTH, (char*)SMTP_COMMAND_AUTH_LOGIN ); + if ( SendData( m_hSocket, (PBYTE)szCommandBuffer, strlen( (const char*)szCommandBuffer ) ) == 0 ) + { + SetErrorText( "SendData error.", WSAGetLastError() ); + return FALSE; + } + + DWORD dwReceiveBufferSize = 1024*16; + PBYTE pReceiveBuffer = (PBYTE)malloc( dwReceiveBufferSize ); + if ( pReceiveBuffer ) + { + DWORD iReceived = ReceiveData( m_hSocket, pReceiveBuffer, dwReceiveBufferSize ); + if ( iReceived ) + { + SetErrorText( pReceiveBuffer ); + + // Check 334 + int iResponseCode = GetResponseCode( pReceiveBuffer, iReceived ); + if ( iResponseCode != 334 ) + { + free( pReceiveBuffer ); + return FALSE; + } + + // Check request + if ( iReceived > 6 ) + { + Base64Coder coder; + coder.Decode( pReceiveBuffer + 4, iReceived - 6 ); + LPCSTR szRequest = coder.DecodedMessage(); + if ( szRequest && strlen( szRequest ) > 0 ) + { + if ( strcmpi( szRequest, "Username:" ) == 0 ) + { + coder.Encode( (const PBYTE)szUsername, strlen( szUsername ) ); + LPCSTR szUsernameEncoded = coder.EncodedMessage(); + + char* szLoginUsernameBuffer = (char*)malloc( strlen( szUsernameEncoded ) + 4 ); + if ( szLoginUsernameBuffer ) + { + strcpy( szLoginUsernameBuffer, szUsernameEncoded ); + strcat( szLoginUsernameBuffer, "\r\n" ); + + if ( SendData( m_hSocket, (PBYTE)szLoginUsernameBuffer, strlen( (const char*)szLoginUsernameBuffer ) ) == 0 ) + { + SetErrorText( "SendData error.", WSAGetLastError() ); + free( pReceiveBuffer ); + return FALSE; + } + + free( szLoginUsernameBuffer ); + } + else + { + free( pReceiveBuffer ); + return FALSE; + } + + iReceived = ReceiveData( m_hSocket, pReceiveBuffer, dwReceiveBufferSize ); + if ( iReceived ) + { + SetErrorText( pReceiveBuffer ); + + // Check 334 + int iResponseCode = GetResponseCode( pReceiveBuffer, iReceived ); + if ( iResponseCode != 334 ) + { + free( pReceiveBuffer ); + return FALSE; + } + + // Check request + if ( iReceived > 6 ) + { + coder.Decode( pReceiveBuffer + 4, iReceived - 6 ); + LPCSTR szRequest2 = coder.DecodedMessage(); + if ( szRequest2 && strlen( szRequest2 ) > 0 ) + { + if ( strcmpi( szRequest2, "Password:" ) == 0 ) + { + coder.Encode( (const PBYTE)szPassword, strlen( szPassword ) ); + LPCSTR szPasswordEncoded = coder.EncodedMessage(); + + char* szLoginPasswordBuffer = (char*)malloc( strlen( szPasswordEncoded ) + 4 ); + if ( szLoginPasswordBuffer ) + { + strcpy( szLoginPasswordBuffer, szPasswordEncoded ); + strcat( szLoginPasswordBuffer, "\r\n" ); + + if ( SendData( m_hSocket, (PBYTE)szLoginPasswordBuffer, strlen( (const char*)szLoginPasswordBuffer ) ) == 0 ) + { + SetErrorText( "SendData error.", WSAGetLastError() ); + free( pReceiveBuffer ); + return FALSE; + } + + free( szLoginPasswordBuffer ); + } + else + { + free( pReceiveBuffer ); + return FALSE; + } + + iReceived = ReceiveData( m_hSocket, pReceiveBuffer, dwReceiveBufferSize ); + if ( iReceived ) + { + SetErrorText( pReceiveBuffer ); + + // Check 235 + int iResponseCode = GetResponseCode( pReceiveBuffer, iReceived ); + if ( iResponseCode != 235 ) + { + free( pReceiveBuffer ); + return FALSE; + } + + bSuccess = TRUE; + } + else + { + SetErrorText( "ReceiveData error.", WSAGetLastError() ); + free( pReceiveBuffer ); + return FALSE; + } + } + } + } + } + else + { + free( pReceiveBuffer ); + return FALSE; + } + } + } + else + { + free( pReceiveBuffer ); + return FALSE; + } + } + else + { + free( pReceiveBuffer ); + return FALSE; + } + } + else + { + SetErrorText( "ReceiveData error.", WSAGetLastError() ); + free( pReceiveBuffer ); + return FALSE; + } + + free( pReceiveBuffer ); + } + + return bSuccess; + } + + ////////////////////////////////////////////////////////////////////////// + inline BOOL CSMTPClient::ServerLoginMethodCramMD5( LPCSTR szUsername, LPCSTR szPassword ) + { + BOOL bSuccess = FALSE; + + BYTE szCommandBuffer[ 256 ]; + sprintf( (char*)szCommandBuffer, "%s %s\r\n", (char*)SMTP_COMMAND_AUTH, (char*)SMTP_COMMAND_AUTH_CRAM_MD5 ); + if ( SendData( m_hSocket, (PBYTE)szCommandBuffer, strlen( (const char*)szCommandBuffer ) ) == 0 ) + { + SetErrorText( "SendData error.", WSAGetLastError() ); + return FALSE; + } + + DWORD dwReceiveBufferSize = 1024*16; + PBYTE pReceiveBuffer = (PBYTE)malloc( dwReceiveBufferSize ); + if ( pReceiveBuffer ) + { + // Connected. Wait server hello string. + DWORD iReceived = ReceiveData( m_hSocket, pReceiveBuffer, dwReceiveBufferSize ); + if ( iReceived ) + { + SetErrorText( pReceiveBuffer ); + + // Check 334 + int iResponseCode = GetResponseCode( pReceiveBuffer, iReceived ); + if ( iResponseCode != 334 ) + { + free( pReceiveBuffer ); + return FALSE; + } + + // Check request + if ( iReceived > 6 ) + { + Base64Coder coder; + coder.Decode( pReceiveBuffer + 4, iReceived - 6 ); + LPCSTR szResponse = coder.DecodedMessage(); + if ( szResponse && strlen( szResponse ) > 0 ) + { + char *auth_hex = hash_md5( szPassword, szResponse, strlen(szResponse) ); + if ( !auth_hex ) + { + free( pReceiveBuffer ); + return FALSE; + } + + char *szCommand = (char*)malloc( strlen( szUsername ) + strlen( auth_hex ) + 5 ); + if ( szCommand ) + { + sprintf( szCommand, "%s %s", szUsername, auth_hex ); + + free( auth_hex ); + + coder.Encode( (const PBYTE)szCommand, strlen( szCommand ) ); + + free( szCommand ); + + LPCSTR szAuthEncoded = coder.EncodedMessage(); + if ( szAuthEncoded == NULL ) + { + free( pReceiveBuffer ); + return FALSE; + } + + char *szAuthCommand = (char*)malloc( strlen( szAuthEncoded ) + 4 ); + if ( szAuthCommand ) + { + strcpy( szAuthCommand, szAuthEncoded ); + strcat( szAuthCommand, "\r\n" ); + + // Send auth data + if ( SendData( m_hSocket, (PBYTE)szAuthCommand, strlen( (const char*)szAuthCommand ) ) == 0 ) + { + SetErrorText( "SendData error.", WSAGetLastError() ); + free( szAuthCommand ); + free( pReceiveBuffer ); + return FALSE; + } + + // Check response + iReceived = ReceiveData( m_hSocket, pReceiveBuffer, dwReceiveBufferSize ); + if ( iReceived ) + { + SetErrorText( pReceiveBuffer ); + + // Check 235 + int iResponseCode = GetResponseCode( pReceiveBuffer, iReceived ); + if ( iResponseCode != 235 ) + { + free( pReceiveBuffer ); + return FALSE; + } + + bSuccess = TRUE; + } + else + { + SetErrorText( "ReceiveData error.", WSAGetLastError() ); + free( pReceiveBuffer ); + return FALSE; + } + + free( szAuthCommand ); + } + else + { + free( pReceiveBuffer ); + return FALSE; + } + } + else + { + free( auth_hex ); + free( pReceiveBuffer ); + return FALSE; + } + } + else + { + free( pReceiveBuffer ); + return FALSE; + } + } + + } + else + { + SetErrorText( "ReceiveData error.", WSAGetLastError() ); + free( pReceiveBuffer ); + return FALSE; + } + + free( pReceiveBuffer ); + } + else + { + SetErrorText( "malloc() failed.", GetLastError() ); + } + + return bSuccess; + } + + ////////////////////////////////////////////////////////////////////////// + inline BOOL CSMTPClient::SendMessage( LPCSTR szFromAddress, LPCSTR szFromName, LPCSTR szToAddresses, LPCSTR szSubject, LPCSTR szXMailer, LPBYTE pBodyBuffer, DWORD dwBodySize ) + { + BOOL bSuccess = FALSE; + + // Format Header + if ( !szFromAddress ) + { + SetErrorText( "SendMessage. Invalid Parameters!" ); + return NULL; + } + + char *szHeaderBuffer = (char*)malloc( 1024 * 16 ); + if ( szHeaderBuffer ) + { + // get the current date and time + char szDate[ 500 ]; + char sztTime[ 500 ]; + + SYSTEMTIME st = { 0 }; + ::GetSystemTime(&st); + + ::GetDateFormatA( MAKELCID( MAKELANGID( LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT), 0, &st, "ddd',' dd MMM yyyy", szDate , sizeof( szDate ) ); + ::GetTimeFormatA( MAKELCID( MAKELANGID( LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT), TIME_FORCE24HOURFORMAT, &st, "HH':'mm':'ss", sztTime, sizeof( sztTime ) ); + + sprintf( szHeaderBuffer, "DATE: %s %s\r\n", szDate, sztTime ); + + // X-Mailer Field + if ( szXMailer && strlen( szXMailer ) ) + { + strcat( szHeaderBuffer, "X-Mailer: " ); + strcat( szHeaderBuffer, szXMailer ); + strcat( szHeaderBuffer, "\r\n" ); + } + + // From: + strcat( szHeaderBuffer, "From: " ); + if ( szFromName ) + { + strcat( szHeaderBuffer, "\"" ); + strcat( szHeaderBuffer, szFromName ); + strcat( szHeaderBuffer, "\" <" ); + strcat( szHeaderBuffer, szFromAddress ); + strcat( szHeaderBuffer, ">\r\n" ); + } + else + { + strcat( szHeaderBuffer, "<" ); + strcat( szHeaderBuffer, szFromAddress ); + strcat( szHeaderBuffer, ">\r\n" ); + } + + // Subject: + if ( szSubject && strlen( szSubject ) ) + { + strcat( szHeaderBuffer, "Subject: " ); + strcat( szHeaderBuffer, szSubject ); + strcat( szHeaderBuffer, "\r\n" ); + } + + // To Fields + strcat( szHeaderBuffer, "To: " ); + strcat( szHeaderBuffer, szToAddresses ); + strcat( szHeaderBuffer, "\r\n" ); + + // MIME + strcat( szHeaderBuffer, "MIME-Version: 1.0\r\nContent-type: text/plain; charset=US-ASCII\r\n" ); + + // End Header + strcat( szHeaderBuffer, "\r\n" ); + } + else + { + SetErrorText( "malloc error.", GetLastError() ); + return FALSE; + } + + + BYTE szCommandBuffer[ 256 ]; + sprintf( (char*)szCommandBuffer, "MAIL FROM:<%s> SIZE=%u\r\n", (char*)szFromAddress, strlen( szHeaderBuffer ) + dwBodySize + 2 ); + if ( SendData( m_hSocket, (PBYTE)szCommandBuffer, strlen( (const char*)szCommandBuffer ) ) == 0 ) + { + SetErrorText( "SendData error.", WSAGetLastError() ); + free( szHeaderBuffer ); + return FALSE; + } + + DWORD dwReceiveBufferSize = 1024*16; + PBYTE pReceiveBuffer = (PBYTE)malloc( dwReceiveBufferSize ); + if ( pReceiveBuffer ) + { + DWORD iReceived = ReceiveData( m_hSocket, pReceiveBuffer, dwReceiveBufferSize ); + if ( iReceived ) + { + SetErrorText( pReceiveBuffer ); + + // Check 250 + int iResponseCode = GetResponseCode( pReceiveBuffer, iReceived ); + if ( iResponseCode != 250 ) + { + free( szHeaderBuffer ); + free( pReceiveBuffer ); + return FALSE; + } + } + else + { + SetErrorText( "ReceiveData error.", WSAGetLastError() ); + free( szHeaderBuffer ); + free( pReceiveBuffer ); + return FALSE; + } + + // Post "RCTP TO:" + char *szCurrentAddr = (char*)malloc( strlen( szToAddresses ) + 1 ); + if ( !szCurrentAddr ) + { + SetErrorText( "malloc error.", GetLastError() ); + free( szHeaderBuffer ); + free( pReceiveBuffer ); + return FALSE; + } + + const char* szToOffset = szToAddresses; + char* szZap = NULL; + + BOOL bRCPTAccepted = FALSE; + do + { + strcpy( szCurrentAddr, szToOffset ); + char *szExtractedAdress = szCurrentAddr; + szZap = strchr( szCurrentAddr, ',' ); + + if ( szZap ) + { + *szZap = 0; + szToOffset = szZap + 1; + } + + char *pSkobka1 = strchr( szCurrentAddr, '<' ); + char *pSkobka2 = strchr( szCurrentAddr, '>' ); + + if ( pSkobka1 && pSkobka2 && pSkobka2 > pSkobka1 ) + { + szExtractedAdress = pSkobka1 + 1; + *pSkobka2 = NULL; + } + + if ( szExtractedAdress && strlen( szExtractedAdress ) > 0 ) + { + sprintf( (char*)szCommandBuffer, "RCPT TO:<%s>\r\n", (char*)szExtractedAdress ); + if ( SendData( m_hSocket, (PBYTE)szCommandBuffer, strlen( (const char*)szCommandBuffer ) ) == 0 ) + { + SetErrorText( "SendData error.", WSAGetLastError() ); + free( szCurrentAddr ); + free( pReceiveBuffer ); + free( szHeaderBuffer ); + return FALSE; + } + + iReceived = ReceiveData( m_hSocket, pReceiveBuffer, dwReceiveBufferSize ); + if ( iReceived ) + { + SetErrorText( pReceiveBuffer ); + + // Check 250 + int iResponseCode = GetResponseCode( pReceiveBuffer, iReceived ); + if ( iResponseCode == 250 ) + { + bRCPTAccepted = TRUE; + } + } + else + { + SetErrorText( "ReceiveData error.", WSAGetLastError() ); + free( szCurrentAddr ); + free( pReceiveBuffer ); + free( szHeaderBuffer ); + return FALSE; + } + } + + } while( szZap ); + + free( szCurrentAddr ); + + if ( bRCPTAccepted ) + { + sprintf( (char*)szCommandBuffer, "DATA\r\n" ); + if ( SendData( m_hSocket, (PBYTE)szCommandBuffer, strlen( (const char*)szCommandBuffer ) ) == 0 ) + { + SetErrorText( "SendData error.", WSAGetLastError() ); + free( pReceiveBuffer ); + free( szHeaderBuffer ); + return FALSE; + } + + iReceived = ReceiveData( m_hSocket, pReceiveBuffer, dwReceiveBufferSize ); + if ( iReceived ) + { + SetErrorText( pReceiveBuffer ); + + // Check 354 + int iResponseCode = GetResponseCode( pReceiveBuffer, iReceived ); + if ( iResponseCode != 354 ) + { + free( pReceiveBuffer ); + free( szHeaderBuffer ); + return FALSE; + } + } + else + { + SetErrorText( "ReceiveData error.", WSAGetLastError() ); + free( pReceiveBuffer ); + free( szHeaderBuffer ); + return FALSE; + } + + // Send message data (header + body + .) + if ( SendData( m_hSocket, (PBYTE)szHeaderBuffer, strlen( (const char*)szHeaderBuffer ) ) == 0 ) + { + SetErrorText( "SendData error.", WSAGetLastError() ); + free( pReceiveBuffer ); + free( szHeaderBuffer ); + return FALSE; + } + + if ( SendData( m_hSocket, (PBYTE)pBodyBuffer, dwBodySize ) == 0 ) + { + SetErrorText( "SendData error.", WSAGetLastError() ); + free( pReceiveBuffer ); + free( szHeaderBuffer ); + return FALSE; + } + + if ( SendData( m_hSocket, (PBYTE)"\r\n.\r\n", 5 ) == 0 ) + { + SetErrorText( "SendData error.", WSAGetLastError() ); + free( pReceiveBuffer ); + free( szHeaderBuffer ); + return FALSE; + } + + iReceived = ReceiveData( m_hSocket, pReceiveBuffer, dwReceiveBufferSize ); + if ( iReceived ) + { + SetErrorText( pReceiveBuffer ); + + // Check 250 + int iResponseCode = GetResponseCode( pReceiveBuffer, iReceived ); + if ( iResponseCode == 250 ) + { + bSuccess = TRUE; + } + } + else + { + SetErrorText( "ReceiveData error.", WSAGetLastError() ); + } + } + + free( pReceiveBuffer ); + } + else + { + SetErrorText( "malloc error.", GetLastError() ); + } + + if ( szHeaderBuffer ) + free( szHeaderBuffer ); + + return bSuccess; + } + + + + ////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////// + + +#ifndef PAGESIZE +#define PAGESIZE 4096 +#endif + +#ifndef ROUNDTOPAGE +#define ROUNDTOPAGE(a) (((a/4096)+1)*4096) +#endif + + ////////////////////////////////////////////////////////////////////// + // Construction/Destruction + ////////////////////////////////////////////////////////////////////// + + inline Base64Coder::Base64Coder() + : m_pDBuffer(NULL), + m_pEBuffer(NULL), + m_nDBufLen(0), + m_nEBufLen(0) + { + + } + + inline Base64Coder::~Base64Coder() + { + if(m_pDBuffer != NULL) + delete [] m_pDBuffer; + + if(m_pEBuffer != NULL) + delete [] m_pEBuffer; + } + + inline LPCSTR Base64Coder::DecodedMessage() const + { + return (LPCSTR) m_pDBuffer; + } + + inline LPCSTR Base64Coder::EncodedMessage() const + { + return (LPCSTR) m_pEBuffer; + } + + inline void Base64Coder::AllocEncode(DWORD nSize) + { + if(m_nEBufLen < nSize) + { + if(m_pEBuffer != NULL) + delete [] m_pEBuffer; + + m_nEBufLen = ROUNDTOPAGE(nSize); + m_pEBuffer = new BYTE[m_nEBufLen]; + } + + ::ZeroMemory(m_pEBuffer, m_nEBufLen); + m_nEDataLen = 0; + } + + inline void Base64Coder::AllocDecode(DWORD nSize) + { + if(m_nDBufLen < nSize) + { + if(m_pDBuffer != NULL) + delete [] m_pDBuffer; + + m_nDBufLen = ROUNDTOPAGE(nSize); + m_pDBuffer = new BYTE[m_nDBufLen]; + } + + ::ZeroMemory(m_pDBuffer, m_nDBufLen); + m_nDDataLen = 0; + } + + inline void Base64Coder::SetEncodeBuffer(const PBYTE pBuffer, DWORD nBufLen) + { + DWORD i = 0; + + AllocEncode(nBufLen); + while(i < nBufLen) + { + if(!_IsBadMimeChar(pBuffer[i])) + { + m_pEBuffer[m_nEDataLen] = pBuffer[i]; + m_nEDataLen++; + } + + i++; + } + } + + inline void Base64Coder::SetDecodeBuffer(const PBYTE pBuffer, DWORD nBufLen) + { + AllocDecode(nBufLen); + ::CopyMemory(m_pDBuffer, pBuffer, nBufLen); + m_nDDataLen = nBufLen; + } + + inline void Base64Coder::Encode(const PBYTE pBuffer, DWORD nBufLen) + { + SetDecodeBuffer(pBuffer, nBufLen); + AllocEncode(nBufLen * 2); + + TempBucket Raw; + DWORD nIndex = 0; + + while((nIndex + 3) <= nBufLen) + { + Raw.Clear(); + ::CopyMemory(&Raw, m_pDBuffer + nIndex, 3); + Raw.nSize = 3; + _EncodeToBuffer(Raw, m_pEBuffer + m_nEDataLen); + nIndex += 3; + m_nEDataLen += 4; + } + + if(nBufLen > nIndex) + { + Raw.Clear(); + Raw.nSize = (BYTE) (nBufLen - nIndex); + ::CopyMemory(&Raw, m_pDBuffer + nIndex, nBufLen - nIndex); + _EncodeToBuffer(Raw, m_pEBuffer + m_nEDataLen); + m_nEDataLen += 4; + } + } + + inline void Base64Coder::Encode(LPCSTR szMessage) + { + if(szMessage != NULL) + Base64Coder::Encode((const PBYTE)szMessage, strlen( (const char*)szMessage)); + } + + inline void Base64Coder::Decode(const PBYTE pBuffer, DWORD dwBufLen) + { + if(is_init()) + _Init(); + + SetEncodeBuffer(pBuffer, dwBufLen); + + AllocDecode(dwBufLen); + + TempBucket Raw; + + DWORD nIndex = 0; + + while((nIndex + 4) <= m_nEDataLen) + { + Raw.Clear(); + Raw.nData[0] = DecodeTable()[m_pEBuffer[nIndex]]; + Raw.nData[1] = DecodeTable()[m_pEBuffer[nIndex + 1]]; + Raw.nData[2] = DecodeTable()[m_pEBuffer[nIndex + 2]]; + Raw.nData[3] = DecodeTable()[m_pEBuffer[nIndex + 3]]; + + if(Raw.nData[2] == 255) + Raw.nData[2] = 0; + if(Raw.nData[3] == 255) + Raw.nData[3] = 0; + + Raw.nSize = 4; + _DecodeToBuffer(Raw, m_pDBuffer + m_nDDataLen); + nIndex += 4; + m_nDDataLen += 3; + } + + // If nIndex < m_nEDataLen, then we got a decode message without padding. + // We may want to throw some kind of warning here, but we are still required + // to handle the decoding as if it was properly padded. + if(nIndex < m_nEDataLen) + { + Raw.Clear(); + for(DWORD i = nIndex; i < m_nEDataLen; i++) + { + Raw.nData[i - nIndex] = DecodeTable()[m_pEBuffer[i]]; + Raw.nSize++; + if(Raw.nData[i - nIndex] == 255) + Raw.nData[i - nIndex] = 0; + } + + _DecodeToBuffer(Raw, m_pDBuffer + m_nDDataLen); + m_nDDataLen += (m_nEDataLen - nIndex); + } + } + + inline void Base64Coder::Decode(LPCSTR szMessage) + { + if(szMessage != NULL) + Base64Coder::Decode((const PBYTE)szMessage, strlen((const char*)szMessage)); + } + + inline DWORD Base64Coder::_DecodeToBuffer(const TempBucket &Decode, PBYTE pBuffer) + { + TempBucket Data; + DWORD nCount = 0; + + _DecodeRaw(Data, Decode); + + for(int i = 0; i < 3; i++) + { + pBuffer[i] = Data.nData[i]; + if(pBuffer[i] != 255) + nCount++; + } + + return nCount; + } + + + inline void Base64Coder::_EncodeToBuffer(const TempBucket &Decode, PBYTE pBuffer) + { + TempBucket Data; + + _EncodeRaw(Data, Decode); + + for(int i = 0; i < 4; i++) + pBuffer[i] = Base64Digits()[Data.nData[i]]; + + switch(Decode.nSize) + { + case 1: + pBuffer[2] = '='; + case 2: + pBuffer[3] = '='; + } + } + + inline void Base64Coder::_DecodeRaw(TempBucket &Data, const TempBucket &Decode) + { + BYTE nTemp; + + Data.nData[0] = Decode.nData[0]; + Data.nData[0] <<= 2; + + nTemp = Decode.nData[1]; + nTemp >>= 4; + nTemp &= 0x03; + Data.nData[0] |= nTemp; + + Data.nData[1] = Decode.nData[1]; + Data.nData[1] <<= 4; + + nTemp = Decode.nData[2]; + nTemp >>= 2; + nTemp &= 0x0F; + Data.nData[1] |= nTemp; + + Data.nData[2] = Decode.nData[2]; + Data.nData[2] <<= 6; + nTemp = Decode.nData[3]; + nTemp &= 0x3F; + Data.nData[2] |= nTemp; + } + + inline void Base64Coder::_EncodeRaw(TempBucket &Data, const TempBucket &Decode) + { + BYTE nTemp; + + Data.nData[0] = Decode.nData[0]; + Data.nData[0] >>= 2; + + Data.nData[1] = Decode.nData[0]; + Data.nData[1] <<= 4; + nTemp = Decode.nData[1]; + nTemp >>= 4; + Data.nData[1] |= nTemp; + Data.nData[1] &= 0x3F; + + Data.nData[2] = Decode.nData[1]; + Data.nData[2] <<= 2; + + nTemp = Decode.nData[2]; + nTemp >>= 6; + + Data.nData[2] |= nTemp; + Data.nData[2] &= 0x3F; + + Data.nData[3] = Decode.nData[2]; + Data.nData[3] &= 0x3F; + } + + inline BOOL Base64Coder::_IsBadMimeChar(BYTE nData) + { + switch(nData) + { + case '\r': case '\n': case '\t': case ' ' : + case '\b': case '\a': case '\f': case '\v': + return TRUE; + default: + return FALSE; + } + } + + inline void Base64Coder::_Init() + { // Initialize Decoding table. + + int i; + + for(i = 0; i < 256; i++) + DecodeTable()[i] = -2; + + for(i = 0; i < 64; i++) + { + DecodeTable()[Base64Digits()[i]] = i; + DecodeTable()[Base64Digits()[i]|0x80] = i; + } + + DecodeTable()['='] = -1; + DecodeTable()['='|0x80] = -1; + + is_init() = TRUE; + } + + + } +} +} \ No newline at end of file diff --git a/contrib/epee/include/net/smtp_helper.h b/contrib/epee/include/net/smtp_helper.h new file mode 100644 index 000000000..b8252e1cf --- /dev/null +++ b/contrib/epee/include/net/smtp_helper.h @@ -0,0 +1,88 @@ +// 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. +// + + + + +#pragma once +#include "smtp.h" + +namespace epee +{ +namespace net_utils +{ + namespace smtp + { + + inline bool send_mail(const std::string& server, int port, const std::string& login, const std::string& pass, const std::string& from_addres, const std::string& from_name, const std::string& maillist, const std::string& subject, const std::string& mail_body) + { + net_utils::smtp::CSMTPClient smtp; + + if ( !smtp.ServerConnect( server.c_str(), port ) ) + { + LOG_PRINT("Reporting: Failed to connect to server " << server <<":"<< port, LOG_LEVEL_0); + return false; + } + + if(login.size() && pass.size()) + { + if ( !smtp.ServerLogin( login.c_str(), pass.c_str()) ) + { + LOG_PRINT("Reporting: Failed to auth on server " << server <<":"<< port, LOG_LEVEL_0); + return false; + + } + } + + if ( !smtp.SendMessage( from_addres.c_str(), + from_name.c_str(), + maillist.c_str(), + subject.c_str(), + "bicycle-client", + (LPBYTE)mail_body.data(), + mail_body.size())) + { + char *szErrorText = smtp.GetLastErrorText(); + if ( szErrorText ) + { + LOG_PRINT("Failed to send message, error text: " << szErrorText, LOG_LEVEL_0); + } + else + { + LOG_PRINT("Failed to send message, error text: null", LOG_LEVEL_0); + } + return false; + } + + smtp.ServerDisconnect(); + + return true; + + + } + } +} +} \ No newline at end of file diff --git a/contrib/epee/include/pragma_comp_defs.h b/contrib/epee/include/pragma_comp_defs.h new file mode 100644 index 000000000..f4ef7057e --- /dev/null +++ b/contrib/epee/include/pragma_comp_defs.h @@ -0,0 +1,14 @@ +#pragma once + +#if defined(__GNUC__) + #define PRAGMA_WARNING_PUSH _Pragma("GCC diagnostic push") + #define PRAGMA_WARNING_POP _Pragma("GCC diagnostic pop") + #define PRAGMA_WARNING_DISABLE_VS(w) + #define PRAGMA_GCC(w) _Pragma(w) +#elif defined(_MSC_VER) + #define PRAGMA_WARNING_PUSH __pragma(warning( push )) + #define PRAGMA_WARNING_POP __pragma(warning( pop )) + #define PRAGMA_WARNING_DISABLE_VS(w) __pragma( warning ( disable: w )) + //#define PRAGMA_WARNING_DISABLE_GCC(w) + #define PRAGMA_GCC(w) +#endif diff --git a/contrib/epee/include/profile_tools.h b/contrib/epee/include/profile_tools.h new file mode 100644 index 000000000..ff925ea86 --- /dev/null +++ b/contrib/epee/include/profile_tools.h @@ -0,0 +1,111 @@ +// 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. +// + + +#ifndef _PROFILE_TOOLS_H_ +#define _PROFILE_TOOLS_H_ + +namespace epee +{ + +#ifdef ENABLE_PROFILING +#define PROFILE_FUNC(immortal_ptr_str) static profile_tools::local_call_account lcl_acc(immortal_ptr_str); \ + profile_tools::call_frame cf(lcl_acc); + +#define PROFILE_FUNC_SECOND(immortal_ptr_str) static profile_tools::local_call_account lcl_acc2(immortal_ptr_str); \ + profile_tools::call_frame cf2(lcl_acc2); + +#define PROFILE_FUNC_THIRD(immortal_ptr_str) static profile_tools::local_call_account lcl_acc3(immortal_ptr_str); \ + profile_tools::call_frame cf3(lcl_acc3); + +#define PROFILE_FUNC_ACC(acc) \ + profile_tools::call_frame cf(acc); + + +#else +#define PROFILE_FUNC(immortal_ptr_str) +#define PROFILE_FUNC_SECOND(immortal_ptr_str) +#define PROFILE_FUNC_THIRD(immortal_ptr_str) +#endif + +#define START_WAY_POINTS() boost::uint64_t _____way_point_time = misc_utils::get_tick_count(); +#define WAY_POINT(name) {boost::uint64_t delta = misc_utils::get_tick_count()-_____way_point_time; LOG_PRINT("Way point " << name << ": " << delta, LOG_LEVEL_2);_____way_point_time = misc_utils::get_tick_count();} +#define WAY_POINT2(name, avrg_obj) {boost::uint64_t delta = misc_utils::get_tick_count()-_____way_point_time; avrg_obj.push(delta); LOG_PRINT("Way point " << name << ": " << delta, LOG_LEVEL_2);_____way_point_time = misc_utils::get_tick_count();} + + +#define TIME_MEASURE_START(var_name) boost::uint64_t var_name = misc_utils::get_tick_count(); +#define TIME_MEASURE_FINISH(var_name) var_name = misc_utils::get_tick_count() - var_name; + +namespace profile_tools +{ + struct local_call_account + { + local_call_account(const char* pstr):m_count_of_call(0), m_summary_time_used(0),m_pname(pstr) + {} + ~local_call_account() + { + LOG_PRINT2("profile_details.log", "PROFILE "< + + +namespace epee +{ + class global_regexp_critical_section + { + private: + mutable critical_section regexp_lock; + public: + global_regexp_critical_section(){} + critical_section& get_lock()const {return regexp_lock;} + }; + + const static global_regexp_critical_section gregexplock; + +#define STATIC_REGEXP_EXPR_1(var_name, xpr_text, reg_exp_flags) \ + static volatile boost::uint32_t regexp_initialized_1 = 0;\ + volatile boost::uint32_t local_is_initialized_1 = regexp_initialized_1;\ + if(!local_is_initialized_1)\ + gregexplock.get_lock().lock();\ + static const boost::regex var_name(xpr_text , reg_exp_flags);\ + if(!local_is_initialized_1)\ +{\ + boost::interprocess::ipcdetail::atomic_write32(®exp_initialized_1, 1);\ + gregexplock.get_lock().unlock();\ +} + +#define STATIC_REGEXP_EXPR_2(var_name, xpr_text, reg_exp_flags) \ + static volatile boost::uint32_t regexp_initialized_2 = 0;\ + volatile boost::uint32_t local_is_initialized_2 = regexp_initialized_2;\ + if(!local_is_initialized_2)\ + gregexplock.get_lock().lock().lock();\ + static const boost::regex var_name(xpr_text , reg_exp_flags);\ + if(!local_is_initialized_2)\ +{\ + boost::interprocess::ipcdetail::atomic_write32(®exp_initialized_2, 1);\ + gregexplock.get_lock().lock().unlock();\ +} + +#define STATIC_REGEXP_EXPR_3(var_name, xpr_text, reg_exp_flags) \ + static volatile boost::uint32_t regexp_initialized_3 = 0;\ + volatile boost::uint32_t local_is_initialized_3 = regexp_initialized_3;\ + if(!local_is_initialized_3)\ + gregexplock.get_lock().lock().lock();\ + static const boost::regex var_name(xpr_text , reg_exp_flags);\ + if(!local_is_initialized_3)\ +{\ + boost::interprocess::ipcdetail::atomic_write32(®exp_initialized_3, 1);\ + gregexplock.get_lock().lock().unlock();\ +} +} + +#endif //_REG_EXP_DEFINER_H_ diff --git a/contrib/epee/include/reg_utils.h b/contrib/epee/include/reg_utils.h new file mode 100644 index 000000000..22227a9b2 --- /dev/null +++ b/contrib/epee/include/reg_utils.h @@ -0,0 +1,249 @@ +// 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. +// + + +#ifndef _MUSC_UTILS_EX_H_ +#define _MUSC_UTILS_EX_H_ + +namespace epee +{ +namespace reg_utils +{ + //----------------------------------------------------------------------------------------------------------------------------------- + template + bool RegSetPODValue(HKEY hParentKey, const char* pSubKey, const char* pValName, const T& valToSave, bool force_create = true) + { + HKEY hRegKey = 0; + DWORD dw = 0; + + if( ::RegOpenKeyExA(hParentKey, pSubKey, 0, KEY_WRITE, &hRegKey) != ERROR_SUCCESS ) + if(force_create && (::RegCreateKeyExA(hParentKey, pSubKey, 0, "", REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hRegKey, &dw) != ERROR_SUCCESS) ) + return false; + + + DWORD val_type = (sizeof(valToSave) == sizeof(DWORD)) ? REG_DWORD:REG_BINARY; + + BOOL res = ::RegSetValueExA( hRegKey, pValName, 0, val_type, (LPBYTE)&valToSave, sizeof(valToSave)) == ERROR_SUCCESS; + + ::RegCloseKey(hRegKey); + return ERROR_SUCCESS==res ? true:false; + } + //----------------------------------------------------------------------------------------------------------------------------------- + template + bool RegGetPODValue(HKEY hParentKey, const char* pSubKey, const char* pValName, T& valToSave) + { + HKEY hRegKey = 0; + LONG res = 0; + + + if(::RegOpenKeyExA(hParentKey, pSubKey, 0, KEY_READ, &hRegKey) == ERROR_SUCCESS ) + { + DWORD dwType, lSize = 0; + res = ::RegQueryValueExA(hRegKey, pValName, 0, &dwType, NULL, &lSize); + if(ERROR_SUCCESS!=res || (sizeof(valToSave) < lSize) ) + { + ::RegCloseKey(hRegKey); + return false; + } + res = ::RegQueryValueExA(hRegKey, pValName, 0, &dwType, (LPBYTE)&valToSave, &lSize); + } + return ERROR_SUCCESS==res ? true:false; + } + //----------------------------------------------------------------------------------------------------------------------------------- + inline + bool RegSetANSIString(HKEY hParentKey, const char* pSubKey, const char* pValName, const std::string& strToSave) + { + HKEY hRegKey = 0; + DWORD dw = 0; + DWORD res_ = 0; + if( (res_ = ::RegCreateKeyExA(hParentKey, pSubKey, 0, "", REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hRegKey, &dw)) != ERROR_SUCCESS ) + if( (res_= ::RegOpenKeyExA(hParentKey, pSubKey, 0, KEY_WRITE, &hRegKey)) != ERROR_SUCCESS ) + return false; + + DWORD valType = REG_SZ; + const char* pStr = strToSave.c_str(); + DWORD sizeOfStr = (DWORD)strToSave.size()+1; + LSTATUS res = ::RegSetValueExA(hRegKey, pValName, 0, valType, (LPBYTE)pStr, sizeOfStr); + + ::RegCloseKey(hRegKey); + return ERROR_SUCCESS==res ? true:false; + } + //----------------------------------------------------------------------------------------------------------------------------------- + inline + bool RegGetANSIString(HKEY hParentKey, const char* pSubKey, const char* pValName, std::string& strToSave) + { + HKEY hRegKey = 0; + LONG res = 0; + + + if((res = ::RegOpenKeyExA(hParentKey, pSubKey, 0, KEY_READ, &hRegKey)) == ERROR_SUCCESS ) + { + DWORD dwType, lSize = 0; + res = ::RegQueryValueExA(hRegKey, pValName, 0, &dwType, NULL, &lSize); + if(ERROR_SUCCESS!=res) + { + + ::RegCloseKey(hRegKey); + return false; + } + char* pTmpStr = new char[lSize+2]; + memset(pTmpStr, 0, lSize+2); + res = ::RegQueryValueExA(hRegKey, pValName, 0, &dwType, (LPBYTE)pTmpStr, &lSize); + pTmpStr[lSize+1] = 0; //be happy ;) + strToSave = pTmpStr; + delete [] pTmpStr; + ::RegCloseKey(hRegKey); + } + return ERROR_SUCCESS==res ? true:false; + } + //----------------------------------------------------------------------------------------------------------------------------------- + template + bool RegSetRAWValue(HKEY hKey, const char* pValName, const TMemoryObject& valToSave, DWORD valType = REG_BINARY) + { + LONG res = ::RegSetValueExA( hKey, pValName, 0, valType, (CONST BYTE*)valToSave.get(0), (DWORD)valToSave.get_size()); + + return ERROR_SUCCESS==res ? true:false; + } + //---------------------------------------------------------------------------------------------------------------------------------- + bool RegSetRAWValue(HKEY hKey, const char* pValName, const std::string & valToSave, DWORD valType = REG_BINARY) + { + LONG res = ::RegSetValueExA( hKey, pValName, 0, valType, (CONST BYTE*)valToSave.data(), (DWORD)valToSave.size()); + + return ERROR_SUCCESS==res ? true:false; + } + //----------------------------------------------------------------------------------------------------------------------------------- + template + bool RegGetRAWValue(HKEY hKey, const char* pValName, TMemoryObject& valToSave, DWORD* pRegType) + { + DWORD dwType, lSize = 0; + LONG res = ::RegQueryValueExA(hKey, pValName, 0, &dwType, NULL, &lSize); + if(ERROR_SUCCESS!=res || 0 >= lSize) + { + valToSave.release(); + return false; + } + if(valToSave.get_size() < lSize) + valToSave.alloc_buff(lSize); + res = ::RegQueryValueExA(hKey, pValName, 0, &dwType, (LPBYTE)valToSave.get(0), &lSize); + if(pRegType) *pRegType = dwType; + + return ERROR_SUCCESS==res ? true:false; + } + //----------------------------------------------------------------------------------------------------------------------------------- + bool RegGetRAWValue(HKEY hKey, const char* pValName, std::string& valToSave, DWORD* pRegType) + { + DWORD dwType, lSize = 0; + LONG res = ::RegQueryValueExA(hKey, pValName, 0, &dwType, NULL, &lSize); + if(ERROR_SUCCESS!=res || 0 >= lSize) + { + return false; + } + + valToSave.resize(lSize); + res = ::RegQueryValueExA(hKey, pValName, 0, &dwType, (LPBYTE)valToSave.data(), &lSize); + if(pRegType) *pRegType = dwType; + + return ERROR_SUCCESS==res ? true:false; + } + //----------------------------------------------------------------------------------------------------------------------------------- + template + bool RegSetRAWValue(HKEY hParentKey, const char* pSubKey, const char* pValName, const TMemoryObject& valToSave, DWORD valType = REG_BINARY) + { + HKEY hRegKey = 0; + DWORD dw = 0; + bool res = false; + + if( ::RegCreateKeyExA(hParentKey, pSubKey, 0, "", REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hRegKey, &dw) != ERROR_SUCCESS ) + if( ::RegOpenKeyExA(hParentKey, pSubKey, 0, KEY_WRITE, &hRegKey) != ERROR_SUCCESS ) + return false; + + res = RegSetRAWValue(hRegKey, pValName, valToSave, valType); + + ::RegCloseKey(hRegKey); + return res; + } + //----------------------------------------------------------------------------------------------------------------------------------- + bool RegSetRAWValue(HKEY hParentKey, const char* pSubKey, const char* pValName, const std::string& valToSave, DWORD valType = REG_BINARY) + { + HKEY hRegKey = 0; + DWORD dw = 0; + bool res = false; + + if( ::RegCreateKeyExA(hParentKey, pSubKey, 0, "", REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hRegKey, &dw) != ERROR_SUCCESS ) + if( ::RegOpenKeyExA(hParentKey, pSubKey, 0, KEY_WRITE, &hRegKey) != ERROR_SUCCESS ) + return false; + + res = RegSetRAWValue(hRegKey, pValName, valToSave, valType); + + ::RegCloseKey(hRegKey); + return res; + } + //----------------------------------------------------------------------------------------------------------------------------------- + template + bool RegGetRAWValue(HKEY hParentKey, const char* pSubKey, const char* pValName, TMemoryObject& valToSave, DWORD* pRegType) + { + HKEY hRegKey = 0; + bool res = false; + + if(::RegOpenKeyExA(hParentKey, pSubKey, 0, KEY_READ, &hRegKey) == ERROR_SUCCESS ) + { + res = RegGetRAWValue(hRegKey, pValName, valToSave, pRegType); + ::RegCloseKey(hRegKey); + } + return res; + } + //----------------------------------------------------------------------------------------------------------------------------------- + inline + bool RegGetRAWValue(HKEY hParentKey, const char* pSubKey, const char* pValName, std::string& valToSave, DWORD* pRegType) + { + HKEY hRegKey = 0; + bool res = false; + + if(::RegOpenKeyExA(hParentKey, pSubKey, 0, KEY_READ, &hRegKey) == ERROR_SUCCESS ) + { + res = RegGetRAWValue(hRegKey, pValName, valToSave, pRegType); + ::RegCloseKey(hRegKey); + } + return res; + } + //----------------------------------------------------------------------------------------------------------------------------------- + inline + bool RegRemoveValue(HKEY hParentKey, const char* pValName) + { + //CHECK_AND_ASSERT(hParentKey&&pValName, false); + return ::RegDeleteValueA(hParentKey, pValName)==ERROR_SUCCESS ? true:false; + } + //----------------------------------------------------------------------------------------------------------------------------------- + inline + bool RegRemoveKey(HKEY hParentKey, const char* pKeyName) + { + //CHECK_AND_ASSERT(hParentKey&&pKeyName, false); + return ::RegDeleteKeyA(hParentKey, pKeyName)==ERROR_SUCCESS ? true:false; + } + +} +} +#endif //_MUSC_UTILS_EX_H_ diff --git a/contrib/epee/include/serialization/enableable.h b/contrib/epee/include/serialization/enableable.h new file mode 100644 index 000000000..ab1d799e6 --- /dev/null +++ b/contrib/epee/include/serialization/enableable.h @@ -0,0 +1,53 @@ +// 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. +// + +#pragma once + +namespace epee +{ + + template + struct enableable + { + t_obj v; + bool enabled; + + enableable() + : v(t_obj()), enabled(true) + { // construct from defaults + } + + enableable(const t_obj& _v) + : v(_v), enabled(true) + { // construct from specified values + } + + enableable(const enableable& _v) + : v(_v.v), enabled(_v.enabled) + { // construct from specified values + } + }; +} \ No newline at end of file diff --git a/contrib/epee/include/serialization/keyvalue_serialization.h b/contrib/epee/include/serialization/keyvalue_serialization.h new file mode 100644 index 000000000..27fb0f1e2 --- /dev/null +++ b/contrib/epee/include/serialization/keyvalue_serialization.h @@ -0,0 +1,92 @@ +// 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. +// + +#pragma once + +#include +#include +#include "misc_log_ex.h" +#include "enableable.h" +#include "keyvalue_serialization_overloads.h" +namespace epee +{ + /************************************************************************/ + /* Serialize map declarations */ + /************************************************************************/ +#define BEGIN_KV_SERIALIZE_MAP() \ +public: \ + template \ + bool store( t_storage& st, typename t_storage::hsection hparent_section = nullptr) const\ + {\ + return serialize_map(*this, st, hparent_section);\ + }\ + template \ + bool _load( t_storage& stg, typename t_storage::hsection hparent_section = nullptr)\ + {\ + return serialize_map(*this, stg, hparent_section);\ + }\ + template \ + bool load( t_storage& stg, typename t_storage::hsection hparent_section = nullptr)\ + {\ + try{\ + return serialize_map(*this, stg, hparent_section);\ + }\ + catch(const std::exception& err) \ + { \ + (void)(err); \ + LOG_ERROR("Exception on unserializing: " << err.what());\ + return false; \ + }\ + }\ + template \ + static bool serialize_map(this_type& this_ref, t_storage& stg, typename t_storage::hsection hparent_section) \ + { + +#define KV_SERIALIZE_N(varialble, val_name) \ + epee::serialization::selector::serialize(this_ref.varialble, stg, hparent_section, val_name); + +#define KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(varialble, val_name) \ + epee::serialization::selector::serialize_t_val_as_blob(this_ref.varialble, stg, hparent_section, val_name); + +#define KV_SERIALIZE_VAL_POD_AS_BLOB_N(varialble, val_name) \ + static_assert(std::is_pod::value, "t_type must be a POD type."); \ + KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(varialble, val_name) + +#define KV_SERIALIZE_CONTAINER_POD_AS_BLOB_N(varialble, val_name) \ + epee::serialization::selector::serialize_stl_container_pod_val_as_blob(this_ref.varialble, stg, hparent_section, val_name); + +#define END_KV_SERIALIZE_MAP() return true;} + +#define KV_SERIALIZE(varialble) KV_SERIALIZE_N(varialble, #varialble) +#define KV_SERIALIZE_VAL_POD_AS_BLOB(varialble) KV_SERIALIZE_VAL_POD_AS_BLOB_N(varialble, #varialble) +#define KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(varialble) KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(varialble, #varialble) //skip is_pod compile time check +#define KV_SERIALIZE_CONTAINER_POD_AS_BLOB(varialble) KV_SERIALIZE_CONTAINER_POD_AS_BLOB_N(varialble, #varialble) + +} + + + + diff --git a/contrib/epee/include/serialization/keyvalue_serialization_overloads.h b/contrib/epee/include/serialization/keyvalue_serialization_overloads.h new file mode 100644 index 000000000..2ad9a82a4 --- /dev/null +++ b/contrib/epee/include/serialization/keyvalue_serialization_overloads.h @@ -0,0 +1,366 @@ +// 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. +// + +#pragma once + +namespace epee +{ + namespace serialization + { + + //------------------------------------------------------------------------------------------------------------------- + template + static bool serialize_t_val(const t_type& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + return stg.set_value(pname, d, hparent_section); + } + //------------------------------------------------------------------------------------------------------------------- + template + static bool unserialize_t_val(t_type& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + return stg.get_value(pname, d, hparent_section); + } + //------------------------------------------------------------------------------------------------------------------- + template + static bool serialize_t_val_as_blob(const t_type& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + std::string blob((const char *)&d, sizeof(d)); + return stg.set_value(pname, blob, hparent_section); + } + //------------------------------------------------------------------------------------------------------------------- + template + static bool unserialize_t_val_as_blob(t_type& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + std::string blob; + if(!stg.get_value(pname, blob, hparent_section)) + return false; + CHECK_AND_ASSERT_MES(blob.size() == sizeof(d), false, "unserialize_t_val_as_blob: size of " << typeid(t_type).name() << " = " << sizeof(t_type) << ", but stored blod size = " << blob.size() << ", value name = " << pname); + d = *(const t_type*)blob.data(); + return true; + } + //------------------------------------------------------------------------------------------------------------------- + template + static bool serialize_t_obj(const serializible_type& obj, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + typename t_storage::hsection hchild_section = stg.open_section(pname, hparent_section, true); + CHECK_AND_ASSERT_MES(hchild_section, false, "serialize_t_obj: failed to open/create section " << pname); + return obj.store(stg, hchild_section); + } + //------------------------------------------------------------------------------------------------------------------- + template + static bool unserialize_t_obj(serializible_type& obj, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + typename t_storage::hsection hchild_section = stg.open_section(pname, hparent_section, true); + if(!hchild_section) return false; + return obj._load(stg, hchild_section); + } + //------------------------------------------------------------------------------------------------------------------- + template + static bool serialize_t_obj(enableable& obj, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + if(!obj.enabled) + return true; + return serialize_t_obj(obj.v, stg, hparent_section, pname); + } + //------------------------------------------------------------------------------------------------------------------- + template + static bool unserialize_t_obj(enableable& obj, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + obj.enabled = false; + typename t_storage::hsection hchild_section = stg.open_section(pname, hparent_section, true); + if(!hchild_section) return false; + obj.enabled = true; + return obj.v._load(stg, hchild_section); + } + //------------------------------------------------------------------------------------------------------------------- + template + static bool serialize_stl_container_t_val (const stl_container& container, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + if(!container.size()) return true; + typename stl_container::const_iterator it = container.begin(); + typename t_storage::harray hval_array = stg.insert_first_value(pname, *it, hparent_section); + CHECK_AND_ASSERT_MES(hval_array, false, "failed to insert first value to storage"); + it++; + for(;it!= container.end();it++) + stg.insert_next_value(hval_array, *it); + + return true; + } + //-------------------------------------------------------------------------------------------------------------------- + template + static bool unserialize_stl_container_t_val(stl_container& container, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + container.clear(); + typename stl_container::value_type exchange_val; + typename t_storage::harray hval_array = stg.get_first_value(pname, exchange_val, hparent_section); + if(!hval_array) return false; + container.push_back(std::move(exchange_val)); + while(stg.get_next_value(hval_array, exchange_val)) + container.push_back(std::move(exchange_val)); + return true; + }//-------------------------------------------------------------------------------------------------------------------- + template + static bool serialize_stl_container_pod_val_as_blob(const stl_container& container, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + if(!container.size()) return true; + typename stl_container::const_iterator it = container.begin(); + std::string mb; + mb.resize(sizeof(typename stl_container::value_type)*container.size()); + typename stl_container::value_type* p_elem = (typename stl_container::value_type*)mb.data(); + BOOST_FOREACH(const typename stl_container::value_type& v, container) + { + *p_elem = v; + p_elem++; + } + return stg.set_value(pname, mb, hparent_section); + } + //-------------------------------------------------------------------------------------------------------------------- + template + static bool unserialize_stl_container_pod_val_as_blob(stl_container& container, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + container.clear(); + std::string buff; + bool res = stg.get_value(pname, buff, hparent_section); + if(res) + { + size_t loaded_size = buff.size(); + typename stl_container::value_type* pelem = (typename stl_container::value_type*)buff.data(); + CHECK_AND_ASSERT_MES(!(loaded_size%sizeof(typename stl_container::value_type)), + false, + "size in blob " << loaded_size << " not have not zero modulo for sizeof(value_type) = " << sizeof(typename stl_container::value_type)); + size_t count = (loaded_size/sizeof(typename stl_container::value_type)); + for(size_t i = 0; i < count; i++) + container.push_back(*(pelem++)); + } + return res; + } + //-------------------------------------------------------------------------------------------------------------------- + template + static bool serialize_stl_container_t_obj (const stl_container& container, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + bool res = false; + if(!container.size()) return true; + typename stl_container::const_iterator it = container.begin(); + typename t_storage::hsection hchild_section = nullptr; + typename t_storage::harray hsec_array = stg.insert_first_section(pname, hchild_section, hparent_section); + CHECK_AND_ASSERT_MES(hsec_array && hchild_section, false, "failed to insert first section with section name " << pname); + res = it->store(stg, hchild_section); + it++; + for(;it!= container.end();it++) + { + stg.insert_next_section(hsec_array, hchild_section); + res |= it->store(stg, hchild_section); + } + return res; + } + //-------------------------------------------------------------------------------------------------------------------- + template + static bool unserialize_stl_container_t_obj(stl_container& container, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + bool res = false; + container.clear(); + typename stl_container::value_type val = typename stl_container::value_type(); + typename t_storage::hsection hchild_section = nullptr; + typename t_storage::harray hsec_array = stg.get_first_section(pname, hchild_section, hparent_section); + if(!hsec_array || !hchild_section) return false; + res = val._load(stg, hchild_section); + container.push_back(val); + while(stg.get_next_section(hsec_array, hchild_section)) + { + typename stl_container::value_type val_l = typename stl_container::value_type(); + res |= val_l._load(stg, hchild_section); + container.push_back(std::move(val_l)); + } + return res; + } + //-------------------------------------------------------------------------------------------------------------------- + template + struct kv_serialization_overloads_impl_is_base_serializable_types; + + template<> + struct kv_serialization_overloads_impl_is_base_serializable_types + { + template + static bool kv_serialize(const t_type& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + return stg.set_value(pname, d, hparent_section); + } + //------------------------------------------------------------------------------------------------------------------- + template + static bool kv_unserialize(t_type& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + return stg.get_value(pname, d, hparent_section); + } + //------------------------------------------------------------------------------------------------------------------- + template + static bool kv_serialize(const std::vector& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + return serialize_stl_container_t_val(d, stg, hparent_section, pname); + } + //------------------------------------------------------------------------------------------------------------------- + template + static bool kv_unserialize(std::vector& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + return unserialize_stl_container_t_val(d, stg, hparent_section, pname); + } + //------------------------------------------------------------------------------------------------------------------- + template + static bool kv_serialize(const std::list& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + return serialize_stl_container_t_val(d, stg, hparent_section, pname); + } + //------------------------------------------------------------------------------------------------------------------- + template + static bool kv_unserialize(std::list& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + return unserialize_stl_container_t_val(d, stg, hparent_section, pname); + } + //------------------------------------------------------------------------------------------------------------------- + }; + template<> + struct kv_serialization_overloads_impl_is_base_serializable_types + { + template + static bool kv_serialize(const t_type& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + return serialize_t_obj(d, stg, hparent_section, pname); + } + //------------------------------------------------------------------------------------------------------------------- + template + static bool kv_unserialize(t_type& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + return unserialize_t_obj(d, stg, hparent_section, pname); + } + + //------------------------------------------------------------------------------------------------------------------- + template + static bool kv_serialize(const std::vector& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + return serialize_stl_container_t_obj(d, stg, hparent_section, pname); + } + //------------------------------------------------------------------------------------------------------------------- + template + static bool kv_unserialize(std::vector& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + return unserialize_stl_container_t_obj(d, stg, hparent_section, pname); + } + //------------------------------------------------------------------------------------------------------------------- + template + static bool kv_serialize(const std::list& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + return serialize_stl_container_t_obj(d, stg, hparent_section, pname); + } + //------------------------------------------------------------------------------------------------------------------- + template + static bool kv_unserialize(std::list& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + return unserialize_stl_container_t_obj(d, stg, hparent_section, pname); + } + }; + typedef boost::mpl::vector::type base_serializable_types; + //------------------------------------------------------------------------------------------------------------------- + template struct selector; + template<> + struct selector + { + template + static bool serialize(const t_type& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + return kv_serialize(d, stg, hparent_section, pname); + } + + template + static bool serialize_stl_container_pod_val_as_blob(const t_type& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + return epee::serialization::serialize_stl_container_pod_val_as_blob(d, stg, hparent_section, pname); + } + + template + static bool serialize_t_val_as_blob(const t_type& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + return epee::serialization::serialize_t_val_as_blob(d, stg, hparent_section, pname); + } + + + }; + template<> + struct selector + { + template + static bool serialize(t_type& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + return kv_unserialize(d, stg, hparent_section, pname); + } + template + static bool serialize_stl_container_pod_val_as_blob(t_type& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + return epee::serialization::unserialize_stl_container_pod_val_as_blob(d, stg, hparent_section, pname); + } + + template + static bool serialize_t_val_as_blob(t_type& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + return epee::serialization::unserialize_t_val_as_blob(d, stg, hparent_section, pname); + } + }; + + template + bool kv_serialize(const t_type& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + return kv_serialization_overloads_impl_is_base_serializable_types::type>::value>::kv_serialize(d, stg, hparent_section, pname); + } + //------------------------------------------------------------------------------------------------------------------- + template + bool kv_unserialize(t_type& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + return kv_serialization_overloads_impl_is_base_serializable_types::type>::value>::kv_unserialize(d, stg, hparent_section, pname); + } + //------------------------------------------------------------------------------------------------------------------- + template + bool kv_serialize(const std::vector& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + return kv_serialization_overloads_impl_is_base_serializable_types::type>::value>::kv_serialize(d, stg, hparent_section, pname); + } + //------------------------------------------------------------------------------------------------------------------- + template + bool kv_unserialize(std::vector& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + return kv_serialization_overloads_impl_is_base_serializable_types::type>::value>::kv_unserialize(d, stg, hparent_section, pname); + } + //------------------------------------------------------------------------------------------------------------------- + template + bool kv_serialize(const std::list& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + return kv_serialization_overloads_impl_is_base_serializable_types::type>::value>::kv_serialize(d, stg, hparent_section, pname); + } + //------------------------------------------------------------------------------------------------------------------- + template + bool kv_unserialize(std::list& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + return kv_serialization_overloads_impl_is_base_serializable_types::type>::value>::kv_unserialize(d, stg, hparent_section, pname); + } + } +} \ No newline at end of file diff --git a/contrib/epee/include/serialization/serialize_base.h b/contrib/epee/include/serialization/serialize_base.h new file mode 100644 index 000000000..84a1624cb --- /dev/null +++ b/contrib/epee/include/serialization/serialize_base.h @@ -0,0 +1,2 @@ +#pragma once + diff --git a/contrib/epee/include/service_impl_base.h b/contrib/epee/include/service_impl_base.h new file mode 100644 index 000000000..6e9aefc46 --- /dev/null +++ b/contrib/epee/include/service_impl_base.h @@ -0,0 +1,323 @@ +// 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. +// + + +#ifndef _SERVICE_IMPL_BASE_H_ +#define _SERVICE_IMPL_BASE_H_ + +#pragma comment(lib, "advapi32.lib") + + +namespace epee +{ +class service_impl_base { + public: + service_impl_base(); + virtual ~service_impl_base(); + + virtual const char *get_name() = 0; + virtual const char *get_caption() = 0; + virtual const char *get_description() = 0; + + bool run_service(); + virtual bool install(); + virtual bool remove(); + virtual bool init(); + void set_control_accepted(unsigned controls); + void set_status(unsigned state, unsigned pending = 0); + unsigned get_control_accepted(); + + private: + virtual void service_main() = 0; + virtual unsigned service_handler(unsigned control, unsigned event_code, + void *pdata) = 0; + //------------------------------------------------------------------------- + static service_impl_base*& instance(); + //------------------------------------------------------------------------- + static DWORD __stdcall _service_handler(DWORD control, DWORD event, + void *pdata, void *pcontext); + static void __stdcall service_entry(DWORD argc, char **pargs); + virtual SERVICE_FAILURE_ACTIONSA* get_failure_actions(); + + private: + SC_HANDLE m_manager; + SC_HANDLE m_service; + SERVICE_STATUS_HANDLE m_status_handle; + DWORD m_accepted_control; +}; + +inline service_impl_base::service_impl_base() { + m_manager = 0; + m_service = 0; + m_status_handle = 0; + m_accepted_control = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN + | SERVICE_ACCEPT_PAUSE_CONTINUE; + + instance() = this; +} +//----------------------------------------------------------------------------- +inline service_impl_base::~service_impl_base() { + if (m_service) { + ::CloseServiceHandle(m_service); + } + m_service = 0; + if (m_manager) { + ::CloseServiceHandle(m_manager); + } + m_manager = 0; + instance() = 0; +} +//----------------------------------------------------------------------------- +inline service_impl_base*& service_impl_base::instance() { + static service_impl_base *pservice = NULL; + return pservice; +} +//----------------------------------------------------------------------------- +inline +bool service_impl_base::install() { + CHECK_AND_ASSERT(!m_service, false); + const char *psz_descr = get_description(); + SERVICE_FAILURE_ACTIONSA* fail_acts = get_failure_actions(); + + char sz_path[MAX_PATH]; + ::GetModuleFileNameA(0, sz_path, sizeof(sz_path)); + ::GetShortPathNameA(sz_path, sz_path, sizeof(sz_path)); + + while (TRUE) { + if (!m_manager) { + m_manager = ::OpenSCManager(NULL, NULL, GENERIC_ALL); + if (!m_manager) { + int err = GetLastError(); + LOG_ERROR( + "Failed to OpenSCManager(), last err=" + << log_space::get_win32_err_descr(err)); + break; + } + } + m_service = ::CreateServiceA(m_manager, get_name(), get_caption(), + SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START, + SERVICE_ERROR_IGNORE, sz_path, 0, 0, 0, 0, 0); + if (!m_service) { + int err = GetLastError(); + LOG_ERROR( + "Failed to CreateService(), last err=" + << log_space::get_win32_err_descr(err)); + break; + } + + if (psz_descr) { + SERVICE_DESCRIPTIONA sd = { (char*) psz_descr }; + if (!::ChangeServiceConfig2A(m_service, SERVICE_CONFIG_DESCRIPTION, + &sd)) { + int err = GetLastError(); + LOG_ERROR( + "Failed to ChangeServiceConfig2(SERVICE_CONFIG_DESCRIPTION), last err=" + << log_space::get_win32_err_descr(err)); + break; + } + } + + if (fail_acts) { + if (!::ChangeServiceConfig2A(m_service, SERVICE_CONFIG_FAILURE_ACTIONS, + fail_acts)) { + int err = GetLastError(); + LOG_ERROR( + "Failed to ChangeServiceConfig2(SERVICE_CONFIG_FAILURE_ACTIONS), last err=" + << log_space::get_win32_err_descr(err)); + break; + } + } + LOG_PRINT("Installed succesfully.", LOG_LEVEL_0); + return true; + } + LOG_PRINT("Failed to install.", LOG_LEVEL_0); + return false; +} +//----------------------------------------------------------------------------- +inline +bool service_impl_base::remove() { + CHECK_AND_ASSERT(!m_service, false); + + while (TRUE) { + if (!m_manager) { + m_manager = ::OpenSCManager(0, 0, GENERIC_ALL); + if (!m_manager) { + int err = GetLastError(); + LOG_ERROR( + "Failed to OpenSCManager(), last err=" + << log_space::get_win32_err_descr(err)); + break; + } + } + + if (!m_service) { + m_service = ::OpenServiceA(m_manager, get_name(), SERVICE_STOP | DELETE); + if (!m_service) { + int err = GetLastError(); + LOG_ERROR( + "Failed to OpenService(), last err=" + << log_space::get_win32_err_descr(err)); + break; + } + } + + SERVICE_STATUS status = { }; + if (!::ControlService(m_service, SERVICE_CONTROL_STOP, &status)) { + int err = ::GetLastError(); + if (err == ERROR_SHUTDOWN_IN_PROGRESS) + continue; + else if (err != ERROR_SERVICE_NOT_ACTIVE) { + LOG_ERROR( + "Failed to ControlService(SERVICE_CONTROL_STOP), last err=" + << log_space::get_win32_err_descr(err)); + break; + } + } + + if (!::DeleteService(m_service)) { + int err = ::GetLastError(); + LOG_ERROR( + "Failed to ControlService(SERVICE_CONTROL_STOP), last err=" + << log_space::get_win32_err_descr(err)); + break; + } + + LOG_PRINT("Removed successfully.", LOG_LEVEL_0); + break; + } + + return true; +} +//----------------------------------------------------------------------------- +inline +bool service_impl_base::init() { + return true; +} +//----------------------------------------------------------------------------- +inline +bool service_impl_base::run_service() { + CHECK_AND_ASSERT(!m_service, false); + + long error_code = 0; + + SERVICE_TABLE_ENTRYA service_table[2]; + ZeroMemory(&service_table, sizeof(service_table)); + + service_table->lpServiceName = (char*) get_name(); + service_table->lpServiceProc = service_entry; + + LOG_PRINT("[+] Start service control dispatcher for \"" << get_name() << "\"", + LOG_LEVEL_1); + + error_code = 1; + BOOL res = ::StartServiceCtrlDispatcherA(service_table); + if (!res) { + int err = GetLastError(); + LOG_PRINT( + "[+] Error starting service control dispatcher, err=" + << log_space::get_win32_err_descr(err), LOG_LEVEL_1); + return false; + } else { + LOG_PRINT("[+] End service control dispatcher for \"" << get_name() << "\"", + LOG_LEVEL_1); + } + return true; +} +//----------------------------------------------------------------------------- +inline DWORD __stdcall service_impl_base::_service_handler(DWORD control, + DWORD event, void *pdata, void *pcontext) { + CHECK_AND_ASSERT(pcontext, ERROR_CALL_NOT_IMPLEMENTED); + + service_impl_base *pservice = (service_impl_base*) pcontext; + return pservice->service_handler(control, event, pdata); +} +//----------------------------------------------------------------------------- +inline +void __stdcall service_impl_base::service_entry(DWORD argc, char **pargs) { + service_impl_base *pme = instance(); + LOG_PRINT("instance: " << pme, LOG_LEVEL_4); + if (!pme) { + LOG_ERROR("Error: at service_entry() pme = NULL"); + return; + } + pme->m_status_handle = ::RegisterServiceCtrlHandlerExA(pme->get_name(), + _service_handler, pme); + + pme->set_status(SERVICE_RUNNING); + pme->service_main(); + pme->set_status(SERVICE_STOPPED); +} +//----------------------------------------------------------------------------- +inline +void service_impl_base::set_status(unsigned state, unsigned pending) { + if (!m_status_handle) + return; + + SERVICE_STATUS status = { 0 }; + status.dwServiceType = SERVICE_WIN32_OWN_PROCESS; + status.dwCurrentState = state; + status.dwControlsAccepted = m_accepted_control; + /*status.dwWin32ExitCode = NO_ERROR; + status.dwServiceSpecificExitCode = ERROR_SERVICE_SPECIFIC_ERROR; + status.dwCheckPoint = 0; + status.dwWaitHint = 0; + + status.dwCurrentState = state;*/ + + if (state == SERVICE_START_PENDING || state == SERVICE_STOP_PENDING + || state == SERVICE_CONTINUE_PENDING || state == SERVICE_PAUSE_PENDING) { + status.dwWaitHint = 2000; + status.dwCheckPoint = pending; + } + ::SetServiceStatus(m_status_handle, &status); +} +//----------------------------------------------------------------------------------------- +inline +void service_impl_base::set_control_accepted(unsigned controls) { + m_accepted_control = controls; +} +//----------------------------------------------------------------------------------------- +inline +unsigned service_impl_base::get_control_accepted() { + return m_accepted_control; +} +//----------------------------------------------------------------------------------------- +inline SERVICE_FAILURE_ACTIONSA* service_impl_base::get_failure_actions() { + // first 3 failures in 30 minutes. Service will be restarted. + // do nothing for next failures + static SC_ACTION sa[] = { { SC_ACTION_RESTART, 3 * 1000 }, { + SC_ACTION_RESTART, 3 * 1000 }, { SC_ACTION_RESTART, 3 * 1000 }, { + SC_ACTION_NONE, 0 } }; + + static SERVICE_FAILURE_ACTIONSA sfa = { 1800, // interval for failures counter - 30 min + "", NULL, 4, (SC_ACTION*) &sa }; + + // TODO: refactor this code, really unsafe! + return &sfa; +} +} + +#endif //_SERVICE_IMPL_BASE_H_ diff --git a/contrib/epee/include/sha1.h b/contrib/epee/include/sha1.h new file mode 100644 index 000000000..ce42082f8 --- /dev/null +++ b/contrib/epee/include/sha1.h @@ -0,0 +1,51 @@ + +/* + Copyright (c) 2011, Micael Hildenborg + 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 Micael Hildenborg 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 Micael Hildenborg ''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 Micael Hildenborg 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. + */ + +#ifndef SHA1_DEFINED +#define SHA1_DEFINED + +namespace sha1 { + + /** + @param src points to any kind of data to be hashed. + @param bytelength the number of bytes to hash from the src pointer. + @param hash should point to a buffer of at least 20 bytes of size for storing the sha1 result in. + */ + void calc(const void* src, const int bytelength, unsigned char* hash); + + /** + @param hash is 20 bytes of sha1 hash. This is the same data that is the result from the calc function. + @param hexstring should point to a buffer of at least 41 bytes of size for storing the hexadecimal representation of the hash. A zero will be written at position 40, so the buffer will be a valid zero ended string. + */ + void toHexString(const unsigned char* hash, char* hexstring); + +} // namespace sha1 + +#include "sha1.inl" + +#endif // SHA1_DEFINED diff --git a/contrib/epee/include/sha1.inl b/contrib/epee/include/sha1.inl new file mode 100644 index 000000000..d33202724 --- /dev/null +++ b/contrib/epee/include/sha1.inl @@ -0,0 +1,179 @@ + +/* + Copyright (c) 2011, Micael Hildenborg + 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 Micael Hildenborg 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 Micael Hildenborg ''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 Micael Hildenborg 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. + */ + +/* + Contributors: + Gustav + Several members in the gamedev.se forum. + Gregory Petrosyan + */ + +#include "sha1.h" + +namespace sha1 { +namespace {// local +// Rotate an integer value to left. +inline const unsigned int rol(const unsigned int value, + const unsigned int steps) { + return ((value << steps) | (value >> (32 - steps))); +} + +// Sets the first 16 integers in the buffert to zero. +// Used for clearing the W buffert. +inline void clearWBuffert(unsigned int* buffert) { + for (int pos = 16; --pos >= 0;) + { + buffert[pos] = 0; + } +} + +inline +void innerHash(unsigned int* result, unsigned int* w) { + unsigned int a = result[0]; + unsigned int b = result[1]; + unsigned int c = result[2]; + unsigned int d = result[3]; + unsigned int e = result[4]; + + int round = 0; + +#define sha1macro(func,val) \ + { \ + const unsigned int t = rol(a, 5) + (func) + e + val + w[round]; \ + e = d; \ + d = c; \ + c = rol(b, 30); \ + b = a; \ + a = t; \ + } + + while (round < 16) { + sha1macro((b & c) | (~b & d), 0x5a827999) + ++round; + } + while (round < 20) { + w[round] = rol( + (w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1); + sha1macro((b & c) | (~b & d), 0x5a827999) + ++round; + } + while (round < 40) { + w[round] = rol( + (w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1); + sha1macro(b ^ c ^ d, 0x6ed9eba1) + ++round; + } + while (round < 60) { + w[round] = rol( + (w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1); + sha1macro((b & c) | (b & d) | (c & d), 0x8f1bbcdc) + ++round; + } + while (round < 80) { + w[round] = rol( + (w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1); + sha1macro(b ^ c ^ d, 0xca62c1d6) + ++round; + } + +#undef sha1macro + + result[0] += a; + result[1] += b; + result[2] += c; + result[3] += d; + result[4] += e; +} +} // namespace + +inline +void calc(const void* src, const int bytelength, unsigned char* hash) { + // Init the result array. + unsigned int result[5] = { 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, + 0xc3d2e1f0 }; + + // Cast the void src pointer to be the byte array we can work with. + const unsigned char* sarray = (const unsigned char*) src; + + // The reusable round buffer + unsigned int w[80]; + + // Loop through all complete 64byte blocks. + const int endOfFullBlocks = bytelength - 64; + int endCurrentBlock; + int currentBlock(0); + + while (currentBlock <= endOfFullBlocks) { + endCurrentBlock = currentBlock + 64; + + // Init the round buffer with the 64 byte block data. + for (int roundPos = 0; currentBlock < endCurrentBlock; currentBlock += 4) + { + // This line will swap endian on big endian and keep endian on little endian. + w[roundPos++] = (unsigned int) sarray[currentBlock + 3] + | (((unsigned int) sarray[currentBlock + 2]) << 8) + | (((unsigned int) sarray[currentBlock + 1]) << 16) + | (((unsigned int) sarray[currentBlock]) << 24); + } + innerHash(result, w); + } + + // Handle the last and not full 64 byte block if existing. + endCurrentBlock = bytelength - currentBlock; + clearWBuffert(w); + int lastBlockBytes = 0; + for (; lastBlockBytes < endCurrentBlock; ++lastBlockBytes) { + w[lastBlockBytes >> 2] |= (unsigned int) sarray[lastBlockBytes + + currentBlock] << ((3 - (lastBlockBytes & 3)) << 3); + } + w[lastBlockBytes >> 2] |= 0x80 << ((3 - (lastBlockBytes & 3)) << 3); + if (endCurrentBlock >= 56) { + innerHash(result, w); + clearWBuffert(w); + } + w[15] = bytelength << 3; + innerHash(result, w); + + // Store hash in result pointer, and make sure we get in in the correct order on both endian models. + for (int hashByte = 20; --hashByte >= 0;) { + hash[hashByte] = (result[hashByte >> 2] >> (((3 - hashByte) & 0x3) << 3)) + & 0xff; + } +} +inline +void toHexString(const unsigned char* hash, char* hexstring) { + const char hexDigits[] = { "0123456789abcdef" }; + + for (int hashByte = 20; --hashByte >= 0;) + { + hexstring[hashByte << 1] = hexDigits[(hash[hashByte] >> 4) & 0xf]; + hexstring[(hashByte << 1) + 1] = hexDigits[hash[hashByte] & 0xf]; + } + hexstring[40] = 0; +} +} // namespace sha1 diff --git a/contrib/epee/include/soci_helper.h b/contrib/epee/include/soci_helper.h new file mode 100644 index 000000000..a154f97fc --- /dev/null +++ b/contrib/epee/include/soci_helper.h @@ -0,0 +1,142 @@ +// 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. +// + + +#pragma once +#include "soci.h" +#include "soci-postgresql.h" + +using namespace epee; +namespace soci +{ + + template <> + struct type_conversion + { + typedef long long base_type; + + static void from_base(base_type a_, indicator ind, boost::uint64_t & mi) + { + if (ind == i_null) + { + mi = 0; + //throw soci_error("Null value not allowed for this type"); + } + mi = (boost::uint64_t)a_; + //mi.set(i); + } + + static void to_base(const boost::uint64_t & mi, base_type & i, indicator & ind) + { + i = (base_type)mi; + ind = i_ok; + } + }; + + + + template <> + struct type_conversion + { + typedef int base_type; + + static void from_base(base_type a_, indicator ind, bool& mi) + { + if (ind == i_null) + { + mi = false; + //throw soci_error("Null value not allowed for this type"); + } + mi = a_? true:false; + //mi.set(i); + } + + static void to_base(const bool & mi, base_type & i, indicator & ind) + { + i = mi? 1:0; + ind = i_ok; + } + }; + + + + class per_thread_session + { + public: + bool init(const std::string& connection_string) + { + m_connection_string = connection_string; + + return true; + } + + soci::session& get() + { + + //soci::session + + m_db_connections_lock.lock(); + boost::shared_ptr& conn_ptr = m_db_connections[epee::misc_utils::get_thread_string_id()]; + m_db_connections_lock.unlock(); + if(!conn_ptr.get()) + { + conn_ptr.reset(new soci::session(soci::postgresql, m_connection_string)); + } + //init new connection + return *conn_ptr.get(); + } + + bool reopen() + { + //soci::session + + m_db_connections_lock.lock(); + boost::shared_ptr& conn_ptr = m_db_connections[misc_utils::get_thread_string_id()]; + m_db_connections_lock.unlock(); + if(conn_ptr.get()) + { + conn_ptr->close(); + conn_ptr.reset(new soci::session(soci::postgresql, m_connection_string)); + } + + //init new connection + return true; + } + + //---------------------------------------------------------------------------------------------- + bool check_status() + { + return true; + } + + protected: + private: + std::map > m_db_connections; + epee::critical_section m_db_connections_lock; + std::string m_connection_string; + }; +} +/*}*/ \ No newline at end of file diff --git a/contrib/epee/include/static_initializer.h b/contrib/epee/include/static_initializer.h new file mode 100644 index 000000000..3463a5607 --- /dev/null +++ b/contrib/epee/include/static_initializer.h @@ -0,0 +1,82 @@ +// 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. +// + + + +#ifndef _STATIC_INITIALIZER_H_ +#define _STATIC_INITIALIZER_H_ + + +namespace epee +{ +/*********************************************************************** +class initializer - useful to initialize some static classes + which have init() and un_init() static members +************************************************************************/ + +template +class initializer +{ +public: + initializer() + { + to_initialize::init(); + //get_set_is_initialized(true, true); + } + ~initializer() + { + to_initialize::un_init(); + //get_set_is_uninitialized(true, true); + } + + /*static inline bool is_initialized() + { + return get_set_is_initialized(); + } + static inline bool is_uninitialized() + { + return get_set_is_uninitialized(); + } + +private: + static inline bool get_set_is_initialized(bool need_to_set = false, bool val_to_set= false) + { + static bool val_is_initialized = false; + if(need_to_set) + val_is_initialized = val_to_set; + return val_is_initialized; + } + static inline bool get_set_is_uninitialized(bool need_to_set = false, bool val_to_set = false) + { + static bool val_is_uninitialized = false; + if(need_to_set) + val_is_uninitialized = val_to_set; + return val_is_uninitialized; + }*/ +}; + +} +#endif //_STATIC_INITIALIZER_H_ diff --git a/contrib/epee/include/storages/activity_notifier.h b/contrib/epee/include/storages/activity_notifier.h new file mode 100644 index 000000000..14b6ebbfb --- /dev/null +++ b/contrib/epee/include/storages/activity_notifier.h @@ -0,0 +1,132 @@ +// 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. +// + + + +#pragma once + +#include "inmemtoxml.h" + +//#include "levin/levin_server.h" + +namespace epee +{ + +class activity_printer_base +{ +public: + activity_printer_base(){} + virtual ~activity_printer_base(){} +}; + +template +class notify_activity_printer: public activity_printer_base +{ +public: + notify_activity_printer(int level, A& arg, bool is_notify_mode = true):m_ref_arg(arg), m_level(level), m_is_notify_mode(is_notify_mode) + { + m_command_name = typeid(m_ref_arg).name(); + m_command_name.erase(0, 7); + m_command_name.erase(m_command_name.size()-10, m_command_name.size()-1); + if(level == log_space::get_set_log_detalisation_level()) + { + LOG_PRINT(m_command_name, level); + } + else if(level+1 == log_space::get_set_log_detalisation_level()) + { + LOG_PRINT(" -->>" << m_command_name, level); + } + else if(level+2 == log_space::get_set_log_detalisation_level()) + { + LOG_PRINT(" -->>" << m_command_name << "\n" << StorageNamed::xml::get_t_as_xml(m_ref_arg), level); + } + } + + virtual ~notify_activity_printer() + { + if(m_is_notify_mode) + { + if(m_level+1 == log_space::get_set_log_detalisation_level()) + { + LOG_PRINT(" <<--" << m_command_name, m_level); + } + } + } +protected: + std::string m_command_name; + A& m_ref_arg; + int m_level; + bool m_is_notify_mode; +}; + +template +class command_activity_printer: public notify_activity_printer +{ +public: + command_activity_printer(int level, A& arg, R& rsp):notify_activity_printer(level, arg, false), m_ref_rsp(rsp) + { + } + + virtual ~command_activity_printer() + { + if(m_level+1 == log_space::get_set_log_detalisation_level()) + { + LOG_PRINT(" <<--" << m_command_name, m_level); + } + else if(m_level+2 == log_space::get_set_log_detalisation_level()) + { + LOG_PRINT(" <<--" << m_command_name << "\n" << StorageNamed::trace_as_xml(m_ref_rsp), m_level); + } + } +private: + R& m_ref_rsp; +}; + +template +activity_printer_base* create_activity_printer(int level, A& arg, R& rsp) +{ + return new command_activity_printer(level, arg, rsp); +} + +template +activity_printer_base* create_activity_printer(int level, A& arg) +{ + return new notify_activity_printer(level, arg); +} + +} + +#define PRINT_COMMAND_ACTIVITY(level) boost::shared_ptr local_activity_printer(create_activity_printer(level, in_struct, out_struct)); +#define PRINT_NOTIFY_ACTIVITY(level) boost::shared_ptr local_activity_printer(create_activity_printer(level, in_struct)); + +#define PRINT_ACTIVITY(level) \ +{std::string some_str = typeid(in_struct).name(); \ + some_str.erase(0, 7); \ + some_str.erase(some_str.size()-10, some_str.size()-1); \ + LOG_PRINT(some_str, level);} + +} + diff --git a/contrib/epee/include/storages/crypted_storage.h b/contrib/epee/include/storages/crypted_storage.h new file mode 100644 index 000000000..d6e6edcba --- /dev/null +++ b/contrib/epee/include/storages/crypted_storage.h @@ -0,0 +1,62 @@ +// 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. +// + + +#ifndef _CRYPTED_STORAGE_H_ +#define _CRYPTED_STORAGE_H_ + +#include "cryptopp_helper.h" + +namespace epee +{ +template +class crypted_storage: public t_base_storage +{ +public: + size_t PackToSolidBuffer(std::string& targetObj) + { + size_t res = t_base_storage::PackToSolidBuffer(targetObj); + if(res <= 0) + return res; + + if(!crypt_provider::encrypt(targetObj, t_key_provider::get_storage_default_key())) + return 0; + + return targetObj.size(); + } + + size_t LoadFromSolidBuffer(const std::string& pTargetObj) + { + std::string buff_to_decrypt = pTargetObj; + if(crypt_provider::decrypt(buff_to_decrypt, t_key_provider::get_storage_default_key())) + return t_base_storage::LoadFromSolidBuffer(buff_to_decrypt); + + return 0; + } +}; +} + +#endif //_CRYPTED_STORAGE_H_ \ No newline at end of file diff --git a/contrib/epee/include/storages/gzipped_inmemstorage.h b/contrib/epee/include/storages/gzipped_inmemstorage.h new file mode 100644 index 000000000..5c53fffa7 --- /dev/null +++ b/contrib/epee/include/storages/gzipped_inmemstorage.h @@ -0,0 +1,68 @@ +// 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. +// + + +#ifndef _GZIPPED_INMEMSTORAGE_H_ +#define _GZIPPED_INMEMSTORAGE_H_ + +#include "zlib_helper.h" +namespace epee +{ +namespace StorageNamed +{ + + template + class gziped_storage: public t_base_storage + { + public: + size_t PackToSolidBuffer(std::string& targetObj) + { + size_t res = t_base_storage::PackToSolidBuffer(targetObj); + if(res <= 0) + return res; + + if(!zlib_helper::pack(targetObj)) + return 0; + + return targetObj.size(); + } + + size_t LoadFromSolidBuffer(const std::string& pTargetObj) + { + std::string buff_to_ungzip = pTargetObj; + if(zlib_helper::unpack(buff_to_ungzip)) + return t_base_storage::LoadFromSolidBuffer(buff_to_ungzip); + + return 0; + } + + private: + }; + +} +} + +#endif \ No newline at end of file diff --git a/contrib/epee/include/storages/http_abstract_invoke.h b/contrib/epee/include/storages/http_abstract_invoke.h new file mode 100644 index 000000000..07720f496 --- /dev/null +++ b/contrib/epee/include/storages/http_abstract_invoke.h @@ -0,0 +1,126 @@ + +// 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. +// + +#pragma once +#include "portable_storage_template_helper.h" +#include "net/http_base.h" +#include "net/http_server_handlers_map2.h" + +namespace epee +{ + namespace net_utils + { + template + bool invoke_http_json_remote_command2(const std::string& url, t_request& out_struct, t_response& result_struct, t_transport& transport, unsigned int timeout = 5000, const std::string& method = "GET") + { + std::string req_param; + if(!serialization::store_t_to_json(out_struct, req_param)) + return false; + + const http::http_response_info* pri = NULL; + if(!invoke_request(url, transport, timeout, &pri, method, req_param)) + { + LOG_PRINT_L1("Failed to invoke http request to " << url); + return false; + } + + if(!pri->m_response_code) + { + LOG_PRINT_L1("Failed to invoke http request to " << url << ", internal error (null response ptr)"); + return false; + } + + if(pri->m_response_code != 200) + { + LOG_PRINT_L1("Failed to invoke http request to " << url << ", wrong response code: " << pri->m_response_code); + return false; + } + + return serialization::load_t_from_json(result_struct, pri->m_body); + } + + + + template + bool invoke_http_bin_remote_command2(const std::string& url, t_request& out_struct, t_response& result_struct, t_transport& transport, unsigned int timeout = 5000, const std::string& method = "GET") + { + std::string req_param; + if(!serialization::store_t_to_binary(out_struct, req_param)) + return false; + + const http::http_response_info* pri = NULL; + if(!invoke_request(url, transport, timeout, &pri, method, req_param)) + { + LOG_PRINT_L1("Failed to invoke http request to " << url); + return false; + } + + if(!pri->m_response_code) + { + LOG_PRINT_L1("Failed to invoke http request to " << url << ", internal error (null response ptr)"); + return false; + } + + if(pri->m_response_code != 200) + { + LOG_PRINT_L1("Failed to invoke http request to " << url << ", wrong response code: " << pri->m_response_code); + return false; + } + + return serialization::load_t_from_binary(result_struct, pri->m_body); + } + + template + bool invoke_http_json_rpc(const std::string& url, const std::string& method_name, t_request& out_struct, t_response& result_struct, t_transport& transport, unsigned int timeout = 5000, const std::string& http_method = "GET", const std::string& req_id = "0") + { + epee::json_rpc::request req_t = AUTO_VAL_INIT(req_t); + req_t.params = out_struct; + req_t.id = req_id; + req_t.method = method_name; + req_t.version = "2.0"; + epee::json_rpc::response resp_t = AUTO_VAL_INIT(resp_t); + if(!epee::net_utils::invoke_http_json_remote_command2(url, req_t, resp_t, transport, timeout, http_method)) + { + return false; + } + if(resp_t.error.code || resp_t.error.message.size()) + { + LOG_ERROR("RPC call of \"" << method_name << "\" returned error: " << resp_t.error.code << ", message: " << resp_t.error.message); + return false; + } + result_struct = resp_t.result; + return true; + } + + template + bool invoke_http_json_rpc(const std::string& url, typename t_command::request& out_struct, typename t_command::response& result_struct, t_transport& transport, unsigned int timeout = 5000, const std::string& http_method = "GET", const std::string& req_id = "0") + { + return invoke_http_json_rpc(url, t_command::methodname(), out_struct, result_struct, transport, timeout, http_method, req_id); + } + + } +} diff --git a/contrib/epee/include/storages/levin_abstract_invoke2.h b/contrib/epee/include/storages/levin_abstract_invoke2.h new file mode 100644 index 000000000..1b32c51d1 --- /dev/null +++ b/contrib/epee/include/storages/levin_abstract_invoke2.h @@ -0,0 +1,289 @@ +// 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. +// + +#pragma once + +#include "portable_storage_template_helper.h" +#include +#include "net/levin_base.h" + +namespace epee +{ + namespace net_utils + { + template + bool invoke_remote_command2(int command, const t_arg& out_struct, t_result& result_struct, t_transport& transport) + { + if(!transport.is_connected()) + return false; + + serialization::portable_storage stg; + out_struct.store(stg); + std::string buff_to_send, buff_to_recv; + stg.store_to_binary(buff_to_send); + + int res = transport.invoke(command, buff_to_send, buff_to_recv); + if( res <=0 ) + { + LOG_PRINT_RED("Failed to invoke command " << command << " return code " << res, LOG_LEVEL_1); + return false; + } + serialization::portable_storage stg_ret; + if(!stg_ret.load_from_binary(buff_to_recv)) + { + LOG_ERROR("Failed to load_from_binary on command " << command); + return false; + } + result_struct.load(stg_ret); + return true; + } + + template + bool notify_remote_command2(int command, const t_arg& out_struct, t_transport& transport) + { + if(!transport.is_connected()) + return false; + + serialization::portable_storage stg; + out_struct.store(&stg); + std::string buff_to_send; + stg.store_to_binary(buff_to_send); + + int res = transport.notify(command, buff_to_send); + if(res <=0 ) + { + LOG_ERROR("Failed to notify command " << command << " return code " << res); + return false; + } + return true; + } + + template + bool invoke_remote_command2(boost::uuids::uuid conn_id, int command, const t_arg& out_struct, t_result& result_struct, t_transport& transport) + { + + typename serialization::portable_storage stg; + out_struct.store(stg); + std::string buff_to_send, buff_to_recv; + stg.store_to_binary(buff_to_send); + + int res = transport.invoke(command, buff_to_send, buff_to_recv, conn_id); + if( res <=0 ) + { + LOG_PRINT_L1("Failed to invoke command " << command << " return code " << res); + return false; + } + typename serialization::portable_storage stg_ret; + if(!stg_ret.load_from_binary(buff_to_recv)) + { + LOG_ERROR("Failed to load_from_binary on command " << command); + return false; + } + result_struct.load(stg_ret); + + return true; + } + + template + bool async_invoke_remote_command2(boost::uuids::uuid conn_id, int command, const t_arg& out_struct, t_transport& transport, callback_t cb, size_t inv_timeout = LEVIN_DEFAULT_TIMEOUT_PRECONFIGURED) + { + typename serialization::portable_storage stg; + const_cast(out_struct).store(stg);//TODO: add true const support to searilzation + std::string buff_to_send, buff_to_recv; + stg.store_to_binary(buff_to_send); + int res = transport.invoke_async(command, buff_to_send, conn_id, [cb, command](int code, const std::string& buff, typename t_transport::connection_context& context)->bool + { + t_result result_struct = AUTO_VAL_INIT(result_struct); + if( code <=0 ) + { + LOG_PRINT_L1("Failed to invoke command " << command << " return code " << code); + cb(code, result_struct, context); + return false; + } + serialization::portable_storage stg_ret; + if(!stg_ret.load_from_binary(buff)) + { + LOG_ERROR("Failed to load_from_binary on command " << command); + cb(LEVIN_ERROR_FORMAT, result_struct, context); + return false; + } + result_struct.load(stg_ret); + cb(code, result_struct, context); + return true; + }, inv_timeout); + if( res <=0 ) + { + LOG_PRINT_L1("Failed to invoke command " << command << " return code " << res); + return false; + } + return true; + } + + template + bool notify_remote_command2(boost::uuids::uuid conn_id, int command, const t_arg& out_struct, t_transport& transport) + { + + serialization::portable_storage stg; + out_struct.store(stg); + std::string buff_to_send, buff_to_recv; + stg.store_to_binary(buff_to_send); + + int res = transport.notify(command, buff_to_send, conn_id); + if(res <=0 ) + { + LOG_PRINT_RED_L0("Failed to notify command " << command << " return code " << res); + return false; + } + return true; + } + //---------------------------------------------------------------------------------------------------- + //---------------------------------------------------------------------------------------------------- + template + int buff_to_t_adapter(int command, const std::string& in_buff, std::string& buff_out, callback_t cb, t_context& context ) + { + serialization::portable_storage strg; + if(!strg.load_from_binary(in_buff)) + { + LOG_ERROR("Failed to load_from_binary in command " << command); + return -1; + } + boost::value_initialized in_struct; + boost::value_initialized out_struct; + + static_cast(in_struct).load(strg); + int res = cb(command, static_cast(in_struct), static_cast(out_struct), context); + serialization::portable_storage strg_out; + static_cast(out_struct).store(strg_out); + + if(!strg_out.store_to_binary(buff_out)) + { + LOG_ERROR("Failed to store_to_binary in command" << command); + return -1; + } + + return res; + }; + + template + int buff_to_t_adapter(t_owner* powner, int command, const std::string& in_buff, callback_t cb, t_context& context) + { + serialization::portable_storage strg; + if(!strg.load_from_binary(in_buff)) + { + LOG_ERROR("Failed to load_from_binary in notify " << command); + return -1; + } + boost::value_initialized in_struct; + static_cast(in_struct).load(strg); + return cb(command, in_struct, context); + }; + +#define CHAIN_LEVIN_INVOKE_MAP2(context_type) \ + int invoke(int command, const std::string& in_buff, std::string& buff_out, context_type& context) \ + { \ + bool handled = false; \ + return handle_invoke_map(false, command, in_buff, buff_out, context, handled); \ + } + +#define CHAIN_LEVIN_NOTIFY_MAP2(context_type) \ + int notify(int command, const std::string& in_buff, context_type& context) \ + { \ + bool handled = false; std::string fake_str;\ + return handle_invoke_map(true, command, in_buff, fake_str, context, handled); \ + } + + +#define CHAIN_LEVIN_INVOKE_MAP() \ + int invoke(int command, const std::string& in_buff, std::string& buff_out, epee::net_utils::connection_context_base& context) \ + { \ + bool handled = false; \ + return handle_invoke_map(false, command, in_buff, buff_out, context, handled); \ + } + +#define CHAIN_LEVIN_NOTIFY_MAP() \ + int notify(int command, const std::string& in_buff, epee::net_utils::connection_context_base& context) \ + { \ + bool handled = false; std::string fake_str;\ + return handle_invoke_map(true, command, in_buff, fake_str, context, handled); \ + } + +#define CHAIN_LEVIN_NOTIFY_STUB() \ + int notify(int command, const std::string& in_buff, epee::net_utils::connection_context_base& context) \ + { \ + return -1; \ + } + +#define BEGIN_INVOKE_MAP2(owner_type) \ + template int handle_invoke_map(bool is_notify, int command, const std::string& in_buff, std::string& buff_out, t_context& context, bool& handled) \ + { \ + typedef owner_type internal_owner_type_name; + +#define HANDLE_INVOKE2(command_id, func, type_name_in, typename_out) \ + if(!is_notify && command_id == command) \ + {handled=true;return epee::net_utils::buff_to_t_adapter(this, command, in_buff, buff_out, boost::bind(func, this, _1, _2, _3, _4), context);} + +#define HANDLE_INVOKE_T2(COMMAND, func) \ + if(!is_notify && COMMAND::ID == command) \ + {handled=true;return epee::net_utils::buff_to_t_adapter(command, in_buff, buff_out, boost::bind(func, this, _1, _2, _3, _4), context);} + + +#define HANDLE_NOTIFY2(command_id, func, type_name_in) \ + if(is_notify && command_id == command) \ + {handled=true;return epee::net_utils::buff_to_t_adapter(this, command, in_buff, boost::bind(func, this, _1, _2, _3), context);} + +#define HANDLE_NOTIFY_T2(NOTIFY, func) \ + if(is_notify && NOTIFY::ID == command) \ + {handled=true;return epee::net_utils::buff_to_t_adapter(this, command, in_buff, boost::bind(func, this, _1, _2, _3), context);} + + +#define CHAIN_INVOKE_MAP2(func) \ + { \ + int res = func(is_notify, command, in_buff, buff_out, context, handled); \ + if(handled) \ + return res; \ + } + +#define CHAIN_INVOKE_MAP_TO_OBJ2(obj) \ + { \ + int res = obj.handle_invoke_map(is_notify, command, in_buff, buff_out, context, handled); \ + if(handled) \ + return res; \ + } + +#define CHAIN_INVOKE_MAP_TO_OBJ_FORCE_CONTEXT(obj, context_type) \ + { \ + int res = obj.handle_invoke_map(is_notify, command, in_buff, buff_out, static_cast(context), handled); \ + if(handled) return res; \ + } + + +#define END_INVOKE_MAP2() \ + LOG_ERROR("Unkonown command:" << command); \ + return LEVIN_ERROR_CONNECTION_HANDLER_NOT_DEFINED; \ + } + } +} + diff --git a/contrib/epee/include/storages/parserse_base_utils.h b/contrib/epee/include/storages/parserse_base_utils.h new file mode 100644 index 000000000..baafb5623 --- /dev/null +++ b/contrib/epee/include/storages/parserse_base_utils.h @@ -0,0 +1,260 @@ +// 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. +// + + + +#pragma once + +namespace epee +{ +namespace misc_utils +{ + namespace parse + { + inline std::string transform_to_escape_sequence(const std::string& src) + { + //std::stringstream res; + std::string res; + for(std::string::const_iterator it = src.begin(); it!=src.end(); ++it) + { + switch(*it) + { + case '\b': //Backspace (ascii code 08) + res+="\\b"; break; + case '\f': //Form feed (ascii code 0C) + res+="\\f"; break; + case '\n': //New line + res+="\\n"; break; + case '\r': //Carriage return + res+="\\r"; break; + case '\t': //Tab + res+="\\t"; break; + case '\v': //Vertical tab + res+="\\v"; break; + //case '\'': //Apostrophe or single quote + // res+="\\'"; break; + case '"': //Double quote + res+="\\\""; break; + case '\\': //Backslash caracter + res+="\\\\"; break; + case '/': //Backslash caracter + res+="\\/"; break; + default: + res.push_back(*it); + } + } + return res; + } + /* + + \b Backspace (ascii code 08) + \f Form feed (ascii code 0C) + \n New line + \r Carriage return + \t Tab + \v Vertical tab + \' Apostrophe or single quote + \" Double quote + \\ Backslash character + + */ + inline void match_string2(std::string::const_iterator& star_end_string, std::string::const_iterator buf_end, std::string& val) + { + val.clear(); + bool escape_mode = false; + std::string::const_iterator it = star_end_string; + ++it; + for(;it != buf_end;it++) + { + if(escape_mode/*prev_ch == '\\'*/) + { + switch(*it) + { + case 'b': //Backspace (ascii code 08) + val.push_back(0x08);break; + case 'f': //Form feed (ascii code 0C) + val.push_back(0x0C);break; + case 'n': //New line + val.push_back('\n');break; + case 'r': //Carriage return + val.push_back('\r');break; + case 't': //Tab + val.push_back('\t');break; + case 'v': //Vertical tab + val.push_back('\v');break; + case '\'': //Apostrophe or single quote + val.push_back('\'');break; + case '"': //Double quote + val.push_back('"');break; + case '\\': //Backslash character + val.push_back('\\');break; + case '/': //Slash character + val.push_back('/');break; + default: + val.push_back(*it); + LOG_PRINT_L0("Unknown escape sequence :\"\\" << *it << "\""); + } + escape_mode = false; + }else if(*it == '"') + { + star_end_string = it; + return; + }else if(*it == '\\') + { + escape_mode = true; + } + else + { + val.push_back(*it); + } + } + ASSERT_MES_AND_THROW("Failed to match string in json entry: " << std::string(star_end_string, buf_end)); + } + inline bool match_string(std::string::const_iterator& star_end_string, std::string::const_iterator buf_end, std::string& val) + { + try + { + + match_string2(star_end_string, buf_end, val); + return true; + } + catch(...) + { + return false; + } + } + inline void match_number2(std::string::const_iterator& star_end_string, std::string::const_iterator buf_end, std::string& val, bool& is_float_val, bool& is_signed_val) + { + val.clear(); + is_float_val = false; + for(std::string::const_iterator it = star_end_string;it != buf_end;it++) + { + if(isdigit(*it) || (it == star_end_string && *it == '-') || (val.size() && *it == '.' ) || (is_float_val && (*it == 'e' || *it == 'E' || *it == '-' || *it == '+' )) ) + { + if(!val.size() && *it == '-') + is_signed_val = true; + if(*it == '.' ) + is_float_val = true; + val.push_back(*it); + } + else + { + if(val.size()) + { + star_end_string = --it; + return; + } + else + ASSERT_MES_AND_THROW("wrong number in json entry: " << std::string(star_end_string, buf_end)); + } + } + ASSERT_MES_AND_THROW("wrong number in json entry: " << std::string(star_end_string, buf_end)); + } + inline bool match_number(std::string::const_iterator& star_end_string, std::string::const_iterator buf_end, std::string& val) + { + try + { + bool is_v_float = false;bool is_signed_val = false; + match_number2(star_end_string, buf_end, val, is_v_float, is_signed_val); + return !is_v_float; + } + catch(...) + { + return false; + } + } + inline void match_word2(std::string::const_iterator& star_end_string, std::string::const_iterator buf_end, std::string& val) + { + val.clear(); + + for(std::string::const_iterator it = star_end_string;it != buf_end;it++) + { + if(!isalpha(*it)) + { + val.assign(star_end_string, it); + if(val.size()) + { + star_end_string = --it; + return; + }else + ASSERT_MES_AND_THROW("failed to match word number in json entry: " << std::string(star_end_string, buf_end)); + } + } + ASSERT_MES_AND_THROW("failed to match word number in json entry: " << std::string(star_end_string, buf_end)); + } + inline bool match_word(std::string::const_iterator& star_end_string, std::string::const_iterator buf_end, std::string& val) + { + try + { + match_word2(star_end_string, buf_end, val); + return true; + } + catch(...) + { + return false; + } + } + inline bool match_word_with_extrasymb(std::string::const_iterator& star_end_string, std::string::const_iterator buf_end, std::string& val) + { + val.clear(); + + for(std::string::const_iterator it = star_end_string;it != buf_end;it++) + { + if(!isalnum(*it) && *it != '-' && *it != '_') + { + val.assign(star_end_string, it); + if(val.size()) + { + star_end_string = --it; + return true; + }else + return false; + } + } + return false; + } + inline bool match_word_til_equal_mark(std::string::const_iterator& star_end_string, std::string::const_iterator buf_end, std::string::const_iterator& word_end) + { + word_end = star_end_string; + + for(std::string::const_iterator it = star_end_string;it != buf_end;it++) + { + if(isspace(*it)) + { + + continue; + }else if( *it == '=' ) + { + star_end_string = it; + word_end = it; + return true; + } + } + return false; + } + } +} +} \ No newline at end of file diff --git a/contrib/epee/include/storages/portable_storage.h b/contrib/epee/include/storages/portable_storage.h new file mode 100644 index 000000000..8f4ebc6ae --- /dev/null +++ b/contrib/epee/include/storages/portable_storage.h @@ -0,0 +1,463 @@ +// 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. +// + + + +#pragma once + +#include "misc_language.h" +#include "portable_storage_base.h" +#include "portable_storage_to_bin.h" +#include "portable_storage_from_bin.h" +#include "portable_storage_to_json.h" +#include "portable_storage_from_json.h" +#include "portable_storage_val_converters.h" + +namespace epee +{ + namespace serialization + { + /************************************************************************/ + /* */ + /************************************************************************/ + class portable_storage + { + public: + typedef epee::serialization::hsection hsection; + typedef epee::serialization::harray harray; + + portable_storage(){} + virtual ~portable_storage(){} + hsection open_section(const std::string& section_name, hsection hparent_section, bool create_if_notexist = false); + template + bool get_value(const std::string& value_name, t_value& val, hsection hparent_section); + template + bool set_value(const std::string& value_name, const t_value& target, hsection hparent_section); + + //serial access for arrays of values -------------------------------------- + //values + template + harray get_first_value(const std::string& value_name, t_value& target, hsection hparent_section); + template + bool get_next_value(harray hval_array, t_value& target); + template + harray insert_first_value(const std::string& value_name, const t_value& target, hsection hparent_section); + template + bool insert_next_value(harray hval_array, const t_value& target); + //sections + harray get_first_section(const std::string& pSectionName, hsection& h_child_section, hsection hparent_section); + bool get_next_section(harray hSecArray, hsection& h_child_section); + harray insert_first_section(const std::string& pSectionName, hsection& hinserted_childsection, hsection hparent_section); + bool insert_next_section(harray hSecArray, hsection& hinserted_childsection); + //------------------------------------------------------------------------ + //delete entry (section, value or array) + bool delete_entry(const std::string& pentry_name, hsection hparent_section = nullptr); + + //------------------------------------------------------------------------------- + bool store_to_binary(binarybuffer& target); + bool load_from_binary(const binarybuffer& target); + template + bool dump_as_xml(std::string& targetObj, const std::string& root_name = ""); + bool dump_as_json(std::string& targetObj, size_t indent = 0); + bool load_from_json(const std::string& source); + + private: + section m_root; + hsection get_root_section() {return &m_root;} + storage_entry* find_storage_entry(const std::string& pentry_name, hsection psection); + template + storage_entry* insert_new_entry_get_storage_entry(const std::string& pentry_name, hsection psection, const entry_type& entry); + + hsection insert_new_section(const std::string& pentry_name, hsection psection); + +#pragma pack(push) +#pragma pack(1) + struct storage_block_header + { + uint32_t m_signature_a; + uint32_t m_signature_b; + uint8_t m_ver; + }; +#pragma pack(pop) + }; + inline + bool portable_storage::dump_as_json(std::string& buff, size_t indent) + { + TRY_ENTRY(); + std::stringstream ss; + epee::serialization::dump_as_json(ss, m_root, indent); + buff = ss.str(); + return true; + CATCH_ENTRY("portable_storage::dump_as_json", false) + } + inline + bool portable_storage::load_from_json(const std::string& source) + { + TRY_ENTRY(); + return json::load_from_json(source, *this); + CATCH_ENTRY("portable_storage::load_from_json", false) + } + + template + bool portable_storage::dump_as_xml(std::string& targetObj, const std::string& root_name) + { + return false;//TODO: don't think i ever again will use xml - ambiguous and "overtagged" format + } + + inline + bool portable_storage::store_to_binary(binarybuffer& target) + { + TRY_ENTRY(); + std::stringstream ss; + storage_block_header sbh = AUTO_VAL_INIT(sbh); + sbh.m_signature_a = PORTABLE_STORAGE_SIGNATUREA; + sbh.m_signature_b = PORTABLE_STORAGE_SIGNATUREB; + sbh.m_ver = PORTABLE_STORAGE_FORMAT_VER; + ss.write((const char*)&sbh, sizeof(storage_block_header)); + pack_entry_to_buff(ss, m_root); + target = ss.str(); + return true; + CATCH_ENTRY("portable_storage::store_to_binary", false) + } + inline + bool portable_storage::load_from_binary(const binarybuffer& source) + { + m_root.m_entries.clear(); + if(source.size() < sizeof(storage_block_header)) + { + LOG_ERROR("portable_storage: wrong binary format, packet size = " << source.size() << " less than expected sizeof(storage_block_header)=" << sizeof(storage_block_header)); + return false; + } + storage_block_header* pbuff = (storage_block_header*)source.data(); + if(pbuff->m_signature_a != PORTABLE_STORAGE_SIGNATUREA || + pbuff->m_signature_b != PORTABLE_STORAGE_SIGNATUREB + ) + { + LOG_ERROR("portable_storage: wrong binary format - signature missmatch"); + return false; + } + if(pbuff->m_ver != PORTABLE_STORAGE_FORMAT_VER) + { + LOG_ERROR("portable_storage: wrong binary format - unknown format ver = " << pbuff->m_ver); + return false; + } + TRY_ENTRY(); + throwable_buffer_reader buf_reader(source.data()+sizeof(storage_block_header), source.size()-sizeof(storage_block_header)); + buf_reader.read(m_root); + return true;//TODO: + CATCH_ENTRY("portable_storage::load_from_binary", false); + } + //--------------------------------------------------------------------------------------------------------------- + inline + hsection portable_storage::open_section(const std::string& section_name, hsection hparent_section, bool create_if_notexist) + { + TRY_ENTRY(); + hparent_section = hparent_section ? hparent_section:&m_root; + storage_entry* pentry = find_storage_entry(section_name, hparent_section); + if(!pentry) + { + if(!create_if_notexist) + return nullptr; + return insert_new_section(section_name, hparent_section); + } + CHECK_AND_ASSERT(pentry , nullptr); + //check that section_entry we find is real "CSSection" + if(pentry->type() != typeid(section)) + { + if(create_if_notexist) + *pentry = storage_entry(section());//replace + else + return nullptr; + } + return &boost::get
(*pentry); + CATCH_ENTRY("portable_storage::open_section", nullptr); + } + //--------------------------------------------------------------------------------------------------------------- + template + struct get_value_visitor: boost::static_visitor + { + to_type& m_target; + get_value_visitor(to_type& target):m_target(target){} + template + void operator()(const from_type& v){convert_t(v, m_target);} + }; + + template + bool portable_storage::get_value(const std::string& value_name, t_value& val, hsection hparent_section) + { + BOOST_MPL_ASSERT(( boost::mpl::contains )); + //TRY_ENTRY(); + if(!hparent_section) hparent_section = &m_root; + storage_entry* pentry = find_storage_entry(value_name, hparent_section); + if(!pentry) + return false; + + get_value_visitor gvv(val); + boost::apply_visitor(gvv, *pentry); + return true; + //CATCH_ENTRY("portable_storage::template<>get_value", false); + } + //--------------------------------------------------------------------------------------------------------------- + template + bool portable_storage::set_value(const std::string& value_name, const t_value& v, hsection hparent_section) + { + BOOST_MPL_ASSERT(( boost::mpl::contains )); + TRY_ENTRY(); + if(!hparent_section) + hparent_section = &m_root; + storage_entry* pentry = find_storage_entry(value_name, hparent_section); + if(!pentry) + { + pentry = insert_new_entry_get_storage_entry(value_name, hparent_section, v); + if(!pentry) + return false; + return true; + } + *pentry = storage_entry(v); + return true; + CATCH_ENTRY("portable_storage::template<>set_value", false); + } + //--------------------------------------------------------------------------------------------------------------- + inline + storage_entry* portable_storage::find_storage_entry(const std::string& pentry_name, hsection psection) + { + TRY_ENTRY(); + CHECK_AND_ASSERT(psection, nullptr); + auto it = psection->m_entries.find(pentry_name); + if(it == psection->m_entries.end()) + return nullptr; + + return &it->second; + CATCH_ENTRY("portable_storage::find_storage_entry", nullptr); + } + //--------------------------------------------------------------------------------------------------------------- + template + storage_entry* portable_storage::insert_new_entry_get_storage_entry(const std::string& pentry_name, hsection psection, const entry_type& entry) + { + TRY_ENTRY(); + CHECK_AND_ASSERT(psection, nullptr); + auto ins_res = psection->m_entries.insert(std::pair(pentry_name, entry)); + return &ins_res.first->second; + CATCH_ENTRY("portable_storage::insert_new_entry_get_storage_entry", nullptr); + } + //--------------------------------------------------------------------------------------------------------------- + inline + hsection portable_storage::insert_new_section(const std::string& pentry_name, hsection psection) + { + TRY_ENTRY(); + storage_entry* pse = insert_new_entry_get_storage_entry(pentry_name, psection, section()); + if(!pse) return nullptr; + return &boost::get
(*pse); + CATCH_ENTRY("portable_storage::insert_new_section", nullptr); + } + //--------------------------------------------------------------------------------------------------------------- + template + struct get_first_value_visitor: boost::static_visitor + { + to_type& m_target; + get_first_value_visitor(to_type& target):m_target(target){} + template + bool operator()(const array_entry_t& a) + { + const from_type* pv = a.get_first_val(); + if(!pv) + return false; + convert_t(*pv, m_target); + return true; + } + }; + //--------------------------------------------------------------------------------------------------------------- + template + harray portable_storage::get_first_value(const std::string& value_name, t_value& target, hsection hparent_section) + { + BOOST_MPL_ASSERT(( boost::mpl::contains )); + //TRY_ENTRY(); + if(!hparent_section) hparent_section = &m_root; + storage_entry* pentry = find_storage_entry(value_name, hparent_section); + if(!pentry) + return nullptr; + if(pentry->type() != typeid(array_entry)) + return nullptr; + array_entry& ar_entry = boost::get(*pentry); + + get_first_value_visitor gfv(target); + if(!boost::apply_visitor(gfv, ar_entry)) + return nullptr; + return &ar_entry; + //CATCH_ENTRY("portable_storage::get_first_value", nullptr); + } + //--------------------------------------------------------------------------------------------------------------- + template + struct get_next_value_visitor: boost::static_visitor + { + to_type& m_target; + get_next_value_visitor(to_type& target):m_target(target){} + template + bool operator()(const array_entry_t& a) + { + //TODO: optimize code here: work without get_next_val function + const from_type* pv = a.get_next_val(); + if(!pv) + return false; + convert_t(*pv, m_target); + return true; + } + }; + + + template + bool portable_storage::get_next_value(harray hval_array, t_value& target) + { + BOOST_MPL_ASSERT(( boost::mpl::contains )); + //TRY_ENTRY(); + CHECK_AND_ASSERT(hval_array, false); + array_entry& ar_entry = *hval_array; + get_next_value_visitor gnv(target); + if(!boost::apply_visitor(gnv, ar_entry)) + return false; + return true; + //CATCH_ENTRY("portable_storage::get_next_value", false); + } + //--------------------------------------------------------------------------------------------------------------- + template + harray portable_storage::insert_first_value(const std::string& value_name, const t_value& target, hsection hparent_section) + { + TRY_ENTRY(); + if(!hparent_section) hparent_section = &m_root; + storage_entry* pentry = find_storage_entry(value_name, hparent_section); + if(!pentry) + { + pentry = insert_new_entry_get_storage_entry(value_name, hparent_section, array_entry(array_entry_t())); + if(!pentry) + return nullptr; + } + if(pentry->type() != typeid(array_entry)) + *pentry = storage_entry(array_entry(array_entry_t())); + + array_entry& arr = boost::get(*pentry); + if(arr.type() != typeid(array_entry_t)) + arr = array_entry(array_entry_t()); + + array_entry_t& arr_typed = boost::get >(arr); + arr_typed.insert_first_val(target); + return &arr; + CATCH_ENTRY("portable_storage::insert_first_value", nullptr); + } + //--------------------------------------------------------------------------------------------------------------- + template + bool portable_storage::insert_next_value(harray hval_array, const t_value& target) + { + TRY_ENTRY(); + CHECK_AND_ASSERT(hval_array, false); + + CHECK_AND_ASSERT_MES(hval_array->type() == typeid(array_entry_t), + false, "unexpected type in insert_next_value: " << typeid(array_entry_t).name()); + + array_entry_t& arr_typed = boost::get >(*hval_array); + arr_typed.insert_next_value(target); + return true; + CATCH_ENTRY("portable_storage::insert_next_value", false); + } + //--------------------------------------------------------------------------------------------------------------- + //sections + inline + harray portable_storage::get_first_section(const std::string& sec_name, hsection& h_child_section, hsection hparent_section) + { + TRY_ENTRY(); + if(!hparent_section) hparent_section = &m_root; + storage_entry* pentry = find_storage_entry(sec_name, hparent_section); + if(!pentry) + return nullptr; + if(pentry->type() != typeid(array_entry)) + return nullptr; + array_entry& ar_entry = boost::get(*pentry); + if(ar_entry.type() != typeid(array_entry_t
)) + return nullptr; + array_entry_t
& sec_array = boost::get>(ar_entry); + section* psec = sec_array.get_first_val(); + if(!psec) + return nullptr; + h_child_section = psec; + return &ar_entry; + CATCH_ENTRY("portable_storage::get_first_section", nullptr); + } + //--------------------------------------------------------------------------------------------------------------- + inline + bool portable_storage::get_next_section(harray hsec_array, hsection& h_child_section) + { + TRY_ENTRY(); + CHECK_AND_ASSERT(hsec_array, false); + if(hsec_array->type() != typeid(array_entry_t
)) + return nullptr; + array_entry_t
& sec_array = boost::get>(*hsec_array); + h_child_section = sec_array.get_next_val(); + if(!h_child_section) + return false; + return true; + CATCH_ENTRY("portable_storage::get_next_section", false); + } + //--------------------------------------------------------------------------------------------------------------- + inline + harray portable_storage::insert_first_section(const std::string& sec_name, hsection& hinserted_childsection, hsection hparent_section) + { + TRY_ENTRY(); + if(!hparent_section) hparent_section = &m_root; + storage_entry* pentry = find_storage_entry(sec_name, hparent_section); + if(!pentry) + { + pentry = insert_new_entry_get_storage_entry(sec_name, hparent_section, array_entry(array_entry_t
())); + if(!pentry) + return nullptr; + } + if(pentry->type() != typeid(array_entry)) + *pentry = storage_entry(array_entry(array_entry_t
())); + + array_entry& ar_entry = boost::get(*pentry); + if(ar_entry.type() != typeid(array_entry_t
)) + ar_entry = array_entry(array_entry_t
()); + + array_entry_t
& sec_array = boost::get>(ar_entry); + hinserted_childsection = &sec_array.insert_first_val(section()); + return &ar_entry; + CATCH_ENTRY("portable_storage::insert_first_section", nullptr); + } + //--------------------------------------------------------------------------------------------------------------- + inline + bool portable_storage::insert_next_section(harray hsec_array, hsection& hinserted_childsection) + { + TRY_ENTRY(); + CHECK_AND_ASSERT(hsec_array, false); + CHECK_AND_ASSERT_MES(hsec_array->type() == typeid(array_entry_t
), + false, "unexpected type(not 'section') in insert_next_section, type: " << hsec_array->type().name()); + + array_entry_t
& sec_array = boost::get>(*hsec_array); + hinserted_childsection = &sec_array.insert_next_value(section()); + return true; + CATCH_ENTRY("portable_storage::insert_next_section", false); + } + //--------------------------------------------------------------------------------------------------------------- + } +} \ No newline at end of file diff --git a/contrib/epee/include/storages/portable_storage_base.h b/contrib/epee/include/storages/portable_storage_base.h new file mode 100644 index 000000000..3f1637538 --- /dev/null +++ b/contrib/epee/include/storages/portable_storage_base.h @@ -0,0 +1,160 @@ +// 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. +// + + + +#pragma once + +#include +#include +#include +#include + +#define PORTABLE_STORAGE_SIGNATUREA 0x01011101 +#define PORTABLE_STORAGE_SIGNATUREB 0x01020101 // bender's nightmare +#define PORTABLE_STORAGE_FORMAT_VER 1 + +#define PORTABLE_RAW_SIZE_MARK_MASK 0x03 +#define PORTABLE_RAW_SIZE_MARK_BYTE 0 +#define PORTABLE_RAW_SIZE_MARK_WORD 1 +#define PORTABLE_RAW_SIZE_MARK_DWORD 2 +#define PORTABLE_RAW_SIZE_MARK_INT64 3 + +#ifndef MAX_STRING_LEN_POSSIBLE +#define MAX_STRING_LEN_POSSIBLE 2000000000 //do not let string be so big +#endif + +//data types +#define SERIALIZE_TYPE_INT64 1 +#define SERIALIZE_TYPE_INT32 2 +#define SERIALIZE_TYPE_INT16 3 +#define SERIALIZE_TYPE_INT8 4 +#define SERIALIZE_TYPE_UINT64 5 +#define SERIALIZE_TYPE_UINT32 6 +#define SERIALIZE_TYPE_UINT16 7 +#define SERIALIZE_TYPE_UINT8 8 +#define SERIALIZE_TYPE_DUOBLE 9 +#define SERIALIZE_TYPE_STRING 10 +#define SERIALIZE_TYPE_BOOL 11 +#define SERIALIZE_TYPE_OBJECT 12 +#define SERIALIZE_TYPE_ARRAY 13 + +#define SERIALIZE_FLAG_ARRAY 0x80 + + +namespace epee +{ + namespace serialization + { + struct section; + + /************************************************************************/ + /* */ + /************************************************************************/ + template + struct array_entry_t + { + array_entry_t():m_it(m_array.end()){} + + const t_entry_type* get_first_val() const + { + m_it = m_array.begin(); + return get_next_val(); + } + + t_entry_type* get_first_val() + { + m_it = m_array.begin(); + return get_next_val(); + } + + + const t_entry_type* get_next_val() const + { + if(m_it == m_array.end()) + return nullptr; + return &(*(m_it++)); + } + + t_entry_type* get_next_val() + { + if(m_it == m_array.end()) + return nullptr; + return (t_entry_type*)&(*(m_it++));//fuckoff + } + + t_entry_type& insert_first_val(const t_entry_type& v) + { + m_array.clear(); + m_it = m_array.end(); + return insert_next_value(v); + } + + t_entry_type& insert_next_value(const t_entry_type& v) + { + m_array.push_back(v); + return m_array.back(); + } + + std::list m_array; + mutable typename std::list::const_iterator m_it; + }; + + + typedef boost::make_recursive_variant< + array_entry_t
, + array_entry_t, + array_entry_t, + array_entry_t, + array_entry_t, + array_entry_t, + array_entry_t, + array_entry_t, + array_entry_t, + array_entry_t, + array_entry_t, + array_entry_t, + array_entry_t
, + array_entry_t + >::type array_entry; + + typedef boost::variant storage_entry; + + typedef std::string binarybuffer;//it's ok + + /************************************************************************/ + /* */ + /************************************************************************/ + struct section + { + std::map m_entries; + }; + + //handle-like aliases + typedef section* hsection; + typedef array_entry* harray; + } +} \ No newline at end of file diff --git a/contrib/epee/include/storages/portable_storage_from_bin.h b/contrib/epee/include/storages/portable_storage_from_bin.h new file mode 100644 index 000000000..e9b7e2e6f --- /dev/null +++ b/contrib/epee/include/storages/portable_storage_from_bin.h @@ -0,0 +1,281 @@ +// 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. +// + + + +#pragma once + +#include "misc_language.h" +#include "portable_storage_base.h" + +#ifdef EPEE_PORTABLE_STORAGE_RECURSION_LIMIT +#define EPEE_PORTABLE_STORAGE_RECURSION_LIMIT_INTERNAL EPEE_PORTABLE_STORAGE_RECURSION_LIMIT +#else +#define EPEE_PORTABLE_STORAGE_RECURSION_LIMIT_INTERNAL 100 +#endif + +namespace epee +{ + namespace serialization + { + struct throwable_buffer_reader + { + throwable_buffer_reader(const void* ptr, size_t sz); + void read(void* target, size_t count); + void read_sec_name(std::string& sce_name); + template + void read(t_pod_type& pod_val); + template + t_type read(); + template + storage_entry read_ae(); + storage_entry load_storage_array_entry(uint8_t type); + size_t read_varint(); + template + storage_entry read_se(); + storage_entry load_storage_entry(); + void read(section& sec); + void read(std::string& str); + private: + struct recursuion_limitation_guard + { + size_t& m_counter_ref; + recursuion_limitation_guard(size_t& counter):m_counter_ref(counter) + { + ++m_counter_ref; + CHECK_AND_ASSERT_THROW_MES(m_counter_ref < EPEE_PORTABLE_STORAGE_RECURSION_LIMIT_INTERNAL, "Wrong blob data in portable storage: recursion limitation (" << EPEE_PORTABLE_STORAGE_RECURSION_LIMIT_INTERNAL << ") exceeded"); + } + ~recursuion_limitation_guard() + { + CHECK_AND_ASSERT_THROW_MES(m_counter_ref != 0, "Internal error: m_counter_ref == 0 while ~recursuion_limitation_guard()"); + --m_counter_ref; + } + }; +#define RECURSION_LIMITATION() recursuion_limitation_guard rl(m_recursion_count) + + const uint8_t* m_ptr; + size_t m_count; + size_t m_recursion_count; + }; + + inline throwable_buffer_reader::throwable_buffer_reader(const void* ptr, size_t sz) + { + if(!ptr) + throw std::runtime_error("throwable_buffer_reader: ptr==nullptr"); + if(!sz) + throw std::runtime_error("throwable_buffer_reader: sz==0"); + m_ptr = (uint8_t*)ptr; + m_count = sz; + m_recursion_count = 0; + } + inline + void throwable_buffer_reader::read(void* target, size_t count) + { + RECURSION_LIMITATION(); + CHECK_AND_ASSERT_THROW_MES(m_count >= count, " attempt to read " << count << " bytes from buffer with " << m_count << " bytes remained"); + memcpy(target, m_ptr, count); + m_ptr += count; + m_count -= count; + } + inline + void throwable_buffer_reader::read_sec_name(std::string& sce_name) + { + RECURSION_LIMITATION(); + uint8_t name_len = 0; + read(name_len); + sce_name.resize(name_len); + read((void*)sce_name.data(), name_len); + } + + template + void throwable_buffer_reader::read(t_pod_type& pod_val) + { + RECURSION_LIMITATION(); + read(&pod_val, sizeof(pod_val)); + } + + template + t_type throwable_buffer_reader::read() + { + RECURSION_LIMITATION(); + t_type v; + read(v); + return v; + } + + + template + storage_entry throwable_buffer_reader::read_ae() + { + RECURSION_LIMITATION(); + //for pod types + array_entry_t sa; + size_t size = read_varint(); + //TODO: add some optimization here later + while(size--) + sa.m_array.push_back(read()); + return storage_entry(array_entry(sa)); + } + + inline + storage_entry throwable_buffer_reader::load_storage_array_entry(uint8_t type) + { + RECURSION_LIMITATION(); + type &= ~SERIALIZE_FLAG_ARRAY; + switch(type) + { + case SERIALIZE_TYPE_INT64: return read_ae(); + case SERIALIZE_TYPE_INT32: return read_ae(); + case SERIALIZE_TYPE_INT16: return read_ae(); + case SERIALIZE_TYPE_INT8: return read_ae(); + case SERIALIZE_TYPE_UINT64: return read_ae(); + case SERIALIZE_TYPE_UINT32: return read_ae(); + case SERIALIZE_TYPE_UINT16: return read_ae(); + case SERIALIZE_TYPE_UINT8: return read_ae(); + case SERIALIZE_TYPE_DUOBLE: return read_ae(); + case SERIALIZE_TYPE_BOOL: return read_ae(); + case SERIALIZE_TYPE_STRING: return read_ae(); + case SERIALIZE_TYPE_OBJECT: return read_ae
(); + case SERIALIZE_TYPE_ARRAY: return read_ae(); + default: + CHECK_AND_ASSERT_THROW_MES(false, "unknown entry_type code = " << type); + } + } + + inline + size_t throwable_buffer_reader::read_varint() + { + RECURSION_LIMITATION(); + CHECK_AND_ASSERT_THROW_MES(m_count >= 1, "empty buff, expected place for varint"); + size_t v = 0; + uint8_t size_mask = (*(uint8_t*)m_ptr) &PORTABLE_RAW_SIZE_MARK_MASK; + switch (size_mask) + { + case PORTABLE_RAW_SIZE_MARK_BYTE: v = read();break; + case PORTABLE_RAW_SIZE_MARK_WORD: v = read();break; + case PORTABLE_RAW_SIZE_MARK_DWORD: v = read();break; + case PORTABLE_RAW_SIZE_MARK_INT64: v = read();break; + default: + CHECK_AND_ASSERT_THROW_MES(false, "unknown varint size_mask = " << size_mask); + } + v >>= 2; + return v; + } + + template + storage_entry throwable_buffer_reader::read_se() + { + RECURSION_LIMITATION(); + t_type v; + read(v); + return storage_entry(v); + } + + template<> + inline storage_entry throwable_buffer_reader::read_se() + { + RECURSION_LIMITATION(); + return storage_entry(read()); + } + + + template<> + inline storage_entry throwable_buffer_reader::read_se
() + { + RECURSION_LIMITATION(); + section s;//use extra variable due to vs bug, line "storage_entry se(section()); " can't be compiled in visual studio + storage_entry se(s); + section& section_entry = boost::get
(se); + read(section_entry); + return se; + } + + template<> + inline storage_entry throwable_buffer_reader::read_se() + { + RECURSION_LIMITATION(); + uint8_t ent_type = 0; + read(ent_type); + CHECK_AND_ASSERT_THROW_MES(ent_type&SERIALIZE_FLAG_ARRAY, "wrong type sequenses"); + return load_storage_array_entry(ent_type); + } + + inline + storage_entry throwable_buffer_reader::load_storage_entry() + { + RECURSION_LIMITATION(); + uint8_t ent_type = 0; + read(ent_type); + if(ent_type&SERIALIZE_FLAG_ARRAY) + return load_storage_array_entry(ent_type); + + switch(ent_type) + { + case SERIALIZE_TYPE_INT64: return read_se(); + case SERIALIZE_TYPE_INT32: return read_se(); + case SERIALIZE_TYPE_INT16: return read_se(); + case SERIALIZE_TYPE_INT8: return read_se(); + case SERIALIZE_TYPE_UINT64: return read_se(); + case SERIALIZE_TYPE_UINT32: return read_se(); + case SERIALIZE_TYPE_UINT16: return read_se(); + case SERIALIZE_TYPE_UINT8: return read_se(); + case SERIALIZE_TYPE_DUOBLE: return read_se(); + case SERIALIZE_TYPE_BOOL: return read_se(); + case SERIALIZE_TYPE_STRING: return read_se(); + case SERIALIZE_TYPE_OBJECT: return read_se
(); + case SERIALIZE_TYPE_ARRAY: return read_se(); + default: + CHECK_AND_ASSERT_THROW_MES(false, "unknown entry_type code = " << ent_type); + } + } + inline + void throwable_buffer_reader::read(section& sec) + { + RECURSION_LIMITATION(); + sec.m_entries.clear(); + size_t count = read_varint(); + while(count--) + { + //read section name string + std::string sec_name; + read_sec_name(sec_name); + sec.m_entries.insert(std::make_pair(sec_name, load_storage_entry())); + } + } + inline + void throwable_buffer_reader::read(std::string& str) + { + RECURSION_LIMITATION(); + size_t len = read_varint(); + CHECK_AND_ASSERT_THROW_MES(len < MAX_STRING_LEN_POSSIBLE, "to big string len value in storage: " << len); + CHECK_AND_ASSERT_THROW_MES(m_count >= len, "string len count value " << len << " goes out of remain storage len " << m_count); + //do this manually to avoid double memory write in huge strings (first time at resize, second at read) + str.assign((const char*)m_ptr, len); + m_ptr+=len; + m_count -= len; + } + } +} \ No newline at end of file diff --git a/contrib/epee/include/storages/portable_storage_from_json.h b/contrib/epee/include/storages/portable_storage_from_json.h new file mode 100644 index 000000000..557db3da6 --- /dev/null +++ b/contrib/epee/include/storages/portable_storage_from_json.h @@ -0,0 +1,379 @@ +// 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. +// + +#pragma once +#include "parserse_base_utils.h" +#include "file_io_utils.h" + +namespace epee +{ + using namespace misc_utils::parse; + namespace serialization + { + namespace json + { +#define CHECK_ISSPACE() if(!isspace(*it)){ ASSERT_MES_AND_THROW("Wrong JSON character at: " << std::string(it, buf_end));} + + /*inline void parse_error() + { + ASSERT_MES_AND_THROW("json parse error"); + }*/ + template + inline void run_handler(typename t_storage::hsection current_section, std::string::const_iterator& sec_buf_begin, std::string::const_iterator buf_end, t_storage& stg) + { + + std::string::const_iterator sub_element_start; + std::string name; + typename t_storage::harray h_array = nullptr; + enum match_state + { + match_state_lookup_for_section_start, + match_state_lookup_for_name, + match_state_waiting_separator, + match_state_wonder_after_separator, + match_state_wonder_after_value, + match_state_wonder_array, + match_state_array_after_value, + match_state_array_waiting_value, + match_state_error + }; + + enum array_mode + { + array_mode_undifined = 0, + array_mode_sections, + array_mode_string, + array_mode_numbers, + array_mode_booleans + }; + + match_state state = match_state_lookup_for_section_start; + array_mode array_md = array_mode_undifined; + std::string::const_iterator it = sec_buf_begin; + for(;it != buf_end;it++) + { + switch (state) + { + case match_state_lookup_for_section_start: + if(*it == '{') + state = match_state_lookup_for_name; + else CHECK_ISSPACE(); + break; + case match_state_lookup_for_name: + switch(*it) + { + case '"': + match_string2(it, buf_end, name); + state = match_state_waiting_separator; + break; + case '}': + //this is it! section ends here. + //seems that it is empty section + sec_buf_begin = it; + return; + default: + CHECK_ISSPACE(); + } + break; + case match_state_waiting_separator: + if(*it == ':') + state = match_state_wonder_after_separator; + else CHECK_ISSPACE(); + break; + case match_state_wonder_after_separator: + if(*it == '"') + {//just a named string value started + std::string val; + match_string2(it, buf_end, val); + //insert text value + stg.set_value(name, val, current_section); + state = match_state_wonder_after_value; + }else if (isdigit(*it) || *it == '-') + {//just a named number value started + std::string val; + bool is_v_float = false;bool is_signed = false; + match_number2(it, buf_end, val, is_v_float, is_signed); + if(!is_v_float) + { + if(is_signed) + { + int64_t nval = boost::lexical_cast(val); + stg.set_value(name, nval, current_section); + }else + { + uint64_t nval = boost::lexical_cast(val); + stg.set_value(name, nval, current_section); + } + }else + { + double nval = boost::lexical_cast(val); + stg.set_value(name, nval, current_section); + } + state = match_state_wonder_after_value; + }else if(isalpha(*it) ) + {// could be null, true or false + std::string word; + match_word2(it, buf_end, word); + if(boost::iequals(word, "null")) + { + state = match_state_wonder_after_value; + //just skip this, + }else if(boost::iequals(word, "true")) + { + stg.set_value(name, true, current_section); + state = match_state_wonder_after_value; + }else if(boost::iequals(word, "false")) + { + stg.set_value(name, false, current_section); + state = match_state_wonder_after_value; + }else ASSERT_MES_AND_THROW("Unknown value keyword " << word); + }else if(*it == '{') + { + //sub section here + typename t_storage::hsection new_sec = stg.open_section(name, current_section, true); + CHECK_AND_ASSERT_THROW_MES(new_sec, "Failed to insert new section in json: " << std::string(it, buf_end)); + run_handler(new_sec, it, buf_end, stg); + state = match_state_wonder_after_value; + }else if(*it == '[') + {//array of something + state = match_state_wonder_array; + }else CHECK_ISSPACE(); + break; + case match_state_wonder_after_value: + if(*it == ',') + state = match_state_lookup_for_name; + else if(*it == '}') + { + //this is it! section ends here. + sec_buf_begin = it; + return; + }else CHECK_ISSPACE(); + break; + case match_state_wonder_array: + if(*it == '[') + { + ASSERT_MES_AND_THROW("array of array not suppoerted yet :( sorry"); + //mean array of array + } + if(*it == '{') + { + //mean array of sections + typename t_storage::hsection new_sec = nullptr; + h_array = stg.insert_first_section(name, new_sec, current_section); + CHECK_AND_ASSERT_THROW_MES(h_array&&new_sec, "failed to create new section"); + run_handler(new_sec, it, buf_end, stg); + state = match_state_array_after_value; + array_md = array_mode_sections; + }else if(*it == '"') + { + //mean array of strings + std::string val; + match_string2(it, buf_end, val); + h_array = stg.insert_first_value(name, val, current_section); + CHECK_AND_ASSERT_THROW_MES(h_array, " failed to insert values entry"); + state = match_state_array_after_value; + array_md = array_mode_string; + }else if (isdigit(*it) || *it == '-') + {//array of numbers value started + std::string val; + bool is_v_float = false;bool is_signed_val = false; + match_number2(it, buf_end, val, is_v_float, is_signed_val); + if(!is_v_float) + { + int64_t nval = boost::lexical_cast(val);//bool res = string_tools::string_to_num_fast(val, nval); + h_array = stg.insert_first_value(name, nval, current_section); + CHECK_AND_ASSERT_THROW_MES(h_array, " failed to insert values section entry"); + }else + { + double nval = boost::lexical_cast(val);//bool res = string_tools::string_to_num_fast(val, nval); + h_array = stg.insert_first_value(name, nval, current_section); + CHECK_AND_ASSERT_THROW_MES(h_array, " failed to insert values section entry"); + } + + state = match_state_array_after_value; + array_md = array_mode_numbers; + }else if(*it == ']')//empty array + { + array_md = array_mode_undifined; + state = match_state_wonder_after_value; + }else if(isalpha(*it) ) + {// array of booleans + std::string word; + match_word2(it, buf_end, word); + if(boost::iequals(word, "true")) + { + h_array = stg.insert_first_value(name, true, current_section); + CHECK_AND_ASSERT_THROW_MES(h_array, " failed to insert values section entry"); + state = match_state_array_after_value; + array_md = array_mode_booleans; + }else if(boost::iequals(word, "false")) + { + h_array = stg.insert_first_value(name, false, current_section); + CHECK_AND_ASSERT_THROW_MES(h_array, " failed to insert values section entry"); + state = match_state_array_after_value; + array_md = array_mode_booleans; + + }else ASSERT_MES_AND_THROW("Unknown value keyword " << word) + }else CHECK_ISSPACE(); + break; + case match_state_array_after_value: + if(*it == ',') + state = match_state_array_waiting_value; + else if(*it == ']') + { + h_array = nullptr; + array_md = array_mode_undifined; + state = match_state_wonder_after_value; + }else CHECK_ISSPACE(); + break; + case match_state_array_waiting_value: + switch(array_md) + { + case array_mode_sections: + if(*it == '{') + { + typename t_storage::hsection new_sec = NULL; + bool res = stg.insert_next_section(h_array, new_sec); + CHECK_AND_ASSERT_THROW_MES(res&&new_sec, "failed to insert next section"); + run_handler(new_sec, it, buf_end, stg); + state = match_state_array_after_value; + }else CHECK_ISSPACE(); + break; + case array_mode_string: + if(*it == '"') + { + std::string val; + match_string2(it, buf_end, val); + bool res = stg.insert_next_value(h_array, val); + CHECK_AND_ASSERT_THROW_MES(res, "failed to insert values"); + state = match_state_array_after_value; + }else CHECK_ISSPACE(); + break; + case array_mode_numbers: + if (isdigit(*it) || *it == '-') + {//array of numbers value started + std::string val; + bool is_v_float = false;bool is_signed_val = false; + match_number2(it, buf_end, val, is_v_float, is_signed_val); + bool insert_res = false; + if(!is_v_float) + { + boost::int64_t nval = boost::lexical_cast(val); //bool res = string_tools::string_to_num_fast(val, nval); + insert_res = stg.insert_next_value(h_array, nval); + + }else + { + //TODO: optimize here if need + double nval = boost::lexical_cast(val); //string_tools::string_to_num_fast(val, nval); + insert_res = stg.insert_next_value(h_array, nval); + } + CHECK_AND_ASSERT_THROW_MES(insert_res, "Failed to insert next value"); + state = match_state_array_after_value; + array_md = array_mode_numbers; + }else CHECK_ISSPACE(); + break; + case array_mode_booleans: + if(isalpha(*it) ) + {// array of booleans + std::string word; + match_word2(it, buf_end, word); + if(boost::iequals(word, "true")) + { + bool r = stg.insert_next_value(h_array, true); + CHECK_AND_ASSERT_THROW_MES(r, " failed to insert values section entry"); + state = match_state_array_after_value; + }else if(boost::iequals(word, "false")) + { + bool r = stg.insert_next_value(h_array, false); + CHECK_AND_ASSERT_THROW_MES(r, " failed to insert values section entry"); + state = match_state_array_after_value; + } + else ASSERT_MES_AND_THROW("Unknown value keyword " << word); + }else CHECK_ISSPACE(); + break; + case array_mode_undifined: + default: + ASSERT_MES_AND_THROW("Bad array state"); + } + break; + case match_state_error: + default: + ASSERT_MES_AND_THROW("WRONG JSON STATE"); + } + } + } +/* +{ + "firstName": "John", + "lastName": "Smith", + "age": 25, + "address": { + "streetAddress": "21 2nd Street", + "city": "New York", + "state": "NY", + "postalCode": -10021, + "have_boobs": true, + "have_balls": false + }, + "phoneNumber": [ + { + "type": "home", + "number": "212 555-1234" + }, + { + "type": "fax", + "number": "646 555-4567" + } + ], + "phoneNumbers": [ + "812 123-1234", + "916 123-4567" + ] +} +*/ + template + inline bool load_from_json(const std::string& buff_json, t_storage& stg) + { + std::string::const_iterator sec_buf_begin = buff_json.begin(); + try + { + run_handler(nullptr, sec_buf_begin, buff_json.end(), stg); + return true; + } + catch(const std::exception& ex) + { + LOG_PRINT_RED_L0("Failed to parse json, what: " << ex.what()); + return false; + } + catch(...) + { + LOG_PRINT_RED_L0("Failed to parse json"); + return false; + } + } + } + } +} \ No newline at end of file diff --git a/contrib/epee/include/storages/portable_storage_template_helper.h b/contrib/epee/include/storages/portable_storage_template_helper.h new file mode 100644 index 000000000..2163cb879 --- /dev/null +++ b/contrib/epee/include/storages/portable_storage_template_helper.h @@ -0,0 +1,120 @@ +// 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. +// + +#pragma once +#include "parserse_base_utils.h" +#include "portable_storage.h" +#include "file_io_utils.h" + +namespace epee +{ + namespace serialization + { + //----------------------------------------------------------------------------------------------------------- + template + bool load_t_from_json(t_struct& out, const std::string& json_buff) + { + portable_storage ps; + bool rs = ps.load_from_json(json_buff); + if(!rs) + return false; + + return out.load(ps); + } + //----------------------------------------------------------------------------------------------------------- + template + bool load_t_from_json_file(t_struct& out, const std::string& json_file) + { + std::string f_buff; + if(!file_io_utils::load_file_to_string(json_file, f_buff)) + return false; + + return load_t_from_json(out, f_buff); + } + //----------------------------------------------------------------------------------------------------------- + template + bool store_t_to_json(t_struct& str_in, std::string& json_buff, size_t indent = 0) + { + portable_storage ps; + str_in.store(ps); + ps.dump_as_json(json_buff, indent); + return true; + } + //----------------------------------------------------------------------------------------------------------- + template + std::string store_t_to_json(t_struct& str_in, size_t indent = 0) + { + std::string json_buff; + store_t_to_json(str_in, json_buff, indent); + return std::move(json_buff); + } + //----------------------------------------------------------------------------------------------------------- + template + bool store_t_to_json_file(t_struct& str_in, const std::string& fpath) + { + std::string json_buff; + store_t_to_json(str_in, json_buff); + return file_io_utils::save_string_to_file(fpath, json_buff); + } + //----------------------------------------------------------------------------------------------------------- + template + bool load_t_from_binary(t_struct& out, const std::string& binary_buff) + { + portable_storage ps; + bool rs = ps.load_from_binary(binary_buff); + if(!rs) + return false; + + return out.load(ps); + } + //----------------------------------------------------------------------------------------------------------- + template + bool load_t_from_binary_file(t_struct& out, const std::string& binary_file) + { + std::string f_buff; + if(!file_io_utils::load_file_to_string(binary_file, f_buff)) + return false; + + return load_t_from_binary(out, f_buff); + } + //----------------------------------------------------------------------------------------------------------- + template + bool store_t_to_binary(t_struct& str_in, std::string& binary_buff, size_t indent = 0) + { + portable_storage ps; + str_in.store(ps); + return ps.store_to_binary(binary_buff); + } + //----------------------------------------------------------------------------------------------------------- + template + std::string store_t_to_binary(t_struct& str_in, size_t indent = 0) + { + std::string binary_buff; + store_t_to_binary(str_in, binary_buff, indent); + return std::move(binary_buff); + } + } +} \ No newline at end of file diff --git a/contrib/epee/include/storages/portable_storage_to_bin.h b/contrib/epee/include/storages/portable_storage_to_bin.h new file mode 100644 index 000000000..baf90290a --- /dev/null +++ b/contrib/epee/include/storages/portable_storage_to_bin.h @@ -0,0 +1,212 @@ +// 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. +// + + + +#pragma once + +#include "misc_language.h" +#include "portable_storage_base.h" + +namespace epee +{ + namespace serialization + { + + template + size_t pack_varint_t(t_stream& strm, uint8_t type_or, size_t& pv) + { + pack_value v = (*((pack_value*)&pv)) << 2; + v |= type_or; + strm.write((const char*)&v, sizeof(pack_value)); + return sizeof(pack_value); + } + + PRAGMA_WARNING_PUSH + PRAGMA_GCC("GCC diagnostic ignored \"-Wstrict-aliasing\"") + template + size_t pack_varint(t_stream& strm, size_t val) + { //the first two bits always reserved for size information + + if(val <= 63) + {//mean enough one byte + return pack_varint_t(strm, PORTABLE_RAW_SIZE_MARK_BYTE, val); + } + else if(val <= 16383) + {//mean need word + return pack_varint_t(strm, PORTABLE_RAW_SIZE_MARK_WORD, val); + }else if(val <= 1073741823) + {//mean need dword + return pack_varint_t(strm, PORTABLE_RAW_SIZE_MARK_DWORD, val); + }else + { + CHECK_AND_ASSERT_THROW_MES(val <= 4611686018427387903, "failed to pack varint - too big amount = " << val); + return pack_varint_t(strm, PORTABLE_RAW_SIZE_MARK_INT64, val); + } + } + PRAGMA_WARNING_POP + + template + bool put_string(t_stream& strm, const std::string& v) + { + pack_varint(strm, v.size()); + if(v.size()) + strm.write((const char*)v.data(), v.size()); + return true; + } + + template + struct array_entry_store_visitor: public boost::static_visitor + { + t_stream& m_strm; + + template + bool pack_pod_array_type(uint8_t contained_type, const array_entry_t& arr_pod) + { + uint8_t type = contained_type|SERIALIZE_FLAG_ARRAY; + m_strm.write((const char*)&type, 1); + pack_varint(m_strm, arr_pod.m_array.size()); + for(const t_pod_type& x: arr_pod.m_array) + m_strm.write((const char*)&x, sizeof(t_pod_type)); + return true; + } + + array_entry_store_visitor(t_stream& strm):m_strm(strm){} + bool operator()(const array_entry_t& v){ return pack_pod_array_type(SERIALIZE_TYPE_UINT64, v);} + bool operator()(const array_entry_t& v){ return pack_pod_array_type(SERIALIZE_TYPE_UINT32, v);} + bool operator()(const array_entry_t& v){ return pack_pod_array_type(SERIALIZE_TYPE_UINT16, v);} + bool operator()(const array_entry_t& v) { return pack_pod_array_type(SERIALIZE_TYPE_UINT8, v);} + bool operator()(const array_entry_t& v) { return pack_pod_array_type(SERIALIZE_TYPE_INT64, v);} + bool operator()(const array_entry_t& v) { return pack_pod_array_type(SERIALIZE_TYPE_INT32, v);} + bool operator()(const array_entry_t& v) { return pack_pod_array_type(SERIALIZE_TYPE_INT16, v);} + bool operator()(const array_entry_t& v) { return pack_pod_array_type(SERIALIZE_TYPE_INT8, v);} + bool operator()(const array_entry_t& v) { return pack_pod_array_type(SERIALIZE_TYPE_DUOBLE, v);} + bool operator()(const array_entry_t& v) { return pack_pod_array_type(SERIALIZE_TYPE_BOOL, v);} + bool operator()(const array_entry_t& arr_str) + { + uint8_t type = SERIALIZE_TYPE_STRING|SERIALIZE_FLAG_ARRAY; + m_strm.write((const char*)&type, 1); + pack_varint(m_strm, arr_str.m_array.size()); + for(const std::string& s: arr_str.m_array) + put_string(m_strm, s); + return true; + } + bool operator()(const array_entry_t
& arr_sec) + { + uint8_t type = SERIALIZE_TYPE_OBJECT|SERIALIZE_FLAG_ARRAY; + m_strm.write((const char*)&type, 1); + pack_varint(m_strm, arr_sec.m_array.size()); + for(const section& s: arr_sec.m_array) + pack_entry_to_buff(m_strm, s); + return true; + } + bool operator()(const array_entry_t& arra_ar) + { + uint8_t type = SERIALIZE_TYPE_ARRAY|SERIALIZE_FLAG_ARRAY; + m_strm.write((const char*)&type, 1); + pack_varint(m_strm, arra_ar.m_array.size()); + for(const array_entry& s: arra_ar.m_array) + pack_entry_to_buff(m_strm, s); + return true; + } + }; + + template + struct storage_entry_store_visitor: public boost::static_visitor + { + t_stream& m_strm; + storage_entry_store_visitor(t_stream& strm):m_strm(strm){} + template + bool pack_pod_type(uint8_t type, const pod_type& v) + { + m_strm.write((const char*)&type, 1); + m_strm.write((const char*)&v, sizeof(pod_type)); + return true; + } + //section, array_entry + bool operator()(const uint64_t& v){ return pack_pod_type(SERIALIZE_TYPE_UINT64, v);} + bool operator()(const uint32_t& v){ return pack_pod_type(SERIALIZE_TYPE_UINT32, v);} + bool operator()(const uint16_t& v){ return pack_pod_type(SERIALIZE_TYPE_UINT16, v);} + bool operator()(const uint8_t& v) { return pack_pod_type(SERIALIZE_TYPE_UINT8, v);} + bool operator()(const int64_t& v) { return pack_pod_type(SERIALIZE_TYPE_INT64, v);} + bool operator()(const int32_t& v) { return pack_pod_type(SERIALIZE_TYPE_INT32, v);} + bool operator()(const int16_t& v) { return pack_pod_type(SERIALIZE_TYPE_INT16, v);} + bool operator()(const int8_t& v) { return pack_pod_type(SERIALIZE_TYPE_INT8, v);} + bool operator()(const double& v) { return pack_pod_type(SERIALIZE_TYPE_DUOBLE, v);} + bool operator()(const bool& v) { return pack_pod_type(SERIALIZE_TYPE_BOOL, v);} + bool operator()(const std::string& v) + { + uint8_t type = SERIALIZE_TYPE_STRING; + m_strm.write((const char*)&type, 1); + put_string(m_strm, v); + return true; + } + bool operator()(const section& v) + { + uint8_t type = SERIALIZE_TYPE_OBJECT; + m_strm.write((const char*)&type, 1); + return pack_entry_to_buff(m_strm, v); + } + + bool operator()(const array_entry& v) + { + //uint8_t type = SERIALIZE_TYPE_ARRAY; + //m_strm.write((const char*)&type, 1); + return pack_entry_to_buff(m_strm, v); + } + }; + + template + bool pack_entry_to_buff(t_stream& strm, const array_entry& ae) + { + array_entry_store_visitor aesv(strm); + return boost::apply_visitor(aesv, ae); + } + + template + bool pack_entry_to_buff(t_stream& strm, const storage_entry& se) + { + storage_entry_store_visitor sv(strm); + return boost::apply_visitor(sv, se); + } + + template + bool pack_entry_to_buff(t_stream& strm, const section& sec) + { + typedef std::map::value_type section_pair; + pack_varint(strm, sec.m_entries.size()); + for(const section_pair& se: sec.m_entries) + { + CHECK_AND_ASSERT_THROW_MES(se.first.size() < std::numeric_limits::max(), "storage_entry_name is too long: " << se.first.size() << ", val: " << se.first); + uint8_t len = static_cast(se.first.size()); + strm.write((const char*)&len, sizeof(len)); + strm.write(se.first.data(), size_t(len)); + pack_entry_to_buff(strm, se.second); + } + return true; + } + } +} \ No newline at end of file diff --git a/contrib/epee/include/storages/portable_storage_to_json.h b/contrib/epee/include/storages/portable_storage_to_json.h new file mode 100644 index 000000000..aff85b201 --- /dev/null +++ b/contrib/epee/include/storages/portable_storage_to_json.h @@ -0,0 +1,172 @@ +// 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. +// + + + +#pragma once + +#include "misc_language.h" +#include "portable_storage_base.h" + +namespace epee +{ + namespace serialization + { + + template + void dump_as_json(t_stream& strm, const array_entry& ae, size_t indent); + template + void dump_as_json(t_stream& strm, const storage_entry& se, size_t indent); + template + void dump_as_json(t_stream& strm, const std::string& v, size_t indent); + template + void dump_as_json(t_stream& strm, const int8_t& v, size_t indent); + template + void dump_as_json(t_stream& strm, const uint8_t& v, size_t indent); + template + void dump_as_json(t_stream& strm, const bool& v, size_t indent); + template + void dump_as_json(t_stream& strm, const t_type& v, size_t indent); + template + void dump_as_json(t_stream& strm, const section& sec, size_t indent); + + + inline std::string make_indent(size_t indent) + { + return std::string(indent*2, ' '); + } + + template + struct array_entry_store_to_json_visitor: public boost::static_visitor + { + t_stream& m_strm; + size_t m_indent; + array_entry_store_to_json_visitor(t_stream& strm, size_t indent):m_strm(strm), m_indent(indent){} + + template + void operator()(const array_entry_t& a) + { + m_strm << "["; + if(a.m_array.size()) + { + auto last_it = --a.m_array.end(); + for(auto it = a.m_array.begin(); it != a.m_array.end(); it++) + { + dump_as_json(m_strm, *it, m_indent); + if(it != last_it) + m_strm << ","; + } + } + m_strm << "]"; + } + }; + + template + struct storage_entry_store_to_json_visitor: public boost::static_visitor + { + t_stream& m_strm; + size_t m_indent; + storage_entry_store_to_json_visitor(t_stream& strm, size_t indent):m_strm(strm), m_indent(indent) + {} + //section, array_entry + template + void operator()(const visited_type& v) + { + dump_as_json(m_strm, v, m_indent); + } + }; + + template + void dump_as_json(t_stream& strm, const array_entry& ae, size_t indent) + { + array_entry_store_to_json_visitor aesv(strm, indent); + boost::apply_visitor(aesv, ae); + } + + template + void dump_as_json(t_stream& strm, const storage_entry& se, size_t indent) + { + storage_entry_store_to_json_visitor sv(strm, indent); + boost::apply_visitor(sv, se); + } + + template + void dump_as_json(t_stream& strm, const std::string& v, size_t indent) + { + strm << "\"" << misc_utils::parse::transform_to_escape_sequence(v) << "\""; + } + + template + void dump_as_json(t_stream& strm, const int8_t& v, size_t indent) + { + strm << static_cast(v); + } + + template + void dump_as_json(t_stream& strm, const uint8_t& v, size_t indent) + { + strm << static_cast(v); + } + + template + void dump_as_json(t_stream& strm, const bool& v, size_t indent) + { + if(v) + strm << "true"; + else + strm << "false"; + } + + + + template + void dump_as_json(t_stream& strm, const t_type& v, size_t indent) + { + strm << v; + } + + template + void dump_as_json(t_stream& strm, const section& sec, size_t indent) + { + size_t local_indent = indent + 1; + strm << "{\r\n"; + std::string indent_str = make_indent(local_indent); + if(sec.m_entries.size()) + { + auto it_last = --sec.m_entries.end(); + for(auto it = sec.m_entries.begin(); it!= sec.m_entries.end();it++) + { + strm << indent_str << "\"" << misc_utils::parse::transform_to_escape_sequence(it->first) << "\"" << ": "; + dump_as_json(strm, it->second, local_indent); + if(it_last != it) + strm << ","; + strm << "\r\n"; + } + } + strm << make_indent(indent) << "}"; + } + } +} \ No newline at end of file diff --git a/contrib/epee/include/storages/portable_storage_val_converters.h b/contrib/epee/include/storages/portable_storage_val_converters.h new file mode 100644 index 000000000..6ea505886 --- /dev/null +++ b/contrib/epee/include/storages/portable_storage_val_converters.h @@ -0,0 +1,169 @@ +// 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. +// + + + +#pragma once + +#include "misc_language.h" +#include "portable_storage_base.h" +#include "warnings.h" + +namespace epee +{ + namespace serialization + { +#define ASSERT_AND_THROW_WRONG_CONVERSION() ASSERT_MES_AND_THROW("WRONG DATA CONVERSION: from type=" << typeid(from).name() << " to type " << typeid(to).name()) + + template + void convert_int_to_uint(const from_type& from, to_type& to) + { +PUSH_WARNINGS +DISABLE_VS_WARNINGS(4018) + CHECK_AND_ASSERT_THROW_MES(from >=0, "unexpected int value with signed storage value less than 0, and unsigned receiver value"); +DISABLE_GCC_AND_CLANG_WARNING(sign-compare) + CHECK_AND_ASSERT_THROW_MES(from <= std::numeric_limits::max(), "int value overhead: try to set value " << from << " to type " << typeid(to_type).name() << " with max possible value = " << std::numeric_limits::max()); + to = static_cast(from); +POP_WARNINGS + } + template + void convert_int_to_int(const from_type& from, to_type& to) + { + CHECK_AND_ASSERT_THROW_MES(from >= boost::numeric::bounds::lowest(), "int value overhead: try to set value " << from << " to type " << typeid(to_type).name() << " with lowest possible value = " << boost::numeric::bounds::lowest()); +PUSH_WARNINGS +DISABLE_CLANG_WARNING(tautological-constant-out-of-range-compare) + CHECK_AND_ASSERT_THROW_MES(from <= std::numeric_limits::max(), "int value overhead: try to set value " << from << " to type " << typeid(to_type).name() << " with max possible value = " << std::numeric_limits::max()); +POP_WARNINGS + to = static_cast(from); + } + template + void convert_uint_to_any_int(const from_type& from, to_type& to) + { +PUSH_WARNINGS +DISABLE_VS_WARNINGS(4018) +DISABLE_CLANG_WARNING(tautological-constant-out-of-range-compare) + CHECK_AND_ASSERT_THROW_MES(from <= std::numeric_limits::max(), "uint value overhead: try to set value " << from << " to type " << typeid(to_type).name() << " with max possible value = " << std::numeric_limits::max()); + to = static_cast(from); +POP_WARNINGS + } + + template //is from signed, is from to signed + struct convert_to_signed_unsigned; + + template + struct convert_to_signed_unsigned + { + static void convert(const from_type& from, to_type& to) + {//from signed to signed + convert_int_to_int(from, to); + } + }; + + template + struct convert_to_signed_unsigned + { + static void convert(const from_type& from, to_type& to) + {//from signed to unsigned + convert_int_to_uint(from, to); + } + }; + + template + struct convert_to_signed_unsigned + { + static void convert(const from_type& from, to_type& to) + {//from unsigned to signed + convert_uint_to_any_int(from, to); + } + }; + + template + struct convert_to_signed_unsigned + { + static void convert(const from_type& from, to_type& to) + { + //from unsigned to unsigned + convert_uint_to_any_int(from, to); + } + }; + + template + struct convert_to_integral; + + template + struct convert_to_integral + { + static void convert(const from_type& from, to_type& to) + { + convert_to_signed_unsigned::value, std::is_signed::value>::convert(from, to); + } + }; + + template + struct convert_to_integral + { + static void convert(const from_type& from, to_type& to) + { + ASSERT_AND_THROW_WRONG_CONVERSION(); + } + }; + + template + struct is_convertable: std::integral_constant::value && + std::is_integral::value && + !std::is_same::value && + !std::is_same::value > {}; + + template + struct convert_to_same; + + template + struct convert_to_same + { + static void convert(const from_type& from, to_type& to) + { + to = from; + } + }; + + template + struct convert_to_same + { + static void convert(const from_type& from, to_type& to) + { + convert_to_integral::value>::convert(from, to);// ASSERT_AND_THROW_WRONG_CONVERSION(); + } + }; + + + template + void convert_t(const from_type& from, to_type& to) + { + convert_to_same::value>::convert(from, to); + } + } +} \ No newline at end of file diff --git a/contrib/epee/include/string_coding.h b/contrib/epee/include/string_coding.h new file mode 100644 index 000000000..a2e3d6eb2 --- /dev/null +++ b/contrib/epee/include/string_coding.h @@ -0,0 +1,295 @@ +// 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. +// + + +#ifndef _STRING_CODING_H_ +#define _STRING_CODING_H_ + +#include +//#include "md5_l.h" +namespace epee +{ +namespace string_encoding +{ + inline std::string convert_to_ansii(const std::wstring& str_from) + { + + std::string res(str_from.begin(), str_from.end()); + return res; + /* + std::string result; + std::locale loc; + for(unsigned int i= 0; i < str_from.size(); ++i) + { + result += std::use_facet >(loc).narrow(str_from[i]); + } + return result; + */ + + //return boost::lexical_cast(str_from); + /* + std::string str_trgt; + if(!str_from.size()) + return str_trgt; + int cb = ::WideCharToMultiByte( code_page, 0, str_from.data(), (__int32)str_from.size(), 0, 0, 0, 0 ); + if(!cb) + return str_trgt; + str_trgt.resize(cb); + ::WideCharToMultiByte( code_page, 0, str_from.data(), (int)str_from.size(), + (char*)str_trgt.data(), (int)str_trgt.size(), 0, 0); + return str_trgt;*/ + } +#ifdef WINDOWS_PLATFORM_EX + inline std::string convert_to_ansii_win(const std::wstring& str_from) + { + + int code_page = CP_ACP; + std::string str_trgt; + if(!str_from.size()) + return str_trgt; + int cb = ::WideCharToMultiByte( code_page, 0, str_from.data(), (__int32)str_from.size(), 0, 0, 0, 0 ); + if(!cb) + return str_trgt; + str_trgt.resize(cb); + ::WideCharToMultiByte( code_page, 0, str_from.data(), (int)str_from.size(), + (char*)str_trgt.data(), (int)str_trgt.size(), 0, 0); + return str_trgt; + } +#endif + + inline std::string convert_to_ansii(const std::string& str_from) + { + return str_from; + } + + inline std::wstring convert_to_unicode(const std::string& str_from) + { + std::wstring result; + std::locale loc; + for(unsigned int i= 0; i < str_from.size(); ++i) + { + result += std::use_facet >(loc).widen(str_from[i]); + } + return result; + + //return boost::lexical_cast(str_from); + /* + std::wstring str_trgt; + if(!str_from.size()) + return str_trgt; + + int cb = ::MultiByteToWideChar( code_page, 0, str_from.data(), (int)str_from.size(), 0, 0 ); + if(!cb) + return str_trgt; + + str_trgt.resize(cb); + ::MultiByteToWideChar( code_page, 0, str_from.data(),(int)str_from.size(), + (wchar_t*)str_trgt.data(),(int)str_trgt.size()); + return str_trgt;*/ + } + inline std::wstring convert_to_unicode(const std::wstring& str_from) + { + return str_from; + } + + template + inline target_string convert_to_t(const std::wstring& str_from); + + template<> + inline std::string convert_to_t(const std::wstring& str_from) + { + return convert_to_ansii(str_from); + } + + template<> + inline std::wstring convert_to_t(const std::wstring& str_from) + { + return str_from; + } + + template + inline target_string convert_to_t(const std::string& str_from); + + template<> + inline std::string convert_to_t(const std::string& str_from) + { + return str_from; + } + + template<> + inline std::wstring convert_to_t(const std::string& str_from) + { + return convert_to_unicode(str_from); + } + + inline + std::string& base64_chars() + { + + static std::string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + + return chars; + + } + + inline + std::string base64_encode(unsigned char const* bytes_to_encode, size_t in_len) { + std::string ret; + int i = 0; + int j = 0; + unsigned char char_array_3[3]; + unsigned char char_array_4[4]; + + while (in_len--) { + char_array_3[i++] = *(bytes_to_encode++); + if (i == 3) { + char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; + char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); + char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); + char_array_4[3] = char_array_3[2] & 0x3f; + + for(i = 0; (i <4) ; i++) + ret += base64_chars()[char_array_4[i]]; + i = 0; + } + } + + if (i) + { + for(j = i; j < 3; j++) + char_array_3[j] = '\0'; + + char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; + char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); + char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); + char_array_4[3] = char_array_3[2] & 0x3f; + + for (j = 0; (j < i + 1); j++) + ret += base64_chars()[char_array_4[j]]; + + while((i++ < 3)) + ret += '='; + + } + + return ret; + + } + + inline + std::string base64_encode(const std::string& str) + { + return base64_encode((unsigned char const* )str.data(), str.size()); + } + + inline bool is_base64(unsigned char c) { + return (isalnum(c) || (c == '+') || (c == '/')); + } + + + inline + std::string base64_decode(std::string const& encoded_string) { + size_t in_len = encoded_string.size(); + size_t i = 0; + size_t j = 0; + size_t in_ = 0; + unsigned char char_array_4[4], char_array_3[3]; + std::string ret; + + while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) { + char_array_4[i++] = encoded_string[in_]; in_++; + if (i ==4) { + for (i = 0; i <4; i++) + char_array_4[i] = (unsigned char)base64_chars().find(char_array_4[i]); + + char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); + char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); + char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; + + for (i = 0; (i < 3); i++) + ret += char_array_3[i]; + i = 0; + } + } + + if (i) { + for (j = i; j <4; j++) + char_array_4[j] = 0; + + for (j = 0; j <4; j++) + char_array_4[j] = (unsigned char)base64_chars().find(char_array_4[j]); + + char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); + char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); + char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; + + for (j = 0; (j < i - 1); j++) ret += char_array_3[j]; + } + + return ret; + } + + //md5 +#ifdef MD5_H + inline + std::string get_buf_as_hex_string(const void* pbuf, size_t len) + { + std::ostringstream result; + + const unsigned char* p_buff = (const unsigned char*)pbuf; + + for(unsigned int i=0;i(len), output); + return get_buf_as_hex_string(output, sizeof(output)); + } + + inline + std::string get_md5_as_hexstring(const std::string& src) + { + return get_md5_as_hexstring(src.data(), src.size()); + } +#endif + + +} +} + +#endif //_STRING_CODING_H_ diff --git a/contrib/epee/include/string_tools.h b/contrib/epee/include/string_tools.h new file mode 100644 index 000000000..53be7c3ae --- /dev/null +++ b/contrib/epee/include/string_tools.h @@ -0,0 +1,736 @@ +// 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. +// + + + +#ifndef _STRING_TOOLS_H_ +#define _STRING_TOOLS_H_ + +//#include +#include +#include +//#include +#include +#include +#include +#include +#include +#include +#include "warnings.h" + + +#ifndef OUT + #define OUT +#endif + +#ifdef WINDOWS_PLATFORM +#pragma comment (lib, "Rpcrt4.lib") +#endif + +namespace epee +{ +namespace string_tools +{ + inline std::wstring get_str_from_guid(const boost::uuids::uuid& rid) + { + return boost::lexical_cast(rid); + } + //---------------------------------------------------------------------------- + inline std::string get_str_from_guid_a(const boost::uuids::uuid& rid) + { + return boost::lexical_cast(rid); + } + //---------------------------------------------------------------------------- + inline bool get_guid_from_string( boost::uuids::uuid& inetifer, std::wstring str_id) + { + if(str_id.size() < 36) + return false; + + if('{' == *str_id.begin()) + str_id.erase(0, 1); + + if('}' == *(--str_id.end())) + str_id.erase(--str_id.end()); + + try + { + inetifer = boost::lexical_cast(str_id); + return true; + } + catch(...) + { + return false; + } + } + //---------------------------------------------------------------------------- + inline bool get_guid_from_string(OUT boost::uuids::uuid& inetifer, const std::string& str_id) + { + std::string local_str_id = str_id; + if(local_str_id.size() < 36) + return false; + + if('{' == *local_str_id.begin()) + local_str_id.erase(0, 1); + + if('}' == *(--local_str_id.end())) + local_str_id.erase(--local_str_id.end()); + + try + { + inetifer = boost::lexical_cast(local_str_id); + return true; + } + catch(...) + { + return false; + } + } + //---------------------------------------------------------------------------- + template + std::basic_string buff_to_hex(const std::basic_string& s) + { + using namespace std; + basic_stringstream hexStream; + hexStream << hex << noshowbase << setw(2); + + for(typename std::basic_string::const_iterator it = s.begin(); it != s.end(); it++) + { + hexStream << "0x"<< static_cast(static_cast(*it)) << " "; + } + return hexStream.str(); + } + //---------------------------------------------------------------------------- + template + std::basic_string buff_to_hex_nodelimer(const std::basic_string& s) + { + using namespace std; + basic_stringstream hexStream; + hexStream << hex << noshowbase; + + for(typename std::basic_string::const_iterator it = s.begin(); it != s.end(); it++) + { + hexStream << setw(2) << setfill('0') << static_cast(static_cast(*it)); + } + return hexStream.str(); + } + //---------------------------------------------------------------------------- + template + bool parse_hexstr_to_binbuff(const std::basic_string& s, std::basic_string& res) + { + res.clear(); + try + { + long v = 0; + for(size_t i = 0; i < (s.size() + 1) / 2; i++) + { + CharT byte_str[3]; + size_t copied = s.copy(byte_str, 2, 2 * i); + byte_str[copied] = CharT(0); + CharT* endptr; + v = strtoul(byte_str, &endptr, 16); + if (v < 0 || 0xFF < v || endptr != byte_str + copied) + { + return false; + } + res.push_back(static_cast(v)); + } + + return true; + }catch(...) + { + return false; + } + } + //---------------------------------------------------------------------------- + template + bool parse_tpod_from_hex_string(const std::string& str_hash, t_pod_type& t_pod) + { + std::string buf; + bool res = epee::string_tools::parse_hexstr_to_binbuff(str_hash, buf); + if (!res || buf.size() != sizeof(t_pod_type)) + { + return false; + } + else + { + buf.copy(reinterpret_cast(&t_pod), sizeof(t_pod_type)); + return true; + } + } + //---------------------------------------------------------------------------- +PUSH_WARNINGS +DISABLE_GCC_WARNING(maybe-uninitialized) + template + inline bool get_xtype_from_string(OUT XType& val, const std::string& str_id) + { + try + { + val = boost::lexical_cast(str_id); + return true; + } + catch(std::exception& /*e*/) + { + //const char* pmsg = e.what(); + return false; + } + + catch(...) + { + return false; + } + + return true; + } +POP_WARNINGS + //--------------------------------------------------- + template + bool get_xnum_from_hex_string(const std::string str, int_t& res ) + { + try + { + std::stringstream ss; + ss << std::hex << str; + ss >> res; + return true; + } + catch(...) + { + return false; + } + } + //---------------------------------------------------------------------------- + template + inline bool xtype_to_string(const XType& val, std::string& str) + { + try + { + str = boost::lexical_cast(val); + } + catch(...) + { + return false; + } + + return true; + } + + typedef std::map command_line_params_a; + typedef std::map command_line_params_w; + + template + bool parse_commandline(std::map& res, int argc, char** argv) + { + t_string key; + for(int i = 1; i < argc; i++) + { + if(!argv[i]) + break; + t_string s = argv[i]; + std::string::size_type p = s.find('='); + if(std::string::npos == p) + { + res[s] = ""; + }else + { + std::string ss; + t_string nm = s.substr(0, p); + t_string vl = s.substr(p+1, s.size()); + res[nm] = vl; + } + } + return true; + } + +/* template + bool get_xparam_from_command_line(const std::map& res, const std::basic_string & key, t_type& val) + { + + } + */ + + template + bool get_xparam_from_command_line(const std::map& res, const t_string & key, t_type& val) + { + typename std::map::const_iterator it = res.find(key); + if(it == res.end()) + return false; + + if(it->second.size()) + { + return get_xtype_from_string(val, it->second); + } + + return true; + } + + template + t_type get_xparam_from_command_line(const std::map& res, const t_string & key, const t_type& default_value) + { + typename std::map::const_iterator it = res.find(key); + if(it == res.end()) + return default_value; + + if(it->second.size()) + { + t_type s; + get_xtype_from_string(s, it->second); + return s; + } + + return default_value; + } + + template + bool have_in_command_line(const std::map& res, const std::basic_string& key) + { + typename std::map::const_iterator it = res.find(key); + if(it == res.end()) + return false; + + return true; + } + + //---------------------------------------------------------------------------- +//#ifdef _WINSOCK2API_ + inline std::string get_ip_string_from_int32(boost::uint32_t ip) + { + in_addr adr; + adr.s_addr = ip; + const char* pbuf = inet_ntoa(adr); + if(pbuf) + return pbuf; + else + return "[failed]"; + } + //---------------------------------------------------------------------------- + inline bool get_ip_int32_from_string(boost::uint32_t& ip, const std::string& ip_str) + { + ip = inet_addr(ip_str.c_str()); + if(INADDR_NONE == ip) + return false; + + return true; + } + //---------------------------------------------------------------------------- + inline bool parse_peer_from_string(uint32_t& ip, uint32_t& port, const std::string& addres) + { + //parse ip and address + std::string::size_type p = addres.find(':'); + if(p == std::string::npos) + { + return false; + } + std::string ip_str = addres.substr(0, p); + std::string port_str = addres.substr(p+1, addres.size()); + + if(!get_ip_int32_from_string(ip, ip_str)) + { + return false; + } + + if(!get_xtype_from_string(port, port_str)) + { + return false; + } + return true; + } + +//#endif + //---------------------------------------------------------------------------- + template + inline std::string get_t_as_hex_nwidth(const t& v, std::streamsize w = 8) + { + std::stringstream ss; + ss << std::setfill ('0') << std::setw (w) << std::hex << std::noshowbase; + ss << v; + return ss.str(); + } + + inline std::string num_to_string_fast(boost::int64_t val) + { + /* + char buff[30] = {0}; + i64toa_s(val, buff, sizeof(buff)-1, 10); + return buff;*/ + return boost::lexical_cast(val); + } + //---------------------------------------------------------------------------- + inline bool string_to_num_fast(const std::string& buff, boost::int64_t& val) + { + //return get_xtype_from_string(val, buff); +#if (defined _MSC_VER) + val = _atoi64(buff.c_str()); +#else + val = atoll(buff.c_str()); +#endif + /* + * val = atoi64(buff.c_str()); + */ + if(buff != "0" && val == 0) + return false; + return true; + } + //---------------------------------------------------------------------------- + inline bool string_to_num_fast(const std::string& buff, int& val) + { + val = atoi(buff.c_str()); + if(buff != "0" && val == 0) + return false; + + return true; + } + //---------------------------------------------------------------------------- +#ifdef WINDOWS_PLATFORM + inline std::string system_time_to_string(const SYSTEMTIME& st) + { + + /* + TIME_ZONE_INFORMATION tzi; + GetTimeZoneInformation(&tzi); + SystemTimeToTzSpecificLocalTime(&tzi, &stUTC, &stLocal); + */ + + char szTime[25], szDate[25]; + ::GetTimeFormatA( + LOCALE_USER_DEFAULT, // locale + TIME_FORCE24HOURFORMAT, // options + &st, // time + NULL, // time format string + szTime, // formatted string buffer + 25 // size of string buffer + ); + + ::GetDateFormatA( + LOCALE_USER_DEFAULT, // locale + NULL, // options + &st, // date + NULL, // date format + szDate, // formatted string buffer + 25 // size of buffer + ); + szTime[24] = szDate[24] = 0; //be happy :) + + std::string res = szDate; + (res += " " )+= szTime; + return res; + + } +#endif + //---------------------------------------------------------------------------- + + inline bool compare_no_case(const std::string& str1, const std::string& str2) + { + + return !boost::iequals(str1, str2); + } + //---------------------------------------------------------------------------- + inline bool compare_no_case(const std::wstring& str1, const std::wstring& str2) + { + return !boost::iequals(str1, str2); + } + //---------------------------------------------------------------------------- + inline bool is_match_prefix(const std::wstring& str1, const std::wstring& prefix) + { + if(prefix.size()>str1.size()) + return false; + + if(!compare_no_case(str1.substr(0, prefix.size()), prefix)) + return true; + else + return false; + } + //---------------------------------------------------------------------------- + inline bool is_match_prefix(const std::string& str1, const std::string& prefix) + { + if(prefix.size()>str1.size()) + return false; + + if(!compare_no_case(str1.substr(0, prefix.size()), prefix)) + return true; + else + return false; + } + //---------------------------------------------------------------------------- + inline std::string& get_current_module_name() + { + static std::string module_name; + return module_name; + } + //---------------------------------------------------------------------------- + inline std::string& get_current_module_folder() + { + static std::string module_folder; + return module_folder; + } + //---------------------------------------------------------------------------- +#ifdef _WIN32 + inline std::string get_current_module_path() + { + char pname [5000] = {0}; + GetModuleFileNameA( NULL, pname, sizeof(pname)); + pname[sizeof(pname)-1] = 0; //be happy ;) + return pname; + } +#endif + //---------------------------------------------------------------------------- + inline bool set_module_name_and_folder(const std::string& path_to_process_) + { + std::string path_to_process = path_to_process_; +#ifdef _WIN32 + path_to_process = get_current_module_path(); +#endif + std::string::size_type a = path_to_process.rfind( '\\' ); + if(a == std::string::npos ) + { + a = path_to_process.rfind( '/' ); + } + if ( a != std::string::npos ) + { + get_current_module_name() = path_to_process.substr(a+1, path_to_process.size()); + get_current_module_folder() = path_to_process.substr(0, a); + return true; + }else + return false; + + } + + //---------------------------------------------------------------------------- + inline bool trim_left(std::string& str) + { + for(std::string::iterator it = str.begin(); it!= str.end() && isspace(static_cast(*it));) + str.erase(str.begin()); + + return true; + } + //---------------------------------------------------------------------------- + inline bool trim_right(std::string& str) + { + + for(std::string::reverse_iterator it = str.rbegin(); it!= str.rend() && isspace(static_cast(*it));) + str.erase( --((it++).base())); + + return true; + } + //---------------------------------------------------------------------------- + inline std::string& trim(std::string& str) + { + + trim_left(str); + trim_right(str); + return str; + } + //---------------------------------------------------------------------------- + inline std::string trim(const std::string& str_) + { + std::string str = str_; + trim_left(str); + trim_right(str); + return str; + } + //---------------------------------------------------------------------------- + template + std::string pod_to_hex(const t_pod_type& s) + { + std::string buff; + buff.assign(reinterpret_cast(&s), sizeof(s)); + return buff_to_hex_nodelimer(buff); + } + //---------------------------------------------------------------------------- + template + bool hex_to_pod(const std::string& hex_str, t_pod_type& s) + { + std::string hex_str_tr = trim(hex_str); + if(sizeof(s)*2 != hex_str.size()) + return false; + std::string bin_buff; + if(!parse_hexstr_to_binbuff(hex_str_tr, bin_buff)) + return false; + if(bin_buff.size()!=sizeof(s)) + return false; + + s = *(t_pod_type*)bin_buff.data(); + return true; + } + //---------------------------------------------------------------------------- + inline std::string get_extension(const std::string& str) + { + std::string res; + std::string::size_type pos = str.rfind('.'); + if(std::string::npos == pos) + return res; + + res = str.substr(pos+1, str.size()-pos); + return res; + } + //---------------------------------------------------------------------------- + inline std::string get_filename_from_path(const std::string& str) + { + std::string res; + std::string::size_type pos = str.rfind('\\'); + if(std::string::npos == pos) + return str; + + res = str.substr(pos+1, str.size()-pos); + return res; + } + //---------------------------------------------------------------------------- + + + + inline std::string cut_off_extension(const std::string& str) + { + std::string res; + std::string::size_type pos = str.rfind('.'); + if(std::string::npos == pos) + return str; + + res = str.substr(0, pos); + return res; + } + + //---------------------------------------------------------------------------- +#ifdef _WININET_ + inline std::string get_string_from_systemtime(const SYSTEMTIME& sys_time) + { + std::string result_string; + + char buff[100] = {0}; + BOOL res = ::InternetTimeFromSystemTimeA(&sys_time, INTERNET_RFC1123_FORMAT, buff, 99); + if(!res) + { + LOG_ERROR("Failed to load SytemTime to string"); + } + + result_string = buff; + return result_string; + + } + //------------------------------------------------------------------------------------- + inline SYSTEMTIME get_systemtime_from_string(const std::string& buff) + { + SYSTEMTIME result_time = {0}; + + BOOL res = ::InternetTimeToSystemTimeA(buff.c_str(), &result_time, NULL); + if(!res) + { + LOG_ERROR("Failed to load SytemTime from string " << buff << "interval set to 15 minutes"); + } + + return result_time; + } +#endif + +#ifdef WINDOWS_PLATFORM + static const DWORD INFO_BUFFER_SIZE = 10000; + + static const wchar_t* get_pc_name() + { + static wchar_t info[INFO_BUFFER_SIZE]; + static DWORD bufCharCount = INFO_BUFFER_SIZE; + static bool init = false; + + if (!init) { + if (!GetComputerNameW( info, &bufCharCount )) + info[0] = 0; + else + init = true; + } + + return info; + } + + static const wchar_t* get_user_name() + { + static wchar_t info[INFO_BUFFER_SIZE]; + static DWORD bufCharCount = INFO_BUFFER_SIZE; + static bool init = false; + + if (!init) { + if (!GetUserNameW( info, &bufCharCount )) + info[0] = 0; + else + init = true; + } + + return info; + } +#endif + +#ifdef _LM_ + static const wchar_t* get_domain_name() + { + static wchar_t info[INFO_BUFFER_SIZE]; + static DWORD bufCharCount = 0; + static bool init = false; + + if (!init) { + LPWSTR domain( NULL ); + NETSETUP_JOIN_STATUS status; + info[0] = 0; + + if (NET_API_STATUS result = NetGetJoinInformation( NULL, &domain, &status )) + { + LOG_ERROR("get_domain_name error: " << log_space::get_win32_err_descr(result)); + } else + { + StringCchCopyW( info, sizeof(info)/sizeof( info[0] ), domain ); + NetApiBufferFree((void*)domain); + init = true; + } + } + + return info; + } +#endif +#ifdef WINDOWS_PLATFORM + inline + std::string load_resource_string_a(int id, const char* pmodule_name = NULL) + { + //slow realization + HMODULE h = ::GetModuleHandleA( pmodule_name ); + + char buff[2000] = {0}; + + ::LoadStringA( h, id, buff, sizeof(buff)); + buff[sizeof(buff)-1] = 0; //be happy :) + return buff; + } + inline + std::wstring load_resource_string_w(int id, const char* pmodule_name = NULL) + { + //slow realization + HMODULE h = ::GetModuleHandleA( pmodule_name ); + + wchar_t buff[2000] = {0}; + + ::LoadStringW( h, id, buff, sizeof(buff) / sizeof( buff[0] ) ); + buff[(sizeof(buff)/sizeof(buff[0]))-1] = 0; //be happy :) + return buff; + } +#endif +} +} +#endif //_STRING_TOOLS_H_ diff --git a/contrib/epee/include/syncobj.h b/contrib/epee/include/syncobj.h new file mode 100644 index 000000000..ca7514ede --- /dev/null +++ b/contrib/epee/include/syncobj.h @@ -0,0 +1,241 @@ +// 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. +// + + + + +#ifndef __WINH_OBJ_H__ +#define __WINH_OBJ_H__ + +#include +#include +#include +#include +#include + +namespace epee +{ + + struct simple_event + { + simple_event() + { + rised = false; + } + std::mutex m_mx; + std::condition_variable m_cond_var; + bool rised; + + void rise() + { + std::unique_lock lock(m_mx); + rised = true; + m_cond_var.notify_one(); + } + + void wait() + { + std::unique_lock lock(m_mx); + while (!rised) + m_cond_var.wait(lock); + } + }; + + class critical_region; + + class critical_section + { + boost::recursive_mutex m_section; + + public: + //to make copy fake! + critical_section(const critical_section& section) + { + } + + critical_section() + { + } + + ~critical_section() + { + } + + void lock() + { + m_section.lock(); + //EnterCriticalSection( &m_section ); + } + + void unlock() + { + m_section.unlock(); + } + + bool tryLock() + { + return m_section.try_lock(); + } + + // to make copy fake + critical_section& operator=(const critical_section& section) + { + return *this; + } + }; + + + template + class critical_region_t + { + t_lock& m_locker; + bool m_unlocked; + + critical_region_t(const critical_region_t&) {} + + public: + critical_region_t(t_lock& cs): m_locker(cs), m_unlocked(false) + { + m_locker.lock(); + } + + ~critical_region_t() + { + unlock(); + } + + void unlock() + { + if (!m_unlocked) + { + m_locker.unlock(); + m_unlocked = true; + } + } + }; + + +#if defined(WINDWOS_PLATFORM) + class shared_critical_section + { + public: + shared_critical_section() + { + ::InitializeSRWLock(&m_srw_lock); + } + ~shared_critical_section() + {} + + bool lock_shared() + { + AcquireSRWLockShared(&m_srw_lock); + return true; + } + bool unlock_shared() + { + ReleaseSRWLockShared(&m_srw_lock); + return true; + } + bool lock_exclusive() + { + ::AcquireSRWLockExclusive(&m_srw_lock); + return true; + } + bool unlock_exclusive() + { + ::ReleaseSRWLockExclusive(&m_srw_lock); + return true; + } + private: + SRWLOCK m_srw_lock; + }; + + + class shared_guard + { + public: + shared_guard(shared_critical_section& ref_sec):m_ref_sec(ref_sec) + { + m_ref_sec.lock_shared(); + } + + ~shared_guard() + { + m_ref_sec.unlock_shared(); + } + + private: + shared_critical_section& m_ref_sec; + }; + + + class exclusive_guard + { + public: + exclusive_guard(shared_critical_section& ref_sec):m_ref_sec(ref_sec) + { + m_ref_sec.lock_exclusive(); + } + + ~exclusive_guard() + { + m_ref_sec.unlock_exclusive(); + } + + private: + shared_critical_section& m_ref_sec; + }; +#endif + +#define SHARED_CRITICAL_REGION_BEGIN(x) { shared_guard critical_region_var(x) +#define EXCLUSIVE_CRITICAL_REGION_BEGIN(x) { exclusive_guard critical_region_var(x) + +#define CRITICAL_REGION_LOCAL(x) epee::critical_region_t critical_region_var(x) +#define CRITICAL_REGION_BEGIN(x) { epee::critical_region_t critical_region_var(x) +#define CRITICAL_REGION_LOCAL1(x) epee::critical_region_t critical_region_var1(x) +#define CRITICAL_REGION_BEGIN1(x) { epee::critical_region_t critical_region_var1(x) + +#define CRITICAL_REGION_END() } + + +#if defined(WINDWOS_PLATFORM) + inline const char* get_wait_for_result_as_text(DWORD res) + { + switch(res) + { + case WAIT_ABANDONED: return "WAIT_ABANDONED"; + case WAIT_TIMEOUT: return "WAIT_TIMEOUT"; + case WAIT_OBJECT_0: return "WAIT_OBJECT_0"; + case WAIT_OBJECT_0+1: return "WAIT_OBJECT_1"; + case WAIT_OBJECT_0+2: return "WAIT_OBJECT_2"; + default: return "UNKNOWN CODE"; + } + } +#endif + +} + +#endif diff --git a/contrib/epee/include/time_helper.h b/contrib/epee/include/time_helper.h new file mode 100644 index 000000000..958176da6 --- /dev/null +++ b/contrib/epee/include/time_helper.h @@ -0,0 +1,159 @@ +// 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. +// + + + +#pragma once + +//#include +//#include +#include +#include +#include "pragma_comp_defs.h" + +namespace epee +{ +namespace misc_utils +{ + +#ifdef __ATLTIME_H__ + + inline + bool get_time_t_from_ole_date(DATE src, time_t& res) + { + SYSTEMTIME st = {0}; + if(TRUE != ::VariantTimeToSystemTime(src, &st)) + return false; + ATL::CTime ss(st); + res = ss.GetTime(); + return true; + } +#endif + inline + std::string get_time_str(const time_t& time_) + { + + + char tmpbuf[200] = {0}; + tm* pt = NULL; +PRAGMA_WARNING_PUSH +PRAGMA_WARNING_DISABLE_VS(4996) + pt = localtime(&time_); +PRAGMA_WARNING_POP + + if(pt) + strftime( tmpbuf, 199, "%d.%m.%Y %H:%M:%S", pt ); + else + { + std::stringstream strs; + strs << "[wrong_time: " << std::hex << time_ << "]"; + return strs.str(); + } + return tmpbuf; + } + + inline + std::string get_time_str_v2(const time_t& time_) + { + + char tmpbuf[200] = {0}; + tm* pt = NULL; +PRAGMA_WARNING_PUSH +PRAGMA_WARNING_DISABLE_VS(4996) + pt = localtime(&time_); +PRAGMA_WARNING_POP + + if(pt) + strftime( tmpbuf, 199, "%Y_%m_%d %H_%M_%S", pt ); + else + { + std::stringstream strs; + strs << "[wrong_time: " << std::hex << time_ << "]"; + return strs.str(); + } + return tmpbuf; + } + + inline + std::string get_time_str_v3(const boost::posix_time::ptime& time_) + { + return boost::posix_time::to_simple_string(time_); + } + + + + inline std::string get_internet_time_str(const time_t& time_) + { + char tmpbuf[200] = {0}; + tm* pt = NULL; +PRAGMA_WARNING_PUSH +PRAGMA_WARNING_DISABLE_VS(4996) + pt = gmtime(&time_); +PRAGMA_WARNING_POP + strftime( tmpbuf, 199, "%a, %d %b %Y %H:%M:%S GMT", pt ); + return tmpbuf; + } + + inline std::string get_time_interval_string(const time_t& time_) + { + std::string res; + time_t tail = time_; +PRAGMA_WARNING_PUSH +PRAGMA_WARNING_DISABLE_VS(4244) + int days = tail/(60*60*24); + tail = tail%(60*60*24); + int hours = tail/(60*60); + tail = tail%(60*60); + int minutes = tail/(60); + tail = tail%(60); + int seconds = tail; +PRAGMA_WARNING_POP + res = std::string() + "d" + boost::lexical_cast(days) + ".h" + boost::lexical_cast(hours) + ".m" + boost::lexical_cast(minutes) + ".s" + boost::lexical_cast(seconds); + return res; + } + +#ifdef __SQLEXT + inline + bool odbc_time_to_oledb_taime(const SQL_TIMESTAMP_STRUCT& odbc_timestamp, DATE& oledb_date) + { + + SYSTEMTIME st = {0}; + st.wYear = odbc_timestamp.year; + st.wDay = odbc_timestamp.day; + st.wHour = odbc_timestamp.hour ; + st.wMilliseconds = (WORD)odbc_timestamp.fraction ; + st.wMinute = odbc_timestamp.minute ; + st.wMonth = odbc_timestamp.month ; + st.wSecond = odbc_timestamp.second ; + + if(TRUE != ::SystemTimeToVariantTime(&st, &oledb_date)) + return false; + return true; + } + +#endif +} +} \ No newline at end of file diff --git a/contrib/epee/include/tiny_ini.h b/contrib/epee/include/tiny_ini.h new file mode 100644 index 000000000..2bc71fc1a --- /dev/null +++ b/contrib/epee/include/tiny_ini.h @@ -0,0 +1,75 @@ +// 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. +// + + +#ifndef _TINY_INI_H_ +#define _TINY_INI_H_ + +#include +#include +#include "string_tools.h" + +namespace epee +{ +namespace tiny_ini +{ + + inline + bool get_param_value(const std::string& param_name, const std::string& ini_entry, std::string& res) + { + std::string expr_str = std::string() + "^("+ param_name +") *=(.*?)$"; + const boost::regex match_ini_entry( expr_str, boost::regex::icase | boost::regex::normal); + boost::smatch result; + if(!boost::regex_search(ini_entry, result, match_ini_entry, boost::match_default)) + return false; + res = result[2]; + string_tools::trim(res); + return true; + } + inline + std::string get_param_value(const std::string& param_name, const std::string& ini_entry) + { + std::string buff; + get_param_value(param_name, ini_entry, buff); + return buff; + } + + template + bool get_param_value_as_t(const std::string& param_name, const std::string& ini_entry, T& res) + { + std::string str_res = get_param_value(param_name, ini_entry); + + string_tools::trim(str_res); + if(!str_res.size()) + return false; + + return string_tools::get_xtype_from_string(res, str_res); + } + +} +} + +#endif //_TINY_INI_H_ diff --git a/contrib/epee/include/to_nonconst_iterator.h b/contrib/epee/include/to_nonconst_iterator.h new file mode 100644 index 000000000..729b0e8b2 --- /dev/null +++ b/contrib/epee/include/to_nonconst_iterator.h @@ -0,0 +1,52 @@ +// 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. +// + + + +#ifndef _TO_NONCONST_ITERATOR_H_ +#define _TO_NONCONST_ITERATOR_H_ + +namespace epee +{ + +template +typename Type::iterator to_nonsonst_iterator(Type& obj, typename Type::const_iterator it) +{ + typename Type::difference_type dist = std::distance(static_cast(obj.begin()), it); + typename Type::iterator res_it = obj.begin()+dist; + return res_it; +} + + +template +typename Type::iterator to_nonsonst_iterator(typename Type::iterator base_it, typename Type::const_iterator it) +{ + typename Type::difference_type dist = std::distance(static_cast(base_it), it); + typename Type::iterator res_it = base_it+dist; + return res_it; +} +}//namespace epee +#endif //_TO_NONCONST_ITERATOR_H_ diff --git a/contrib/epee/include/warnings.h b/contrib/epee/include/warnings.h new file mode 100644 index 000000000..37d7a2900 --- /dev/null +++ b/contrib/epee/include/warnings.h @@ -0,0 +1,30 @@ +#pragma once + +#if defined(_MSC_VER) + +#define PUSH_WARNINGS __pragma(warning(push)) +#define POP_WARNINGS __pragma(warning(pop)) +#define DISABLE_VS_WARNINGS(w) __pragma(warning(disable: w)) +#define DISABLE_GCC_WARNING(w) +#define DISABLE_CLANG_WARNING(w) +#define DISABLE_GCC_AND_CLANG_WARNING(w) + +#else + +#include + +#define PUSH_WARNINGS _Pragma("GCC diagnostic push") +#define POP_WARNINGS _Pragma("GCC diagnostic pop") +#define DISABLE_VS_WARNINGS(w) + +#if defined(__clang__) +#define DISABLE_GCC_WARNING(w) +#define DISABLE_CLANG_WARNING DISABLE_GCC_AND_CLANG_WARNING +#else +#define DISABLE_GCC_WARNING DISABLE_GCC_AND_CLANG_WARNING +#define DISABLE_CLANG_WARNING(w) +#endif + +#define DISABLE_GCC_AND_CLANG_WARNING(w) _Pragma(BOOST_PP_STRINGIZE(GCC diagnostic ignored BOOST_PP_STRINGIZE(-W##w))) + +#endif \ No newline at end of file diff --git a/contrib/epee/include/winobj.h b/contrib/epee/include/winobj.h new file mode 100644 index 000000000..3279cdac6 --- /dev/null +++ b/contrib/epee/include/winobj.h @@ -0,0 +1,227 @@ +// 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. +// + + +#ifndef __WINH_OBJ_H__ +#define __WINH_OBJ_H__ + +#include + +namespace epee +{ +class critical_region; + +class critical_section { + + boost::mutex m_section; + +public: + + critical_section( const critical_section& section ) { + InitializeCriticalSection( &m_section ); + } + + critical_section() { + InitializeCriticalSection( &m_section ); + } + + ~critical_section() { + DeleteCriticalSection( &m_section ); + } + + void lock() { + EnterCriticalSection( &m_section ); + } + + void unlock() { + LeaveCriticalSection( &m_section ); + } + + bool tryLock() { + return TryEnterCriticalSection( &m_section )? true:false; + } + + critical_section& operator=( const critical_section& section ) + { + return *this; + } + + +}; + +class critical_region { + + ::critical_section *m_locker; + + critical_region( const critical_region& ){} + +public: + + critical_region(critical_section &cs ) { + m_locker = &cs; + cs.lock(); + } + + ~critical_region() + { + m_locker->unlock(); + } +}; + + +class shared_critical_section +{ +public: + shared_critical_section() + { + ::InitializeSRWLock(&m_srw_lock); + } + ~shared_critical_section() + {} + + bool lock_shared() + { + AcquireSRWLockShared(&m_srw_lock); + return true; + } + bool unlock_shared() + { + ReleaseSRWLockShared(&m_srw_lock); + return true; + } + bool lock_exclusive() + { + ::AcquireSRWLockExclusive(&m_srw_lock); + return true; + } + bool unlock_exclusive() + { + ::ReleaseSRWLockExclusive(&m_srw_lock); + return true; + } +private: + SRWLOCK m_srw_lock; + +}; + + +class shared_guard +{ +public: + shared_guard(shared_critical_section& ref_sec):m_ref_sec(ref_sec) + { + m_ref_sec.lock_shared(); + } + + ~shared_guard() + { + m_ref_sec.unlock_shared(); + } + +private: + shared_critical_section& m_ref_sec; +}; + + +class exclusive_guard +{ +public: + exclusive_guard(shared_critical_section& ref_sec):m_ref_sec(ref_sec) + { + m_ref_sec.lock_exclusive(); + } + + ~exclusive_guard() + { + m_ref_sec.unlock_exclusive(); + } + +private: + shared_critical_section& m_ref_sec; +}; + + +class event +{ +public: + event() + { + m_hevent = ::CreateEvent(NULL, FALSE, FALSE, NULL); + } + ~event() + { + ::CloseHandle(m_hevent); + + } + + bool set() + { + return ::SetEvent(m_hevent) ? true:false; + } + + bool reset() + { + return ::ResetEvent(m_hevent) ? true:false; + } + + HANDLE get_handle() + { + return m_hevent; + } +private: + HANDLE m_hevent; + +}; + + +#define SHARED_CRITICAL_REGION_BEGIN(x) { shared_guard critical_region_var(x) +#define EXCLUSIVE_CRITICAL_REGION_BEGIN(x) { exclusive_guard critical_region_var(x) + + + +#define CRITICAL_REGION_LOCAL(x) critical_region critical_region_var(x) +#define CRITICAL_REGION_BEGIN(x) { critical_region critical_region_var(x) +#define CRITICAL_REGION_END() } + + + inline const char* get_wait_for_result_as_text(DWORD res) + { + switch(res) + { + case WAIT_ABANDONED: return "WAIT_ABANDONED"; + case WAIT_TIMEOUT: return "WAIT_TIMEOUT"; + case WAIT_OBJECT_0: return "WAIT_OBJECT_0"; + case WAIT_OBJECT_0+1: return "WAIT_OBJECT_1"; + case WAIT_OBJECT_0+2: return "WAIT_OBJECT_2"; + default: + return "UNKNOWN CODE"; + } + + } + +}// namespace epee + +#endif diff --git a/contrib/epee/include/zlib_helper.h b/contrib/epee/include/zlib_helper.h new file mode 100644 index 000000000..46c7f48e6 --- /dev/null +++ b/contrib/epee/include/zlib_helper.h @@ -0,0 +1,139 @@ +// 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. +// + + +#pragma once +extern "C" { +#include "zlib/zlib.h" +} +#pragma comment(lib, "zlibstat.lib") + +namespace epee +{ +namespace zlib_helper +{ + inline + bool pack(std::string& target){ + std::string result_packed_buff; + + z_stream zstream = {0}; + int ret = deflateInit(&zstream, Z_DEFAULT_COMPRESSION); + if(target.size()) + { + + + result_packed_buff.resize(target.size()*2, 'X'); + + zstream.next_in = (Bytef*)target.data(); + zstream.avail_in = (uInt)target.size(); + zstream.next_out = (Bytef*)result_packed_buff.data(); + zstream.avail_out = (uInt)result_packed_buff.size(); + + ret = deflate(&zstream, Z_FINISH); + CHECK_AND_ASSERT_MES(ret>=0, false, "Failed to deflate. err = " << ret); + + if(result_packed_buff.size() != zstream.avail_out) + result_packed_buff.resize(result_packed_buff.size()-zstream.avail_out); + + + result_packed_buff.erase(0, 2); + target.swap(result_packed_buff); + } + + deflateEnd(& zstream ); + return true; + } + + inline bool unpack(std::string& target) + { + z_stream zstream = {0}; + int ret = inflateInit(&zstream);// + + std::string decode_summary_buff; + size_t ungzip_buff_size = target.size() * 0x30; + std::string current_decode_buff(ungzip_buff_size, 'X'); + + while(target.size()) + { + + + zstream.next_out = (Bytef*)current_decode_buff.data(); + zstream.avail_out = (uInt)ungzip_buff_size; + + int flag = Z_SYNC_FLUSH; + + static char dummy_head[2] = + { + 0x8 + 0x7 * 0x10, + (((0x8 + 0x7 * 0x10) * 0x100 + 30) / 31 * 31) & 0xFF, + }; + zstream.next_in = (Bytef*) dummy_head; + zstream.avail_in = sizeof(dummy_head); + ret = inflate(&zstream, Z_NO_FLUSH); + if (ret != Z_OK) + { + LOCAL_ASSERT(0); + return false; + } + + zstream.next_in = (Bytef*)target.data(); + zstream.avail_in = (uInt)target.size(); + + ret = inflate(&zstream, Z_SYNC_FLUSH); + if (ret != Z_OK && ret != Z_STREAM_END) + { + LOCAL_ASSERT(0); + return false; + } + + + target.erase(0, target.size()-zstream.avail_in); + + + if(ungzip_buff_size == zstream.avail_out) + { + LOG_ERROR("Can't unpack buffer"); + return false; + } + + + current_decode_buff.resize(ungzip_buff_size - zstream.avail_out); + if(decode_summary_buff.size()) + decode_summary_buff += current_decode_buff; + else + current_decode_buff.swap(decode_summary_buff); + + current_decode_buff.resize(ungzip_buff_size); + } + + inflateEnd(&zstream ); + + decode_summary_buff.swap(target); + return 1; + } + +}; +}//namespace epee diff --git a/contrib/epee/tests/.gitignore b/contrib/epee/tests/.gitignore new file mode 100644 index 000000000..d9b4f015d --- /dev/null +++ b/contrib/epee/tests/.gitignore @@ -0,0 +1 @@ +/build/* diff --git a/contrib/epee/tests/data/storages/invalid_storage_1.bin b/contrib/epee/tests/data/storages/invalid_storage_1.bin new file mode 100644 index 000000000..fac7b3e97 Binary files /dev/null and b/contrib/epee/tests/data/storages/invalid_storage_1.bin differ diff --git a/contrib/epee/tests/data/storages/invalid_storage_2.bin b/contrib/epee/tests/data/storages/invalid_storage_2.bin new file mode 100644 index 000000000..a8c29f155 Binary files /dev/null and b/contrib/epee/tests/data/storages/invalid_storage_2.bin differ diff --git a/contrib/epee/tests/data/storages/invalid_storage_3.bin b/contrib/epee/tests/data/storages/invalid_storage_3.bin new file mode 100644 index 000000000..b5c31aa05 --- /dev/null +++ b/contrib/epee/tests/data/storages/invalid_storage_3.bin @@ -0,0 +1 @@ +¢IMóÙŸˆm_bo \ No newline at end of file diff --git a/contrib/epee/tests/data/storages/invalid_storage_4.bin b/contrib/epee/tests/data/storages/invalid_storage_4.bin new file mode 100644 index 000000000..4f8372d19 Binary files /dev/null and b/contrib/epee/tests/data/storages/invalid_storage_4.bin differ diff --git a/contrib/epee/tests/data/storages/valid_storage.bin b/contrib/epee/tests/data/storages/valid_storage.bin new file mode 100644 index 000000000..e13f780b1 Binary files /dev/null and b/contrib/epee/tests/data/storages/valid_storage.bin differ diff --git a/contrib/epee/tests/generate_vc_proj.bat b/contrib/epee/tests/generate_vc_proj.bat new file mode 100644 index 000000000..a81bdce05 --- /dev/null +++ b/contrib/epee/tests/generate_vc_proj.bat @@ -0,0 +1,5 @@ +mkdir build +cd build +cmake "-DBoost_USE_STATIC_LIBS=TRUE" -G "Visual Studio 11 Win64" ../src +cd .. +pause \ No newline at end of file diff --git a/contrib/epee/tests/src/CMakeLists.txt b/contrib/epee/tests/src/CMakeLists.txt new file mode 100644 index 000000000..c7d31735b --- /dev/null +++ b/contrib/epee/tests/src/CMakeLists.txt @@ -0,0 +1,33 @@ + +cmake_minimum_required(VERSION 2.8) + +set(Boost_USE_MULTITHREADED ON) + +include_directories(.) +include_directories(../../include) + +find_package(Boost COMPONENTS system filesystem thread date_time chrono regex) +include_directories( ${Boost_INCLUDE_DIRS} ) + +IF (MSVC) + add_definitions( "/W3 /D_CRT_SECURE_NO_WARNINGS /wd4996 /wd4345 /nologo /D_WIN32_WINNT=0x0600 /DWIN32_LEAN_AND_MEAN /bigobj" ) + include_directories(SYSTEM platform/msvc) +ELSE() + # set stuff for other systems + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c11 -Wall -Werror") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -Werror -Wno-reorder") +ENDIF() + + +# Add folders to filters +file(GLOB_RECURSE SRC RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/*.inl + ${CMAKE_CURRENT_SOURCE_DIR}/*.h) + +source_group(general FILES ${SRC}) + + +add_executable(tests ${SRC} ) +target_link_libraries( tests ${Boost_LIBRARIES} ) + diff --git a/contrib/epee/tests/src/misc/test_math.h b/contrib/epee/tests/src/misc/test_math.h new file mode 100644 index 000000000..8b3064c2a --- /dev/null +++ b/contrib/epee/tests/src/misc/test_math.h @@ -0,0 +1,82 @@ +// 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. +// + + + +#pragma once + +#include "misc_language.h" + +namespace epee +{ + namespace tests + { + bool test_median() + { + LOG_PRINT_L0("Testing median"); + std::vector sz; + size_t m = misc_utils::median(sz); + CHECK_AND_ASSERT_MES(m == 0, false, "test failed"); + sz.push_back(1); + m = misc_utils::median(sz); + CHECK_AND_ASSERT_MES(m == 1, false, "test failed"); + sz.push_back(10); + m = misc_utils::median(sz); + CHECK_AND_ASSERT_MES(m == 5, false, "test failed"); + + sz.clear(); + sz.resize(3); + sz[0] = 0; + sz[1] = 9; + sz[2] = 3; + m = misc_utils::median(sz); + CHECK_AND_ASSERT_MES(m == 3, false, "test failed"); + + sz.clear(); + sz.resize(4); + sz[0] = 77; + sz[1] = 9; + sz[2] = 22; + sz[3] = 60; + m = misc_utils::median(sz); + CHECK_AND_ASSERT_MES(m == 41, false, "test failed"); + + + + sz.clear(); + sz.resize(5); + sz[0] = 77; + sz[1] = 9; + sz[2] = 22; + sz[3] = 60; + sz[4] = 11; + m = misc_utils::median(sz); + CHECK_AND_ASSERT_MES(m == 22, false, "test failed"); + return true; + } + } +} + diff --git a/contrib/epee/tests/src/net/test_net.h b/contrib/epee/tests/src/net/test_net.h new file mode 100644 index 000000000..8d73c1f02 --- /dev/null +++ b/contrib/epee/tests/src/net/test_net.h @@ -0,0 +1,403 @@ +// 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. +// + +#pragma once + +#include +#include + +#include "net/levin_server_cp2.h" +#include "storages/abstract_invoke.h" + +namespace epee +{ +namespace StorageNamed +{ + typedef CInMemStorage DefaultStorageType; +} +namespace tests +{ + struct some_subdata + { + + std::string str1; + std::list array_of_id; + + BEGIN_NAMED_SERIALIZE_MAP() + SERIALIZE_STL_ANSI_STRING(str1) + SERIALIZE_STL_CONTAINER_POD(array_of_id) + END_NAMED_SERIALIZE_MAP() + }; + + + /************************************************************************/ + /* */ + /************************************************************************/ + struct COMMAND_EXAMPLE_1 + { + const static int ID = 1000; + + struct request + { + + std::string example_string_data; + boost::uint64_t example_id_data; + some_subdata sub; + + BEGIN_NAMED_SERIALIZE_MAP() + SERIALIZE_STL_ANSI_STRING(example_string_data) + SERIALIZE_POD(example_id_data) + SERIALIZE_T(sub) + END_NAMED_SERIALIZE_MAP() + }; + + + struct response + { + bool m_success; + boost::uint64_t example_id_data; + std::list subs; + + BEGIN_NAMED_SERIALIZE_MAP() + SERIALIZE_POD(m_success) + SERIALIZE_POD(example_id_data) + SERIALIZE_STL_CONTAINER_T(subs) + END_NAMED_SERIALIZE_MAP() + }; + }; + + struct COMMAND_EXAMPLE_2 + { + const static int ID = 1001; + + struct request + { + std::string example_string_data2; + boost::uint64_t example_id_data; + + BEGIN_NAMED_SERIALIZE_MAP() + SERIALIZE_POD(example_id_data) + SERIALIZE_STL_ANSI_STRING(example_string_data2) + END_NAMED_SERIALIZE_MAP() + }; + + struct response + { + bool m_success; + boost::uint64_t example_id_data; + + BEGIN_NAMED_SERIALIZE_MAP() + SERIALIZE_POD(example_id_data) + SERIALIZE_POD(m_success) + END_NAMED_SERIALIZE_MAP() + }; + }; + typedef boost::uuids::uuid uuid; + + class test_levin_server: public levin::levin_commands_handler<> + { + test_levin_server(const test_levin_server&){} + public: + test_levin_server(){} + void set_thread_prefix(const std::string& pref) + { + m_net_server.set_threads_prefix(pref); + } + template + bool connect_async(const std::string adr, const std::string& port, boost::uint32_t conn_timeot, calback_t cb, const std::string& bind_ip = "0.0.0.0") + { + return m_net_server.connect_async(adr, port, conn_timeot, cb, bind_ip); + } + + bool connect(const std::string adr, const std::string& port, boost::uint32_t conn_timeot, net_utils::connection_context_base& cn, const std::string& bind_ip = "0.0.0.0") + { + return m_net_server.connect(adr, port, conn_timeot, cn, bind_ip); + } + void close(net_utils::connection_context_base& cn) + { + m_net_server.get_config_object().close(cn.m_connection_id); + } + + template + bool invoke(uuid con_id, int command, t_request& req, t_response& resp) + { + return invoke_remote_command(con_id, command, req, resp, m_net_server.get_config_object()); + } + + template< class t_response, class t_request, class callback_t> + bool invoke_async(uuid con_id, int command, t_request& req, callback_t cb) + { + return async_invoke_remote_command(con_id, command, req, m_net_server.get_config_object(), cb); + } + + bool init(const std::string& bind_port = "", const std::string& bind_ip = "0.0.0.0") + { + m_net_server.get_config_object().m_pcommands_handler = this; + m_net_server.get_config_object().m_invoke_timeout = 1000; + LOG_PRINT_L0("Binding on " << bind_ip << ":" << bind_port); + return m_net_server.init_server(bind_port, bind_ip); + } + + bool run() + { + //here you can set worker threads count + int thrds_count = 4; + + //go to loop + LOG_PRINT("Run net_service loop( " << thrds_count << " threads)...", LOG_LEVEL_0); + if(!m_net_server.run_server(thrds_count)) + { + LOG_ERROR("Failed to run net tcp server!"); + } + + LOG_PRINT("net_service loop stopped.", LOG_LEVEL_0); + return true; + } + + bool deinit() + { + return m_net_server.deinit_server(); + } + + bool send_stop_signal() + { + m_net_server.send_stop_signal(); + return true; + } + + uint32_t get_binded_port() + { + return m_net_server.get_binded_port(); + } + private: + + + CHAIN_LEVIN_INVOKE_TO_MAP(); //move levin_commands_handler interface invoke(...) callbacks into invoke map + CHAIN_LEVIN_NOTIFY_TO_STUB(); //move levin_commands_handler interface notify(...) callbacks into nothing + + BEGIN_INVOKE_MAP(test_levin_server) + HANDLE_INVOKE_T(COMMAND_EXAMPLE_1, &test_levin_server::handle_1) + HANDLE_INVOKE_T(COMMAND_EXAMPLE_2, &test_levin_server::handle_2) + END_INVOKE_MAP() + + //----------------- commands handlers ---------------------------------------------- + int handle_1(int command, COMMAND_EXAMPLE_1::request& arg, COMMAND_EXAMPLE_1::response& rsp, const net_utils::connection_context_base& context) + { + LOG_PRINT_L0("on_command_1: id " << arg.example_id_data << "---->>"); + COMMAND_EXAMPLE_2::request arg_ = AUTO_VAL_INIT(arg_); + arg_.example_id_data = arg.example_id_data; + COMMAND_EXAMPLE_2::response rsp_ = AUTO_VAL_INIT(rsp_); + invoke_async(context.m_connection_id, COMMAND_EXAMPLE_2::ID, arg_, [](int code, const COMMAND_EXAMPLE_2::response& rsp, const net_utils::connection_context_base& context) + { + if(code < 0) + {LOG_PRINT_RED_L0("on_command_1: command_2 failed to invoke");} + else + {LOG_PRINT_L0("on_command_1: command_2 response " << rsp.example_id_data);} + }); + rsp.example_id_data = arg.example_id_data; + LOG_PRINT_L0("on_command_1: id " << arg.example_id_data << "<<----"); + return true; + } + int handle_2(int command, COMMAND_EXAMPLE_2::request& arg, COMMAND_EXAMPLE_2::response& rsp, const net_utils::connection_context_base& context) + { + LOG_PRINT_L0("on_command_2: id "<< arg.example_id_data); + rsp.example_id_data = arg.example_id_data; + //misc_utils::sleep_no_w(6000); + return true; + } + //---------------------------------------------------------------------------------- + net_utils::boosted_levin_async_server m_net_server; + }; + + + inline + bool do_run_test_server() + { + + test_levin_server srv1, srv2; + + + std::string bind_param = "0.0.0.0"; + std::string port = ""; + + if(!srv1.init(port, bind_param)) + { + LOG_ERROR("Failed to initialize srv!"); + return 1; + } + + if(!srv2.init(port, bind_param)) + { + LOG_ERROR("Failed to initialize srv!"); + return 1; + } + + srv1.set_thread_prefix("SRV_A"); + srv2.set_thread_prefix("SRV_B"); + + boost::thread th1( boost::bind(&test_levin_server::run, &srv1)); + boost::thread th2( boost::bind(&test_levin_server::run, &srv2)); + + LOG_PRINT_L0("Initalized servers, waiting for worker threads started..."); + misc_utils::sleep_no_w(1000); + + + LOG_PRINT_L0("Connecting to each other..."); + uint32_t port1 = srv1.get_binded_port(); + uint32_t port2 = srv2.get_binded_port(); + + COMMAND_EXAMPLE_1::request arg; + COMMAND_EXAMPLE_1::request resp; + + net_utils::connection_context_base cntxt_1; + bool r = srv1.connect("127.0.0.1", string_tools::num_to_string_fast(port2), 5000, cntxt_1); + CHECK_AND_ASSERT_MES(r, false, "connect to server failed"); + + net_utils::connection_context_base cntxt_2; + r = srv2.connect("127.0.0.1", string_tools::num_to_string_fast(port1), 5000, cntxt_2); + CHECK_AND_ASSERT_MES(r, false, "connect to server failed"); + + while(true) + { + LOG_PRINT_L0("Invoking from A to B..."); + int r = srv1.invoke(cntxt_1.m_connection_id, COMMAND_EXAMPLE_1::ID, arg, resp); + if(r<=0) + { + LOG_ERROR("Failed tp invoke A to B"); + break; + } + + LOG_PRINT_L0("Invoking from B to A..."); + r = srv2.invoke(cntxt_2.m_connection_id, COMMAND_EXAMPLE_1::ID, arg, resp); + if(r<=0) + { + LOG_ERROR("Failed tp invoke B to A"); + break; + } + } + srv1.send_stop_signal(); + srv2.send_stop_signal(); + th1.join(); + th1.join(); + + return true; + } + + + + inline bool do_test2_work_with_srv(test_levin_server& srv, int port) + { + uint64_t i = 0; + boost::mutex wait_event; + wait_event.lock(); + while(true) + { + net_utils::connection_context_base cntxt_local = AUTO_VAL_INIT(cntxt_local); + bool r = srv.connect_async("127.0.0.1", string_tools::num_to_string_fast(port), 5000, [&srv, &port, &wait_event, &i, &cntxt_local](const net_utils::connection_context_base& cntxt, const boost::system::error_code& ec) + { + CHECK_AND_ASSERT_MES(!ec, void(), "Some problems at connect, message: " << ec.message() ); + cntxt_local = cntxt; + LOG_PRINT_L0("Invoking command 1 to " << port); + COMMAND_EXAMPLE_1::request arg = AUTO_VAL_INIT(arg); + arg.example_id_data = i; + /*vc2010 workaround*/ + int port_ = port; + boost::mutex& wait_event_ = wait_event; + int r = srv.invoke_async(cntxt.m_connection_id, COMMAND_EXAMPLE_1::ID, arg, [port_, &wait_event_](int code, const COMMAND_EXAMPLE_1::request& rsp, const net_utils::connection_context_base& cntxt) + { + CHECK_AND_ASSERT_MES(code > 0, void(), "Failed to invoke"); + LOG_PRINT_L0("command 1 invoke to " << port_ << " OK."); + wait_event_.unlock(); + }); + }); + wait_event.lock(); + srv.close(cntxt_local); + ++i; + } + return true; + } + + inline + bool do_run_test_server_async_connect() + { + test_levin_server srv1, srv2; + + + std::string bind_param = "0.0.0.0"; + std::string port = ""; + + if(!srv1.init(port, bind_param)) + { + LOG_ERROR("Failed to initialize srv!"); + return 1; + } + + if(!srv2.init(port, bind_param)) + { + LOG_ERROR("Failed to initialize srv!"); + return 1; + } + + srv1.set_thread_prefix("SRV_A"); + srv2.set_thread_prefix("SRV_B"); + + boost::thread thmain1( boost::bind(&test_levin_server::run, &srv1)); + boost::thread thmain2( boost::bind(&test_levin_server::run, &srv2)); + + LOG_PRINT_L0("Initalized servers, waiting for worker threads started..."); + misc_utils::sleep_no_w(1000); + + + LOG_PRINT_L0("Connecting to each other..."); + uint32_t port1 = srv1.get_binded_port(); + uint32_t port2 = srv2.get_binded_port(); + + COMMAND_EXAMPLE_1::request arg; + COMMAND_EXAMPLE_1::request resp; + + + boost::thread work_1( boost::bind(do_test2_work_with_srv, boost::ref(srv1), port2)); + boost::thread work_2( boost::bind(do_test2_work_with_srv, boost::ref(srv2), port1)); + boost::thread work_3( boost::bind(do_test2_work_with_srv, boost::ref(srv1), port2)); + boost::thread work_4( boost::bind(do_test2_work_with_srv, boost::ref(srv2), port1)); + boost::thread work_5( boost::bind(do_test2_work_with_srv, boost::ref(srv1), port2)); + boost::thread work_6( boost::bind(do_test2_work_with_srv, boost::ref(srv2), port1)); + boost::thread work_7( boost::bind(do_test2_work_with_srv, boost::ref(srv1), port2)); + boost::thread work_8( boost::bind(do_test2_work_with_srv, boost::ref(srv2), port1)); + + + work_1.join(); + work_2.join(); + srv1.send_stop_signal(); + srv2.send_stop_signal(); + thmain1.join(); + thmain2.join(); + + return true; + } + +} +} \ No newline at end of file diff --git a/contrib/epee/tests/src/storages/portable_storages_test.h b/contrib/epee/tests/src/storages/portable_storages_test.h new file mode 100644 index 000000000..ecded8dad --- /dev/null +++ b/contrib/epee/tests/src/storages/portable_storages_test.h @@ -0,0 +1,232 @@ +// 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. +// + + + +#pragma once + +#include +#include +#include "storages/serializeble_struct_helper.h" +#include "serialization/keyvalue_serialization.h" +#include "storages/portable_storage.h" +#include "storages/portable_storage_template_helper.h" + +namespace epee +{ + namespace tests + { + + struct port_test_struct_sub + { + std::string m_str; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_VAL(m_str) + END_KV_SERIALIZE_MAP() + }; + +#pragma pack (push, 1) + struct some_pod_struct + { + uint64_t a; + int32_t b; + }; +#pragma pack(pop) + + struct port_test_struct + { + std::string m_str; + uint64_t m_uint64; + uint32_t m_uint32; + uint16_t m_uint16; + uint8_t m_uint8; + int64_t m_int64; + int32_t m_int32; + int16_t m_int16; + int8_t m_int8; + double m_double; + bool m_bool; + some_pod_struct m_pod; + std::list m_list_of_str; + std::list m_list_of_uint64_t; + std::list m_list_of_uint32_t; + std::list m_list_of_uint16_t; + std::list m_list_of_uint8_t; + std::list m_list_of_int64_t; + std::list m_list_of_int32_t; + std::list m_list_of_int16_t; + std::list m_list_of_int8_t; + std::list m_list_of_double; + std::list m_list_of_bool; + port_test_struct_sub m_subobj; + std::list m_list_of_self; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_VAL(m_str) + KV_SERIALIZE_VAL(m_uint64) + KV_SERIALIZE_VAL(m_uint32) + KV_SERIALIZE_VAL(m_uint16) + KV_SERIALIZE_VAL(m_uint8) + KV_SERIALIZE_VAL(m_int64) + KV_SERIALIZE_VAL(m_int32) + KV_SERIALIZE_VAL(m_int16) + KV_SERIALIZE_VAL(m_int8) + KV_SERIALIZE_VAL(m_double) + KV_SERIALIZE_VAL(m_bool) + KV_SERIALIZE_VAL_POD_AS_BLOB(m_pod) + KV_SERIALIZE_OBJ(m_subobj) + KV_SERIALIZE_CONTAINER_VAL(m_list_of_str) + KV_SERIALIZE_CONTAINER_VAL(m_list_of_uint64_t) + KV_SERIALIZE_CONTAINER_VAL(m_list_of_uint32_t) + KV_SERIALIZE_CONTAINER_VAL(m_list_of_uint16_t) + KV_SERIALIZE_CONTAINER_VAL(m_list_of_uint8_t) + KV_SERIALIZE_CONTAINER_VAL(m_list_of_int64_t) + KV_SERIALIZE_CONTAINER_VAL(m_list_of_int32_t) + KV_SERIALIZE_CONTAINER_VAL(m_list_of_int16_t) + KV_SERIALIZE_CONTAINER_VAL(m_list_of_int8_t) + KV_SERIALIZE_CONTAINER_VAL(m_list_of_double) + KV_SERIALIZE_CONTAINER_VAL(m_list_of_bool) + KV_SERIALIZE_CONTAINER_OBJ(m_list_of_self) + END_KV_SERIALIZE_MAP() + }; + + bool operator != (const port_test_struct_sub& a, const port_test_struct_sub& b) + { + return b.m_str != a.m_str; + } + + bool operator == (const port_test_struct& a, const port_test_struct& b) + { + if( b.m_str != a.m_str + || b.m_uint64 != a.m_uint64 + || b.m_uint32 != a.m_uint32 + || b.m_uint16 != a.m_uint16 + || b.m_uint8 != a.m_uint8 + || b.m_int64 != a.m_int64 + || b.m_int32 != a.m_int32 + || b.m_int16 != a.m_int16 + || b.m_int8 != a.m_int8 + || b.m_double != a.m_double + || b.m_bool != a.m_bool + || b.m_pod.a != a.m_pod.a + || b.m_pod.b != a.m_pod.b + || b.m_list_of_str != a.m_list_of_str + || b.m_list_of_uint64_t != a.m_list_of_uint64_t + || b.m_list_of_uint32_t != a.m_list_of_uint32_t + || b.m_list_of_uint16_t != a.m_list_of_uint16_t + || b.m_list_of_uint8_t != a.m_list_of_uint8_t + || b.m_list_of_int64_t != a.m_list_of_int64_t + || b.m_list_of_int32_t != a.m_list_of_int32_t + || b.m_list_of_int16_t != a.m_list_of_int16_t + || b.m_list_of_int8_t != a.m_list_of_int8_t + || b.m_list_of_double != a.m_list_of_double + || b.m_list_of_bool != a.m_list_of_bool + || b.m_subobj != a.m_subobj + || b.m_list_of_self != a.m_list_of_self + ) + return false; + return true; + } + + void fill_struct_with_test_values(port_test_struct& s) + { + s.m_str = "zuzuzuzuzuz"; + s.m_uint64 = 111111111111111; + s.m_uint32 = 2222222; + s.m_uint16 = 2222; + s.m_uint8 = 22; + s.m_int64 = -111111111111111; + s.m_int32 = -2222222; + s.m_int16 = -2222; + s.m_int8 = -24; + s.m_double = 0.11111; + s.m_bool = true; + s.m_pod.a = 32342342342342; + s.m_pod.b = -342342; + s.m_list_of_str.push_back("1112121"); + s.m_list_of_uint64_t.push_back(1111111111); + s.m_list_of_uint64_t.push_back(2222222222); + s.m_list_of_uint32_t.push_back(1111111); + s.m_list_of_uint32_t.push_back(2222222); + s.m_list_of_uint16_t.push_back(1111); + s.m_list_of_uint16_t.push_back(2222); + s.m_list_of_uint8_t.push_back(11); + s.m_list_of_uint8_t.push_back(22); + + + s.m_list_of_int64_t.push_back(-1111111111); + s.m_list_of_int64_t.push_back(-222222222); + s.m_list_of_int32_t.push_back(-1111111); + s.m_list_of_int32_t.push_back(-2222222); + s.m_list_of_int16_t.push_back(-1111); + s.m_list_of_int16_t.push_back(-2222); + s.m_list_of_int8_t.push_back(-11); + s.m_list_of_int8_t.push_back(-22); + + s.m_list_of_double.push_back(0.11111); + s.m_list_of_double.push_back(0.22222); + s.m_list_of_bool.push_back(true); + s.m_list_of_bool.push_back(false); + + s.m_subobj.m_str = "subszzzzzzzz"; + s.m_list_of_self.push_back(s); + } + + bool test_portable_storages(const std::string& tests_folder) + { + serialization::portable_storage ps, ps2; + port_test_struct s1, s2; + fill_struct_with_test_values(s1); + + s1.store(ps); + std::string binbuf; + bool r = ps.store_to_binary(binbuf); + + ps2.load_from_binary(binbuf); + s2.load(ps2); + if(!(s1 == s2)) + { + LOG_ERROR("Portable storage test failed!"); + return false; + } + + + port_test_struct ss1, ss2; + fill_struct_with_test_values(ss1); + std::string json_buff = epee::serialization::store_t_to_json(ss1); + epee::serialization::load_t_from_json(ss2, json_buff); + if(!(ss1 == ss2)) + { + LOG_ERROR("Portable storage test failed!"); + return false; + } + + return true; + } + + } +} \ No newline at end of file diff --git a/contrib/epee/tests/src/storages/storage_tests.h b/contrib/epee/tests/src/storages/storage_tests.h new file mode 100644 index 000000000..522e589c4 --- /dev/null +++ b/contrib/epee/tests/src/storages/storage_tests.h @@ -0,0 +1,142 @@ +// 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. +// + + + +#pragma once + +#include "storages/serializeble_struct_helper.h" +#include "storages/portable_storage.h" + +namespace epee +{ + namespace tests + { + + + struct test_struct + { + + std::string m_str; + unsigned int m_uint; + bool m_bool; + std::list m_list_of_str; + std::list m_list_of_int; + std::list m_list_of_self; + + + BEGIN_NAMED_SERIALIZE_MAP() + SERIALIZE_STL_ANSI_STRING(m_str) + SERIALIZE_POD(m_uint) + SERIALIZE_POD(m_bool) + SERIALIZE_STL_CONTAINER_ANSII_STRING(m_list_of_str) + SERIALIZE_STL_CONTAINER_POD(m_list_of_int) + SERIALIZE_STL_CONTAINER_T(m_list_of_self) + END_NAMED_SERIALIZE_MAP() + + }; + + + bool operator == (const test_struct& a, const test_struct& b) + { + if( b.m_str != a.m_str + || b.m_uint != a.m_uint + || b.m_bool != a.m_bool + || b.m_list_of_str != a.m_list_of_str + || b.m_list_of_int != a.m_list_of_int + || b.m_list_of_self != a.m_list_of_self + ) + return false; + return true; + } + + inline test_struct get_test_struct() + { + test_struct t = boost::value_initialized(); + t.m_bool = true; + t.m_str = "ackamdc'kmecemcececmacmecmcm[aicm[oeicm[oeicm[qaicm[qoe"; + t.m_uint = 233242; + for(int i = 0; i!=500; i++) + t.m_list_of_int.push_back(i); + + for(int i = 0; i!=500; i++) + t.m_list_of_str.push_back("ssccd"); + + for(int i = 0; i!=5; i++) + { + t.m_list_of_self.push_back(t); + } + return t; + } + + bool test_storages(const std::string& tests_folder) + { + + epee::serialization::portable_storage ps; + auto s = ps.open_section("zzz", nullptr); + uint64_t i = 0; + ps.get_value("afdsdf", i, s); + + + LOG_PRINT_L0("Generating test struct..."); + boost::filesystem::path storage_folder = tests_folder; + storage_folder /= "storages"; + + + test_struct t = get_test_struct(); + + LOG_PRINT_L0("Loading test struct from storage..."); + test_struct t2; + bool res = epee::StorageNamed::load_struct_from_storage_file(t2, (storage_folder /+ "valid_storage.bin").string()); + CHECK_AND_ASSERT_MES(res, false, "Failed to load valid_storage.bin"); + + LOG_PRINT_L0("Comparing generated and loaded test struct..."); + if(!(t == t2)) + return false; + + LOG_PRINT_L0("Loading broken archive 1..."); + test_struct t3; + res = epee::StorageNamed::load_struct_from_storage_file(t3, (storage_folder /+ "invalid_storage_1.bin").string()); + CHECK_AND_ASSERT_MES(!res, false, "invalid_storage_1.bin loaded, but should not "); + + + LOG_PRINT_L0("Loading broken archive 2..."); + res = epee::StorageNamed::load_struct_from_storage_file(t3, (storage_folder /+ "invalid_storage_2.bin").string()); + CHECK_AND_ASSERT_MES(!res, false, "invalid_storage_2.bin loaded, but should not "); + + LOG_PRINT_L0("Loading broken archive 3..."); + res = epee::StorageNamed::load_struct_from_storage_file(t3, (storage_folder /+ "invalid_storage_3.bin").string()); + CHECK_AND_ASSERT_MES(!res, false, "invalid_storage_3.bin loaded, but should not "); + + LOG_PRINT_L0("Loading broken archive 4..."); + res = epee::StorageNamed::load_struct_from_storage_file(t3, (storage_folder /+ "invalid_storage_4.bin").string()); + CHECK_AND_ASSERT_MES(!res, false, "invalid_storage_3.bin loaded, but should not "); + + return true; + } + } +} + diff --git a/contrib/epee/tests/src/tests.cpp b/contrib/epee/tests/src/tests.cpp new file mode 100644 index 000000000..ed045d833 --- /dev/null +++ b/contrib/epee/tests/src/tests.cpp @@ -0,0 +1,59 @@ + +#include "include_base_utils.h" +#include "storages/storage_tests.h" +#include "misc/test_math.h" +#include "storages/portable_storages_test.h" +#include "net/test_net.h" + +using namespace epee; + +int main(int argc, char* argv[]) +{ + + string_tools::set_module_name_and_folder(argv[0]); + + //set up logging options + log_space::get_set_log_detalisation_level(true, LOG_LEVEL_2); + log_space::log_singletone::add_logger(LOGGER_CONSOLE, NULL, NULL); + log_space::log_singletone::add_logger(LOGGER_FILE, + log_space::log_singletone::get_default_log_file().c_str(), + log_space::log_singletone::get_default_log_folder().c_str()); + + + string_tools::command_line_params_a start_params; + string_tools::parse_commandline(start_params, argc, argv); + std::string tests_data_path; + string_tools::get_xparam_from_command_line(start_params, std::string("/tests_folder"), tests_data_path); + + if(string_tools::have_in_command_line(start_params, std::string("/run_net_tests"))) + { + if(!tests::do_run_test_server()) + { + LOG_ERROR("net tests failed"); + return 1; + } + if(!tests::do_run_test_server_async_connect() ) + { + LOG_ERROR("net tests failed"); + return 1; + } + }else if(string_tools::have_in_command_line(start_params, std::string("/run_unit_tests"))) + { + if(!tests::test_median()) + { + LOG_ERROR("median test failed"); + return 1; + } + + + if(!tests::test_storages(tests_data_path)) + { + LOG_ERROR("storage test failed"); + return 1; + } + }else if(string_tools::have_in_command_line(start_params, std::string("/run_portable_storage_test"))) + { + tests::test_portable_storages(tests_data_path); + } + return 1; +} \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 000000000..600413f16 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,44 @@ +file(GLOB_RECURSE COMMON common/*) +file(GLOB_RECURSE CRYPTO crypto/*) +file(GLOB_RECURSE CRYPTONOTE_CORE cryptonote_core/*) +file(GLOB_RECURSE CRYPTONOTE_PROTOCOL cryptonote_protocol/*) +file(GLOB_RECURSE DAEMON daemon/*) +file(GLOB_RECURSE P2P p2p/*) +file(GLOB_RECURSE RPC rpc/*) +file(GLOB_RECURSE SIMPLEWALLET simplewallet/*) +file(GLOB_RECURSE CONN_TOOL connectivity_tool/*) +file(GLOB_RECURSE WALLET wallet/*) +file(GLOB_RECURSE MINER miner/*) + +source_group(common FILES ${COMMON}) +source_group(crypto FILES ${CRYPTO}) +source_group(cryptonote_core FILES ${CRYPTONOTE_CORE}) +source_group(cryptonote_protocol FILES ${CRYPTONOTE_PROTOCOL}) +source_group(daemon FILES ${DAEMON}) +source_group(p2p FILES ${P2P}) +source_group(rpc FILES ${RPC}) +source_group(simplewallet FILES ${SIMPLEWALLET}) +source_group(connectivity-tool FILES ${CONN_TOOL}) +source_group(wallet FILES ${WALLET}) +source_group(simpleminer FILES ${MINER}) + +add_library(common ${COMMON}) +add_library(crypto ${CRYPTO}) +add_library(cryptonote_core ${CRYPTONOTE_CORE}) +add_executable(daemon ${DAEMON} ${P2P} ${CRYPTONOTE_PROTOCOL}) +add_executable(connectivity_tool ${CONN_TOOL}) +add_executable(simpleminer ${MINER}) +target_link_libraries(daemon rpc cryptonote_core crypto common ${Boost_LIBRARIES}) +target_link_libraries(connectivity_tool cryptonote_core crypto common ${Boost_LIBRARIES}) +target_link_libraries(simpleminer cryptonote_core crypto common ${Boost_LIBRARIES}) +add_dependencies(daemon version) +add_library(rpc ${RPC}) +add_library(wallet ${WALLET}) +add_executable(simplewallet ${SIMPLEWALLET} ) +target_link_libraries(simplewallet wallet rpc cryptonote_core crypto common ${Boost_LIBRARIES}) +add_dependencies(simplewallet version) +add_dependencies(daemon version) + +set_property(TARGET common crypto cryptonote_core rpc wallet PROPERTY FOLDER "libs") +set_property(TARGET daemon simplewallet connectivity_tool simpleminer PROPERTY FOLDER "prog") +set_property(TARGET daemon PROPERTY OUTPUT_NAME "bytecoind") diff --git a/src/common/base58.cpp b/src/common/base58.cpp new file mode 100644 index 000000000..30042eeba --- /dev/null +++ b/src/common/base58.cpp @@ -0,0 +1,246 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#include "base58.h" + +#include +#include +#include + +#include "crypto/hash.h" +#include "int-util.h" +#include "util.h" +#include "varint.h" + +namespace tools +{ + namespace base58 + { + namespace + { + const char alphabet[] = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; + const size_t alphabet_size = sizeof(alphabet) - 1; + const size_t encoded_block_sizes[] = {0, 2, 3, 5, 6, 7, 9, 10, 11}; + const size_t full_block_size = sizeof(encoded_block_sizes) / sizeof(encoded_block_sizes[0]) - 1; + const size_t full_encoded_block_size = encoded_block_sizes[full_block_size]; + const size_t addr_checksum_size = 4; + + struct reverse_alphabet + { + reverse_alphabet() + { + m_data.resize(alphabet[alphabet_size - 1] - alphabet[0] + 1, -1); + + for (size_t i = 0; i < alphabet_size; ++i) + { + size_t idx = static_cast(alphabet[i] - alphabet[0]); + m_data[idx] = static_cast(i); + } + } + + int operator()(char letter) const + { + size_t idx = static_cast(letter - alphabet[0]); + return idx < m_data.size() ? m_data[idx] : -1; + } + + static reverse_alphabet instance; + + private: + std::vector m_data; + }; + + reverse_alphabet reverse_alphabet::instance; + + struct decoded_block_sizes + { + decoded_block_sizes() + { + m_data.resize(encoded_block_sizes[full_block_size] + 1, -1); + for (size_t i = 0; i <= full_block_size; ++i) + { + m_data[encoded_block_sizes[i]] = static_cast(i); + } + } + + int operator()(size_t encoded_block_size) const + { + assert(encoded_block_size <= full_encoded_block_size); + return m_data[encoded_block_size]; + } + + static decoded_block_sizes instance; + + private: + std::vector m_data; + }; + + decoded_block_sizes decoded_block_sizes::instance; + + uint64_t uint_8be_to_64(const uint8_t* data, size_t size) + { + assert(1 <= size && size <= sizeof(uint64_t)); + + uint64_t res = 0; + switch (9 - size) + { + case 1: res |= *data++; + case 2: res <<= 8; res |= *data++; + case 3: res <<= 8; res |= *data++; + case 4: res <<= 8; res |= *data++; + case 5: res <<= 8; res |= *data++; + case 6: res <<= 8; res |= *data++; + case 7: res <<= 8; res |= *data++; + case 8: res <<= 8; res |= *data; break; + default: assert(false); + } + + return res; + } + + void uint_64_to_8be(uint64_t num, size_t size, uint8_t* data) + { + assert(1 <= size && size <= sizeof(uint64_t)); + + uint64_t num_be = SWAP64BE(num); + memcpy(data, reinterpret_cast(&num_be) + sizeof(uint64_t) - size, size); + } + + void encode_block(const char* block, size_t size, char* res) + { + assert(1 <= size && size <= sizeof(full_block_size)); + + uint64_t num = uint_8be_to_64(reinterpret_cast(block), size); + int i = static_cast(encoded_block_sizes[size]) - 1; + while (0 < num) + { + uint64_t remainder = num % alphabet_size; + num /= alphabet_size; + res[i] = alphabet[remainder]; + --i; + } + } + + bool decode_block(const char* block, size_t size, char* res) + { + assert(1 <= size && size <= full_encoded_block_size); + + int res_size = decoded_block_sizes::instance(size); + if (res_size <= 0) + return false; // Invalid block size + + uint64_t res_num = 0; + uint64_t order = 1; + for (size_t i = size - 1; i < size; --i) + { + int digit = reverse_alphabet::instance(block[i]); + if (digit < 0) + return false; // Invalid symbol + + uint64_t product_hi; + uint64_t tmp = res_num + mul128(order, digit, &product_hi); + if (tmp < res_num || 0 != product_hi) + return false; // Overflow + + res_num = tmp; + order *= alphabet_size; // Never overflows, 58^10 < 2^64 + } + + if (static_cast(res_size) < full_block_size && (UINT64_C(1) << (8 * res_size)) <= res_num) + return false; // Overflow + + uint_64_to_8be(res_num, res_size, reinterpret_cast(res)); + + return true; + } + } + + std::string encode(const std::string& data) + { + if (data.empty()) + return std::string(); + + size_t full_block_count = data.size() / full_block_size; + size_t last_block_size = data.size() % full_block_size; + size_t res_size = full_block_count * full_encoded_block_size + encoded_block_sizes[last_block_size]; + + std::string res(res_size, alphabet[0]); + for (size_t i = 0; i < full_block_count; ++i) + { + encode_block(data.data() + i * full_block_size, full_block_size, &res[i * full_encoded_block_size]); + } + + if (0 < last_block_size) + { + encode_block(data.data() + full_block_count * full_block_size, last_block_size, &res[full_block_count * full_encoded_block_size]); + } + + return res; + } + + bool decode(const std::string& enc, std::string& data) + { + if (enc.empty()) + { + data.clear(); + return true; + } + + size_t full_block_count = enc.size() / full_encoded_block_size; + size_t last_block_size = enc.size() % full_encoded_block_size; + int last_block_decoded_size = decoded_block_sizes::instance(last_block_size); + if (last_block_decoded_size < 0) + return false; // Invalid enc length + size_t data_size = full_block_count * full_block_size + last_block_decoded_size; + + data.resize(data_size, 0); + for (size_t i = 0; i < full_block_count; ++i) + { + if (!decode_block(enc.data() + i * full_encoded_block_size, full_encoded_block_size, &data[i * full_block_size])) + return false; + } + + if (0 < last_block_size) + { + if (!decode_block(enc.data() + full_block_count * full_encoded_block_size, last_block_size, + &data[full_block_count * full_block_size])) + return false; + } + + return true; + } + + std::string encode_addr(uint64_t tag, const std::string& data) + { + std::string buf = get_varint_data(tag); + buf += data; + crypto::hash hash = crypto::cn_fast_hash(buf.data(), buf.size()); + const char* hash_data = reinterpret_cast(&hash); + buf.append(hash_data, addr_checksum_size); + return encode(buf); + } + + bool decode_addr(std::string addr, uint64_t& tag, std::string& data) + { + std::string addr_data; + bool r = decode(addr, addr_data); + if (!r) return false; + + std::string checksum(addr_checksum_size, '\0'); + checksum = addr_data.substr(addr_data.size() - addr_checksum_size); + + addr_data.resize(addr_data.size() - addr_checksum_size); + crypto::hash hash = crypto::cn_fast_hash(addr_data.data(), addr_data.size()); + std::string expected_checksum(reinterpret_cast(&hash), addr_checksum_size); + if (expected_checksum != checksum) return false; + + int read = tools::read_varint(addr_data.begin(), addr_data.end(), tag); + if (read <= 0) return false; + + data = addr_data.substr(read); + return true; + } + } +} diff --git a/src/common/base58.h b/src/common/base58.h new file mode 100644 index 000000000..4055f62ba --- /dev/null +++ b/src/common/base58.h @@ -0,0 +1,20 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include +#include + +namespace tools +{ + namespace base58 + { + std::string encode(const std::string& data); + bool decode(const std::string& enc, std::string& data); + + std::string encode_addr(uint64_t tag, const std::string& data); + bool decode_addr(std::string addr, uint64_t& tag, std::string& data); + } +} diff --git a/src/common/boost_serialization_helper.h b/src/common/boost_serialization_helper.h new file mode 100644 index 000000000..74016ae75 --- /dev/null +++ b/src/common/boost_serialization_helper.h @@ -0,0 +1,44 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include +#include + + +namespace tools +{ + template + bool serialize_obj_to_file(t_object& obj, const std::string& file_path) + { + TRY_ENTRY(); + std::ofstream data_file; + data_file.open( file_path , std::ios_base::binary | std::ios_base::out| std::ios::trunc); + if(data_file.fail()) + return false; + + boost::archive::binary_oarchive a(data_file); + a << obj; + + return !data_file.fail(); + CATCH_ENTRY_L0("serialize_obj_to_file", false); + } + + template + bool unserialize_obj_from_file(t_object& obj, const std::string& file_path) + { + TRY_ENTRY(); + + std::ifstream data_file; + data_file.open( file_path, std::ios_base::binary | std::ios_base::in); + if(data_file.fail()) + return false; + boost::archive::binary_iarchive a(data_file); + + a >> obj; + return !data_file.fail(); + CATCH_ENTRY_L0("unserialize_obj_from_file", false); + } +} diff --git a/src/common/command_line.cpp b/src/common/command_line.cpp new file mode 100644 index 000000000..0b90345d9 --- /dev/null +++ b/src/common/command_line.cpp @@ -0,0 +1,12 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "command_line.h" + +namespace command_line +{ + const arg_descriptor arg_help = {"help", "Produce help message"}; + const arg_descriptor arg_version = {"version", "Output version information"}; + const arg_descriptor arg_data_dir = {"data-dir", "Specify data directory"}; +} diff --git a/src/common/command_line.h b/src/common/command_line.h new file mode 100644 index 000000000..860653772 --- /dev/null +++ b/src/common/command_line.h @@ -0,0 +1,177 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include +#include + +#include +#include +#include +#include "include_base_utils.h" + +namespace command_line +{ + template + struct arg_descriptor; + + template + struct arg_descriptor + { + typedef T value_type; + + const char* name; + const char* description; + T default_value; + bool not_use_default; + }; + + template + struct arg_descriptor, false> + { + typedef std::vector value_type; + + const char* name; + const char* description; + }; + + template + struct arg_descriptor + { + static_assert(!std::is_same::value, "Boolean switch can't be required"); + + typedef T value_type; + + const char* name; + const char* description; + }; + + template + boost::program_options::typed_value* make_semantic(const arg_descriptor& /*arg*/) + { + return boost::program_options::value()->required(); + } + + template + boost::program_options::typed_value* make_semantic(const arg_descriptor& arg) + { + auto semantic = boost::program_options::value(); + if (!arg.not_use_default) + semantic->default_value(arg.default_value); + return semantic; + } + + template + boost::program_options::typed_value* make_semantic(const arg_descriptor& arg, const T& def) + { + auto semantic = boost::program_options::value(); + if (!arg.not_use_default) + semantic->default_value(def); + return semantic; + } + + template + boost::program_options::typed_value, char>* make_semantic(const arg_descriptor, false>& /*arg*/) + { + auto semantic = boost::program_options::value< std::vector >(); + semantic->default_value(std::vector(), ""); + return semantic; + } + + template + void add_arg(boost::program_options::options_description& description, const arg_descriptor& arg, bool unique = true) + { + if (0 != description.find_nothrow(arg.name, false)) + { + CHECK_AND_ASSERT_MES(!unique, void(), "Argument already exists: " << arg.name); + return; + } + + description.add_options()(arg.name, make_semantic(arg), arg.description); + } + + template + void add_arg(boost::program_options::options_description& description, const arg_descriptor& arg, const T& def, bool unique = true) + { + if (0 != description.find_nothrow(arg.name, false)) + { + CHECK_AND_ASSERT_MES(!unique, void(), "Argument already exists: " << arg.name); + return; + } + + description.add_options()(arg.name, make_semantic(arg, def), arg.description); + } + + template<> + inline void add_arg(boost::program_options::options_description& description, const arg_descriptor& arg, bool unique) + { + if (0 != description.find_nothrow(arg.name, false)) + { + CHECK_AND_ASSERT_MES(!unique, void(), "Argument already exists: " << arg.name); + return; + } + + description.add_options()(arg.name, boost::program_options::bool_switch(), arg.description); + } + + template + boost::program_options::basic_parsed_options parse_command_line(int argc, const charT* const argv[], + const boost::program_options::options_description& desc, bool allow_unregistered = false) + { + auto parser = boost::program_options::command_line_parser(argc, argv); + parser.options(desc); + if (allow_unregistered) + { + parser.allow_unregistered(); + } + return parser.run(); + } + + template + bool handle_error_helper(const boost::program_options::options_description& desc, F parser) + { + try + { + return parser(); + } + catch (std::exception& e) + { + std::cerr << "Failed to parse arguments: " << e.what() << std::endl; + std::cerr << desc << std::endl; + return false; + } + catch (...) + { + std::cerr << "Failed to parse arguments: unknown exception" << std::endl; + std::cerr << desc << std::endl; + return false; + } + } + + template + bool has_arg(const boost::program_options::variables_map& vm, const arg_descriptor& arg) + { + auto value = vm[arg.name]; + return !value.empty(); + } + + + template + T get_arg(const boost::program_options::variables_map& vm, const arg_descriptor& arg) + { + return vm[arg.name].template as(); + } + + template<> + inline bool has_arg(const boost::program_options::variables_map& vm, const arg_descriptor& arg) + { + return get_arg(vm, arg); + } + + + extern const arg_descriptor arg_help; + extern const arg_descriptor arg_version; + extern const arg_descriptor arg_data_dir; +} diff --git a/src/common/int-util.h b/src/common/int-util.h new file mode 100644 index 000000000..ad0ef60e0 --- /dev/null +++ b/src/common/int-util.h @@ -0,0 +1,205 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include +#include +#include +#include +#include + +#if defined(_MSC_VER) +#include + +static inline uint32_t rol32(uint32_t x, int r) { + static_assert(sizeof(uint32_t) == sizeof(unsigned int), "this code assumes 32-bit integers"); + return _rotl(x, r); +} + +static inline uint64_t rol64(uint64_t x, int r) { + return _rotl64(x, r); +} + +#else + +static inline uint32_t rol32(uint32_t x, int r) { + return (x << (r & 31)) | (x >> (-r & 31)); +} + +static inline uint64_t rol64(uint64_t x, int r) { + return (x << (r & 63)) | (x >> (-r & 63)); +} + +#endif + +inline uint64_t hi_dword(uint64_t val) { + return val >> 32; +} + +inline uint64_t lo_dword(uint64_t val) { + return val & 0xFFFFFFFF; +} + +inline uint64_t mul128(uint64_t multiplier, uint64_t multiplicand, uint64_t* product_hi) { + // multiplier = ab = a * 2^32 + b + // multiplicand = cd = c * 2^32 + d + // ab * cd = a * c * 2^64 + (a * d + b * c) * 2^32 + b * d + uint64_t a = hi_dword(multiplier); + uint64_t b = lo_dword(multiplier); + uint64_t c = hi_dword(multiplicand); + uint64_t d = lo_dword(multiplicand); + + uint64_t ac = a * c; + uint64_t ad = a * d; + uint64_t bc = b * c; + uint64_t bd = b * d; + + uint64_t adbc = ad + bc; + uint64_t adbc_carry = adbc < ad ? 1 : 0; + + // multiplier * multiplicand = product_hi * 2^64 + product_lo + uint64_t product_lo = bd + (adbc << 32); + uint64_t product_lo_carry = product_lo < bd ? 1 : 0; + *product_hi = ac + (adbc >> 32) + (adbc_carry << 32) + product_lo_carry; + assert(ac <= *product_hi); + + return product_lo; +} + +inline uint64_t div_with_reminder(uint64_t dividend, uint32_t divisor, uint32_t* remainder) { + dividend |= ((uint64_t)*remainder) << 32; + *remainder = dividend % divisor; + return dividend / divisor; +} + +// Long division with 2^32 base +inline uint32_t div128_32(uint64_t dividend_hi, uint64_t dividend_lo, uint32_t divisor, uint64_t* quotient_hi, uint64_t* quotient_lo) { + uint64_t dividend_dwords[4]; + uint32_t remainder = 0; + + dividend_dwords[3] = hi_dword(dividend_hi); + dividend_dwords[2] = lo_dword(dividend_hi); + dividend_dwords[1] = hi_dword(dividend_lo); + dividend_dwords[0] = lo_dword(dividend_lo); + + *quotient_hi = div_with_reminder(dividend_dwords[3], divisor, &remainder) << 32; + *quotient_hi |= div_with_reminder(dividend_dwords[2], divisor, &remainder); + *quotient_lo = div_with_reminder(dividend_dwords[1], divisor, &remainder) << 32; + *quotient_lo |= div_with_reminder(dividend_dwords[0], divisor, &remainder); + + return remainder; +} + +#define IDENT32(x) ((uint32_t) (x)) +#define IDENT64(x) ((uint64_t) (x)) + +#define SWAP32(x) ((((uint32_t) (x) & 0x000000ff) << 24) | \ + (((uint32_t) (x) & 0x0000ff00) << 8) | \ + (((uint32_t) (x) & 0x00ff0000) >> 8) | \ + (((uint32_t) (x) & 0xff000000) >> 24)) +#define SWAP64(x) ((((uint64_t) (x) & 0x00000000000000ff) << 56) | \ + (((uint64_t) (x) & 0x000000000000ff00) << 40) | \ + (((uint64_t) (x) & 0x0000000000ff0000) << 24) | \ + (((uint64_t) (x) & 0x00000000ff000000) << 8) | \ + (((uint64_t) (x) & 0x000000ff00000000) >> 8) | \ + (((uint64_t) (x) & 0x0000ff0000000000) >> 24) | \ + (((uint64_t) (x) & 0x00ff000000000000) >> 40) | \ + (((uint64_t) (x) & 0xff00000000000000) >> 56)) + +static inline uint32_t ident32(uint32_t x) { return x; } +static inline uint64_t ident64(uint64_t x) { return x; } + +static inline uint32_t swap32(uint32_t x) { + x = ((x & 0x00ff00ff) << 8) | ((x & 0xff00ff00) >> 8); + return (x << 16) | (x >> 16); +} +static inline uint64_t swap64(uint64_t x) { + x = ((x & 0x00ff00ff00ff00ff) << 8) | ((x & 0xff00ff00ff00ff00) >> 8); + x = ((x & 0x0000ffff0000ffff) << 16) | ((x & 0xffff0000ffff0000) >> 16); + return (x << 32) | (x >> 32); +} + +#if defined(__GNUC__) +#define UNUSED __attribute__((unused)) +#else +#define UNUSED +#endif +static inline void mem_inplace_ident(void *mem UNUSED, size_t n UNUSED) { } +#undef UNUSED + +static inline void mem_inplace_swap32(void *mem, size_t n) { + size_t i; + for (i = 0; i < n; i++) { + ((uint32_t *) mem)[i] = swap32(((const uint32_t *) mem)[i]); + } +} +static inline void mem_inplace_swap64(void *mem, size_t n) { + size_t i; + for (i = 0; i < n; i++) { + ((uint64_t *) mem)[i] = swap64(((const uint64_t *) mem)[i]); + } +} + +static inline void memcpy_ident32(void *dst, const void *src, size_t n) { + memcpy(dst, src, 4 * n); +} +static inline void memcpy_ident64(void *dst, const void *src, size_t n) { + memcpy(dst, src, 8 * n); +} + +static inline void memcpy_swap32(void *dst, const void *src, size_t n) { + size_t i; + for (i = 0; i < n; i++) { + ((uint32_t *) dst)[i] = swap32(((const uint32_t *) src)[i]); + } +} +static inline void memcpy_swap64(void *dst, const void *src, size_t n) { + size_t i; + for (i = 0; i < n; i++) { + ((uint64_t *) dst)[i] = swap64(((const uint64_t *) src)[i]); + } +} + +#if !defined(BYTE_ORDER) || !defined(LITTLE_ENDIAN) || !defined(BIG_ENDIAN) +static_assert(false, "BYTE_ORDER is undefined. Perhaps, GNU extensions are not enabled"); +#endif + +#if BYTE_ORDER == LITTLE_ENDIAN +#define SWAP32LE IDENT32 +#define SWAP32BE SWAP32 +#define swap32le ident32 +#define swap32be swap32 +#define mem_inplace_swap32le mem_inplace_ident +#define mem_inplace_swap32be mem_inplace_swap32 +#define memcpy_swap32le memcpy_ident32 +#define memcpy_swap32be memcpy_swap32 +#define SWAP64LE IDENT64 +#define SWAP64BE SWAP64 +#define swap64le ident64 +#define swap64be swap64 +#define mem_inplace_swap64le mem_inplace_ident +#define mem_inplace_swap64be mem_inplace_swap64 +#define memcpy_swap64le memcpy_ident64 +#define memcpy_swap64be memcpy_swap64 +#endif + +#if BYTE_ORDER == BIG_ENDIAN +#define SWAP32BE IDENT32 +#define SWAP32LE SWAP32 +#define swap32be ident32 +#define swap32le swap32 +#define mem_inplace_swap32be mem_inplace_ident +#define mem_inplace_swap32le mem_inplace_swap32 +#define memcpy_swap32be memcpy_ident32 +#define memcpy_swap32le memcpy_swap32 +#define SWAP64BE IDENT64 +#define SWAP64LE SWAP64 +#define swap64be ident64 +#define swap64le swap64 +#define mem_inplace_swap64be mem_inplace_ident +#define mem_inplace_swap64le mem_inplace_swap64 +#define memcpy_swap64be memcpy_ident64 +#define memcpy_swap64le memcpy_swap64 +#endif diff --git a/src/common/pod-class.h b/src/common/pod-class.h new file mode 100644 index 000000000..c07edb208 --- /dev/null +++ b/src/common/pod-class.h @@ -0,0 +1,11 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#if defined(_MSC_VER) +#define POD_CLASS struct +#else +#define POD_CLASS class +#endif diff --git a/src/common/unordered_containers_boost_serialization.h b/src/common/unordered_containers_boost_serialization.h new file mode 100644 index 000000000..0804660ca --- /dev/null +++ b/src/common/unordered_containers_boost_serialization.h @@ -0,0 +1,80 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include +#include +#include + +namespace boost +{ + namespace serialization + { + template + inline void save(Archive &a, const std::unordered_map &x, const boost::serialization::version_type ver) + { + size_t s = x.size(); + a << s; + BOOST_FOREACH(auto& v, x) + { + a << v.first; + a << v.second; + } + } + + template + inline void load(Archive &a, std::unordered_map &x, const boost::serialization::version_type ver) + { + size_t s = 0; + a >> s; + for(size_t i = 0; i != s; i++) + { + h_key k; + hval v; + a >> k; + a >> v; + x.insert(std::pair(k, v)); + } + } + + + template + inline void save(Archive &a, const std::unordered_set &x, const boost::serialization::version_type ver) + { + size_t s = x.size(); + a << s; + BOOST_FOREACH(auto& v, x) + { + a << v; + } + } + + template + inline void load(Archive &a, std::unordered_set &x, const boost::serialization::version_type ver) + { + size_t s = 0; + a >> s; + for(size_t i = 0; i != s; i++) + { + hval v; + a >> v; + x.insert(v); + } + } + + + template + inline void serialize(Archive &a, std::unordered_map &x, const boost::serialization::version_type ver) + { + split_free(a, x, ver); + } + + template + inline void serialize(Archive &a, std::unordered_set &x, const boost::serialization::version_type ver) + { + split_free(a, x, ver); + } + } +} diff --git a/src/common/util.cpp b/src/common/util.cpp new file mode 100644 index 000000000..b24016cc3 --- /dev/null +++ b/src/common/util.cpp @@ -0,0 +1,363 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include + +#include "include_base_utils.h" +using namespace epee; + +#include "util.h" +#include "cryptonote_config.h" + +#ifdef WIN32 +#include +#include +#include +#else +#include +#endif + + +namespace tools +{ + +#ifdef WIN32 + std::string get_windows_version_display_string() + { + typedef void (WINAPI *PGNSI)(LPSYSTEM_INFO); + typedef BOOL (WINAPI *PGPI)(DWORD, DWORD, DWORD, DWORD, PDWORD); +#define BUFSIZE 10000 + + char pszOS[BUFSIZE] = {0}; + OSVERSIONINFOEX osvi; + SYSTEM_INFO si; + PGNSI pGNSI; + PGPI pGPI; + BOOL bOsVersionInfoEx; + DWORD dwType; + + ZeroMemory(&si, sizeof(SYSTEM_INFO)); + ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX)); + + osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); + bOsVersionInfoEx = GetVersionEx((OSVERSIONINFO*) &osvi); + + if(!bOsVersionInfoEx) return pszOS; + + // Call GetNativeSystemInfo if supported or GetSystemInfo otherwise. + + pGNSI = (PGNSI) GetProcAddress( + GetModuleHandle(TEXT("kernel32.dll")), + "GetNativeSystemInfo"); + if(NULL != pGNSI) + pGNSI(&si); + else GetSystemInfo(&si); + + if ( VER_PLATFORM_WIN32_NT==osvi.dwPlatformId && + osvi.dwMajorVersion > 4 ) + { + StringCchCopy(pszOS, BUFSIZE, TEXT("Microsoft ")); + + // Test for the specific product. + + if ( osvi.dwMajorVersion == 6 ) + { + if( osvi.dwMinorVersion == 0 ) + { + if( osvi.wProductType == VER_NT_WORKSTATION ) + StringCchCat(pszOS, BUFSIZE, TEXT("Windows Vista ")); + else StringCchCat(pszOS, BUFSIZE, TEXT("Windows Server 2008 " )); + } + + if ( osvi.dwMinorVersion == 1 ) + { + if( osvi.wProductType == VER_NT_WORKSTATION ) + StringCchCat(pszOS, BUFSIZE, TEXT("Windows 7 ")); + else StringCchCat(pszOS, BUFSIZE, TEXT("Windows Server 2008 R2 " )); + } + + pGPI = (PGPI) GetProcAddress( + GetModuleHandle(TEXT("kernel32.dll")), + "GetProductInfo"); + + pGPI( osvi.dwMajorVersion, osvi.dwMinorVersion, 0, 0, &dwType); + + switch( dwType ) + { + case PRODUCT_ULTIMATE: + StringCchCat(pszOS, BUFSIZE, TEXT("Ultimate Edition" )); + break; + case PRODUCT_PROFESSIONAL: + StringCchCat(pszOS, BUFSIZE, TEXT("Professional" )); + break; + case PRODUCT_HOME_PREMIUM: + StringCchCat(pszOS, BUFSIZE, TEXT("Home Premium Edition" )); + break; + case PRODUCT_HOME_BASIC: + StringCchCat(pszOS, BUFSIZE, TEXT("Home Basic Edition" )); + break; + case PRODUCT_ENTERPRISE: + StringCchCat(pszOS, BUFSIZE, TEXT("Enterprise Edition" )); + break; + case PRODUCT_BUSINESS: + StringCchCat(pszOS, BUFSIZE, TEXT("Business Edition" )); + break; + case PRODUCT_STARTER: + StringCchCat(pszOS, BUFSIZE, TEXT("Starter Edition" )); + break; + case PRODUCT_CLUSTER_SERVER: + StringCchCat(pszOS, BUFSIZE, TEXT("Cluster Server Edition" )); + break; + case PRODUCT_DATACENTER_SERVER: + StringCchCat(pszOS, BUFSIZE, TEXT("Datacenter Edition" )); + break; + case PRODUCT_DATACENTER_SERVER_CORE: + StringCchCat(pszOS, BUFSIZE, TEXT("Datacenter Edition (core installation)" )); + break; + case PRODUCT_ENTERPRISE_SERVER: + StringCchCat(pszOS, BUFSIZE, TEXT("Enterprise Edition" )); + break; + case PRODUCT_ENTERPRISE_SERVER_CORE: + StringCchCat(pszOS, BUFSIZE, TEXT("Enterprise Edition (core installation)" )); + break; + case PRODUCT_ENTERPRISE_SERVER_IA64: + StringCchCat(pszOS, BUFSIZE, TEXT("Enterprise Edition for Itanium-based Systems" )); + break; + case PRODUCT_SMALLBUSINESS_SERVER: + StringCchCat(pszOS, BUFSIZE, TEXT("Small Business Server" )); + break; + case PRODUCT_SMALLBUSINESS_SERVER_PREMIUM: + StringCchCat(pszOS, BUFSIZE, TEXT("Small Business Server Premium Edition" )); + break; + case PRODUCT_STANDARD_SERVER: + StringCchCat(pszOS, BUFSIZE, TEXT("Standard Edition" )); + break; + case PRODUCT_STANDARD_SERVER_CORE: + StringCchCat(pszOS, BUFSIZE, TEXT("Standard Edition (core installation)" )); + break; + case PRODUCT_WEB_SERVER: + StringCchCat(pszOS, BUFSIZE, TEXT("Web Server Edition" )); + break; + } + } + + if ( osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2 ) + { + if( GetSystemMetrics(SM_SERVERR2) ) + StringCchCat(pszOS, BUFSIZE, TEXT( "Windows Server 2003 R2, ")); + else if ( osvi.wSuiteMask & VER_SUITE_STORAGE_SERVER ) + StringCchCat(pszOS, BUFSIZE, TEXT( "Windows Storage Server 2003")); + else if ( osvi.wSuiteMask & VER_SUITE_WH_SERVER ) + StringCchCat(pszOS, BUFSIZE, TEXT( "Windows Home Server")); + else if( osvi.wProductType == VER_NT_WORKSTATION && + si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_AMD64) + { + StringCchCat(pszOS, BUFSIZE, TEXT( "Windows XP Professional x64 Edition")); + } + else StringCchCat(pszOS, BUFSIZE, TEXT("Windows Server 2003, ")); + + // Test for the server type. + if ( osvi.wProductType != VER_NT_WORKSTATION ) + { + if ( si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_IA64 ) + { + if( osvi.wSuiteMask & VER_SUITE_DATACENTER ) + StringCchCat(pszOS, BUFSIZE, TEXT( "Datacenter Edition for Itanium-based Systems" )); + else if( osvi.wSuiteMask & VER_SUITE_ENTERPRISE ) + StringCchCat(pszOS, BUFSIZE, TEXT( "Enterprise Edition for Itanium-based Systems" )); + } + + else if ( si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_AMD64 ) + { + if( osvi.wSuiteMask & VER_SUITE_DATACENTER ) + StringCchCat(pszOS, BUFSIZE, TEXT( "Datacenter x64 Edition" )); + else if( osvi.wSuiteMask & VER_SUITE_ENTERPRISE ) + StringCchCat(pszOS, BUFSIZE, TEXT( "Enterprise x64 Edition" )); + else StringCchCat(pszOS, BUFSIZE, TEXT( "Standard x64 Edition" )); + } + + else + { + if ( osvi.wSuiteMask & VER_SUITE_COMPUTE_SERVER ) + StringCchCat(pszOS, BUFSIZE, TEXT( "Compute Cluster Edition" )); + else if( osvi.wSuiteMask & VER_SUITE_DATACENTER ) + StringCchCat(pszOS, BUFSIZE, TEXT( "Datacenter Edition" )); + else if( osvi.wSuiteMask & VER_SUITE_ENTERPRISE ) + StringCchCat(pszOS, BUFSIZE, TEXT( "Enterprise Edition" )); + else if ( osvi.wSuiteMask & VER_SUITE_BLADE ) + StringCchCat(pszOS, BUFSIZE, TEXT( "Web Edition" )); + else StringCchCat(pszOS, BUFSIZE, TEXT( "Standard Edition" )); + } + } + } + + if ( osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1 ) + { + StringCchCat(pszOS, BUFSIZE, TEXT("Windows XP ")); + if( osvi.wSuiteMask & VER_SUITE_PERSONAL ) + StringCchCat(pszOS, BUFSIZE, TEXT( "Home Edition" )); + else StringCchCat(pszOS, BUFSIZE, TEXT( "Professional" )); + } + + if ( osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0 ) + { + StringCchCat(pszOS, BUFSIZE, TEXT("Windows 2000 ")); + + if ( osvi.wProductType == VER_NT_WORKSTATION ) + { + StringCchCat(pszOS, BUFSIZE, TEXT( "Professional" )); + } + else + { + if( osvi.wSuiteMask & VER_SUITE_DATACENTER ) + StringCchCat(pszOS, BUFSIZE, TEXT( "Datacenter Server" )); + else if( osvi.wSuiteMask & VER_SUITE_ENTERPRISE ) + StringCchCat(pszOS, BUFSIZE, TEXT( "Advanced Server" )); + else StringCchCat(pszOS, BUFSIZE, TEXT( "Server" )); + } + } + + // Include service pack (if any) and build number. + + if( strlen(osvi.szCSDVersion) > 0 ) + { + StringCchCat(pszOS, BUFSIZE, TEXT(" ") ); + StringCchCat(pszOS, BUFSIZE, osvi.szCSDVersion); + } + + TCHAR buf[80]; + + StringCchPrintf( buf, 80, TEXT(" (build %d)"), osvi.dwBuildNumber); + StringCchCat(pszOS, BUFSIZE, buf); + + if ( osvi.dwMajorVersion >= 6 ) + { + if ( si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_AMD64 ) + StringCchCat(pszOS, BUFSIZE, TEXT( ", 64-bit" )); + else if (si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_INTEL ) + StringCchCat(pszOS, BUFSIZE, TEXT(", 32-bit")); + } + + return pszOS; + } + else + { + printf( "This sample does not support this version of Windows.\n"); + return pszOS; + } + } +#else +std::string get_nix_version_display_string() +{ + utsname un; + + if(uname(&un) < 0) + return std::string("*nix: failed to get os version"); + return std::string() + un.sysname + " " + un.version + " " + un.release; +} +#endif + + + + std::string get_os_version_string() + { +#ifdef WIN32 + return get_windows_version_display_string(); +#else + return get_nix_version_display_string(); +#endif + } + + + +#ifdef WIN32 + std::string get_special_folder_path(int nfolder, bool iscreate) + { + namespace fs = boost::filesystem; + char psz_path[MAX_PATH] = ""; + + if(SHGetSpecialFolderPathA(NULL, psz_path, nfolder, iscreate)) + { + return psz_path; + } + + LOG_ERROR("SHGetSpecialFolderPathA() failed, could not obtain requested path."); + return ""; + } +#endif + + std::string get_default_data_dir() + { + //namespace fs = boost::filesystem; + // Windows < Vista: C:\Documents and Settings\Username\Application Data\CRYPTONOTE_NAME + // Windows >= Vista: C:\Users\Username\AppData\Roaming\CRYPTONOTE_NAME + // Mac: ~/Library/Application Support/CRYPTONOTE_NAME + // Unix: ~/.CRYPTONOTE_NAME + std::string config_folder; +#ifdef WIN32 + // Windows + config_folder = get_special_folder_path(CSIDL_APPDATA, true) + "/" + CRYPTONOTE_NAME; +#else + std::string pathRet; + char* pszHome = getenv("HOME"); + if (pszHome == NULL || strlen(pszHome) == 0) + pathRet = "/"; + else + pathRet = pszHome; +#ifdef MAC_OSX + // Mac + pathRet /= "Library/Application Support"; + config_folder = (pathRet + "/" + CRYPTONOTE_NAME); +#else + // Unix + config_folder = (pathRet + "/." + CRYPTONOTE_NAME); +#endif +#endif + + return config_folder; + } + + bool create_directories_if_necessary(const std::string& path) + { + namespace fs = boost::filesystem; + boost::system::error_code ec; + fs::path fs_path(path); + if (fs::is_directory(fs_path, ec)) + { + return true; + } + + bool res = fs::create_directories(fs_path, ec); + if (res) + { + LOG_PRINT_L2("Created directory: " << path); + } + else + { + LOG_PRINT_L2("Can't create directory: " << path << ", err: "<< ec.message()); + } + + return res; + } + + std::error_code replace_file(const std::string& replacement_name, const std::string& replaced_name) + { + int code; +#if defined(WIN32) + // Maximizing chances for success + DWORD attributes = ::GetFileAttributes(replaced_name.c_str()); + if (INVALID_FILE_ATTRIBUTES != attributes) + { + ::SetFileAttributes(replaced_name.c_str(), attributes & (~FILE_ATTRIBUTE_READONLY)); + } + + bool ok = 0 != ::MoveFileEx(replacement_name.c_str(), replaced_name.c_str(), MOVEFILE_REPLACE_EXISTING); + code = ok ? 0 : static_cast(::GetLastError()); +#else + bool ok = 0 == std::rename(replacement_name.c_str(), replaced_name.c_str()); + code = ok ? 0 : errno; +#endif + return std::error_code(code, std::system_category()); + } +} diff --git a/src/common/util.h b/src/common/util.h new file mode 100644 index 000000000..a29a30fff --- /dev/null +++ b/src/common/util.h @@ -0,0 +1,29 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include +#include + +#include "crypto/crypto.h" +#include "crypto/hash.h" +#include "misc_language.h" +#include "p2p/p2p_protocol_defs.h" + +namespace tools +{ + std::string get_default_data_dir(); + std::string get_os_version_string(); + bool create_directories_if_necessary(const std::string& path); + std::error_code replace_file(const std::string& replacement_name, const std::string& replaced_name); + + inline crypto::hash get_proof_of_trust_hash(const nodetool::proof_of_trust& pot) + { + std::string s; + s.append(reinterpret_cast(&pot.peer_id), sizeof(pot.peer_id)); + s.append(reinterpret_cast(&pot.time), sizeof(pot.time)); + return crypto::cn_fast_hash(s.data(), s.size()); + } +} diff --git a/src/common/varint.h b/src/common/varint.h new file mode 100644 index 000000000..e62470fdf --- /dev/null +++ b/src/common/varint.h @@ -0,0 +1,62 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include +#include +#include +#include +#include + +namespace tools { + + template + typename std::enable_if::value && std::is_unsigned::value, void>::type + write_varint(OutputIt &&dest, T i) { + while (i >= 0x80) { + *dest++ = (static_cast(i) & 0x7f) | 0x80; + i >>= 7; + } + *dest++ = static_cast(i); + } + + template + std::string get_varint_data(const t_type& v) + { + std::stringstream ss; + write_varint(std::ostreambuf_iterator(ss), v); + return ss.str(); + } + + template + typename std::enable_if::value && std::is_unsigned::value && 0 <= bits && bits <= std::numeric_limits::digits, int>::type + read_varint(InputIt &&first, InputIt &&last, T &i) { + int read = 0; + i = 0; + for (int shift = 0;; shift += 7) { + if (first == last) { + return read; // End of input. + } + unsigned char byte = *first++; + ++read; + if (shift + 7 >= bits && byte >= 1 << (bits - shift)) { + return -1; // Overflow. + } + if (byte == 0 && shift != 0) { + return -2; // Non-canonical representation. + } + i |= static_cast(byte & 0x7f) << shift; + if ((byte & 0x80) == 0) { + break; + } + } + return read; + } + + template + int read_varint(InputIt &&first, InputIt &&last, T &i) { + return read_varint::digits, InputIt, T>(std::move(first), std::move(last), i); + } +} diff --git a/src/connectivity_tool/conn_tool.cpp b/src/connectivity_tool/conn_tool.cpp new file mode 100644 index 000000000..4b83b4f49 --- /dev/null +++ b/src/connectivity_tool/conn_tool.cpp @@ -0,0 +1,353 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + + +#include "include_base_utils.h" +#include "version.h" + +using namespace epee; +#include +#include "p2p/p2p_protocol_defs.h" +#include "common/command_line.h" +#include "cryptonote_core/cryptonote_core.h" +#include "cryptonote_protocol/cryptonote_protocol_handler.h" +#include "net/levin_client.h" +#include "storages/levin_abstract_invoke2.h" +#include "cryptonote_core/cryptonote_core.h" +#include "storages/portable_storage_template_helper.h" +#include "crypto/crypto.h" +#include "storages/http_abstract_invoke.h" +#include "net/http_client.h" + +namespace po = boost::program_options; +using namespace cryptonote; +using namespace nodetool; + +namespace +{ + const command_line::arg_descriptor arg_ip = {"ip", "set ip"}; + const command_line::arg_descriptor arg_port = {"port", "set port"}; + const command_line::arg_descriptor arg_rpc_port = {"rpc_port", "set rpc port"}; + const command_line::arg_descriptor arg_timeout = {"timeout", "set timeout"}; + const command_line::arg_descriptor arg_priv_key = {"private_key", "private key to subscribe debug command", "", true}; + const command_line::arg_descriptor arg_peer_id = {"peer_id", "peer_id if known(if not - will be requested)", 0}; + const command_line::arg_descriptor arg_generate_keys = {"generate_keys_pair", "generate private and public keys pair"}; + const command_line::arg_descriptor arg_request_stat_info = {"request_stat_info", "request statistics information"}; + const command_line::arg_descriptor arg_request_net_state = {"request_net_state", "request network state information (peer list, connections count)"}; + const command_line::arg_descriptor arg_get_daemon_info = {"rpc_get_daemon_info", "request daemon state info vie rpc (--rpc_port option should be set ).", "", true}; +} + +typedef COMMAND_REQUEST_STAT_INFO_T::stat_info> COMMAND_REQUEST_STAT_INFO; + +struct response_schema +{ + std::string status; + std::string COMMAND_REQUEST_STAT_INFO_status; + std::string COMMAND_REQUEST_NETWORK_STATE_status; + enableable si_rsp; + enableable ns_rsp; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(status) + KV_SERIALIZE(COMMAND_REQUEST_STAT_INFO_status) + KV_SERIALIZE(COMMAND_REQUEST_NETWORK_STATE_status) + KV_SERIALIZE(si_rsp) + KV_SERIALIZE(ns_rsp) + END_KV_SERIALIZE_MAP() +}; + + std::string get_response_schema_as_json(response_schema& rs) + { + std::stringstream ss; + ss << "{" << ENDL + << " \"status\": \"" << rs.status << "\"," << ENDL + << " \"COMMAND_REQUEST_NETWORK_STATE_status\": \"" << rs.COMMAND_REQUEST_NETWORK_STATE_status << "\"," << ENDL + << " \"COMMAND_REQUEST_STAT_INFO_status\": \"" << rs.COMMAND_REQUEST_STAT_INFO_status << "\""; + if(rs.si_rsp.enabled) + { + ss << "," << ENDL << " \"si_rsp\": " << epee::serialization::store_t_to_json(rs.si_rsp.v, 1); + } + if(rs.ns_rsp.enabled) + { + ss << "," << ENDL << " \"ns_rsp\": {" << ENDL + << " \"local_time\": " << rs.ns_rsp.v.local_time << "," << ENDL + << " \"my_id\": \"" << rs.ns_rsp.v.my_id << "\"," << ENDL + << " \"connections_list\": [" << ENDL; + + size_t i = 0; + BOOST_FOREACH(const connection_entry& ce, rs.ns_rsp.v.connections_list) + { + ss << " {\"peer_id\": \"" << ce.id << "\", \"ip\": \"" << string_tools::get_ip_string_from_int32(ce.adr.ip) << "\", \"port\": " << ce.adr.port << ", \"is_income\": "<< ce.is_income << "}"; + if(rs.ns_rsp.v.connections_list.size()-1 != i) + ss << ","; + ss << ENDL; + i++; + } + ss << " ]," << ENDL; + ss << " \"local_peerlist_white\": [" << ENDL; + i = 0; + BOOST_FOREACH(const peerlist_entry& pe, rs.ns_rsp.v.local_peerlist_white) + { + ss << " {\"peer_id\": \"" << pe.id << "\", \"ip\": \"" << string_tools::get_ip_string_from_int32(pe.adr.ip) << "\", \"port\": " << pe.adr.port << ", \"last_seen\": "<< rs.ns_rsp.v.local_time - pe.last_seen << "}"; + if(rs.ns_rsp.v.local_peerlist_white.size()-1 != i) + ss << ","; + ss << ENDL; + i++; + } + ss << " ]," << ENDL; + + ss << " \"local_peerlist_gray\": [" << ENDL; + i = 0; + BOOST_FOREACH(const peerlist_entry& pe, rs.ns_rsp.v.local_peerlist_gray) + { + ss << " {\"peer_id\": \"" << pe.id << "\", \"ip\": \"" << string_tools::get_ip_string_from_int32(pe.adr.ip) << "\", \"port\": " << pe.adr.port << ", \"last_seen\": "<< rs.ns_rsp.v.local_time - pe.last_seen << "}"; + if(rs.ns_rsp.v.local_peerlist_gray.size()-1 != i) + ss << ","; + ss << ENDL; + i++; + } + ss << " ]" << ENDL << " }" << ENDL; + } + ss << "}"; + return std::move(ss.str()); + } +//--------------------------------------------------------------------------------------------------------------- +bool print_COMMAND_REQUEST_STAT_INFO(const COMMAND_REQUEST_STAT_INFO::response& si) +{ + std::cout << " ------ COMMAND_REQUEST_STAT_INFO ------ " << ENDL; + std::cout << "Version: " << si.version << ENDL; + std::cout << "OS Version: " << si.os_version << ENDL; + std::cout << "Connections: " << si.connections_count << ENDL; + std::cout << "INC Connections: " << si.incoming_connections_count << ENDL; + + + std::cout << "Tx pool size: " << si.payload_info.tx_pool_size << ENDL; + std::cout << "BC height: " << si.payload_info.blockchain_height << ENDL; + std::cout << "Mining speed: " << si.payload_info.mining_speed << ENDL; + std::cout << "Alternative blocks: " << si.payload_info.alternative_blocks << ENDL; + std::cout << "Top block id: " << si.payload_info.top_block_id_str << ENDL; + return true; +} +//--------------------------------------------------------------------------------------------------------------- +bool print_COMMAND_REQUEST_NETWORK_STATE(const COMMAND_REQUEST_NETWORK_STATE::response& ns) +{ + std::cout << " ------ COMMAND_REQUEST_NETWORK_STATE ------ " << ENDL; + std::cout << "Peer id: " << ns.my_id << ENDL; + std::cout << "Active connections:" << ENDL; + BOOST_FOREACH(const connection_entry& ce, ns.connections_list) + { + std::cout << ce.id << "\t" << string_tools::get_ip_string_from_int32(ce.adr.ip) << ":" << ce.adr.port << (ce.is_income ? "(INC)":"(OUT)") << ENDL; + } + + std::cout << "Peer list white:" << ns.my_id << ENDL; + BOOST_FOREACH(const peerlist_entry& pe, ns.local_peerlist_white) + { + std::cout << pe.id << "\t" << string_tools::get_ip_string_from_int32(pe.adr.ip) << ":" << pe.adr.port << "\t" << misc_utils::get_time_interval_string(ns.local_time - pe.last_seen) << ENDL; + } + + std::cout << "Peer list gray:" << ns.my_id << ENDL; + BOOST_FOREACH(const peerlist_entry& pe, ns.local_peerlist_gray) + { + std::cout << pe.id << "\t" << string_tools::get_ip_string_from_int32(pe.adr.ip) << ":" << pe.adr.port << "\t" << misc_utils::get_time_interval_string(ns.local_time - pe.last_seen) << ENDL; + } + + + return true; +} +//--------------------------------------------------------------------------------------------------------------- +bool handle_get_daemon_info(po::variables_map& vm) +{ + if(!command_line::has_arg(vm, arg_rpc_port)) + { + std::cout << "ERROR: rpc port not set" << ENDL; + return false; + } + + epee::net_utils::http::http_simple_client http_client; + + cryptonote::COMMAND_RPC_GET_INFO::request req = AUTO_VAL_INIT(req); + cryptonote::COMMAND_RPC_GET_INFO::response res = AUTO_VAL_INIT(res); + std::string daemon_addr = command_line::get_arg(vm, arg_ip) + ":" + std::to_string(command_line::get_arg(vm, arg_rpc_port)); + bool r = net_utils::invoke_http_json_remote_command2(daemon_addr + "/getinfo", req, res, http_client, command_line::get_arg(vm, arg_timeout)); + if(!r) + { + std::cout << "ERROR: failed to invoke request" << ENDL; + return false; + } + std::cout << "OK" << ENDL + << "height: " << res.height << ENDL + << "difficulty: " << res.difficulty << ENDL + << "tx_count: " << res.tx_count << ENDL + << "tx_pool_size: " << res.tx_pool_size << ENDL + << "alt_blocks_count: " << res.alt_blocks_count << ENDL + << "outgoing_connections_count: " << res.outgoing_connections_count << ENDL + << "incoming_connections_count: " << res.incoming_connections_count << ENDL + << "white_peerlist_size: " << res.white_peerlist_size << ENDL + << "grey_peerlist_size: " << res.grey_peerlist_size << ENDL; + + return true; +} +//--------------------------------------------------------------------------------------------------------------- +bool handle_request_stat(po::variables_map& vm, peerid_type peer_id) +{ + + if(!command_line::has_arg(vm, arg_priv_key)) + { + std::cout << "{" << ENDL << " \"status\": \"ERROR: " << "secret key not set \"" << ENDL << "}"; + return false; + } + crypto::secret_key prvk = AUTO_VAL_INIT(prvk); + if(!string_tools::hex_to_pod(command_line::get_arg(vm, arg_priv_key) , prvk)) + { + std::cout << "{" << ENDL << " \"status\": \"ERROR: " << "wrong secret key set \"" << ENDL << "}"; + return false; + } + + + response_schema rs = AUTO_VAL_INIT(rs); + + levin::levin_client_impl2 transport; + if(!transport.connect(command_line::get_arg(vm, arg_ip), static_cast(command_line::get_arg(vm, arg_port)), static_cast(command_line::get_arg(vm, arg_timeout)))) + { + std::cout << "{" << ENDL << " \"status\": \"ERROR: " << "Failed to connect to " << command_line::get_arg(vm, arg_ip) << ":" << command_line::get_arg(vm, arg_port) << "\"" << ENDL << "}"; + return false; + }else + rs.status = "OK"; + + if(!peer_id) + { + COMMAND_REQUEST_PEER_ID::request req = AUTO_VAL_INIT(req); + COMMAND_REQUEST_PEER_ID::response rsp = AUTO_VAL_INIT(rsp); + if(!net_utils::invoke_remote_command2(COMMAND_REQUEST_PEER_ID::ID, req, rsp, transport)) + { + std::cout << "{" << ENDL << " \"status\": \"ERROR: " << "Failed to connect to " << command_line::get_arg(vm, arg_ip) << ":" << command_line::get_arg(vm, arg_port) << "\"" << ENDL << "}"; + return false; + }else + { + peer_id = rsp.my_id; + } + } + + + nodetool::proof_of_trust pot = AUTO_VAL_INIT(pot); + pot.peer_id = peer_id; + pot.time = time(NULL); + crypto::public_key pubk = AUTO_VAL_INIT(pubk); + string_tools::hex_to_pod(P2P_STAT_TRUSTED_PUB_KEY, pubk); + crypto::hash h = tools::get_proof_of_trust_hash(pot); + crypto::generate_signature(h, pubk, prvk, pot.sign); + + if(command_line::get_arg(vm, arg_request_stat_info)) + { + COMMAND_REQUEST_STAT_INFO::request req = AUTO_VAL_INIT(req); + req.tr = pot; + if(!net_utils::invoke_remote_command2(COMMAND_REQUEST_STAT_INFO::ID, req, rs.si_rsp.v, transport)) + { + std::stringstream ss; + ss << "ERROR: " << "Failed to invoke remote command COMMAND_REQUEST_STAT_INFO to " << command_line::get_arg(vm, arg_ip) << ":" << command_line::get_arg(vm, arg_port); + rs.COMMAND_REQUEST_STAT_INFO_status = ss.str(); + }else + { + rs.si_rsp.enabled = true; + rs.COMMAND_REQUEST_STAT_INFO_status = "OK"; + } + } + + + if(command_line::get_arg(vm, arg_request_net_state)) + { + ++pot.time; + h = tools::get_proof_of_trust_hash(pot); + crypto::generate_signature(h, pubk, prvk, pot.sign); + COMMAND_REQUEST_NETWORK_STATE::request req = AUTO_VAL_INIT(req); + req.tr = pot; + if(!net_utils::invoke_remote_command2(COMMAND_REQUEST_NETWORK_STATE::ID, req, rs.ns_rsp.v, transport)) + { + std::stringstream ss; + ss << "ERROR: " << "Failed to invoke remote command COMMAND_REQUEST_NETWORK_STATE to " << command_line::get_arg(vm, arg_ip) << ":" << command_line::get_arg(vm, arg_port); + rs.COMMAND_REQUEST_NETWORK_STATE_status = ss.str(); + }else + { + rs.ns_rsp.enabled = true; + rs.COMMAND_REQUEST_NETWORK_STATE_status = "OK"; + } + } + std::cout << get_response_schema_as_json(rs); + return true; +} +//--------------------------------------------------------------------------------------------------------------- +bool generate_and_print_keys() +{ + crypto::public_key pk = AUTO_VAL_INIT(pk); + crypto::secret_key sk = AUTO_VAL_INIT(sk); + generate_keys(pk, sk); + std::cout << "PUBLIC KEY: " << epee::string_tools::pod_to_hex(pk) << ENDL + << "PRIVATE KEY: " << epee::string_tools::pod_to_hex(sk); + return true; +} +int main(int argc, char* argv[]) +{ + string_tools::set_module_name_and_folder(argv[0]); + log_space::get_set_log_detalisation_level(true, LOG_LEVEL_0); + + // Declare the supported options. + po::options_description desc_general("General options"); + command_line::add_arg(desc_general, command_line::arg_help); + + po::options_description desc_params("Connectivity options"); + command_line::add_arg(desc_params, arg_ip); + command_line::add_arg(desc_params, arg_port); + command_line::add_arg(desc_params, arg_rpc_port); + command_line::add_arg(desc_params, arg_timeout); + command_line::add_arg(desc_params, arg_request_stat_info); + command_line::add_arg(desc_params, arg_request_net_state); + command_line::add_arg(desc_params, arg_generate_keys); + command_line::add_arg(desc_params, arg_peer_id); + command_line::add_arg(desc_params, arg_priv_key); + command_line::add_arg(desc_params, arg_get_daemon_info); + + + po::options_description desc_all; + desc_all.add(desc_general).add(desc_params); + + po::variables_map vm; + bool r = command_line::handle_error_helper(desc_all, [&]() + { + po::store(command_line::parse_command_line(argc, argv, desc_general, true), vm); + if (command_line::get_arg(vm, command_line::arg_help)) + { + std::cout << desc_all << ENDL; + return false; + } + + po::store(command_line::parse_command_line(argc, argv, desc_params, false), vm); + po::notify(vm); + + return true; + }); + if (!r) + return 1; + + if(command_line::has_arg(vm, arg_request_stat_info) || command_line::has_arg(vm, arg_request_net_state)) + { + return handle_request_stat(vm, command_line::get_arg(vm, arg_peer_id)) ? 0:1; + } + if(command_line::has_arg(vm, arg_get_daemon_info)) + { + return handle_get_daemon_info(vm) ? 0:1; + } + else if(command_line::has_arg(vm, arg_generate_keys)) + { + return generate_and_print_keys() ? 0:1; + } + else + { + std::cerr << "Not enough arguments." << ENDL; + std::cerr << desc_all << ENDL; + } + + return 1; +} + diff --git a/src/crypto/blake256.c b/src/crypto/blake256.c new file mode 100644 index 000000000..36b42c3d4 --- /dev/null +++ b/src/crypto/blake256.c @@ -0,0 +1,326 @@ +/* + * The blake256_* and blake224_* functions are largely copied from + * blake256_light.c and blake224_light.c from the BLAKE website: + * + * http://131002.net/blake/ + * + * The hmac_* functions implement HMAC-BLAKE-256 and HMAC-BLAKE-224. + * HMAC is specified by RFC 2104. + */ + +#include +#include +#include +#include "blake256.h" + +#define U8TO32(p) \ + (((uint32_t)((p)[0]) << 24) | ((uint32_t)((p)[1]) << 16) | \ + ((uint32_t)((p)[2]) << 8) | ((uint32_t)((p)[3]) )) +#define U32TO8(p, v) \ + (p)[0] = (uint8_t)((v) >> 24); (p)[1] = (uint8_t)((v) >> 16); \ + (p)[2] = (uint8_t)((v) >> 8); (p)[3] = (uint8_t)((v) ); + +const uint8_t sigma[][16] = { + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15}, + {14,10, 4, 8, 9,15,13, 6, 1,12, 0, 2,11, 7, 5, 3}, + {11, 8,12, 0, 5, 2,15,13,10,14, 3, 6, 7, 1, 9, 4}, + { 7, 9, 3, 1,13,12,11,14, 2, 6, 5,10, 4, 0,15, 8}, + { 9, 0, 5, 7, 2, 4,10,15,14, 1,11,12, 6, 8, 3,13}, + { 2,12, 6,10, 0,11, 8, 3, 4,13, 7, 5,15,14, 1, 9}, + {12, 5, 1,15,14,13, 4,10, 0, 7, 6, 3, 9, 2, 8,11}, + {13,11, 7,14,12, 1, 3, 9, 5, 0,15, 4, 8, 6, 2,10}, + { 6,15,14, 9,11, 3, 0, 8,12, 2,13, 7, 1, 4,10, 5}, + {10, 2, 8, 4, 7, 6, 1, 5,15,11, 9,14, 3,12,13, 0}, + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15}, + {14,10, 4, 8, 9,15,13, 6, 1,12, 0, 2,11, 7, 5, 3}, + {11, 8,12, 0, 5, 2,15,13,10,14, 3, 6, 7, 1, 9, 4}, + { 7, 9, 3, 1,13,12,11,14, 2, 6, 5,10, 4, 0,15, 8} +}; + +const uint32_t cst[16] = { + 0x243F6A88, 0x85A308D3, 0x13198A2E, 0x03707344, + 0xA4093822, 0x299F31D0, 0x082EFA98, 0xEC4E6C89, + 0x452821E6, 0x38D01377, 0xBE5466CF, 0x34E90C6C, + 0xC0AC29B7, 0xC97C50DD, 0x3F84D5B5, 0xB5470917 +}; + +static const uint8_t padding[] = { + 0x80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +}; + + +void blake256_compress(state *S, const uint8_t *block) { + uint32_t v[16], m[16], i; + +#define ROT(x,n) (((x)<<(32-n))|((x)>>(n))) +#define G(a,b,c,d,e) \ + v[a] += (m[sigma[i][e]] ^ cst[sigma[i][e+1]]) + v[b]; \ + v[d] = ROT(v[d] ^ v[a],16); \ + v[c] += v[d]; \ + v[b] = ROT(v[b] ^ v[c],12); \ + v[a] += (m[sigma[i][e+1]] ^ cst[sigma[i][e]])+v[b]; \ + v[d] = ROT(v[d] ^ v[a], 8); \ + v[c] += v[d]; \ + v[b] = ROT(v[b] ^ v[c], 7); + + for (i = 0; i < 16; ++i) m[i] = U8TO32(block + i * 4); + for (i = 0; i < 8; ++i) v[i] = S->h[i]; + v[ 8] = S->s[0] ^ 0x243F6A88; + v[ 9] = S->s[1] ^ 0x85A308D3; + v[10] = S->s[2] ^ 0x13198A2E; + v[11] = S->s[3] ^ 0x03707344; + v[12] = 0xA4093822; + v[13] = 0x299F31D0; + v[14] = 0x082EFA98; + v[15] = 0xEC4E6C89; + + if (S->nullt == 0) { + v[12] ^= S->t[0]; + v[13] ^= S->t[0]; + v[14] ^= S->t[1]; + v[15] ^= S->t[1]; + } + + for (i = 0; i < 14; ++i) { + G(0, 4, 8, 12, 0); + G(1, 5, 9, 13, 2); + G(2, 6, 10, 14, 4); + G(3, 7, 11, 15, 6); + G(3, 4, 9, 14, 14); + G(2, 7, 8, 13, 12); + G(0, 5, 10, 15, 8); + G(1, 6, 11, 12, 10); + } + + for (i = 0; i < 16; ++i) S->h[i % 8] ^= v[i]; + for (i = 0; i < 8; ++i) S->h[i] ^= S->s[i % 4]; +} + +void blake256_init(state *S) { + S->h[0] = 0x6A09E667; + S->h[1] = 0xBB67AE85; + S->h[2] = 0x3C6EF372; + S->h[3] = 0xA54FF53A; + S->h[4] = 0x510E527F; + S->h[5] = 0x9B05688C; + S->h[6] = 0x1F83D9AB; + S->h[7] = 0x5BE0CD19; + S->t[0] = S->t[1] = S->buflen = S->nullt = 0; + S->s[0] = S->s[1] = S->s[2] = S->s[3] = 0; +} + +void blake224_init(state *S) { + S->h[0] = 0xC1059ED8; + S->h[1] = 0x367CD507; + S->h[2] = 0x3070DD17; + S->h[3] = 0xF70E5939; + S->h[4] = 0xFFC00B31; + S->h[5] = 0x68581511; + S->h[6] = 0x64F98FA7; + S->h[7] = 0xBEFA4FA4; + S->t[0] = S->t[1] = S->buflen = S->nullt = 0; + S->s[0] = S->s[1] = S->s[2] = S->s[3] = 0; +} + +// datalen = number of bits +void blake256_update(state *S, const uint8_t *data, uint64_t datalen) { + int left = S->buflen >> 3; + int fill = 64 - left; + + if (left && (((datalen >> 3) & 0x3F) >= (unsigned) fill)) { + memcpy((void *) (S->buf + left), (void *) data, fill); + S->t[0] += 512; + if (S->t[0] == 0) S->t[1]++; + blake256_compress(S, S->buf); + data += fill; + datalen -= (fill << 3); + left = 0; + } + + while (datalen >= 512) { + S->t[0] += 512; + if (S->t[0] == 0) S->t[1]++; + blake256_compress(S, data); + data += 64; + datalen -= 512; + } + + if (datalen > 0) { + memcpy((void *) (S->buf + left), (void *) data, datalen >> 3); + S->buflen = (left << 3) + datalen; + } else { + S->buflen = 0; + } +} + +// datalen = number of bits +void blake224_update(state *S, const uint8_t *data, uint64_t datalen) { + blake256_update(S, data, datalen); +} + +void blake256_final_h(state *S, uint8_t *digest, uint8_t pa, uint8_t pb) { + uint8_t msglen[8]; + uint32_t lo = S->t[0] + S->buflen, hi = S->t[1]; + if (lo < (unsigned) S->buflen) hi++; + U32TO8(msglen + 0, hi); + U32TO8(msglen + 4, lo); + + if (S->buflen == 440) { /* one padding byte */ + S->t[0] -= 8; + blake256_update(S, &pa, 8); + } else { + if (S->buflen < 440) { /* enough space to fill the block */ + if (S->buflen == 0) S->nullt = 1; + S->t[0] -= 440 - S->buflen; + blake256_update(S, padding, 440 - S->buflen); + } else { /* need 2 compressions */ + S->t[0] -= 512 - S->buflen; + blake256_update(S, padding, 512 - S->buflen); + S->t[0] -= 440; + blake256_update(S, padding + 1, 440); + S->nullt = 1; + } + blake256_update(S, &pb, 8); + S->t[0] -= 8; + } + S->t[0] -= 64; + blake256_update(S, msglen, 64); + + U32TO8(digest + 0, S->h[0]); + U32TO8(digest + 4, S->h[1]); + U32TO8(digest + 8, S->h[2]); + U32TO8(digest + 12, S->h[3]); + U32TO8(digest + 16, S->h[4]); + U32TO8(digest + 20, S->h[5]); + U32TO8(digest + 24, S->h[6]); + U32TO8(digest + 28, S->h[7]); +} + +void blake256_final(state *S, uint8_t *digest) { + blake256_final_h(S, digest, 0x81, 0x01); +} + +void blake224_final(state *S, uint8_t *digest) { + blake256_final_h(S, digest, 0x80, 0x00); +} + +// inlen = number of bytes +void blake256_hash(uint8_t *out, const uint8_t *in, uint64_t inlen) { + state S; + blake256_init(&S); + blake256_update(&S, in, inlen * 8); + blake256_final(&S, out); +} + +// inlen = number of bytes +void blake224_hash(uint8_t *out, const uint8_t *in, uint64_t inlen) { + state S; + blake224_init(&S); + blake224_update(&S, in, inlen * 8); + blake224_final(&S, out); +} + +// keylen = number of bytes +void hmac_blake256_init(hmac_state *S, const uint8_t *_key, uint64_t keylen) { + const uint8_t *key = _key; + uint8_t keyhash[32]; + uint8_t pad[64]; + uint64_t i; + + if (keylen > 64) { + blake256_hash(keyhash, key, keylen); + key = keyhash; + keylen = 32; + } + + blake256_init(&S->inner); + memset(pad, 0x36, 64); + for (i = 0; i < keylen; ++i) { + pad[i] ^= key[i]; + } + blake256_update(&S->inner, pad, 512); + + blake256_init(&S->outer); + memset(pad, 0x5c, 64); + for (i = 0; i < keylen; ++i) { + pad[i] ^= key[i]; + } + blake256_update(&S->outer, pad, 512); + + memset(keyhash, 0, 32); +} + +// keylen = number of bytes +void hmac_blake224_init(hmac_state *S, const uint8_t *_key, uint64_t keylen) { + const uint8_t *key = _key; + uint8_t keyhash[32]; + uint8_t pad[64]; + uint64_t i; + + if (keylen > 64) { + blake256_hash(keyhash, key, keylen); + key = keyhash; + keylen = 28; + } + + blake224_init(&S->inner); + memset(pad, 0x36, 64); + for (i = 0; i < keylen; ++i) { + pad[i] ^= key[i]; + } + blake224_update(&S->inner, pad, 512); + + blake224_init(&S->outer); + memset(pad, 0x5c, 64); + for (i = 0; i < keylen; ++i) { + pad[i] ^= key[i]; + } + blake224_update(&S->outer, pad, 512); + + memset(keyhash, 0, 32); +} + +// datalen = number of bits +void hmac_blake256_update(hmac_state *S, const uint8_t *data, uint64_t datalen) { + // update the inner state + blake256_update(&S->inner, data, datalen); +} + +// datalen = number of bits +void hmac_blake224_update(hmac_state *S, const uint8_t *data, uint64_t datalen) { + // update the inner state + blake224_update(&S->inner, data, datalen); +} + +void hmac_blake256_final(hmac_state *S, uint8_t *digest) { + uint8_t ihash[32]; + blake256_final(&S->inner, ihash); + blake256_update(&S->outer, ihash, 256); + blake256_final(&S->outer, digest); + memset(ihash, 0, 32); +} + +void hmac_blake224_final(hmac_state *S, uint8_t *digest) { + uint8_t ihash[32]; + blake224_final(&S->inner, ihash); + blake224_update(&S->outer, ihash, 224); + blake224_final(&S->outer, digest); + memset(ihash, 0, 32); +} + +// keylen = number of bytes; inlen = number of bytes +void hmac_blake256_hash(uint8_t *out, const uint8_t *key, uint64_t keylen, const uint8_t *in, uint64_t inlen) { + hmac_state S; + hmac_blake256_init(&S, key, keylen); + hmac_blake256_update(&S, in, inlen * 8); + hmac_blake256_final(&S, out); +} + +// keylen = number of bytes; inlen = number of bytes +void hmac_blake224_hash(uint8_t *out, const uint8_t *key, uint64_t keylen, const uint8_t *in, uint64_t inlen) { + hmac_state S; + hmac_blake224_init(&S, key, keylen); + hmac_blake224_update(&S, in, inlen * 8); + hmac_blake224_final(&S, out); +} diff --git a/src/crypto/blake256.h b/src/crypto/blake256.h new file mode 100644 index 000000000..b9c2aad0d --- /dev/null +++ b/src/crypto/blake256.h @@ -0,0 +1,43 @@ +#ifndef _BLAKE256_H_ +#define _BLAKE256_H_ + +#include + +typedef struct { + uint32_t h[8], s[4], t[2]; + int buflen, nullt; + uint8_t buf[64]; +} state; + +typedef struct { + state inner; + state outer; +} hmac_state; + +void blake256_init(state *); +void blake224_init(state *); + +void blake256_update(state *, const uint8_t *, uint64_t); +void blake224_update(state *, const uint8_t *, uint64_t); + +void blake256_final(state *, uint8_t *); +void blake224_final(state *, uint8_t *); + +void blake256_hash(uint8_t *, const uint8_t *, uint64_t); +void blake224_hash(uint8_t *, const uint8_t *, uint64_t); + +/* HMAC functions: */ + +void hmac_blake256_init(hmac_state *, const uint8_t *, uint64_t); +void hmac_blake224_init(hmac_state *, const uint8_t *, uint64_t); + +void hmac_blake256_update(hmac_state *, const uint8_t *, uint64_t); +void hmac_blake224_update(hmac_state *, const uint8_t *, uint64_t); + +void hmac_blake256_final(hmac_state *, uint8_t *); +void hmac_blake224_final(hmac_state *, uint8_t *); + +void hmac_blake256_hash(uint8_t *, const uint8_t *, uint64_t, const uint8_t *, uint64_t); +void hmac_blake224_hash(uint8_t *, const uint8_t *, uint64_t, const uint8_t *, uint64_t); + +#endif /* _BLAKE256_H_ */ diff --git a/src/crypto/chacha8.c b/src/crypto/chacha8.c new file mode 100644 index 000000000..df135af59 --- /dev/null +++ b/src/crypto/chacha8.c @@ -0,0 +1,170 @@ +/* +chacha-merged.c version 20080118 +D. J. Bernstein +Public domain. +*/ + +#include +#include +#include + +#include "chacha8.h" +#include "common/int-util.h" +#include "warnings.h" + +/* + * The following macros are used to obtain exact-width results. + */ +#define U8V(v) ((uint8_t)(v) & UINT8_C(0xFF)) +#define U32V(v) ((uint32_t)(v) & UINT32_C(0xFFFFFFFF)) + +/* + * The following macros load words from an array of bytes with + * different types of endianness, and vice versa. + */ +#define U8TO32_LITTLE(p) SWAP32LE(((uint32_t*)(p))[0]) +#define U32TO8_LITTLE(p, v) (((uint32_t*)(p))[0] = SWAP32LE(v)) + +#define ROTATE(v,c) (rol32(v,c)) +#define XOR(v,w) ((v) ^ (w)) +#define PLUS(v,w) (U32V((v) + (w))) +#define PLUSONE(v) (PLUS((v),1)) + +#define QUARTERROUND(a,b,c,d) \ + a = PLUS(a,b); d = ROTATE(XOR(d,a),16); \ + c = PLUS(c,d); b = ROTATE(XOR(b,c),12); \ + a = PLUS(a,b); d = ROTATE(XOR(d,a), 8); \ + c = PLUS(c,d); b = ROTATE(XOR(b,c), 7); + +static const char sigma[] = "expand 32-byte k"; + +DISABLE_GCC_AND_CLANG_WARNING(strict-aliasing) + +void chacha8(const void* data, size_t length, const uint8_t* key, const uint8_t* iv, char* cipher) { + uint32_t x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15; + uint32_t j0, j1, j2, j3, j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15; + char* ctarget = 0; + char tmp[64]; + int i; + + if (!length) return; + + j0 = U8TO32_LITTLE(sigma + 0); + j1 = U8TO32_LITTLE(sigma + 4); + j2 = U8TO32_LITTLE(sigma + 8); + j3 = U8TO32_LITTLE(sigma + 12); + j4 = U8TO32_LITTLE(key + 0); + j5 = U8TO32_LITTLE(key + 4); + j6 = U8TO32_LITTLE(key + 8); + j7 = U8TO32_LITTLE(key + 12); + j8 = U8TO32_LITTLE(key + 16); + j9 = U8TO32_LITTLE(key + 20); + j10 = U8TO32_LITTLE(key + 24); + j11 = U8TO32_LITTLE(key + 28); + j12 = 0; + j13 = 0; + j14 = U8TO32_LITTLE(iv + 0); + j15 = U8TO32_LITTLE(iv + 4); + + for (;;) { + if (length < 64) { + memcpy(tmp, data, length); + data = tmp; + ctarget = cipher; + cipher = tmp; + } + x0 = j0; + x1 = j1; + x2 = j2; + x3 = j3; + x4 = j4; + x5 = j5; + x6 = j6; + x7 = j7; + x8 = j8; + x9 = j9; + x10 = j10; + x11 = j11; + x12 = j12; + x13 = j13; + x14 = j14; + x15 = j15; + for (i = 8;i > 0;i -= 2) { + QUARTERROUND( x0, x4, x8,x12) + QUARTERROUND( x1, x5, x9,x13) + QUARTERROUND( x2, x6,x10,x14) + QUARTERROUND( x3, x7,x11,x15) + QUARTERROUND( x0, x5,x10,x15) + QUARTERROUND( x1, x6,x11,x12) + QUARTERROUND( x2, x7, x8,x13) + QUARTERROUND( x3, x4, x9,x14) + } + x0 = PLUS( x0, j0); + x1 = PLUS( x1, j1); + x2 = PLUS( x2, j2); + x3 = PLUS( x3, j3); + x4 = PLUS( x4, j4); + x5 = PLUS( x5, j5); + x6 = PLUS( x6, j6); + x7 = PLUS( x7, j7); + x8 = PLUS( x8, j8); + x9 = PLUS( x9, j9); + x10 = PLUS(x10,j10); + x11 = PLUS(x11,j11); + x12 = PLUS(x12,j12); + x13 = PLUS(x13,j13); + x14 = PLUS(x14,j14); + x15 = PLUS(x15,j15); + + x0 = XOR( x0,U8TO32_LITTLE((uint8_t*)data + 0)); + x1 = XOR( x1,U8TO32_LITTLE((uint8_t*)data + 4)); + x2 = XOR( x2,U8TO32_LITTLE((uint8_t*)data + 8)); + x3 = XOR( x3,U8TO32_LITTLE((uint8_t*)data + 12)); + x4 = XOR( x4,U8TO32_LITTLE((uint8_t*)data + 16)); + x5 = XOR( x5,U8TO32_LITTLE((uint8_t*)data + 20)); + x6 = XOR( x6,U8TO32_LITTLE((uint8_t*)data + 24)); + x7 = XOR( x7,U8TO32_LITTLE((uint8_t*)data + 28)); + x8 = XOR( x8,U8TO32_LITTLE((uint8_t*)data + 32)); + x9 = XOR( x9,U8TO32_LITTLE((uint8_t*)data + 36)); + x10 = XOR(x10,U8TO32_LITTLE((uint8_t*)data + 40)); + x11 = XOR(x11,U8TO32_LITTLE((uint8_t*)data + 44)); + x12 = XOR(x12,U8TO32_LITTLE((uint8_t*)data + 48)); + x13 = XOR(x13,U8TO32_LITTLE((uint8_t*)data + 52)); + x14 = XOR(x14,U8TO32_LITTLE((uint8_t*)data + 56)); + x15 = XOR(x15,U8TO32_LITTLE((uint8_t*)data + 60)); + + j12 = PLUSONE(j12); + if (!j12) + { + j13 = PLUSONE(j13); + /* stopping at 2^70 bytes per iv is user's responsibility */ + } + + U32TO8_LITTLE(cipher + 0,x0); + U32TO8_LITTLE(cipher + 4,x1); + U32TO8_LITTLE(cipher + 8,x2); + U32TO8_LITTLE(cipher + 12,x3); + U32TO8_LITTLE(cipher + 16,x4); + U32TO8_LITTLE(cipher + 20,x5); + U32TO8_LITTLE(cipher + 24,x6); + U32TO8_LITTLE(cipher + 28,x7); + U32TO8_LITTLE(cipher + 32,x8); + U32TO8_LITTLE(cipher + 36,x9); + U32TO8_LITTLE(cipher + 40,x10); + U32TO8_LITTLE(cipher + 44,x11); + U32TO8_LITTLE(cipher + 48,x12); + U32TO8_LITTLE(cipher + 52,x13); + U32TO8_LITTLE(cipher + 56,x14); + U32TO8_LITTLE(cipher + 60,x15); + + if (length <= 64) { + if (length < 64) { + memcpy(ctarget, cipher, length); + } + return; + } + length -= 64; + cipher += 64; + data = (uint8_t*)data + 64; + } +} diff --git a/src/crypto/chacha8.h b/src/crypto/chacha8.h new file mode 100644 index 000000000..e4fe46799 --- /dev/null +++ b/src/crypto/chacha8.h @@ -0,0 +1,56 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include +#include + +#define CHACHA8_KEY_SIZE 32 +#define CHACHA8_IV_SIZE 8 + +#if defined(__cplusplus) +#include + +#include "hash.h" + +namespace crypto { + extern "C" { +#endif + void chacha8(const void* data, size_t length, const uint8_t* key, const uint8_t* iv, char* cipher); +#if defined(__cplusplus) + } + +#pragma pack(push, 1) + struct chacha8_key { + uint8_t data[CHACHA8_KEY_SIZE]; + + ~chacha8_key() + { + memset(data, 0, sizeof(data)); + } + }; + + // MS VC 2012 doesn't interpret `class chacha8_iv` as POD in spite of [9.0.10], so it is a struct + struct chacha8_iv { + uint8_t data[CHACHA8_IV_SIZE]; + }; +#pragma pack(pop) + + static_assert(sizeof(chacha8_key) == CHACHA8_KEY_SIZE && sizeof(chacha8_iv) == CHACHA8_IV_SIZE, "Invalid structure size"); + + inline void chacha8(const void* data, std::size_t length, const chacha8_key& key, const chacha8_iv& iv, char* cipher) { + chacha8(data, length, reinterpret_cast(&key), reinterpret_cast(&iv), cipher); + } + + inline void generate_chacha8_key(std::string password, chacha8_key& key) { + static_assert(sizeof(chacha8_key) <= sizeof(hash), "Size of hash must be at least that of chacha8_key"); + char pwd_hash[HASH_SIZE]; + crypto::cn_slow_hash(password.data(), password.size(), pwd_hash); + memcpy(&key, pwd_hash, sizeof(key)); + memset(pwd_hash, 0, sizeof(pwd_hash)); + } +} + +#endif diff --git a/src/crypto/crypto-ops-data.c b/src/crypto/crypto-ops-data.c index 8eb2fbb0b..48bfe21a2 100644 --- a/src/crypto/crypto-ops-data.c +++ b/src/crypto/crypto-ops-data.c @@ -1,3 +1,7 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + #include #include "crypto-ops.h" @@ -839,4 +843,4 @@ const fe fe_ma = {-486662, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* -A */ const fe fe_fffb1 = {-31702527, -2466483, -26106795, -12203692, -12169197, -321052, 14850977, -10296299, -16929438, -407568}; /* sqrt(-2 * A * (A + 2)) */ const fe fe_fffb2 = {8166131, -6741800, -17040804, 3154616, 21461005, 1466302, -30876704, -6368709, 10503587, -13363080}; /* sqrt(2 * A * (A + 2)) */ const fe fe_fffb3 = {-13620103, 14639558, 4532995, 7679154, 16815101, -15883539, -22863840, -14813421, 13716513, -6477756}; /* sqrt(-sqrt(-1) * A * (A + 2)) */ -const fe fe_fffb4 = {-21786234, -12173074, 21573800, 4524538, -4645904, 16204591, 8012863, -8444712, 3212926, 6885324}; /* sqrt(sqrt(-1) * A * (A + 2)) */ \ No newline at end of file +const fe fe_fffb4 = {-21786234, -12173074, 21573800, 4524538, -4645904, 16204591, 8012863, -8444712, 3212926, 6885324}; /* sqrt(sqrt(-1) * A * (A + 2)) */ diff --git a/src/crypto/crypto-ops.c b/src/crypto/crypto-ops.c index dc2c7092c..97e7df50e 100644 --- a/src/crypto/crypto-ops.c +++ b/src/crypto/crypto-ops.c @@ -1,8 +1,15 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + #include #include +#include "warnings.h" #include "crypto-ops.h" +DISABLE_VS_WARNINGS(4146 4244) + /* Predeclarations */ static void fe_mul(fe, const fe, const fe); @@ -232,28 +239,53 @@ static void fe_invert(fe out, const fe z) { fe t3; int i; - fe_sq(t0,z); for (i = 1;i < 1;++i) fe_sq(t0,t0); - fe_sq(t1,t0); for (i = 1;i < 2;++i) fe_sq(t1,t1); - fe_mul(t1,z,t1); - fe_mul(t0,t0,t1); - fe_sq(t2,t0); for (i = 1;i < 1;++i) fe_sq(t2,t2); - fe_mul(t1,t1,t2); - fe_sq(t2,t1); for (i = 1;i < 5;++i) fe_sq(t2,t2); - fe_mul(t1,t2,t1); - fe_sq(t2,t1); for (i = 1;i < 10;++i) fe_sq(t2,t2); - fe_mul(t2,t2,t1); - fe_sq(t3,t2); for (i = 1;i < 20;++i) fe_sq(t3,t3); - fe_mul(t2,t3,t2); - fe_sq(t2,t2); for (i = 1;i < 10;++i) fe_sq(t2,t2); - fe_mul(t1,t2,t1); - fe_sq(t2,t1); for (i = 1;i < 50;++i) fe_sq(t2,t2); - fe_mul(t2,t2,t1); - fe_sq(t3,t2); for (i = 1;i < 100;++i) fe_sq(t3,t3); - fe_mul(t2,t3,t2); - fe_sq(t2,t2); for (i = 1;i < 50;++i) fe_sq(t2,t2); - fe_mul(t1,t2,t1); - fe_sq(t1,t1); for (i = 1;i < 5;++i) fe_sq(t1,t1); - fe_mul(out,t1,t0); + fe_sq(t0, z); + fe_sq(t1, t0); + fe_sq(t1, t1); + fe_mul(t1, z, t1); + fe_mul(t0, t0, t1); + fe_sq(t2, t0); + fe_mul(t1, t1, t2); + fe_sq(t2, t1); + for (i = 0; i < 4; ++i) { + fe_sq(t2, t2); + } + fe_mul(t1, t2, t1); + fe_sq(t2, t1); + for (i = 0; i < 9; ++i) { + fe_sq(t2, t2); + } + fe_mul(t2, t2, t1); + fe_sq(t3, t2); + for (i = 0; i < 19; ++i) { + fe_sq(t3, t3); + } + fe_mul(t2, t3, t2); + fe_sq(t2, t2); + for (i = 0; i < 9; ++i) { + fe_sq(t2, t2); + } + fe_mul(t1, t2, t1); + fe_sq(t2, t1); + for (i = 0; i < 49; ++i) { + fe_sq(t2, t2); + } + fe_mul(t2, t2, t1); + fe_sq(t3, t2); + for (i = 0; i < 99; ++i) { + fe_sq(t3, t3); + } + fe_mul(t2, t3, t2); + fe_sq(t2, t2); + for (i = 0; i < 49; ++i) { + fe_sq(t2, t2); + } + fe_mul(t1, t2, t1); + fe_sq(t1, t1); + for (i = 0; i < 4; ++i) { + fe_sq(t1, t1); + } + fe_mul(out, t1, t0); return; } @@ -1089,8 +1121,9 @@ static void slide(signed char *r, const unsigned char *a) { int b; int k; - for (i = 0; i < 256; ++i) + for (i = 0; i < 256; ++i) { r[i] = 1 & (a[i >> 3] >> (i & 7)); + } for (i = 0; i < 256; ++i) { if (r[i]) { @@ -1417,8 +1450,8 @@ void ge_p3_tobytes(unsigned char *s, const ge_p3 *h) { fe y; fe_invert(recip, h->Z); - fe_mul(x,h->X, recip); - fe_mul(y,h->Y, recip); + fe_mul(x, h->X, recip); + fe_mul(y, h->Y, recip); fe_tobytes(s, y); s[31] ^= fe_isnegative(x) << 7; } @@ -1492,7 +1525,7 @@ void ge_scalarmult_base(ge_p3 *h, const unsigned char *a) { ge_precomp t; int i; - for (i = 0;i < 32;++i) { + for (i = 0; i < 32; ++i) { e[2 * i + 0] = (a[i] >> 0) & 15; e[2 * i + 1] = (a[i] >> 4) & 15; } @@ -1500,7 +1533,7 @@ void ge_scalarmult_base(ge_p3 *h, const unsigned char *a) { /* e[63] is between 0 and 7 */ carry = 0; - for (i = 0;i < 63;++i) { + for (i = 0; i < 63; ++i) { e[i] += carry; carry = e[i] + 8; carry >>= 4; @@ -1510,7 +1543,7 @@ void ge_scalarmult_base(ge_p3 *h, const unsigned char *a) { /* each e[i] is between -8 and 8 */ ge_p3_0(h); - for (i = 1;i < 64;i += 2) { + for (i = 1; i < 64; i += 2) { select(&t, i / 2, e[i]); ge_madd(&r, h, &t); ge_p1p1_to_p3(h, &r); } @@ -1520,7 +1553,7 @@ void ge_scalarmult_base(ge_p3 *h, const unsigned char *a) { ge_p2_dbl(&r, &s); ge_p1p1_to_p2(&s, &r); ge_p2_dbl(&r, &s); ge_p1p1_to_p3(h, &r); - for (i = 0;i < 64;i += 2) { + for (i = 0; i < 64; i += 2) { select(&t, i / 2, e[i]); ge_madd(&r, h, &t); ge_p1p1_to_p3(h, &r); } @@ -1622,7 +1655,6 @@ void sc_reduce(unsigned char *s) { s14 -= s23 * 997805; s15 += s23 * 136657; s16 -= s23 * 683901; - s23 = 0; s10 += s22 * 666643; s11 += s22 * 470296; @@ -1630,7 +1662,6 @@ void sc_reduce(unsigned char *s) { s13 -= s22 * 997805; s14 += s22 * 136657; s15 -= s22 * 683901; - s22 = 0; s9 += s21 * 666643; s10 += s21 * 470296; @@ -1638,7 +1669,6 @@ void sc_reduce(unsigned char *s) { s12 -= s21 * 997805; s13 += s21 * 136657; s14 -= s21 * 683901; - s21 = 0; s8 += s20 * 666643; s9 += s20 * 470296; @@ -1646,7 +1676,6 @@ void sc_reduce(unsigned char *s) { s11 -= s20 * 997805; s12 += s20 * 136657; s13 -= s20 * 683901; - s20 = 0; s7 += s19 * 666643; s8 += s19 * 470296; @@ -1654,7 +1683,6 @@ void sc_reduce(unsigned char *s) { s10 -= s19 * 997805; s11 += s19 * 136657; s12 -= s19 * 683901; - s19 = 0; s6 += s18 * 666643; s7 += s18 * 470296; @@ -1662,7 +1690,6 @@ void sc_reduce(unsigned char *s) { s9 -= s18 * 997805; s10 += s18 * 136657; s11 -= s18 * 683901; - s18 = 0; carry6 = (s6 + (1<<20)) >> 21; s7 += carry6; s6 -= carry6 << 21; carry8 = (s8 + (1<<20)) >> 21; s9 += carry8; s8 -= carry8 << 21; @@ -1683,7 +1710,6 @@ void sc_reduce(unsigned char *s) { s8 -= s17 * 997805; s9 += s17 * 136657; s10 -= s17 * 683901; - s17 = 0; s4 += s16 * 666643; s5 += s16 * 470296; @@ -1691,7 +1717,6 @@ void sc_reduce(unsigned char *s) { s7 -= s16 * 997805; s8 += s16 * 136657; s9 -= s16 * 683901; - s16 = 0; s3 += s15 * 666643; s4 += s15 * 470296; @@ -1699,7 +1724,6 @@ void sc_reduce(unsigned char *s) { s6 -= s15 * 997805; s7 += s15 * 136657; s8 -= s15 * 683901; - s15 = 0; s2 += s14 * 666643; s3 += s14 * 470296; @@ -1707,7 +1731,6 @@ void sc_reduce(unsigned char *s) { s5 -= s14 * 997805; s6 += s14 * 136657; s7 -= s14 * 683901; - s14 = 0; s1 += s13 * 666643; s2 += s13 * 470296; @@ -1715,7 +1738,6 @@ void sc_reduce(unsigned char *s) { s4 -= s13 * 997805; s5 += s13 * 136657; s6 -= s13 * 683901; - s13 = 0; s0 += s12 * 666643; s1 += s12 * 470296; @@ -1766,7 +1788,6 @@ void sc_reduce(unsigned char *s) { s3 -= s12 * 997805; s4 += s12 * 136657; s5 -= s12 * 683901; - s12 = 0; carry0 = s0 >> 21; s1 += carry0; s0 -= carry0 << 21; carry1 = s1 >> 21; s2 += carry1; s1 -= carry1 << 21; @@ -2196,7 +2217,6 @@ void sc_reduce32(unsigned char *s) { s3 -= s12 * 997805; s4 += s12 * 136657; s5 -= s12 * 683901; - s12 = 0; carry0 = s0 >> 21; s1 += carry0; s0 -= carry0 << 21; carry1 = s1 >> 21; s2 += carry1; s1 -= carry1 << 21; @@ -2336,7 +2356,6 @@ void sc_add(unsigned char *s, const unsigned char *a, const unsigned char *b) { s3 -= s12 * 997805; s4 += s12 * 136657; s5 -= s12 * 683901; - s12 = 0; carry0 = s0 >> 21; s1 += carry0; s0 -= carry0 << 21; carry1 = s1 >> 21; s2 += carry1; s1 -= carry1 << 21; @@ -2476,7 +2495,6 @@ void sc_sub(unsigned char *s, const unsigned char *a, const unsigned char *b) { s3 -= s12 * 997805; s4 += s12 * 136657; s5 -= s12 * 683901; - s12 = 0; carry0 = s0 >> 21; s1 += carry0; s0 -= carry0 << 21; carry1 = s1 >> 21; s2 += carry1; s1 -= carry1 << 21; @@ -2676,7 +2694,6 @@ void sc_mulsub(unsigned char *s, const unsigned char *a, const unsigned char *b, s14 -= s23 * 997805; s15 += s23 * 136657; s16 -= s23 * 683901; - s23 = 0; s10 += s22 * 666643; s11 += s22 * 470296; @@ -2684,7 +2701,6 @@ void sc_mulsub(unsigned char *s, const unsigned char *a, const unsigned char *b, s13 -= s22 * 997805; s14 += s22 * 136657; s15 -= s22 * 683901; - s22 = 0; s9 += s21 * 666643; s10 += s21 * 470296; @@ -2692,7 +2708,6 @@ void sc_mulsub(unsigned char *s, const unsigned char *a, const unsigned char *b, s12 -= s21 * 997805; s13 += s21 * 136657; s14 -= s21 * 683901; - s21 = 0; s8 += s20 * 666643; s9 += s20 * 470296; @@ -2700,7 +2715,6 @@ void sc_mulsub(unsigned char *s, const unsigned char *a, const unsigned char *b, s11 -= s20 * 997805; s12 += s20 * 136657; s13 -= s20 * 683901; - s20 = 0; s7 += s19 * 666643; s8 += s19 * 470296; @@ -2708,7 +2722,6 @@ void sc_mulsub(unsigned char *s, const unsigned char *a, const unsigned char *b, s10 -= s19 * 997805; s11 += s19 * 136657; s12 -= s19 * 683901; - s19 = 0; s6 += s18 * 666643; s7 += s18 * 470296; @@ -2716,7 +2729,6 @@ void sc_mulsub(unsigned char *s, const unsigned char *a, const unsigned char *b, s9 -= s18 * 997805; s10 += s18 * 136657; s11 -= s18 * 683901; - s18 = 0; carry6 = (s6 + (1<<20)) >> 21; s7 += carry6; s6 -= carry6 << 21; carry8 = (s8 + (1<<20)) >> 21; s9 += carry8; s8 -= carry8 << 21; @@ -2737,7 +2749,6 @@ void sc_mulsub(unsigned char *s, const unsigned char *a, const unsigned char *b, s8 -= s17 * 997805; s9 += s17 * 136657; s10 -= s17 * 683901; - s17 = 0; s4 += s16 * 666643; s5 += s16 * 470296; @@ -2745,7 +2756,6 @@ void sc_mulsub(unsigned char *s, const unsigned char *a, const unsigned char *b, s7 -= s16 * 997805; s8 += s16 * 136657; s9 -= s16 * 683901; - s16 = 0; s3 += s15 * 666643; s4 += s15 * 470296; @@ -2753,7 +2763,6 @@ void sc_mulsub(unsigned char *s, const unsigned char *a, const unsigned char *b, s6 -= s15 * 997805; s7 += s15 * 136657; s8 -= s15 * 683901; - s15 = 0; s2 += s14 * 666643; s3 += s14 * 470296; @@ -2761,7 +2770,6 @@ void sc_mulsub(unsigned char *s, const unsigned char *a, const unsigned char *b, s5 -= s14 * 997805; s6 += s14 * 136657; s7 -= s14 * 683901; - s14 = 0; s1 += s13 * 666643; s2 += s13 * 470296; @@ -2769,7 +2777,6 @@ void sc_mulsub(unsigned char *s, const unsigned char *a, const unsigned char *b, s4 -= s13 * 997805; s5 += s13 * 136657; s6 -= s13 * 683901; - s13 = 0; s0 += s12 * 666643; s1 += s12 * 470296; @@ -2820,7 +2827,6 @@ void sc_mulsub(unsigned char *s, const unsigned char *a, const unsigned char *b, s3 -= s12 * 997805; s4 += s12 * 136657; s5 -= s12 * 683901; - s12 = 0; carry0 = s0 >> 21; s1 += carry0; s0 -= carry0 << 21; carry1 = s1 >> 21; s2 += carry1; s1 -= carry1 << 21; @@ -2890,4 +2896,4 @@ int sc_isnonzero(const unsigned char *s) { s[9] | s[10] | s[11] | s[12] | s[13] | s[14] | s[15] | s[16] | s[17] | s[18] | s[19] | s[20] | s[21] | s[22] | s[23] | s[24] | s[25] | s[26] | s[27] | s[28] | s[29] | s[30] | s[31]) - 1) >> 8) + 1; -} \ No newline at end of file +} diff --git a/src/crypto/crypto-ops.h b/src/crypto/crypto-ops.h index 3c253bb5e..9d07fc8b0 100644 --- a/src/crypto/crypto-ops.h +++ b/src/crypto/crypto-ops.h @@ -1,3 +1,7 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + #pragma once /* From fe.h */ @@ -112,4 +116,4 @@ void sc_add(unsigned char *, const unsigned char *, const unsigned char *); void sc_sub(unsigned char *, const unsigned char *, const unsigned char *); void sc_mulsub(unsigned char *, const unsigned char *, const unsigned char *, const unsigned char *); int sc_check(const unsigned char *); -int sc_isnonzero(const unsigned char *); /* Doesn't normalize */ \ No newline at end of file +int sc_isnonzero(const unsigned char *); /* Doesn't normalize */ diff --git a/src/crypto/crypto.cpp b/src/crypto/crypto.cpp index 020e413e9..f5f525700 100644 --- a/src/crypto/crypto.cpp +++ b/src/crypto/crypto.cpp @@ -1,3 +1,7 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + #include #include #include @@ -5,23 +9,31 @@ #include #include #include +#include +#include "common/varint.h" +#include "warnings.h" #include "crypto.h" -#include "random.h" +#include "hash.h" namespace crypto { using std::abort; using std::int32_t; using std::int64_t; + using std::lock_guard; + using std::mutex; using std::size_t; using std::uint32_t; using std::uint64_t; extern "C" { #include "crypto-ops.h" +#include "random.h" } + mutex random_lock; + static inline unsigned char *operator &(ec_point &point) { return &reinterpret_cast(point); } @@ -46,11 +58,12 @@ namespace crypto { } static inline void hash_to_scalar(const void *data, size_t length, ec_scalar &res) { - keccak(data, length, reinterpret_cast(res)); + cn_fast_hash(data, length, reinterpret_cast(res)); sc_reduce32(&res); } void crypto_ops::generate_keys(public_key &pub, secret_key &sec) { + lock_guard lock(random_lock); ge_p3 point; random_scalar(sec); ge_scalarmult_base(&point, &sec); @@ -62,13 +75,79 @@ namespace crypto { return ge_frombytes_vartime(&point, &key) == 0; } + bool crypto_ops::secret_key_to_public_key(const secret_key &sec, public_key &pub) { + ge_p3 point; + if (sc_check(&sec) != 0) { + return false; + } + ge_scalarmult_base(&point, &sec); + ge_p3_tobytes(&pub, &point); + return true; + } + + bool crypto_ops::generate_key_derivation(const public_key &key1, const secret_key &key2, key_derivation &derivation) { + ge_p3 point; + ge_p2 point2; + ge_p1p1 point3; + assert(sc_check(&key2) == 0); + if (ge_frombytes_vartime(&point, &key1) != 0) { + return false; + } + ge_scalarmult(&point2, &key2, &point); + ge_mul8(&point3, &point2); + ge_p1p1_to_p2(&point2, &point3); + ge_tobytes(&derivation, &point2); + return true; + } + + static void derivation_to_scalar(const key_derivation &derivation, size_t output_index, ec_scalar &res) { + struct { + key_derivation derivation; + char output_index[(sizeof(size_t) * 8 + 6) / 7]; + } buf; + char *end = buf.output_index; + buf.derivation = derivation; + tools::write_varint(end, output_index); + assert(end <= buf.output_index + sizeof buf.output_index); + hash_to_scalar(&buf, end - reinterpret_cast(&buf), res); + } + + bool crypto_ops::derive_public_key(const key_derivation &derivation, size_t output_index, + const public_key &base, public_key &derived_key) { + ec_scalar scalar; + ge_p3 point1; + ge_p3 point2; + ge_cached point3; + ge_p1p1 point4; + ge_p2 point5; + if (ge_frombytes_vartime(&point1, &base) != 0) { + return false; + } + derivation_to_scalar(derivation, output_index, scalar); + ge_scalarmult_base(&point2, &scalar); + ge_p3_to_cached(&point3, &point2); + ge_add(&point4, &point1, &point3); + ge_p1p1_to_p2(&point5, &point4); + ge_tobytes(&derived_key, &point5); + return true; + } + + void crypto_ops::derive_secret_key(const key_derivation &derivation, size_t output_index, + const secret_key &base, secret_key &derived_key) { + ec_scalar scalar; + assert(sc_check(&base) == 0); + derivation_to_scalar(derivation, output_index, scalar); + sc_add(&derived_key, &base, &scalar); + } + struct s_comm { hash h; ec_point key; ec_point comm; }; - void crypto_ops::generate_signature(const hash &message_hash, const public_key &pub, const secret_key &sec, signature &sig) { + void crypto_ops::generate_signature(const hash &prefix_hash, const public_key &pub, const secret_key &sec, signature &sig) { + lock_guard lock(random_lock); ge_p3 tmp3; ec_scalar k; s_comm buf; @@ -82,7 +161,7 @@ namespace crypto { assert(pub == t2); } #endif - buf.h = message_hash; + buf.h = prefix_hash; buf.key = pub; random_scalar(k); ge_scalarmult_base(&tmp3, &k); @@ -91,13 +170,13 @@ namespace crypto { sc_mulsub(&sig.r, &sig.c, &sec, &k); } - bool crypto_ops::check_signature(const hash &message_hash, const public_key &pub, const signature &sig) { + bool crypto_ops::check_signature(const hash &prefix_hash, const public_key &pub, const signature &sig) { ge_p2 tmp2; ge_p3 tmp3; ec_scalar c; s_comm buf; assert(check_key(pub)); - buf.h = message_hash; + buf.h = prefix_hash; buf.key = pub; if (ge_frombytes_vartime(&tmp3, &pub) != 0) { abort(); @@ -116,7 +195,7 @@ namespace crypto { hash h; ge_p2 point; ge_p1p1 point2; - keccak(std::addressof(key), sizeof(public_key), h); + cn_fast_hash(std::addressof(key), sizeof(public_key), h); ge_fromfe_frombytes_vartime(&point, reinterpret_cast(&h)); ge_mul8(&point2, &point); ge_p1p1_to_p3(&res, &point2); @@ -131,21 +210,25 @@ namespace crypto { ge_tobytes(&image, &point2); } +PUSH_WARNINGS +DISABLE_VS_WARNINGS(4200) struct rs_comm { hash h; struct { ec_point a, b; } ab[]; }; +POP_WARNINGS static inline size_t rs_comm_size(size_t pubs_count) { return sizeof(rs_comm) + pubs_count * sizeof(rs_comm().ab[0]); } - void crypto_ops::generate_ring_signature(const hash &message_hash, const key_image &image, + void crypto_ops::generate_ring_signature(const hash &prefix_hash, const key_image &image, const public_key *const *pubs, size_t pubs_count, const secret_key &sec, size_t sec_index, signature *sig) { + lock_guard lock(random_lock); size_t i; ge_p3 image_unp; ge_dsmp image_pre; @@ -173,7 +256,7 @@ namespace crypto { } ge_dsm_precomp(image_pre, &image_unp); sc_0(&sum); - buf->h = message_hash; + buf->h = prefix_hash; for (i = 0; i < pubs_count; i++) { ge_p2 tmp2; ge_p3 tmp3; @@ -203,7 +286,7 @@ namespace crypto { sc_mulsub(&sig[sec_index].r, &sig[sec_index].c, &sec, &k); } - bool crypto_ops::check_ring_signature(const hash &message_hash, const key_image &image, + bool crypto_ops::check_ring_signature(const hash &prefix_hash, const key_image &image, const public_key *const *pubs, size_t pubs_count, const signature *sig) { size_t i; @@ -221,7 +304,7 @@ namespace crypto { } ge_dsm_precomp(image_pre, &image_unp); sc_0(&sum); - buf->h = message_hash; + buf->h = prefix_hash; for (i = 0; i < pubs_count; i++) { ge_p2 tmp2; ge_p3 tmp3; @@ -242,4 +325,4 @@ namespace crypto { sc_sub(&h, &h, &sum); return sc_isnonzero(&h) == 0; } -} \ No newline at end of file +} diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h index 2dd2085ce..61641fbcf 100644 --- a/src/crypto/crypto.h +++ b/src/crypto/crypto.h @@ -1,66 +1,61 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + #pragma once #include -#include +#include +#include + +#include "common/pod-class.h" +#include "generic-ops.h" +#include "hash.h" namespace crypto { + extern "C" { +#include "random.h" + } + + extern std::mutex random_lock; + #pragma pack(push, 1) - class hash { + POD_CLASS ec_point { char data[32]; }; - class ec_point { + POD_CLASS ec_scalar { char data[32]; }; - class ec_scalar { - char data[32]; - }; - - class public_key: ec_point { + POD_CLASS public_key: ec_point { friend class crypto_ops; }; - class secret_key: ec_scalar { + POD_CLASS secret_key: ec_scalar { friend class crypto_ops; }; - class key_image: ec_point { + POD_CLASS key_derivation: ec_point { friend class crypto_ops; }; - class signature { + POD_CLASS key_image: ec_point { + friend class crypto_ops; + }; + + POD_CLASS signature { ec_scalar c, r; friend class crypto_ops; }; #pragma pack(pop) - static_assert(sizeof(hash) == 32 && sizeof(ec_point) == 32 && - sizeof(ec_scalar) == 32 && sizeof(public_key) == 32 && - sizeof(secret_key) == 32 && sizeof(key_image) == 32 && + static_assert(sizeof(ec_point) == 32 && sizeof(ec_scalar) == 32 && + sizeof(public_key) == 32 && sizeof(secret_key) == 32 && + sizeof(key_derivation) == 32 && sizeof(key_image) == 32 && sizeof(signature) == 64, "Invalid structure size"); - extern "C" { - void keccak(const void *data, std::size_t length, char *hash); - } - - inline void keccak(const void *data, std::size_t length, hash &hash) { - keccak(data, length, reinterpret_cast(&hash)); - } - - inline bool operator==(const hash &a, const hash &b) { - return std::memcmp(&a, &b, sizeof(struct hash)) == 0; - } - - inline bool operator==(const public_key &a, const public_key &b) { - return std::memcmp(&a, &b, sizeof(struct public_key)) == 0; - } - - inline bool operator==(const key_image &a, const key_image &b) { - return std::memcmp(&a, &b, sizeof(struct key_image)) == 0; - } - class crypto_ops { crypto_ops(); crypto_ops(const crypto_ops &); @@ -71,6 +66,14 @@ namespace crypto { friend void generate_keys(public_key &, secret_key &); static bool check_key(const public_key &); friend bool check_key(const public_key &); + static bool secret_key_to_public_key(const secret_key &, public_key &); + friend bool secret_key_to_public_key(const secret_key &, public_key &); + static bool generate_key_derivation(const public_key &, const secret_key &, key_derivation &); + friend bool generate_key_derivation(const public_key &, const secret_key &, key_derivation &); + static bool derive_public_key(const key_derivation &, std::size_t, const public_key &, public_key &); + friend bool derive_public_key(const key_derivation &, std::size_t, const public_key &, public_key &); + static void derive_secret_key(const key_derivation &, std::size_t, const secret_key &, secret_key &); + friend void derive_secret_key(const key_derivation &, std::size_t, const secret_key &, secret_key &); static void generate_signature(const hash &, const public_key &, const secret_key &, signature &); friend void generate_signature(const hash &, const public_key &, const secret_key &, signature &); static bool check_signature(const hash &, const public_key &, const signature &); @@ -87,80 +90,97 @@ namespace crypto { const public_key *const *, std::size_t, const signature *); }; - /* Generate a new key pair. - * pub: a newly generated public key. - * sec: a newly generated secret key. + /* Generate a value filled with random bytes. + */ + template + typename std::enable_if::value, T>::type rand() { + typename std::remove_cv::type res; + std::lock_guard lock(random_lock); + generate_random_bytes(sizeof(T), &res); + return res; + } + + /* Generate a new key pair */ inline void generate_keys(public_key &pub, secret_key &sec) { crypto_ops::generate_keys(pub, sec); } - /* Check a public key. - * key: a key to check. - * returns: true if the key is valid, false otherwise. + /* Check a public key. Returns true if it is valid, false otherwise. */ inline bool check_key(const public_key &key) { return crypto_ops::check_key(key); } - /* Sign a message. - * message_hash: hash of a message. - * pub: public key used for signing. Assumed to be valid. - * sec: secret key used for signing. Assumed to correspond to pub. - * sig: the resulting signature. + /* Checks a private key and computes the corresponding public key. */ - inline void generate_signature(const hash &message_hash, const public_key &pub, const secret_key &sec, signature &sig) { - crypto_ops::generate_signature(message_hash, pub, sec, sig); + inline bool secret_key_to_public_key(const secret_key &sec, public_key &pub) { + return crypto_ops::secret_key_to_public_key(sec, pub); } - /* Verify a signature. - * message_hash: hash of a message. - * pub: public key used for signing. Assumed to be valid, use check_key to check it first. - * sig: a signature. - * returns: true if the signature is valid, false otherwise. + /* To generate an ephemeral key used to send money to: + * * The sender generates a new key pair, which becomes the transaction key. The public transaction key is included in "extra" field. + * * Both the sender and the receiver generate key derivation from the transaction key, the receivers' "view" key and the output index. + * * The sender uses key derivation and the receivers' "spend" key to derive an ephemeral public key. + * * The receiver can either derive the public key (to check that the transaction is addressed to him) or the private key (to spend the money). */ - inline bool check_signature(const hash &message_hash, const public_key &pub, const signature &sig) { - return crypto_ops::check_signature(message_hash, pub, sig); + inline bool generate_key_derivation(const public_key &key1, const secret_key &key2, key_derivation &derivation) { + return crypto_ops::generate_key_derivation(key1, key2, derivation); + } + inline bool derive_public_key(const key_derivation &derivation, std::size_t output_index, + const public_key &base, public_key &derived_key) { + return crypto_ops::derive_public_key(derivation, output_index, base, derived_key); + } + inline void derive_secret_key(const key_derivation &derivation, std::size_t output_index, + const secret_key &base, secret_key &derived_key) { + crypto_ops::derive_secret_key(derivation, output_index, base, derived_key); } - /* Generate the image of a key. - * pub: public key used for signing. Assumed to be valid. - * sec: secret key used for signing. Assumed to correspond to pub. - * image: the resulting key image. + /* Generation and checking of a standard signature. + */ + inline void generate_signature(const hash &prefix_hash, const public_key &pub, const secret_key &sec, signature &sig) { + crypto_ops::generate_signature(prefix_hash, pub, sec, sig); + } + inline bool check_signature(const hash &prefix_hash, const public_key &pub, const signature &sig) { + return crypto_ops::check_signature(prefix_hash, pub, sig); + } + + /* To send money to a key: + * * The sender generates an ephemeral key and includes it in transaction output. + * * To spend the money, the receiver generates a key image from it. + * * Then he selects a bunch of outputs, including the one he spends, and uses them to generate a ring signature. + * To check the signature, it is necessary to collect all the keys that were used to generate it. To detect double spends, it is necessary to check that each key image is used at most once. */ inline void generate_key_image(const public_key &pub, const secret_key &sec, key_image &image) { crypto_ops::generate_key_image(pub, sec, image); } - - /* Sign a message using linkable ring signature. - * message_hash: hash of a message. - * image: image of the key used for signing. Use generate_key_image to create it. Assumed to correspond to the key used for signing. - * pubs: pointer to an array of pointers to public keys of a ring. All keys are assumed to be valid, use check_key to check them first. - * pubs_count: number of keys in a ring. - * sec: secret key used for signing. - * sec_index: index of the key used for signing in pubs. It is assumed that 0 <= sec_index < pubs_count and that sec corresponds to *pubs[sec_index]. - * sig: the resulting signature (occupies pubs_count elements). To verify it, image of the key is also necessary. - */ - inline void generate_ring_signature(const hash &message_hash, const key_image &image, + inline void generate_ring_signature(const hash &prefix_hash, const key_image &image, const public_key *const *pubs, std::size_t pubs_count, const secret_key &sec, std::size_t sec_index, signature *sig) { - crypto_ops::generate_ring_signature(message_hash, image, pubs, pubs_count, sec, sec_index, sig); + crypto_ops::generate_ring_signature(prefix_hash, image, pubs, pubs_count, sec, sec_index, sig); } - - /* Verify a linkable ring signature. - * message_hash: hash of a message. - * image: image of the key used for signing. - * pubs: pointer to an array of pointers to public keys of a ring. All keys are assumed to be valid, use check_key to check them first. - * pubs_count: number of keys in a ring. - * sig: a signature (occupies pubs_count elements). - * returns: true if the signature is valid, false otherwise. - */ - inline bool check_ring_signature(const hash &message_hash, const key_image &image, + inline bool check_ring_signature(const hash &prefix_hash, const key_image &image, const public_key *const *pubs, std::size_t pubs_count, const signature *sig) { - return crypto_ops::check_ring_signature(message_hash, image, pubs, pubs_count, sig); + return crypto_ops::check_ring_signature(prefix_hash, image, pubs, pubs_count, sig); } - /* To check whether two signatures are linked, compare their key images. */ -} \ No newline at end of file + /* Variants with vector parameters. + */ + inline void generate_ring_signature(const hash &prefix_hash, const key_image &image, + const std::vector &pubs, + const secret_key &sec, std::size_t sec_index, + signature *sig) { + generate_ring_signature(prefix_hash, image, pubs.data(), pubs.size(), sec, sec_index, sig); + } + inline bool check_ring_signature(const hash &prefix_hash, const key_image &image, + const std::vector &pubs, + const signature *sig) { + return check_ring_signature(prefix_hash, image, pubs.data(), pubs.size(), sig); + } +} + +CRYPTO_MAKE_COMPARABLE(public_key) +CRYPTO_MAKE_HASHABLE(key_image) +CRYPTO_MAKE_COMPARABLE(signature) diff --git a/src/crypto/example.cpp b/src/crypto/example.cpp deleted file mode 100644 index a8b6b7462..000000000 --- a/src/crypto/example.cpp +++ /dev/null @@ -1,42 +0,0 @@ -#include - -#include "crypto.h" -#include "random.h" - -using namespace crypto; - -int main(int argc, char *argv[]) { - char message[10]; - hash mh; - public_key pk1, pk2; - secret_key sk1, sk2; - key_image ki; - public_key *ppk[2]; - signature sig[2]; - bool res; - /* Call this before using functions that depend on randomness. */ - init_random(); - /* Generate a random message. */ - generate_random_bytes(sizeof message, message); - /* Find its hash */ - keccak(message, sizeof message, mh); - /* Generate some keys */ - generate_keys(pk1, sk1); - generate_keys(pk2, sk2); - /* Sign the message */ - generate_signature(mh, pk1, sk1, sig[0]); - /* Check the signature */ - res = check_signature(mh, pk1, sig[0]); - assert(res); - /* Sign the message using ring signature */ - /* First, generate a key image */ - generate_key_image(pk2, sk2, ki); - /* Then, generate the signature */ - ppk[0] = &pk1; - ppk[1] = &pk2; - generate_ring_signature(mh, ki, ppk, 2, sk2, 1, sig); - /* Check it */ - res = check_ring_signature(mh, ki, ppk, 2, sig); - assert(res); - return 0; -} \ No newline at end of file diff --git a/src/crypto/generic-ops.h b/src/crypto/generic-ops.h new file mode 100644 index 000000000..8cade72a8 --- /dev/null +++ b/src/crypto/generic-ops.h @@ -0,0 +1,36 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include +#include +#include + +#define CRYPTO_MAKE_COMPARABLE(type) \ +namespace crypto { \ + inline bool operator==(const type &_v1, const type &_v2) { \ + return std::memcmp(&_v1, &_v2, sizeof(type)) == 0; \ + } \ + inline bool operator!=(const type &_v1, const type &_v2) { \ + return std::memcmp(&_v1, &_v2, sizeof(type)) != 0; \ + } \ +} + +#define CRYPTO_MAKE_HASHABLE(type) \ +CRYPTO_MAKE_COMPARABLE(type) \ +namespace crypto { \ + static_assert(sizeof(std::size_t) <= sizeof(type), "Size of " #type " must be at least that of size_t"); \ + inline std::size_t hash_value(const type &_v) { \ + return reinterpret_cast(_v); \ + } \ +} \ +namespace std { \ + template<> \ + struct hash { \ + std::size_t operator()(const crypto::type &_v) const { \ + return reinterpret_cast(_v); \ + } \ + }; \ +} diff --git a/src/crypto/groestl.c b/src/crypto/groestl.c new file mode 100644 index 000000000..00bf987c9 --- /dev/null +++ b/src/crypto/groestl.c @@ -0,0 +1,360 @@ +/* hash.c April 2012 + * Groestl ANSI C code optimised for 32-bit machines + * Author: Thomas Krinninger + * + * This work is based on the implementation of + * Soeren S. Thomsen and Krystian Matusiewicz + * + * + */ + +#include "groestl.h" +#include "groestl_tables.h" + +#define P_TYPE 0 +#define Q_TYPE 1 + +const uint8_t shift_Values[2][8] = {{0,1,2,3,4,5,6,7},{1,3,5,7,0,2,4,6}}; + +const uint8_t indices_cyclic[15] = {0,1,2,3,4,5,6,7,0,1,2,3,4,5,6}; + + +#define ROTATE_COLUMN_DOWN(v1, v2, amount_bytes, temp_var) {temp_var = (v1<<(8*amount_bytes))|(v2>>(8*(4-amount_bytes))); \ + v2 = (v2<<(8*amount_bytes))|(v1>>(8*(4-amount_bytes))); \ + v1 = temp_var;} + + +#define COLUMN(x,y,i,c0,c1,c2,c3,c4,c5,c6,c7,tv1,tv2,tu,tl,t) \ + tu = T[2*(uint32_t)x[4*c0+0]]; \ + tl = T[2*(uint32_t)x[4*c0+0]+1]; \ + tv1 = T[2*(uint32_t)x[4*c1+1]]; \ + tv2 = T[2*(uint32_t)x[4*c1+1]+1]; \ + ROTATE_COLUMN_DOWN(tv1,tv2,1,t) \ + tu ^= tv1; \ + tl ^= tv2; \ + tv1 = T[2*(uint32_t)x[4*c2+2]]; \ + tv2 = T[2*(uint32_t)x[4*c2+2]+1]; \ + ROTATE_COLUMN_DOWN(tv1,tv2,2,t) \ + tu ^= tv1; \ + tl ^= tv2; \ + tv1 = T[2*(uint32_t)x[4*c3+3]]; \ + tv2 = T[2*(uint32_t)x[4*c3+3]+1]; \ + ROTATE_COLUMN_DOWN(tv1,tv2,3,t) \ + tu ^= tv1; \ + tl ^= tv2; \ + tl ^= T[2*(uint32_t)x[4*c4+0]]; \ + tu ^= T[2*(uint32_t)x[4*c4+0]+1]; \ + tv1 = T[2*(uint32_t)x[4*c5+1]]; \ + tv2 = T[2*(uint32_t)x[4*c5+1]+1]; \ + ROTATE_COLUMN_DOWN(tv1,tv2,1,t) \ + tl ^= tv1; \ + tu ^= tv2; \ + tv1 = T[2*(uint32_t)x[4*c6+2]]; \ + tv2 = T[2*(uint32_t)x[4*c6+2]+1]; \ + ROTATE_COLUMN_DOWN(tv1,tv2,2,t) \ + tl ^= tv1; \ + tu ^= tv2; \ + tv1 = T[2*(uint32_t)x[4*c7+3]]; \ + tv2 = T[2*(uint32_t)x[4*c7+3]+1]; \ + ROTATE_COLUMN_DOWN(tv1,tv2,3,t) \ + tl ^= tv1; \ + tu ^= tv2; \ + y[i] = tu; \ + y[i+1] = tl; + + +/* compute one round of P (short variants) */ +static void RND512P(uint8_t *x, uint32_t *y, uint32_t r) { + uint32_t temp_v1, temp_v2, temp_upper_value, temp_lower_value, temp; + uint32_t* x32 = (uint32_t*)x; + x32[ 0] ^= 0x00000000^r; + x32[ 2] ^= 0x00000010^r; + x32[ 4] ^= 0x00000020^r; + x32[ 6] ^= 0x00000030^r; + x32[ 8] ^= 0x00000040^r; + x32[10] ^= 0x00000050^r; + x32[12] ^= 0x00000060^r; + x32[14] ^= 0x00000070^r; + COLUMN(x,y, 0, 0, 2, 4, 6, 9, 11, 13, 15, temp_v1, temp_v2, temp_upper_value, temp_lower_value, temp); + COLUMN(x,y, 2, 2, 4, 6, 8, 11, 13, 15, 1, temp_v1, temp_v2, temp_upper_value, temp_lower_value, temp); + COLUMN(x,y, 4, 4, 6, 8, 10, 13, 15, 1, 3, temp_v1, temp_v2, temp_upper_value, temp_lower_value, temp); + COLUMN(x,y, 6, 6, 8, 10, 12, 15, 1, 3, 5, temp_v1, temp_v2, temp_upper_value, temp_lower_value, temp); + COLUMN(x,y, 8, 8, 10, 12, 14, 1, 3, 5, 7, temp_v1, temp_v2, temp_upper_value, temp_lower_value, temp); + COLUMN(x,y,10, 10, 12, 14, 0, 3, 5, 7, 9, temp_v1, temp_v2, temp_upper_value, temp_lower_value, temp); + COLUMN(x,y,12, 12, 14, 0, 2, 5, 7, 9, 11, temp_v1, temp_v2, temp_upper_value, temp_lower_value, temp); + COLUMN(x,y,14, 14, 0, 2, 4, 7, 9, 11, 13, temp_v1, temp_v2, temp_upper_value, temp_lower_value, temp); +} + +/* compute one round of Q (short variants) */ +static void RND512Q(uint8_t *x, uint32_t *y, uint32_t r) { + uint32_t temp_v1, temp_v2, temp_upper_value, temp_lower_value, temp; + uint32_t* x32 = (uint32_t*)x; + x32[ 0] = ~x32[ 0]; + x32[ 1] ^= 0xffffffff^r; + x32[ 2] = ~x32[ 2]; + x32[ 3] ^= 0xefffffff^r; + x32[ 4] = ~x32[ 4]; + x32[ 5] ^= 0xdfffffff^r; + x32[ 6] = ~x32[ 6]; + x32[ 7] ^= 0xcfffffff^r; + x32[ 8] = ~x32[ 8]; + x32[ 9] ^= 0xbfffffff^r; + x32[10] = ~x32[10]; + x32[11] ^= 0xafffffff^r; + x32[12] = ~x32[12]; + x32[13] ^= 0x9fffffff^r; + x32[14] = ~x32[14]; + x32[15] ^= 0x8fffffff^r; + COLUMN(x,y, 0, 2, 6, 10, 14, 1, 5, 9, 13, temp_v1, temp_v2, temp_upper_value, temp_lower_value, temp); + COLUMN(x,y, 2, 4, 8, 12, 0, 3, 7, 11, 15, temp_v1, temp_v2, temp_upper_value, temp_lower_value, temp); + COLUMN(x,y, 4, 6, 10, 14, 2, 5, 9, 13, 1, temp_v1, temp_v2, temp_upper_value, temp_lower_value, temp); + COLUMN(x,y, 6, 8, 12, 0, 4, 7, 11, 15, 3, temp_v1, temp_v2, temp_upper_value, temp_lower_value, temp); + COLUMN(x,y, 8, 10, 14, 2, 6, 9, 13, 1, 5, temp_v1, temp_v2, temp_upper_value, temp_lower_value, temp); + COLUMN(x,y,10, 12, 0, 4, 8, 11, 15, 3, 7, temp_v1, temp_v2, temp_upper_value, temp_lower_value, temp); + COLUMN(x,y,12, 14, 2, 6, 10, 13, 1, 5, 9, temp_v1, temp_v2, temp_upper_value, temp_lower_value, temp); + COLUMN(x,y,14, 0, 4, 8, 12, 15, 3, 7, 11, temp_v1, temp_v2, temp_upper_value, temp_lower_value, temp); +} + +/* compute compression function (short variants) */ +static void F512(uint32_t *h, const uint32_t *m) { + int i; + uint32_t Ptmp[2*COLS512]; + uint32_t Qtmp[2*COLS512]; + uint32_t y[2*COLS512]; + uint32_t z[2*COLS512]; + + for (i = 0; i < 2*COLS512; i++) { + z[i] = m[i]; + Ptmp[i] = h[i]^m[i]; + } + + /* compute Q(m) */ + RND512Q((uint8_t*)z, y, 0x00000000); + RND512Q((uint8_t*)y, z, 0x01000000); + RND512Q((uint8_t*)z, y, 0x02000000); + RND512Q((uint8_t*)y, z, 0x03000000); + RND512Q((uint8_t*)z, y, 0x04000000); + RND512Q((uint8_t*)y, z, 0x05000000); + RND512Q((uint8_t*)z, y, 0x06000000); + RND512Q((uint8_t*)y, z, 0x07000000); + RND512Q((uint8_t*)z, y, 0x08000000); + RND512Q((uint8_t*)y, Qtmp, 0x09000000); + + /* compute P(h+m) */ + RND512P((uint8_t*)Ptmp, y, 0x00000000); + RND512P((uint8_t*)y, z, 0x00000001); + RND512P((uint8_t*)z, y, 0x00000002); + RND512P((uint8_t*)y, z, 0x00000003); + RND512P((uint8_t*)z, y, 0x00000004); + RND512P((uint8_t*)y, z, 0x00000005); + RND512P((uint8_t*)z, y, 0x00000006); + RND512P((uint8_t*)y, z, 0x00000007); + RND512P((uint8_t*)z, y, 0x00000008); + RND512P((uint8_t*)y, Ptmp, 0x00000009); + + /* compute P(h+m) + Q(m) + h */ + for (i = 0; i < 2*COLS512; i++) { + h[i] ^= Ptmp[i]^Qtmp[i]; + } +} + + +/* digest up to msglen bytes of input (full blocks only) */ +static void Transform(hashState *ctx, + const uint8_t *input, + int msglen) { + + /* digest message, one block at a time */ + for (; msglen >= SIZE512; + msglen -= SIZE512, input += SIZE512) { + F512(ctx->chaining,(uint32_t*)input); + + /* increment block counter */ + ctx->block_counter1++; + if (ctx->block_counter1 == 0) ctx->block_counter2++; + } +} + +/* given state h, do h <- P(h)+h */ +static void OutputTransformation(hashState *ctx) { + int j; + uint32_t temp[2*COLS512]; + uint32_t y[2*COLS512]; + uint32_t z[2*COLS512]; + + + + for (j = 0; j < 2*COLS512; j++) { + temp[j] = ctx->chaining[j]; + } + RND512P((uint8_t*)temp, y, 0x00000000); + RND512P((uint8_t*)y, z, 0x00000001); + RND512P((uint8_t*)z, y, 0x00000002); + RND512P((uint8_t*)y, z, 0x00000003); + RND512P((uint8_t*)z, y, 0x00000004); + RND512P((uint8_t*)y, z, 0x00000005); + RND512P((uint8_t*)z, y, 0x00000006); + RND512P((uint8_t*)y, z, 0x00000007); + RND512P((uint8_t*)z, y, 0x00000008); + RND512P((uint8_t*)y, temp, 0x00000009); + for (j = 0; j < 2*COLS512; j++) { + ctx->chaining[j] ^= temp[j]; + } +} + +/* initialise context */ +static void Init(hashState* ctx) { + int i = 0; + /* allocate memory for state and data buffer */ + + for(;i<(SIZE512/sizeof(uint32_t));i++) + { + ctx->chaining[i] = 0; + } + + /* set initial value */ + ctx->chaining[2*COLS512-1] = u32BIG((uint32_t)HASH_BIT_LEN); + + /* set other variables */ + ctx->buf_ptr = 0; + ctx->block_counter1 = 0; + ctx->block_counter2 = 0; + ctx->bits_in_last_byte = 0; +} + +/* update state with databitlen bits of input */ +static void Update(hashState* ctx, + const BitSequence* input, + DataLength databitlen) { + int index = 0; + int msglen = (int)(databitlen/8); + int rem = (int)(databitlen%8); + + /* if the buffer contains data that has not yet been digested, first + add data to buffer until full */ + if (ctx->buf_ptr) { + while (ctx->buf_ptr < SIZE512 && index < msglen) { + ctx->buffer[(int)ctx->buf_ptr++] = input[index++]; + } + if (ctx->buf_ptr < SIZE512) { + /* buffer still not full, return */ + if (rem) { + ctx->bits_in_last_byte = rem; + ctx->buffer[(int)ctx->buf_ptr++] = input[index]; + } + return; + } + + /* digest buffer */ + ctx->buf_ptr = 0; + Transform(ctx, ctx->buffer, SIZE512); + } + + /* digest bulk of message */ + Transform(ctx, input+index, msglen-index); + index += ((msglen-index)/SIZE512)*SIZE512; + + /* store remaining data in buffer */ + while (index < msglen) { + ctx->buffer[(int)ctx->buf_ptr++] = input[index++]; + } + + /* if non-integral number of bytes have been supplied, store + remaining bits in last byte, together with information about + number of bits */ + if (rem) { + ctx->bits_in_last_byte = rem; + ctx->buffer[(int)ctx->buf_ptr++] = input[index]; + } +} + +#define BILB ctx->bits_in_last_byte + +/* finalise: process remaining data (including padding), perform + output transformation, and write hash result to 'output' */ +static void Final(hashState* ctx, + BitSequence* output) { + int i, j = 0, hashbytelen = HASH_BIT_LEN/8; + uint8_t *s = (BitSequence*)ctx->chaining; + + /* pad with '1'-bit and first few '0'-bits */ + if (BILB) { + ctx->buffer[(int)ctx->buf_ptr-1] &= ((1<buffer[(int)ctx->buf_ptr-1] ^= 0x1<<(7-BILB); + BILB = 0; + } + else ctx->buffer[(int)ctx->buf_ptr++] = 0x80; + + /* pad with '0'-bits */ + if (ctx->buf_ptr > SIZE512-LENGTHFIELDLEN) { + /* padding requires two blocks */ + while (ctx->buf_ptr < SIZE512) { + ctx->buffer[(int)ctx->buf_ptr++] = 0; + } + /* digest first padding block */ + Transform(ctx, ctx->buffer, SIZE512); + ctx->buf_ptr = 0; + } + while (ctx->buf_ptr < SIZE512-LENGTHFIELDLEN) { + ctx->buffer[(int)ctx->buf_ptr++] = 0; + } + + /* length padding */ + ctx->block_counter1++; + if (ctx->block_counter1 == 0) ctx->block_counter2++; + ctx->buf_ptr = SIZE512; + + while (ctx->buf_ptr > SIZE512-(int)sizeof(uint32_t)) { + ctx->buffer[(int)--ctx->buf_ptr] = (uint8_t)ctx->block_counter1; + ctx->block_counter1 >>= 8; + } + while (ctx->buf_ptr > SIZE512-LENGTHFIELDLEN) { + ctx->buffer[(int)--ctx->buf_ptr] = (uint8_t)ctx->block_counter2; + ctx->block_counter2 >>= 8; + } + /* digest final padding block */ + Transform(ctx, ctx->buffer, SIZE512); + /* perform output transformation */ + OutputTransformation(ctx); + + /* store hash result in output */ + for (i = SIZE512-hashbytelen; i < SIZE512; i++,j++) { + output[j] = s[i]; + } + + /* zeroise relevant variables and deallocate memory */ + for (i = 0; i < COLS512; i++) { + ctx->chaining[i] = 0; + } + for (i = 0; i < SIZE512; i++) { + ctx->buffer[i] = 0; + } +} + +/* hash bit sequence */ +void groestl(const BitSequence* data, + DataLength databitlen, + BitSequence* hashval) { + + hashState context; + + /* initialise */ + Init(&context); + + + /* process message */ + Update(&context, data, databitlen); + + /* finalise */ + Final(&context, hashval); +} +/* +static int crypto_hash(unsigned char *out, + const unsigned char *in, + unsigned long long len) +{ + groestl(in, 8*len, out); + return 0; +} + +*/ \ No newline at end of file diff --git a/src/crypto/groestl.h b/src/crypto/groestl.h new file mode 100644 index 000000000..078515cee --- /dev/null +++ b/src/crypto/groestl.h @@ -0,0 +1,60 @@ +#ifndef __hash_h +#define __hash_h +/* +#include "crypto_uint8.h" +#include "crypto_uint32.h" +#include "crypto_uint64.h" +#include "crypto_hash.h" + +typedef crypto_uint8 uint8_t; +typedef crypto_uint32 uint32_t; +typedef crypto_uint64 uint64_t; +*/ +#include + +/* some sizes (number of bytes) */ +#define ROWS 8 +#define LENGTHFIELDLEN ROWS +#define COLS512 8 + +#define SIZE512 (ROWS*COLS512) + +#define ROUNDS512 10 +#define HASH_BIT_LEN 256 + +#define ROTL32(v, n) ((((v)<<(n))|((v)>>(32-(n))))&li_32(ffffffff)) + + +#define li_32(h) 0x##h##u +#define EXT_BYTE(var,n) ((uint8_t)((uint32_t)(var) >> (8*n))) +#define u32BIG(a) \ + ((ROTL32(a,8) & li_32(00FF00FF)) | \ + (ROTL32(a,24) & li_32(FF00FF00))) + + +/* NIST API begin */ +typedef unsigned char BitSequence; +typedef unsigned long long DataLength; +typedef struct { + uint32_t chaining[SIZE512/sizeof(uint32_t)]; /* actual state */ + uint32_t block_counter1, + block_counter2; /* message block counter(s) */ + BitSequence buffer[SIZE512]; /* data buffer */ + int buf_ptr; /* data buffer pointer */ + int bits_in_last_byte; /* no. of message bits in last byte of + data buffer */ +} hashState; + +/*void Init(hashState*); +void Update(hashState*, const BitSequence*, DataLength); +void Final(hashState*, BitSequence*); */ +void groestl(const BitSequence*, DataLength, BitSequence*); +/* NIST API end */ + +/* +int crypto_hash(unsigned char *out, + const unsigned char *in, + unsigned long long len); +*/ + +#endif /* __hash_h */ diff --git a/src/crypto/groestl_tables.h b/src/crypto/groestl_tables.h new file mode 100644 index 000000000..a23295c35 --- /dev/null +++ b/src/crypto/groestl_tables.h @@ -0,0 +1,38 @@ +#ifndef __tables_h +#define __tables_h + + +const uint32_t T[512] = {0xa5f432c6, 0xc6a597f4, 0x84976ff8, 0xf884eb97, 0x99b05eee, 0xee99c7b0, 0x8d8c7af6, 0xf68df78c, 0xd17e8ff, 0xff0de517, 0xbddc0ad6, 0xd6bdb7dc, 0xb1c816de, 0xdeb1a7c8, 0x54fc6d91, 0x915439fc +, 0x50f09060, 0x6050c0f0, 0x3050702, 0x2030405, 0xa9e02ece, 0xcea987e0, 0x7d87d156, 0x567dac87, 0x192bcce7, 0xe719d52b, 0x62a613b5, 0xb56271a6, 0xe6317c4d, 0x4de69a31, 0x9ab559ec, 0xec9ac3b5 +, 0x45cf408f, 0x8f4505cf, 0x9dbca31f, 0x1f9d3ebc, 0x40c04989, 0x894009c0, 0x879268fa, 0xfa87ef92, 0x153fd0ef, 0xef15c53f, 0xeb2694b2, 0xb2eb7f26, 0xc940ce8e, 0x8ec90740, 0xb1de6fb, 0xfb0bed1d +, 0xec2f6e41, 0x41ec822f, 0x67a91ab3, 0xb3677da9, 0xfd1c435f, 0x5ffdbe1c, 0xea256045, 0x45ea8a25, 0xbfdaf923, 0x23bf46da, 0xf7025153, 0x53f7a602, 0x96a145e4, 0xe496d3a1, 0x5bed769b, 0x9b5b2ded +, 0xc25d2875, 0x75c2ea5d, 0x1c24c5e1, 0xe11cd924, 0xaee9d43d, 0x3dae7ae9, 0x6abef24c, 0x4c6a98be, 0x5aee826c, 0x6c5ad8ee, 0x41c3bd7e, 0x7e41fcc3, 0x206f3f5, 0xf502f106, 0x4fd15283, 0x834f1dd1 +, 0x5ce48c68, 0x685cd0e4, 0xf4075651, 0x51f4a207, 0x345c8dd1, 0xd134b95c, 0x818e1f9, 0xf908e918, 0x93ae4ce2, 0xe293dfae, 0x73953eab, 0xab734d95, 0x53f59762, 0x6253c4f5, 0x3f416b2a, 0x2a3f5441 +, 0xc141c08, 0x80c1014, 0x52f66395, 0x955231f6, 0x65afe946, 0x46658caf, 0x5ee27f9d, 0x9d5e21e2, 0x28784830, 0x30286078, 0xa1f8cf37, 0x37a16ef8, 0xf111b0a, 0xa0f1411, 0xb5c4eb2f, 0x2fb55ec4 +, 0x91b150e, 0xe091c1b, 0x365a7e24, 0x2436485a, 0x9bb6ad1b, 0x1b9b36b6, 0x3d4798df, 0xdf3da547, 0x266aa7cd, 0xcd26816a, 0x69bbf54e, 0x4e699cbb, 0xcd4c337f, 0x7fcdfe4c, 0x9fba50ea, 0xea9fcfba +, 0x1b2d3f12, 0x121b242d, 0x9eb9a41d, 0x1d9e3ab9, 0x749cc458, 0x5874b09c, 0x2e724634, 0x342e6872, 0x2d774136, 0x362d6c77, 0xb2cd11dc, 0xdcb2a3cd, 0xee299db4, 0xb4ee7329, 0xfb164d5b, 0x5bfbb616 +, 0xf601a5a4, 0xa4f65301, 0x4dd7a176, 0x764decd7, 0x61a314b7, 0xb76175a3, 0xce49347d, 0x7dcefa49, 0x7b8ddf52, 0x527ba48d, 0x3e429fdd, 0xdd3ea142, 0x7193cd5e, 0x5e71bc93, 0x97a2b113, 0x139726a2 +, 0xf504a2a6, 0xa6f55704, 0x68b801b9, 0xb96869b8, 0x0, 0x0, 0x2c74b5c1, 0xc12c9974, 0x60a0e040, 0x406080a0, 0x1f21c2e3, 0xe31fdd21, 0xc8433a79, 0x79c8f243, 0xed2c9ab6, 0xb6ed772c +, 0xbed90dd4, 0xd4beb3d9, 0x46ca478d, 0x8d4601ca, 0xd9701767, 0x67d9ce70, 0x4bddaf72, 0x724be4dd, 0xde79ed94, 0x94de3379, 0xd467ff98, 0x98d42b67, 0xe82393b0, 0xb0e87b23, 0x4ade5b85, 0x854a11de +, 0x6bbd06bb, 0xbb6b6dbd, 0x2a7ebbc5, 0xc52a917e, 0xe5347b4f, 0x4fe59e34, 0x163ad7ed, 0xed16c13a, 0xc554d286, 0x86c51754, 0xd762f89a, 0x9ad72f62, 0x55ff9966, 0x6655ccff, 0x94a7b611, 0x119422a7 +, 0xcf4ac08a, 0x8acf0f4a, 0x1030d9e9, 0xe910c930, 0x60a0e04, 0x406080a, 0x819866fe, 0xfe81e798, 0xf00baba0, 0xa0f05b0b, 0x44ccb478, 0x7844f0cc, 0xbad5f025, 0x25ba4ad5, 0xe33e754b, 0x4be3963e +, 0xf30eaca2, 0xa2f35f0e, 0xfe19445d, 0x5dfeba19, 0xc05bdb80, 0x80c01b5b, 0x8a858005, 0x58a0a85, 0xadecd33f, 0x3fad7eec, 0xbcdffe21, 0x21bc42df, 0x48d8a870, 0x7048e0d8, 0x40cfdf1, 0xf104f90c +, 0xdf7a1963, 0x63dfc67a, 0xc1582f77, 0x77c1ee58, 0x759f30af, 0xaf75459f, 0x63a5e742, 0x426384a5, 0x30507020, 0x20304050, 0x1a2ecbe5, 0xe51ad12e, 0xe12effd, 0xfd0ee112, 0x6db708bf, 0xbf6d65b7 +, 0x4cd45581, 0x814c19d4, 0x143c2418, 0x1814303c, 0x355f7926, 0x26354c5f, 0x2f71b2c3, 0xc32f9d71, 0xe13886be, 0xbee16738, 0xa2fdc835, 0x35a26afd, 0xcc4fc788, 0x88cc0b4f, 0x394b652e, 0x2e395c4b +, 0x57f96a93, 0x93573df9, 0xf20d5855, 0x55f2aa0d, 0x829d61fc, 0xfc82e39d, 0x47c9b37a, 0x7a47f4c9, 0xacef27c8, 0xc8ac8bef, 0xe73288ba, 0xbae76f32, 0x2b7d4f32, 0x322b647d, 0x95a442e6, 0xe695d7a4 +, 0xa0fb3bc0, 0xc0a09bfb, 0x98b3aa19, 0x199832b3, 0xd168f69e, 0x9ed12768, 0x7f8122a3, 0xa37f5d81, 0x66aaee44, 0x446688aa, 0x7e82d654, 0x547ea882, 0xabe6dd3b, 0x3bab76e6, 0x839e950b, 0xb83169e +, 0xca45c98c, 0x8cca0345, 0x297bbcc7, 0xc729957b, 0xd36e056b, 0x6bd3d66e, 0x3c446c28, 0x283c5044, 0x798b2ca7, 0xa779558b, 0xe23d81bc, 0xbce2633d, 0x1d273116, 0x161d2c27, 0x769a37ad, 0xad76419a +, 0x3b4d96db, 0xdb3bad4d, 0x56fa9e64, 0x6456c8fa, 0x4ed2a674, 0x744ee8d2, 0x1e223614, 0x141e2822, 0xdb76e492, 0x92db3f76, 0xa1e120c, 0xc0a181e, 0x6cb4fc48, 0x486c90b4, 0xe4378fb8, 0xb8e46b37 +, 0x5de7789f, 0x9f5d25e7, 0x6eb20fbd, 0xbd6e61b2, 0xef2a6943, 0x43ef862a, 0xa6f135c4, 0xc4a693f1, 0xa8e3da39, 0x39a872e3, 0xa4f7c631, 0x31a462f7, 0x37598ad3, 0xd337bd59, 0x8b8674f2, 0xf28bff86 +, 0x325683d5, 0xd532b156, 0x43c54e8b, 0x8b430dc5, 0x59eb856e, 0x6e59dceb, 0xb7c218da, 0xdab7afc2, 0x8c8f8e01, 0x18c028f, 0x64ac1db1, 0xb16479ac, 0xd26df19c, 0x9cd2236d, 0xe03b7249, 0x49e0923b +, 0xb4c71fd8, 0xd8b4abc7, 0xfa15b9ac, 0xacfa4315, 0x709faf3, 0xf307fd09, 0x256fa0cf, 0xcf25856f, 0xafea20ca, 0xcaaf8fea, 0x8e897df4, 0xf48ef389, 0xe9206747, 0x47e98e20, 0x18283810, 0x10182028 +, 0xd5640b6f, 0x6fd5de64, 0x888373f0, 0xf088fb83, 0x6fb1fb4a, 0x4a6f94b1, 0x7296ca5c, 0x5c72b896, 0x246c5438, 0x3824706c, 0xf1085f57, 0x57f1ae08, 0xc7522173, 0x73c7e652, 0x51f36497, 0x975135f3 +, 0x2365aecb, 0xcb238d65, 0x7c8425a1, 0xa17c5984, 0x9cbf57e8, 0xe89ccbbf, 0x21635d3e, 0x3e217c63, 0xdd7cea96, 0x96dd377c, 0xdc7f1e61, 0x61dcc27f, 0x86919c0d, 0xd861a91, 0x85949b0f, 0xf851e94 +, 0x90ab4be0, 0xe090dbab, 0x42c6ba7c, 0x7c42f8c6, 0xc4572671, 0x71c4e257, 0xaae529cc, 0xccaa83e5, 0xd873e390, 0x90d83b73, 0x50f0906, 0x6050c0f, 0x103f4f7, 0xf701f503, 0x12362a1c, 0x1c123836 +, 0xa3fe3cc2, 0xc2a39ffe, 0x5fe18b6a, 0x6a5fd4e1, 0xf910beae, 0xaef94710, 0xd06b0269, 0x69d0d26b, 0x91a8bf17, 0x17912ea8, 0x58e87199, 0x995829e8, 0x2769533a, 0x3a277469, 0xb9d0f727, 0x27b94ed0 +, 0x384891d9, 0xd938a948, 0x1335deeb, 0xeb13cd35, 0xb3cee52b, 0x2bb356ce, 0x33557722, 0x22334455, 0xbbd604d2, 0xd2bbbfd6, 0x709039a9, 0xa9704990, 0x89808707, 0x7890e80, 0xa7f2c133, 0x33a766f2 +, 0xb6c1ec2d, 0x2db65ac1, 0x22665a3c, 0x3c227866, 0x92adb815, 0x15922aad, 0x2060a9c9, 0xc9208960, 0x49db5c87, 0x874915db, 0xff1ab0aa, 0xaaff4f1a, 0x7888d850, 0x5078a088, 0x7a8e2ba5, 0xa57a518e +, 0x8f8a8903, 0x38f068a, 0xf8134a59, 0x59f8b213, 0x809b9209, 0x980129b, 0x1739231a, 0x1a173439, 0xda751065, 0x65daca75, 0x315384d7, 0xd731b553, 0xc651d584, 0x84c61351, 0xb8d303d0, 0xd0b8bbd3 +, 0xc35edc82, 0x82c31f5e, 0xb0cbe229, 0x29b052cb, 0x7799c35a, 0x5a77b499, 0x11332d1e, 0x1e113c33, 0xcb463d7b, 0x7bcbf646, 0xfc1fb7a8, 0xa8fc4b1f, 0xd6610c6d, 0x6dd6da61, 0x3a4e622c, 0x2c3a584e}; + +#endif /* __tables_h */ diff --git a/src/crypto/hash-extra-blake.c b/src/crypto/hash-extra-blake.c new file mode 100644 index 000000000..2eeb52020 --- /dev/null +++ b/src/crypto/hash-extra-blake.c @@ -0,0 +1,12 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include +#include + +#include "blake256.h" + +void hash_extra_blake(const void *data, size_t length, char *hash) { + blake256_hash((uint8_t*)hash, data, length); +} diff --git a/src/crypto/hash-extra-groestl.c b/src/crypto/hash-extra-groestl.c new file mode 100644 index 000000000..7918cfc53 --- /dev/null +++ b/src/crypto/hash-extra-groestl.c @@ -0,0 +1,12 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include +#include + +#include "groestl.h" + +void hash_extra_groestl(const void *data, size_t length, char *hash) { + groestl(data, length * 8, (uint8_t*)hash); +} diff --git a/src/crypto/hash-extra-jh.c b/src/crypto/hash-extra-jh.c new file mode 100644 index 000000000..15c271b2a --- /dev/null +++ b/src/crypto/hash-extra-jh.c @@ -0,0 +1,16 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include +#include +#include +#include + +#include "jh.h" +#include "hash-ops.h" + +void hash_extra_jh(const void *data, size_t length, char *hash) { + int r = jh_hash(HASH_SIZE * 8, data, 8 * length, (uint8_t*)hash); + assert(SUCCESS == r); +} diff --git a/src/crypto/hash-extra-skein.c b/src/crypto/hash-extra-skein.c new file mode 100644 index 000000000..92361e6db --- /dev/null +++ b/src/crypto/hash-extra-skein.c @@ -0,0 +1,14 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include +#include + +#include "hash-ops.h" +#include "skein.h" + +void hash_extra_skein(const void *data, size_t length, char *hash) { + int r = skein_hash(8 * HASH_SIZE, data, 8 * length, (uint8_t*)hash); + assert(SKEIN_SUCCESS == r); +} diff --git a/src/crypto/hash-ops.h b/src/crypto/hash-ops.h new file mode 100644 index 000000000..9e6c821ef --- /dev/null +++ b/src/crypto/hash-ops.h @@ -0,0 +1,63 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#if !defined(__cplusplus) + +#include +#include +#include +#include + +#include "common/int-util.h" +#include "warnings.h" + +static inline void *padd(void *p, size_t i) { + return (char *) p + i; +} + +static inline const void *cpadd(const void *p, size_t i) { + return (const char *) p + i; +} + +PUSH_WARNINGS +DISABLE_VS_WARNINGS(4267) +static_assert(sizeof(size_t) == 4 || sizeof(size_t) == 8, "size_t must be 4 or 8 bytes long"); +static inline void place_length(uint8_t *buffer, size_t bufsize, size_t length) { + if (sizeof(size_t) == 4) { + *(uint32_t *) padd(buffer, bufsize - 4) = swap32be(length); + } else { + *(uint64_t *) padd(buffer, bufsize - 8) = swap64be(length); + } +} +POP_WARNINGS + +#pragma pack(push, 1) +union hash_state { + uint8_t b[200]; + uint64_t w[25]; +}; +#pragma pack(pop) +static_assert(sizeof(union hash_state) == 200, "Invalid structure size"); + +void hash_permutation(union hash_state *state); +void hash_process(union hash_state *state, const uint8_t *buf, size_t count); + +#endif + +enum { + HASH_SIZE = 32, + HASH_DATA_AREA = 136 +}; + +void cn_fast_hash(const void *data, size_t length, char *hash); +void cn_slow_hash(const void *data, size_t length, char *hash); + +void hash_extra_blake(const void *data, size_t length, char *hash); +void hash_extra_groestl(const void *data, size_t length, char *hash); +void hash_extra_jh(const void *data, size_t length, char *hash); +void hash_extra_skein(const void *data, size_t length, char *hash); + +void tree_hash(const char (*hashes)[HASH_SIZE], size_t count, char *root_hash); diff --git a/src/crypto/hash.c b/src/crypto/hash.c index f07ffd661..a3989d88c 100644 --- a/src/crypto/hash.c +++ b/src/crypto/hash.c @@ -1,82 +1,24 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + #include #include #include -#include "int-util.h" -#include "hash.h" +#include "hash-ops.h" +#include "keccak.h" -static const uint64_t round_constants[] = { - 0x0000000000000001, 0x0000000000008082, 0x800000000000808a, 0x8000000080008000, - 0x000000000000808b, 0x0000000080000001, 0x8000000080008081, 0x8000000000008009, - 0x000000000000008a, 0x0000000000000088, 0x0000000080008009, 0x000000008000000a, - 0x000000008000808b, 0x800000000000008b, 0x8000000000008089, 0x8000000000008003, - 0x8000000000008002, 0x8000000000000080, 0x000000000000800a, 0x800000008000000a, - 0x8000000080008081, 0x8000000000008080, 0x0000000080000001, 0x8000000080008008 -}; - -static const uint8_t rho_offsets[] = { - 0, 1, 62, 28, 27, - 36, 44, 6, 55, 20, - 3, 10, 43, 25, 39, - 41, 45, 15, 21, 8, - 18, 2, 61, 56, 14 -}; - -static void keccak_round(uint64_t *state, unsigned int i) { - unsigned int j, k; - uint64_t tmp[25]; - for(j = 0; j < 5; j++) { - tmp[j] = state[j] ^ state[j + 5] ^ state[j + 10] ^ state[j + 15] ^ state[j + 20]; - } - for(j = 0; j < 5; j++) { - uint64_t t = rol64(tmp[(j + 1) % 5], 1) ^ tmp[(j + 4) % 5]; - state[j] ^= t; - state[j + 5] ^= t; - state[j + 10] ^= t; - state[j + 15] ^= t; - state[j + 20] ^= t; - } - for(j = 0; j < 25; j++) { - state[j] = rol64(state[j], rho_offsets[j]); - } -#define index(x, y) (((x) % 5) + 5 * ((y) % 5)) - for(j = 0; j < 5; j++) { - for(k = 0; k < 5; k++) { - tmp[index(0 * k + 1 * j, 2 * k + 3 * j)] = state[index(k, j)]; - } - } - for(j = 0; j < 5; j++) { - for(k = 0; k < 5; k++) { - state[index(k, j)] = tmp[index(k, j)] ^ ((~tmp[index(k + 1, j)]) & tmp[index(k + 2, j)]); - } - } -#undef index - state[0] ^= round_constants[i]; +void hash_permutation(union hash_state *state) { + keccakf((uint64_t*)state, 24); } -void keccak_permutation(struct keccak_state *state) { - unsigned int i; - mem_inplace_swap64le(&state, 25); - for (i = 0; i < 24; i++) { - keccak_round(state->w, i); - } - mem_inplace_swap64le(&state, 25); +void hash_process(union hash_state *state, const uint8_t *buf, size_t count) { + keccak1600(buf, count, (uint8_t*)state); } -void keccak(const void *data, size_t length, char *hash) { - struct keccak_state state; - const void *limit = cpadd(data, length); - size_t i; - memset(&state, 0, sizeof(struct keccak_state)); - for (i = 0; data != limit; ++data) { - state.b[i] ^= *(char *) data; - if (++i == 136) { - keccak_permutation(&state); - i = 0; - } - } - state.b[i] ^= 1; - state.b[135] ^= 0x80; - keccak_permutation(&state); - memcpy(hash, &state, 32); -} \ No newline at end of file +void cn_fast_hash(const void *data, size_t length, char *hash) { + union hash_state state; + hash_process(&state, data, length); + memcpy(hash, &state, HASH_SIZE); +} diff --git a/src/crypto/hash.h b/src/crypto/hash.h index 32e8f4701..fb65494b6 100644 --- a/src/crypto/hash.h +++ b/src/crypto/hash.h @@ -1,40 +1,50 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + #pragma once -#include -#include #include -#include -#include "int-util.h" +#include "common/pod-class.h" +#include "generic-ops.h" -static inline void *padd(void *p, size_t i) { - return (char *) p + i; -} +namespace crypto { -static inline const void *cpadd(const void *p, size_t i) { - return (const char *) p + i; -} - -static_assert(sizeof(size_t) == 4 || sizeof(size_t) == 8, "size_t must be 4 or 8 bytes long"); -static inline void place_length(uint8_t *buffer, size_t bufsize, size_t length) { - if (sizeof(size_t) == 4) { - *(uint32_t *) padd(buffer, bufsize - 4) = swap32be(length); - } else { - *(uint64_t *) padd(buffer, bufsize - 8) = swap64be(length); + extern "C" { +#include "hash-ops.h" } + +#pragma pack(push, 1) + POD_CLASS hash { + char data[HASH_SIZE]; + }; +#pragma pack(pop) + + static_assert(sizeof(hash) == HASH_SIZE, "Invalid structure size"); + + /* + Cryptonight hash functions + */ + + inline void cn_fast_hash(const void *data, std::size_t length, hash &hash) { + cn_fast_hash(data, length, reinterpret_cast(&hash)); + } + + inline hash cn_fast_hash(const void *data, std::size_t length) { + hash h; + cn_fast_hash(data, length, reinterpret_cast(&h)); + return h; + } + + inline void cn_slow_hash(const void *data, std::size_t length, hash &hash) { + cn_slow_hash(data, length, reinterpret_cast(&hash)); + } + + inline void tree_hash(const hash *hashes, std::size_t count, hash &root_hash) { + tree_hash(reinterpret_cast(hashes), count, reinterpret_cast(&root_hash)); + } + } -struct keccak_state { - union { - uint8_t b[200]; - uint64_t w[25]; - }; -}; - -enum { - HASH_SIZE = 32, - HASH_DATA_AREA = 136 -}; - -void keccak(const void *data, size_t length, char *hash); -void keccak_permutation(struct keccak_state *state); \ No newline at end of file +CRYPTO_MAKE_HASHABLE(hash) diff --git a/src/crypto/initializer.h b/src/crypto/initializer.h new file mode 100644 index 000000000..8c84621bf --- /dev/null +++ b/src/crypto/initializer.h @@ -0,0 +1,32 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#if defined(__GNUC__) +#define INITIALIZER(name) __attribute__((constructor(101))) static void name(void) +#define FINALIZER(name) __attribute__((destructor(101))) static void name(void) +#define REGISTER_FINALIZER(name) ((void) 0) + +#elif defined(_MSC_VER) +#include +#include +// http://stackoverflow.com/questions/1113409/attribute-constructor-equivalent-in-vc +// http://msdn.microsoft.com/en-us/library/bb918180.aspx +#pragma section(".CRT$XCT", read) +#define INITIALIZER(name) \ + static void __cdecl name(void); \ + __declspec(allocate(".CRT$XCT")) void (__cdecl *const _##name)(void) = &name; \ + static void __cdecl name(void) +#define FINALIZER(name) \ + static void __cdecl name(void) +#define REGISTER_FINALIZER(name) \ + do { \ + int _res = atexit(name); \ + assert(_res == 0); \ + } while (0); + +#else +#error Unsupported compiler +#endif diff --git a/src/crypto/int-util.h b/src/crypto/int-util.h deleted file mode 100644 index 933f2e784..000000000 --- a/src/crypto/int-util.h +++ /dev/null @@ -1,121 +0,0 @@ -#pragma once - -#include -#include -#include - -static inline uint32_t rol32(uint32_t x, int r) { - return (x << (r & 31)) | (x >> (-r & 31)); -} - -static inline uint64_t rol64(uint64_t x, int r) { - return (x << (r & 63)) | (x >> (-r & 63)); -} - -#define IDENT32(x) ((uint32_t) (x)) -#define IDENT64(x) ((uint64_t) (x)) - -#define SWAP32(x) ((((uint32_t) (x) & 0x000000ff) << 24) | \ - (((uint32_t) (x) & 0x0000ff00) << 8) | \ - (((uint32_t) (x) & 0x00ff0000) >> 8) | \ - (((uint32_t) (x) & 0xff000000) >> 24)) -#define SWAP64(x) ((((uint64_t) (x) & 0x00000000000000ff) << 56) | \ - (((uint64_t) (x) & 0x000000000000ff00) << 40) | \ - (((uint64_t) (x) & 0x0000000000ff0000) << 24) | \ - (((uint64_t) (x) & 0x00000000ff000000) << 8) | \ - (((uint64_t) (x) & 0x000000ff00000000) >> 8) | \ - (((uint64_t) (x) & 0x0000ff0000000000) >> 24) | \ - (((uint64_t) (x) & 0x00ff000000000000) >> 40) | \ - (((uint64_t) (x) & 0xff00000000000000) >> 56)) - -static inline uint32_t ident32(uint32_t x) { return x; } -static inline uint64_t ident64(uint64_t x) { return x; } - -static inline uint32_t swap32(uint32_t x) { - x = ((x & 0x00ff00ff) << 8) | ((x & 0xff00ff00) >> 8); - return (x << 16) | (x >> 16); -} -static inline uint64_t swap64(uint64_t x) { - x = ((x & 0x00ff00ff00ff00ff) << 8) | ((x & 0xff00ff00ff00ff00) >> 8); - x = ((x & 0x0000ffff0000ffff) << 16) | ((x & 0xffff0000ffff0000) >> 16); - return (x << 32) | (x >> 32); -} - -#if defined(__GNUC__) -#define UNUSED __attribute__((unused)) -#else -#define UNUSED -#endif -static inline void mem_inplace_ident(void *mem UNUSED, size_t n UNUSED) { } -#undef UNUSED - -static inline void mem_inplace_swap32(void *mem, size_t n) { - size_t i; - for (i = 0; i < n; i++) { - ((uint32_t *) mem)[i] = swap32(((const uint32_t *) mem)[i]); - } -} -static inline void mem_inplace_swap64(void *mem, size_t n) { - size_t i; - for (i = 0; i < n; i++) { - ((uint64_t *) mem)[i] = swap64(((const uint64_t *) mem)[i]); - } -} - -static inline void memcpy_ident32(void *dst, const void *src, size_t n) { - memcpy(dst, src, 4 * n); -} -static inline void memcpy_ident64(void *dst, const void *src, size_t n) { - memcpy(dst, src, 8 * n); -} - -static inline void memcpy_swap32(void *dst, const void *src, size_t n) { - size_t i; - for (i = 0; i < n; i++) { - ((uint32_t *) dst)[i] = swap32(((const uint32_t *) src)[i]); - } -} -static inline void memcpy_swap64(void *dst, const void *src, size_t n) { - size_t i; - for (i = 0; i < n; i++) { - ((uint64_t *) dst)[i] = swap64(((const uint64_t *) src)[i]); - } -} - -#if BYTE_ORDER == LITTLE_ENDIAN -#define SWAP32LE IDENT32 -#define SWAP32BE SWAP32 -#define swap32le ident32 -#define swap32be swap32 -#define mem_inplace_swap32le mem_inplace_ident -#define mem_inplace_swap32be mem_inplace_swap32 -#define memcpy_swap32le memcpy_ident32 -#define memcpy_swap32be memcpy_swap32 -#define SWAP64LE IDENT64 -#define SWAP64BE SWAP64 -#define swap64le ident64 -#define swap64be swap64 -#define mem_inplace_swap64le mem_inplace_ident -#define mem_inplace_swap64be mem_inplace_swap64 -#define memcpy_swap64le memcpy_ident64 -#define memcpy_swap64be memcpy_swap64 -#endif - -#if BYTE_ORDER == BIG_ENDIAN -#define SWAP32BE IDENT32 -#define SWAP32LE SWAP32 -#define swap32be ident32 -#define swap32le swap32 -#define mem_inplace_swap32be mem_inplace_ident -#define mem_inplace_swap32le mem_inplace_swap32 -#define memcpy_swap32be memcpy_ident32 -#define memcpy_swap32le memcpy_swap32 -#define SWAP64BE IDENT64 -#define SWAP64LE SWAP64 -#define swap64be ident64 -#define swap64le swap64 -#define mem_inplace_swap64be mem_inplace_ident -#define mem_inplace_swap64le mem_inplace_swap64 -#define memcpy_swap64be memcpy_ident64 -#define memcpy_swap64le memcpy_swap64 -#endif \ No newline at end of file diff --git a/src/crypto/jh.c b/src/crypto/jh.c new file mode 100644 index 000000000..12d536375 --- /dev/null +++ b/src/crypto/jh.c @@ -0,0 +1,367 @@ +/*This program gives the 64-bit optimized bitslice implementation of JH using ANSI C + + -------------------------------- + Performance + + Microprocessor: Intel CORE 2 processor (Core 2 Duo Mobile T6600 2.2GHz) + Operating System: 64-bit Ubuntu 10.04 (Linux kernel 2.6.32-22-generic) + Speed for long message: + 1) 45.8 cycles/byte compiler: Intel C++ Compiler 11.1 compilation option: icc -O2 + 2) 56.8 cycles/byte compiler: gcc 4.4.3 compilation option: gcc -O3 + + -------------------------------- + Last Modified: January 16, 2011 +*/ + +#include "jh.h" + +#include +#include + +/*typedef unsigned long long uint64;*/ +typedef uint64_t uint64; + +/*define data alignment for different C compilers*/ +#if defined(__GNUC__) + #define DATA_ALIGN16(x) x __attribute__ ((aligned(16))) +#else + #define DATA_ALIGN16(x) __declspec(align(16)) x +#endif + + +typedef struct { + int hashbitlen; /*the message digest size*/ + unsigned long long databitlen; /*the message size in bits*/ + unsigned long long datasize_in_buffer; /*the size of the message remained in buffer; assumed to be multiple of 8bits except for the last partial block at the end of the message*/ + DATA_ALIGN16(uint64 x[8][2]); /*the 1024-bit state, ( x[i][0] || x[i][1] ) is the ith row of the state in the pseudocode*/ + unsigned char buffer[64]; /*the 512-bit message block to be hashed;*/ +} hashState; + + +/*The initial hash value H(0)*/ +const unsigned char JH224_H0[128]={0x2d,0xfe,0xdd,0x62,0xf9,0x9a,0x98,0xac,0xae,0x7c,0xac,0xd6,0x19,0xd6,0x34,0xe7,0xa4,0x83,0x10,0x5,0xbc,0x30,0x12,0x16,0xb8,0x60,0x38,0xc6,0xc9,0x66,0x14,0x94,0x66,0xd9,0x89,0x9f,0x25,0x80,0x70,0x6f,0xce,0x9e,0xa3,0x1b,0x1d,0x9b,0x1a,0xdc,0x11,0xe8,0x32,0x5f,0x7b,0x36,0x6e,0x10,0xf9,0x94,0x85,0x7f,0x2,0xfa,0x6,0xc1,0x1b,0x4f,0x1b,0x5c,0xd8,0xc8,0x40,0xb3,0x97,0xf6,0xa1,0x7f,0x6e,0x73,0x80,0x99,0xdc,0xdf,0x93,0xa5,0xad,0xea,0xa3,0xd3,0xa4,0x31,0xe8,0xde,0xc9,0x53,0x9a,0x68,0x22,0xb4,0xa9,0x8a,0xec,0x86,0xa1,0xe4,0xd5,0x74,0xac,0x95,0x9c,0xe5,0x6c,0xf0,0x15,0x96,0xd,0xea,0xb5,0xab,0x2b,0xbf,0x96,0x11,0xdc,0xf0,0xdd,0x64,0xea,0x6e}; +const unsigned char JH256_H0[128]={0xeb,0x98,0xa3,0x41,0x2c,0x20,0xd3,0xeb,0x92,0xcd,0xbe,0x7b,0x9c,0xb2,0x45,0xc1,0x1c,0x93,0x51,0x91,0x60,0xd4,0xc7,0xfa,0x26,0x0,0x82,0xd6,0x7e,0x50,0x8a,0x3,0xa4,0x23,0x9e,0x26,0x77,0x26,0xb9,0x45,0xe0,0xfb,0x1a,0x48,0xd4,0x1a,0x94,0x77,0xcd,0xb5,0xab,0x26,0x2,0x6b,0x17,0x7a,0x56,0xf0,0x24,0x42,0xf,0xff,0x2f,0xa8,0x71,0xa3,0x96,0x89,0x7f,0x2e,0x4d,0x75,0x1d,0x14,0x49,0x8,0xf7,0x7d,0xe2,0x62,0x27,0x76,0x95,0xf7,0x76,0x24,0x8f,0x94,0x87,0xd5,0xb6,0x57,0x47,0x80,0x29,0x6c,0x5c,0x5e,0x27,0x2d,0xac,0x8e,0xd,0x6c,0x51,0x84,0x50,0xc6,0x57,0x5,0x7a,0xf,0x7b,0xe4,0xd3,0x67,0x70,0x24,0x12,0xea,0x89,0xe3,0xab,0x13,0xd3,0x1c,0xd7,0x69}; +const unsigned char JH384_H0[128]={0x48,0x1e,0x3b,0xc6,0xd8,0x13,0x39,0x8a,0x6d,0x3b,0x5e,0x89,0x4a,0xde,0x87,0x9b,0x63,0xfa,0xea,0x68,0xd4,0x80,0xad,0x2e,0x33,0x2c,0xcb,0x21,0x48,0xf,0x82,0x67,0x98,0xae,0xc8,0x4d,0x90,0x82,0xb9,0x28,0xd4,0x55,0xea,0x30,0x41,0x11,0x42,0x49,0x36,0xf5,0x55,0xb2,0x92,0x48,0x47,0xec,0xc7,0x25,0xa,0x93,0xba,0xf4,0x3c,0xe1,0x56,0x9b,0x7f,0x8a,0x27,0xdb,0x45,0x4c,0x9e,0xfc,0xbd,0x49,0x63,0x97,0xaf,0xe,0x58,0x9f,0xc2,0x7d,0x26,0xaa,0x80,0xcd,0x80,0xc0,0x8b,0x8c,0x9d,0xeb,0x2e,0xda,0x8a,0x79,0x81,0xe8,0xf8,0xd5,0x37,0x3a,0xf4,0x39,0x67,0xad,0xdd,0xd1,0x7a,0x71,0xa9,0xb4,0xd3,0xbd,0xa4,0x75,0xd3,0x94,0x97,0x6c,0x3f,0xba,0x98,0x42,0x73,0x7f}; +const unsigned char JH512_H0[128]={0x6f,0xd1,0x4b,0x96,0x3e,0x0,0xaa,0x17,0x63,0x6a,0x2e,0x5,0x7a,0x15,0xd5,0x43,0x8a,0x22,0x5e,0x8d,0xc,0x97,0xef,0xb,0xe9,0x34,0x12,0x59,0xf2,0xb3,0xc3,0x61,0x89,0x1d,0xa0,0xc1,0x53,0x6f,0x80,0x1e,0x2a,0xa9,0x5,0x6b,0xea,0x2b,0x6d,0x80,0x58,0x8e,0xcc,0xdb,0x20,0x75,0xba,0xa6,0xa9,0xf,0x3a,0x76,0xba,0xf8,0x3b,0xf7,0x1,0x69,0xe6,0x5,0x41,0xe3,0x4a,0x69,0x46,0xb5,0x8a,0x8e,0x2e,0x6f,0xe6,0x5a,0x10,0x47,0xa7,0xd0,0xc1,0x84,0x3c,0x24,0x3b,0x6e,0x71,0xb1,0x2d,0x5a,0xc1,0x99,0xcf,0x57,0xf6,0xec,0x9d,0xb1,0xf8,0x56,0xa7,0x6,0x88,0x7c,0x57,0x16,0xb1,0x56,0xe3,0xc2,0xfc,0xdf,0xe6,0x85,0x17,0xfb,0x54,0x5a,0x46,0x78,0xcc,0x8c,0xdd,0x4b}; + +/*42 round constants, each round constant is 32-byte (256-bit)*/ +const unsigned char E8_bitslice_roundconstant[42][32]={ +{0x72,0xd5,0xde,0xa2,0xdf,0x15,0xf8,0x67,0x7b,0x84,0x15,0xa,0xb7,0x23,0x15,0x57,0x81,0xab,0xd6,0x90,0x4d,0x5a,0x87,0xf6,0x4e,0x9f,0x4f,0xc5,0xc3,0xd1,0x2b,0x40}, +{0xea,0x98,0x3a,0xe0,0x5c,0x45,0xfa,0x9c,0x3,0xc5,0xd2,0x99,0x66,0xb2,0x99,0x9a,0x66,0x2,0x96,0xb4,0xf2,0xbb,0x53,0x8a,0xb5,0x56,0x14,0x1a,0x88,0xdb,0xa2,0x31}, +{0x3,0xa3,0x5a,0x5c,0x9a,0x19,0xe,0xdb,0x40,0x3f,0xb2,0xa,0x87,0xc1,0x44,0x10,0x1c,0x5,0x19,0x80,0x84,0x9e,0x95,0x1d,0x6f,0x33,0xeb,0xad,0x5e,0xe7,0xcd,0xdc}, +{0x10,0xba,0x13,0x92,0x2,0xbf,0x6b,0x41,0xdc,0x78,0x65,0x15,0xf7,0xbb,0x27,0xd0,0xa,0x2c,0x81,0x39,0x37,0xaa,0x78,0x50,0x3f,0x1a,0xbf,0xd2,0x41,0x0,0x91,0xd3}, +{0x42,0x2d,0x5a,0xd,0xf6,0xcc,0x7e,0x90,0xdd,0x62,0x9f,0x9c,0x92,0xc0,0x97,0xce,0x18,0x5c,0xa7,0xb,0xc7,0x2b,0x44,0xac,0xd1,0xdf,0x65,0xd6,0x63,0xc6,0xfc,0x23}, +{0x97,0x6e,0x6c,0x3,0x9e,0xe0,0xb8,0x1a,0x21,0x5,0x45,0x7e,0x44,0x6c,0xec,0xa8,0xee,0xf1,0x3,0xbb,0x5d,0x8e,0x61,0xfa,0xfd,0x96,0x97,0xb2,0x94,0x83,0x81,0x97}, +{0x4a,0x8e,0x85,0x37,0xdb,0x3,0x30,0x2f,0x2a,0x67,0x8d,0x2d,0xfb,0x9f,0x6a,0x95,0x8a,0xfe,0x73,0x81,0xf8,0xb8,0x69,0x6c,0x8a,0xc7,0x72,0x46,0xc0,0x7f,0x42,0x14}, +{0xc5,0xf4,0x15,0x8f,0xbd,0xc7,0x5e,0xc4,0x75,0x44,0x6f,0xa7,0x8f,0x11,0xbb,0x80,0x52,0xde,0x75,0xb7,0xae,0xe4,0x88,0xbc,0x82,0xb8,0x0,0x1e,0x98,0xa6,0xa3,0xf4}, +{0x8e,0xf4,0x8f,0x33,0xa9,0xa3,0x63,0x15,0xaa,0x5f,0x56,0x24,0xd5,0xb7,0xf9,0x89,0xb6,0xf1,0xed,0x20,0x7c,0x5a,0xe0,0xfd,0x36,0xca,0xe9,0x5a,0x6,0x42,0x2c,0x36}, +{0xce,0x29,0x35,0x43,0x4e,0xfe,0x98,0x3d,0x53,0x3a,0xf9,0x74,0x73,0x9a,0x4b,0xa7,0xd0,0xf5,0x1f,0x59,0x6f,0x4e,0x81,0x86,0xe,0x9d,0xad,0x81,0xaf,0xd8,0x5a,0x9f}, +{0xa7,0x5,0x6,0x67,0xee,0x34,0x62,0x6a,0x8b,0xb,0x28,0xbe,0x6e,0xb9,0x17,0x27,0x47,0x74,0x7,0x26,0xc6,0x80,0x10,0x3f,0xe0,0xa0,0x7e,0x6f,0xc6,0x7e,0x48,0x7b}, +{0xd,0x55,0xa,0xa5,0x4a,0xf8,0xa4,0xc0,0x91,0xe3,0xe7,0x9f,0x97,0x8e,0xf1,0x9e,0x86,0x76,0x72,0x81,0x50,0x60,0x8d,0xd4,0x7e,0x9e,0x5a,0x41,0xf3,0xe5,0xb0,0x62}, +{0xfc,0x9f,0x1f,0xec,0x40,0x54,0x20,0x7a,0xe3,0xe4,0x1a,0x0,0xce,0xf4,0xc9,0x84,0x4f,0xd7,0x94,0xf5,0x9d,0xfa,0x95,0xd8,0x55,0x2e,0x7e,0x11,0x24,0xc3,0x54,0xa5}, +{0x5b,0xdf,0x72,0x28,0xbd,0xfe,0x6e,0x28,0x78,0xf5,0x7f,0xe2,0xf,0xa5,0xc4,0xb2,0x5,0x89,0x7c,0xef,0xee,0x49,0xd3,0x2e,0x44,0x7e,0x93,0x85,0xeb,0x28,0x59,0x7f}, +{0x70,0x5f,0x69,0x37,0xb3,0x24,0x31,0x4a,0x5e,0x86,0x28,0xf1,0x1d,0xd6,0xe4,0x65,0xc7,0x1b,0x77,0x4,0x51,0xb9,0x20,0xe7,0x74,0xfe,0x43,0xe8,0x23,0xd4,0x87,0x8a}, +{0x7d,0x29,0xe8,0xa3,0x92,0x76,0x94,0xf2,0xdd,0xcb,0x7a,0x9,0x9b,0x30,0xd9,0xc1,0x1d,0x1b,0x30,0xfb,0x5b,0xdc,0x1b,0xe0,0xda,0x24,0x49,0x4f,0xf2,0x9c,0x82,0xbf}, +{0xa4,0xe7,0xba,0x31,0xb4,0x70,0xbf,0xff,0xd,0x32,0x44,0x5,0xde,0xf8,0xbc,0x48,0x3b,0xae,0xfc,0x32,0x53,0xbb,0xd3,0x39,0x45,0x9f,0xc3,0xc1,0xe0,0x29,0x8b,0xa0}, +{0xe5,0xc9,0x5,0xfd,0xf7,0xae,0x9,0xf,0x94,0x70,0x34,0x12,0x42,0x90,0xf1,0x34,0xa2,0x71,0xb7,0x1,0xe3,0x44,0xed,0x95,0xe9,0x3b,0x8e,0x36,0x4f,0x2f,0x98,0x4a}, +{0x88,0x40,0x1d,0x63,0xa0,0x6c,0xf6,0x15,0x47,0xc1,0x44,0x4b,0x87,0x52,0xaf,0xff,0x7e,0xbb,0x4a,0xf1,0xe2,0xa,0xc6,0x30,0x46,0x70,0xb6,0xc5,0xcc,0x6e,0x8c,0xe6}, +{0xa4,0xd5,0xa4,0x56,0xbd,0x4f,0xca,0x0,0xda,0x9d,0x84,0x4b,0xc8,0x3e,0x18,0xae,0x73,0x57,0xce,0x45,0x30,0x64,0xd1,0xad,0xe8,0xa6,0xce,0x68,0x14,0x5c,0x25,0x67}, +{0xa3,0xda,0x8c,0xf2,0xcb,0xe,0xe1,0x16,0x33,0xe9,0x6,0x58,0x9a,0x94,0x99,0x9a,0x1f,0x60,0xb2,0x20,0xc2,0x6f,0x84,0x7b,0xd1,0xce,0xac,0x7f,0xa0,0xd1,0x85,0x18}, +{0x32,0x59,0x5b,0xa1,0x8d,0xdd,0x19,0xd3,0x50,0x9a,0x1c,0xc0,0xaa,0xa5,0xb4,0x46,0x9f,0x3d,0x63,0x67,0xe4,0x4,0x6b,0xba,0xf6,0xca,0x19,0xab,0xb,0x56,0xee,0x7e}, +{0x1f,0xb1,0x79,0xea,0xa9,0x28,0x21,0x74,0xe9,0xbd,0xf7,0x35,0x3b,0x36,0x51,0xee,0x1d,0x57,0xac,0x5a,0x75,0x50,0xd3,0x76,0x3a,0x46,0xc2,0xfe,0xa3,0x7d,0x70,0x1}, +{0xf7,0x35,0xc1,0xaf,0x98,0xa4,0xd8,0x42,0x78,0xed,0xec,0x20,0x9e,0x6b,0x67,0x79,0x41,0x83,0x63,0x15,0xea,0x3a,0xdb,0xa8,0xfa,0xc3,0x3b,0x4d,0x32,0x83,0x2c,0x83}, +{0xa7,0x40,0x3b,0x1f,0x1c,0x27,0x47,0xf3,0x59,0x40,0xf0,0x34,0xb7,0x2d,0x76,0x9a,0xe7,0x3e,0x4e,0x6c,0xd2,0x21,0x4f,0xfd,0xb8,0xfd,0x8d,0x39,0xdc,0x57,0x59,0xef}, +{0x8d,0x9b,0xc,0x49,0x2b,0x49,0xeb,0xda,0x5b,0xa2,0xd7,0x49,0x68,0xf3,0x70,0xd,0x7d,0x3b,0xae,0xd0,0x7a,0x8d,0x55,0x84,0xf5,0xa5,0xe9,0xf0,0xe4,0xf8,0x8e,0x65}, +{0xa0,0xb8,0xa2,0xf4,0x36,0x10,0x3b,0x53,0xc,0xa8,0x7,0x9e,0x75,0x3e,0xec,0x5a,0x91,0x68,0x94,0x92,0x56,0xe8,0x88,0x4f,0x5b,0xb0,0x5c,0x55,0xf8,0xba,0xbc,0x4c}, +{0xe3,0xbb,0x3b,0x99,0xf3,0x87,0x94,0x7b,0x75,0xda,0xf4,0xd6,0x72,0x6b,0x1c,0x5d,0x64,0xae,0xac,0x28,0xdc,0x34,0xb3,0x6d,0x6c,0x34,0xa5,0x50,0xb8,0x28,0xdb,0x71}, +{0xf8,0x61,0xe2,0xf2,0x10,0x8d,0x51,0x2a,0xe3,0xdb,0x64,0x33,0x59,0xdd,0x75,0xfc,0x1c,0xac,0xbc,0xf1,0x43,0xce,0x3f,0xa2,0x67,0xbb,0xd1,0x3c,0x2,0xe8,0x43,0xb0}, +{0x33,0xa,0x5b,0xca,0x88,0x29,0xa1,0x75,0x7f,0x34,0x19,0x4d,0xb4,0x16,0x53,0x5c,0x92,0x3b,0x94,0xc3,0xe,0x79,0x4d,0x1e,0x79,0x74,0x75,0xd7,0xb6,0xee,0xaf,0x3f}, +{0xea,0xa8,0xd4,0xf7,0xbe,0x1a,0x39,0x21,0x5c,0xf4,0x7e,0x9,0x4c,0x23,0x27,0x51,0x26,0xa3,0x24,0x53,0xba,0x32,0x3c,0xd2,0x44,0xa3,0x17,0x4a,0x6d,0xa6,0xd5,0xad}, +{0xb5,0x1d,0x3e,0xa6,0xaf,0xf2,0xc9,0x8,0x83,0x59,0x3d,0x98,0x91,0x6b,0x3c,0x56,0x4c,0xf8,0x7c,0xa1,0x72,0x86,0x60,0x4d,0x46,0xe2,0x3e,0xcc,0x8,0x6e,0xc7,0xf6}, +{0x2f,0x98,0x33,0xb3,0xb1,0xbc,0x76,0x5e,0x2b,0xd6,0x66,0xa5,0xef,0xc4,0xe6,0x2a,0x6,0xf4,0xb6,0xe8,0xbe,0xc1,0xd4,0x36,0x74,0xee,0x82,0x15,0xbc,0xef,0x21,0x63}, +{0xfd,0xc1,0x4e,0xd,0xf4,0x53,0xc9,0x69,0xa7,0x7d,0x5a,0xc4,0x6,0x58,0x58,0x26,0x7e,0xc1,0x14,0x16,0x6,0xe0,0xfa,0x16,0x7e,0x90,0xaf,0x3d,0x28,0x63,0x9d,0x3f}, +{0xd2,0xc9,0xf2,0xe3,0x0,0x9b,0xd2,0xc,0x5f,0xaa,0xce,0x30,0xb7,0xd4,0xc,0x30,0x74,0x2a,0x51,0x16,0xf2,0xe0,0x32,0x98,0xd,0xeb,0x30,0xd8,0xe3,0xce,0xf8,0x9a}, +{0x4b,0xc5,0x9e,0x7b,0xb5,0xf1,0x79,0x92,0xff,0x51,0xe6,0x6e,0x4,0x86,0x68,0xd3,0x9b,0x23,0x4d,0x57,0xe6,0x96,0x67,0x31,0xcc,0xe6,0xa6,0xf3,0x17,0xa,0x75,0x5}, +{0xb1,0x76,0x81,0xd9,0x13,0x32,0x6c,0xce,0x3c,0x17,0x52,0x84,0xf8,0x5,0xa2,0x62,0xf4,0x2b,0xcb,0xb3,0x78,0x47,0x15,0x47,0xff,0x46,0x54,0x82,0x23,0x93,0x6a,0x48}, +{0x38,0xdf,0x58,0x7,0x4e,0x5e,0x65,0x65,0xf2,0xfc,0x7c,0x89,0xfc,0x86,0x50,0x8e,0x31,0x70,0x2e,0x44,0xd0,0xb,0xca,0x86,0xf0,0x40,0x9,0xa2,0x30,0x78,0x47,0x4e}, +{0x65,0xa0,0xee,0x39,0xd1,0xf7,0x38,0x83,0xf7,0x5e,0xe9,0x37,0xe4,0x2c,0x3a,0xbd,0x21,0x97,0xb2,0x26,0x1,0x13,0xf8,0x6f,0xa3,0x44,0xed,0xd1,0xef,0x9f,0xde,0xe7}, +{0x8b,0xa0,0xdf,0x15,0x76,0x25,0x92,0xd9,0x3c,0x85,0xf7,0xf6,0x12,0xdc,0x42,0xbe,0xd8,0xa7,0xec,0x7c,0xab,0x27,0xb0,0x7e,0x53,0x8d,0x7d,0xda,0xaa,0x3e,0xa8,0xde}, +{0xaa,0x25,0xce,0x93,0xbd,0x2,0x69,0xd8,0x5a,0xf6,0x43,0xfd,0x1a,0x73,0x8,0xf9,0xc0,0x5f,0xef,0xda,0x17,0x4a,0x19,0xa5,0x97,0x4d,0x66,0x33,0x4c,0xfd,0x21,0x6a}, +{0x35,0xb4,0x98,0x31,0xdb,0x41,0x15,0x70,0xea,0x1e,0xf,0xbb,0xed,0xcd,0x54,0x9b,0x9a,0xd0,0x63,0xa1,0x51,0x97,0x40,0x72,0xf6,0x75,0x9d,0xbf,0x91,0x47,0x6f,0xe2}}; + + +static void E8(hashState *state); /*The bijective function E8, in bitslice form*/ +static void F8(hashState *state); /*The compression function F8 */ + +/*The API functions*/ +static HashReturn Init(hashState *state, int hashbitlen); +static HashReturn Update(hashState *state, const BitSequence *data, DataLength databitlen); +static HashReturn Final(hashState *state, BitSequence *hashval); +HashReturn jh_hash(int hashbitlen, const BitSequence *data,DataLength databitlen, BitSequence *hashval); + +/*swapping bit 2i with bit 2i+1 of 64-bit x*/ +#define SWAP1(x) (x) = ((((x) & 0x5555555555555555ULL) << 1) | (((x) & 0xaaaaaaaaaaaaaaaaULL) >> 1)); +/*swapping bits 4i||4i+1 with bits 4i+2||4i+3 of 64-bit x*/ +#define SWAP2(x) (x) = ((((x) & 0x3333333333333333ULL) << 2) | (((x) & 0xccccccccccccccccULL) >> 2)); +/*swapping bits 8i||8i+1||8i+2||8i+3 with bits 8i+4||8i+5||8i+6||8i+7 of 64-bit x*/ +#define SWAP4(x) (x) = ((((x) & 0x0f0f0f0f0f0f0f0fULL) << 4) | (((x) & 0xf0f0f0f0f0f0f0f0ULL) >> 4)); +/*swapping bits 16i||16i+1||......||16i+7 with bits 16i+8||16i+9||......||16i+15 of 64-bit x*/ +#define SWAP8(x) (x) = ((((x) & 0x00ff00ff00ff00ffULL) << 8) | (((x) & 0xff00ff00ff00ff00ULL) >> 8)); +/*swapping bits 32i||32i+1||......||32i+15 with bits 32i+16||32i+17||......||32i+31 of 64-bit x*/ +#define SWAP16(x) (x) = ((((x) & 0x0000ffff0000ffffULL) << 16) | (((x) & 0xffff0000ffff0000ULL) >> 16)); +/*swapping bits 64i||64i+1||......||64i+31 with bits 64i+32||64i+33||......||64i+63 of 64-bit x*/ +#define SWAP32(x) (x) = (((x) << 32) | ((x) >> 32)); + +/*The MDS transform*/ +#define L(m0,m1,m2,m3,m4,m5,m6,m7) \ + (m4) ^= (m1); \ + (m5) ^= (m2); \ + (m6) ^= (m0) ^ (m3); \ + (m7) ^= (m0); \ + (m0) ^= (m5); \ + (m1) ^= (m6); \ + (m2) ^= (m4) ^ (m7); \ + (m3) ^= (m4); + +/*Two Sboxes are computed in parallel, each Sbox implements S0 and S1, selected by a constant bit*/ +/*The reason to compute two Sboxes in parallel is to try to fully utilize the parallel processing power*/ +#define SS(m0,m1,m2,m3,m4,m5,m6,m7,cc0,cc1) \ + m3 = ~(m3); \ + m7 = ~(m7); \ + m0 ^= ((~(m2)) & (cc0)); \ + m4 ^= ((~(m6)) & (cc1)); \ + temp0 = (cc0) ^ ((m0) & (m1));\ + temp1 = (cc1) ^ ((m4) & (m5));\ + m0 ^= ((m2) & (m3)); \ + m4 ^= ((m6) & (m7)); \ + m3 ^= ((~(m1)) & (m2)); \ + m7 ^= ((~(m5)) & (m6)); \ + m1 ^= ((m0) & (m2)); \ + m5 ^= ((m4) & (m6)); \ + m2 ^= ((m0) & (~(m3))); \ + m6 ^= ((m4) & (~(m7))); \ + m0 ^= ((m1) | (m3)); \ + m4 ^= ((m5) | (m7)); \ + m3 ^= ((m1) & (m2)); \ + m7 ^= ((m5) & (m6)); \ + m1 ^= (temp0 & (m0)); \ + m5 ^= (temp1 & (m4)); \ + m2 ^= temp0; \ + m6 ^= temp1; + +/*The bijective function E8, in bitslice form*/ +static void E8(hashState *state) +{ + uint64 i,roundnumber,temp0,temp1; + + for (roundnumber = 0; roundnumber < 42; roundnumber = roundnumber+7) { + /*round 7*roundnumber+0: Sbox, MDS and Swapping layers*/ + for (i = 0; i < 2; i++) { + SS(state->x[0][i],state->x[2][i],state->x[4][i],state->x[6][i],state->x[1][i],state->x[3][i],state->x[5][i],state->x[7][i],((uint64*)E8_bitslice_roundconstant[roundnumber+0])[i],((uint64*)E8_bitslice_roundconstant[roundnumber+0])[i+2] ); + L(state->x[0][i],state->x[2][i],state->x[4][i],state->x[6][i],state->x[1][i],state->x[3][i],state->x[5][i],state->x[7][i]); + SWAP1(state->x[1][i]); SWAP1(state->x[3][i]); SWAP1(state->x[5][i]); SWAP1(state->x[7][i]); + } + + /*round 7*roundnumber+1: Sbox, MDS and Swapping layers*/ + for (i = 0; i < 2; i++) { + SS(state->x[0][i],state->x[2][i],state->x[4][i],state->x[6][i],state->x[1][i],state->x[3][i],state->x[5][i],state->x[7][i],((uint64*)E8_bitslice_roundconstant[roundnumber+1])[i],((uint64*)E8_bitslice_roundconstant[roundnumber+1])[i+2] ); + L(state->x[0][i],state->x[2][i],state->x[4][i],state->x[6][i],state->x[1][i],state->x[3][i],state->x[5][i],state->x[7][i]); + SWAP2(state->x[1][i]); SWAP2(state->x[3][i]); SWAP2(state->x[5][i]); SWAP2(state->x[7][i]); + } + + /*round 7*roundnumber+2: Sbox, MDS and Swapping layers*/ + for (i = 0; i < 2; i++) { + SS(state->x[0][i],state->x[2][i],state->x[4][i],state->x[6][i],state->x[1][i],state->x[3][i],state->x[5][i],state->x[7][i],((uint64*)E8_bitslice_roundconstant[roundnumber+2])[i],((uint64*)E8_bitslice_roundconstant[roundnumber+2])[i+2] ); + L(state->x[0][i],state->x[2][i],state->x[4][i],state->x[6][i],state->x[1][i],state->x[3][i],state->x[5][i],state->x[7][i]); + SWAP4(state->x[1][i]); SWAP4(state->x[3][i]); SWAP4(state->x[5][i]); SWAP4(state->x[7][i]); + } + + /*round 7*roundnumber+3: Sbox, MDS and Swapping layers*/ + for (i = 0; i < 2; i++) { + SS(state->x[0][i],state->x[2][i],state->x[4][i],state->x[6][i],state->x[1][i],state->x[3][i],state->x[5][i],state->x[7][i],((uint64*)E8_bitslice_roundconstant[roundnumber+3])[i],((uint64*)E8_bitslice_roundconstant[roundnumber+3])[i+2] ); + L(state->x[0][i],state->x[2][i],state->x[4][i],state->x[6][i],state->x[1][i],state->x[3][i],state->x[5][i],state->x[7][i]); + SWAP8(state->x[1][i]); SWAP8(state->x[3][i]); SWAP8(state->x[5][i]); SWAP8(state->x[7][i]); + } + + /*round 7*roundnumber+4: Sbox, MDS and Swapping layers*/ + for (i = 0; i < 2; i++) { + SS(state->x[0][i],state->x[2][i],state->x[4][i],state->x[6][i],state->x[1][i],state->x[3][i],state->x[5][i],state->x[7][i],((uint64*)E8_bitslice_roundconstant[roundnumber+4])[i],((uint64*)E8_bitslice_roundconstant[roundnumber+4])[i+2] ); + L(state->x[0][i],state->x[2][i],state->x[4][i],state->x[6][i],state->x[1][i],state->x[3][i],state->x[5][i],state->x[7][i]); + SWAP16(state->x[1][i]); SWAP16(state->x[3][i]); SWAP16(state->x[5][i]); SWAP16(state->x[7][i]); + } + + /*round 7*roundnumber+5: Sbox, MDS and Swapping layers*/ + for (i = 0; i < 2; i++) { + SS(state->x[0][i],state->x[2][i],state->x[4][i],state->x[6][i],state->x[1][i],state->x[3][i],state->x[5][i],state->x[7][i],((uint64*)E8_bitslice_roundconstant[roundnumber+5])[i],((uint64*)E8_bitslice_roundconstant[roundnumber+5])[i+2] ); + L(state->x[0][i],state->x[2][i],state->x[4][i],state->x[6][i],state->x[1][i],state->x[3][i],state->x[5][i],state->x[7][i]); + SWAP32(state->x[1][i]); SWAP32(state->x[3][i]); SWAP32(state->x[5][i]); SWAP32(state->x[7][i]); + } + + /*round 7*roundnumber+6: Sbox and MDS layers*/ + for (i = 0; i < 2; i++) { + SS(state->x[0][i],state->x[2][i],state->x[4][i],state->x[6][i],state->x[1][i],state->x[3][i],state->x[5][i],state->x[7][i],((uint64*)E8_bitslice_roundconstant[roundnumber+6])[i],((uint64*)E8_bitslice_roundconstant[roundnumber+6])[i+2] ); + L(state->x[0][i],state->x[2][i],state->x[4][i],state->x[6][i],state->x[1][i],state->x[3][i],state->x[5][i],state->x[7][i]); + } + /*round 7*roundnumber+6: swapping layer*/ + for (i = 1; i < 8; i = i+2) { + temp0 = state->x[i][0]; state->x[i][0] = state->x[i][1]; state->x[i][1] = temp0; + } + } + +} + +/*The compression function F8 */ +static void F8(hashState *state) +{ + uint64 i; + + /*xor the 512-bit message with the fist half of the 1024-bit hash state*/ + for (i = 0; i < 8; i++) state->x[i >> 1][i & 1] ^= ((uint64*)state->buffer)[i]; + + /*the bijective function E8 */ + E8(state); + + /*xor the 512-bit message with the second half of the 1024-bit hash state*/ + for (i = 0; i < 8; i++) state->x[(8+i) >> 1][(8+i) & 1] ^= ((uint64*)state->buffer)[i]; +} + +/*before hashing a message, initialize the hash state as H0 */ +static HashReturn Init(hashState *state, int hashbitlen) +{ + state->databitlen = 0; + state->datasize_in_buffer = 0; + + /*initialize the initial hash value of JH*/ + state->hashbitlen = hashbitlen; + + /*load the intital hash value into state*/ + switch (hashbitlen) + { + case 224: memcpy(state->x,JH224_H0,128); break; + case 256: memcpy(state->x,JH256_H0,128); break; + case 384: memcpy(state->x,JH384_H0,128); break; + case 512: memcpy(state->x,JH512_H0,128); break; + } + + return(SUCCESS); +} + + +/*hash each 512-bit message block, except the last partial block*/ +static HashReturn Update(hashState *state, const BitSequence *data, DataLength databitlen) +{ + DataLength index; /*the starting address of the data to be compressed*/ + + state->databitlen += databitlen; + index = 0; + + /*if there is remaining data in the buffer, fill it to a full message block first*/ + /*we assume that the size of the data in the buffer is the multiple of 8 bits if it is not at the end of a message*/ + + /*There is data in the buffer, but the incoming data is insufficient for a full block*/ + if ( (state->datasize_in_buffer > 0 ) && (( state->datasize_in_buffer + databitlen) < 512) ) { + if ( (databitlen & 7) == 0 ) { + memcpy(state->buffer + (state->datasize_in_buffer >> 3), data, 64-(state->datasize_in_buffer >> 3)) ; + } + else memcpy(state->buffer + (state->datasize_in_buffer >> 3), data, 64-(state->datasize_in_buffer >> 3)+1) ; + state->datasize_in_buffer += databitlen; + databitlen = 0; + } + + /*There is data in the buffer, and the incoming data is sufficient for a full block*/ + if ( (state->datasize_in_buffer > 0 ) && (( state->datasize_in_buffer + databitlen) >= 512) ) { + memcpy( state->buffer + (state->datasize_in_buffer >> 3), data, 64-(state->datasize_in_buffer >> 3) ) ; + index = 64-(state->datasize_in_buffer >> 3); + databitlen = databitlen - (512 - state->datasize_in_buffer); + F8(state); + state->datasize_in_buffer = 0; + } + + /*hash the remaining full message blocks*/ + for ( ; databitlen >= 512; index = index+64, databitlen = databitlen - 512) { + memcpy(state->buffer, data+index, 64); + F8(state); + } + + /*store the partial block into buffer, assume that -- if part of the last byte is not part of the message, then that part consists of 0 bits*/ + if ( databitlen > 0) { + if ((databitlen & 7) == 0) + memcpy(state->buffer, data+index, (databitlen & 0x1ff) >> 3); + else + memcpy(state->buffer, data+index, ((databitlen & 0x1ff) >> 3)+1); + state->datasize_in_buffer = databitlen; + } + + return(SUCCESS); +} + +/*pad the message, process the padded block(s), truncate the hash value H to obtain the message digest*/ +static HashReturn Final(hashState *state, BitSequence *hashval) +{ + unsigned int i; + + if ( (state->databitlen & 0x1ff) == 0 ) { + /*pad the message when databitlen is multiple of 512 bits, then process the padded block*/ + memset(state->buffer, 0, 64); + state->buffer[0] = 0x80; + state->buffer[63] = state->databitlen & 0xff; + state->buffer[62] = (state->databitlen >> 8) & 0xff; + state->buffer[61] = (state->databitlen >> 16) & 0xff; + state->buffer[60] = (state->databitlen >> 24) & 0xff; + state->buffer[59] = (state->databitlen >> 32) & 0xff; + state->buffer[58] = (state->databitlen >> 40) & 0xff; + state->buffer[57] = (state->databitlen >> 48) & 0xff; + state->buffer[56] = (state->databitlen >> 56) & 0xff; + F8(state); + } + else { + /*set the rest of the bytes in the buffer to 0*/ + if ( (state->datasize_in_buffer & 7) == 0) + for (i = (state->databitlen & 0x1ff) >> 3; i < 64; i++) state->buffer[i] = 0; + else + for (i = ((state->databitlen & 0x1ff) >> 3)+1; i < 64; i++) state->buffer[i] = 0; + + /*pad and process the partial block when databitlen is not multiple of 512 bits, then hash the padded blocks*/ + state->buffer[((state->databitlen & 0x1ff) >> 3)] |= 1 << (7- (state->databitlen & 7)); + + F8(state); + memset(state->buffer, 0, 64); + state->buffer[63] = state->databitlen & 0xff; + state->buffer[62] = (state->databitlen >> 8) & 0xff; + state->buffer[61] = (state->databitlen >> 16) & 0xff; + state->buffer[60] = (state->databitlen >> 24) & 0xff; + state->buffer[59] = (state->databitlen >> 32) & 0xff; + state->buffer[58] = (state->databitlen >> 40) & 0xff; + state->buffer[57] = (state->databitlen >> 48) & 0xff; + state->buffer[56] = (state->databitlen >> 56) & 0xff; + F8(state); + } + + /*truncating the final hash value to generate the message digest*/ + switch(state->hashbitlen) { + case 224: memcpy(hashval,(unsigned char*)state->x+64+36,28); break; + case 256: memcpy(hashval,(unsigned char*)state->x+64+32,32); break; + case 384: memcpy(hashval,(unsigned char*)state->x+64+16,48); break; + case 512: memcpy(hashval,(unsigned char*)state->x+64,64); break; + } + + return(SUCCESS); +} + +/* hash a message, + three inputs: message digest size in bits (hashbitlen); message (data); message length in bits (databitlen) + one output: message digest (hashval) +*/ +HashReturn jh_hash(int hashbitlen, const BitSequence *data,DataLength databitlen, BitSequence *hashval) +{ + hashState state; + + if ( hashbitlen == 224 || hashbitlen == 256 || hashbitlen == 384 || hashbitlen == 512 ) { + Init(&state, hashbitlen); + Update(&state, data, databitlen); + Final(&state, hashval); + return SUCCESS; + } + else + return(BAD_HASHLEN); +} diff --git a/src/crypto/jh.h b/src/crypto/jh.h new file mode 100644 index 000000000..627a9ff05 --- /dev/null +++ b/src/crypto/jh.h @@ -0,0 +1,21 @@ +/*This program gives the 64-bit optimized bitslice implementation of JH using ANSI C + + -------------------------------- + Performance + + Microprocessor: Intel CORE 2 processor (Core 2 Duo Mobile T6600 2.2GHz) + Operating System: 64-bit Ubuntu 10.04 (Linux kernel 2.6.32-22-generic) + Speed for long message: + 1) 45.8 cycles/byte compiler: Intel C++ Compiler 11.1 compilation option: icc -O2 + 2) 56.8 cycles/byte compiler: gcc 4.4.3 compilation option: gcc -O3 + + -------------------------------- + Last Modified: January 16, 2011 +*/ +#pragma once + +typedef unsigned char BitSequence; +typedef unsigned long long DataLength; +typedef enum {SUCCESS = 0, FAIL = 1, BAD_HASHLEN = 2} HashReturn; + +HashReturn jh_hash(int hashbitlen, const BitSequence *data, DataLength databitlen, BitSequence *hashval); diff --git a/src/crypto/keccak.c b/src/crypto/keccak.c new file mode 100644 index 000000000..3ee2a887c --- /dev/null +++ b/src/crypto/keccak.c @@ -0,0 +1,112 @@ +// keccak.c +// 19-Nov-11 Markku-Juhani O. Saarinen +// A baseline Keccak (3rd round) implementation. + +#include "hash-ops.h" +#include "keccak.h" + +const uint64_t keccakf_rndc[24] = +{ + 0x0000000000000001, 0x0000000000008082, 0x800000000000808a, + 0x8000000080008000, 0x000000000000808b, 0x0000000080000001, + 0x8000000080008081, 0x8000000000008009, 0x000000000000008a, + 0x0000000000000088, 0x0000000080008009, 0x000000008000000a, + 0x000000008000808b, 0x800000000000008b, 0x8000000000008089, + 0x8000000000008003, 0x8000000000008002, 0x8000000000000080, + 0x000000000000800a, 0x800000008000000a, 0x8000000080008081, + 0x8000000000008080, 0x0000000080000001, 0x8000000080008008 +}; + +const int keccakf_rotc[24] = +{ + 1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14, + 27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44 +}; + +const int keccakf_piln[24] = +{ + 10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4, + 15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1 +}; + +// update the state with given number of rounds + +void keccakf(uint64_t st[25], int rounds) +{ + int i, j, round; + uint64_t t, bc[5]; + + for (round = 0; round < rounds; round++) { + + // Theta + for (i = 0; i < 5; i++) + bc[i] = st[i] ^ st[i + 5] ^ st[i + 10] ^ st[i + 15] ^ st[i + 20]; + + for (i = 0; i < 5; i++) { + t = bc[(i + 4) % 5] ^ ROTL64(bc[(i + 1) % 5], 1); + for (j = 0; j < 25; j += 5) + st[j + i] ^= t; + } + + // Rho Pi + t = st[1]; + for (i = 0; i < 24; i++) { + j = keccakf_piln[i]; + bc[0] = st[j]; + st[j] = ROTL64(t, keccakf_rotc[i]); + t = bc[0]; + } + + // Chi + for (j = 0; j < 25; j += 5) { + for (i = 0; i < 5; i++) + bc[i] = st[j + i]; + for (i = 0; i < 5; i++) + st[j + i] ^= (~bc[(i + 1) % 5]) & bc[(i + 2) % 5]; + } + + // Iota + st[0] ^= keccakf_rndc[round]; + } +} + +// compute a keccak hash (md) of given byte length from "in" +typedef uint64_t state_t[25]; + +int keccak(const uint8_t *in, int inlen, uint8_t *md, int mdlen) +{ + state_t st; + uint8_t temp[144]; + int i, rsiz, rsizw; + + rsiz = sizeof(state_t) == mdlen ? HASH_DATA_AREA : 200 - 2 * mdlen; + rsizw = rsiz / 8; + + memset(st, 0, sizeof(st)); + + for ( ; inlen >= rsiz; inlen -= rsiz, in += rsiz) { + for (i = 0; i < rsizw; i++) + st[i] ^= ((uint64_t *) in)[i]; + keccakf(st, KECCAK_ROUNDS); + } + + // last block and padding + memcpy(temp, in, inlen); + temp[inlen++] = 1; + memset(temp + inlen, 0, rsiz - inlen); + temp[rsiz - 1] |= 0x80; + + for (i = 0; i < rsizw; i++) + st[i] ^= ((uint64_t *) temp)[i]; + + keccakf(st, KECCAK_ROUNDS); + + memcpy(md, st, mdlen); + + return 0; +} + +void keccak1600(const uint8_t *in, int inlen, uint8_t *md) +{ + keccak(in, inlen, md, sizeof(state_t)); +} diff --git a/src/crypto/keccak.h b/src/crypto/keccak.h new file mode 100644 index 000000000..4f7f85729 --- /dev/null +++ b/src/crypto/keccak.h @@ -0,0 +1,26 @@ +// keccak.h +// 19-Nov-11 Markku-Juhani O. Saarinen + +#ifndef KECCAK_H +#define KECCAK_H + +#include +#include + +#ifndef KECCAK_ROUNDS +#define KECCAK_ROUNDS 24 +#endif + +#ifndef ROTL64 +#define ROTL64(x, y) (((x) << (y)) | ((x) >> (64 - (y)))) +#endif + +// compute a keccak hash (md) of given byte length from "in" +int keccak(const uint8_t *in, int inlen, uint8_t *md, int mdlen); + +// update the state +void keccakf(uint64_t st[25], int norounds); + +void keccak1600(const uint8_t *in, int inlen, uint8_t *md); + +#endif diff --git a/src/crypto/oaes_config.h b/src/crypto/oaes_config.h new file mode 100644 index 000000000..8a58c3cfc --- /dev/null +++ b/src/crypto/oaes_config.h @@ -0,0 +1,50 @@ +/* + * --------------------------------------------------------------------------- + * OpenAES License + * --------------------------------------------------------------------------- + * Copyright (c) 2012, Nabil S. Al Ramli, www.nalramli.com + * 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. + * + * 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. + * --------------------------------------------------------------------------- + */ + +#ifndef _OAES_CONFIG_H +#define _OAES_CONFIG_H + +#ifdef __cplusplus +extern "C" { +#endif + +//#ifndef OAES_HAVE_ISAAC +//#define OAES_HAVE_ISAAC 1 +//#endif // OAES_HAVE_ISAAC + +#ifndef OAES_DEBUG +#define OAES_DEBUG 0 +#endif // OAES_DEBUG + +#ifdef __cplusplus +} +#endif + +#endif // _OAES_CONFIG_H diff --git a/src/crypto/oaes_lib.c b/src/crypto/oaes_lib.c new file mode 100644 index 000000000..d77c52f11 --- /dev/null +++ b/src/crypto/oaes_lib.c @@ -0,0 +1,1486 @@ +/* + * --------------------------------------------------------------------------- + * OpenAES License + * --------------------------------------------------------------------------- + * Copyright (c) 2012, Nabil S. Al Ramli, www.nalramli.com + * 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. + * + * 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. + * --------------------------------------------------------------------------- + */ +static const char _NR[] = { + 0x4e,0x61,0x62,0x69,0x6c,0x20,0x53,0x2e,0x20, + 0x41,0x6c,0x20,0x52,0x61,0x6d,0x6c,0x69,0x00 }; + +#include +#include +#include +#include +#include +#include + +#ifdef WIN32 +#include +#else +#include +#include +#endif + +#include "oaes_config.h" +#include "oaes_lib.h" + +#ifdef OAES_HAVE_ISAAC +#include "rand.h" +#endif // OAES_HAVE_ISAAC + +#define OAES_RKEY_LEN 4 +#define OAES_COL_LEN 4 +#define OAES_ROUND_BASE 7 + +// the block is padded +#define OAES_FLAG_PAD 0x01 + +#ifndef min +# define min(a,b) (((a)<(b)) ? (a) : (b)) +#endif /* min */ + +typedef struct _oaes_key +{ + size_t data_len; + uint8_t *data; + size_t exp_data_len; + uint8_t *exp_data; + size_t num_keys; + size_t key_base; +} oaes_key; + +typedef struct _oaes_ctx +{ +#ifdef OAES_HAVE_ISAAC + randctx * rctx; +#endif // OAES_HAVE_ISAAC + +#ifdef OAES_DEBUG + oaes_step_cb step_cb; +#endif // OAES_DEBUG + + oaes_key * key; + OAES_OPTION options; + uint8_t iv[OAES_BLOCK_SIZE]; +} oaes_ctx; + +// "OAES<8-bit header version><8-bit type><16-bit options><8-bit flags><56-bit reserved>" +static uint8_t oaes_header[OAES_BLOCK_SIZE] = { + // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f, + /*0*/ 0x4f, 0x41, 0x45, 0x53, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; +static uint8_t oaes_gf_8[] = { + 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36 }; + +static uint8_t oaes_sub_byte_value[16][16] = { + // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f, + /*0*/ { 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76 }, + /*1*/ { 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0 }, + /*2*/ { 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15 }, + /*3*/ { 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75 }, + /*4*/ { 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84 }, + /*5*/ { 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf }, + /*6*/ { 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8 }, + /*7*/ { 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2 }, + /*8*/ { 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73 }, + /*9*/ { 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb }, + /*a*/ { 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79 }, + /*b*/ { 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08 }, + /*c*/ { 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a }, + /*d*/ { 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e }, + /*e*/ { 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf }, + /*f*/ { 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 }, +}; + +static uint8_t oaes_inv_sub_byte_value[16][16] = { + // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f, + /*0*/ { 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb }, + /*1*/ { 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb }, + /*2*/ { 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e }, + /*3*/ { 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25 }, + /*4*/ { 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92 }, + /*5*/ { 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84 }, + /*6*/ { 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06 }, + /*7*/ { 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b }, + /*8*/ { 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73 }, + /*9*/ { 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e }, + /*a*/ { 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b }, + /*b*/ { 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4 }, + /*c*/ { 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f }, + /*d*/ { 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef }, + /*e*/ { 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61 }, + /*f*/ { 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d }, +}; + +static uint8_t oaes_gf_mul_2[16][16] = { + // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f, + /*0*/ { 0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e }, + /*1*/ { 0x20, 0x22, 0x24, 0x26, 0x28, 0x2a, 0x2c, 0x2e, 0x30, 0x32, 0x34, 0x36, 0x38, 0x3a, 0x3c, 0x3e }, + /*2*/ { 0x40, 0x42, 0x44, 0x46, 0x48, 0x4a, 0x4c, 0x4e, 0x50, 0x52, 0x54, 0x56, 0x58, 0x5a, 0x5c, 0x5e }, + /*3*/ { 0x60, 0x62, 0x64, 0x66, 0x68, 0x6a, 0x6c, 0x6e, 0x70, 0x72, 0x74, 0x76, 0x78, 0x7a, 0x7c, 0x7e }, + /*4*/ { 0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c, 0x8e, 0x90, 0x92, 0x94, 0x96, 0x98, 0x9a, 0x9c, 0x9e }, + /*5*/ { 0xa0, 0xa2, 0xa4, 0xa6, 0xa8, 0xaa, 0xac, 0xae, 0xb0, 0xb2, 0xb4, 0xb6, 0xb8, 0xba, 0xbc, 0xbe }, + /*6*/ { 0xc0, 0xc2, 0xc4, 0xc6, 0xc8, 0xca, 0xcc, 0xce, 0xd0, 0xd2, 0xd4, 0xd6, 0xd8, 0xda, 0xdc, 0xde }, + /*7*/ { 0xe0, 0xe2, 0xe4, 0xe6, 0xe8, 0xea, 0xec, 0xee, 0xf0, 0xf2, 0xf4, 0xf6, 0xf8, 0xfa, 0xfc, 0xfe }, + /*8*/ { 0x1b, 0x19, 0x1f, 0x1d, 0x13, 0x11, 0x17, 0x15, 0x0b, 0x09, 0x0f, 0x0d, 0x03, 0x01, 0x07, 0x05 }, + /*9*/ { 0x3b, 0x39, 0x3f, 0x3d, 0x33, 0x31, 0x37, 0x35, 0x2b, 0x29, 0x2f, 0x2d, 0x23, 0x21, 0x27, 0x25 }, + /*a*/ { 0x5b, 0x59, 0x5f, 0x5d, 0x53, 0x51, 0x57, 0x55, 0x4b, 0x49, 0x4f, 0x4d, 0x43, 0x41, 0x47, 0x45 }, + /*b*/ { 0x7b, 0x79, 0x7f, 0x7d, 0x73, 0x71, 0x77, 0x75, 0x6b, 0x69, 0x6f, 0x6d, 0x63, 0x61, 0x67, 0x65 }, + /*c*/ { 0x9b, 0x99, 0x9f, 0x9d, 0x93, 0x91, 0x97, 0x95, 0x8b, 0x89, 0x8f, 0x8d, 0x83, 0x81, 0x87, 0x85 }, + /*d*/ { 0xbb, 0xb9, 0xbf, 0xbd, 0xb3, 0xb1, 0xb7, 0xb5, 0xab, 0xa9, 0xaf, 0xad, 0xa3, 0xa1, 0xa7, 0xa5 }, + /*e*/ { 0xdb, 0xd9, 0xdf, 0xdd, 0xd3, 0xd1, 0xd7, 0xd5, 0xcb, 0xc9, 0xcf, 0xcd, 0xc3, 0xc1, 0xc7, 0xc5 }, + /*f*/ { 0xfb, 0xf9, 0xff, 0xfd, 0xf3, 0xf1, 0xf7, 0xf5, 0xeb, 0xe9, 0xef, 0xed, 0xe3, 0xe1, 0xe7, 0xe5 }, +}; + +static uint8_t oaes_gf_mul_3[16][16] = { + // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f, + /*0*/ { 0x00, 0x03, 0x06, 0x05, 0x0c, 0x0f, 0x0a, 0x09, 0x18, 0x1b, 0x1e, 0x1d, 0x14, 0x17, 0x12, 0x11 }, + /*1*/ { 0x30, 0x33, 0x36, 0x35, 0x3c, 0x3f, 0x3a, 0x39, 0x28, 0x2b, 0x2e, 0x2d, 0x24, 0x27, 0x22, 0x21 }, + /*2*/ { 0x60, 0x63, 0x66, 0x65, 0x6c, 0x6f, 0x6a, 0x69, 0x78, 0x7b, 0x7e, 0x7d, 0x74, 0x77, 0x72, 0x71 }, + /*3*/ { 0x50, 0x53, 0x56, 0x55, 0x5c, 0x5f, 0x5a, 0x59, 0x48, 0x4b, 0x4e, 0x4d, 0x44, 0x47, 0x42, 0x41 }, + /*4*/ { 0xc0, 0xc3, 0xc6, 0xc5, 0xcc, 0xcf, 0xca, 0xc9, 0xd8, 0xdb, 0xde, 0xdd, 0xd4, 0xd7, 0xd2, 0xd1 }, + /*5*/ { 0xf0, 0xf3, 0xf6, 0xf5, 0xfc, 0xff, 0xfa, 0xf9, 0xe8, 0xeb, 0xee, 0xed, 0xe4, 0xe7, 0xe2, 0xe1 }, + /*6*/ { 0xa0, 0xa3, 0xa6, 0xa5, 0xac, 0xaf, 0xaa, 0xa9, 0xb8, 0xbb, 0xbe, 0xbd, 0xb4, 0xb7, 0xb2, 0xb1 }, + /*7*/ { 0x90, 0x93, 0x96, 0x95, 0x9c, 0x9f, 0x9a, 0x99, 0x88, 0x8b, 0x8e, 0x8d, 0x84, 0x87, 0x82, 0x81 }, + /*8*/ { 0x9b, 0x98, 0x9d, 0x9e, 0x97, 0x94, 0x91, 0x92, 0x83, 0x80, 0x85, 0x86, 0x8f, 0x8c, 0x89, 0x8a }, + /*9*/ { 0xab, 0xa8, 0xad, 0xae, 0xa7, 0xa4, 0xa1, 0xa2, 0xb3, 0xb0, 0xb5, 0xb6, 0xbf, 0xbc, 0xb9, 0xba }, + /*a*/ { 0xfb, 0xf8, 0xfd, 0xfe, 0xf7, 0xf4, 0xf1, 0xf2, 0xe3, 0xe0, 0xe5, 0xe6, 0xef, 0xec, 0xe9, 0xea }, + /*b*/ { 0xcb, 0xc8, 0xcd, 0xce, 0xc7, 0xc4, 0xc1, 0xc2, 0xd3, 0xd0, 0xd5, 0xd6, 0xdf, 0xdc, 0xd9, 0xda }, + /*c*/ { 0x5b, 0x58, 0x5d, 0x5e, 0x57, 0x54, 0x51, 0x52, 0x43, 0x40, 0x45, 0x46, 0x4f, 0x4c, 0x49, 0x4a }, + /*d*/ { 0x6b, 0x68, 0x6d, 0x6e, 0x67, 0x64, 0x61, 0x62, 0x73, 0x70, 0x75, 0x76, 0x7f, 0x7c, 0x79, 0x7a }, + /*e*/ { 0x3b, 0x38, 0x3d, 0x3e, 0x37, 0x34, 0x31, 0x32, 0x23, 0x20, 0x25, 0x26, 0x2f, 0x2c, 0x29, 0x2a }, + /*f*/ { 0x0b, 0x08, 0x0d, 0x0e, 0x07, 0x04, 0x01, 0x02, 0x13, 0x10, 0x15, 0x16, 0x1f, 0x1c, 0x19, 0x1a }, +}; + +static uint8_t oaes_gf_mul_9[16][16] = { + // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f, + /*0*/ { 0x00, 0x09, 0x12, 0x1b, 0x24, 0x2d, 0x36, 0x3f, 0x48, 0x41, 0x5a, 0x53, 0x6c, 0x65, 0x7e, 0x77 }, + /*1*/ { 0x90, 0x99, 0x82, 0x8b, 0xb4, 0xbd, 0xa6, 0xaf, 0xd8, 0xd1, 0xca, 0xc3, 0xfc, 0xf5, 0xee, 0xe7 }, + /*2*/ { 0x3b, 0x32, 0x29, 0x20, 0x1f, 0x16, 0x0d, 0x04, 0x73, 0x7a, 0x61, 0x68, 0x57, 0x5e, 0x45, 0x4c }, + /*3*/ { 0xab, 0xa2, 0xb9, 0xb0, 0x8f, 0x86, 0x9d, 0x94, 0xe3, 0xea, 0xf1, 0xf8, 0xc7, 0xce, 0xd5, 0xdc }, + /*4*/ { 0x76, 0x7f, 0x64, 0x6d, 0x52, 0x5b, 0x40, 0x49, 0x3e, 0x37, 0x2c, 0x25, 0x1a, 0x13, 0x08, 0x01 }, + /*5*/ { 0xe6, 0xef, 0xf4, 0xfd, 0xc2, 0xcb, 0xd0, 0xd9, 0xae, 0xa7, 0xbc, 0xb5, 0x8a, 0x83, 0x98, 0x91 }, + /*6*/ { 0x4d, 0x44, 0x5f, 0x56, 0x69, 0x60, 0x7b, 0x72, 0x05, 0x0c, 0x17, 0x1e, 0x21, 0x28, 0x33, 0x3a }, + /*7*/ { 0xdd, 0xd4, 0xcf, 0xc6, 0xf9, 0xf0, 0xeb, 0xe2, 0x95, 0x9c, 0x87, 0x8e, 0xb1, 0xb8, 0xa3, 0xaa }, + /*8*/ { 0xec, 0xe5, 0xfe, 0xf7, 0xc8, 0xc1, 0xda, 0xd3, 0xa4, 0xad, 0xb6, 0xbf, 0x80, 0x89, 0x92, 0x9b }, + /*9*/ { 0x7c, 0x75, 0x6e, 0x67, 0x58, 0x51, 0x4a, 0x43, 0x34, 0x3d, 0x26, 0x2f, 0x10, 0x19, 0x02, 0x0b }, + /*a*/ { 0xd7, 0xde, 0xc5, 0xcc, 0xf3, 0xfa, 0xe1, 0xe8, 0x9f, 0x96, 0x8d, 0x84, 0xbb, 0xb2, 0xa9, 0xa0 }, + /*b*/ { 0x47, 0x4e, 0x55, 0x5c, 0x63, 0x6a, 0x71, 0x78, 0x0f, 0x06, 0x1d, 0x14, 0x2b, 0x22, 0x39, 0x30 }, + /*c*/ { 0x9a, 0x93, 0x88, 0x81, 0xbe, 0xb7, 0xac, 0xa5, 0xd2, 0xdb, 0xc0, 0xc9, 0xf6, 0xff, 0xe4, 0xed }, + /*d*/ { 0x0a, 0x03, 0x18, 0x11, 0x2e, 0x27, 0x3c, 0x35, 0x42, 0x4b, 0x50, 0x59, 0x66, 0x6f, 0x74, 0x7d }, + /*e*/ { 0xa1, 0xa8, 0xb3, 0xba, 0x85, 0x8c, 0x97, 0x9e, 0xe9, 0xe0, 0xfb, 0xf2, 0xcd, 0xc4, 0xdf, 0xd6 }, + /*f*/ { 0x31, 0x38, 0x23, 0x2a, 0x15, 0x1c, 0x07, 0x0e, 0x79, 0x70, 0x6b, 0x62, 0x5d, 0x54, 0x4f, 0x46 }, +}; + +static uint8_t oaes_gf_mul_b[16][16] = { + // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f, + /*0*/ { 0x00, 0x0b, 0x16, 0x1d, 0x2c, 0x27, 0x3a, 0x31, 0x58, 0x53, 0x4e, 0x45, 0x74, 0x7f, 0x62, 0x69 }, + /*1*/ { 0xb0, 0xbb, 0xa6, 0xad, 0x9c, 0x97, 0x8a, 0x81, 0xe8, 0xe3, 0xfe, 0xf5, 0xc4, 0xcf, 0xd2, 0xd9 }, + /*2*/ { 0x7b, 0x70, 0x6d, 0x66, 0x57, 0x5c, 0x41, 0x4a, 0x23, 0x28, 0x35, 0x3e, 0x0f, 0x04, 0x19, 0x12 }, + /*3*/ { 0xcb, 0xc0, 0xdd, 0xd6, 0xe7, 0xec, 0xf1, 0xfa, 0x93, 0x98, 0x85, 0x8e, 0xbf, 0xb4, 0xa9, 0xa2 }, + /*4*/ { 0xf6, 0xfd, 0xe0, 0xeb, 0xda, 0xd1, 0xcc, 0xc7, 0xae, 0xa5, 0xb8, 0xb3, 0x82, 0x89, 0x94, 0x9f }, + /*5*/ { 0x46, 0x4d, 0x50, 0x5b, 0x6a, 0x61, 0x7c, 0x77, 0x1e, 0x15, 0x08, 0x03, 0x32, 0x39, 0x24, 0x2f }, + /*6*/ { 0x8d, 0x86, 0x9b, 0x90, 0xa1, 0xaa, 0xb7, 0xbc, 0xd5, 0xde, 0xc3, 0xc8, 0xf9, 0xf2, 0xef, 0xe4 }, + /*7*/ { 0x3d, 0x36, 0x2b, 0x20, 0x11, 0x1a, 0x07, 0x0c, 0x65, 0x6e, 0x73, 0x78, 0x49, 0x42, 0x5f, 0x54 }, + /*8*/ { 0xf7, 0xfc, 0xe1, 0xea, 0xdb, 0xd0, 0xcd, 0xc6, 0xaf, 0xa4, 0xb9, 0xb2, 0x83, 0x88, 0x95, 0x9e }, + /*9*/ { 0x47, 0x4c, 0x51, 0x5a, 0x6b, 0x60, 0x7d, 0x76, 0x1f, 0x14, 0x09, 0x02, 0x33, 0x38, 0x25, 0x2e }, + /*a*/ { 0x8c, 0x87, 0x9a, 0x91, 0xa0, 0xab, 0xb6, 0xbd, 0xd4, 0xdf, 0xc2, 0xc9, 0xf8, 0xf3, 0xee, 0xe5 }, + /*b*/ { 0x3c, 0x37, 0x2a, 0x21, 0x10, 0x1b, 0x06, 0x0d, 0x64, 0x6f, 0x72, 0x79, 0x48, 0x43, 0x5e, 0x55 }, + /*c*/ { 0x01, 0x0a, 0x17, 0x1c, 0x2d, 0x26, 0x3b, 0x30, 0x59, 0x52, 0x4f, 0x44, 0x75, 0x7e, 0x63, 0x68 }, + /*d*/ { 0xb1, 0xba, 0xa7, 0xac, 0x9d, 0x96, 0x8b, 0x80, 0xe9, 0xe2, 0xff, 0xf4, 0xc5, 0xce, 0xd3, 0xd8 }, + /*e*/ { 0x7a, 0x71, 0x6c, 0x67, 0x56, 0x5d, 0x40, 0x4b, 0x22, 0x29, 0x34, 0x3f, 0x0e, 0x05, 0x18, 0x13 }, + /*f*/ { 0xca, 0xc1, 0xdc, 0xd7, 0xe6, 0xed, 0xf0, 0xfb, 0x92, 0x99, 0x84, 0x8f, 0xbe, 0xb5, 0xa8, 0xa3 }, +}; + +static uint8_t oaes_gf_mul_d[16][16] = { + // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f, + /*0*/ { 0x00, 0x0d, 0x1a, 0x17, 0x34, 0x39, 0x2e, 0x23, 0x68, 0x65, 0x72, 0x7f, 0x5c, 0x51, 0x46, 0x4b }, + /*1*/ { 0xd0, 0xdd, 0xca, 0xc7, 0xe4, 0xe9, 0xfe, 0xf3, 0xb8, 0xb5, 0xa2, 0xaf, 0x8c, 0x81, 0x96, 0x9b }, + /*2*/ { 0xbb, 0xb6, 0xa1, 0xac, 0x8f, 0x82, 0x95, 0x98, 0xd3, 0xde, 0xc9, 0xc4, 0xe7, 0xea, 0xfd, 0xf0 }, + /*3*/ { 0x6b, 0x66, 0x71, 0x7c, 0x5f, 0x52, 0x45, 0x48, 0x03, 0x0e, 0x19, 0x14, 0x37, 0x3a, 0x2d, 0x20 }, + /*4*/ { 0x6d, 0x60, 0x77, 0x7a, 0x59, 0x54, 0x43, 0x4e, 0x05, 0x08, 0x1f, 0x12, 0x31, 0x3c, 0x2b, 0x26 }, + /*5*/ { 0xbd, 0xb0, 0xa7, 0xaa, 0x89, 0x84, 0x93, 0x9e, 0xd5, 0xd8, 0xcf, 0xc2, 0xe1, 0xec, 0xfb, 0xf6 }, + /*6*/ { 0xd6, 0xdb, 0xcc, 0xc1, 0xe2, 0xef, 0xf8, 0xf5, 0xbe, 0xb3, 0xa4, 0xa9, 0x8a, 0x87, 0x90, 0x9d }, + /*7*/ { 0x06, 0x0b, 0x1c, 0x11, 0x32, 0x3f, 0x28, 0x25, 0x6e, 0x63, 0x74, 0x79, 0x5a, 0x57, 0x40, 0x4d }, + /*8*/ { 0xda, 0xd7, 0xc0, 0xcd, 0xee, 0xe3, 0xf4, 0xf9, 0xb2, 0xbf, 0xa8, 0xa5, 0x86, 0x8b, 0x9c, 0x91 }, + /*9*/ { 0x0a, 0x07, 0x10, 0x1d, 0x3e, 0x33, 0x24, 0x29, 0x62, 0x6f, 0x78, 0x75, 0x56, 0x5b, 0x4c, 0x41 }, + /*a*/ { 0x61, 0x6c, 0x7b, 0x76, 0x55, 0x58, 0x4f, 0x42, 0x09, 0x04, 0x13, 0x1e, 0x3d, 0x30, 0x27, 0x2a }, + /*b*/ { 0xb1, 0xbc, 0xab, 0xa6, 0x85, 0x88, 0x9f, 0x92, 0xd9, 0xd4, 0xc3, 0xce, 0xed, 0xe0, 0xf7, 0xfa }, + /*c*/ { 0xb7, 0xba, 0xad, 0xa0, 0x83, 0x8e, 0x99, 0x94, 0xdf, 0xd2, 0xc5, 0xc8, 0xeb, 0xe6, 0xf1, 0xfc }, + /*d*/ { 0x67, 0x6a, 0x7d, 0x70, 0x53, 0x5e, 0x49, 0x44, 0x0f, 0x02, 0x15, 0x18, 0x3b, 0x36, 0x21, 0x2c }, + /*e*/ { 0x0c, 0x01, 0x16, 0x1b, 0x38, 0x35, 0x22, 0x2f, 0x64, 0x69, 0x7e, 0x73, 0x50, 0x5d, 0x4a, 0x47 }, + /*f*/ { 0xdc, 0xd1, 0xc6, 0xcb, 0xe8, 0xe5, 0xf2, 0xff, 0xb4, 0xb9, 0xae, 0xa3, 0x80, 0x8d, 0x9a, 0x97 }, +}; + +static uint8_t oaes_gf_mul_e[16][16] = { + // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f, + /*0*/ { 0x00, 0x0e, 0x1c, 0x12, 0x38, 0x36, 0x24, 0x2a, 0x70, 0x7e, 0x6c, 0x62, 0x48, 0x46, 0x54, 0x5a }, + /*1*/ { 0xe0, 0xee, 0xfc, 0xf2, 0xd8, 0xd6, 0xc4, 0xca, 0x90, 0x9e, 0x8c, 0x82, 0xa8, 0xa6, 0xb4, 0xba }, + /*2*/ { 0xdb, 0xd5, 0xc7, 0xc9, 0xe3, 0xed, 0xff, 0xf1, 0xab, 0xa5, 0xb7, 0xb9, 0x93, 0x9d, 0x8f, 0x81 }, + /*3*/ { 0x3b, 0x35, 0x27, 0x29, 0x03, 0x0d, 0x1f, 0x11, 0x4b, 0x45, 0x57, 0x59, 0x73, 0x7d, 0x6f, 0x61 }, + /*4*/ { 0xad, 0xa3, 0xb1, 0xbf, 0x95, 0x9b, 0x89, 0x87, 0xdd, 0xd3, 0xc1, 0xcf, 0xe5, 0xeb, 0xf9, 0xf7 }, + /*5*/ { 0x4d, 0x43, 0x51, 0x5f, 0x75, 0x7b, 0x69, 0x67, 0x3d, 0x33, 0x21, 0x2f, 0x05, 0x0b, 0x19, 0x17 }, + /*6*/ { 0x76, 0x78, 0x6a, 0x64, 0x4e, 0x40, 0x52, 0x5c, 0x06, 0x08, 0x1a, 0x14, 0x3e, 0x30, 0x22, 0x2c }, + /*7*/ { 0x96, 0x98, 0x8a, 0x84, 0xae, 0xa0, 0xb2, 0xbc, 0xe6, 0xe8, 0xfa, 0xf4, 0xde, 0xd0, 0xc2, 0xcc }, + /*8*/ { 0x41, 0x4f, 0x5d, 0x53, 0x79, 0x77, 0x65, 0x6b, 0x31, 0x3f, 0x2d, 0x23, 0x09, 0x07, 0x15, 0x1b }, + /*9*/ { 0xa1, 0xaf, 0xbd, 0xb3, 0x99, 0x97, 0x85, 0x8b, 0xd1, 0xdf, 0xcd, 0xc3, 0xe9, 0xe7, 0xf5, 0xfb }, + /*a*/ { 0x9a, 0x94, 0x86, 0x88, 0xa2, 0xac, 0xbe, 0xb0, 0xea, 0xe4, 0xf6, 0xf8, 0xd2, 0xdc, 0xce, 0xc0 }, + /*b*/ { 0x7a, 0x74, 0x66, 0x68, 0x42, 0x4c, 0x5e, 0x50, 0x0a, 0x04, 0x16, 0x18, 0x32, 0x3c, 0x2e, 0x20 }, + /*c*/ { 0xec, 0xe2, 0xf0, 0xfe, 0xd4, 0xda, 0xc8, 0xc6, 0x9c, 0x92, 0x80, 0x8e, 0xa4, 0xaa, 0xb8, 0xb6 }, + /*d*/ { 0x0c, 0x02, 0x10, 0x1e, 0x34, 0x3a, 0x28, 0x26, 0x7c, 0x72, 0x60, 0x6e, 0x44, 0x4a, 0x58, 0x56 }, + /*e*/ { 0x37, 0x39, 0x2b, 0x25, 0x0f, 0x01, 0x13, 0x1d, 0x47, 0x49, 0x5b, 0x55, 0x7f, 0x71, 0x63, 0x6d }, + /*f*/ { 0xd7, 0xd9, 0xcb, 0xc5, 0xef, 0xe1, 0xf3, 0xfd, 0xa7, 0xa9, 0xbb, 0xb5, 0x9f, 0x91, 0x83, 0x8d }, +}; + +static OAES_RET oaes_sub_byte( uint8_t * byte ) +{ + size_t _x, _y; + + if( NULL == byte ) + return OAES_RET_ARG1; + + _x = _y = *byte; + _x &= 0x0f; + _y &= 0xf0; + _y >>= 4; + *byte = oaes_sub_byte_value[_y][_x]; + + return OAES_RET_SUCCESS; +} + +static OAES_RET oaes_inv_sub_byte( uint8_t * byte ) +{ + size_t _x, _y; + + if( NULL == byte ) + return OAES_RET_ARG1; + + _x = _y = *byte; + _x &= 0x0f; + _y &= 0xf0; + _y >>= 4; + *byte = oaes_inv_sub_byte_value[_y][_x]; + + return OAES_RET_SUCCESS; +} +/* +static OAES_RET oaes_word_rot_right( uint8_t word[OAES_COL_LEN] ) +{ + uint8_t _temp[OAES_COL_LEN]; + + if( NULL == word ) + return OAES_RET_ARG1; + + memcpy( _temp + 1, word, OAES_COL_LEN - 1 ); + _temp[0] = word[OAES_COL_LEN - 1]; + memcpy( word, _temp, OAES_COL_LEN ); + + return OAES_RET_SUCCESS; +} +*/ +static OAES_RET oaes_word_rot_left( uint8_t word[OAES_COL_LEN] ) +{ + uint8_t _temp[OAES_COL_LEN]; + + if( NULL == word ) + return OAES_RET_ARG1; + + memcpy( _temp, word + 1, OAES_COL_LEN - 1 ); + _temp[OAES_COL_LEN - 1] = word[0]; + memcpy( word, _temp, OAES_COL_LEN ); + + return OAES_RET_SUCCESS; +} + +static OAES_RET oaes_shift_rows( uint8_t block[OAES_BLOCK_SIZE] ) +{ + uint8_t _temp[OAES_BLOCK_SIZE]; + + if( NULL == block ) + return OAES_RET_ARG1; + + _temp[0x00] = block[0x00]; + _temp[0x01] = block[0x05]; + _temp[0x02] = block[0x0a]; + _temp[0x03] = block[0x0f]; + _temp[0x04] = block[0x04]; + _temp[0x05] = block[0x09]; + _temp[0x06] = block[0x0e]; + _temp[0x07] = block[0x03]; + _temp[0x08] = block[0x08]; + _temp[0x09] = block[0x0d]; + _temp[0x0a] = block[0x02]; + _temp[0x0b] = block[0x07]; + _temp[0x0c] = block[0x0c]; + _temp[0x0d] = block[0x01]; + _temp[0x0e] = block[0x06]; + _temp[0x0f] = block[0x0b]; + memcpy( block, _temp, OAES_BLOCK_SIZE ); + + return OAES_RET_SUCCESS; +} + +static OAES_RET oaes_inv_shift_rows( uint8_t block[OAES_BLOCK_SIZE] ) +{ + uint8_t _temp[OAES_BLOCK_SIZE]; + + if( NULL == block ) + return OAES_RET_ARG1; + + _temp[0x00] = block[0x00]; + _temp[0x01] = block[0x0d]; + _temp[0x02] = block[0x0a]; + _temp[0x03] = block[0x07]; + _temp[0x04] = block[0x04]; + _temp[0x05] = block[0x01]; + _temp[0x06] = block[0x0e]; + _temp[0x07] = block[0x0b]; + _temp[0x08] = block[0x08]; + _temp[0x09] = block[0x05]; + _temp[0x0a] = block[0x02]; + _temp[0x0b] = block[0x0f]; + _temp[0x0c] = block[0x0c]; + _temp[0x0d] = block[0x09]; + _temp[0x0e] = block[0x06]; + _temp[0x0f] = block[0x03]; + memcpy( block, _temp, OAES_BLOCK_SIZE ); + + return OAES_RET_SUCCESS; +} + +static uint8_t oaes_gf_mul(uint8_t left, uint8_t right) +{ + size_t _x, _y; + + _x = _y = left; + _x &= 0x0f; + _y &= 0xf0; + _y >>= 4; + + switch( right ) + { + case 0x02: + return oaes_gf_mul_2[_y][_x]; + break; + case 0x03: + return oaes_gf_mul_3[_y][_x]; + break; + case 0x09: + return oaes_gf_mul_9[_y][_x]; + break; + case 0x0b: + return oaes_gf_mul_b[_y][_x]; + break; + case 0x0d: + return oaes_gf_mul_d[_y][_x]; + break; + case 0x0e: + return oaes_gf_mul_e[_y][_x]; + break; + default: + return left; + break; + } +} + +static OAES_RET oaes_mix_cols( uint8_t word[OAES_COL_LEN] ) +{ + uint8_t _temp[OAES_COL_LEN]; + + if( NULL == word ) + return OAES_RET_ARG1; + + _temp[0] = oaes_gf_mul(word[0], 0x02) ^ oaes_gf_mul( word[1], 0x03 ) ^ + word[2] ^ word[3]; + _temp[1] = word[0] ^ oaes_gf_mul( word[1], 0x02 ) ^ + oaes_gf_mul( word[2], 0x03 ) ^ word[3]; + _temp[2] = word[0] ^ word[1] ^ + oaes_gf_mul( word[2], 0x02 ) ^ oaes_gf_mul( word[3], 0x03 ); + _temp[3] = oaes_gf_mul( word[0], 0x03 ) ^ word[1] ^ + word[2] ^ oaes_gf_mul( word[3], 0x02 ); + memcpy( word, _temp, OAES_COL_LEN ); + + return OAES_RET_SUCCESS; +} + +static OAES_RET oaes_inv_mix_cols( uint8_t word[OAES_COL_LEN] ) +{ + uint8_t _temp[OAES_COL_LEN]; + + if( NULL == word ) + return OAES_RET_ARG1; + + _temp[0] = oaes_gf_mul( word[0], 0x0e ) ^ oaes_gf_mul( word[1], 0x0b ) ^ + oaes_gf_mul( word[2], 0x0d ) ^ oaes_gf_mul( word[3], 0x09 ); + _temp[1] = oaes_gf_mul( word[0], 0x09 ) ^ oaes_gf_mul( word[1], 0x0e ) ^ + oaes_gf_mul( word[2], 0x0b ) ^ oaes_gf_mul( word[3], 0x0d ); + _temp[2] = oaes_gf_mul( word[0], 0x0d ) ^ oaes_gf_mul( word[1], 0x09 ) ^ + oaes_gf_mul( word[2], 0x0e ) ^ oaes_gf_mul( word[3], 0x0b ); + _temp[3] = oaes_gf_mul( word[0], 0x0b ) ^ oaes_gf_mul( word[1], 0x0d ) ^ + oaes_gf_mul( word[2], 0x09 ) ^ oaes_gf_mul( word[3], 0x0e ); + memcpy( word, _temp, OAES_COL_LEN ); + + return OAES_RET_SUCCESS; +} + +OAES_RET oaes_sprintf( + char * buf, size_t * buf_len, const uint8_t * data, size_t data_len ) +{ + size_t _i, _buf_len_in; + char _temp[4]; + + if( NULL == buf_len ) + return OAES_RET_ARG2; + + _buf_len_in = *buf_len; + *buf_len = data_len * 3 + data_len / OAES_BLOCK_SIZE + 1; + + if( NULL == buf ) + return OAES_RET_SUCCESS; + + if( *buf_len > _buf_len_in ) + return OAES_RET_BUF; + + if( NULL == data ) + return OAES_RET_ARG3; + + strcpy( buf, "" ); + + for( _i = 0; _i < data_len; _i++ ) + { + sprintf( _temp, "%02x ", data[_i] ); + strcat( buf, _temp ); + if( _i && 0 == ( _i + 1 ) % OAES_BLOCK_SIZE ) + strcat( buf, "\n" ); + } + + return OAES_RET_SUCCESS; +} + +#ifdef OAES_HAVE_ISAAC +static void oaes_get_seed( char buf[RANDSIZ + 1] ) +{ + struct timeb timer; + struct tm *gmTimer; + char * _test = NULL; + + ftime (&timer); + gmTimer = gmtime( &timer.time ); + _test = (char *) calloc( sizeof( char ), timer.millitm ); + sprintf( buf, "%04d%02d%02d%02d%02d%02d%03d%p%d", + gmTimer->tm_year + 1900, gmTimer->tm_mon + 1, gmTimer->tm_mday, + gmTimer->tm_hour, gmTimer->tm_min, gmTimer->tm_sec, timer.millitm, + _test + timer.millitm, getpid() ); + + if( _test ) + free( _test ); +} +#else +static uint32_t oaes_get_seed(void) +{ + struct timeb timer; + struct tm *gmTimer; + char * _test = NULL; + uint32_t _ret = 0; + + ftime (&timer); + gmTimer = gmtime( &timer.time ); + _test = (char *) calloc( sizeof( char ), timer.millitm ); + _ret = gmTimer->tm_year + 1900 + gmTimer->tm_mon + 1 + gmTimer->tm_mday + + gmTimer->tm_hour + gmTimer->tm_min + gmTimer->tm_sec + timer.millitm + + (uintptr_t) ( _test + timer.millitm ) + getpid(); + + if( _test ) + free( _test ); + + return _ret; +} +#endif // OAES_HAVE_ISAAC + +static OAES_RET oaes_key_destroy( oaes_key ** key ) +{ + if( NULL == *key ) + return OAES_RET_SUCCESS; + + if( (*key)->data ) + { + free( (*key)->data ); + (*key)->data = NULL; + } + + if( (*key)->exp_data ) + { + free( (*key)->exp_data ); + (*key)->exp_data = NULL; + } + + (*key)->data_len = 0; + (*key)->exp_data_len = 0; + (*key)->num_keys = 0; + (*key)->key_base = 0; + free( *key ); + *key = NULL; + + return OAES_RET_SUCCESS; +} + +static OAES_RET oaes_key_expand( OAES_CTX * ctx ) +{ + size_t _i, _j; + oaes_ctx * _ctx = (oaes_ctx *) ctx; + + if( NULL == _ctx ) + return OAES_RET_ARG1; + + if( NULL == _ctx->key ) + return OAES_RET_NOKEY; + + _ctx->key->key_base = _ctx->key->data_len / OAES_RKEY_LEN; + _ctx->key->num_keys = _ctx->key->key_base + OAES_ROUND_BASE; + + _ctx->key->exp_data_len = _ctx->key->num_keys * OAES_RKEY_LEN * OAES_COL_LEN; + _ctx->key->exp_data = (uint8_t *) + calloc( _ctx->key->exp_data_len, sizeof( uint8_t )); + + if( NULL == _ctx->key->exp_data ) + return OAES_RET_MEM; + + // the first _ctx->key->data_len are a direct copy + memcpy( _ctx->key->exp_data, _ctx->key->data, _ctx->key->data_len ); + + // apply ExpandKey algorithm for remainder + for( _i = _ctx->key->key_base; _i < _ctx->key->num_keys * OAES_RKEY_LEN; _i++ ) + { + uint8_t _temp[OAES_COL_LEN]; + + memcpy( _temp, + _ctx->key->exp_data + ( _i - 1 ) * OAES_RKEY_LEN, OAES_COL_LEN ); + + // transform key column + if( 0 == _i % _ctx->key->key_base ) + { + oaes_word_rot_left( _temp ); + + for( _j = 0; _j < OAES_COL_LEN; _j++ ) + oaes_sub_byte( _temp + _j ); + + _temp[0] = _temp[0] ^ oaes_gf_8[ _i / _ctx->key->key_base - 1 ]; + } + else if( _ctx->key->key_base > 6 && 4 == _i % _ctx->key->key_base ) + { + for( _j = 0; _j < OAES_COL_LEN; _j++ ) + oaes_sub_byte( _temp + _j ); + } + + for( _j = 0; _j < OAES_COL_LEN; _j++ ) + { + _ctx->key->exp_data[ _i * OAES_RKEY_LEN + _j ] = + _ctx->key->exp_data[ ( _i - _ctx->key->key_base ) * + OAES_RKEY_LEN + _j ] ^ _temp[_j]; + } + } + + return OAES_RET_SUCCESS; +} + +static OAES_RET oaes_key_gen( OAES_CTX * ctx, size_t key_size ) +{ + size_t _i; + oaes_key * _key = NULL; + oaes_ctx * _ctx = (oaes_ctx *) ctx; + OAES_RET _rc = OAES_RET_SUCCESS; + + if( NULL == _ctx ) + return OAES_RET_ARG1; + + _key = (oaes_key *) calloc( sizeof( oaes_key ), 1 ); + + if( NULL == _key ) + return OAES_RET_MEM; + + if( _ctx->key ) + oaes_key_destroy( &(_ctx->key) ); + + _key->data_len = key_size; + _key->data = (uint8_t *) calloc( key_size, sizeof( uint8_t )); + + if( NULL == _key->data ) + return OAES_RET_MEM; + + for( _i = 0; _i < key_size; _i++ ) +#ifdef OAES_HAVE_ISAAC + _key->data[_i] = (uint8_t) rand( _ctx->rctx ); +#else + _key->data[_i] = (uint8_t) rand(); +#endif // OAES_HAVE_ISAAC + + _ctx->key = _key; + _rc = _rc || oaes_key_expand( ctx ); + + if( _rc != OAES_RET_SUCCESS ) + { + oaes_key_destroy( &(_ctx->key) ); + return _rc; + } + + return OAES_RET_SUCCESS; +} + +OAES_RET oaes_key_gen_128( OAES_CTX * ctx ) +{ + return oaes_key_gen( ctx, 16 ); +} + +OAES_RET oaes_key_gen_192( OAES_CTX * ctx ) +{ + return oaes_key_gen( ctx, 24 ); +} + +OAES_RET oaes_key_gen_256( OAES_CTX * ctx ) +{ + return oaes_key_gen( ctx, 32 ); +} + +OAES_RET oaes_key_export( OAES_CTX * ctx, + uint8_t * data, size_t * data_len ) +{ + size_t _data_len_in; + oaes_ctx * _ctx = (oaes_ctx *) ctx; + + if( NULL == _ctx ) + return OAES_RET_ARG1; + + if( NULL == _ctx->key ) + return OAES_RET_NOKEY; + + if( NULL == data_len ) + return OAES_RET_ARG3; + + _data_len_in = *data_len; + // data + header + *data_len = _ctx->key->data_len + OAES_BLOCK_SIZE; + + if( NULL == data ) + return OAES_RET_SUCCESS; + + if( _data_len_in < *data_len ) + return OAES_RET_BUF; + + // header + memcpy( data, oaes_header, OAES_BLOCK_SIZE ); + data[5] = 0x01; + data[7] = _ctx->key->data_len; + memcpy( data + OAES_BLOCK_SIZE, _ctx->key->data, _ctx->key->data_len ); + + return OAES_RET_SUCCESS; +} + +OAES_RET oaes_key_export_data( OAES_CTX * ctx, + uint8_t * data, size_t * data_len ) +{ + size_t _data_len_in; + oaes_ctx * _ctx = (oaes_ctx *) ctx; + + if( NULL == _ctx ) + return OAES_RET_ARG1; + + if( NULL == _ctx->key ) + return OAES_RET_NOKEY; + + if( NULL == data_len ) + return OAES_RET_ARG3; + + _data_len_in = *data_len; + *data_len = _ctx->key->data_len; + + if( NULL == data ) + return OAES_RET_SUCCESS; + + if( _data_len_in < *data_len ) + return OAES_RET_BUF; + + memcpy( data, _ctx->key->data, *data_len ); + + return OAES_RET_SUCCESS; +} + +OAES_RET oaes_key_import( OAES_CTX * ctx, + const uint8_t * data, size_t data_len ) +{ + oaes_ctx * _ctx = (oaes_ctx *) ctx; + OAES_RET _rc = OAES_RET_SUCCESS; + int _key_length; + + if( NULL == _ctx ) + return OAES_RET_ARG1; + + if( NULL == data ) + return OAES_RET_ARG2; + + switch( data_len ) + { + case 16 + OAES_BLOCK_SIZE: + case 24 + OAES_BLOCK_SIZE: + case 32 + OAES_BLOCK_SIZE: + break; + default: + return OAES_RET_ARG3; + } + + // header + if( 0 != memcmp( data, oaes_header, 4 ) ) + return OAES_RET_HEADER; + + // header version + switch( data[4] ) + { + case 0x01: + break; + default: + return OAES_RET_HEADER; + } + + // header type + switch( data[5] ) + { + case 0x01: + break; + default: + return OAES_RET_HEADER; + } + + // options + _key_length = data[7]; + switch( _key_length ) + { + case 16: + case 24: + case 32: + break; + default: + return OAES_RET_HEADER; + } + + if( (int)data_len != _key_length + OAES_BLOCK_SIZE ) + return OAES_RET_ARG3; + + if( _ctx->key ) + oaes_key_destroy( &(_ctx->key) ); + + _ctx->key = (oaes_key *) calloc( sizeof( oaes_key ), 1 ); + + if( NULL == _ctx->key ) + return OAES_RET_MEM; + + _ctx->key->data_len = _key_length; + _ctx->key->data = (uint8_t *) + calloc( _key_length, sizeof( uint8_t )); + + if( NULL == _ctx->key->data ) + { + oaes_key_destroy( &(_ctx->key) ); + return OAES_RET_MEM; + } + + memcpy( _ctx->key->data, data + OAES_BLOCK_SIZE, _key_length ); + _rc = _rc || oaes_key_expand( ctx ); + + if( _rc != OAES_RET_SUCCESS ) + { + oaes_key_destroy( &(_ctx->key) ); + return _rc; + } + + return OAES_RET_SUCCESS; +} + +OAES_RET oaes_key_import_data( OAES_CTX * ctx, + const uint8_t * data, size_t data_len ) +{ + oaes_ctx * _ctx = (oaes_ctx *) ctx; + OAES_RET _rc = OAES_RET_SUCCESS; + + if( NULL == _ctx ) + return OAES_RET_ARG1; + + if( NULL == data ) + return OAES_RET_ARG2; + + switch( data_len ) + { + case 16: + case 24: + case 32: + break; + default: + return OAES_RET_ARG3; + } + + if( _ctx->key ) + oaes_key_destroy( &(_ctx->key) ); + + _ctx->key = (oaes_key *) calloc( sizeof( oaes_key ), 1 ); + + if( NULL == _ctx->key ) + return OAES_RET_MEM; + + _ctx->key->data_len = data_len; + _ctx->key->data = (uint8_t *) + calloc( data_len, sizeof( uint8_t )); + + if( NULL == _ctx->key->data ) + { + oaes_key_destroy( &(_ctx->key) ); + return OAES_RET_MEM; + } + + memcpy( _ctx->key->data, data, data_len ); + _rc = _rc || oaes_key_expand( ctx ); + + if( _rc != OAES_RET_SUCCESS ) + { + oaes_key_destroy( &(_ctx->key) ); + return _rc; + } + + return OAES_RET_SUCCESS; +} + +OAES_CTX * oaes_alloc(void) +{ + oaes_ctx * _ctx = (oaes_ctx *) calloc( sizeof( oaes_ctx ), 1 ); + + if( NULL == _ctx ) + return NULL; + +#ifdef OAES_HAVE_ISAAC + { + ub4 _i = 0; + char _seed[RANDSIZ + 1]; + + _ctx->rctx = (randctx *) calloc( sizeof( randctx ), 1 ); + + if( NULL == _ctx->rctx ) + { + free( _ctx ); + return NULL; + } + + oaes_get_seed( _seed ); + memset( _ctx->rctx->randrsl, 0, RANDSIZ ); + memcpy( _ctx->rctx->randrsl, _seed, RANDSIZ ); + randinit( _ctx->rctx, TRUE); + } +#else + srand( oaes_get_seed() ); +#endif // OAES_HAVE_ISAAC + + _ctx->key = NULL; + oaes_set_option( _ctx, OAES_OPTION_CBC, NULL ); + +#ifdef OAES_DEBUG + _ctx->step_cb = NULL; + oaes_set_option( _ctx, OAES_OPTION_STEP_OFF, NULL ); +#endif // OAES_DEBUG + + return (OAES_CTX *) _ctx; +} + +OAES_RET oaes_free( OAES_CTX ** ctx ) +{ + oaes_ctx ** _ctx = (oaes_ctx **) ctx; + + if( NULL == _ctx ) + return OAES_RET_ARG1; + + if( NULL == *_ctx ) + return OAES_RET_SUCCESS; + + if( (*_ctx)->key ) + oaes_key_destroy( &((*_ctx)->key) ); + +#ifdef OAES_HAVE_ISAAC + if( (*_ctx)->rctx ) + { + free( (*_ctx)->rctx ); + (*_ctx)->rctx = NULL; + } +#endif // OAES_HAVE_ISAAC + + free( *_ctx ); + *_ctx = NULL; + + return OAES_RET_SUCCESS; +} + +OAES_RET oaes_set_option( OAES_CTX * ctx, + OAES_OPTION option, const void * value ) +{ + size_t _i; + oaes_ctx * _ctx = (oaes_ctx *) ctx; + + if( NULL == _ctx ) + return OAES_RET_ARG1; + + switch( option ) + { + case OAES_OPTION_ECB: + _ctx->options &= ~OAES_OPTION_CBC; + memset( _ctx->iv, 0, OAES_BLOCK_SIZE ); + break; + + case OAES_OPTION_CBC: + _ctx->options &= ~OAES_OPTION_ECB; + if( value ) + memcpy( _ctx->iv, value, OAES_BLOCK_SIZE ); + else + { + for( _i = 0; _i < OAES_BLOCK_SIZE; _i++ ) +#ifdef OAES_HAVE_ISAAC + _ctx->iv[_i] = (uint8_t) rand( _ctx->rctx ); +#else + _ctx->iv[_i] = (uint8_t) rand(); +#endif // OAES_HAVE_ISAAC + } + break; + +#ifdef OAES_DEBUG + + case OAES_OPTION_STEP_ON: + if( value ) + { + _ctx->options &= ~OAES_OPTION_STEP_OFF; + _ctx->step_cb = value; + } + else + { + _ctx->options &= ~OAES_OPTION_STEP_ON; + _ctx->options |= OAES_OPTION_STEP_OFF; + _ctx->step_cb = NULL; + return OAES_RET_ARG3; + } + break; + + case OAES_OPTION_STEP_OFF: + _ctx->options &= ~OAES_OPTION_STEP_ON; + _ctx->step_cb = NULL; + break; + +#endif // OAES_DEBUG + + default: + return OAES_RET_ARG2; + } + + _ctx->options |= option; + + return OAES_RET_SUCCESS; +} + +static OAES_RET oaes_encrypt_block( + OAES_CTX * ctx, uint8_t * c, size_t c_len ) +{ + size_t _i, _j; + oaes_ctx * _ctx = (oaes_ctx *) ctx; + + if( NULL == _ctx ) + return OAES_RET_ARG1; + + if( NULL == c ) + return OAES_RET_ARG2; + + if( c_len != OAES_BLOCK_SIZE ) + return OAES_RET_ARG3; + + if( NULL == _ctx->key ) + return OAES_RET_NOKEY; + +#ifdef OAES_DEBUG + if( _ctx->step_cb ) + _ctx->step_cb( c, "input", 1, NULL ); +#endif // OAES_DEBUG + + // AddRoundKey(State, K0) + for( _i = 0; _i < c_len; _i++ ) + c[_i] = c[_i] ^ _ctx->key->exp_data[_i]; + +#ifdef OAES_DEBUG + if( _ctx->step_cb ) + { + _ctx->step_cb( _ctx->key->exp_data, "k_sch", 1, NULL ); + _ctx->step_cb( c, "k_add", 1, NULL ); + } +#endif // OAES_DEBUG + + // for round = 1 step 1 to Nr–1 + for( _i = 1; _i < _ctx->key->num_keys - 1; _i++ ) + { + // SubBytes(state) + for( _j = 0; _j < c_len; _j++ ) + oaes_sub_byte( c + _j ); + +#ifdef OAES_DEBUG + if( _ctx->step_cb ) + _ctx->step_cb( c, "s_box", _i, NULL ); +#endif // OAES_DEBUG + + // ShiftRows(state) + oaes_shift_rows( c ); + +#ifdef OAES_DEBUG + if( _ctx->step_cb ) + _ctx->step_cb( c, "s_row", _i, NULL ); +#endif // OAES_DEBUG + + // MixColumns(state) + oaes_mix_cols( c ); + oaes_mix_cols( c + 4 ); + oaes_mix_cols( c + 8 ); + oaes_mix_cols( c + 12 ); + +#ifdef OAES_DEBUG + if( _ctx->step_cb ) + _ctx->step_cb( c, "m_col", _i, NULL ); +#endif // OAES_DEBUG + + // AddRoundKey(state, w[round*Nb, (round+1)*Nb-1]) + for( _j = 0; _j < c_len; _j++ ) + c[_j] = c[_j] ^ + _ctx->key->exp_data[_i * OAES_RKEY_LEN * OAES_COL_LEN + _j]; + +#ifdef OAES_DEBUG + if( _ctx->step_cb ) + { + _ctx->step_cb( _ctx->key->exp_data + _i * OAES_RKEY_LEN * OAES_COL_LEN, + "k_sch", _i, NULL ); + _ctx->step_cb( c, "k_add", _i, NULL ); + } +#endif // OAES_DEBUG + + } + + // SubBytes(state) + for( _i = 0; _i < c_len; _i++ ) + oaes_sub_byte( c + _i ); + +#ifdef OAES_DEBUG + if( _ctx->step_cb ) + _ctx->step_cb( c, "s_box", _ctx->key->num_keys - 1, NULL ); +#endif // OAES_DEBUG + + // ShiftRows(state) + oaes_shift_rows( c ); + +#ifdef OAES_DEBUG + if( _ctx->step_cb ) + _ctx->step_cb( c, "s_row", _ctx->key->num_keys - 1, NULL ); +#endif // OAES_DEBUG + + // AddRoundKey(state, w[Nr*Nb, (Nr+1)*Nb-1]) + for( _i = 0; _i < c_len; _i++ ) + c[_i] = c[_i] ^ _ctx->key->exp_data[ + ( _ctx->key->num_keys - 1 ) * OAES_RKEY_LEN * OAES_COL_LEN + _i ]; + +#ifdef OAES_DEBUG + if( _ctx->step_cb ) + { + _ctx->step_cb( _ctx->key->exp_data + + ( _ctx->key->num_keys - 1 ) * OAES_RKEY_LEN * OAES_COL_LEN, + "k_sch", _ctx->key->num_keys - 1, NULL ); + _ctx->step_cb( c, "output", _ctx->key->num_keys - 1, NULL ); + } +#endif // OAES_DEBUG + + return OAES_RET_SUCCESS; +} + +static OAES_RET oaes_decrypt_block( + OAES_CTX * ctx, uint8_t * c, size_t c_len ) +{ + size_t _i, _j; + oaes_ctx * _ctx = (oaes_ctx *) ctx; + + if( NULL == _ctx ) + return OAES_RET_ARG1; + + if( NULL == c ) + return OAES_RET_ARG2; + + if( c_len != OAES_BLOCK_SIZE ) + return OAES_RET_ARG3; + + if( NULL == _ctx->key ) + return OAES_RET_NOKEY; + +#ifdef OAES_DEBUG + if( _ctx->step_cb ) + _ctx->step_cb( c, "iinput", _ctx->key->num_keys - 1, NULL ); +#endif // OAES_DEBUG + + // AddRoundKey(state, w[Nr*Nb, (Nr+1)*Nb-1]) + for( _i = 0; _i < c_len; _i++ ) + c[_i] = c[_i] ^ _ctx->key->exp_data[ + ( _ctx->key->num_keys - 1 ) * OAES_RKEY_LEN * OAES_COL_LEN + _i ]; + +#ifdef OAES_DEBUG + if( _ctx->step_cb ) + { + _ctx->step_cb( _ctx->key->exp_data + + ( _ctx->key->num_keys - 1 ) * OAES_RKEY_LEN * OAES_COL_LEN, + "ik_sch", _ctx->key->num_keys - 1, NULL ); + _ctx->step_cb( c, "ik_add", _ctx->key->num_keys - 1, NULL ); + } +#endif // OAES_DEBUG + + for( _i = _ctx->key->num_keys - 2; _i > 0; _i-- ) + { + // InvShiftRows(state) + oaes_inv_shift_rows( c ); + +#ifdef OAES_DEBUG + if( _ctx->step_cb ) + _ctx->step_cb( c, "is_row", _i, NULL ); +#endif // OAES_DEBUG + + // InvSubBytes(state) + for( _j = 0; _j < c_len; _j++ ) + oaes_inv_sub_byte( c + _j ); + +#ifdef OAES_DEBUG + if( _ctx->step_cb ) + _ctx->step_cb( c, "is_box", _i, NULL ); +#endif // OAES_DEBUG + + // AddRoundKey(state, w[round*Nb, (round+1)*Nb-1]) + for( _j = 0; _j < c_len; _j++ ) + c[_j] = c[_j] ^ + _ctx->key->exp_data[_i * OAES_RKEY_LEN * OAES_COL_LEN + _j]; + +#ifdef OAES_DEBUG + if( _ctx->step_cb ) + { + _ctx->step_cb( _ctx->key->exp_data + _i * OAES_RKEY_LEN * OAES_COL_LEN, + "ik_sch", _i, NULL ); + _ctx->step_cb( c, "ik_add", _i, NULL ); + } +#endif // OAES_DEBUG + + // InvMixColums(state) + oaes_inv_mix_cols( c ); + oaes_inv_mix_cols( c + 4 ); + oaes_inv_mix_cols( c + 8 ); + oaes_inv_mix_cols( c + 12 ); + +#ifdef OAES_DEBUG + if( _ctx->step_cb ) + _ctx->step_cb( c, "im_col", _i, NULL ); +#endif // OAES_DEBUG + + } + + // InvShiftRows(state) + oaes_inv_shift_rows( c ); + +#ifdef OAES_DEBUG + if( _ctx->step_cb ) + _ctx->step_cb( c, "is_row", 1, NULL ); +#endif // OAES_DEBUG + + // InvSubBytes(state) + for( _i = 0; _i < c_len; _i++ ) + oaes_inv_sub_byte( c + _i ); + +#ifdef OAES_DEBUG + if( _ctx->step_cb ) + _ctx->step_cb( c, "is_box", 1, NULL ); +#endif // OAES_DEBUG + + // AddRoundKey(state, w[0, Nb-1]) + for( _i = 0; _i < c_len; _i++ ) + c[_i] = c[_i] ^ _ctx->key->exp_data[_i]; + +#ifdef OAES_DEBUG + if( _ctx->step_cb ) + { + _ctx->step_cb( _ctx->key->exp_data, "ik_sch", 1, NULL ); + _ctx->step_cb( c, "ioutput", 1, NULL ); + } +#endif // OAES_DEBUG + + return OAES_RET_SUCCESS; +} + +OAES_RET oaes_encrypt( OAES_CTX * ctx, + const uint8_t * m, size_t m_len, uint8_t * c, size_t * c_len ) +{ + size_t _i, _j, _c_len_in, _c_data_len; + size_t _pad_len = m_len % OAES_BLOCK_SIZE == 0 ? + 0 : OAES_BLOCK_SIZE - m_len % OAES_BLOCK_SIZE; + oaes_ctx * _ctx = (oaes_ctx *) ctx; + OAES_RET _rc = OAES_RET_SUCCESS; + uint8_t _flags = _pad_len ? OAES_FLAG_PAD : 0; + + if( NULL == _ctx ) + return OAES_RET_ARG1; + + if( NULL == m ) + return OAES_RET_ARG2; + + if( NULL == c_len ) + return OAES_RET_ARG5; + + _c_len_in = *c_len; + // data + pad + _c_data_len = m_len + _pad_len; + // header + iv + data + pad + *c_len = 2 * OAES_BLOCK_SIZE + m_len + _pad_len; + + if( NULL == c ) + return OAES_RET_SUCCESS; + + if( _c_len_in < *c_len ) + return OAES_RET_BUF; + + if( NULL == _ctx->key ) + return OAES_RET_NOKEY; + + // header + memcpy(c, oaes_header, OAES_BLOCK_SIZE ); + memcpy(c + 6, &_ctx->options, sizeof(_ctx->options)); + memcpy(c + 8, &_flags, sizeof(_flags)); + // iv + memcpy(c + OAES_BLOCK_SIZE, _ctx->iv, OAES_BLOCK_SIZE ); + // data + memcpy(c + 2 * OAES_BLOCK_SIZE, m, m_len ); + + for( _i = 0; _i < _c_data_len; _i += OAES_BLOCK_SIZE ) + { + uint8_t _block[OAES_BLOCK_SIZE]; + size_t _block_size = min( m_len - _i, OAES_BLOCK_SIZE ); + + memcpy( _block, c + 2 * OAES_BLOCK_SIZE + _i, _block_size ); + + // insert pad + for( _j = 0; _j < OAES_BLOCK_SIZE - _block_size; _j++ ) + _block[ _block_size + _j ] = _j + 1; + + // CBC + if( _ctx->options & OAES_OPTION_CBC ) + { + for( _j = 0; _j < OAES_BLOCK_SIZE; _j++ ) + _block[_j] = _block[_j] ^ _ctx->iv[_j]; + } + + _rc = _rc || + oaes_encrypt_block( ctx, _block, OAES_BLOCK_SIZE ); + memcpy( c + 2 * OAES_BLOCK_SIZE + _i, _block, OAES_BLOCK_SIZE ); + + if( _ctx->options & OAES_OPTION_CBC ) + memcpy( _ctx->iv, _block, OAES_BLOCK_SIZE ); + } + + return _rc; +} + +OAES_RET oaes_decrypt( OAES_CTX * ctx, + const uint8_t * c, size_t c_len, uint8_t * m, size_t * m_len ) +{ + size_t _i, _j, _m_len_in; + oaes_ctx * _ctx = (oaes_ctx *) ctx; + OAES_RET _rc = OAES_RET_SUCCESS; + uint8_t _iv[OAES_BLOCK_SIZE]; + uint8_t _flags; + OAES_OPTION _options; + + if( NULL == ctx ) + return OAES_RET_ARG1; + + if( NULL == c ) + return OAES_RET_ARG2; + + if( c_len % OAES_BLOCK_SIZE ) + return OAES_RET_ARG3; + + if( NULL == m_len ) + return OAES_RET_ARG5; + + _m_len_in = *m_len; + *m_len = c_len - 2 * OAES_BLOCK_SIZE; + + if( NULL == m ) + return OAES_RET_SUCCESS; + + if( _m_len_in < *m_len ) + return OAES_RET_BUF; + + if( NULL == _ctx->key ) + return OAES_RET_NOKEY; + + // header + if( 0 != memcmp( c, oaes_header, 4 ) ) + return OAES_RET_HEADER; + + // header version + switch( c[4] ) + { + case 0x01: + break; + default: + return OAES_RET_HEADER; + } + + // header type + switch( c[5] ) + { + case 0x02: + break; + default: + return OAES_RET_HEADER; + } + + // options + memcpy(&_options, c + 6, sizeof(_options)); + // validate that all options are valid + if( _options & ~( + OAES_OPTION_ECB + | OAES_OPTION_CBC +#ifdef OAES_DEBUG + | OAES_OPTION_STEP_ON + | OAES_OPTION_STEP_OFF +#endif // OAES_DEBUG + ) ) + return OAES_RET_HEADER; + if( ( _options & OAES_OPTION_ECB ) && + ( _options & OAES_OPTION_CBC ) ) + return OAES_RET_HEADER; + if( _options == OAES_OPTION_NONE ) + return OAES_RET_HEADER; + + // flags + memcpy(&_flags, c + 8, sizeof(_flags)); + // validate that all flags are valid + if( _flags & ~( + OAES_FLAG_PAD + ) ) + return OAES_RET_HEADER; + + // iv + memcpy( _iv, c + OAES_BLOCK_SIZE, OAES_BLOCK_SIZE); + // data + pad + memcpy( m, c + 2 * OAES_BLOCK_SIZE, *m_len ); + + for( _i = 0; _i < *m_len; _i += OAES_BLOCK_SIZE ) + { + if( ( _options & OAES_OPTION_CBC ) && _i > 0 ) + memcpy( _iv, c + OAES_BLOCK_SIZE + _i, OAES_BLOCK_SIZE ); + + _rc = _rc || + oaes_decrypt_block( ctx, m + _i, min( *m_len - _i, OAES_BLOCK_SIZE ) ); + + // CBC + if( _options & OAES_OPTION_CBC ) + { + for( _j = 0; _j < OAES_BLOCK_SIZE; _j++ ) + m[ _i + _j ] = m[ _i + _j ] ^ _iv[_j]; + } + } + + // remove pad + if( _flags & OAES_FLAG_PAD ) + { + int _is_pad = 1; + size_t _temp = (size_t) m[*m_len - 1]; + + if( _temp <= 0x00 || _temp > 0x0f ) + return OAES_RET_HEADER; + for( _i = 0; _i < _temp; _i++ ) + if( m[*m_len - 1 - _i] != _temp - _i ) + _is_pad = 0; + if( _is_pad ) + { + memset( m + *m_len - _temp, 0, _temp ); + *m_len -= _temp; + } + else + return OAES_RET_HEADER; + } + + return OAES_RET_SUCCESS; +} + + +OAES_API OAES_RET oaes_encryption_round( const uint8_t * key, uint8_t * c ) +{ + size_t _i; + + if( NULL == key ) + return OAES_RET_ARG1; + + if( NULL == c ) + return OAES_RET_ARG2; + + // SubBytes(state) + for( _i = 0; _i < OAES_BLOCK_SIZE; _i++ ) + oaes_sub_byte( c + _i ); + + // ShiftRows(state) + oaes_shift_rows( c ); + + // MixColumns(state) + oaes_mix_cols( c ); + oaes_mix_cols( c + 4 ); + oaes_mix_cols( c + 8 ); + oaes_mix_cols( c + 12 ); + + // AddRoundKey(State, key) + for( _i = 0; _i < OAES_BLOCK_SIZE; _i++ ) + c[_i] ^= key[_i]; + + return OAES_RET_SUCCESS; +} + +OAES_API OAES_RET oaes_pseudo_encrypt_ecb( OAES_CTX * ctx, uint8_t * c ) +{ + size_t _i; + oaes_ctx * _ctx = (oaes_ctx *) ctx; + + if( NULL == _ctx ) + return OAES_RET_ARG1; + + if( NULL == c ) + return OAES_RET_ARG2; + + if( NULL == _ctx->key ) + return OAES_RET_NOKEY; + + for ( _i = 0; _i < 10; ++_i ) + { + oaes_encryption_round( &_ctx->key->exp_data[_i * OAES_RKEY_LEN * OAES_COL_LEN], c ); + } + + return OAES_RET_SUCCESS; +} diff --git a/src/crypto/oaes_lib.h b/src/crypto/oaes_lib.h new file mode 100644 index 000000000..9155bce02 --- /dev/null +++ b/src/crypto/oaes_lib.h @@ -0,0 +1,190 @@ +/* + * --------------------------------------------------------------------------- + * OpenAES License + * --------------------------------------------------------------------------- + * Copyright (c) 2012, Nabil S. Al Ramli, www.nalramli.com + * 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. + * + * 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. + * --------------------------------------------------------------------------- + */ + +#ifndef _OAES_LIB_H +#define _OAES_LIB_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _WIN32 +# ifdef OAES_SHARED +# ifdef oaes_lib_EXPORTS +# define OAES_API __declspec(dllexport) +# else +# define OAES_API __declspec(dllimport) +# endif +# else +# define OAES_API +# endif +#else +# define OAES_API +#endif // WIN32 + +#define OAES_VERSION "0.8.1" +#define OAES_BLOCK_SIZE 16 + +typedef void OAES_CTX; + +typedef enum +{ + OAES_RET_FIRST = 0, + OAES_RET_SUCCESS = 0, + OAES_RET_UNKNOWN, + OAES_RET_ARG1, + OAES_RET_ARG2, + OAES_RET_ARG3, + OAES_RET_ARG4, + OAES_RET_ARG5, + OAES_RET_NOKEY, + OAES_RET_MEM, + OAES_RET_BUF, + OAES_RET_HEADER, + OAES_RET_COUNT +} OAES_RET; + +/* + * oaes_set_option() takes one of these values for its [option] parameter + * some options accept either an optional or a required [value] parameter + */ +// no option +#define OAES_OPTION_NONE 0 +// enable ECB mode, disable CBC mode +#define OAES_OPTION_ECB 1 +// enable CBC mode, disable ECB mode +// value is optional, may pass uint8_t iv[OAES_BLOCK_SIZE] to specify +// the value of the initialization vector, iv +#define OAES_OPTION_CBC 2 + +#ifdef OAES_DEBUG +typedef int ( * oaes_step_cb ) ( + const uint8_t state[OAES_BLOCK_SIZE], + const char * step_name, + int step_count, + void * user_data ); +// enable state stepping mode +// value is required, must pass oaes_step_cb to receive the state at each step +#define OAES_OPTION_STEP_ON 4 +// disable state stepping mode +#define OAES_OPTION_STEP_OFF 8 +#endif // OAES_DEBUG + +typedef uint16_t OAES_OPTION; + +/* + * // usage: + * + * OAES_CTX * ctx = oaes_alloc(); + * . + * . + * . + * { + * oaes_gen_key_xxx( ctx ); + * { + * oaes_key_export( ctx, _buf, &_buf_len ); + * // or + * oaes_key_export_data( ctx, _buf, &_buf_len );\ + * } + * } + * // or + * { + * oaes_key_import( ctx, _buf, _buf_len ); + * // or + * oaes_key_import_data( ctx, _buf, _buf_len ); + * } + * . + * . + * . + * oaes_encrypt( ctx, m, m_len, c, &c_len ); + * . + * . + * . + * oaes_decrypt( ctx, c, c_len, m, &m_len ); + * . + * . + * . + * oaes_free( &ctx ); + */ + +OAES_API OAES_CTX * oaes_alloc(void); + +OAES_API OAES_RET oaes_free( OAES_CTX ** ctx ); + +OAES_API OAES_RET oaes_set_option( OAES_CTX * ctx, + OAES_OPTION option, const void * value ); + +OAES_API OAES_RET oaes_key_gen_128( OAES_CTX * ctx ); + +OAES_API OAES_RET oaes_key_gen_192( OAES_CTX * ctx ); + +OAES_API OAES_RET oaes_key_gen_256( OAES_CTX * ctx ); + +// export key with header information +// set data == NULL to get the required data_len +OAES_API OAES_RET oaes_key_export( OAES_CTX * ctx, + uint8_t * data, size_t * data_len ); + +// directly export the data from key +// set data == NULL to get the required data_len +OAES_API OAES_RET oaes_key_export_data( OAES_CTX * ctx, + uint8_t * data, size_t * data_len ); + +// import key with header information +OAES_API OAES_RET oaes_key_import( OAES_CTX * ctx, + const uint8_t * data, size_t data_len ); + +// directly import data into key +OAES_API OAES_RET oaes_key_import_data( OAES_CTX * ctx, + const uint8_t * data, size_t data_len ); + +// set c == NULL to get the required c_len +OAES_API OAES_RET oaes_encrypt( OAES_CTX * ctx, + const uint8_t * m, size_t m_len, uint8_t * c, size_t * c_len ); + +// set m == NULL to get the required m_len +OAES_API OAES_RET oaes_decrypt( OAES_CTX * ctx, + const uint8_t * c, size_t c_len, uint8_t * m, size_t * m_len ); + +// set buf == NULL to get the required buf_len +OAES_API OAES_RET oaes_sprintf( + char * buf, size_t * buf_len, const uint8_t * data, size_t data_len ); + +OAES_API OAES_RET oaes_encryption_round( const uint8_t * key, uint8_t * c ); + +OAES_API OAES_RET oaes_pseudo_encrypt_ecb( OAES_CTX * ctx, uint8_t * c ); + +#ifdef __cplusplus +} +#endif + +#endif // _OAES_LIB_H diff --git a/src/crypto/random.c b/src/crypto/random.c index 582d0b8f6..a17cb47d2 100644 --- a/src/crypto/random.c +++ b/src/crypto/random.c @@ -1,17 +1,41 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + #include +#include +#include + +#include "hash-ops.h" +#include "initializer.h" +#include "random.h" + +static void generate_system_random_bytes(size_t n, void *result); + +#if defined(_WIN32) + +#include +#include + +static void generate_system_random_bytes(size_t n, void *result) { + HCRYPTPROV prov; +#define must_succeed(x) do if (!(x)) assert(0); while (0) + must_succeed(CryptAcquireContext(&prov, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_SILENT)); + must_succeed(CryptGenRandom(prov, (DWORD)n, result)); + must_succeed(CryptReleaseContext(prov, 0)); +#undef must_succeed +} + +#else + #include #include #include -#include #include -#include #include #include #include -#include "hash.h" -#include "random.h" - static void generate_system_random_bytes(size_t n, void *result) { int fd; if ((fd = open("/dev/urandom", O_RDONLY | O_NOCTTY | O_CLOEXEC)) < 0) { @@ -38,20 +62,51 @@ static void generate_system_random_bytes(size_t n, void *result) { } } -static struct keccak_state state; +#endif -void init_random(void) { +static union hash_state state; + +#if !defined(NDEBUG) +static volatile int curstate; /* To catch thread safety problems. */ +#endif + +FINALIZER(deinit_random) { +#if !defined(NDEBUG) + assert(curstate == 1); + curstate = 0; +#endif + memset(&state, 0, sizeof(union hash_state)); +} + +INITIALIZER(init_random) { generate_system_random_bytes(32, &state); + REGISTER_FINALIZER(deinit_random); +#if !defined(NDEBUG) + assert(curstate == 0); + curstate = 1; +#endif } void generate_random_bytes(size_t n, void *result) { +#if !defined(NDEBUG) + assert(curstate == 1); + curstate = 2; +#endif if (n == 0) { +#if !defined(NDEBUG) + assert(curstate == 2); + curstate = 1; +#endif return; } for (;;) { - keccak_permutation(&state); + hash_permutation(&state); if (n <= HASH_DATA_AREA) { memcpy(result, &state, n); +#if !defined(NDEBUG) + assert(curstate == 2); + curstate = 1; +#endif return; } else { memcpy(result, &state, HASH_DATA_AREA); @@ -59,4 +114,4 @@ void generate_random_bytes(size_t n, void *result) { n -= HASH_DATA_AREA; } } -} \ No newline at end of file +} diff --git a/src/crypto/random.h b/src/crypto/random.h index b37963c0f..afc8f3b2d 100644 --- a/src/crypto/random.h +++ b/src/crypto/random.h @@ -1,19 +1,9 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + #pragma once #include -#if defined(__cplusplus) -#include - -namespace crypto { - - extern "C" { -#endif - - void init_random(void); - void generate_random_bytes(size_t n, void *result); - -#if defined(__cplusplus) - } -} -#endif \ No newline at end of file +void generate_random_bytes(size_t n, void *result); diff --git a/src/crypto/skein.c b/src/crypto/skein.c new file mode 100644 index 000000000..9c8ac288d --- /dev/null +++ b/src/crypto/skein.c @@ -0,0 +1,2036 @@ +/*********************************************************************** +** +** Implementation of the Skein hash function. +** +** Source code author: Doug Whiting, 2008. +** +** This algorithm and source code is released to the public domain. +** +************************************************************************/ + +#define SKEIN_PORT_CODE /* instantiate any code in skein_port.h */ + +#include /* get size_t definition */ +#include /* get the memcpy/memset functions */ +#include "skein.h" /* get the Skein API definitions */ + +#define DISABLE_UNUSED 0 + +#ifndef SKEIN_256_NIST_MAX_HASHBITS +#define SKEIN_256_NIST_MAX_HASHBITS (0) +#endif + +#ifndef SKEIN_512_NIST_MAX_HASHBITS +#define SKEIN_512_NIST_MAX_HASHBITS (512) +#endif + +#define SKEIN_MODIFIER_WORDS ( 2) /* number of modifier (tweak) words */ + +#define SKEIN_256_STATE_WORDS ( 4) +#define SKEIN_512_STATE_WORDS ( 8) +#define SKEIN1024_STATE_WORDS (16) +#define SKEIN_MAX_STATE_WORDS (16) + +#define SKEIN_256_STATE_BYTES ( 8*SKEIN_256_STATE_WORDS) +#define SKEIN_512_STATE_BYTES ( 8*SKEIN_512_STATE_WORDS) +#define SKEIN1024_STATE_BYTES ( 8*SKEIN1024_STATE_WORDS) + +#define SKEIN_256_STATE_BITS (64*SKEIN_256_STATE_WORDS) +#define SKEIN_512_STATE_BITS (64*SKEIN_512_STATE_WORDS) +#define SKEIN1024_STATE_BITS (64*SKEIN1024_STATE_WORDS) + +#define SKEIN_256_BLOCK_BYTES ( 8*SKEIN_256_STATE_WORDS) +#define SKEIN_512_BLOCK_BYTES ( 8*SKEIN_512_STATE_WORDS) +#define SKEIN1024_BLOCK_BYTES ( 8*SKEIN1024_STATE_WORDS) + +#define SKEIN_RND_SPECIAL (1000u) +#define SKEIN_RND_KEY_INITIAL (SKEIN_RND_SPECIAL+0u) +#define SKEIN_RND_KEY_INJECT (SKEIN_RND_SPECIAL+1u) +#define SKEIN_RND_FEED_FWD (SKEIN_RND_SPECIAL+2u) + +typedef struct +{ + size_t hashBitLen; /* size of hash result, in bits */ + size_t bCnt; /* current byte count in buffer b[] */ + u64b_t T[SKEIN_MODIFIER_WORDS]; /* tweak words: T[0]=byte cnt, T[1]=flags */ +} Skein_Ctxt_Hdr_t; + +typedef struct /* 256-bit Skein hash context structure */ +{ + Skein_Ctxt_Hdr_t h; /* common header context variables */ + u64b_t X[SKEIN_256_STATE_WORDS]; /* chaining variables */ + u08b_t b[SKEIN_256_BLOCK_BYTES]; /* partial block buffer (8-byte aligned) */ +} Skein_256_Ctxt_t; + +typedef struct /* 512-bit Skein hash context structure */ +{ + Skein_Ctxt_Hdr_t h; /* common header context variables */ + u64b_t X[SKEIN_512_STATE_WORDS]; /* chaining variables */ + u08b_t b[SKEIN_512_BLOCK_BYTES]; /* partial block buffer (8-byte aligned) */ +} Skein_512_Ctxt_t; + +typedef struct /* 1024-bit Skein hash context structure */ +{ + Skein_Ctxt_Hdr_t h; /* common header context variables */ + u64b_t X[SKEIN1024_STATE_WORDS]; /* chaining variables */ + u08b_t b[SKEIN1024_BLOCK_BYTES]; /* partial block buffer (8-byte aligned) */ +} Skein1024_Ctxt_t; + +/* Skein APIs for (incremental) "straight hashing" */ +#if SKEIN_256_NIST_MAX_HASH_BITS +static int Skein_256_Init (Skein_256_Ctxt_t *ctx, size_t hashBitLen); +#endif +static int Skein_512_Init (Skein_512_Ctxt_t *ctx, size_t hashBitLen); +static int Skein1024_Init (Skein1024_Ctxt_t *ctx, size_t hashBitLen); + +static int Skein_256_Update(Skein_256_Ctxt_t *ctx, const u08b_t *msg, size_t msgByteCnt); +static int Skein_512_Update(Skein_512_Ctxt_t *ctx, const u08b_t *msg, size_t msgByteCnt); +static int Skein1024_Update(Skein1024_Ctxt_t *ctx, const u08b_t *msg, size_t msgByteCnt); + +static int Skein_256_Final (Skein_256_Ctxt_t *ctx, u08b_t * hashVal); +static int Skein_512_Final (Skein_512_Ctxt_t *ctx, u08b_t * hashVal); +static int Skein1024_Final (Skein1024_Ctxt_t *ctx, u08b_t * hashVal); + +/* +** Skein APIs for "extended" initialization: MAC keys, tree hashing. +** After an InitExt() call, just use Update/Final calls as with Init(). +** +** Notes: Same parameters as _Init() calls, plus treeInfo/key/keyBytes. +** When keyBytes == 0 and treeInfo == SKEIN_SEQUENTIAL, +** the results of InitExt() are identical to calling Init(). +** The function Init() may be called once to "precompute" the IV for +** a given hashBitLen value, then by saving a copy of the context +** the IV computation may be avoided in later calls. +** Similarly, the function InitExt() may be called once per MAC key +** to precompute the MAC IV, then a copy of the context saved and +** reused for each new MAC computation. +**/ +#if 0 +static int Skein_256_InitExt(Skein_256_Ctxt_t *ctx, size_t hashBitLen, u64b_t treeInfo, const u08b_t *key, size_t keyBytes); +static int Skein_512_InitExt(Skein_512_Ctxt_t *ctx, size_t hashBitLen, u64b_t treeInfo, const u08b_t *key, size_t keyBytes); +static int Skein1024_InitExt(Skein1024_Ctxt_t *ctx, size_t hashBitLen, u64b_t treeInfo, const u08b_t *key, size_t keyBytes); +#endif + +/* +** Skein APIs for MAC and tree hash: +** Final_Pad: pad, do final block, but no OUTPUT type +** Output: do just the output stage +*/ +#if 0 +static int Skein_256_Final_Pad(Skein_256_Ctxt_t *ctx, u08b_t * hashVal); +static int Skein_512_Final_Pad(Skein_512_Ctxt_t *ctx, u08b_t * hashVal); +static int Skein1024_Final_Pad(Skein1024_Ctxt_t *ctx, u08b_t * hashVal); +#endif + +#ifndef SKEIN_TREE_HASH +#define SKEIN_TREE_HASH (1) +#endif +#if 0 +#if SKEIN_TREE_HASH +static int Skein_256_Output (Skein_256_Ctxt_t *ctx, u08b_t * hashVal); +static int Skein_512_Output (Skein_512_Ctxt_t *ctx, u08b_t * hashVal); +static int Skein1024_Output (Skein1024_Ctxt_t *ctx, u08b_t * hashVal); +#endif +#endif + +/***************************************************************** +** "Internal" Skein definitions +** -- not needed for sequential hashing API, but will be +** helpful for other uses of Skein (e.g., tree hash mode). +** -- included here so that they can be shared between +** reference and optimized code. +******************************************************************/ + +/* tweak word T[1]: bit field starting positions */ +#define SKEIN_T1_BIT(BIT) ((BIT) - 64) /* offset 64 because it's the second word */ + +#define SKEIN_T1_POS_TREE_LVL SKEIN_T1_BIT(112) /* bits 112..118: level in hash tree */ +#define SKEIN_T1_POS_BIT_PAD SKEIN_T1_BIT(119) /* bit 119 : partial final input byte */ +#define SKEIN_T1_POS_BLK_TYPE SKEIN_T1_BIT(120) /* bits 120..125: type field */ +#define SKEIN_T1_POS_FIRST SKEIN_T1_BIT(126) /* bits 126 : first block flag */ +#define SKEIN_T1_POS_FINAL SKEIN_T1_BIT(127) /* bit 127 : final block flag */ + +/* tweak word T[1]: flag bit definition(s) */ +#define SKEIN_T1_FLAG_FIRST (((u64b_t) 1 ) << SKEIN_T1_POS_FIRST) +#define SKEIN_T1_FLAG_FINAL (((u64b_t) 1 ) << SKEIN_T1_POS_FINAL) +#define SKEIN_T1_FLAG_BIT_PAD (((u64b_t) 1 ) << SKEIN_T1_POS_BIT_PAD) + +/* tweak word T[1]: tree level bit field mask */ +#define SKEIN_T1_TREE_LVL_MASK (((u64b_t)0x7F) << SKEIN_T1_POS_TREE_LVL) +#define SKEIN_T1_TREE_LEVEL(n) (((u64b_t) (n)) << SKEIN_T1_POS_TREE_LVL) + +/* tweak word T[1]: block type field */ +#define SKEIN_BLK_TYPE_KEY ( 0) /* key, for MAC and KDF */ +#define SKEIN_BLK_TYPE_CFG ( 4) /* configuration block */ +#define SKEIN_BLK_TYPE_PERS ( 8) /* personalization string */ +#define SKEIN_BLK_TYPE_PK (12) /* public key (for digital signature hashing) */ +#define SKEIN_BLK_TYPE_KDF (16) /* key identifier for KDF */ +#define SKEIN_BLK_TYPE_NONCE (20) /* nonce for PRNG */ +#define SKEIN_BLK_TYPE_MSG (48) /* message processing */ +#define SKEIN_BLK_TYPE_OUT (63) /* output stage */ +#define SKEIN_BLK_TYPE_MASK (63) /* bit field mask */ + +#define SKEIN_T1_BLK_TYPE(T) (((u64b_t) (SKEIN_BLK_TYPE_##T)) << SKEIN_T1_POS_BLK_TYPE) +#define SKEIN_T1_BLK_TYPE_KEY SKEIN_T1_BLK_TYPE(KEY) /* key, for MAC and KDF */ +#define SKEIN_T1_BLK_TYPE_CFG SKEIN_T1_BLK_TYPE(CFG) /* configuration block */ +#define SKEIN_T1_BLK_TYPE_PERS SKEIN_T1_BLK_TYPE(PERS) /* personalization string */ +#define SKEIN_T1_BLK_TYPE_PK SKEIN_T1_BLK_TYPE(PK) /* public key (for digital signature hashing) */ +#define SKEIN_T1_BLK_TYPE_KDF SKEIN_T1_BLK_TYPE(KDF) /* key identifier for KDF */ +#define SKEIN_T1_BLK_TYPE_NONCE SKEIN_T1_BLK_TYPE(NONCE)/* nonce for PRNG */ +#define SKEIN_T1_BLK_TYPE_MSG SKEIN_T1_BLK_TYPE(MSG) /* message processing */ +#define SKEIN_T1_BLK_TYPE_OUT SKEIN_T1_BLK_TYPE(OUT) /* output stage */ +#define SKEIN_T1_BLK_TYPE_MASK SKEIN_T1_BLK_TYPE(MASK) /* field bit mask */ + +#define SKEIN_T1_BLK_TYPE_CFG_FINAL (SKEIN_T1_BLK_TYPE_CFG | SKEIN_T1_FLAG_FINAL) +#define SKEIN_T1_BLK_TYPE_OUT_FINAL (SKEIN_T1_BLK_TYPE_OUT | SKEIN_T1_FLAG_FINAL) + +#define SKEIN_VERSION (1) + +#ifndef SKEIN_ID_STRING_LE /* allow compile-time personalization */ +#define SKEIN_ID_STRING_LE (0x33414853) /* "SHA3" (little-endian)*/ +#endif + +#define SKEIN_MK_64(hi32,lo32) ((lo32) + (((u64b_t) (hi32)) << 32)) +#define SKEIN_SCHEMA_VER SKEIN_MK_64(SKEIN_VERSION,SKEIN_ID_STRING_LE) +#define SKEIN_KS_PARITY SKEIN_MK_64(0x1BD11BDA,0xA9FC1A22) + +#define SKEIN_CFG_STR_LEN (4*8) + +/* bit field definitions in config block treeInfo word */ +#define SKEIN_CFG_TREE_LEAF_SIZE_POS ( 0) +#define SKEIN_CFG_TREE_NODE_SIZE_POS ( 8) +#define SKEIN_CFG_TREE_MAX_LEVEL_POS (16) + +#define SKEIN_CFG_TREE_LEAF_SIZE_MSK (((u64b_t) 0xFF) << SKEIN_CFG_TREE_LEAF_SIZE_POS) +#define SKEIN_CFG_TREE_NODE_SIZE_MSK (((u64b_t) 0xFF) << SKEIN_CFG_TREE_NODE_SIZE_POS) +#define SKEIN_CFG_TREE_MAX_LEVEL_MSK (((u64b_t) 0xFF) << SKEIN_CFG_TREE_MAX_LEVEL_POS) + +#define SKEIN_CFG_TREE_INFO(leaf,node,maxLvl) \ + ( (((u64b_t)(leaf )) << SKEIN_CFG_TREE_LEAF_SIZE_POS) | \ + (((u64b_t)(node )) << SKEIN_CFG_TREE_NODE_SIZE_POS) | \ + (((u64b_t)(maxLvl)) << SKEIN_CFG_TREE_MAX_LEVEL_POS) ) + +#define SKEIN_CFG_TREE_INFO_SEQUENTIAL SKEIN_CFG_TREE_INFO(0,0,0) /* use as treeInfo in InitExt() call for sequential processing */ + +/* +** Skein macros for getting/setting tweak words, etc. +** These are useful for partial input bytes, hash tree init/update, etc. +**/ +#define Skein_Get_Tweak(ctxPtr,TWK_NUM) ((ctxPtr)->h.T[TWK_NUM]) +#define Skein_Set_Tweak(ctxPtr,TWK_NUM,tVal) {(ctxPtr)->h.T[TWK_NUM] = (tVal);} + +#define Skein_Get_T0(ctxPtr) Skein_Get_Tweak(ctxPtr,0) +#define Skein_Get_T1(ctxPtr) Skein_Get_Tweak(ctxPtr,1) +#define Skein_Set_T0(ctxPtr,T0) Skein_Set_Tweak(ctxPtr,0,T0) +#define Skein_Set_T1(ctxPtr,T1) Skein_Set_Tweak(ctxPtr,1,T1) + +/* set both tweak words at once */ +#define Skein_Set_T0_T1(ctxPtr,T0,T1) \ +{ \ + Skein_Set_T0(ctxPtr,(T0)); \ + Skein_Set_T1(ctxPtr,(T1)); \ +} + +#define Skein_Set_Type(ctxPtr,BLK_TYPE) \ + Skein_Set_T1(ctxPtr,SKEIN_T1_BLK_TYPE_##BLK_TYPE) + +/* set up for starting with a new type: h.T[0]=0; h.T[1] = NEW_TYPE; h.bCnt=0; */ +#define Skein_Start_New_Type(ctxPtr,BLK_TYPE) \ +{ Skein_Set_T0_T1(ctxPtr,0,SKEIN_T1_FLAG_FIRST | SKEIN_T1_BLK_TYPE_##BLK_TYPE); (ctxPtr)->h.bCnt=0; } + +#define Skein_Clear_First_Flag(hdr) { (hdr).T[1] &= ~SKEIN_T1_FLAG_FIRST; } +#define Skein_Set_Bit_Pad_Flag(hdr) { (hdr).T[1] |= SKEIN_T1_FLAG_BIT_PAD; } + +#define Skein_Set_Tree_Level(hdr,height) { (hdr).T[1] |= SKEIN_T1_TREE_LEVEL(height);} + +/***************************************************************** +** "Internal" Skein definitions for debugging and error checking +******************************************************************/ +#define Skein_Show_Block(bits,ctx,X,blkPtr,wPtr,ksEvenPtr,ksOddPtr) +#define Skein_Show_Round(bits,ctx,r,X) +#define Skein_Show_R_Ptr(bits,ctx,r,X_ptr) +#define Skein_Show_Final(bits,ctx,cnt,outPtr) +#define Skein_Show_Key(bits,ctx,key,keyBytes) + + +#ifndef SKEIN_ERR_CHECK /* run-time checks (e.g., bad params, uninitialized context)? */ +#define Skein_Assert(x,retCode)/* default: ignore all Asserts, for performance */ +#define Skein_assert(x) +#elif defined(SKEIN_ASSERT) +#include +#define Skein_Assert(x,retCode) assert(x) +#define Skein_assert(x) assert(x) +#else +#include +#define Skein_Assert(x,retCode) { if (!(x)) return retCode; } /* caller error */ +#define Skein_assert(x) assert(x) /* internal error */ +#endif + +/***************************************************************** +** Skein block function constants (shared across Ref and Opt code) +******************************************************************/ +enum +{ + /* Skein_256 round rotation constants */ + R_256_0_0=14, R_256_0_1=16, + R_256_1_0=52, R_256_1_1=57, + R_256_2_0=23, R_256_2_1=40, + R_256_3_0= 5, R_256_3_1=37, + R_256_4_0=25, R_256_4_1=33, + R_256_5_0=46, R_256_5_1=12, + R_256_6_0=58, R_256_6_1=22, + R_256_7_0=32, R_256_7_1=32, + + /* Skein_512 round rotation constants */ + R_512_0_0=46, R_512_0_1=36, R_512_0_2=19, R_512_0_3=37, + R_512_1_0=33, R_512_1_1=27, R_512_1_2=14, R_512_1_3=42, + R_512_2_0=17, R_512_2_1=49, R_512_2_2=36, R_512_2_3=39, + R_512_3_0=44, R_512_3_1= 9, R_512_3_2=54, R_512_3_3=56, + R_512_4_0=39, R_512_4_1=30, R_512_4_2=34, R_512_4_3=24, + R_512_5_0=13, R_512_5_1=50, R_512_5_2=10, R_512_5_3=17, + R_512_6_0=25, R_512_6_1=29, R_512_6_2=39, R_512_6_3=43, + R_512_7_0= 8, R_512_7_1=35, R_512_7_2=56, R_512_7_3=22, + + /* Skein1024 round rotation constants */ + R1024_0_0=24, R1024_0_1=13, R1024_0_2= 8, R1024_0_3=47, R1024_0_4= 8, R1024_0_5=17, R1024_0_6=22, R1024_0_7=37, + R1024_1_0=38, R1024_1_1=19, R1024_1_2=10, R1024_1_3=55, R1024_1_4=49, R1024_1_5=18, R1024_1_6=23, R1024_1_7=52, + R1024_2_0=33, R1024_2_1= 4, R1024_2_2=51, R1024_2_3=13, R1024_2_4=34, R1024_2_5=41, R1024_2_6=59, R1024_2_7=17, + R1024_3_0= 5, R1024_3_1=20, R1024_3_2=48, R1024_3_3=41, R1024_3_4=47, R1024_3_5=28, R1024_3_6=16, R1024_3_7=25, + R1024_4_0=41, R1024_4_1= 9, R1024_4_2=37, R1024_4_3=31, R1024_4_4=12, R1024_4_5=47, R1024_4_6=44, R1024_4_7=30, + R1024_5_0=16, R1024_5_1=34, R1024_5_2=56, R1024_5_3=51, R1024_5_4= 4, R1024_5_5=53, R1024_5_6=42, R1024_5_7=41, + R1024_6_0=31, R1024_6_1=44, R1024_6_2=47, R1024_6_3=46, R1024_6_4=19, R1024_6_5=42, R1024_6_6=44, R1024_6_7=25, + R1024_7_0= 9, R1024_7_1=48, R1024_7_2=35, R1024_7_3=52, R1024_7_4=23, R1024_7_5=31, R1024_7_6=37, R1024_7_7=20 +}; + +#ifndef SKEIN_ROUNDS +#define SKEIN_256_ROUNDS_TOTAL (72) /* number of rounds for the different block sizes */ +#define SKEIN_512_ROUNDS_TOTAL (72) +#define SKEIN1024_ROUNDS_TOTAL (80) +#else /* allow command-line define in range 8*(5..14) */ +#define SKEIN_256_ROUNDS_TOTAL (8*((((SKEIN_ROUNDS/100) + 5) % 10) + 5)) +#define SKEIN_512_ROUNDS_TOTAL (8*((((SKEIN_ROUNDS/ 10) + 5) % 10) + 5)) +#define SKEIN1024_ROUNDS_TOTAL (8*((((SKEIN_ROUNDS ) + 5) % 10) + 5)) +#endif + + +/* +***************** Pre-computed Skein IVs ******************* +** +** NOTE: these values are not "magic" constants, but +** are generated using the Threefish block function. +** They are pre-computed here only for speed; i.e., to +** avoid the need for a Threefish call during Init(). +** +** The IV for any fixed hash length may be pre-computed. +** Only the most common values are included here. +** +************************************************************ +**/ + +#define MK_64 SKEIN_MK_64 + +/* blkSize = 256 bits. hashSize = 128 bits */ +const u64b_t SKEIN_256_IV_128[] = + { + MK_64(0xE1111906,0x964D7260), + MK_64(0x883DAAA7,0x7C8D811C), + MK_64(0x10080DF4,0x91960F7A), + MK_64(0xCCF7DDE5,0xB45BC1C2) + }; + +/* blkSize = 256 bits. hashSize = 160 bits */ +const u64b_t SKEIN_256_IV_160[] = + { + MK_64(0x14202314,0x72825E98), + MK_64(0x2AC4E9A2,0x5A77E590), + MK_64(0xD47A5856,0x8838D63E), + MK_64(0x2DD2E496,0x8586AB7D) + }; + +/* blkSize = 256 bits. hashSize = 224 bits */ +const u64b_t SKEIN_256_IV_224[] = + { + MK_64(0xC6098A8C,0x9AE5EA0B), + MK_64(0x876D5686,0x08C5191C), + MK_64(0x99CB88D7,0xD7F53884), + MK_64(0x384BDDB1,0xAEDDB5DE) + }; + +/* blkSize = 256 bits. hashSize = 256 bits */ +const u64b_t SKEIN_256_IV_256[] = + { + MK_64(0xFC9DA860,0xD048B449), + MK_64(0x2FCA6647,0x9FA7D833), + MK_64(0xB33BC389,0x6656840F), + MK_64(0x6A54E920,0xFDE8DA69) + }; + +/* blkSize = 512 bits. hashSize = 128 bits */ +const u64b_t SKEIN_512_IV_128[] = + { + MK_64(0xA8BC7BF3,0x6FBF9F52), + MK_64(0x1E9872CE,0xBD1AF0AA), + MK_64(0x309B1790,0xB32190D3), + MK_64(0xBCFBB854,0x3F94805C), + MK_64(0x0DA61BCD,0x6E31B11B), + MK_64(0x1A18EBEA,0xD46A32E3), + MK_64(0xA2CC5B18,0xCE84AA82), + MK_64(0x6982AB28,0x9D46982D) + }; + +/* blkSize = 512 bits. hashSize = 160 bits */ +const u64b_t SKEIN_512_IV_160[] = + { + MK_64(0x28B81A2A,0xE013BD91), + MK_64(0xC2F11668,0xB5BDF78F), + MK_64(0x1760D8F3,0xF6A56F12), + MK_64(0x4FB74758,0x8239904F), + MK_64(0x21EDE07F,0x7EAF5056), + MK_64(0xD908922E,0x63ED70B8), + MK_64(0xB8EC76FF,0xECCB52FA), + MK_64(0x01A47BB8,0xA3F27A6E) + }; + +/* blkSize = 512 bits. hashSize = 224 bits */ +const u64b_t SKEIN_512_IV_224[] = + { + MK_64(0xCCD06162,0x48677224), + MK_64(0xCBA65CF3,0xA92339EF), + MK_64(0x8CCD69D6,0x52FF4B64), + MK_64(0x398AED7B,0x3AB890B4), + MK_64(0x0F59D1B1,0x457D2BD0), + MK_64(0x6776FE65,0x75D4EB3D), + MK_64(0x99FBC70E,0x997413E9), + MK_64(0x9E2CFCCF,0xE1C41EF7) + }; + +/* blkSize = 512 bits. hashSize = 256 bits */ +const u64b_t SKEIN_512_IV_256[] = + { + MK_64(0xCCD044A1,0x2FDB3E13), + MK_64(0xE8359030,0x1A79A9EB), + MK_64(0x55AEA061,0x4F816E6F), + MK_64(0x2A2767A4,0xAE9B94DB), + MK_64(0xEC06025E,0x74DD7683), + MK_64(0xE7A436CD,0xC4746251), + MK_64(0xC36FBAF9,0x393AD185), + MK_64(0x3EEDBA18,0x33EDFC13) + }; + +/* blkSize = 512 bits. hashSize = 384 bits */ +const u64b_t SKEIN_512_IV_384[] = + { + MK_64(0xA3F6C6BF,0x3A75EF5F), + MK_64(0xB0FEF9CC,0xFD84FAA4), + MK_64(0x9D77DD66,0x3D770CFE), + MK_64(0xD798CBF3,0xB468FDDA), + MK_64(0x1BC4A666,0x8A0E4465), + MK_64(0x7ED7D434,0xE5807407), + MK_64(0x548FC1AC,0xD4EC44D6), + MK_64(0x266E1754,0x6AA18FF8) + }; + +/* blkSize = 512 bits. hashSize = 512 bits */ +const u64b_t SKEIN_512_IV_512[] = + { + MK_64(0x4903ADFF,0x749C51CE), + MK_64(0x0D95DE39,0x9746DF03), + MK_64(0x8FD19341,0x27C79BCE), + MK_64(0x9A255629,0xFF352CB1), + MK_64(0x5DB62599,0xDF6CA7B0), + MK_64(0xEABE394C,0xA9D5C3F4), + MK_64(0x991112C7,0x1A75B523), + MK_64(0xAE18A40B,0x660FCC33) + }; + +/* blkSize = 1024 bits. hashSize = 384 bits */ +const u64b_t SKEIN1024_IV_384[] = + { + MK_64(0x5102B6B8,0xC1894A35), + MK_64(0xFEEBC9E3,0xFE8AF11A), + MK_64(0x0C807F06,0xE32BED71), + MK_64(0x60C13A52,0xB41A91F6), + MK_64(0x9716D35D,0xD4917C38), + MK_64(0xE780DF12,0x6FD31D3A), + MK_64(0x797846B6,0xC898303A), + MK_64(0xB172C2A8,0xB3572A3B), + MK_64(0xC9BC8203,0xA6104A6C), + MK_64(0x65909338,0xD75624F4), + MK_64(0x94BCC568,0x4B3F81A0), + MK_64(0x3EBBF51E,0x10ECFD46), + MK_64(0x2DF50F0B,0xEEB08542), + MK_64(0x3B5A6530,0x0DBC6516), + MK_64(0x484B9CD2,0x167BBCE1), + MK_64(0x2D136947,0xD4CBAFEA) + }; + +/* blkSize = 1024 bits. hashSize = 512 bits */ +const u64b_t SKEIN1024_IV_512[] = + { + MK_64(0xCAEC0E5D,0x7C1B1B18), + MK_64(0xA01B0E04,0x5F03E802), + MK_64(0x33840451,0xED912885), + MK_64(0x374AFB04,0xEAEC2E1C), + MK_64(0xDF25A0E2,0x813581F7), + MK_64(0xE4004093,0x8B12F9D2), + MK_64(0xA662D539,0xC2ED39B6), + MK_64(0xFA8B85CF,0x45D8C75A), + MK_64(0x8316ED8E,0x29EDE796), + MK_64(0x053289C0,0x2E9F91B8), + MK_64(0xC3F8EF1D,0x6D518B73), + MK_64(0xBDCEC3C4,0xD5EF332E), + MK_64(0x549A7E52,0x22974487), + MK_64(0x67070872,0x5B749816), + MK_64(0xB9CD28FB,0xF0581BD1), + MK_64(0x0E2940B8,0x15804974) + }; + +/* blkSize = 1024 bits. hashSize = 1024 bits */ +const u64b_t SKEIN1024_IV_1024[] = + { + MK_64(0xD593DA07,0x41E72355), + MK_64(0x15B5E511,0xAC73E00C), + MK_64(0x5180E5AE,0xBAF2C4F0), + MK_64(0x03BD41D3,0xFCBCAFAF), + MK_64(0x1CAEC6FD,0x1983A898), + MK_64(0x6E510B8B,0xCDD0589F), + MK_64(0x77E2BDFD,0xC6394ADA), + MK_64(0xC11E1DB5,0x24DCB0A3), + MK_64(0xD6D14AF9,0xC6329AB5), + MK_64(0x6A9B0BFC,0x6EB67E0D), + MK_64(0x9243C60D,0xCCFF1332), + MK_64(0x1A1F1DDE,0x743F02D4), + MK_64(0x0996753C,0x10ED0BB8), + MK_64(0x6572DD22,0xF2B4969A), + MK_64(0x61FD3062,0xD00A579A), + MK_64(0x1DE0536E,0x8682E539) + }; + + +#ifndef SKEIN_USE_ASM +#define SKEIN_USE_ASM (0) /* default is all C code (no ASM) */ +#endif + +#ifndef SKEIN_LOOP +#define SKEIN_LOOP 001 /* default: unroll 256 and 512, but not 1024 */ +#endif + +#define BLK_BITS (WCNT*64) /* some useful definitions for code here */ +#define KW_TWK_BASE (0) +#define KW_KEY_BASE (3) +#define ks (kw + KW_KEY_BASE) +#define ts (kw + KW_TWK_BASE) + +#ifdef SKEIN_DEBUG +#define DebugSaveTweak(ctx) { ctx->h.T[0] = ts[0]; ctx->h.T[1] = ts[1]; } +#else +#define DebugSaveTweak(ctx) +#endif + +/***************************** Skein_256 ******************************/ +#if !(SKEIN_USE_ASM & 256) +static void Skein_256_Process_Block(Skein_256_Ctxt_t *ctx,const u08b_t *blkPtr,size_t blkCnt,size_t byteCntAdd) + { /* do it in C */ + enum + { + WCNT = SKEIN_256_STATE_WORDS + }; +#undef RCNT +#define RCNT (SKEIN_256_ROUNDS_TOTAL/8) + +#ifdef SKEIN_LOOP /* configure how much to unroll the loop */ +#define SKEIN_UNROLL_256 (((SKEIN_LOOP)/100)%10) +#else +#define SKEIN_UNROLL_256 (0) +#endif + +#if SKEIN_UNROLL_256 +#if (RCNT % SKEIN_UNROLL_256) +#error "Invalid SKEIN_UNROLL_256" /* sanity check on unroll count */ +#endif + size_t r; + u64b_t kw[WCNT+4+RCNT*2]; /* key schedule words : chaining vars + tweak + "rotation"*/ +#else + u64b_t kw[WCNT+4]; /* key schedule words : chaining vars + tweak */ +#endif + u64b_t X0,X1,X2,X3; /* local copy of context vars, for speed */ + u64b_t w [WCNT]; /* local copy of input block */ +#ifdef SKEIN_DEBUG + const u64b_t *Xptr[4]; /* use for debugging (help compiler put Xn in registers) */ + Xptr[0] = &X0; Xptr[1] = &X1; Xptr[2] = &X2; Xptr[3] = &X3; +#endif + Skein_assert(blkCnt != 0); /* never call with blkCnt == 0! */ + ts[0] = ctx->h.T[0]; + ts[1] = ctx->h.T[1]; + do { + /* this implementation only supports 2**64 input bytes (no carry out here) */ + ts[0] += byteCntAdd; /* update processed length */ + + /* precompute the key schedule for this block */ + ks[0] = ctx->X[0]; + ks[1] = ctx->X[1]; + ks[2] = ctx->X[2]; + ks[3] = ctx->X[3]; + ks[4] = ks[0] ^ ks[1] ^ ks[2] ^ ks[3] ^ SKEIN_KS_PARITY; + + ts[2] = ts[0] ^ ts[1]; + + Skein_Get64_LSB_First(w,blkPtr,WCNT); /* get input block in little-endian format */ + DebugSaveTweak(ctx); + Skein_Show_Block(BLK_BITS,&ctx->h,ctx->X,blkPtr,w,ks,ts); + + X0 = w[0] + ks[0]; /* do the first full key injection */ + X1 = w[1] + ks[1] + ts[0]; + X2 = w[2] + ks[2] + ts[1]; + X3 = w[3] + ks[3]; + + Skein_Show_R_Ptr(BLK_BITS,&ctx->h,SKEIN_RND_KEY_INITIAL,Xptr); /* show starting state values */ + + blkPtr += SKEIN_256_BLOCK_BYTES; + + /* run the rounds */ + +#define Round256(p0,p1,p2,p3,ROT,rNum) \ + X##p0 += X##p1; X##p1 = RotL_64(X##p1,ROT##_0); X##p1 ^= X##p0; \ + X##p2 += X##p3; X##p3 = RotL_64(X##p3,ROT##_1); X##p3 ^= X##p2; \ + +#if SKEIN_UNROLL_256 == 0 +#define R256(p0,p1,p2,p3,ROT,rNum) /* fully unrolled */ \ + Round256(p0,p1,p2,p3,ROT,rNum) \ + Skein_Show_R_Ptr(BLK_BITS,&ctx->h,rNum,Xptr); + +#define I256(R) \ + X0 += ks[((R)+1) % 5]; /* inject the key schedule value */ \ + X1 += ks[((R)+2) % 5] + ts[((R)+1) % 3]; \ + X2 += ks[((R)+3) % 5] + ts[((R)+2) % 3]; \ + X3 += ks[((R)+4) % 5] + (R)+1; \ + Skein_Show_R_Ptr(BLK_BITS,&ctx->h,SKEIN_RND_KEY_INJECT,Xptr); +#else /* looping version */ +#define R256(p0,p1,p2,p3,ROT,rNum) \ + Round256(p0,p1,p2,p3,ROT,rNum) \ + Skein_Show_R_Ptr(BLK_BITS,&ctx->h,4*(r-1)+rNum,Xptr); + +#define I256(R) \ + X0 += ks[r+(R)+0]; /* inject the key schedule value */ \ + X1 += ks[r+(R)+1] + ts[r+(R)+0]; \ + X2 += ks[r+(R)+2] + ts[r+(R)+1]; \ + X3 += ks[r+(R)+3] + r+(R) ; \ + ks[r + (R)+4 ] = ks[r+(R)-1]; /* rotate key schedule */\ + ts[r + (R)+2 ] = ts[r+(R)-1]; \ + Skein_Show_R_Ptr(BLK_BITS,&ctx->h,SKEIN_RND_KEY_INJECT,Xptr); + + for (r=1;r < 2*RCNT;r+=2*SKEIN_UNROLL_256) /* loop thru it */ +#endif + { +#define R256_8_rounds(R) \ + R256(0,1,2,3,R_256_0,8*(R) + 1); \ + R256(0,3,2,1,R_256_1,8*(R) + 2); \ + R256(0,1,2,3,R_256_2,8*(R) + 3); \ + R256(0,3,2,1,R_256_3,8*(R) + 4); \ + I256(2*(R)); \ + R256(0,1,2,3,R_256_4,8*(R) + 5); \ + R256(0,3,2,1,R_256_5,8*(R) + 6); \ + R256(0,1,2,3,R_256_6,8*(R) + 7); \ + R256(0,3,2,1,R_256_7,8*(R) + 8); \ + I256(2*(R)+1); + + R256_8_rounds( 0); + +#define R256_Unroll_R(NN) ((SKEIN_UNROLL_256 == 0 && SKEIN_256_ROUNDS_TOTAL/8 > (NN)) || (SKEIN_UNROLL_256 > (NN))) + + #if R256_Unroll_R( 1) + R256_8_rounds( 1); + #endif + #if R256_Unroll_R( 2) + R256_8_rounds( 2); + #endif + #if R256_Unroll_R( 3) + R256_8_rounds( 3); + #endif + #if R256_Unroll_R( 4) + R256_8_rounds( 4); + #endif + #if R256_Unroll_R( 5) + R256_8_rounds( 5); + #endif + #if R256_Unroll_R( 6) + R256_8_rounds( 6); + #endif + #if R256_Unroll_R( 7) + R256_8_rounds( 7); + #endif + #if R256_Unroll_R( 8) + R256_8_rounds( 8); + #endif + #if R256_Unroll_R( 9) + R256_8_rounds( 9); + #endif + #if R256_Unroll_R(10) + R256_8_rounds(10); + #endif + #if R256_Unroll_R(11) + R256_8_rounds(11); + #endif + #if R256_Unroll_R(12) + R256_8_rounds(12); + #endif + #if R256_Unroll_R(13) + R256_8_rounds(13); + #endif + #if R256_Unroll_R(14) + R256_8_rounds(14); + #endif + #if (SKEIN_UNROLL_256 > 14) +#error "need more unrolling in Skein_256_Process_Block" + #endif + } + /* do the final "feedforward" xor, update context chaining vars */ + ctx->X[0] = X0 ^ w[0]; + ctx->X[1] = X1 ^ w[1]; + ctx->X[2] = X2 ^ w[2]; + ctx->X[3] = X3 ^ w[3]; + + Skein_Show_Round(BLK_BITS,&ctx->h,SKEIN_RND_FEED_FWD,ctx->X); + + ts[1] &= ~SKEIN_T1_FLAG_FIRST; + } + while (--blkCnt); + ctx->h.T[0] = ts[0]; + ctx->h.T[1] = ts[1]; + } + +#if defined(SKEIN_CODE_SIZE) || defined(SKEIN_PERF) +static size_t Skein_256_Process_Block_CodeSize(void) + { + return ((u08b_t *) Skein_256_Process_Block_CodeSize) - + ((u08b_t *) Skein_256_Process_Block); + } +static uint_t Skein_256_Unroll_Cnt(void) + { + return SKEIN_UNROLL_256; + } +#endif +#endif + +/***************************** Skein_512 ******************************/ +#if !(SKEIN_USE_ASM & 512) +static void Skein_512_Process_Block(Skein_512_Ctxt_t *ctx,const u08b_t *blkPtr,size_t blkCnt,size_t byteCntAdd) + { /* do it in C */ + enum + { + WCNT = SKEIN_512_STATE_WORDS + }; +#undef RCNT +#define RCNT (SKEIN_512_ROUNDS_TOTAL/8) + +#ifdef SKEIN_LOOP /* configure how much to unroll the loop */ +#define SKEIN_UNROLL_512 (((SKEIN_LOOP)/10)%10) +#else +#define SKEIN_UNROLL_512 (0) +#endif + +#if SKEIN_UNROLL_512 +#if (RCNT % SKEIN_UNROLL_512) +#error "Invalid SKEIN_UNROLL_512" /* sanity check on unroll count */ +#endif + size_t r; + u64b_t kw[WCNT+4+RCNT*2]; /* key schedule words : chaining vars + tweak + "rotation"*/ +#else + u64b_t kw[WCNT+4]; /* key schedule words : chaining vars + tweak */ +#endif + u64b_t X0,X1,X2,X3,X4,X5,X6,X7; /* local copy of vars, for speed */ + u64b_t w [WCNT]; /* local copy of input block */ +#ifdef SKEIN_DEBUG + const u64b_t *Xptr[8]; /* use for debugging (help compiler put Xn in registers) */ + Xptr[0] = &X0; Xptr[1] = &X1; Xptr[2] = &X2; Xptr[3] = &X3; + Xptr[4] = &X4; Xptr[5] = &X5; Xptr[6] = &X6; Xptr[7] = &X7; +#endif + + Skein_assert(blkCnt != 0); /* never call with blkCnt == 0! */ + ts[0] = ctx->h.T[0]; + ts[1] = ctx->h.T[1]; + do { + /* this implementation only supports 2**64 input bytes (no carry out here) */ + ts[0] += byteCntAdd; /* update processed length */ + + /* precompute the key schedule for this block */ + ks[0] = ctx->X[0]; + ks[1] = ctx->X[1]; + ks[2] = ctx->X[2]; + ks[3] = ctx->X[3]; + ks[4] = ctx->X[4]; + ks[5] = ctx->X[5]; + ks[6] = ctx->X[6]; + ks[7] = ctx->X[7]; + ks[8] = ks[0] ^ ks[1] ^ ks[2] ^ ks[3] ^ + ks[4] ^ ks[5] ^ ks[6] ^ ks[7] ^ SKEIN_KS_PARITY; + + ts[2] = ts[0] ^ ts[1]; + + Skein_Get64_LSB_First(w,blkPtr,WCNT); /* get input block in little-endian format */ + DebugSaveTweak(ctx); + Skein_Show_Block(BLK_BITS,&ctx->h,ctx->X,blkPtr,w,ks,ts); + + X0 = w[0] + ks[0]; /* do the first full key injection */ + X1 = w[1] + ks[1]; + X2 = w[2] + ks[2]; + X3 = w[3] + ks[3]; + X4 = w[4] + ks[4]; + X5 = w[5] + ks[5] + ts[0]; + X6 = w[6] + ks[6] + ts[1]; + X7 = w[7] + ks[7]; + + blkPtr += SKEIN_512_BLOCK_BYTES; + + Skein_Show_R_Ptr(BLK_BITS,&ctx->h,SKEIN_RND_KEY_INITIAL,Xptr); + /* run the rounds */ +#define Round512(p0,p1,p2,p3,p4,p5,p6,p7,ROT,rNum) \ + X##p0 += X##p1; X##p1 = RotL_64(X##p1,ROT##_0); X##p1 ^= X##p0; \ + X##p2 += X##p3; X##p3 = RotL_64(X##p3,ROT##_1); X##p3 ^= X##p2; \ + X##p4 += X##p5; X##p5 = RotL_64(X##p5,ROT##_2); X##p5 ^= X##p4; \ + X##p6 += X##p7; X##p7 = RotL_64(X##p7,ROT##_3); X##p7 ^= X##p6; \ + +#if SKEIN_UNROLL_512 == 0 +#define R512(p0,p1,p2,p3,p4,p5,p6,p7,ROT,rNum) /* unrolled */ \ + Round512(p0,p1,p2,p3,p4,p5,p6,p7,ROT,rNum) \ + Skein_Show_R_Ptr(BLK_BITS,&ctx->h,rNum,Xptr); + +#define I512(R) \ + X0 += ks[((R)+1) % 9]; /* inject the key schedule value */ \ + X1 += ks[((R)+2) % 9]; \ + X2 += ks[((R)+3) % 9]; \ + X3 += ks[((R)+4) % 9]; \ + X4 += ks[((R)+5) % 9]; \ + X5 += ks[((R)+6) % 9] + ts[((R)+1) % 3]; \ + X6 += ks[((R)+7) % 9] + ts[((R)+2) % 3]; \ + X7 += ks[((R)+8) % 9] + (R)+1; \ + Skein_Show_R_Ptr(BLK_BITS,&ctx->h,SKEIN_RND_KEY_INJECT,Xptr); +#else /* looping version */ +#define R512(p0,p1,p2,p3,p4,p5,p6,p7,ROT,rNum) \ + Round512(p0,p1,p2,p3,p4,p5,p6,p7,ROT,rNum) \ + Skein_Show_R_Ptr(BLK_BITS,&ctx->h,4*(r-1)+rNum,Xptr); + +#define I512(R) \ + X0 += ks[r+(R)+0]; /* inject the key schedule value */ \ + X1 += ks[r+(R)+1]; \ + X2 += ks[r+(R)+2]; \ + X3 += ks[r+(R)+3]; \ + X4 += ks[r+(R)+4]; \ + X5 += ks[r+(R)+5] + ts[r+(R)+0]; \ + X6 += ks[r+(R)+6] + ts[r+(R)+1]; \ + X7 += ks[r+(R)+7] + r+(R) ; \ + ks[r + (R)+8] = ks[r+(R)-1]; /* rotate key schedule */ \ + ts[r + (R)+2] = ts[r+(R)-1]; \ + Skein_Show_R_Ptr(BLK_BITS,&ctx->h,SKEIN_RND_KEY_INJECT,Xptr); + + for (r=1;r < 2*RCNT;r+=2*SKEIN_UNROLL_512) /* loop thru it */ +#endif /* end of looped code definitions */ + { +#define R512_8_rounds(R) /* do 8 full rounds */ \ + R512(0,1,2,3,4,5,6,7,R_512_0,8*(R)+ 1); \ + R512(2,1,4,7,6,5,0,3,R_512_1,8*(R)+ 2); \ + R512(4,1,6,3,0,5,2,7,R_512_2,8*(R)+ 3); \ + R512(6,1,0,7,2,5,4,3,R_512_3,8*(R)+ 4); \ + I512(2*(R)); \ + R512(0,1,2,3,4,5,6,7,R_512_4,8*(R)+ 5); \ + R512(2,1,4,7,6,5,0,3,R_512_5,8*(R)+ 6); \ + R512(4,1,6,3,0,5,2,7,R_512_6,8*(R)+ 7); \ + R512(6,1,0,7,2,5,4,3,R_512_7,8*(R)+ 8); \ + I512(2*(R)+1); /* and key injection */ + + R512_8_rounds( 0); + +#define R512_Unroll_R(NN) ((SKEIN_UNROLL_512 == 0 && SKEIN_512_ROUNDS_TOTAL/8 > (NN)) || (SKEIN_UNROLL_512 > (NN))) + + #if R512_Unroll_R( 1) + R512_8_rounds( 1); + #endif + #if R512_Unroll_R( 2) + R512_8_rounds( 2); + #endif + #if R512_Unroll_R( 3) + R512_8_rounds( 3); + #endif + #if R512_Unroll_R( 4) + R512_8_rounds( 4); + #endif + #if R512_Unroll_R( 5) + R512_8_rounds( 5); + #endif + #if R512_Unroll_R( 6) + R512_8_rounds( 6); + #endif + #if R512_Unroll_R( 7) + R512_8_rounds( 7); + #endif + #if R512_Unroll_R( 8) + R512_8_rounds( 8); + #endif + #if R512_Unroll_R( 9) + R512_8_rounds( 9); + #endif + #if R512_Unroll_R(10) + R512_8_rounds(10); + #endif + #if R512_Unroll_R(11) + R512_8_rounds(11); + #endif + #if R512_Unroll_R(12) + R512_8_rounds(12); + #endif + #if R512_Unroll_R(13) + R512_8_rounds(13); + #endif + #if R512_Unroll_R(14) + R512_8_rounds(14); + #endif + #if (SKEIN_UNROLL_512 > 14) +#error "need more unrolling in Skein_512_Process_Block" + #endif + } + + /* do the final "feedforward" xor, update context chaining vars */ + ctx->X[0] = X0 ^ w[0]; + ctx->X[1] = X1 ^ w[1]; + ctx->X[2] = X2 ^ w[2]; + ctx->X[3] = X3 ^ w[3]; + ctx->X[4] = X4 ^ w[4]; + ctx->X[5] = X5 ^ w[5]; + ctx->X[6] = X6 ^ w[6]; + ctx->X[7] = X7 ^ w[7]; + Skein_Show_Round(BLK_BITS,&ctx->h,SKEIN_RND_FEED_FWD,ctx->X); + + ts[1] &= ~SKEIN_T1_FLAG_FIRST; + } + while (--blkCnt); + ctx->h.T[0] = ts[0]; + ctx->h.T[1] = ts[1]; + } + +#if defined(SKEIN_CODE_SIZE) || defined(SKEIN_PERF) +static size_t Skein_512_Process_Block_CodeSize(void) + { + return ((u08b_t *) Skein_512_Process_Block_CodeSize) - + ((u08b_t *) Skein_512_Process_Block); + } +static uint_t Skein_512_Unroll_Cnt(void) + { + return SKEIN_UNROLL_512; + } +#endif +#endif + +/***************************** Skein1024 ******************************/ +#if !(SKEIN_USE_ASM & 1024) +static void Skein1024_Process_Block(Skein1024_Ctxt_t *ctx,const u08b_t *blkPtr,size_t blkCnt,size_t byteCntAdd) + { /* do it in C, always looping (unrolled is bigger AND slower!) */ + enum + { + WCNT = SKEIN1024_STATE_WORDS + }; +#undef RCNT +#define RCNT (SKEIN1024_ROUNDS_TOTAL/8) + +#ifdef SKEIN_LOOP /* configure how much to unroll the loop */ +#define SKEIN_UNROLL_1024 ((SKEIN_LOOP)%10) +#else +#define SKEIN_UNROLL_1024 (0) +#endif + +#if (SKEIN_UNROLL_1024 != 0) +#if (RCNT % SKEIN_UNROLL_1024) +#error "Invalid SKEIN_UNROLL_1024" /* sanity check on unroll count */ +#endif + size_t r; + u64b_t kw[WCNT+4+RCNT*2]; /* key schedule words : chaining vars + tweak + "rotation"*/ +#else + u64b_t kw[WCNT+4]; /* key schedule words : chaining vars + tweak */ +#endif + + u64b_t X00,X01,X02,X03,X04,X05,X06,X07, /* local copy of vars, for speed */ + X08,X09,X10,X11,X12,X13,X14,X15; + u64b_t w [WCNT]; /* local copy of input block */ +#ifdef SKEIN_DEBUG + const u64b_t *Xptr[16]; /* use for debugging (help compiler put Xn in registers) */ + Xptr[ 0] = &X00; Xptr[ 1] = &X01; Xptr[ 2] = &X02; Xptr[ 3] = &X03; + Xptr[ 4] = &X04; Xptr[ 5] = &X05; Xptr[ 6] = &X06; Xptr[ 7] = &X07; + Xptr[ 8] = &X08; Xptr[ 9] = &X09; Xptr[10] = &X10; Xptr[11] = &X11; + Xptr[12] = &X12; Xptr[13] = &X13; Xptr[14] = &X14; Xptr[15] = &X15; +#endif + + Skein_assert(blkCnt != 0); /* never call with blkCnt == 0! */ + ts[0] = ctx->h.T[0]; + ts[1] = ctx->h.T[1]; + do { + /* this implementation only supports 2**64 input bytes (no carry out here) */ + ts[0] += byteCntAdd; /* update processed length */ + + /* precompute the key schedule for this block */ + ks[ 0] = ctx->X[ 0]; + ks[ 1] = ctx->X[ 1]; + ks[ 2] = ctx->X[ 2]; + ks[ 3] = ctx->X[ 3]; + ks[ 4] = ctx->X[ 4]; + ks[ 5] = ctx->X[ 5]; + ks[ 6] = ctx->X[ 6]; + ks[ 7] = ctx->X[ 7]; + ks[ 8] = ctx->X[ 8]; + ks[ 9] = ctx->X[ 9]; + ks[10] = ctx->X[10]; + ks[11] = ctx->X[11]; + ks[12] = ctx->X[12]; + ks[13] = ctx->X[13]; + ks[14] = ctx->X[14]; + ks[15] = ctx->X[15]; + ks[16] = ks[ 0] ^ ks[ 1] ^ ks[ 2] ^ ks[ 3] ^ + ks[ 4] ^ ks[ 5] ^ ks[ 6] ^ ks[ 7] ^ + ks[ 8] ^ ks[ 9] ^ ks[10] ^ ks[11] ^ + ks[12] ^ ks[13] ^ ks[14] ^ ks[15] ^ SKEIN_KS_PARITY; + + ts[2] = ts[0] ^ ts[1]; + + Skein_Get64_LSB_First(w,blkPtr,WCNT); /* get input block in little-endian format */ + DebugSaveTweak(ctx); + Skein_Show_Block(BLK_BITS,&ctx->h,ctx->X,blkPtr,w,ks,ts); + + X00 = w[ 0] + ks[ 0]; /* do the first full key injection */ + X01 = w[ 1] + ks[ 1]; + X02 = w[ 2] + ks[ 2]; + X03 = w[ 3] + ks[ 3]; + X04 = w[ 4] + ks[ 4]; + X05 = w[ 5] + ks[ 5]; + X06 = w[ 6] + ks[ 6]; + X07 = w[ 7] + ks[ 7]; + X08 = w[ 8] + ks[ 8]; + X09 = w[ 9] + ks[ 9]; + X10 = w[10] + ks[10]; + X11 = w[11] + ks[11]; + X12 = w[12] + ks[12]; + X13 = w[13] + ks[13] + ts[0]; + X14 = w[14] + ks[14] + ts[1]; + X15 = w[15] + ks[15]; + + Skein_Show_R_Ptr(BLK_BITS,&ctx->h,SKEIN_RND_KEY_INITIAL,Xptr); + +#define Round1024(p0,p1,p2,p3,p4,p5,p6,p7,p8,p9,pA,pB,pC,pD,pE,pF,ROT,rNum) \ + X##p0 += X##p1; X##p1 = RotL_64(X##p1,ROT##_0); X##p1 ^= X##p0; \ + X##p2 += X##p3; X##p3 = RotL_64(X##p3,ROT##_1); X##p3 ^= X##p2; \ + X##p4 += X##p5; X##p5 = RotL_64(X##p5,ROT##_2); X##p5 ^= X##p4; \ + X##p6 += X##p7; X##p7 = RotL_64(X##p7,ROT##_3); X##p7 ^= X##p6; \ + X##p8 += X##p9; X##p9 = RotL_64(X##p9,ROT##_4); X##p9 ^= X##p8; \ + X##pA += X##pB; X##pB = RotL_64(X##pB,ROT##_5); X##pB ^= X##pA; \ + X##pC += X##pD; X##pD = RotL_64(X##pD,ROT##_6); X##pD ^= X##pC; \ + X##pE += X##pF; X##pF = RotL_64(X##pF,ROT##_7); X##pF ^= X##pE; \ + +#if SKEIN_UNROLL_1024 == 0 +#define R1024(p0,p1,p2,p3,p4,p5,p6,p7,p8,p9,pA,pB,pC,pD,pE,pF,ROT,rn) \ + Round1024(p0,p1,p2,p3,p4,p5,p6,p7,p8,p9,pA,pB,pC,pD,pE,pF,ROT,rn) \ + Skein_Show_R_Ptr(BLK_BITS,&ctx->h,rn,Xptr); + +#define I1024(R) \ + X00 += ks[((R)+ 1) % 17]; /* inject the key schedule value */ \ + X01 += ks[((R)+ 2) % 17]; \ + X02 += ks[((R)+ 3) % 17]; \ + X03 += ks[((R)+ 4) % 17]; \ + X04 += ks[((R)+ 5) % 17]; \ + X05 += ks[((R)+ 6) % 17]; \ + X06 += ks[((R)+ 7) % 17]; \ + X07 += ks[((R)+ 8) % 17]; \ + X08 += ks[((R)+ 9) % 17]; \ + X09 += ks[((R)+10) % 17]; \ + X10 += ks[((R)+11) % 17]; \ + X11 += ks[((R)+12) % 17]; \ + X12 += ks[((R)+13) % 17]; \ + X13 += ks[((R)+14) % 17] + ts[((R)+1) % 3]; \ + X14 += ks[((R)+15) % 17] + ts[((R)+2) % 3]; \ + X15 += ks[((R)+16) % 17] + (R)+1; \ + Skein_Show_R_Ptr(BLK_BITS,&ctx->h,SKEIN_RND_KEY_INJECT,Xptr); +#else /* looping version */ +#define R1024(p0,p1,p2,p3,p4,p5,p6,p7,p8,p9,pA,pB,pC,pD,pE,pF,ROT,rn) \ + Round1024(p0,p1,p2,p3,p4,p5,p6,p7,p8,p9,pA,pB,pC,pD,pE,pF,ROT,rn) \ + Skein_Show_R_Ptr(BLK_BITS,&ctx->h,4*(r-1)+rn,Xptr); + +#define I1024(R) \ + X00 += ks[r+(R)+ 0]; /* inject the key schedule value */ \ + X01 += ks[r+(R)+ 1]; \ + X02 += ks[r+(R)+ 2]; \ + X03 += ks[r+(R)+ 3]; \ + X04 += ks[r+(R)+ 4]; \ + X05 += ks[r+(R)+ 5]; \ + X06 += ks[r+(R)+ 6]; \ + X07 += ks[r+(R)+ 7]; \ + X08 += ks[r+(R)+ 8]; \ + X09 += ks[r+(R)+ 9]; \ + X10 += ks[r+(R)+10]; \ + X11 += ks[r+(R)+11]; \ + X12 += ks[r+(R)+12]; \ + X13 += ks[r+(R)+13] + ts[r+(R)+0]; \ + X14 += ks[r+(R)+14] + ts[r+(R)+1]; \ + X15 += ks[r+(R)+15] + r+(R) ; \ + ks[r + (R)+16] = ks[r+(R)-1]; /* rotate key schedule */ \ + ts[r + (R)+ 2] = ts[r+(R)-1]; \ + Skein_Show_R_Ptr(BLK_BITS,&ctx->h,SKEIN_RND_KEY_INJECT,Xptr); + + for (r=1;r <= 2*RCNT;r+=2*SKEIN_UNROLL_1024) /* loop thru it */ +#endif + { +#define R1024_8_rounds(R) /* do 8 full rounds */ \ + R1024(00,01,02,03,04,05,06,07,08,09,10,11,12,13,14,15,R1024_0,8*(R) + 1); \ + R1024(00,09,02,13,06,11,04,15,10,07,12,03,14,05,08,01,R1024_1,8*(R) + 2); \ + R1024(00,07,02,05,04,03,06,01,12,15,14,13,08,11,10,09,R1024_2,8*(R) + 3); \ + R1024(00,15,02,11,06,13,04,09,14,01,08,05,10,03,12,07,R1024_3,8*(R) + 4); \ + I1024(2*(R)); \ + R1024(00,01,02,03,04,05,06,07,08,09,10,11,12,13,14,15,R1024_4,8*(R) + 5); \ + R1024(00,09,02,13,06,11,04,15,10,07,12,03,14,05,08,01,R1024_5,8*(R) + 6); \ + R1024(00,07,02,05,04,03,06,01,12,15,14,13,08,11,10,09,R1024_6,8*(R) + 7); \ + R1024(00,15,02,11,06,13,04,09,14,01,08,05,10,03,12,07,R1024_7,8*(R) + 8); \ + I1024(2*(R)+1); + + R1024_8_rounds( 0); + +#define R1024_Unroll_R(NN) ((SKEIN_UNROLL_1024 == 0 && SKEIN1024_ROUNDS_TOTAL/8 > (NN)) || (SKEIN_UNROLL_1024 > (NN))) + + #if R1024_Unroll_R( 1) + R1024_8_rounds( 1); + #endif + #if R1024_Unroll_R( 2) + R1024_8_rounds( 2); + #endif + #if R1024_Unroll_R( 3) + R1024_8_rounds( 3); + #endif + #if R1024_Unroll_R( 4) + R1024_8_rounds( 4); + #endif + #if R1024_Unroll_R( 5) + R1024_8_rounds( 5); + #endif + #if R1024_Unroll_R( 6) + R1024_8_rounds( 6); + #endif + #if R1024_Unroll_R( 7) + R1024_8_rounds( 7); + #endif + #if R1024_Unroll_R( 8) + R1024_8_rounds( 8); + #endif + #if R1024_Unroll_R( 9) + R1024_8_rounds( 9); + #endif + #if R1024_Unroll_R(10) + R1024_8_rounds(10); + #endif + #if R1024_Unroll_R(11) + R1024_8_rounds(11); + #endif + #if R1024_Unroll_R(12) + R1024_8_rounds(12); + #endif + #if R1024_Unroll_R(13) + R1024_8_rounds(13); + #endif + #if R1024_Unroll_R(14) + R1024_8_rounds(14); + #endif + #if (SKEIN_UNROLL_1024 > 14) +#error "need more unrolling in Skein_1024_Process_Block" + #endif + } + /* do the final "feedforward" xor, update context chaining vars */ + + ctx->X[ 0] = X00 ^ w[ 0]; + ctx->X[ 1] = X01 ^ w[ 1]; + ctx->X[ 2] = X02 ^ w[ 2]; + ctx->X[ 3] = X03 ^ w[ 3]; + ctx->X[ 4] = X04 ^ w[ 4]; + ctx->X[ 5] = X05 ^ w[ 5]; + ctx->X[ 6] = X06 ^ w[ 6]; + ctx->X[ 7] = X07 ^ w[ 7]; + ctx->X[ 8] = X08 ^ w[ 8]; + ctx->X[ 9] = X09 ^ w[ 9]; + ctx->X[10] = X10 ^ w[10]; + ctx->X[11] = X11 ^ w[11]; + ctx->X[12] = X12 ^ w[12]; + ctx->X[13] = X13 ^ w[13]; + ctx->X[14] = X14 ^ w[14]; + ctx->X[15] = X15 ^ w[15]; + + Skein_Show_Round(BLK_BITS,&ctx->h,SKEIN_RND_FEED_FWD,ctx->X); + + ts[1] &= ~SKEIN_T1_FLAG_FIRST; + blkPtr += SKEIN1024_BLOCK_BYTES; + } + while (--blkCnt); + ctx->h.T[0] = ts[0]; + ctx->h.T[1] = ts[1]; + } + +#if defined(SKEIN_CODE_SIZE) || defined(SKEIN_PERF) +static size_t Skein1024_Process_Block_CodeSize(void) + { + return ((u08b_t *) Skein1024_Process_Block_CodeSize) - + ((u08b_t *) Skein1024_Process_Block); + } +static uint_t Skein1024_Unroll_Cnt(void) + { + return SKEIN_UNROLL_1024; + } +#endif +#endif + + +#if 0 +/*****************************************************************/ +/* 256-bit Skein */ +/*****************************************************************/ + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* init the context for a straight hashing operation */ +static int Skein_256_Init(Skein_256_Ctxt_t *ctx, size_t hashBitLen) + { + union + { + u08b_t b[SKEIN_256_STATE_BYTES]; + u64b_t w[SKEIN_256_STATE_WORDS]; + } cfg; /* config block */ + + Skein_Assert(hashBitLen > 0,SKEIN_BAD_HASHLEN); + ctx->h.hashBitLen = hashBitLen; /* output hash bit count */ + + switch (hashBitLen) + { /* use pre-computed values, where available */ +#ifndef SKEIN_NO_PRECOMP + case 256: memcpy(ctx->X,SKEIN_256_IV_256,sizeof(ctx->X)); break; + case 224: memcpy(ctx->X,SKEIN_256_IV_224,sizeof(ctx->X)); break; + case 160: memcpy(ctx->X,SKEIN_256_IV_160,sizeof(ctx->X)); break; + case 128: memcpy(ctx->X,SKEIN_256_IV_128,sizeof(ctx->X)); break; +#endif + default: + /* here if there is no precomputed IV value available */ + /* build/process the config block, type == CONFIG (could be precomputed) */ + Skein_Start_New_Type(ctx,CFG_FINAL); /* set tweaks: T0=0; T1=CFG | FINAL */ + + cfg.w[0] = Skein_Swap64(SKEIN_SCHEMA_VER); /* set the schema, version */ + cfg.w[1] = Skein_Swap64(hashBitLen); /* hash result length in bits */ + cfg.w[2] = Skein_Swap64(SKEIN_CFG_TREE_INFO_SEQUENTIAL); + memset(&cfg.w[3],0,sizeof(cfg) - 3*sizeof(cfg.w[0])); /* zero pad config block */ + + /* compute the initial chaining values from config block */ + memset(ctx->X,0,sizeof(ctx->X)); /* zero the chaining variables */ + Skein_256_Process_Block(ctx,cfg.b,1,SKEIN_CFG_STR_LEN); + break; + } + /* The chaining vars ctx->X are now initialized for the given hashBitLen. */ + /* Set up to process the data message portion of the hash (default) */ + Skein_Start_New_Type(ctx,MSG); /* T0=0, T1= MSG type */ + + return SKEIN_SUCCESS; + } + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* init the context for a MAC and/or tree hash operation */ +/* [identical to Skein_256_Init() when keyBytes == 0 && treeInfo == SKEIN_CFG_TREE_INFO_SEQUENTIAL] */ +static int Skein_256_InitExt(Skein_256_Ctxt_t *ctx,size_t hashBitLen,u64b_t treeInfo, const u08b_t *key, size_t keyBytes) + { + union + { + u08b_t b[SKEIN_256_STATE_BYTES]; + u64b_t w[SKEIN_256_STATE_WORDS]; + } cfg; /* config block */ + + Skein_Assert(hashBitLen > 0,SKEIN_BAD_HASHLEN); + Skein_Assert(keyBytes == 0 || key != NULL,SKEIN_FAIL); + + /* compute the initial chaining values ctx->X[], based on key */ + if (keyBytes == 0) /* is there a key? */ + { + memset(ctx->X,0,sizeof(ctx->X)); /* no key: use all zeroes as key for config block */ + } + else /* here to pre-process a key */ + { + Skein_assert(sizeof(cfg.b) >= sizeof(ctx->X)); + /* do a mini-Init right here */ + ctx->h.hashBitLen=8*sizeof(ctx->X); /* set output hash bit count = state size */ + Skein_Start_New_Type(ctx,KEY); /* set tweaks: T0 = 0; T1 = KEY type */ + memset(ctx->X,0,sizeof(ctx->X)); /* zero the initial chaining variables */ + Skein_256_Update(ctx,key,keyBytes); /* hash the key */ + Skein_256_Final_Pad(ctx,cfg.b); /* put result into cfg.b[] */ + memcpy(ctx->X,cfg.b,sizeof(cfg.b)); /* copy over into ctx->X[] */ +#if SKEIN_NEED_SWAP + { + uint_t i; + for (i=0;iX[i] = Skein_Swap64(ctx->X[i]); + } +#endif + } + /* build/process the config block, type == CONFIG (could be precomputed for each key) */ + ctx->h.hashBitLen = hashBitLen; /* output hash bit count */ + Skein_Start_New_Type(ctx,CFG_FINAL); + + memset(&cfg.w,0,sizeof(cfg.w)); /* pre-pad cfg.w[] with zeroes */ + cfg.w[0] = Skein_Swap64(SKEIN_SCHEMA_VER); + cfg.w[1] = Skein_Swap64(hashBitLen); /* hash result length in bits */ + cfg.w[2] = Skein_Swap64(treeInfo); /* tree hash config info (or SKEIN_CFG_TREE_INFO_SEQUENTIAL) */ + + Skein_Show_Key(256,&ctx->h,key,keyBytes); + + /* compute the initial chaining values from config block */ + Skein_256_Process_Block(ctx,cfg.b,1,SKEIN_CFG_STR_LEN); + + /* The chaining vars ctx->X are now initialized */ + /* Set up to process the data message portion of the hash (default) */ + ctx->h.bCnt = 0; /* buffer b[] starts out empty */ + Skein_Start_New_Type(ctx,MSG); + + return SKEIN_SUCCESS; + } +#endif + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* process the input bytes */ +static int Skein_256_Update(Skein_256_Ctxt_t *ctx, const u08b_t *msg, size_t msgByteCnt) + { + size_t n; + + Skein_Assert(ctx->h.bCnt <= SKEIN_256_BLOCK_BYTES,SKEIN_FAIL); /* catch uninitialized context */ + + /* process full blocks, if any */ + if (msgByteCnt + ctx->h.bCnt > SKEIN_256_BLOCK_BYTES) + { + if (ctx->h.bCnt) /* finish up any buffered message data */ + { + n = SKEIN_256_BLOCK_BYTES - ctx->h.bCnt; /* # bytes free in buffer b[] */ + if (n) + { + Skein_assert(n < msgByteCnt); /* check on our logic here */ + memcpy(&ctx->b[ctx->h.bCnt],msg,n); + msgByteCnt -= n; + msg += n; + ctx->h.bCnt += n; + } + Skein_assert(ctx->h.bCnt == SKEIN_256_BLOCK_BYTES); + Skein_256_Process_Block(ctx,ctx->b,1,SKEIN_256_BLOCK_BYTES); + ctx->h.bCnt = 0; + } + /* now process any remaining full blocks, directly from input message data */ + if (msgByteCnt > SKEIN_256_BLOCK_BYTES) + { + n = (msgByteCnt-1) / SKEIN_256_BLOCK_BYTES; /* number of full blocks to process */ + Skein_256_Process_Block(ctx,msg,n,SKEIN_256_BLOCK_BYTES); + msgByteCnt -= n * SKEIN_256_BLOCK_BYTES; + msg += n * SKEIN_256_BLOCK_BYTES; + } + Skein_assert(ctx->h.bCnt == 0); + } + + /* copy any remaining source message data bytes into b[] */ + if (msgByteCnt) + { + Skein_assert(msgByteCnt + ctx->h.bCnt <= SKEIN_256_BLOCK_BYTES); + memcpy(&ctx->b[ctx->h.bCnt],msg,msgByteCnt); + ctx->h.bCnt += msgByteCnt; + } + + return SKEIN_SUCCESS; + } + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* finalize the hash computation and output the result */ +static int Skein_256_Final(Skein_256_Ctxt_t *ctx, u08b_t *hashVal) + { + size_t i,n,byteCnt; + u64b_t X[SKEIN_256_STATE_WORDS]; + Skein_Assert(ctx->h.bCnt <= SKEIN_256_BLOCK_BYTES,SKEIN_FAIL); /* catch uninitialized context */ + + ctx->h.T[1] |= SKEIN_T1_FLAG_FINAL; /* tag as the final block */ + if (ctx->h.bCnt < SKEIN_256_BLOCK_BYTES) /* zero pad b[] if necessary */ + memset(&ctx->b[ctx->h.bCnt],0,SKEIN_256_BLOCK_BYTES - ctx->h.bCnt); + + Skein_256_Process_Block(ctx,ctx->b,1,ctx->h.bCnt); /* process the final block */ + + /* now output the result */ + byteCnt = (ctx->h.hashBitLen + 7) >> 3; /* total number of output bytes */ + + /* run Threefish in "counter mode" to generate output */ + memset(ctx->b,0,sizeof(ctx->b)); /* zero out b[], so it can hold the counter */ + memcpy(X,ctx->X,sizeof(X)); /* keep a local copy of counter mode "key" */ + for (i=0;i*SKEIN_256_BLOCK_BYTES < byteCnt;i++) + { + ((u64b_t *)ctx->b)[0]= Skein_Swap64((u64b_t) i); /* build the counter block */ + Skein_Start_New_Type(ctx,OUT_FINAL); + Skein_256_Process_Block(ctx,ctx->b,1,sizeof(u64b_t)); /* run "counter mode" */ + n = byteCnt - i*SKEIN_256_BLOCK_BYTES; /* number of output bytes left to go */ + if (n >= SKEIN_256_BLOCK_BYTES) + n = SKEIN_256_BLOCK_BYTES; + Skein_Put64_LSB_First(hashVal+i*SKEIN_256_BLOCK_BYTES,ctx->X,n); /* "output" the ctr mode bytes */ + Skein_Show_Final(256,&ctx->h,n,hashVal+i*SKEIN_256_BLOCK_BYTES); + memcpy(ctx->X,X,sizeof(X)); /* restore the counter mode key for next time */ + } + return SKEIN_SUCCESS; + } + +#if defined(SKEIN_CODE_SIZE) || defined(SKEIN_PERF) +static size_t Skein_256_API_CodeSize(void) + { + return ((u08b_t *) Skein_256_API_CodeSize) - + ((u08b_t *) Skein_256_Init); + } +#endif + +/*****************************************************************/ +/* 512-bit Skein */ +/*****************************************************************/ + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* init the context for a straight hashing operation */ +static int Skein_512_Init(Skein_512_Ctxt_t *ctx, size_t hashBitLen) + { + union + { + u08b_t b[SKEIN_512_STATE_BYTES]; + u64b_t w[SKEIN_512_STATE_WORDS]; + } cfg; /* config block */ + + Skein_Assert(hashBitLen > 0,SKEIN_BAD_HASHLEN); + ctx->h.hashBitLen = hashBitLen; /* output hash bit count */ + + switch (hashBitLen) + { /* use pre-computed values, where available */ +#ifndef SKEIN_NO_PRECOMP + case 512: memcpy(ctx->X,SKEIN_512_IV_512,sizeof(ctx->X)); break; + case 384: memcpy(ctx->X,SKEIN_512_IV_384,sizeof(ctx->X)); break; + case 256: memcpy(ctx->X,SKEIN_512_IV_256,sizeof(ctx->X)); break; + case 224: memcpy(ctx->X,SKEIN_512_IV_224,sizeof(ctx->X)); break; +#endif + default: + /* here if there is no precomputed IV value available */ + /* build/process the config block, type == CONFIG (could be precomputed) */ + Skein_Start_New_Type(ctx,CFG_FINAL); /* set tweaks: T0=0; T1=CFG | FINAL */ + + cfg.w[0] = Skein_Swap64(SKEIN_SCHEMA_VER); /* set the schema, version */ + cfg.w[1] = Skein_Swap64(hashBitLen); /* hash result length in bits */ + cfg.w[2] = Skein_Swap64(SKEIN_CFG_TREE_INFO_SEQUENTIAL); + memset(&cfg.w[3],0,sizeof(cfg) - 3*sizeof(cfg.w[0])); /* zero pad config block */ + + /* compute the initial chaining values from config block */ + memset(ctx->X,0,sizeof(ctx->X)); /* zero the chaining variables */ + Skein_512_Process_Block(ctx,cfg.b,1,SKEIN_CFG_STR_LEN); + break; + } + + /* The chaining vars ctx->X are now initialized for the given hashBitLen. */ + /* Set up to process the data message portion of the hash (default) */ + Skein_Start_New_Type(ctx,MSG); /* T0=0, T1= MSG type */ + + return SKEIN_SUCCESS; + } + +#if 0 +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* init the context for a MAC and/or tree hash operation */ +/* [identical to Skein_512_Init() when keyBytes == 0 && treeInfo == SKEIN_CFG_TREE_INFO_SEQUENTIAL] */ +static int Skein_512_InitExt(Skein_512_Ctxt_t *ctx,size_t hashBitLen,u64b_t treeInfo, const u08b_t *key, size_t keyBytes) + { + union + { + u08b_t b[SKEIN_512_STATE_BYTES]; + u64b_t w[SKEIN_512_STATE_WORDS]; + } cfg; /* config block */ + + Skein_Assert(hashBitLen > 0,SKEIN_BAD_HASHLEN); + Skein_Assert(keyBytes == 0 || key != NULL,SKEIN_FAIL); + + /* compute the initial chaining values ctx->X[], based on key */ + if (keyBytes == 0) /* is there a key? */ + { + memset(ctx->X,0,sizeof(ctx->X)); /* no key: use all zeroes as key for config block */ + } + else /* here to pre-process a key */ + { + Skein_assert(sizeof(cfg.b) >= sizeof(ctx->X)); + /* do a mini-Init right here */ + ctx->h.hashBitLen=8*sizeof(ctx->X); /* set output hash bit count = state size */ + Skein_Start_New_Type(ctx,KEY); /* set tweaks: T0 = 0; T1 = KEY type */ + memset(ctx->X,0,sizeof(ctx->X)); /* zero the initial chaining variables */ + Skein_512_Update(ctx,key,keyBytes); /* hash the key */ + Skein_512_Final_Pad(ctx,cfg.b); /* put result into cfg.b[] */ + memcpy(ctx->X,cfg.b,sizeof(cfg.b)); /* copy over into ctx->X[] */ +#if SKEIN_NEED_SWAP + { + uint_t i; + for (i=0;iX[i] = Skein_Swap64(ctx->X[i]); + } +#endif + } + /* build/process the config block, type == CONFIG (could be precomputed for each key) */ + ctx->h.hashBitLen = hashBitLen; /* output hash bit count */ + Skein_Start_New_Type(ctx,CFG_FINAL); + + memset(&cfg.w,0,sizeof(cfg.w)); /* pre-pad cfg.w[] with zeroes */ + cfg.w[0] = Skein_Swap64(SKEIN_SCHEMA_VER); + cfg.w[1] = Skein_Swap64(hashBitLen); /* hash result length in bits */ + cfg.w[2] = Skein_Swap64(treeInfo); /* tree hash config info (or SKEIN_CFG_TREE_INFO_SEQUENTIAL) */ + + Skein_Show_Key(512,&ctx->h,key,keyBytes); + + /* compute the initial chaining values from config block */ + Skein_512_Process_Block(ctx,cfg.b,1,SKEIN_CFG_STR_LEN); + + /* The chaining vars ctx->X are now initialized */ + /* Set up to process the data message portion of the hash (default) */ + ctx->h.bCnt = 0; /* buffer b[] starts out empty */ + Skein_Start_New_Type(ctx,MSG); + + return SKEIN_SUCCESS; + } +#endif + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* process the input bytes */ +static int Skein_512_Update(Skein_512_Ctxt_t *ctx, const u08b_t *msg, size_t msgByteCnt) + { + size_t n; + + Skein_Assert(ctx->h.bCnt <= SKEIN_512_BLOCK_BYTES,SKEIN_FAIL); /* catch uninitialized context */ + + /* process full blocks, if any */ + if (msgByteCnt + ctx->h.bCnt > SKEIN_512_BLOCK_BYTES) + { + if (ctx->h.bCnt) /* finish up any buffered message data */ + { + n = SKEIN_512_BLOCK_BYTES - ctx->h.bCnt; /* # bytes free in buffer b[] */ + if (n) + { + Skein_assert(n < msgByteCnt); /* check on our logic here */ + memcpy(&ctx->b[ctx->h.bCnt],msg,n); + msgByteCnt -= n; + msg += n; + ctx->h.bCnt += n; + } + Skein_assert(ctx->h.bCnt == SKEIN_512_BLOCK_BYTES); + Skein_512_Process_Block(ctx,ctx->b,1,SKEIN_512_BLOCK_BYTES); + ctx->h.bCnt = 0; + } + /* now process any remaining full blocks, directly from input message data */ + if (msgByteCnt > SKEIN_512_BLOCK_BYTES) + { + n = (msgByteCnt-1) / SKEIN_512_BLOCK_BYTES; /* number of full blocks to process */ + Skein_512_Process_Block(ctx,msg,n,SKEIN_512_BLOCK_BYTES); + msgByteCnt -= n * SKEIN_512_BLOCK_BYTES; + msg += n * SKEIN_512_BLOCK_BYTES; + } + Skein_assert(ctx->h.bCnt == 0); + } + + /* copy any remaining source message data bytes into b[] */ + if (msgByteCnt) + { + Skein_assert(msgByteCnt + ctx->h.bCnt <= SKEIN_512_BLOCK_BYTES); + memcpy(&ctx->b[ctx->h.bCnt],msg,msgByteCnt); + ctx->h.bCnt += msgByteCnt; + } + + return SKEIN_SUCCESS; + } + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* finalize the hash computation and output the result */ +static int Skein_512_Final(Skein_512_Ctxt_t *ctx, u08b_t *hashVal) + { + size_t i,n,byteCnt; + u64b_t X[SKEIN_512_STATE_WORDS]; + Skein_Assert(ctx->h.bCnt <= SKEIN_512_BLOCK_BYTES,SKEIN_FAIL); /* catch uninitialized context */ + + ctx->h.T[1] |= SKEIN_T1_FLAG_FINAL; /* tag as the final block */ + if (ctx->h.bCnt < SKEIN_512_BLOCK_BYTES) /* zero pad b[] if necessary */ + memset(&ctx->b[ctx->h.bCnt],0,SKEIN_512_BLOCK_BYTES - ctx->h.bCnt); + + Skein_512_Process_Block(ctx,ctx->b,1,ctx->h.bCnt); /* process the final block */ + + /* now output the result */ + byteCnt = (ctx->h.hashBitLen + 7) >> 3; /* total number of output bytes */ + + /* run Threefish in "counter mode" to generate output */ + memset(ctx->b,0,sizeof(ctx->b)); /* zero out b[], so it can hold the counter */ + memcpy(X,ctx->X,sizeof(X)); /* keep a local copy of counter mode "key" */ + for (i=0;i*SKEIN_512_BLOCK_BYTES < byteCnt;i++) + { + ((u64b_t *)ctx->b)[0]= Skein_Swap64((u64b_t) i); /* build the counter block */ + Skein_Start_New_Type(ctx,OUT_FINAL); + Skein_512_Process_Block(ctx,ctx->b,1,sizeof(u64b_t)); /* run "counter mode" */ + n = byteCnt - i*SKEIN_512_BLOCK_BYTES; /* number of output bytes left to go */ + if (n >= SKEIN_512_BLOCK_BYTES) + n = SKEIN_512_BLOCK_BYTES; + Skein_Put64_LSB_First(hashVal+i*SKEIN_512_BLOCK_BYTES,ctx->X,n); /* "output" the ctr mode bytes */ + Skein_Show_Final(512,&ctx->h,n,hashVal+i*SKEIN_512_BLOCK_BYTES); + memcpy(ctx->X,X,sizeof(X)); /* restore the counter mode key for next time */ + } + return SKEIN_SUCCESS; + } + +#if defined(SKEIN_CODE_SIZE) || defined(SKEIN_PERF) +static size_t Skein_512_API_CodeSize(void) + { + return ((u08b_t *) Skein_512_API_CodeSize) - + ((u08b_t *) Skein_512_Init); + } +#endif + +/*****************************************************************/ +/* 1024-bit Skein */ +/*****************************************************************/ +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* init the context for a straight hashing operation */ +static int Skein1024_Init(Skein1024_Ctxt_t *ctx, size_t hashBitLen) + { + union + { + u08b_t b[SKEIN1024_STATE_BYTES]; + u64b_t w[SKEIN1024_STATE_WORDS]; + } cfg; /* config block */ + + Skein_Assert(hashBitLen > 0,SKEIN_BAD_HASHLEN); + ctx->h.hashBitLen = hashBitLen; /* output hash bit count */ + + switch (hashBitLen) + { /* use pre-computed values, where available */ +#ifndef SKEIN_NO_PRECOMP + case 512: memcpy(ctx->X,SKEIN1024_IV_512 ,sizeof(ctx->X)); break; + case 384: memcpy(ctx->X,SKEIN1024_IV_384 ,sizeof(ctx->X)); break; + case 1024: memcpy(ctx->X,SKEIN1024_IV_1024,sizeof(ctx->X)); break; +#endif + default: + /* here if there is no precomputed IV value available */ + /* build/process the config block, type == CONFIG (could be precomputed) */ + Skein_Start_New_Type(ctx,CFG_FINAL); /* set tweaks: T0=0; T1=CFG | FINAL */ + + cfg.w[0] = Skein_Swap64(SKEIN_SCHEMA_VER); /* set the schema, version */ + cfg.w[1] = Skein_Swap64(hashBitLen); /* hash result length in bits */ + cfg.w[2] = Skein_Swap64(SKEIN_CFG_TREE_INFO_SEQUENTIAL); + memset(&cfg.w[3],0,sizeof(cfg) - 3*sizeof(cfg.w[0])); /* zero pad config block */ + + /* compute the initial chaining values from config block */ + memset(ctx->X,0,sizeof(ctx->X)); /* zero the chaining variables */ + Skein1024_Process_Block(ctx,cfg.b,1,SKEIN_CFG_STR_LEN); + break; + } + + /* The chaining vars ctx->X are now initialized for the given hashBitLen. */ + /* Set up to process the data message portion of the hash (default) */ + Skein_Start_New_Type(ctx,MSG); /* T0=0, T1= MSG type */ + + return SKEIN_SUCCESS; + } + +#if 0 +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* init the context for a MAC and/or tree hash operation */ +/* [identical to Skein1024_Init() when keyBytes == 0 && treeInfo == SKEIN_CFG_TREE_INFO_SEQUENTIAL] */ +static int Skein1024_InitExt(Skein1024_Ctxt_t *ctx,size_t hashBitLen,u64b_t treeInfo, const u08b_t *key, size_t keyBytes) + { + union + { + u08b_t b[SKEIN1024_STATE_BYTES]; + u64b_t w[SKEIN1024_STATE_WORDS]; + } cfg; /* config block */ + + Skein_Assert(hashBitLen > 0,SKEIN_BAD_HASHLEN); + Skein_Assert(keyBytes == 0 || key != NULL,SKEIN_FAIL); + + /* compute the initial chaining values ctx->X[], based on key */ + if (keyBytes == 0) /* is there a key? */ + { + memset(ctx->X,0,sizeof(ctx->X)); /* no key: use all zeroes as key for config block */ + } + else /* here to pre-process a key */ + { + Skein_assert(sizeof(cfg.b) >= sizeof(ctx->X)); + /* do a mini-Init right here */ + ctx->h.hashBitLen=8*sizeof(ctx->X); /* set output hash bit count = state size */ + Skein_Start_New_Type(ctx,KEY); /* set tweaks: T0 = 0; T1 = KEY type */ + memset(ctx->X,0,sizeof(ctx->X)); /* zero the initial chaining variables */ + Skein1024_Update(ctx,key,keyBytes); /* hash the key */ + Skein1024_Final_Pad(ctx,cfg.b); /* put result into cfg.b[] */ + memcpy(ctx->X,cfg.b,sizeof(cfg.b)); /* copy over into ctx->X[] */ +#if SKEIN_NEED_SWAP + { + uint_t i; + for (i=0;iX[i] = Skein_Swap64(ctx->X[i]); + } +#endif + } + /* build/process the config block, type == CONFIG (could be precomputed for each key) */ + ctx->h.hashBitLen = hashBitLen; /* output hash bit count */ + Skein_Start_New_Type(ctx,CFG_FINAL); + + memset(&cfg.w,0,sizeof(cfg.w)); /* pre-pad cfg.w[] with zeroes */ + cfg.w[0] = Skein_Swap64(SKEIN_SCHEMA_VER); + cfg.w[1] = Skein_Swap64(hashBitLen); /* hash result length in bits */ + cfg.w[2] = Skein_Swap64(treeInfo); /* tree hash config info (or SKEIN_CFG_TREE_INFO_SEQUENTIAL) */ + + Skein_Show_Key(1024,&ctx->h,key,keyBytes); + + /* compute the initial chaining values from config block */ + Skein1024_Process_Block(ctx,cfg.b,1,SKEIN_CFG_STR_LEN); + + /* The chaining vars ctx->X are now initialized */ + /* Set up to process the data message portion of the hash (default) */ + ctx->h.bCnt = 0; /* buffer b[] starts out empty */ + Skein_Start_New_Type(ctx,MSG); + + return SKEIN_SUCCESS; + } +#endif + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* process the input bytes */ +static int Skein1024_Update(Skein1024_Ctxt_t *ctx, const u08b_t *msg, size_t msgByteCnt) + { + size_t n; + + Skein_Assert(ctx->h.bCnt <= SKEIN1024_BLOCK_BYTES,SKEIN_FAIL); /* catch uninitialized context */ + + /* process full blocks, if any */ + if (msgByteCnt + ctx->h.bCnt > SKEIN1024_BLOCK_BYTES) + { + if (ctx->h.bCnt) /* finish up any buffered message data */ + { + n = SKEIN1024_BLOCK_BYTES - ctx->h.bCnt; /* # bytes free in buffer b[] */ + if (n) + { + Skein_assert(n < msgByteCnt); /* check on our logic here */ + memcpy(&ctx->b[ctx->h.bCnt],msg,n); + msgByteCnt -= n; + msg += n; + ctx->h.bCnt += n; + } + Skein_assert(ctx->h.bCnt == SKEIN1024_BLOCK_BYTES); + Skein1024_Process_Block(ctx,ctx->b,1,SKEIN1024_BLOCK_BYTES); + ctx->h.bCnt = 0; + } + /* now process any remaining full blocks, directly from input message data */ + if (msgByteCnt > SKEIN1024_BLOCK_BYTES) + { + n = (msgByteCnt-1) / SKEIN1024_BLOCK_BYTES; /* number of full blocks to process */ + Skein1024_Process_Block(ctx,msg,n,SKEIN1024_BLOCK_BYTES); + msgByteCnt -= n * SKEIN1024_BLOCK_BYTES; + msg += n * SKEIN1024_BLOCK_BYTES; + } + Skein_assert(ctx->h.bCnt == 0); + } + + /* copy any remaining source message data bytes into b[] */ + if (msgByteCnt) + { + Skein_assert(msgByteCnt + ctx->h.bCnt <= SKEIN1024_BLOCK_BYTES); + memcpy(&ctx->b[ctx->h.bCnt],msg,msgByteCnt); + ctx->h.bCnt += msgByteCnt; + } + + return SKEIN_SUCCESS; + } + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* finalize the hash computation and output the result */ +static int Skein1024_Final(Skein1024_Ctxt_t *ctx, u08b_t *hashVal) + { + size_t i,n,byteCnt; + u64b_t X[SKEIN1024_STATE_WORDS]; + Skein_Assert(ctx->h.bCnt <= SKEIN1024_BLOCK_BYTES,SKEIN_FAIL); /* catch uninitialized context */ + + ctx->h.T[1] |= SKEIN_T1_FLAG_FINAL; /* tag as the final block */ + if (ctx->h.bCnt < SKEIN1024_BLOCK_BYTES) /* zero pad b[] if necessary */ + memset(&ctx->b[ctx->h.bCnt],0,SKEIN1024_BLOCK_BYTES - ctx->h.bCnt); + + Skein1024_Process_Block(ctx,ctx->b,1,ctx->h.bCnt); /* process the final block */ + + /* now output the result */ + byteCnt = (ctx->h.hashBitLen + 7) >> 3; /* total number of output bytes */ + + /* run Threefish in "counter mode" to generate output */ + memset(ctx->b,0,sizeof(ctx->b)); /* zero out b[], so it can hold the counter */ + memcpy(X,ctx->X,sizeof(X)); /* keep a local copy of counter mode "key" */ + for (i=0;i*SKEIN1024_BLOCK_BYTES < byteCnt;i++) + { + ((u64b_t *)ctx->b)[0]= Skein_Swap64((u64b_t) i); /* build the counter block */ + Skein_Start_New_Type(ctx,OUT_FINAL); + Skein1024_Process_Block(ctx,ctx->b,1,sizeof(u64b_t)); /* run "counter mode" */ + n = byteCnt - i*SKEIN1024_BLOCK_BYTES; /* number of output bytes left to go */ + if (n >= SKEIN1024_BLOCK_BYTES) + n = SKEIN1024_BLOCK_BYTES; + Skein_Put64_LSB_First(hashVal+i*SKEIN1024_BLOCK_BYTES,ctx->X,n); /* "output" the ctr mode bytes */ + Skein_Show_Final(1024,&ctx->h,n,hashVal+i*SKEIN1024_BLOCK_BYTES); + memcpy(ctx->X,X,sizeof(X)); /* restore the counter mode key for next time */ + } + return SKEIN_SUCCESS; + } + +#if defined(SKEIN_CODE_SIZE) || defined(SKEIN_PERF) +static size_t Skein1024_API_CodeSize(void) + { + return ((u08b_t *) Skein1024_API_CodeSize) - + ((u08b_t *) Skein1024_Init); + } +#endif + +/**************** Functions to support MAC/tree hashing ***************/ +/* (this code is identical for Optimized and Reference versions) */ + +#if 0 +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* finalize the hash computation and output the block, no OUTPUT stage */ +static int Skein_256_Final_Pad(Skein_256_Ctxt_t *ctx, u08b_t *hashVal) + { + Skein_Assert(ctx->h.bCnt <= SKEIN_256_BLOCK_BYTES,SKEIN_FAIL); /* catch uninitialized context */ + + ctx->h.T[1] |= SKEIN_T1_FLAG_FINAL; /* tag as the final block */ + if (ctx->h.bCnt < SKEIN_256_BLOCK_BYTES) /* zero pad b[] if necessary */ + memset(&ctx->b[ctx->h.bCnt],0,SKEIN_256_BLOCK_BYTES - ctx->h.bCnt); + Skein_256_Process_Block(ctx,ctx->b,1,ctx->h.bCnt); /* process the final block */ + + Skein_Put64_LSB_First(hashVal,ctx->X,SKEIN_256_BLOCK_BYTES); /* "output" the state bytes */ + + return SKEIN_SUCCESS; + } + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* finalize the hash computation and output the block, no OUTPUT stage */ +static int Skein_512_Final_Pad(Skein_512_Ctxt_t *ctx, u08b_t *hashVal) + { + Skein_Assert(ctx->h.bCnt <= SKEIN_512_BLOCK_BYTES,SKEIN_FAIL); /* catch uninitialized context */ + + ctx->h.T[1] |= SKEIN_T1_FLAG_FINAL; /* tag as the final block */ + if (ctx->h.bCnt < SKEIN_512_BLOCK_BYTES) /* zero pad b[] if necessary */ + memset(&ctx->b[ctx->h.bCnt],0,SKEIN_512_BLOCK_BYTES - ctx->h.bCnt); + Skein_512_Process_Block(ctx,ctx->b,1,ctx->h.bCnt); /* process the final block */ + + Skein_Put64_LSB_First(hashVal,ctx->X,SKEIN_512_BLOCK_BYTES); /* "output" the state bytes */ + + return SKEIN_SUCCESS; + } + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* finalize the hash computation and output the block, no OUTPUT stage */ +static int Skein1024_Final_Pad(Skein1024_Ctxt_t *ctx, u08b_t *hashVal) + { + Skein_Assert(ctx->h.bCnt <= SKEIN1024_BLOCK_BYTES,SKEIN_FAIL); /* catch uninitialized context */ + + ctx->h.T[1] |= SKEIN_T1_FLAG_FINAL; /* tag as the final block */ + if (ctx->h.bCnt < SKEIN1024_BLOCK_BYTES) /* zero pad b[] if necessary */ + memset(&ctx->b[ctx->h.bCnt],0,SKEIN1024_BLOCK_BYTES - ctx->h.bCnt); + Skein1024_Process_Block(ctx,ctx->b,1,ctx->h.bCnt); /* process the final block */ + + Skein_Put64_LSB_First(hashVal,ctx->X,SKEIN1024_BLOCK_BYTES); /* "output" the state bytes */ + + return SKEIN_SUCCESS; + } + + +#if SKEIN_TREE_HASH +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* just do the OUTPUT stage */ +static int Skein_256_Output(Skein_256_Ctxt_t *ctx, u08b_t *hashVal) + { + size_t i,n,byteCnt; + u64b_t X[SKEIN_256_STATE_WORDS]; + Skein_Assert(ctx->h.bCnt <= SKEIN_256_BLOCK_BYTES,SKEIN_FAIL); /* catch uninitialized context */ + + /* now output the result */ + byteCnt = (ctx->h.hashBitLen + 7) >> 3; /* total number of output bytes */ + + /* run Threefish in "counter mode" to generate output */ + memset(ctx->b,0,sizeof(ctx->b)); /* zero out b[], so it can hold the counter */ + memcpy(X,ctx->X,sizeof(X)); /* keep a local copy of counter mode "key" */ + for (i=0;i*SKEIN_256_BLOCK_BYTES < byteCnt;i++) + { + ((u64b_t *)ctx->b)[0]= Skein_Swap64((u64b_t) i); /* build the counter block */ + Skein_Start_New_Type(ctx,OUT_FINAL); + Skein_256_Process_Block(ctx,ctx->b,1,sizeof(u64b_t)); /* run "counter mode" */ + n = byteCnt - i*SKEIN_256_BLOCK_BYTES; /* number of output bytes left to go */ + if (n >= SKEIN_256_BLOCK_BYTES) + n = SKEIN_256_BLOCK_BYTES; + Skein_Put64_LSB_First(hashVal+i*SKEIN_256_BLOCK_BYTES,ctx->X,n); /* "output" the ctr mode bytes */ + Skein_Show_Final(256,&ctx->h,n,hashVal+i*SKEIN_256_BLOCK_BYTES); + memcpy(ctx->X,X,sizeof(X)); /* restore the counter mode key for next time */ + } + return SKEIN_SUCCESS; + } + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* just do the OUTPUT stage */ +static int Skein_512_Output(Skein_512_Ctxt_t *ctx, u08b_t *hashVal) + { + size_t i,n,byteCnt; + u64b_t X[SKEIN_512_STATE_WORDS]; + Skein_Assert(ctx->h.bCnt <= SKEIN_512_BLOCK_BYTES,SKEIN_FAIL); /* catch uninitialized context */ + + /* now output the result */ + byteCnt = (ctx->h.hashBitLen + 7) >> 3; /* total number of output bytes */ + + /* run Threefish in "counter mode" to generate output */ + memset(ctx->b,0,sizeof(ctx->b)); /* zero out b[], so it can hold the counter */ + memcpy(X,ctx->X,sizeof(X)); /* keep a local copy of counter mode "key" */ + for (i=0;i*SKEIN_512_BLOCK_BYTES < byteCnt;i++) + { + ((u64b_t *)ctx->b)[0]= Skein_Swap64((u64b_t) i); /* build the counter block */ + Skein_Start_New_Type(ctx,OUT_FINAL); + Skein_512_Process_Block(ctx,ctx->b,1,sizeof(u64b_t)); /* run "counter mode" */ + n = byteCnt - i*SKEIN_512_BLOCK_BYTES; /* number of output bytes left to go */ + if (n >= SKEIN_512_BLOCK_BYTES) + n = SKEIN_512_BLOCK_BYTES; + Skein_Put64_LSB_First(hashVal+i*SKEIN_512_BLOCK_BYTES,ctx->X,n); /* "output" the ctr mode bytes */ + Skein_Show_Final(256,&ctx->h,n,hashVal+i*SKEIN_512_BLOCK_BYTES); + memcpy(ctx->X,X,sizeof(X)); /* restore the counter mode key for next time */ + } + return SKEIN_SUCCESS; + } + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* just do the OUTPUT stage */ +static int Skein1024_Output(Skein1024_Ctxt_t *ctx, u08b_t *hashVal) + { + size_t i,n,byteCnt; + u64b_t X[SKEIN1024_STATE_WORDS]; + Skein_Assert(ctx->h.bCnt <= SKEIN1024_BLOCK_BYTES,SKEIN_FAIL); /* catch uninitialized context */ + + /* now output the result */ + byteCnt = (ctx->h.hashBitLen + 7) >> 3; /* total number of output bytes */ + + /* run Threefish in "counter mode" to generate output */ + memset(ctx->b,0,sizeof(ctx->b)); /* zero out b[], so it can hold the counter */ + memcpy(X,ctx->X,sizeof(X)); /* keep a local copy of counter mode "key" */ + for (i=0;i*SKEIN1024_BLOCK_BYTES < byteCnt;i++) + { + ((u64b_t *)ctx->b)[0]= Skein_Swap64((u64b_t) i); /* build the counter block */ + Skein_Start_New_Type(ctx,OUT_FINAL); + Skein1024_Process_Block(ctx,ctx->b,1,sizeof(u64b_t)); /* run "counter mode" */ + n = byteCnt - i*SKEIN1024_BLOCK_BYTES; /* number of output bytes left to go */ + if (n >= SKEIN1024_BLOCK_BYTES) + n = SKEIN1024_BLOCK_BYTES; + Skein_Put64_LSB_First(hashVal+i*SKEIN1024_BLOCK_BYTES,ctx->X,n); /* "output" the ctr mode bytes */ + Skein_Show_Final(256,&ctx->h,n,hashVal+i*SKEIN1024_BLOCK_BYTES); + memcpy(ctx->X,X,sizeof(X)); /* restore the counter mode key for next time */ + } + return SKEIN_SUCCESS; + } +#endif +#endif + +typedef struct +{ + uint_t statebits; /* 256, 512, or 1024 */ + union + { + Skein_Ctxt_Hdr_t h; /* common header "overlay" */ + Skein_256_Ctxt_t ctx_256; + Skein_512_Ctxt_t ctx_512; + Skein1024_Ctxt_t ctx1024; + } u; +} +hashState; + +/* "incremental" hashing API */ +static HashReturn Init (hashState *state, int hashbitlen); +static HashReturn Update(hashState *state, const BitSequence *data, DataLength databitlen); +static HashReturn Final (hashState *state, BitSequence *hashval); + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* select the context size and init the context */ +static HashReturn Init(hashState *state, int hashbitlen) +{ +#if SKEIN_256_NIST_MAX_HASH_BITS + if (hashbitlen <= SKEIN_256_NIST_MAX_HASHBITS) + { + Skein_Assert(hashbitlen > 0,BAD_HASHLEN); + state->statebits = 64*SKEIN_256_STATE_WORDS; + return Skein_256_Init(&state->u.ctx_256,(size_t) hashbitlen); + } +#endif + if (hashbitlen <= SKEIN_512_NIST_MAX_HASHBITS) + { + state->statebits = 64*SKEIN_512_STATE_WORDS; + return Skein_512_Init(&state->u.ctx_512,(size_t) hashbitlen); + } + else + { + state->statebits = 64*SKEIN1024_STATE_WORDS; + return Skein1024_Init(&state->u.ctx1024,(size_t) hashbitlen); + } +} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* process data to be hashed */ +static HashReturn Update(hashState *state, const BitSequence *data, DataLength databitlen) +{ + /* only the final Update() call is allowed do partial bytes, else assert an error */ + Skein_Assert((state->u.h.T[1] & SKEIN_T1_FLAG_BIT_PAD) == 0 || databitlen == 0, SKEIN_FAIL); + + Skein_Assert(state->statebits % 256 == 0 && (state->statebits-256) < 1024,SKEIN_FAIL); + if ((databitlen & 7) == 0) /* partial bytes? */ + { + switch ((state->statebits >> 8) & 3) + { + case 2: return Skein_512_Update(&state->u.ctx_512,data,databitlen >> 3); + case 1: return Skein_256_Update(&state->u.ctx_256,data,databitlen >> 3); + case 0: return Skein1024_Update(&state->u.ctx1024,data,databitlen >> 3); + default: return SKEIN_FAIL; + } + } + else + { /* handle partial final byte */ + size_t bCnt = (databitlen >> 3) + 1; /* number of bytes to handle (nonzero here!) */ + u08b_t b,mask; + + mask = (u08b_t) (1u << (7 - (databitlen & 7))); /* partial byte bit mask */ + b = (u08b_t) ((data[bCnt-1] & (0-mask)) | mask); /* apply bit padding on final byte */ + + switch ((state->statebits >> 8) & 3) + { + case 2: Skein_512_Update(&state->u.ctx_512,data,bCnt-1); /* process all but the final byte */ + Skein_512_Update(&state->u.ctx_512,&b , 1 ); /* process the (masked) partial byte */ + break; + case 1: Skein_256_Update(&state->u.ctx_256,data,bCnt-1); /* process all but the final byte */ + Skein_256_Update(&state->u.ctx_256,&b , 1 ); /* process the (masked) partial byte */ + break; + case 0: Skein1024_Update(&state->u.ctx1024,data,bCnt-1); /* process all but the final byte */ + Skein1024_Update(&state->u.ctx1024,&b , 1 ); /* process the (masked) partial byte */ + break; + default: return SKEIN_FAIL; + } + Skein_Set_Bit_Pad_Flag(state->u.h); /* set tweak flag for the final call */ + + return SKEIN_SUCCESS; + } +} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* finalize hash computation and output the result (hashbitlen bits) */ +static HashReturn Final(hashState *state, BitSequence *hashval) +{ + Skein_Assert(state->statebits % 256 == 0 && (state->statebits-256) < 1024,FAIL); + switch ((state->statebits >> 8) & 3) + { + case 2: return Skein_512_Final(&state->u.ctx_512,hashval); + case 1: return Skein_256_Final(&state->u.ctx_256,hashval); + case 0: return Skein1024_Final(&state->u.ctx1024,hashval); + default: return SKEIN_FAIL; + } +} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* all-in-one hash function */ +HashReturn skein_hash(int hashbitlen, const BitSequence *data, /* all-in-one call */ + DataLength databitlen,BitSequence *hashval) +{ + hashState state; + HashReturn r = Init(&state,hashbitlen); + if (r == SKEIN_SUCCESS) + { /* these calls do not fail when called properly */ + r = Update(&state,data,databitlen); + Final(&state,hashval); + } + return r; +} diff --git a/src/crypto/skein.h b/src/crypto/skein.h new file mode 100644 index 000000000..5c9cc5518 --- /dev/null +++ b/src/crypto/skein.h @@ -0,0 +1,47 @@ +#ifndef _SKEIN_H_ +#define _SKEIN_H_ 1 +/************************************************************************** +** +** Interface declarations and internal definitions for Skein hashing. +** +** Source code author: Doug Whiting, 2008. +** +** This algorithm and source code is released to the public domain. +** +*************************************************************************** +** +** The following compile-time switches may be defined to control some +** tradeoffs between speed, code size, error checking, and security. +** +** The "default" note explains what happens when the switch is not defined. +** +** SKEIN_DEBUG -- make callouts from inside Skein code +** to examine/display intermediate values. +** [default: no callouts (no overhead)] +** +** SKEIN_ERR_CHECK -- how error checking is handled inside Skein +** code. If not defined, most error checking +** is disabled (for performance). Otherwise, +** the switch value is interpreted as: +** 0: use assert() to flag errors +** 1: return SKEIN_FAIL to flag errors +** +***************************************************************************/ +#include "skein_port.h" /* get platform-specific definitions */ + +typedef enum +{ + SKEIN_SUCCESS = 0, /* return codes from Skein calls */ + SKEIN_FAIL = 1, + SKEIN_BAD_HASHLEN = 2 +} +HashReturn; + +typedef size_t DataLength; /* bit count type */ +typedef u08b_t BitSequence; /* bit stream type */ + +/* "all-in-one" call */ +HashReturn skein_hash(int hashbitlen, const BitSequence *data, + DataLength databitlen, BitSequence *hashval); + +#endif /* ifndef _SKEIN_H_ */ diff --git a/src/crypto/skein_port.h b/src/crypto/skein_port.h new file mode 100644 index 000000000..bd47065ad --- /dev/null +++ b/src/crypto/skein_port.h @@ -0,0 +1,190 @@ +#ifndef _SKEIN_PORT_H_ +#define _SKEIN_PORT_H_ + +#include +#include + +#ifndef RETURN_VALUES +# define RETURN_VALUES +# if defined( DLL_EXPORT ) +# if defined( _MSC_VER ) || defined ( __INTEL_COMPILER ) +# define VOID_RETURN __declspec( dllexport ) void __stdcall +# define INT_RETURN __declspec( dllexport ) int __stdcall +# elif defined( __GNUC__ ) +# define VOID_RETURN __declspec( __dllexport__ ) void +# define INT_RETURN __declspec( __dllexport__ ) int +# else +# error Use of the DLL is only available on the Microsoft, Intel and GCC compilers +# endif +# elif defined( DLL_IMPORT ) +# if defined( _MSC_VER ) || defined ( __INTEL_COMPILER ) +# define VOID_RETURN __declspec( dllimport ) void __stdcall +# define INT_RETURN __declspec( dllimport ) int __stdcall +# elif defined( __GNUC__ ) +# define VOID_RETURN __declspec( __dllimport__ ) void +# define INT_RETURN __declspec( __dllimport__ ) int +# else +# error Use of the DLL is only available on the Microsoft, Intel and GCC compilers +# endif +# elif defined( __WATCOMC__ ) +# define VOID_RETURN void __cdecl +# define INT_RETURN int __cdecl +# else +# define VOID_RETURN void +# define INT_RETURN int +# endif +#endif + +/* These defines are used to declare buffers in a way that allows + faster operations on longer variables to be used. In all these + defines 'size' must be a power of 2 and >= 8 + + dec_unit_type(size,x) declares a variable 'x' of length + 'size' bits + + dec_bufr_type(size,bsize,x) declares a buffer 'x' of length 'bsize' + bytes defined as an array of variables + each of 'size' bits (bsize must be a + multiple of size / 8) + + ptr_cast(x,size) casts a pointer to a pointer to a + varaiable of length 'size' bits +*/ + +#define ui_type(size) uint##size##_t +#define dec_unit_type(size,x) typedef ui_type(size) x +#define dec_bufr_type(size,bsize,x) typedef ui_type(size) x[bsize / (size >> 3)] +#define ptr_cast(x,size) ((ui_type(size)*)(x)) + +typedef unsigned int uint_t; /* native unsigned integer */ +typedef uint8_t u08b_t; /* 8-bit unsigned integer */ +typedef uint64_t u64b_t; /* 64-bit unsigned integer */ + +#ifndef RotL_64 +#define RotL_64(x,N) (((x) << (N)) | ((x) >> (64-(N)))) +#endif + +/* + * Skein is "natively" little-endian (unlike SHA-xxx), for optimal + * performance on x86 CPUs. The Skein code requires the following + * definitions for dealing with endianness: + * + * SKEIN_NEED_SWAP: 0 for little-endian, 1 for big-endian + * Skein_Put64_LSB_First + * Skein_Get64_LSB_First + * Skein_Swap64 + * + * If SKEIN_NEED_SWAP is defined at compile time, it is used here + * along with the portable versions of Put64/Get64/Swap64, which + * are slow in general. + * + * Otherwise, an "auto-detect" of endianness is attempted below. + * If the default handling doesn't work well, the user may insert + * platform-specific code instead (e.g., for big-endian CPUs). + * + */ +#ifndef SKEIN_NEED_SWAP /* compile-time "override" for endianness? */ + + +#include "common/int-util.h" + +#define IS_BIG_ENDIAN 4321 /* byte 0 is most significant (mc68k) */ +#define IS_LITTLE_ENDIAN 1234 /* byte 0 is least significant (i386) */ + +#if BYTE_ORDER == LITTLE_ENDIAN +# define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN +#endif + +#if BYTE_ORDER == BIG_ENDIAN +# define PLATFORM_BYTE_ORDER IS_BIG_ENDIAN +#endif + +/* special handler for IA64, which may be either endianness (?) */ +/* here we assume little-endian, but this may need to be changed */ +#if defined(__ia64) || defined(__ia64__) || defined(_M_IA64) +# define PLATFORM_MUST_ALIGN (1) +#ifndef PLATFORM_BYTE_ORDER +# define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN +#endif +#endif + +#ifndef PLATFORM_MUST_ALIGN +# define PLATFORM_MUST_ALIGN (0) +#endif + + +#if PLATFORM_BYTE_ORDER == IS_BIG_ENDIAN + /* here for big-endian CPUs */ +#define SKEIN_NEED_SWAP (1) +#elif PLATFORM_BYTE_ORDER == IS_LITTLE_ENDIAN + /* here for x86 and x86-64 CPUs (and other detected little-endian CPUs) */ +#define SKEIN_NEED_SWAP (0) +#if PLATFORM_MUST_ALIGN == 0 /* ok to use "fast" versions? */ +#define Skein_Put64_LSB_First(dst08,src64,bCnt) memcpy(dst08,src64,bCnt) +#define Skein_Get64_LSB_First(dst64,src08,wCnt) memcpy(dst64,src08,8*(wCnt)) +#endif +#else +#error "Skein needs endianness setting!" +#endif + +#endif /* ifndef SKEIN_NEED_SWAP */ + +/* + ****************************************************************** + * Provide any definitions still needed. + ****************************************************************** + */ +#ifndef Skein_Swap64 /* swap for big-endian, nop for little-endian */ +#if SKEIN_NEED_SWAP +#define Skein_Swap64(w64) \ + ( (( ((u64b_t)(w64)) & 0xFF) << 56) | \ + (((((u64b_t)(w64)) >> 8) & 0xFF) << 48) | \ + (((((u64b_t)(w64)) >>16) & 0xFF) << 40) | \ + (((((u64b_t)(w64)) >>24) & 0xFF) << 32) | \ + (((((u64b_t)(w64)) >>32) & 0xFF) << 24) | \ + (((((u64b_t)(w64)) >>40) & 0xFF) << 16) | \ + (((((u64b_t)(w64)) >>48) & 0xFF) << 8) | \ + (((((u64b_t)(w64)) >>56) & 0xFF) ) ) +#else +#define Skein_Swap64(w64) (w64) +#endif +#endif /* ifndef Skein_Swap64 */ + + +#ifndef Skein_Put64_LSB_First +void Skein_Put64_LSB_First(u08b_t *dst,const u64b_t *src,size_t bCnt) +#ifdef SKEIN_PORT_CODE /* instantiate the function code here? */ + { /* this version is fully portable (big-endian or little-endian), but slow */ + size_t n; + + for (n=0;n>3] >> (8*(n&7))); + } +#else + ; /* output only the function prototype */ +#endif +#endif /* ifndef Skein_Put64_LSB_First */ + + +#ifndef Skein_Get64_LSB_First +void Skein_Get64_LSB_First(u64b_t *dst,const u08b_t *src,size_t wCnt) +#ifdef SKEIN_PORT_CODE /* instantiate the function code here? */ + { /* this version is fully portable (big-endian or little-endian), but slow */ + size_t n; + + for (n=0;n<8*wCnt;n+=8) + dst[n/8] = (((u64b_t) src[n ]) ) + + (((u64b_t) src[n+1]) << 8) + + (((u64b_t) src[n+2]) << 16) + + (((u64b_t) src[n+3]) << 24) + + (((u64b_t) src[n+4]) << 32) + + (((u64b_t) src[n+5]) << 40) + + (((u64b_t) src[n+6]) << 48) + + (((u64b_t) src[n+7]) << 56) ; + } +#else + ; /* output only the function prototype */ +#endif +#endif /* ifndef Skein_Get64_LSB_First */ + +#endif /* ifndef _SKEIN_PORT_H_ */ diff --git a/src/crypto/slow-hash.c b/src/crypto/slow-hash.c new file mode 100644 index 000000000..c7264bd96 --- /dev/null +++ b/src/crypto/slow-hash.c @@ -0,0 +1,153 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include +#include +#include +#include + +#include "common/int-util.h" +#include "hash-ops.h" +#include "oaes_lib.h" + +static void (*const extra_hashes[4])(const void *, size_t, char *) = { + hash_extra_blake, hash_extra_groestl, hash_extra_jh, hash_extra_skein +}; + +#define MEMORY (1 << 21) /* 2 MiB */ +#define ITER (1 << 20) +#define AES_BLOCK_SIZE 16 +#define AES_KEY_SIZE 32 /*16*/ +#define INIT_SIZE_BLK 8 +#define INIT_SIZE_BYTE (INIT_SIZE_BLK * AES_BLOCK_SIZE) + +static size_t e2i(const uint8_t* a, size_t count) { return (*((uint64_t*)a) / AES_BLOCK_SIZE) & (count - 1); } + +static void mul(const uint8_t* a, const uint8_t* b, uint8_t* res) { + uint64_t a0, b0; + uint64_t hi, lo; + + a0 = SWAP64LE(((uint64_t*)a)[0]); + b0 = SWAP64LE(((uint64_t*)b)[0]); + lo = mul128(a0, b0, &hi); + ((uint64_t*)res)[0] = SWAP64LE(hi); + ((uint64_t*)res)[1] = SWAP64LE(lo); +} + +static void sum_half_blocks(uint8_t* a, const uint8_t* b) { + uint64_t a0, a1, b0, b1; + + a0 = SWAP64LE(((uint64_t*)a)[0]); + a1 = SWAP64LE(((uint64_t*)a)[1]); + b0 = SWAP64LE(((uint64_t*)b)[0]); + b1 = SWAP64LE(((uint64_t*)b)[1]); + a0 += b0; + a1 += b1; + ((uint64_t*)a)[0] = SWAP64LE(a0); + ((uint64_t*)a)[1] = SWAP64LE(a1); +} + +static void copy_block(uint8_t* dst, const uint8_t* src) { + memcpy(dst, src, AES_BLOCK_SIZE); +} + +static void swap_blocks(uint8_t* a, uint8_t* b) { + size_t i; + uint8_t t; + for (i = 0; i < AES_BLOCK_SIZE; i++) { + t = a[i]; + a[i] = b[i]; + b[i] = t; + } +} + +static void xor_blocks(uint8_t* a, const uint8_t* b) { + size_t i; + for (i = 0; i < AES_BLOCK_SIZE; i++) { + a[i] ^= b[i]; + } +} + +#pragma pack(push, 1) +union cn_slow_hash_state { + union hash_state hs; + struct { + uint8_t k[64]; + uint8_t init[INIT_SIZE_BYTE]; + }; +}; +#pragma pack(pop) + +void cn_slow_hash(const void *data, size_t length, char *hash) { + uint8_t long_state[MEMORY]; + union cn_slow_hash_state state; + uint8_t text[INIT_SIZE_BYTE]; + uint8_t a[AES_BLOCK_SIZE]; + uint8_t b[AES_BLOCK_SIZE]; + uint8_t c[AES_BLOCK_SIZE]; + uint8_t d[AES_BLOCK_SIZE]; + size_t i, j; + uint8_t aes_key[AES_KEY_SIZE]; + OAES_CTX* aes_ctx; + + hash_process(&state.hs, data, length); + memcpy(text, state.init, INIT_SIZE_BYTE); + memcpy(aes_key, state.hs.b, AES_KEY_SIZE); + aes_ctx = oaes_alloc(); + for (i = 0; i < MEMORY / INIT_SIZE_BYTE; i++) { + for (j = 0; j < INIT_SIZE_BLK; j++) { + oaes_key_import_data(aes_ctx, aes_key, AES_KEY_SIZE); + oaes_pseudo_encrypt_ecb(aes_ctx, &text[AES_BLOCK_SIZE * j]); + /*memcpy(aes_key, &text[AES_BLOCK_SIZE * j], AES_KEY_SIZE);*/ + memcpy(aes_key, state.hs.b, AES_KEY_SIZE); + } + memcpy(&long_state[i * INIT_SIZE_BYTE], text, INIT_SIZE_BYTE); + } + + for (i = 0; i < 16; i++) { + a[i] = state.k[ i] ^ state.k[32 + i]; + b[i] = state.k[16 + i] ^ state.k[48 + i]; + } + + for (i = 0; i < ITER / 2; i++) { + /* Dependency chain: address -> read value ------+ + * written value <-+ hard function (AES or MUL) <+ + * next address <-+ + */ + /* Iteration 1 */ + j = e2i(a, MEMORY / AES_BLOCK_SIZE); + copy_block(c, &long_state[j * AES_BLOCK_SIZE]); + oaes_encryption_round(a, c); + xor_blocks(b, c); + swap_blocks(b, c); + copy_block(&long_state[j * AES_BLOCK_SIZE], c); + assert(j == e2i(a, MEMORY / AES_BLOCK_SIZE)); + swap_blocks(a, b); + /* Iteration 2 */ + j = e2i(a, MEMORY / AES_BLOCK_SIZE); + copy_block(c, &long_state[j * AES_BLOCK_SIZE]); + mul(a, c, d); + sum_half_blocks(b, d); + swap_blocks(b, c); + xor_blocks(b, c); + copy_block(&long_state[j * AES_BLOCK_SIZE], c); + assert(j == e2i(a, MEMORY / AES_BLOCK_SIZE)); + swap_blocks(a, b); + } + + memcpy(text, state.init, INIT_SIZE_BYTE); + for (i = 0; i < MEMORY / INIT_SIZE_BYTE; i++) { + for (j = 0; j < INIT_SIZE_BLK; j++) { + /*oaes_key_import_data(aes_ctx, &long_state[i * INIT_SIZE_BYTE + j * AES_BLOCK_SIZE], AES_KEY_SIZE);*/ + oaes_key_import_data(aes_ctx, &state.hs.b[32], AES_KEY_SIZE); + xor_blocks(&text[j * AES_BLOCK_SIZE], &long_state[i * INIT_SIZE_BYTE + j * AES_BLOCK_SIZE]); + oaes_pseudo_encrypt_ecb(aes_ctx, &text[j * AES_BLOCK_SIZE]); + } + } + memcpy(state.init, text, INIT_SIZE_BYTE); + hash_permutation(&state.hs); + /*memcpy(hash, &state, 32);*/ + extra_hashes[state.hs.b[0] & 3](&state, 200, hash); + oaes_free(&aes_ctx); +} diff --git a/src/crypto/tree-hash.c b/src/crypto/tree-hash.c new file mode 100644 index 000000000..a2b0eeaa5 --- /dev/null +++ b/src/crypto/tree-hash.c @@ -0,0 +1,40 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include +#include +#include +#include + +#include "hash-ops.h" + +void tree_hash(const char (*hashes)[HASH_SIZE], size_t count, char *root_hash) { + assert(count > 0); + if (count == 1) { + memcpy(root_hash, hashes, HASH_SIZE); + } else if (count == 2) { + cn_fast_hash(hashes, 2 * HASH_SIZE, root_hash); + } else { + size_t i, j; + size_t cnt = count - 1; + char (*ints)[HASH_SIZE]; + for (i = 1; i < sizeof(size_t); i <<= 1) { + cnt |= cnt >> i; + } + cnt &= ~(cnt >> 1); + ints = alloca(cnt * HASH_SIZE); + memcpy(ints, hashes, (2 * cnt - count) * HASH_SIZE); + for (i = 2 * cnt - count, j = 2 * cnt - count; j < cnt; i += 2, ++j) { + cn_fast_hash(hashes[i], 64, ints[j]); + } + assert(i == count); + while (cnt > 2) { + cnt >>= 1; + for (i = 0, j = 0; j < cnt; i += 2, ++j) { + cn_fast_hash(ints[i], 64, ints[j]); + } + } + cn_fast_hash(ints[0], 64, root_hash); + } +} diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h new file mode 100644 index 000000000..55efb1508 --- /dev/null +++ b/src/cryptonote_config.h @@ -0,0 +1,82 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#define CRYPTONOTE_MAX_BLOCK_NUMBER 500000000 +#define CRYPTONOTE_MAX_BLOCK_SIZE 500000000 // block header blob limit, never used! +#define CRYPTONOTE_MAX_TX_SIZE 1000000000 +#define CRYPTONOTE_PUBLIC_ADDRESS_TEXTBLOB_VER 0 +#define CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX 6 // addresses start with "2" +#define CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW 10 +#define CURRENT_TRANSACTION_VERSION 1 +#define CURRENT_BLOCK_MAJOR_VERSION 1 +#define CURRENT_BLOCK_MINOR_VERSION 0 +#define CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT 60*60*2 + +#define BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW 60 + +// MONEY_SUPPLY - total number coins to be generated +#define MONEY_SUPPLY ((uint64_t)(-1)) +// COIN - number of smallest units in one coin +#define COIN ((uint64_t)1000000000000) // pow(10, 12) +#define DEFAULT_FEE ((uint64_t)1000000) // pow(10, 6) + +#define CRYPTONOTE_REWARD_BLOCKS_WINDOW 100 +#define CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE 10000 //size of block (bytes) after which reward for block calculated using block size +#define CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE 600 +#define CRYPTONOTE_DISPLAY_DECIMAL_POINT 8 + + +#define ORPHANED_BLOCKS_MAX_COUNT 100 + + +#define DIFFICULTY_TARGET 120 // seconds +#define DIFFICULTY_WINDOW 720 // blocks +#define DIFFICULTY_LAG 15 // !!! +#define DIFFICULTY_CUT 60 // timestamps to cut after sorting +#define DIFFICULTY_BLOCKS_COUNT DIFFICULTY_WINDOW + DIFFICULTY_LAG + + +#define CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS DIFFICULTY_TARGET * CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_BLOCKS +#define CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_BLOCKS 1 + + +#define DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN DIFFICULTY_TARGET //just alias + + +#define BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT 10000 //by default, blocks ids count in synchronizing +#define BLOCKS_SYNCHRONIZING_DEFAULT_COUNT 200 //by default, blocks count in blocks downloading +#define CRYPTONOTE_PROTOCOL_HOP_RELAX_COUNT 3 //value of hop, after which we use only announce of new block + + +#define P2P_DEFAULT_PORT 8080 +#define RPC_DEFAULT_PORT 8081 +#define COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT 1000 + +#define P2P_LOCAL_WHITE_PEERLIST_LIMIT 1000 +#define P2P_LOCAL_GRAY_PEERLIST_LIMIT 5000 + +#define P2P_DEFAULT_CONNECTIONS_COUNT 8 +#define P2P_DEFAULT_HANDSHAKE_INTERVAL 60 //secondes +#define P2P_DEFAULT_PACKET_MAX_SIZE 50000000 //50000000 bytes maximum packet size +#define P2P_DEFAULT_PEERS_IN_HANDSHAKE 250 +#define P2P_DEFAULT_CONNECTION_TIMEOUT 5000 //5 seconds +#define P2P_DEFAULT_PING_CONNECTION_TIMEOUT 2000 //2 seconds +#define P2P_DEFAULT_INVOKE_TIMEOUT 20*1000 //2 minutes +#define P2P_DEFAULT_HANDSHAKE_INVOKE_TIMEOUT 5000 //5 seconds +#define P2P_STAT_TRUSTED_PUB_KEY "8f80f9a5a434a9f1510d13336228debfee9c918ce505efe225d8c94d045fa115" +#define P2P_DEFAULT_WHITELIST_CONNECTIONS_PERCENT 70 + +#define ALLOW_DEBUG_COMMANDS + +#define CRYPTONOTE_NAME "bytecoin" +#define CRYPTONOTE_POOLDATA_FILENAME "poolstate.bin" +#define CRYPTONOTE_BLOCKCHAINDATA_FILENAME "blockchain.bin" +#define CRYPTONOTE_BLOCKCHAINDATA_TEMP_FILENAME "blockchain.bin.tmp" +#define P2P_NET_DATA_FILENAME "p2pstate.bin" +#define MINER_CONFIG_FILE_NAME "miner_conf.json" + + + diff --git a/src/cryptonote_core/account.cpp b/src/cryptonote_core/account.cpp new file mode 100644 index 000000000..ba39b9b77 --- /dev/null +++ b/src/cryptonote_core/account.cpp @@ -0,0 +1,50 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include +#include +#include + +#include "include_base_utils.h" +#include "account.h" +#include "warnings.h" +#include "crypto/crypto.h" +#include "cryptonote_core/cryptonote_basic_impl.h" +#include "cryptonote_core/cryptonote_format_utils.h" +using namespace std; + +DISABLE_VS_WARNINGS(4244 4345) + + namespace cryptonote +{ + //----------------------------------------------------------------- + account_base::account_base() + { + set_null(); + } + //----------------------------------------------------------------- + void account_base::set_null() + { + m_keys = account_keys(); + } + //----------------------------------------------------------------- + void account_base::generate() + { + generate_keys(m_keys.m_account_address.m_spend_public_key, m_keys.m_spend_secret_key); + generate_keys(m_keys.m_account_address.m_view_public_key, m_keys.m_view_secret_key); + m_creation_timestamp = time(NULL); + } + //----------------------------------------------------------------- + const account_keys& account_base::get_keys() const + { + return m_keys; + } + //----------------------------------------------------------------- + std::string account_base::get_public_address_str() + { + //TODO: change this code into base 58 + return get_account_address_as_str(m_keys.m_account_address); + } + //----------------------------------------------------------------- +} diff --git a/src/cryptonote_core/account.h b/src/cryptonote_core/account.h new file mode 100644 index 000000000..8b525da97 --- /dev/null +++ b/src/cryptonote_core/account.h @@ -0,0 +1,61 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include "cryptonote_core/cryptonote_basic.h" +#include "crypto/crypto.h" +#include "serialization/keyvalue_serialization.h" + +namespace cryptonote +{ + + struct account_keys + { + account_public_address m_account_address; + crypto::secret_key m_spend_secret_key; + crypto::secret_key m_view_secret_key; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(m_account_address) + KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(m_spend_secret_key) + KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(m_view_secret_key) + END_KV_SERIALIZE_MAP() + }; + + /************************************************************************/ + /* */ + /************************************************************************/ + class account_base + { + public: + account_base(); + void generate(); + const account_keys& get_keys() const; + std::string get_public_address_str(); + + uint64_t get_createtime() const { return m_creation_timestamp; } + void set_createtime(uint64_t val) { m_creation_timestamp = val; } + + bool load(const std::string& file_path); + bool store(const std::string& file_path); + + template + inline void serialize(t_archive &a, const unsigned int /*ver*/) + { + a & m_keys; + a & m_creation_timestamp; + } + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(m_keys) + KV_SERIALIZE(m_creation_timestamp) + END_KV_SERIALIZE_MAP() + + private: + void set_null(); + account_keys m_keys; + uint64_t m_creation_timestamp; + }; +} diff --git a/src/cryptonote_core/account_boost_serialization.h b/src/cryptonote_core/account_boost_serialization.h new file mode 100644 index 000000000..9cc36d14a --- /dev/null +++ b/src/cryptonote_core/account_boost_serialization.h @@ -0,0 +1,31 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include "account.h" +#include "cryptonote_core/cryptonote_boost_serialization.h" + +//namespace cryptonote { +namespace boost +{ + namespace serialization + { + template + inline void serialize(Archive &a, cryptonote::account_keys &x, const boost::serialization::version_type ver) + { + a & x.m_account_address; + a & x.m_spend_secret_key; + a & x.m_view_secret_key; + } + + template + inline void serialize(Archive &a, cryptonote::account_public_address &x, const boost::serialization::version_type ver) + { + a & x.m_spend_public_key; + a & x.m_view_public_key; + } + + } +} diff --git a/src/cryptonote_core/blockchain_storage.cpp b/src/cryptonote_core/blockchain_storage.cpp new file mode 100644 index 000000000..1ec186652 --- /dev/null +++ b/src/cryptonote_core/blockchain_storage.cpp @@ -0,0 +1,1633 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include +#include +#include +#include + +#include "include_base_utils.h" +#include "cryptonote_basic_impl.h" +#include "blockchain_storage.h" +#include "cryptonote_format_utils.h" +#include "cryptonote_boost_serialization.h" +#include "blockchain_storage_boost_serialization.h" +#include "cryptonote_config.h" +#include "miner.h" +#include "misc_language.h" +#include "profile_tools.h" +#include "file_io_utils.h" +#include "common/boost_serialization_helper.h" +#include "warnings.h" +#include "crypto/hash.h" +//#include "serialization/json_archive.h" + +using namespace std; +using namespace epee; +using namespace cryptonote; + +DISABLE_VS_WARNINGS(4267) + +//------------------------------------------------------------------ +bool blockchain_storage::have_tx(const crypto::hash &id) +{ + CRITICAL_REGION_LOCAL(m_blockchain_lock); + return m_transactions.find(id) != m_transactions.end(); +} +//------------------------------------------------------------------ +bool blockchain_storage::have_tx_keyimg_as_spent(const crypto::key_image &key_im) +{ + CRITICAL_REGION_LOCAL(m_blockchain_lock); + return m_spent_keys.find(key_im) != m_spent_keys.end(); +} +//------------------------------------------------------------------ +transaction *blockchain_storage::get_tx(const crypto::hash &id) +{ + auto it = m_transactions.find(id); + if (it == m_transactions.end()) + return NULL; + + return &it->second.tx; +} +//------------------------------------------------------------------ +uint64_t blockchain_storage::get_current_blockchain_height() +{ + CRITICAL_REGION_LOCAL(m_blockchain_lock); + return m_blocks.size(); +} +//------------------------------------------------------------------ +bool blockchain_storage::init(const std::string& config_folder) +{ + CRITICAL_REGION_LOCAL(m_blockchain_lock); + m_config_folder = config_folder; + LOG_PRINT_L0("Loading blockchain..."); + const std::string filename = m_config_folder + "/" CRYPTONOTE_BLOCKCHAINDATA_FILENAME; + if(!tools::unserialize_obj_from_file(*this, filename)) + { + const std::string temp_filename = m_config_folder + "/" CRYPTONOTE_BLOCKCHAINDATA_TEMP_FILENAME; + if(tools::unserialize_obj_from_file(*this, temp_filename)) + { + LOG_PRINT_L0("Blockchain storage loaded from temporary file"); + std::error_code ec = tools::replace_file(temp_filename, filename); + if (ec) + { + LOG_ERROR("Failed to rename blockchain data file " << temp_filename << " to " << filename << ": " << ec.message() << ':' << ec.value()); + return false; + } + } + else + { + LOG_PRINT_L0("Blockchain storage file not found, generating genesis block."); + block bl = boost::value_initialized(); + block_verification_context bvc = boost::value_initialized(); + generate_genesis_block(bl); + add_new_block(bl, bvc); + CHECK_AND_ASSERT_MES(!bvc.m_verifivation_failed && bvc.m_added_to_main_chain, false, "Failed to add genesis block to blockchain"); + } + } + if(!m_blocks.size()) + { + LOG_PRINT_L0("Blockchain not loaded, generating genesis block."); + block bl = boost::value_initialized(); + block_verification_context bvc = boost::value_initialized(); + generate_genesis_block(bl); + add_new_block(bl, bvc); + CHECK_AND_ASSERT_MES(!bvc.m_verifivation_failed, false, "Failed to add genesis block to blockchain"); + } + uint64_t timestamp_diff = time(NULL) - m_blocks.back().bl.timestamp; + if(!m_blocks.back().bl.timestamp) + timestamp_diff = time(NULL) - 1341378000; + LOG_PRINT_GREEN("Blockchain initialized. last block: " << m_blocks.size()-1 << ", " << misc_utils::get_time_interval_string(timestamp_diff) << " time ago, current difficulty: " << get_difficulty_for_next_block(), LOG_LEVEL_0); + return true; +} +//------------------------------------------------------------------ +bool blockchain_storage::store_blockchain() +{ + LOG_PRINT_L0("Storing blockchain..."); + if (!tools::create_directories_if_necessary(m_config_folder)) + { + LOG_PRINT_L0("Failed to create data directory: " << m_config_folder); + return false; + } + + const std::string temp_filename = m_config_folder + "/" CRYPTONOTE_BLOCKCHAINDATA_TEMP_FILENAME; + // There is a chance that temp_filename and filename are hardlinks to the same file + std::remove(temp_filename.c_str()); + if(!tools::serialize_obj_to_file(*this, temp_filename)) + { + //achtung! + LOG_ERROR("Failed to save blockchain data to file: " << temp_filename); + return false; + } + const std::string filename = m_config_folder + "/" CRYPTONOTE_BLOCKCHAINDATA_FILENAME; + std::error_code ec = tools::replace_file(temp_filename, filename); + if (ec) + { + LOG_ERROR("Failed to rename blockchain data file " << temp_filename << " to " << filename << ": " << ec.message() << ':' << ec.value()); + return false; + } + LOG_PRINT_L0("Blockchain stored OK."); + return true; +} +//------------------------------------------------------------------ +bool blockchain_storage::deinit() +{ + return store_blockchain(); +} +//------------------------------------------------------------------ +bool blockchain_storage::pop_block_from_blockchain() +{ + CRITICAL_REGION_LOCAL(m_blockchain_lock); + + CHECK_AND_ASSERT_MES(m_blocks.size() > 1, false, "pop_block_from_blockchain: can't pop from blockchain with size = " << m_blocks.size()); + size_t h = m_blocks.size()-1; + block_extended_info& bei = m_blocks[h]; + //crypto::hash id = get_block_hash(bei.bl); + bool r = purge_block_data_from_blockchain(bei.bl, bei.bl.tx_hashes.size()); + CHECK_AND_ASSERT_MES(r, false, "Failed to purge_block_data_from_blockchain for block " << get_block_hash(bei.bl) << " on height " << h); + + //remove from index + auto bl_ind = m_blocks_index.find(get_block_hash(bei.bl)); + CHECK_AND_ASSERT_MES(bl_ind != m_blocks_index.end(), false, "pop_block_from_blockchain: blockchain id not found in index"); + m_blocks_index.erase(bl_ind); + //pop block from core + m_blocks.pop_back(); + m_tx_pool.on_blockchain_dec(m_blocks.size()-1, get_tail_id()); + return true; +} +//------------------------------------------------------------------ +bool blockchain_storage::reset_and_set_genesis_block(const block& b) +{ + CRITICAL_REGION_LOCAL(m_blockchain_lock); + m_transactions.clear(); + m_spent_keys.clear(); + m_blocks.clear(); + m_blocks_index.clear(); + m_alternative_chains.clear(); + m_outputs.clear(); + + block_verification_context bvc = boost::value_initialized(); + add_new_block(b, bvc); + return bvc.m_added_to_main_chain && !bvc.m_verifivation_failed; +} +//------------------------------------------------------------------ +bool blockchain_storage::purge_transaction_keyimages_from_blockchain(const transaction& tx, bool strict_check) +{ + struct purge_transaction_visitor: public boost::static_visitor + { + key_images_container& m_spent_keys; + bool m_strict_check; + purge_transaction_visitor(key_images_container& spent_keys, bool strict_check):m_spent_keys(spent_keys), m_strict_check(strict_check){} + + bool operator()(const txin_to_key& inp) const + { + //const crypto::key_image& ki = inp.k_image; + auto r = m_spent_keys.find(inp.k_image); + if(r != m_spent_keys.end()) + { + m_spent_keys.erase(r); + }else + { + CHECK_AND_ASSERT_MES(!m_strict_check, false, "purge_block_data_from_blockchain: key image in transaction not found"); + } + return true; + } + bool operator()(const txin_gen& inp) const + { + return true; + } + bool operator()(const txin_to_script& tx) const + { + return false; + } + + bool operator()(const txin_to_scripthash& tx) const + { + return false; + } + }; + + BOOST_FOREACH(const txin_v& in, tx.vin) + { + bool r = boost::apply_visitor(purge_transaction_visitor(m_spent_keys, strict_check), in); + CHECK_AND_ASSERT_MES(!strict_check || r, false, "failed to process purge_transaction_visitor"); + } + return true; +} +//------------------------------------------------------------------ +bool blockchain_storage::purge_transaction_from_blockchain(const crypto::hash& tx_id) +{ + auto tx_index_it = m_transactions.find(tx_id); + CHECK_AND_ASSERT_MES(tx_index_it != m_transactions.end(), false, "purge_block_data_from_blockchain: transaction not found in blockchain index!!"); + transaction& tx = tx_index_it->second.tx; + + purge_transaction_keyimages_from_blockchain(tx, true); + + if(!is_coinbase(tx)) + { + cryptonote::tx_verification_context tvc = AUTO_VAL_INIT(tvc); + bool r = m_tx_pool.add_tx(tx, tvc, true); + CHECK_AND_ASSERT_MES(r, false, "purge_block_data_from_blockchain: failed to add transaction to transaction pool"); + } + + bool res = pop_transaction_from_global_index(tx, tx_id); + m_transactions.erase(tx_index_it); + LOG_PRINT_L1("Removed transaction from blockchain history:" << tx_id << ENDL); + return res; +} +//------------------------------------------------------------------ +bool blockchain_storage::purge_block_data_from_blockchain(const block& bl, size_t processed_tx_count) +{ + CRITICAL_REGION_LOCAL(m_blockchain_lock); + + bool res = true; + CHECK_AND_ASSERT_MES(processed_tx_count <= bl.tx_hashes.size(), false, "wrong processed_tx_count in purge_block_data_from_blockchain"); + for(size_t count = 0; count != processed_tx_count; count++) + { + res = purge_transaction_from_blockchain(bl.tx_hashes[(processed_tx_count -1)- count]) && res; + } + + res = purge_transaction_from_blockchain(get_transaction_hash(bl.miner_tx)) && res; + + return res; +} +//------------------------------------------------------------------ +crypto::hash blockchain_storage::get_tail_id(uint64_t& height) +{ + CRITICAL_REGION_LOCAL(m_blockchain_lock); + height = get_current_blockchain_height()-1; + return get_tail_id(); +} +//------------------------------------------------------------------ +crypto::hash blockchain_storage::get_tail_id() +{ + CRITICAL_REGION_LOCAL(m_blockchain_lock); + crypto::hash id = null_hash; + if(m_blocks.size()) + { + get_block_hash(m_blocks.back().bl, id); + } + return id; +} +//------------------------------------------------------------------ +bool blockchain_storage::get_short_chain_history(std::list& ids) +{ + CRITICAL_REGION_LOCAL(m_blockchain_lock); + size_t i = 0; + size_t current_multiplier = 1; + size_t sz = m_blocks.size(); + if(!sz) + return true; + size_t current_back_offset = 1; + bool genesis_included = false; + while(current_back_offset < sz) + { + ids.push_back(get_block_hash(m_blocks[sz-current_back_offset].bl)); + if(sz-current_back_offset == 0) + genesis_included = true; + if(i < 10) + { + ++current_back_offset; + }else + { + current_back_offset += current_multiplier *= 2; + } + ++i; + } + if(!genesis_included) + ids.push_back(get_block_hash(m_blocks[0].bl)); + + return true; +} +//------------------------------------------------------------------ +crypto::hash blockchain_storage::get_block_id_by_height(uint64_t height) +{ + CRITICAL_REGION_LOCAL(m_blockchain_lock); + if(height >= m_blocks.size()) + return null_hash; + + return get_block_hash(m_blocks[height].bl); +} +//------------------------------------------------------------------ +bool blockchain_storage::get_block_by_hash(const crypto::hash &h, block &blk) { + CRITICAL_REGION_LOCAL(m_blockchain_lock); + + // try to find block in main chain + blocks_by_id_index::const_iterator it = m_blocks_index.find(h); + if (m_blocks_index.end() != it) { + blk = m_blocks[it->second].bl; + return true; + } + + // try to find block in alternative chain + blocks_ext_by_hash::const_iterator it_alt = m_alternative_chains.find(h); + if (m_alternative_chains.end() != it_alt) { + blk = it_alt->second.bl; + return true; + } + + return false; +} +//------------------------------------------------------------------ +void blockchain_storage::get_all_known_block_ids(std::list &main, std::list &alt, std::list &invalid) { + CRITICAL_REGION_LOCAL(m_blockchain_lock); + + BOOST_FOREACH(blocks_by_id_index::value_type &v, m_blocks_index) + main.push_back(v.first); + + BOOST_FOREACH(blocks_ext_by_hash::value_type &v, m_alternative_chains) + alt.push_back(v.first); + + BOOST_FOREACH(blocks_ext_by_hash::value_type &v, m_invalid_blocks) + invalid.push_back(v.first); +} +//------------------------------------------------------------------ +difficulty_type blockchain_storage::get_difficulty_for_next_block() +{ + CRITICAL_REGION_LOCAL(m_blockchain_lock); + std::vector timestamps; + std::vector commulative_difficulties; + size_t offset = m_blocks.size() - std::min(m_blocks.size(), static_cast(DIFFICULTY_BLOCKS_COUNT)); + if(!offset) + ++offset;//skip genesis block + for(; offset < m_blocks.size(); offset++) + { + timestamps.push_back(m_blocks[offset].bl.timestamp); + commulative_difficulties.push_back(m_blocks[offset].cumulative_difficulty); + } + return next_difficulty(timestamps, commulative_difficulties); +} +//------------------------------------------------------------------ +bool blockchain_storage::rollback_blockchain_switching(std::list& original_chain, size_t rollback_height) +{ + CRITICAL_REGION_LOCAL(m_blockchain_lock); + //remove failed subchain + for(size_t i = m_blocks.size()-1; i >=rollback_height; i--) + { + bool r = pop_block_from_blockchain(); + CHECK_AND_ASSERT_MES(r, false, "PANIC!!! failed to remove block while chain switching during the rollback!"); + } + //return back original chain + BOOST_FOREACH(auto& bl, original_chain) + { + block_verification_context bvc = boost::value_initialized(); + bool r = handle_block_to_main_chain(bl, bvc); + CHECK_AND_ASSERT_MES(r && bvc.m_added_to_main_chain, false, "PANIC!!! failed to add (again) block while chain switching during the rollback!"); + } + + LOG_PRINT_L0("Rollback success."); + return true; +} +//------------------------------------------------------------------ +bool blockchain_storage::switch_to_alternative_blockchain(std::list& alt_chain) +{ + CRITICAL_REGION_LOCAL(m_blockchain_lock); + CHECK_AND_ASSERT_MES(alt_chain.size(), false, "switch_to_alternative_blockchain: empty chain passed"); + + size_t split_height = alt_chain.front()->second.height; + CHECK_AND_ASSERT_MES(m_blocks.size() > split_height, false, "switch_to_alternative_blockchain: blockchain size is lower than split height"); + + //disconnecting old chain + std::list disconnected_chain; + for(size_t i = m_blocks.size()-1; i >=split_height; i--) + { + block b = m_blocks[i].bl; + bool r = pop_block_from_blockchain(); + CHECK_AND_ASSERT_MES(r, false, "failed to remove block on chain switching"); + disconnected_chain.push_front(b); + } + + //connecting new alternative chain + for(auto alt_ch_iter = alt_chain.begin(); alt_ch_iter != alt_chain.end(); alt_ch_iter++) + { + auto ch_ent = *alt_ch_iter; + block_verification_context bvc = boost::value_initialized(); + bool r = handle_block_to_main_chain(ch_ent->second.bl, bvc); + if(!r || !bvc.m_added_to_main_chain) + { + LOG_PRINT_L0("Failed to switch to alternative blockchain"); + rollback_blockchain_switching(disconnected_chain, split_height); + add_block_as_invalid(ch_ent->second, get_block_hash(ch_ent->second.bl)); + LOG_PRINT_L0("The block was inserted as invalid while connecting new alternative chain, block_id: " << get_block_hash(ch_ent->second.bl)); + m_alternative_chains.erase(ch_ent); + + for(auto alt_ch_to_orph_iter = ++alt_ch_iter; alt_ch_to_orph_iter != alt_chain.end(); alt_ch_to_orph_iter++) + { + //block_verification_context bvc = boost::value_initialized(); + add_block_as_invalid((*alt_ch_iter)->second, (*alt_ch_iter)->first); + m_alternative_chains.erase(*alt_ch_to_orph_iter); + } + return false; + } + } + + //pushing old chain as alternative chain + BOOST_FOREACH(auto& old_ch_ent, disconnected_chain) + { + block_verification_context bvc = boost::value_initialized(); + bool r = handle_alternative_block(old_ch_ent, get_block_hash(old_ch_ent), bvc); + if(!r) + { + LOG_ERROR("Failed to push ex-main chain blocks to alternative chain "); + rollback_blockchain_switching(disconnected_chain, split_height); + return false; + } + } + + //removing all_chain entries from alternative chain + BOOST_FOREACH(auto ch_ent, alt_chain) + { + m_alternative_chains.erase(ch_ent); + } + + LOG_PRINT_GREEN("REORGANIZE SUCCESS! on height: " << split_height << ", new blockchain size: " << m_blocks.size(), LOG_LEVEL_0); + return true; +} +//------------------------------------------------------------------ +difficulty_type blockchain_storage::get_next_difficulty_for_alternative_chain(const std::list& alt_chain, block_extended_info& bei) +{ + std::vector timestamps; + std::vector commulative_difficulties; + if(alt_chain.size()< DIFFICULTY_BLOCKS_COUNT) + { + CRITICAL_REGION_LOCAL(m_blockchain_lock); + size_t main_chain_stop_offset = alt_chain.size() ? alt_chain.front()->second.height : bei.height; + size_t main_chain_count = DIFFICULTY_BLOCKS_COUNT - std::min(static_cast(DIFFICULTY_BLOCKS_COUNT), alt_chain.size()); + main_chain_count = std::min(main_chain_count, main_chain_stop_offset); + size_t main_chain_start_offset = main_chain_stop_offset - main_chain_count; + + if(!main_chain_start_offset) + ++main_chain_start_offset; //skip genesis block + for(; main_chain_start_offset < main_chain_stop_offset; ++main_chain_start_offset) + { + timestamps.push_back(m_blocks[main_chain_start_offset].bl.timestamp); + commulative_difficulties.push_back(m_blocks[main_chain_start_offset].cumulative_difficulty); + } + + CHECK_AND_ASSERT_MES((alt_chain.size() + timestamps.size()) <= DIFFICULTY_BLOCKS_COUNT, false, "Internal error, alt_chain.size()["<< alt_chain.size() + << "] + vtimestampsec.size()[" << timestamps.size() << "] NOT <= DIFFICULTY_WINDOW[]" << DIFFICULTY_BLOCKS_COUNT ); + BOOST_FOREACH(auto it, alt_chain) + { + timestamps.push_back(it->second.bl.timestamp); + commulative_difficulties.push_back(it->second.cumulative_difficulty); + } + }else + { + timestamps.resize(std::min(alt_chain.size(), static_cast(DIFFICULTY_BLOCKS_COUNT))); + commulative_difficulties.resize(std::min(alt_chain.size(), static_cast(DIFFICULTY_BLOCKS_COUNT))); + size_t count = 0; + size_t max_i = timestamps.size()-1; + BOOST_REVERSE_FOREACH(auto it, alt_chain) + { + timestamps[max_i - count] = it->second.bl.timestamp; + commulative_difficulties[max_i - count] = it->second.cumulative_difficulty; + count++; + if(count >= DIFFICULTY_BLOCKS_COUNT) + break; + } + } + return next_difficulty(timestamps, commulative_difficulties); +} +//------------------------------------------------------------------ +bool blockchain_storage::prevalidate_miner_transaction(const block& b, uint64_t height) +{ + CHECK_AND_ASSERT_MES(b.miner_tx.vin.size() == 1, false, "coinbase transaction in the block has no inputs"); + CHECK_AND_ASSERT_MES(b.miner_tx.vin[0].type() == typeid(txin_gen), false, "coinbase transaction in the block has the wrong type"); + if(boost::get(b.miner_tx.vin[0]).height != height) + { + LOG_PRINT_RED_L0("The miner transaction in block has invalid height: " << boost::get(b.miner_tx.vin[0]).height << ", expected: " << height); + return false; + } + CHECK_AND_ASSERT_MES(b.miner_tx.unlock_time == height + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, + false, + "coinbase transaction transaction have wrong unlock time=" << b.miner_tx.unlock_time << ", expected " << height + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW); + + //check outs overflow + if(!check_outs_overflow(b.miner_tx)) + { + LOG_PRINT_RED_L0("miner transaction have money overflow in block " << get_block_hash(b)); + return false; + } + + return true; +} +//------------------------------------------------------------------ +bool blockchain_storage::validate_miner_transaction(const block& b, size_t cumulative_block_size, uint64_t fee, uint64_t& base_reward, uint64_t already_generated_coins) +{ + //validate reward + uint64_t money_in_use = 0; + BOOST_FOREACH(auto& o, b.miner_tx.vout) + money_in_use += o.amount; + + std::vector last_blocks_sizes; + get_last_n_blocks_sizes(last_blocks_sizes, CRYPTONOTE_REWARD_BLOCKS_WINDOW); + bool block_too_big = false; + base_reward = get_block_reward(last_blocks_sizes, cumulative_block_size, block_too_big, already_generated_coins); + if(block_too_big) + { + LOG_PRINT_L0("block size " << cumulative_block_size << " is bigger than allowed for this blockchain"); + return false; + } + if(base_reward + fee < money_in_use) + { + LOG_ERROR("coinbase transaction spend too much money (" << print_money(money_in_use) << "). Block reward is " << print_money(base_reward + fee) << "(" << print_money(base_reward) << "+" << print_money(fee) << ")"); + return false; + } + if(base_reward + fee != money_in_use) + { + LOG_ERROR("coinbase transaction doesn't use full amount of block reward: spent: " + << print_money(money_in_use) << ", block reward " << print_money(base_reward + fee) << "(" << print_money(base_reward) << "+" << print_money(fee) << ")"); + return false; + } + return true; +} +//------------------------------------------------------------------ +bool blockchain_storage::get_backward_blocks_sizes(size_t from_height, std::vector& sz, size_t count) +{ + CRITICAL_REGION_LOCAL(m_blockchain_lock); + CHECK_AND_ASSERT_MES(from_height < m_blocks.size(), false, "Internal error: get_backward_blocks_sizes called with from_height=" << from_height << ", blockchain height = " << m_blocks.size()); + + size_t start_offset = (from_height+1) - std::min((from_height+1), count); + for(size_t i = start_offset; i != from_height+1; i++) + sz.push_back(m_blocks[i].block_cumulative_size); + + return true; +} +//------------------------------------------------------------------ +bool blockchain_storage::get_last_n_blocks_sizes(std::vector& sz, size_t count) +{ + CRITICAL_REGION_LOCAL(m_blockchain_lock); + if(!m_blocks.size()) + return true; + return get_backward_blocks_sizes(m_blocks.size() -1, sz, count); +} +//------------------------------------------------------------------ +uint64_t blockchain_storage::get_current_comulative_blocksize_limit() +{ + return m_current_block_comul_sz_limit; +} +//------------------------------------------------------------------ +bool blockchain_storage::create_block_template(block& b, const account_public_address& miner_address, difficulty_type& diffic, uint64_t& height, const blobdata& ex_nonce) +{ + size_t txs_cumulative_size = 0; + uint64_t fee = 0; + size_t comul_sz_limit = 0; + std::vector sz; + + CRITICAL_REGION_BEGIN(m_blockchain_lock); + get_last_n_blocks_sizes(sz, CRYPTONOTE_REWARD_BLOCKS_WINDOW); + b.major_version = CURRENT_BLOCK_MAJOR_VERSION; + b.minor_version = CURRENT_BLOCK_MINOR_VERSION; + b.prev_id = get_tail_id(); + b.timestamp = time(NULL); + height = m_blocks.size(); + diffic = get_difficulty_for_next_block(); + CHECK_AND_ASSERT_MES(diffic, false, "difficulty owverhead."); + + comul_sz_limit = m_current_block_comul_sz_limit - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE; + + CRITICAL_REGION_END(); + + m_tx_pool.fill_block_template(b, txs_cumulative_size, comul_sz_limit, fee); + + /* + two-phase miner transaction generation: we don't know exact block size until we prepare block, but we don't know reward until we know + block size, so first miner transaction generated with fake amount of money, and with phase we know think we know expected block size + */ + //make blocks coin-base tx looks close to real coinbase tx to get truthful blob size + bool r = construct_miner_tx(height, m_blocks.back().already_generated_coins, miner_address, b.miner_tx, fee, sz, txs_cumulative_size, ex_nonce, 11); + CHECK_AND_ASSERT_MES(r, false, "Failed to construc miner tx, first chance"); +#ifdef _DEBUG + std::list try_val; + try_val.push_back(get_object_blobsize(b.miner_tx)); +#endif + + size_t cumulative_size = txs_cumulative_size + get_object_blobsize(b.miner_tx); + size_t try_count = 0; + for(; try_count != 10; ++try_count) + { + r = construct_miner_tx(height, m_blocks.back().already_generated_coins, miner_address, b.miner_tx, fee, sz, cumulative_size, ex_nonce, 11); +#ifdef _DEBUG + try_val.push_back(get_object_blobsize(b.miner_tx)); +#endif + + CHECK_AND_ASSERT_MES(r, false, "Failed to construc miner tx, second chance"); + size_t coinbase_blob_size = get_object_blobsize(b.miner_tx); + if(coinbase_blob_size > cumulative_size - txs_cumulative_size ) + { + cumulative_size = txs_cumulative_size + coinbase_blob_size; + continue; + }else + { + if(coinbase_blob_size < cumulative_size - txs_cumulative_size ) + { + size_t delta = cumulative_size - txs_cumulative_size - coinbase_blob_size; + b.miner_tx.extra.insert(b.miner_tx.extra.end(), delta, 0); + //here could be 1 byte difference, because of extra field counter is varint, and it can become from 1-byte len to 2-bytes len. + if(cumulative_size != txs_cumulative_size + get_object_blobsize(b.miner_tx)) + { + CHECK_AND_ASSERT_MES(cumulative_size + 1== txs_cumulative_size + get_object_blobsize(b.miner_tx), false, "unexpected case: cumulative_size=" << cumulative_size << " + 1 is not equal txs_cumulative_size=" << txs_cumulative_size << " + get_object_blobsize(b.miner_tx)=" << get_object_blobsize(b.miner_tx) ); + b.miner_tx.extra.resize(b.miner_tx.extra.size() - 1 ); + if(cumulative_size != txs_cumulative_size + get_object_blobsize(b.miner_tx)) + {//fuck, not lucky, -1 makes varint-counter size smaller, in that case we continue to grow with cumulative_size + LOG_PRINT_RED("Miner tx creation have no luck with delta_extra size = " << delta << " and " << delta - 1 , LOG_LEVEL_2); + cumulative_size += delta + 1; + continue; + } + LOG_PRINT_GREEN("Setting extra for block: " << b.miner_tx.extra.size() << ", try_count=" << try_count, LOG_LEVEL_1); + } + } + CHECK_AND_ASSERT_MES(cumulative_size == txs_cumulative_size + get_object_blobsize(b.miner_tx), false, "unexpected case: cumulative_size=" << cumulative_size << " is not equal txs_cumulative_size=" << txs_cumulative_size << " + get_object_blobsize(b.miner_tx)=" << get_object_blobsize(b.miner_tx) ); + return true; + } + } + LOG_ERROR("Failed to create_block_template with " << try_count << " tries"); + return false; +} +//------------------------------------------------------------------ +bool blockchain_storage::complete_timestamps_vector(uint64_t start_top_height, std::vector& timestamps) +{ + if(timestamps.size() >= BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW) + return true; + + size_t need_elements = BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW - timestamps.size(); + CHECK_AND_ASSERT_MES(start_top_height < m_blocks.size(), false, "internal error: passed start_height = " << start_top_height << " not less then m_blocks.size()=" << m_blocks.size()); + size_t stop_offset = start_top_height > need_elements ? start_top_height - need_elements:0; + do + { + timestamps.push_back(m_blocks[start_top_height].bl.timestamp); + if(start_top_height == 0) + break; + --start_top_height; + }while(start_top_height != stop_offset); + return true; +} +//------------------------------------------------------------------ +bool blockchain_storage::handle_alternative_block(const block& b, const crypto::hash& id, block_verification_context& bvc) +{ + CRITICAL_REGION_LOCAL(m_blockchain_lock); + + //block is not related with head of main chain + //first of all - look in alternative chains container + auto it_main_prev = m_blocks_index.find(b.prev_id); + auto it_prev = m_alternative_chains.find(b.prev_id); + if(it_prev != m_alternative_chains.end() || it_main_prev != m_blocks_index.end()) + { + //we have new block in alternative chain + + //build alternative subchain, front -> mainchain, back -> alternative head + blocks_ext_by_hash::iterator alt_it = it_prev; //m_alternative_chains.find() + std::list alt_chain; + std::vector timestamps; + while(alt_it != m_alternative_chains.end()) + { + alt_chain.push_front(alt_it); + timestamps.push_back(alt_it->second.bl.timestamp); + alt_it = m_alternative_chains.find(alt_it->second.bl.prev_id); + } + + if(alt_chain.size()) + { + //make sure that it has right connection to main chain + CHECK_AND_ASSERT_MES(m_blocks.size() > alt_chain.front()->second.height, false, "main blockchain wrong height"); + crypto::hash h = null_hash; + get_block_hash(m_blocks[alt_chain.front()->second.height - 1].bl, h); + CHECK_AND_ASSERT_MES(h == alt_chain.front()->second.bl.prev_id, false, "alternative chain have wrong connection to main chain"); + complete_timestamps_vector(alt_chain.front()->second.height - 1, timestamps); + }else + { + CHECK_AND_ASSERT_MES(it_main_prev != m_blocks_index.end(), false, "internal error: broken imperative condition it_main_prev != m_blocks_index.end()"); + complete_timestamps_vector(it_main_prev->second, timestamps); + } + //check timestamp correct + if(!check_block_timestamp(timestamps, b)) + { + LOG_PRINT_RED_L0("Block with id: " << id + << ENDL << " for alternative chain, have invalid timestamp: " << b.timestamp); + //add_block_as_invalid(b, id);//do not add blocks to invalid storage before proof of work check was passed + bvc.m_verifivation_failed = true; + return false; + } + + block_extended_info bei = boost::value_initialized(); + bei.bl = b; + bei.height = alt_chain.size() ? it_prev->second.height + 1 : it_main_prev->second + 1; + difficulty_type current_diff = get_next_difficulty_for_alternative_chain(alt_chain, bei); + CHECK_AND_ASSERT_MES(current_diff, false, "!!!!!!! DIFFICULTY OVERHEAD !!!!!!!"); + crypto::hash proof_of_work = null_hash; + if(!m_checkpoints.is_in_checkpoint_zone(bei.height)) + { + m_is_in_checkpoint_zone = false; + get_block_longhash(bei.bl, proof_of_work, bei.height); + + if(!check_hash(proof_of_work, current_diff)) + { + LOG_PRINT_RED_L0("Block with id: " << id + << ENDL << " for alternative chain, have not enough proof of work: " << proof_of_work + << ENDL << " expected difficulty: " << current_diff); + bvc.m_verifivation_failed = true; + return false; + } + }else + { + m_is_in_checkpoint_zone = true; + if(!m_checkpoints.check_block(bei.height, id)) + { + LOG_ERROR("CHECKPOINT VALIDATION FAILED"); + bvc.m_verifivation_failed = true; + return false; + } + } + + if(!prevalidate_miner_transaction(b, bei.height)) + { + LOG_PRINT_RED_L0("Block with id: " << string_tools::pod_to_hex(id) + << " (as alternative) have wrong miner transaction."); + bvc.m_verifivation_failed = true; + return false; + + } + + bei.cumulative_difficulty = alt_chain.size() ? it_prev->second.cumulative_difficulty: m_blocks[it_main_prev->second].cumulative_difficulty; + bei.cumulative_difficulty += current_diff; + +#ifdef _DEBUG + auto i_dres = m_alternative_chains.find(id); + CHECK_AND_ASSERT_MES(i_dres == m_alternative_chains.end(), false, "insertion of new alternative block returned as it already exist"); +#endif + auto i_res = m_alternative_chains.insert(blocks_ext_by_hash::value_type(id, bei)); + CHECK_AND_ASSERT_MES(i_res.second, false, "insertion of new alternative block returned as it already exist"); + alt_chain.push_back(i_res.first); + //check if difficulty bigger then in main chain + if(m_blocks.back().cumulative_difficulty < bei.cumulative_difficulty) + { + //do reorganize! + LOG_PRINT_GREEN("###### REORGANIZE on height: " << alt_chain.front()->second.height << " of " << m_blocks.size() -1 << " with cum_difficulty " << m_blocks.back().cumulative_difficulty + << ENDL << " alternative blockchain size: " << alt_chain.size() << " with cum_difficulty " << bei.cumulative_difficulty, LOG_LEVEL_0); + bool r = switch_to_alternative_blockchain(alt_chain); + if(r) bvc.m_added_to_main_chain = true; + else bvc.m_verifivation_failed = true; + return r; + } + LOG_PRINT_BLUE("----- BLOCK ADDED AS ALTERNATIVE ON HEIGHT " << bei.height + << ENDL << "id:\t" << id + << ENDL << "PoW:\t" << proof_of_work + << ENDL << "difficulty:\t" << current_diff, LOG_LEVEL_0); + return true; + }else + { + //block orphaned + bvc.m_marked_as_orphaned = true; + LOG_PRINT_RED_L0("Block recognized as orphaned and rejected, id = " << id); + } + + + return true; +} +//------------------------------------------------------------------ +bool blockchain_storage::get_blocks(uint64_t start_offset, size_t count, std::list& blocks, std::list& txs) +{ + CRITICAL_REGION_LOCAL(m_blockchain_lock); + if(start_offset >= m_blocks.size()) + return false; + for(size_t i = start_offset; i < start_offset + count && i < m_blocks.size();i++) + { + blocks.push_back(m_blocks[i].bl); + std::list missed_ids; + get_transactions(m_blocks[i].bl.tx_hashes, txs, missed_ids); + CHECK_AND_ASSERT_MES(!missed_ids.size(), false, "have missed transactions in own block in main blockchain"); + } + + return true; +} +//------------------------------------------------------------------ +bool blockchain_storage::get_blocks(uint64_t start_offset, size_t count, std::list& blocks) +{ + CRITICAL_REGION_LOCAL(m_blockchain_lock); + if(start_offset >= m_blocks.size()) + return false; + + for(size_t i = start_offset; i < start_offset + count && i < m_blocks.size();i++) + blocks.push_back(m_blocks[i].bl); + return true; +} +//------------------------------------------------------------------ +bool blockchain_storage::handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NOTIFY_RESPONSE_GET_OBJECTS::request& rsp) +{ + CRITICAL_REGION_LOCAL(m_blockchain_lock); + rsp.current_blockchain_height = get_current_blockchain_height(); + std::list blocks; + get_blocks(arg.blocks, blocks, rsp.missed_ids); + + BOOST_FOREACH(const auto& bl, blocks) + { + std::list missed_tx_id; + std::list txs; + get_transactions(bl.tx_hashes, txs, rsp.missed_ids); + CHECK_AND_ASSERT_MES(!missed_tx_id.size(), false, "Internal error: have missed missed_tx_id.size()=" << missed_tx_id.size() + << ENDL << "for block id = " << get_block_hash(bl)); + rsp.blocks.push_back(block_complete_entry()); + block_complete_entry& e = rsp.blocks.back(); + //pack block + e.block = t_serializable_object_to_blob(bl); + //pack transactions + BOOST_FOREACH(transaction& tx, txs) + e.txs.push_back(t_serializable_object_to_blob(tx)); + + } + //get another transactions, if need + std::list txs; + get_transactions(arg.txs, txs, rsp.missed_ids); + //pack aside transactions + BOOST_FOREACH(const auto& tx, txs) + rsp.txs.push_back(t_serializable_object_to_blob(tx)); + + return true; +} +//------------------------------------------------------------------ +bool blockchain_storage::get_alternative_blocks(std::list& blocks) +{ + CRITICAL_REGION_LOCAL(m_blockchain_lock); + + BOOST_FOREACH(const auto& alt_bl, m_alternative_chains) + { + blocks.push_back(alt_bl.second.bl); + } + return true; +} +//------------------------------------------------------------------ +size_t blockchain_storage::get_alternative_blocks_count() +{ + CRITICAL_REGION_LOCAL(m_blockchain_lock); + return m_alternative_chains.size(); +} +//------------------------------------------------------------------ +bool blockchain_storage::add_out_to_get_random_outs(std::vector >& amount_outs, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs, uint64_t amount, size_t i) +{ + CRITICAL_REGION_LOCAL(m_blockchain_lock); + transactions_container::iterator tx_it = m_transactions.find(amount_outs[i].first); + CHECK_AND_ASSERT_MES(tx_it != m_transactions.end(), false, "internal error: transaction with id " << amount_outs[i].first << ENDL << + ", used in mounts global index for amount=" << amount << ": i=" << i << "not found in transactions index"); + CHECK_AND_ASSERT_MES(tx_it->second.tx.vout.size() > amount_outs[i].second, false, "internal error: in global outs index, transaction out index=" + << amount_outs[i].second << " more than transaction outputs = " << tx_it->second.tx.vout.size() << ", for tx id = " << amount_outs[i].first); + transaction& tx = tx_it->second.tx; + CHECK_AND_ASSERT_MES(tx.vout[amount_outs[i].second].target.type() == typeid(txout_to_key), false, "unknown tx out type"); + + //check if transaction is unlocked + if(!is_tx_spendtime_unlocked(tx.unlock_time)) + return false; + + COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry& oen = *result_outs.outs.insert(result_outs.outs.end(), COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry()); + oen.global_amount_index = i; + oen.out_key = boost::get(tx.vout[amount_outs[i].second].target).key; + return true; +} +//------------------------------------------------------------------ +size_t blockchain_storage::find_end_of_allowed_index(const std::vector >& amount_outs) +{ + CRITICAL_REGION_LOCAL(m_blockchain_lock); + if(!amount_outs.size()) + return 0; + size_t i = amount_outs.size(); + do + { + --i; + transactions_container::iterator it = m_transactions.find(amount_outs[i].first); + CHECK_AND_ASSERT_MES(it != m_transactions.end(), 0, "internal error: failed to find transaction from outputs index with tx_id=" << amount_outs[i].first); + if(it->second.m_keeper_block_height + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW <= get_current_blockchain_height() ) + return i+1; + } while (i != 0); + return 0; +} +//------------------------------------------------------------------ +bool blockchain_storage::get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res) +{ + srand(static_cast(time(NULL))); + CRITICAL_REGION_LOCAL(m_blockchain_lock); + BOOST_FOREACH(uint64_t amount, req.amounts) + { + COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs = *res.outs.insert(res.outs.end(), COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount()); + result_outs.amount = amount; + auto it = m_outputs.find(amount); + if(it == m_outputs.end()) + { + LOG_ERROR("COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS: not outs for amount " << amount << ", wallet should use some real outs when it lookup for some mix, so, at least one out for this amount should exist"); + continue;//actually this is strange situation, wallet should use some real outs when it lookup for some mix, so, at least one out for this amount should exist + } + std::vector >& amount_outs = it->second; + //it is not good idea to use top fresh outs, because it increases possibility of transaction canceling on split + //lets find upper bound of not fresh outs + size_t up_index_limit = find_end_of_allowed_index(amount_outs); + CHECK_AND_ASSERT_MES(up_index_limit <= amount_outs.size(), false, "internal error: find_end_of_allowed_index returned wrong index=" << up_index_limit << ", with amount_outs.size = " << amount_outs.size()); + if(amount_outs.size() > req.outs_count) + { + std::set used; + size_t try_count = 0; + for(uint64_t j = 0; j != req.outs_count && try_count < up_index_limit;) + { + size_t i = rand()%up_index_limit; + if(used.count(i)) + continue; + bool added = add_out_to_get_random_outs(amount_outs, result_outs, amount, i); + used.insert(i); + if(added) + ++j; + ++try_count; + } + }else + { + for(size_t i = 0; i != up_index_limit; i++) + add_out_to_get_random_outs(amount_outs, result_outs, amount, i); + } + } + return true; +} +//------------------------------------------------------------------ +//bool blockchain_storage::get_outs_for_amounts(uint64_t amount, std::vector >& keys, std::map& txs) +//{ +// auto it = m_outputs.find(amount); +// if(it == m_outputs.end()) +// return false; +// keys = it->second; +// typedef std::pair pair; +// BOOST_FOREACH(pair& pr, keys) +// { +// auto it = m_transactions.find(pr.first); +// CHECK_AND_ASSERT_MES(it != m_transactions.end(), false, "internal error: transaction with id " << pr.first << " not found in internal index, but have refference for amount " << amount); +// txs[pr.first] = it->second.tx; +// } +// return true; +//} +//------------------------------------------------------------------ +bool blockchain_storage::find_blockchain_supplement(const std::list& qblock_ids, uint64_t& starter_offset) +{ + CRITICAL_REGION_LOCAL(m_blockchain_lock); + + if(!qblock_ids.size() /*|| !req.m_total_height*/) + { + LOG_ERROR("Client sent wrong NOTIFY_REQUEST_CHAIN: m_block_ids.size()=" << qblock_ids.size() << /*", m_height=" << req.m_total_height <<*/ ", dropping connection"); + return false; + } + //check genesis match + if(qblock_ids.back() != get_block_hash(m_blocks[0].bl)) + { + LOG_ERROR("Client sent wrong NOTIFY_REQUEST_CHAIN: genesis block missmatch: " << ENDL << "id: " + << qblock_ids.back() << ", " << ENDL << "expected: " << get_block_hash(m_blocks[0].bl) + << "," << ENDL << " dropping connection"); + return false; + } + + /* Figure out what blocks we should request to get state_normal */ + size_t i = 0; + auto bl_it = qblock_ids.begin(); + auto block_index_it = m_blocks_index.find(*bl_it); + for(; bl_it != qblock_ids.end(); bl_it++, i++) + { + block_index_it = m_blocks_index.find(*bl_it); + if(block_index_it != m_blocks_index.end()) + break; + } + + if(bl_it == qblock_ids.end()) + { + LOG_ERROR("Internal error handling connection, can't find split point"); + return false; + } + + if(block_index_it == m_blocks_index.end()) + { + //this should NEVER happen, but, dose of paranoia in such cases is not too bad + LOG_ERROR("Internal error handling connection, can't find split point"); + return false; + } + + //we start to put block ids INCLUDING last known id, just to make other side be sure + starter_offset = block_index_it->second; + return true; +} +//------------------------------------------------------------------ +uint64_t blockchain_storage::block_difficulty(size_t i) +{ + CRITICAL_REGION_LOCAL(m_blockchain_lock); + CHECK_AND_ASSERT_MES(i < m_blocks.size(), false, "wrong block index i = " << i << " at blockchain_storage::block_difficulty()"); + if(i == 0) + return m_blocks[i].cumulative_difficulty; + + return m_blocks[i].cumulative_difficulty - m_blocks[i-1].cumulative_difficulty; +} +//------------------------------------------------------------------ +void blockchain_storage::print_blockchain(uint64_t start_index, uint64_t end_index) +{ + std::stringstream ss; + CRITICAL_REGION_LOCAL(m_blockchain_lock); + if(start_index >=m_blocks.size()) + { + LOG_PRINT_L0("Wrong starter index set: " << start_index << ", expected max index " << m_blocks.size()-1); + return; + } + + for(size_t i = start_index; i != m_blocks.size() && i != end_index; i++) + { + ss << "height " << i << ", timastamp " << m_blocks[i].bl.timestamp << ", cumul_dif " << m_blocks[i].cumulative_difficulty << ", cumul_size " << m_blocks[i].block_cumulative_size + << "\nid\t\t" << get_block_hash(m_blocks[i].bl) + << "\ndifficulty\t\t" << block_difficulty(i) << ", nonce " << m_blocks[i].bl.nonce << ", tx_count " << m_blocks[i].bl.tx_hashes.size() << ENDL; + } + LOG_PRINT_L0("Current blockchain:" << ENDL << ss.str()); +} +//------------------------------------------------------------------ +void blockchain_storage::print_blockchain_index() +{ + std::stringstream ss; + CRITICAL_REGION_LOCAL(m_blockchain_lock); + BOOST_FOREACH(const blocks_by_id_index::value_type& v, m_blocks_index) + ss << "id\t\t" << v.first << " height" << v.second << ENDL << ""; + + LOG_PRINT_L0("Current blockchain index:" << ENDL << ss.str()); +} +//------------------------------------------------------------------ +void blockchain_storage::print_blockchain_outs(const std::string& file) +{ + std::stringstream ss; + CRITICAL_REGION_LOCAL(m_blockchain_lock); + BOOST_FOREACH(const outputs_container::value_type& v, m_outputs) + { + const std::vector >& vals = v.second; + if(vals.size()) + { + ss << "amount: " << v.first << ENDL; + for(size_t i = 0; i != vals.size(); i++) + ss << "\t" << vals[i].first << ": " << vals[i].second << ENDL; + } + } + if(file_io_utils::save_string_to_file(file, ss.str())) + { + LOG_PRINT_L0("Current outputs index writen to file: " << file); + }else + { + LOG_PRINT_L0("Failed to write current outputs index to file: " << file); + } +} +//------------------------------------------------------------------ +bool blockchain_storage::find_blockchain_supplement(const std::list& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp) +{ + CRITICAL_REGION_LOCAL(m_blockchain_lock); + if(!find_blockchain_supplement(qblock_ids, resp.start_height)) + return false; + + resp.total_height = get_current_blockchain_height(); + size_t count = 0; + for(size_t i = resp.start_height; i != m_blocks.size() && count < BLOCKS_IDS_SYNCHRONIZING_DEFAULT_COUNT; i++, count++) + resp.m_block_ids.push_back(get_block_hash(m_blocks[i].bl)); + return true; +} +//------------------------------------------------------------------ +bool blockchain_storage::find_blockchain_supplement(const std::list& qblock_ids, std::list > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count) +{ + CRITICAL_REGION_LOCAL(m_blockchain_lock); + if(!find_blockchain_supplement(qblock_ids, start_height)) + return false; + + total_height = get_current_blockchain_height(); + size_t count = 0; + for(size_t i = start_height; i != m_blocks.size() && count < max_count; i++, count++) + { + blocks.resize(blocks.size()+1); + blocks.back().first = m_blocks[i].bl; + std::list mis; + get_transactions(m_blocks[i].bl.tx_hashes, blocks.back().second, mis); + CHECK_AND_ASSERT_MES(!mis.size(), false, "internal error, transaction from block not found"); + } + return true; +} +//------------------------------------------------------------------ +bool blockchain_storage::add_block_as_invalid(const block& bl, const crypto::hash& h) +{ + block_extended_info bei = AUTO_VAL_INIT(bei); + bei.bl = bl; + return add_block_as_invalid(bei, h); +} +//------------------------------------------------------------------ +bool blockchain_storage::add_block_as_invalid(const block_extended_info& bei, const crypto::hash& h) +{ + CRITICAL_REGION_LOCAL(m_blockchain_lock); + auto i_res = m_invalid_blocks.insert(std::map::value_type(h, bei)); + CHECK_AND_ASSERT_MES(i_res.second, false, "at insertion invalid by tx returned status existed"); + LOG_PRINT_L0("BLOCK ADDED AS INVALID: " << h << ENDL << ", prev_id=" << bei.bl.prev_id << ", m_invalid_blocks count=" << m_invalid_blocks.size()); + return true; +} +//------------------------------------------------------------------ +bool blockchain_storage::have_block(const crypto::hash& id) +{ + CRITICAL_REGION_LOCAL(m_blockchain_lock); + if(m_blocks_index.count(id)) + return true; + if(m_alternative_chains.count(id)) + return true; + /*if(m_orphaned_blocks.get().count(id)) + return true;*/ + + /*if(m_orphaned_by_tx.count(id)) + return true;*/ + if(m_invalid_blocks.count(id)) + return true; + + return false; +} +//------------------------------------------------------------------ +bool blockchain_storage::handle_block_to_main_chain(const block& bl, block_verification_context& bvc) +{ + crypto::hash id = get_block_hash(bl); + return handle_block_to_main_chain(bl, id, bvc); +} +//------------------------------------------------------------------ +bool blockchain_storage::push_transaction_to_global_outs_index(const transaction& tx, const crypto::hash& tx_id, std::vector& global_indexes) +{ + CRITICAL_REGION_LOCAL(m_blockchain_lock); + size_t i = 0; + BOOST_FOREACH(const auto& ot, tx.vout) + { + outputs_container::mapped_type& amount_index = m_outputs[ot.amount]; + amount_index.push_back(std::pair(tx_id, i)); + global_indexes.push_back(amount_index.size()-1); + ++i; + } + return true; +} +//------------------------------------------------------------------ +size_t blockchain_storage::get_total_transactions() +{ + CRITICAL_REGION_LOCAL(m_blockchain_lock); + return m_transactions.size(); +} +//------------------------------------------------------------------ +bool blockchain_storage::get_outs(uint64_t amount, std::list& pkeys) +{ + CRITICAL_REGION_LOCAL(m_blockchain_lock); + auto it = m_outputs.find(amount); + if(it == m_outputs.end()) + return true; + + BOOST_FOREACH(const auto& out_entry, it->second) + { + auto tx_it = m_transactions.find(out_entry.first); + CHECK_AND_ASSERT_MES(tx_it != m_transactions.end(), false, "transactions outs global index consistency broken: wrong tx id in index"); + CHECK_AND_ASSERT_MES(tx_it->second.tx.vout.size() > out_entry.second, false, "transactions outs global index consistency broken: index in tx_outx more then size"); + CHECK_AND_ASSERT_MES(tx_it->second.tx.vout[out_entry.second].target.type() == typeid(txout_to_key), false, "transactions outs global index consistency broken: index in tx_outx more then size"); + pkeys.push_back(boost::get(tx_it->second.tx.vout[out_entry.second].target).key); + } + + return true; +} +//------------------------------------------------------------------ +bool blockchain_storage::pop_transaction_from_global_index(const transaction& tx, const crypto::hash& tx_id) +{ + CRITICAL_REGION_LOCAL(m_blockchain_lock); + size_t i = tx.vout.size()-1; + BOOST_REVERSE_FOREACH(const auto& ot, tx.vout) + { + auto it = m_outputs.find(ot.amount); + CHECK_AND_ASSERT_MES(it != m_outputs.end(), false, "transactions outs global index consistency broken"); + CHECK_AND_ASSERT_MES(it->second.size(), false, "transactions outs global index: empty index for amount: " << ot.amount); + CHECK_AND_ASSERT_MES(it->second.back().first == tx_id , false, "transactions outs global index consistency broken: tx id missmatch"); + CHECK_AND_ASSERT_MES(it->second.back().second == i, false, "transactions outs global index consistency broken: in transaction index missmatch"); + it->second.pop_back(); + --i; + } + return true; +} +//------------------------------------------------------------------ +bool blockchain_storage::add_transaction_from_block(const transaction& tx, const crypto::hash& tx_id, const crypto::hash& bl_id, uint64_t bl_height) +{ + CRITICAL_REGION_LOCAL(m_blockchain_lock); + struct add_transaction_input_visitor: public boost::static_visitor + { + key_images_container& m_spent_keys; + const crypto::hash& m_tx_id; + const crypto::hash& m_bl_id; + add_transaction_input_visitor(key_images_container& spent_keys, const crypto::hash& tx_id, const crypto::hash& bl_id):m_spent_keys(spent_keys), m_tx_id(tx_id), m_bl_id(bl_id) + {} + bool operator()(const txin_to_key& in) const + { + const crypto::key_image& ki = in.k_image; + auto r = m_spent_keys.insert(ki); + if(!r.second) + { + //double spend detected + LOG_PRINT_L0("tx with id: " << m_tx_id << " in block id: " << m_bl_id << " have input marked as spent with key image: " << ki << ", block declined"); + return false; + } + return true; + } + + bool operator()(const txin_gen& tx) const{return true;} + bool operator()(const txin_to_script& tx) const{return false;} + bool operator()(const txin_to_scripthash& tx) const{return false;} + }; + + BOOST_FOREACH(const txin_v& in, tx.vin) + { + if(!boost::apply_visitor(add_transaction_input_visitor(m_spent_keys, tx_id, bl_id), in)) + { + LOG_ERROR("critical internal error: add_transaction_input_visitor failed. but here key_images should be shecked"); + purge_transaction_keyimages_from_blockchain(tx, false); + return false; + } + } + transaction_chain_entry ch_e; + ch_e.m_keeper_block_height = bl_height; + ch_e.tx = tx; + auto i_r = m_transactions.insert(std::pair(tx_id, ch_e)); + if(!i_r.second) + { + LOG_PRINT_L0("tx with id: " << tx_id << " in block id: " << bl_id << " already in blockchain"); + return false; + } + bool r = push_transaction_to_global_outs_index(tx, tx_id, i_r.first->second.m_global_output_indexes); + CHECK_AND_ASSERT_MES(r, false, "failed to return push_transaction_to_global_outs_index tx id " << tx_id); + LOG_PRINT_L2("Added transaction to blockchain history:" << ENDL + << "tx_id: " << tx_id << ENDL + << "inputs: " << tx.vin.size() << ", outs: " << tx.vout.size() << ", spend money: " << print_money(get_outs_money_amount(tx)) << "(fee: " << (is_coinbase(tx) ? "0[coinbase]" : print_money(get_tx_fee(tx))) << ")"); + return true; +} +//------------------------------------------------------------------ +bool blockchain_storage::get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector& indexs) +{ + CRITICAL_REGION_LOCAL(m_blockchain_lock); + auto it = m_transactions.find(tx_id); + if(it == m_transactions.end()) + { + LOG_PRINT_RED_L0("warning: get_tx_outputs_gindexs failed to find transaction with id = " << tx_id); + return false; + } + + CHECK_AND_ASSERT_MES(it->second.m_global_output_indexes.size(), false, "internal error: global indexes for transaction " << tx_id << " is empty"); + indexs = it->second.m_global_output_indexes; + return true; +} +//------------------------------------------------------------------ +bool blockchain_storage::check_tx_inputs(const transaction& tx, uint64_t& max_used_block_height, crypto::hash& max_used_block_id) +{ + CRITICAL_REGION_LOCAL(m_blockchain_lock); + bool res = check_tx_inputs(tx, &max_used_block_height); + if(!res) return false; + CHECK_AND_ASSERT_MES(max_used_block_height < m_blocks.size(), false, "internal error: max used block index=" << max_used_block_height << " is not less then blockchain size = " << m_blocks.size()); + get_block_hash(m_blocks[max_used_block_height].bl, max_used_block_id); + return true; +} +//------------------------------------------------------------------ +bool blockchain_storage::have_tx_keyimges_as_spent(const transaction &tx) +{ + BOOST_FOREACH(const txin_v& in, tx.vin) + { + CHECKED_GET_SPECIFIC_VARIANT(in, const txin_to_key, in_to_key, true); + if(have_tx_keyimg_as_spent(in_to_key.k_image)) + return true; + } + return false; +} +//------------------------------------------------------------------ +bool blockchain_storage::check_tx_inputs(const transaction& tx, uint64_t* pmax_used_block_height) +{ + crypto::hash tx_prefix_hash = get_transaction_prefix_hash(tx); + return check_tx_inputs(tx, tx_prefix_hash, pmax_used_block_height); +} +//------------------------------------------------------------------ +bool blockchain_storage::check_tx_inputs(const transaction& tx, const crypto::hash& tx_prefix_hash, uint64_t* pmax_used_block_height) +{ + size_t sig_index = 0; + if(pmax_used_block_height) + *pmax_used_block_height = 0; + + BOOST_FOREACH(const auto& txin, tx.vin) + { + CHECK_AND_ASSERT_MES(txin.type() == typeid(txin_to_key), false, "wrong type id in tx input at blockchain_storage::check_tx_inputs"); + const txin_to_key& in_to_key = boost::get(txin); + + CHECK_AND_ASSERT_MES(in_to_key.key_offsets.size(), false, "empty in_to_key.key_offsets in transaction with id " << get_transaction_hash(tx)); + + if(have_tx_keyimg_as_spent(in_to_key.k_image)) + { + LOG_PRINT_L1("Key image already spent in blockchain: " << string_tools::pod_to_hex(in_to_key.k_image)); + return false; + } + + CHECK_AND_ASSERT_MES(sig_index < tx.signatures.size(), false, "wrong transaction: not signature entry for input with index= " << sig_index); + if(!check_tx_input(in_to_key, tx_prefix_hash, tx.signatures[sig_index], pmax_used_block_height)) + { + LOG_PRINT_L0("Failed to check ring signature for tx " << get_transaction_hash(tx)); + return false; + } + + sig_index++; + } + + return true; +} +//------------------------------------------------------------------ +bool blockchain_storage::is_tx_spendtime_unlocked(uint64_t unlock_time) +{ + if(unlock_time < CRYPTONOTE_MAX_BLOCK_NUMBER) + { + //interpret as block index + if(get_current_blockchain_height()-1 + CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_BLOCKS >= unlock_time) + return true; + else + return false; + }else + { + //interpret as time + uint64_t current_time = static_cast(time(NULL)); + if(current_time + CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS >= unlock_time) + return true; + else + return false; + } + return false; +} +//------------------------------------------------------------------ +bool blockchain_storage::check_tx_input(const txin_to_key& txin, const crypto::hash& tx_prefix_hash, const std::vector& sig, uint64_t* pmax_related_block_height) +{ + CRITICAL_REGION_LOCAL(m_blockchain_lock); + + struct outputs_visitor + { + std::vector& m_results_collector; + blockchain_storage& m_bch; + outputs_visitor(std::vector& results_collector, blockchain_storage& bch):m_results_collector(results_collector), m_bch(bch) + {} + bool handle_output(const transaction& tx, const tx_out& out) + { + //check tx unlock time + if(!m_bch.is_tx_spendtime_unlocked(tx.unlock_time)) + { + LOG_PRINT_L0("One of outputs for one of inputs have wrong tx.unlock_time = " << tx.unlock_time); + return false; + } + + if(out.target.type() != typeid(txout_to_key)) + { + LOG_PRINT_L0("Output have wrong type id, which=" << out.target.which()); + return false; + } + + m_results_collector.push_back(&boost::get(out.target).key); + return true; + } + }; + + //check ring signature + std::vector output_keys; + outputs_visitor vi(output_keys, *this); + if(!scan_outputkeys_for_indexes(txin, vi, pmax_related_block_height)) + { + LOG_PRINT_L0("Failed to get output keys for tx with amount = " << print_money(txin.amount) << " and count indexes " << txin.key_offsets.size()); + return false; + } + + if(txin.key_offsets.size() != output_keys.size()) + { + LOG_PRINT_L0("Output keys for tx with amount = " << txin.amount << " and count indexes " << txin.key_offsets.size() << " returned wrong keys count " << output_keys.size()); + return false; + } + CHECK_AND_ASSERT_MES(sig.size() == output_keys.size(), false, "internal error: tx signatures count=" << sig.size() << " mismatch with outputs keys count for inputs=" << output_keys.size()); + if(m_is_in_checkpoint_zone) + return true; + return crypto::check_ring_signature(tx_prefix_hash, txin.k_image, output_keys, sig.data()); +} +//------------------------------------------------------------------ +uint64_t blockchain_storage::get_adjusted_time() +{ + //TODO: add collecting median time + return time(NULL); +} +//------------------------------------------------------------------ +bool blockchain_storage::check_block_timestamp_main(const block& b) +{ + if(b.timestamp > get_adjusted_time() + CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT) + { + LOG_PRINT_L0("Timestamp of block with id: " << get_block_hash(b) << ", " << b.timestamp << ", bigger than adjusted time + 2 hours"); + return false; + } + + std::vector timestamps; + size_t offset = m_blocks.size() <= BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW ? 0: m_blocks.size()- BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW; + for(;offset!= m_blocks.size(); ++offset) + timestamps.push_back(m_blocks[offset].bl.timestamp); + + return check_block_timestamp(std::move(timestamps), b); +} +//------------------------------------------------------------------ +bool blockchain_storage::check_block_timestamp(std::vector timestamps, const block& b) +{ + if(timestamps.size() < BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW) + return true; + + uint64_t median_ts = epee::misc_utils::median(timestamps); + + if(b.timestamp < median_ts) + { + LOG_PRINT_L0("Timestamp of block with id: " << get_block_hash(b) << ", " << b.timestamp << ", less than median of last " << BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW << " blocks, " << median_ts); + return false; + } + + return true; +} +//------------------------------------------------------------------ +bool blockchain_storage::handle_block_to_main_chain(const block& bl, const crypto::hash& id, block_verification_context& bvc) +{ + TIME_MEASURE_START(block_processing_time); + CRITICAL_REGION_LOCAL(m_blockchain_lock); + if(bl.prev_id != get_tail_id()) + { + LOG_PRINT_L0("Block with id: " << id << ENDL + << "have wrong prev_id: " << bl.prev_id << ENDL + << "expected: " << get_tail_id()); + return false; + } + + if(!check_block_timestamp_main(bl)) + { + LOG_PRINT_L0("Block with id: " << id << ENDL + << "have invalid timestamp: " << bl.timestamp); + //add_block_as_invalid(bl, id);//do not add blocks to invalid storage befor proof of work check was passed + bvc.m_verifivation_failed = true; + return false; + } + + //check proof of work + TIME_MEASURE_START(target_calculating_time); + difficulty_type current_diffic = get_difficulty_for_next_block(); + CHECK_AND_ASSERT_MES(current_diffic, false, "!!!!!!!!! difficulty overhead !!!!!!!!!"); + TIME_MEASURE_FINISH(target_calculating_time); + TIME_MEASURE_START(longhash_calculating_time); + crypto::hash proof_of_work = null_hash; + if(!m_checkpoints.is_in_checkpoint_zone(get_current_blockchain_height())) + { + proof_of_work = get_block_longhash(bl, m_blocks.size()); + + if(!check_hash(proof_of_work, current_diffic)) + { + LOG_PRINT_L0("Block with id: " << id << ENDL + << "have not enough proof of work: " << proof_of_work << ENDL + << "nexpected difficulty: " << current_diffic ); + bvc.m_verifivation_failed = true; + return false; + } + }else + { + if(!m_checkpoints.check_block(get_current_blockchain_height(), id)) + { + LOG_ERROR("CHECKPOINT VALIDATION FAILED"); + bvc.m_verifivation_failed = true; + return false; + } + } + TIME_MEASURE_FINISH(longhash_calculating_time); + + if(!prevalidate_miner_transaction(bl, m_blocks.size())) + { + LOG_PRINT_L0("Block with id: " << id + << " failed to pass prevalidation"); + bvc.m_verifivation_failed = true; + return false; + } + size_t coinbase_blob_size = get_object_blobsize(bl.miner_tx); + size_t cumulative_block_size = coinbase_blob_size; + //process transactions + if(!add_transaction_from_block(bl.miner_tx, get_transaction_hash(bl.miner_tx), id, get_current_blockchain_height())) + { + LOG_PRINT_L0("Block with id: " << id << " failed to add transaction to blockchain storage"); + bvc.m_verifivation_failed = true; + return false; + } + size_t tx_processed_count = 0; + uint64_t fee_summary = 0; + BOOST_FOREACH(const crypto::hash& tx_id, bl.tx_hashes) + { + transaction tx; + size_t blob_size = 0; + uint64_t fee = 0; + if(!m_tx_pool.take_tx(tx_id, tx, blob_size, fee)) + { + LOG_PRINT_L0("Block with id: " << id << "have at least one unknown transaction with id: " << tx_id); + purge_block_data_from_blockchain(bl, tx_processed_count); + //add_block_as_invalid(bl, id); + bvc.m_verifivation_failed = true; + return false; + } + if(!check_tx_inputs(tx)) + { + LOG_PRINT_L0("Block with id: " << id << "have at least one transaction (id: " << tx_id << ") with wrong inputs."); + cryptonote::tx_verification_context tvc = AUTO_VAL_INIT(tvc); + bool add_res = m_tx_pool.add_tx(tx, tvc, true); + CHECK_AND_ASSERT_MES2(add_res, "handle_block_to_main_chain: failed to add transaction back to transaction pool"); + purge_block_data_from_blockchain(bl, tx_processed_count); + add_block_as_invalid(bl, id); + LOG_PRINT_L0("Block with id " << id << " added as invalid becouse of wrong inputs in transactions"); + bvc.m_verifivation_failed = true; + return false; + } + + if(!add_transaction_from_block(tx, tx_id, id, get_current_blockchain_height())) + { + LOG_PRINT_L0("Block with id: " << id << " failed to add transaction to blockchain storage"); + cryptonote::tx_verification_context tvc = AUTO_VAL_INIT(tvc); + bool add_res = m_tx_pool.add_tx(tx, tvc, true); + CHECK_AND_ASSERT_MES2(add_res, "handle_block_to_main_chain: failed to add transaction back to transaction pool"); + purge_block_data_from_blockchain(bl, tx_processed_count); + bvc.m_verifivation_failed = true; + return false; + } + fee_summary += fee; + cumulative_block_size += blob_size; + ++tx_processed_count; + } + uint64_t base_reward = 0; + uint64_t already_generated_coins = m_blocks.size() ? m_blocks.back().already_generated_coins:0; + if(!validate_miner_transaction(bl, cumulative_block_size, fee_summary, base_reward, already_generated_coins)) + { + LOG_PRINT_L0("Block with id: " << id + << " have wrong miner transaction"); + purge_block_data_from_blockchain(bl, tx_processed_count); + bvc.m_verifivation_failed = true; + return false; + } + + + block_extended_info bei = boost::value_initialized(); + bei.bl = bl; + bei.block_cumulative_size = cumulative_block_size; + bei.cumulative_difficulty = current_diffic; + bei.already_generated_coins = already_generated_coins + base_reward; + if(m_blocks.size()) + bei.cumulative_difficulty += m_blocks.back().cumulative_difficulty; + + bei.height = m_blocks.size(); + + auto ind_res = m_blocks_index.insert(std::pair(id, bei.height)); + if(!ind_res.second) + { + LOG_ERROR("block with id: " << id << " already in block indexes"); + purge_block_data_from_blockchain(bl, tx_processed_count); + bvc.m_verifivation_failed = true; + return false; + } + + m_blocks.push_back(bei); + update_next_comulative_size_limit(); + TIME_MEASURE_FINISH(block_processing_time); + LOG_PRINT_L1("+++++ BLOCK SUCCESSFULLY ADDED" << ENDL << "id:\t" << id + << ENDL << "PoW:\t" << proof_of_work + << ENDL << "HEIGHT " << bei.height << ", difficulty:\t" << current_diffic + << ENDL << "block reward: " << print_money(fee_summary + base_reward) << "(" << print_money(base_reward) << " + " << print_money(fee_summary) + << "), coinbase_blob_size: " << coinbase_blob_size << ", cumulative size: " << cumulative_block_size + << ", " << block_processing_time << "("<< target_calculating_time << "/" << longhash_calculating_time << ")ms"); + + bvc.m_added_to_main_chain = true; + /*if(!m_orphanes_reorganize_in_work) + review_orphaned_blocks_with_new_block_id(id, true);*/ + + m_tx_pool.on_blockchain_inc(bei.height, id); + //LOG_PRINT_L0("BLOCK: " << ENDL << "" << dump_obj_as_json(bei.bl)); + return true; +} +//------------------------------------------------------------------ +bool blockchain_storage::update_next_comulative_size_limit() +{ + std::vector sz; + get_last_n_blocks_sizes(sz, CRYPTONOTE_REWARD_BLOCKS_WINDOW); + + uint64_t median = misc_utils::median(sz); + if(median <= CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE) + median = CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE; + + m_current_block_comul_sz_limit = median*2; + return true; +} +//------------------------------------------------------------------ +bool blockchain_storage::add_new_block(const block& bl_, block_verification_context& bvc) +{ + //copy block here to let modify block.target + block bl = bl_; + crypto::hash id = get_block_hash(bl); + CRITICAL_REGION_LOCAL(m_tx_pool);//to avoid deadlock lets lock tx_pool for whole add/reorganize process + CRITICAL_REGION_LOCAL1(m_blockchain_lock); + if(have_block(id)) + { + LOG_PRINT_L3("block with id = " << id << " already exists"); + bvc.m_already_exists = true; + return false; + } + + //check that block refers to chain tail + if(!(bl.prev_id == get_tail_id())) + { + //chain switching or wrong block + bvc.m_added_to_main_chain = false; + return handle_alternative_block(bl, id, bvc); + //never relay alternative blocks + } + + return handle_block_to_main_chain(bl, id, bvc); +} \ No newline at end of file diff --git a/src/cryptonote_core/blockchain_storage.h b/src/cryptonote_core/blockchain_storage.h new file mode 100644 index 000000000..4ff5e83ff --- /dev/null +++ b/src/cryptonote_core/blockchain_storage.h @@ -0,0 +1,297 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tx_pool.h" +#include "cryptonote_basic.h" +#include "common/util.h" +#include "cryptonote_protocol/cryptonote_protocol_defs.h" +#include "rpc/core_rpc_server_commands_defs.h" +#include "difficulty.h" +#include "cryptonote_core/cryptonote_format_utils.h" +#include "verification_context.h" +#include "crypto/hash.h" +#include "checkpoints.h" + +namespace cryptonote +{ + + /************************************************************************/ + /* */ + /************************************************************************/ + class blockchain_storage + { + public: + struct transaction_chain_entry + { + transaction tx; + uint64_t m_keeper_block_height; + size_t m_blob_size; + std::vector m_global_output_indexes; + }; + + struct block_extended_info + { + block bl; + uint64_t height; + size_t block_cumulative_size; + difficulty_type cumulative_difficulty; + uint64_t already_generated_coins; + }; + + blockchain_storage(tx_memory_pool& tx_pool):m_tx_pool(tx_pool), m_current_block_comul_sz_limit(0), m_is_in_checkpoint_zone(false) + {}; + + bool init() { return init(tools::get_default_data_dir()); } + bool init(const std::string& config_folder); + bool deinit(); + + void set_checkpoints(checkpoints&& chk_pts) { m_checkpoints = chk_pts; } + + //bool push_new_block(); + bool get_blocks(uint64_t start_offset, size_t count, std::list& blocks, std::list& txs); + bool get_blocks(uint64_t start_offset, size_t count, std::list& blocks); + bool get_alternative_blocks(std::list& blocks); + //bool get_orphaned_blocks(std::list& orphaned_by_prev_id, std::list& orphaned_by_tx); + //size_t get_orphaned_by_prev_blocks_count(); + //size_t get_orphaned_by_tx_blocks_count(); + size_t get_alternative_blocks_count(); + crypto::hash get_block_id_by_height(uint64_t height); + bool get_block_by_hash(const crypto::hash &h, block &blk); + void get_all_known_block_ids(std::list &main, std::list &alt, std::list &invalid); + + template + void serialize(archive_t & ar, const unsigned int version); + + bool have_tx(const crypto::hash &id); + bool have_tx_keyimges_as_spent(const transaction &tx); + bool have_tx_keyimg_as_spent(const crypto::key_image &key_im); + transaction *get_tx(const crypto::hash &id); + + template + bool scan_outputkeys_for_indexes(const txin_to_key& tx_in_to_key, visitor_t& vis, uint64_t* pmax_related_block_height = NULL); + + uint64_t get_current_blockchain_height(); + crypto::hash get_tail_id(); + crypto::hash get_tail_id(uint64_t& height); + difficulty_type get_difficulty_for_next_block(); + bool add_new_block(const block& bl_, block_verification_context& bvc); + + bool reset_and_set_genesis_block(const block& b); + bool create_block_template(block& b, const account_public_address& miner_address, difficulty_type& di, uint64_t& height, const blobdata& ex_nonce); + bool have_block(const crypto::hash& id); + size_t get_total_transactions(); + bool get_outs(uint64_t amount, std::list& pkeys); + bool get_short_chain_history(std::list& ids); + bool find_blockchain_supplement(const std::list& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp); + bool find_blockchain_supplement(const std::list& qblock_ids, uint64_t& starter_offset); + bool find_blockchain_supplement(const std::list& qblock_ids, std::list > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count); + //bool get_chain_entry(const NOTIFY_REQUEST_CHAIN_ENTRY::request& req, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp); + bool handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NOTIFY_RESPONSE_GET_OBJECTS::request& rsp); + bool handle_get_objects(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res); + bool get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res); + //bool get_outs_for_amounts(uint64_t amount, std::vector >& keys, std::map& txs); + bool get_backward_blocks_sizes(size_t from_height, std::vector& sz, size_t count); + bool get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector& indexs); + bool store_blockchain(); + bool check_tx_input(const txin_to_key& txin, const crypto::hash& tx_prefix_hash, const std::vector& sig, uint64_t* pmax_related_block_height = NULL); + bool check_tx_inputs(const transaction& tx, const crypto::hash& tx_prefix_hash, uint64_t* pmax_used_block_height = NULL); + bool check_tx_inputs(const transaction& tx, uint64_t* pmax_used_block_height = NULL); + bool check_tx_inputs(const transaction& tx, uint64_t& pmax_used_block_height, crypto::hash& max_used_block_id); + uint64_t get_current_comulative_blocksize_limit(); + + template + bool get_blocks(const t_ids_container& block_ids, t_blocks_container& blocks, t_missed_container& missed_bs) + { + CRITICAL_REGION_LOCAL(m_blockchain_lock); + + BOOST_FOREACH(const auto& bl_id, block_ids) + { + auto it = m_blocks_index.find(bl_id); + if(it == m_blocks_index.end()) + missed_bs.push_back(bl_id); + else + { + CHECK_AND_ASSERT_MES(it->second < m_blocks.size(), false, "Internal error: bl_id=" << string_tools::pod_to_hex(bl_id) + << " have index record with offset="<second<< ", bigger then m_blocks.size()=" << m_blocks.size()); + blocks.push_back(m_blocks[it->second].bl); + } + } + return true; + } + + template + bool get_transactions(const t_ids_container& txs_ids, t_tx_container& txs, t_missed_container& missed_txs) + { + CRITICAL_REGION_LOCAL(m_blockchain_lock); + + BOOST_FOREACH(const auto& tx_id, txs_ids) + { + auto it = m_transactions.find(tx_id); + if(it == m_transactions.end()) + { + transaction tx; + if(!m_tx_pool.get_transaction(tx_id, tx)) + missed_txs.push_back(tx_id); + else + txs.push_back(tx); + } + else + txs.push_back(it->second.tx); + } + return true; + } + //debug functions + void print_blockchain(uint64_t start_index, uint64_t end_index); + void print_blockchain_index(); + void print_blockchain_outs(const std::string& file); + + private: + typedef std::unordered_map blocks_by_id_index; + typedef std::unordered_map transactions_container; + typedef std::unordered_set key_images_container; + typedef std::vector blocks_container; + typedef std::unordered_map blocks_ext_by_hash; + typedef std::unordered_map blocks_by_hash; + typedef std::map>> outputs_container; //crypto::hash - tx hash, size_t - index of out in transaction + + tx_memory_pool& m_tx_pool; + critical_section m_blockchain_lock; // TODO: add here reader/writer lock + + // main chain + blocks_container m_blocks; // height -> block_extended_info + blocks_by_id_index m_blocks_index; // crypto::hash -> height + transactions_container m_transactions; + key_images_container m_spent_keys; + size_t m_current_block_comul_sz_limit; + + + // all alternative chains + blocks_ext_by_hash m_alternative_chains; // crypto::hash -> block_extended_info + + // some invalid blocks + blocks_ext_by_hash m_invalid_blocks; // crypto::hash -> block_extended_info + outputs_container m_outputs; + + + std::string m_config_folder; + checkpoints m_checkpoints; + std::atomic m_is_in_checkpoint_zone; + + + + + bool switch_to_alternative_blockchain(std::list& alt_chain); + bool pop_block_from_blockchain(); + bool purge_block_data_from_blockchain(const block& b, size_t processed_tx_count); + bool purge_transaction_from_blockchain(const crypto::hash& tx_id); + bool purge_transaction_keyimages_from_blockchain(const transaction& tx, bool strict_check); + + bool handle_block_to_main_chain(const block& bl, block_verification_context& bvc); + bool handle_block_to_main_chain(const block& bl, const crypto::hash& id, block_verification_context& bvc); + bool handle_alternative_block(const block& b, const crypto::hash& id, block_verification_context& bvc); + //bool handle_block_as_orphaned(const block& b, const crypto::hash& id, block_verification_context& bvc); + difficulty_type get_next_difficulty_for_alternative_chain(const std::list& alt_chain, block_extended_info& bei); + bool prevalidate_miner_transaction(const block& b, uint64_t height); + bool validate_miner_transaction(const block& b, size_t cumulative_block_size, uint64_t fee, uint64_t& base_reward, uint64_t already_generated_coins); + bool validate_transaction(const block& b, uint64_t height, const transaction& tx); + //bool add_block_to_orphaned_by_tx(const block& bl, const crypto::hash& h); + //bool finish_forward_orphaned_chain(std::list< orphans_indexed_container::index::type::iterator >& orph_chain); + bool rollback_blockchain_switching(std::list& original_chain, size_t rollback_height); + bool add_transaction_from_block(const transaction& tx, const crypto::hash& tx_id, const crypto::hash& bl_id, uint64_t bl_height); + bool push_transaction_to_global_outs_index(const transaction& tx, const crypto::hash& tx_id, std::vector& global_indexes); + bool pop_transaction_from_global_index(const transaction& tx, const crypto::hash& tx_id); + //bool review_orphaned_blocks_with_new_block_id(const crypto::hash& id, bool main_chain); + //void review_orphaned_blocks_finisher(); + bool get_last_n_blocks_sizes(std::vector& sz, size_t count); + bool add_out_to_get_random_outs(std::vector >& amount_outs, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& result_outs, uint64_t amount, size_t i); + bool is_tx_spendtime_unlocked(uint64_t unlock_time); + bool add_block_as_invalid(const block& bl, const crypto::hash& h); + bool add_block_as_invalid(const block_extended_info& bei, const crypto::hash& h); + size_t find_end_of_allowed_index(const std::vector >& amount_outs); + uint64_t block_difficulty(size_t i); + bool check_block_timestamp_main(const block& b); + bool check_block_timestamp(std::vector timestamps, const block& b); + uint64_t get_adjusted_time(); + bool complete_timestamps_vector(uint64_t start_height, std::vector& timestamps); + bool update_next_comulative_size_limit(); + }; + + + /************************************************************************/ + /* */ + /************************************************************************/ + + #define CURRENT_BLOCKCHAIN_STORAGE_ARCHIVE_VER 11 + + template + void blockchain_storage::serialize(archive_t & ar, const unsigned int version) + { + if(version < CURRENT_BLOCKCHAIN_STORAGE_ARCHIVE_VER) + return; + CRITICAL_REGION_LOCAL(m_blockchain_lock); + ar & m_blocks; + ar & m_blocks_index; + ar & m_transactions; + ar & m_spent_keys; + ar & m_alternative_chains; + ar & m_outputs; + ar & m_invalid_blocks; + ar & m_current_block_comul_sz_limit; + } + + //------------------------------------------------------------------ + template + bool blockchain_storage::scan_outputkeys_for_indexes(const txin_to_key& tx_in_to_key, visitor_t& vis, uint64_t* pmax_related_block_height) + { + + auto it = m_outputs.find(tx_in_to_key.amount); + if(it == m_outputs.end() || !tx_in_to_key.key_offsets.size()) + return false; + + std::vector absolute_offsets = relative_output_offsets_to_absolute(tx_in_to_key.key_offsets); + + + std::vector >& amount_outs_vec = it->second; + size_t count = 0; + BOOST_FOREACH(uint64_t i, absolute_offsets) + { + if(i >= amount_outs_vec.size() ) + { + LOG_PRINT_L0("Wrong index in transaction inputs: " << i << ", expected maximum " << amount_outs_vec.size() - 1); + return false; + } + transactions_container::iterator tx_it = m_transactions.find(amount_outs_vec[i].first); + CHECK_AND_ASSERT_MES(tx_it != m_transactions.end(), false, "Wrong transaction id in output indexes: " <second.tx.vout.size(), false, + "Wrong index in transaction outputs: " << amount_outs_vec[i].second << ", expected less then " << tx_it->second.tx.vout.size()); + if(!vis.handle_output(tx_it->second.tx, tx_it->second.tx.vout[amount_outs_vec[i].second])) + { + LOG_PRINT_L0("Failed to handle_output for output no = " << count << ", with absolute offset " << i); + return false; + } + if(count++ == absolute_offsets.size()-1 && pmax_related_block_height) + { + if(*pmax_related_block_height < tx_it->second.m_keeper_block_height) + *pmax_related_block_height = tx_it->second.m_keeper_block_height; + } + } + + return true; + } +} + + + +BOOST_CLASS_VERSION(cryptonote::blockchain_storage, CURRENT_BLOCKCHAIN_STORAGE_ARCHIVE_VER) diff --git a/src/cryptonote_core/blockchain_storage_boost_serialization.h b/src/cryptonote_core/blockchain_storage_boost_serialization.h new file mode 100644 index 000000000..6aa06e87f --- /dev/null +++ b/src/cryptonote_core/blockchain_storage_boost_serialization.h @@ -0,0 +1,33 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +namespace boost +{ + namespace serialization + { + + + template + void serialize(archive_t & ar, cryptonote::blockchain_storage::transaction_chain_entry& te, const unsigned int version) + { + ar & te.tx; + ar & te.m_keeper_block_height; + ar & te.m_blob_size; + ar & te.m_global_output_indexes; + } + + template + void serialize(archive_t & ar, cryptonote::blockchain_storage::block_extended_info& ei, const unsigned int version) + { + ar & ei.bl; + ar & ei.height; + ar & ei.cumulative_difficulty; + ar & ei.block_cumulative_size; + ar & ei.already_generated_coins; + } + + } +} diff --git a/src/cryptonote_core/checkpoints.cpp b/src/cryptonote_core/checkpoints.cpp new file mode 100644 index 000000000..54c2f3a6d --- /dev/null +++ b/src/cryptonote_core/checkpoints.cpp @@ -0,0 +1,48 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "include_base_utils.h" +using namespace epee; + +#include "checkpoints.h" + +namespace cryptonote +{ + //--------------------------------------------------------------------------- + checkpoints::checkpoints() + { + } + //--------------------------------------------------------------------------- + bool checkpoints::add_checkpoint(uint64_t height, const std::string& hash_str) + { + crypto::hash h = null_hash; + bool r = epee::string_tools::parse_tpod_from_hex_string(hash_str, h); + CHECK_AND_ASSERT_MES(r, false, "WRONG HASH IN CHECKPOINTS!!!"); + CHECK_AND_ASSERT_MES(0 == m_points.count(height), false, "WRONG HASH IN CHECKPOINTS!!!"); + m_points[height] = h; + return true; + } + //--------------------------------------------------------------------------- + bool checkpoints::is_in_checkpoint_zone(uint64_t height) const + { + return !m_points.empty() && (height <= (--m_points.end())->first); + } + //--------------------------------------------------------------------------- + bool checkpoints::check_block(uint64_t height, const crypto::hash& h) const + { + auto it = m_points.find(height); + if(it == m_points.end()) + return true; + + if(it->second == h) + { + LOG_PRINT_GREEN("CHECKPOINT PASSED FOR HEIGHT " << height << " " << h, LOG_LEVEL_0); + return true; + }else + { + LOG_ERROR("CHECKPOINT FAILED FOR HEIGHT " << height << ". EXPECTED HASH: " << it->second << ", FETCHED HASH: " << h); + return false; + } + } +} diff --git a/src/cryptonote_core/checkpoints.h b/src/cryptonote_core/checkpoints.h new file mode 100644 index 000000000..20014b1c8 --- /dev/null +++ b/src/cryptonote_core/checkpoints.h @@ -0,0 +1,22 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once +#include +#include "cryptonote_basic_impl.h" + + +namespace cryptonote +{ + class checkpoints + { + public: + checkpoints(); + bool add_checkpoint(uint64_t height, const std::string& hash_str); + bool is_in_checkpoint_zone(uint64_t height) const; + bool check_block(uint64_t height, const crypto::hash& h) const; + private: + std::map m_points; + }; +} diff --git a/src/cryptonote_core/checkpoints_create.h b/src/cryptonote_core/checkpoints_create.h new file mode 100644 index 000000000..3d539de98 --- /dev/null +++ b/src/cryptonote_core/checkpoints_create.h @@ -0,0 +1,27 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include "checkpoints.h" +#include "misc_log_ex.h" + +#define ADD_CHECKPOINT(h, hash) CHECK_AND_ASSERT(checkpoints.add_checkpoint(h, hash), false); + +namespace cryptonote { + inline bool create_checkpoints(cryptonote::checkpoints& checkpoints) + { + ADD_CHECKPOINT(79000, "cae33204e624faeb64938d80073bb7bbacc27017dc63f36c5c0f313cad455a02"); + ADD_CHECKPOINT(140000, "993059fb6ab92db7d80d406c67a52d9c02d873ca34b6290a12b744c970208772"); + ADD_CHECKPOINT(200000, "a5f74c7542077df6859f48b5b1f9c3741f29df38f91a47e14c94b5696e6c3073"); + ADD_CHECKPOINT(230580, "32bd7cb6c68a599cf2861941f29002a5e203522b9af54f08dfced316f6459103"); + ADD_CHECKPOINT(260000, "f68e70b360ca194f48084da7a7fd8e0251bbb4b5587f787ca65a6f5baf3f5947"); + ADD_CHECKPOINT(300000, "8e80861713f68354760dc10ea6ea79f5f3ff28f39b3f0835a8637463b09d70ff"); + ADD_CHECKPOINT(390285, "e00bdc9bf407aeace2f3109de11889ed25894bf194231d075eddaec838097eb7"); + ADD_CHECKPOINT(417000, "2dc96f8fc4d4a4d76b3ed06722829a7ab09d310584b8ecedc9b578b2c458a69f"); + ADD_CHECKPOINT(427193, "00feabb08f2d5759ed04fd6b799a7513187478696bba2db2af10d4347134e311"); + + return true; + } +} diff --git a/src/cryptonote_core/connection_context.h b/src/cryptonote_core/connection_context.h new file mode 100644 index 000000000..bf13449bc --- /dev/null +++ b/src/cryptonote_core/connection_context.h @@ -0,0 +1,54 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once +#include +#include +#include "net/net_utils_base.h" + + +namespace cryptonote +{ + class my_atomic: public std::atomic + { + public: + my_atomic() + {}; + my_atomic(const my_atomic& a):std::atomic(a.load()) + {} + my_atomic& operator= (const my_atomic& a) + { + store(a.load()); + return *this; + } + uint32_t operator++() + { + return std::atomic::operator++(); + } + uint32_t operator++(int fake) + { + return std::atomic::operator++(fake); + } + }; + + + struct cryptonote_connection_context: public epee::net_utils::connection_context_base + { + + enum state + { + state_befor_handshake = 0, //default state + state_synchronizing, + state_normal + }; + + state m_state; + std::list m_needed_objects; + std::unordered_set m_requested_objects; + uint64_t m_remote_blockchain_height; + uint64_t m_last_response_height; + my_atomic m_callback_request_count; //in debug purpose: problem with double callback rise + //size_t m_score; TODO: add score calculations + }; +} diff --git a/src/cryptonote_core/cryptonote_basic.h b/src/cryptonote_core/cryptonote_basic.h new file mode 100644 index 000000000..007e62bbd --- /dev/null +++ b/src/cryptonote_core/cryptonote_basic.h @@ -0,0 +1,347 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include +#include +#include +#include // memcmp +#include +#include "serialization/serialization.h" +#include "serialization/variant.h" +#include "serialization/vector.h" +#include "serialization/binary_archive.h" +#include "serialization/json_archive.h" +#include "serialization/debug_archive.h" +#include "serialization/crypto.h" +#include "serialization/keyvalue_serialization.h" // eepe named serialization +#include "string_tools.h" +#include "cryptonote_config.h" +#include "crypto/crypto.h" +#include "crypto/hash.h" +#include "misc_language.h" +#include "tx_extra.h" + + +namespace cryptonote +{ + + const static crypto::hash null_hash = AUTO_VAL_INIT(null_hash); + const static crypto::public_key null_pkey = AUTO_VAL_INIT(null_pkey); + + typedef std::vector ring_signature; + + + /* outputs */ + + struct txout_to_script + { + std::vector keys; + std::vector script; + + BEGIN_SERIALIZE_OBJECT() + FIELD(keys) + FIELD(script) + END_SERIALIZE() + }; + + struct txout_to_scripthash + { + crypto::hash hash; + }; + + struct txout_to_key + { + txout_to_key() { } + txout_to_key(const crypto::public_key &_key) : key(_key) { } + crypto::public_key key; + }; + + + /* inputs */ + + struct txin_gen + { + size_t height; + + BEGIN_SERIALIZE_OBJECT() + VARINT_FIELD(height) + END_SERIALIZE() + }; + + struct txin_to_script + { + crypto::hash prev; + size_t prevout; + std::vector sigset; + + BEGIN_SERIALIZE_OBJECT() + FIELD(prev) + VARINT_FIELD(prevout) + FIELD(sigset) + END_SERIALIZE() + }; + + struct txin_to_scripthash + { + crypto::hash prev; + size_t prevout; + txout_to_script script; + std::vector sigset; + + BEGIN_SERIALIZE_OBJECT() + FIELD(prev) + VARINT_FIELD(prevout) + FIELD(script) + FIELD(sigset) + END_SERIALIZE() + }; + + struct txin_to_key + { + uint64_t amount; + std::vector key_offsets; + crypto::key_image k_image; // double spending protection + + BEGIN_SERIALIZE_OBJECT() + VARINT_FIELD(amount) + FIELD(key_offsets) + FIELD(k_image) + END_SERIALIZE() + }; + + + typedef boost::variant txin_v; + + typedef boost::variant txout_target_v; + + //typedef std::pair out_t; + struct tx_out + { + uint64_t amount; + txout_target_v target; + + BEGIN_SERIALIZE_OBJECT() + VARINT_FIELD(amount) + FIELD(target) + END_SERIALIZE() + + + }; + + class transaction_prefix + { + + public: + // tx information + size_t version; + uint64_t unlock_time; //number of block (or time), used as a limitation like: spend this tx not early then block/time + + std::vector vin; + std::vector vout; + //extra + std::vector extra; + + BEGIN_SERIALIZE() + VARINT_FIELD(version) + if(CURRENT_TRANSACTION_VERSION < version) return false; + VARINT_FIELD(unlock_time) + FIELD(vin) + FIELD(vout) + FIELD(extra) + END_SERIALIZE() + + + protected: + transaction_prefix(){} + }; + + class transaction: public transaction_prefix + { + public: + std::vector > signatures; //count signatures always the same as inputs count + + transaction(); + virtual ~transaction(); + void set_null(); + + BEGIN_SERIALIZE_OBJECT() + FIELDS(*static_cast(this)) + + ar.tag("signatures"); + ar.begin_array(); + PREPARE_CUSTOM_VECTOR_SERIALIZATION(vin.size(), signatures); + bool signatures_not_expected = signatures.empty(); + if (!signatures_not_expected && vin.size() != signatures.size()) + return false; + + for (size_t i = 0; i < vin.size(); ++i) + { + size_t signature_size = get_signature_size(vin[i]); + if (signatures_not_expected) + { + if (0 == signature_size) + continue; + else + return false; + } + + PREPARE_CUSTOM_VECTOR_SERIALIZATION(signature_size, signatures[i]); + if (signature_size != signatures[i].size()) + return false; + + FIELDS(signatures[i]); + + if (vin.size() - i > 1) + ar.delimit_array(); + } + ar.end_array(); + END_SERIALIZE() + + private: + static size_t get_signature_size(const txin_v& tx_in); + }; + + + inline + transaction::transaction() + { + set_null(); + } + + inline + transaction::~transaction() + { + //set_null(); + } + + inline + void transaction::set_null() + { + version = 0; + unlock_time = 0; + vin.clear(); + vout.clear(); + extra.clear(); + signatures.clear(); + } + + inline + size_t transaction::get_signature_size(const txin_v& tx_in) + { + struct txin_signature_size_visitor : public boost::static_visitor + { + size_t operator()(const txin_gen& txin) const{return 0;} + size_t operator()(const txin_to_script& txin) const{return 0;} + size_t operator()(const txin_to_scripthash& txin) const{return 0;} + size_t operator()(const txin_to_key& txin) const {return txin.key_offsets.size();} + }; + + return boost::apply_visitor(txin_signature_size_visitor(), tx_in); + } + + + + /************************************************************************/ + /* */ + /************************************************************************/ + struct block_header + { + uint8_t major_version; + uint8_t minor_version; + uint64_t timestamp; + crypto::hash prev_id; + uint32_t nonce; + + BEGIN_SERIALIZE() + VARINT_FIELD(major_version) + if(major_version > CURRENT_BLOCK_MAJOR_VERSION) return false; + VARINT_FIELD(minor_version) + VARINT_FIELD(timestamp) + FIELD(prev_id) + FIELD(nonce) + END_SERIALIZE() + }; + + struct block: public block_header + { + transaction miner_tx; + std::vector tx_hashes; + + BEGIN_SERIALIZE_OBJECT() + FIELDS(*static_cast(this)) + FIELD(miner_tx) + FIELD(tx_hashes) + END_SERIALIZE() + }; + + + /************************************************************************/ + /* */ + /************************************************************************/ + struct account_public_address + { + crypto::public_key m_spend_public_key; + crypto::public_key m_view_public_key; + + BEGIN_SERIALIZE_OBJECT() + FIELD(m_spend_public_key) + FIELD(m_view_public_key) + END_SERIALIZE() + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(m_spend_public_key) + KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(m_view_public_key) + END_KV_SERIALIZE_MAP() + }; + + struct keypair + { + crypto::public_key pub; + crypto::secret_key sec; + + static inline keypair generate() + { + keypair k; + generate_keys(k.pub, k.sec); + return k; + } + }; + //--------------------------------------------------------------- + +} + +BLOB_SERIALIZER(cryptonote::txout_to_key); +BLOB_SERIALIZER(cryptonote::txout_to_scripthash); + +VARIANT_TAG(binary_archive, cryptonote::txin_gen, 0xff); +VARIANT_TAG(binary_archive, cryptonote::txin_to_script, 0x0); +VARIANT_TAG(binary_archive, cryptonote::txin_to_scripthash, 0x1); +VARIANT_TAG(binary_archive, cryptonote::txin_to_key, 0x2); +VARIANT_TAG(binary_archive, cryptonote::txout_to_script, 0x0); +VARIANT_TAG(binary_archive, cryptonote::txout_to_scripthash, 0x1); +VARIANT_TAG(binary_archive, cryptonote::txout_to_key, 0x2); +VARIANT_TAG(binary_archive, cryptonote::transaction, 0xcc); +VARIANT_TAG(binary_archive, cryptonote::block, 0xbb); + +VARIANT_TAG(json_archive, cryptonote::txin_gen, "gen"); +VARIANT_TAG(json_archive, cryptonote::txin_to_script, "script"); +VARIANT_TAG(json_archive, cryptonote::txin_to_scripthash, "scripthash"); +VARIANT_TAG(json_archive, cryptonote::txin_to_key, "key"); +VARIANT_TAG(json_archive, cryptonote::txout_to_script, "script"); +VARIANT_TAG(json_archive, cryptonote::txout_to_scripthash, "scripthash"); +VARIANT_TAG(json_archive, cryptonote::txout_to_key, "key"); +VARIANT_TAG(json_archive, cryptonote::transaction, "tx"); +VARIANT_TAG(json_archive, cryptonote::block, "block"); + +VARIANT_TAG(debug_archive, cryptonote::txin_gen, "gen"); +VARIANT_TAG(debug_archive, cryptonote::txin_to_script, "script"); +VARIANT_TAG(debug_archive, cryptonote::txin_to_scripthash, "scripthash"); +VARIANT_TAG(debug_archive, cryptonote::txin_to_key, "key"); +VARIANT_TAG(debug_archive, cryptonote::txout_to_script, "script"); +VARIANT_TAG(debug_archive, cryptonote::txout_to_scripthash, "scripthash"); +VARIANT_TAG(debug_archive, cryptonote::txout_to_key, "key"); +VARIANT_TAG(debug_archive, cryptonote::transaction, "tx"); +VARIANT_TAG(debug_archive, cryptonote::block, "block"); diff --git a/src/cryptonote_core/cryptonote_basic_impl.cpp b/src/cryptonote_core/cryptonote_basic_impl.cpp new file mode 100644 index 000000000..b320a3463 --- /dev/null +++ b/src/cryptonote_core/cryptonote_basic_impl.cpp @@ -0,0 +1,193 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#include "include_base_utils.h" +using namespace epee; + +#include "cryptonote_basic_impl.h" +#include "string_tools.h" +#include "serialization/binary_utils.h" +#include "serialization/vector.h" +#include "cryptonote_format_utils.h" +#include "cryptonote_config.h" +#include "misc_language.h" +#include "common/base58.h" +#include "crypto/hash.h" +#include "common/int-util.h" + +namespace cryptonote { + + /************************************************************************/ + /* Cryptonote helper functions */ + /************************************************************************/ + //----------------------------------------------------------------------------------------------- + size_t get_max_block_size() + { + return CRYPTONOTE_MAX_BLOCK_SIZE; + } + //----------------------------------------------------------------------------------------------- + size_t get_max_tx_size() + { + return CRYPTONOTE_MAX_TX_SIZE; + } + //----------------------------------------------------------------------------------------------- + uint64_t get_block_reward(std::vector& last_blocks_sizes, size_t current_block_size, bool& block_too_big, uint64_t already_generated_coins) + { + block_too_big = false; + + uint64_t base_reward = (MONEY_SUPPLY - already_generated_coins) >> 18; + + if(current_block_size < CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE) + return base_reward; + + size_t med_sz = misc_utils::median(last_blocks_sizes); + + //make it soft + if(med_sz < CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE) + med_sz = CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE; + + if(current_block_size > med_sz) + { + if(current_block_size > med_sz*2) + { + LOG_PRINT_L0("Block cumulative size is too big: " << current_block_size << ", expected less than " << med_sz*2); + block_too_big = true; + return 0; + } + + assert(med_sz < std::numeric_limits::max()); + assert(current_block_size < std::numeric_limits::max()); + + uint64_t product_hi; + uint64_t product_lo = mul128(base_reward, current_block_size * (2 * med_sz - current_block_size), &product_hi); + + uint64_t reward_hi; + uint64_t reward_lo; + div128_32(product_hi, product_lo, static_cast(med_sz), &reward_hi, &reward_lo); + div128_32(reward_hi, reward_lo, static_cast(med_sz), &reward_hi, &reward_lo); + assert(0 == reward_hi); + assert(reward_lo < base_reward); + + return reward_lo; + }else + return base_reward; + } + //------------------------------------------------------------------------------------ + uint8_t get_account_address_checksum(const public_address_outer_blob& bl) + { + const unsigned char* pbuf = reinterpret_cast(&bl); + uint8_t summ = 0; + for(size_t i = 0; i!= sizeof(public_address_outer_blob)-1; i++) + summ += pbuf[i]; + + return summ; + } + //----------------------------------------------------------------------- + std::string get_account_address_as_str(const account_public_address& adr) + { + return tools::base58::encode_addr(CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX, t_serializable_object_to_blob(adr)); + } + //----------------------------------------------------------------------- + bool is_coinbase(const transaction& tx) + { + if(tx.vin.size() != 1) + return false; + + if(tx.vin[0].type() != typeid(txin_gen)) + return false; + + return true; + } + //----------------------------------------------------------------------- + bool get_account_address_from_str(account_public_address& adr, const std::string& str) + { + if (2 * sizeof(public_address_outer_blob) != str.size()) + { + blobdata data; + uint64_t prefix; + if (!tools::base58::decode_addr(str, prefix, data)) + { + LOG_PRINT_L0("Invalid address format"); + return false; + } + + if (CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX != prefix) + { + LOG_PRINT_L0("Wrong address prefix: " << prefix << ", expected " << CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX); + return false; + } + + if (!::serialization::parse_binary(data, adr)) + { + LOG_PRINT_L0("Account public address keys can't be parsed"); + return false; + } + + if (!crypto::check_key(adr.m_spend_public_key) || !crypto::check_key(adr.m_view_public_key)) + { + LOG_PRINT_L0("Failed to validate address keys"); + return false; + } + } + else + { + // Old address format + std::string buff; + if(!string_tools::parse_hexstr_to_binbuff(str, buff)) + return false; + + if(buff.size()!=sizeof(public_address_outer_blob)) + { + LOG_PRINT_L0("Wrong public address size: " << buff.size() << ", expected size: " << sizeof(public_address_outer_blob)); + return false; + } + + public_address_outer_blob blob = *reinterpret_cast(buff.data()); + + + if(blob.m_ver > CRYPTONOTE_PUBLIC_ADDRESS_TEXTBLOB_VER) + { + LOG_PRINT_L0("Unknown version of public address: " << blob.m_ver << ", expected " << CRYPTONOTE_PUBLIC_ADDRESS_TEXTBLOB_VER); + return false; + } + + if(blob.check_sum != get_account_address_checksum(blob)) + { + LOG_PRINT_L0("Wrong public address checksum"); + return false; + } + + //we success + adr = blob.m_address; + } + + return true; + } + + bool operator ==(const cryptonote::transaction& a, const cryptonote::transaction& b) { + return cryptonote::get_transaction_hash(a) == cryptonote::get_transaction_hash(b); + } + + bool operator ==(const cryptonote::block& a, const cryptonote::block& b) { + return cryptonote::get_block_hash(a) == cryptonote::get_block_hash(b); + } +} + +//-------------------------------------------------------------------------------- +bool parse_hash256(const std::string str_hash, crypto::hash& hash) +{ + std::string buf; + bool res = epee::string_tools::parse_hexstr_to_binbuff(str_hash, buf); + if (!res || buf.size() != sizeof(crypto::hash)) + { + std::cout << "invalid hash format: <" << str_hash << '>' << std::endl; + return false; + } + else + { + buf.copy(reinterpret_cast(&hash), sizeof(crypto::hash)); + return true; + } +} diff --git a/src/cryptonote_core/cryptonote_basic_impl.h b/src/cryptonote_core/cryptonote_basic_impl.h new file mode 100644 index 000000000..f56d09a8e --- /dev/null +++ b/src/cryptonote_core/cryptonote_basic_impl.h @@ -0,0 +1,65 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include "cryptonote_basic.h" +#include "crypto/crypto.h" +#include "crypto/hash.h" + + +namespace cryptonote { + /************************************************************************/ + /* */ + /************************************************************************/ + template + struct array_hasher: std::unary_function + { + std::size_t operator()(const t_array& val) const + { + return boost::hash_range(&val.data[0], &val.data[sizeof(val.data)]); + } + }; + + +#pragma pack(push, 1) + struct public_address_outer_blob + { + uint8_t m_ver; + account_public_address m_address; + uint8_t check_sum; + }; +#pragma pack (pop) + + + /************************************************************************/ + /* Cryptonote helper functions */ + /************************************************************************/ + size_t get_max_block_size(); + size_t get_max_tx_size(); + uint64_t get_block_reward(std::vector& last_blocks_sizes, size_t current_block_size, bool& block_too_big, uint64_t already_generated_coins); + uint8_t get_account_address_checksum(const public_address_outer_blob& bl); + std::string get_account_address_as_str(const account_public_address& adr); + bool get_account_address_from_str(account_public_address& adr, const std::string& str); + bool is_coinbase(const transaction& tx); + + bool operator ==(const cryptonote::transaction& a, const cryptonote::transaction& b); + bool operator ==(const cryptonote::block& a, const cryptonote::block& b); +} + +template +std::ostream &print256(std::ostream &o, const T &v) { + return o << "<" << epee::string_tools::pod_to_hex(v) << ">"; +} + +bool parse_hash256(const std::string str_hash, crypto::hash& hash); + +namespace crypto { + inline std::ostream &operator <<(std::ostream &o, const crypto::public_key &v) { return print256(o, v); } + inline std::ostream &operator <<(std::ostream &o, const crypto::secret_key &v) { return print256(o, v); } + inline std::ostream &operator <<(std::ostream &o, const crypto::key_derivation &v) { return print256(o, v); } + inline std::ostream &operator <<(std::ostream &o, const crypto::key_image &v) { return print256(o, v); } + inline std::ostream &operator <<(std::ostream &o, const crypto::signature &v) { return print256(o, v); } + inline std::ostream &operator <<(std::ostream &o, const crypto::hash &v) { return print256(o, v); } +} diff --git a/src/cryptonote_core/cryptonote_boost_serialization.h b/src/cryptonote_core/cryptonote_boost_serialization.h new file mode 100644 index 000000000..80c497844 --- /dev/null +++ b/src/cryptonote_core/cryptonote_boost_serialization.h @@ -0,0 +1,143 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include "cryptonote_basic.h" +#include "common/unordered_containers_boost_serialization.h" +#include "crypto/crypto.h" + +//namespace cryptonote { +namespace boost +{ + namespace serialization + { + + //--------------------------------------------------- + template + inline void serialize(Archive &a, crypto::public_key &x, const boost::serialization::version_type ver) + { + a & reinterpret_cast(x); + } + template + inline void serialize(Archive &a, crypto::secret_key &x, const boost::serialization::version_type ver) + { + a & reinterpret_cast(x); + } + template + inline void serialize(Archive &a, crypto::key_derivation &x, const boost::serialization::version_type ver) + { + a & reinterpret_cast(x); + } + template + inline void serialize(Archive &a, crypto::key_image &x, const boost::serialization::version_type ver) + { + a & reinterpret_cast(x); + } + + template + inline void serialize(Archive &a, crypto::signature &x, const boost::serialization::version_type ver) + { + a & reinterpret_cast(x); + } + template + inline void serialize(Archive &a, crypto::hash &x, const boost::serialization::version_type ver) + { + a & reinterpret_cast(x); + } + + template + inline void serialize(Archive &a, cryptonote::txout_to_script &x, const boost::serialization::version_type ver) + { + a & x.keys; + a & x.script; + } + + + template + inline void serialize(Archive &a, cryptonote::txout_to_key &x, const boost::serialization::version_type ver) + { + a & x.key; + } + + template + inline void serialize(Archive &a, cryptonote::txout_to_scripthash &x, const boost::serialization::version_type ver) + { + a & x.hash; + } + + template + inline void serialize(Archive &a, cryptonote::txin_gen &x, const boost::serialization::version_type ver) + { + a & x.height; + } + + template + inline void serialize(Archive &a, cryptonote::txin_to_script &x, const boost::serialization::version_type ver) + { + a & x.prev; + a & x.prevout; + a & x.sigset; + } + + template + inline void serialize(Archive &a, cryptonote::txin_to_scripthash &x, const boost::serialization::version_type ver) + { + a & x.prev; + a & x.prevout; + a & x.script; + a & x.sigset; + } + + template + inline void serialize(Archive &a, cryptonote::txin_to_key &x, const boost::serialization::version_type ver) + { + a & x.amount; + a & x.key_offsets; + a & x.k_image; + } + + template + inline void serialize(Archive &a, cryptonote::tx_out &x, const boost::serialization::version_type ver) + { + a & x.amount; + a & x.target; + } + + + template + inline void serialize(Archive &a, cryptonote::transaction &x, const boost::serialization::version_type ver) + { + a & x.version; + a & x.unlock_time; + a & x.vin; + a & x.vout; + a & x.extra; + a & x.signatures; + } + + + template + inline void serialize(Archive &a, cryptonote::block &b, const boost::serialization::version_type ver) + { + a & b.major_version; + a & b.minor_version; + a & b.timestamp; + a & b.prev_id; + a & b.nonce; + //------------------ + a & b.miner_tx; + a & b.tx_hashes; + } +} +} + +//} diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp new file mode 100644 index 000000000..d5ab8d65a --- /dev/null +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -0,0 +1,519 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#include "include_base_utils.h" +using namespace epee; + +#include +#include +#include "cryptonote_core.h" +#include "common/command_line.h" +#include "common/util.h" +#include "warnings.h" +#include "crypto/crypto.h" +#include "cryptonote_config.h" +#include "cryptonote_format_utils.h" +#include "misc_language.h" + +DISABLE_VS_WARNINGS(4355) + +namespace cryptonote +{ + + //----------------------------------------------------------------------------------------------- + core::core(i_cryptonote_protocol* pprotocol): + m_mempool(m_blockchain_storage), + m_blockchain_storage(m_mempool), + m_miner(this), + m_miner_address(boost::value_initialized()), + m_starter_message_showed(false) + { + set_cryptonote_protocol(pprotocol); + } + void core::set_cryptonote_protocol(i_cryptonote_protocol* pprotocol) + { + if(pprotocol) + m_pprotocol = pprotocol; + else + m_pprotocol = &m_protocol_stub; + } + //----------------------------------------------------------------------------------- + void core::set_checkpoints(checkpoints&& chk_pts) + { + m_blockchain_storage.set_checkpoints(std::move(chk_pts)); + } + //----------------------------------------------------------------------------------- + void core::init_options(boost::program_options::options_description& /*desc*/) + { + } + //----------------------------------------------------------------------------------------------- + bool core::handle_command_line(const boost::program_options::variables_map& vm) + { + m_config_folder = command_line::get_arg(vm, command_line::arg_data_dir); + return true; + } + //----------------------------------------------------------------------------------------------- + uint64_t core::get_current_blockchain_height() + { + return m_blockchain_storage.get_current_blockchain_height(); + } + //----------------------------------------------------------------------------------------------- + bool core::get_blockchain_top(uint64_t& height, crypto::hash& top_id) + { + top_id = m_blockchain_storage.get_tail_id(height); + return true; + } + //----------------------------------------------------------------------------------------------- + bool core::get_blocks(uint64_t start_offset, size_t count, std::list& blocks, std::list& txs) + { + return m_blockchain_storage.get_blocks(start_offset, count, blocks, txs); + } + //----------------------------------------------------------------------------------------------- + bool core::get_blocks(uint64_t start_offset, size_t count, std::list& blocks) + { + return m_blockchain_storage.get_blocks(start_offset, count, blocks); + } //----------------------------------------------------------------------------------------------- + bool core::get_transactions(const std::vector& txs_ids, std::list& txs, std::list& missed_txs) + { + return m_blockchain_storage.get_transactions(txs_ids, txs, missed_txs); + } + //----------------------------------------------------------------------------------------------- + bool core::get_transaction(const crypto::hash &h, transaction &tx) + { + std::vector ids; + ids.push_back(h); + std::list ltx; + std::list missing; + if (m_blockchain_storage.get_transactions(ids, ltx, missing)) + { + if (ltx.size() > 0) + { + tx = *ltx.begin(); + return true; + } + } + + return false; + } + //----------------------------------------------------------------------------------------------- + bool core::get_alternative_blocks(std::list& blocks) + { + return m_blockchain_storage.get_alternative_blocks(blocks); + } + //----------------------------------------------------------------------------------------------- + size_t core::get_alternative_blocks_count() + { + return m_blockchain_storage.get_alternative_blocks_count(); + } + //----------------------------------------------------------------------------------------------- + bool core::init(const boost::program_options::variables_map& vm) + { + bool r = handle_command_line(vm); + + r = m_mempool.init(m_config_folder); + CHECK_AND_ASSERT_MES(r, false, "Failed to initialize memory pool"); + + r = m_blockchain_storage.init(m_config_folder); + CHECK_AND_ASSERT_MES(r, false, "Failed to initialize blockchain storage"); + + r = m_miner.init(vm); + CHECK_AND_ASSERT_MES(r, false, "Failed to initialize blockchain storage"); + + return load_state_data(); + } + //----------------------------------------------------------------------------------------------- + bool core::set_genesis_block(const block& b) + { + return m_blockchain_storage.reset_and_set_genesis_block(b); + } + //----------------------------------------------------------------------------------------------- + bool core::load_state_data() + { + // may be some code later + return true; + } + //----------------------------------------------------------------------------------------------- + bool core::deinit() + { + m_miner.stop(); + m_mempool.deinit(); + m_blockchain_storage.deinit(); + return true; + } + //----------------------------------------------------------------------------------------------- + bool core::handle_incoming_tx(const blobdata& tx_blob, tx_verification_context& tvc, bool keeped_by_block) + { + tvc = boost::value_initialized(); + //want to process all transactions sequentially + CRITICAL_REGION_LOCAL(m_incoming_tx_lock); + + if(tx_blob.size() > get_max_tx_size()) + { + LOG_PRINT_L0("WRONG TRANSACTION BLOB, too big size " << tx_blob.size() << ", rejected"); + tvc.m_verifivation_failed = true; + return false; + } + + crypto::hash tx_hash = null_hash; + crypto::hash tx_prefixt_hash = null_hash; + transaction tx; + + if(!parse_tx_from_blob(tx, tx_hash, tx_prefixt_hash, tx_blob)) + { + LOG_PRINT_L0("WRONG TRANSACTION BLOB, Failed to parse, rejected"); + tvc.m_verifivation_failed = true; + return false; + } + //std::cout << "!"<< tx.vin.size() << std::endl; + + if(!check_tx_syntax(tx)) + { + LOG_PRINT_L0("WRONG TRANSACTION BLOB, Failed to check tx " << tx_hash << " syntax, rejected"); + tvc.m_verifivation_failed = true; + return false; + } + + if(!check_tx_semantic(tx, keeped_by_block)) + { + LOG_PRINT_L0("WRONG TRANSACTION BLOB, Failed to check tx " << tx_hash << " semantic, rejected"); + tvc.m_verifivation_failed = true; + return false; + } + + bool r = add_new_tx(tx, tx_hash, tx_prefixt_hash, tx_blob.size(), tvc, keeped_by_block); + if(tvc.m_verifivation_failed) + {LOG_PRINT_RED_L0("Transaction verification failed: " << tx_hash);} + else if(tvc.m_verifivation_impossible) + {LOG_PRINT_RED_L0("Transaction verification impossible: " << tx_hash);} + + if(tvc.m_added_to_pool) + LOG_PRINT_L1("tx added: " << tx_hash); + return r; + } + //----------------------------------------------------------------------------------------------- + bool core::get_stat_info(core_stat_info& st_inf) + { + st_inf.mining_speed = m_miner.get_speed(); + st_inf.alternative_blocks = m_blockchain_storage.get_alternative_blocks_count(); + st_inf.blockchain_height = m_blockchain_storage.get_current_blockchain_height(); + st_inf.tx_pool_size = m_mempool.get_transactions_count(); + st_inf.top_block_id_str = epee::string_tools::pod_to_hex(m_blockchain_storage.get_tail_id()); + return true; + } + + //----------------------------------------------------------------------------------------------- + bool core::check_tx_semantic(const transaction& tx, bool keeped_by_block) + { + if(!tx.vin.size()) + { + LOG_PRINT_RED_L0("tx with empty inputs, rejected for tx id= " << get_transaction_hash(tx)); + return false; + } + + if(!check_inputs_types_supported(tx)) + { + LOG_PRINT_RED_L0("unsupported input types for tx id= " << get_transaction_hash(tx)); + return false; + } + + if(!check_outs_valid(tx)) + { + LOG_PRINT_RED_L0("tx with invalid outputs, rejected for tx id= " << get_transaction_hash(tx)); + return false; + } + + if(!check_money_overflow(tx)) + { + LOG_PRINT_RED_L0("tx have money overflow, rejected for tx id= " << get_transaction_hash(tx)); + return false; + } + + boost::uint64_t amount_in = 0; + get_inputs_money_amount(tx, amount_in); + boost::uint64_t amount_out = get_outs_money_amount(tx); + + if(amount_in <= amount_out) + { + LOG_PRINT_RED_L0("tx with wrong amounts: ins " << amount_in << ", outs " << amount_out << ", rejected for tx id= " << get_transaction_hash(tx)); + return false; + } + + if(!keeped_by_block && get_object_blobsize(tx) >= m_blockchain_storage.get_current_comulative_blocksize_limit() - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE) + { + LOG_PRINT_RED_L0("tx have to big size " << get_object_blobsize(tx) << ", expected not bigger than " << m_blockchain_storage.get_current_comulative_blocksize_limit() - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE); + return false; + } + + //check if tx use different key images + if(!check_tx_inputs_keyimages_diff(tx)) + { + LOG_PRINT_RED_L0("tx have to big size " << get_object_blobsize(tx) << ", expected not bigger than " << m_blockchain_storage.get_current_comulative_blocksize_limit() - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE); + return false; + } + + + return true; + } + //----------------------------------------------------------------------------------------------- + bool core::check_tx_inputs_keyimages_diff(const transaction& tx) + { + std::unordered_set ki; + BOOST_FOREACH(const auto& in, tx.vin) + { + CHECKED_GET_SPECIFIC_VARIANT(in, const txin_to_key, tokey_in, false); + if(!ki.insert(tokey_in.k_image).second) + return false; + } + return true; + } + //----------------------------------------------------------------------------------------------- + bool core::add_new_tx(const transaction& tx, tx_verification_context& tvc, bool keeped_by_block) + { + crypto::hash tx_hash = get_transaction_hash(tx); + crypto::hash tx_prefix_hash = get_transaction_prefix_hash(tx); + blobdata bl; + t_serializable_object_to_blob(tx, bl); + return add_new_tx(tx, tx_hash, tx_prefix_hash, bl.size(), tvc, keeped_by_block); + } + //----------------------------------------------------------------------------------------------- + size_t core::get_blockchain_total_transactions() + { + return m_blockchain_storage.get_total_transactions(); + } + //----------------------------------------------------------------------------------------------- + bool core::get_outs(uint64_t amount, std::list& pkeys) + { + return m_blockchain_storage.get_outs(amount, pkeys); + } + //----------------------------------------------------------------------------------------------- + bool core::add_new_tx(const transaction& tx, const crypto::hash& tx_hash, const crypto::hash& tx_prefix_hash, size_t blob_size, tx_verification_context& tvc, bool keeped_by_block) + { + if(m_mempool.have_tx(tx_hash)) + { + LOG_PRINT_L2("tx " << tx_hash << "already have transaction in tx_pool"); + return true; + } + + if(m_blockchain_storage.have_tx(tx_hash)) + { + LOG_PRINT_L2("tx " << tx_hash << " already have transaction in blockchain"); + return true; + } + + return m_mempool.add_tx(tx, tx_hash, blob_size, tvc, keeped_by_block); + } + //----------------------------------------------------------------------------------------------- + bool core::get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, const blobdata& ex_nonce) + { + return m_blockchain_storage.create_block_template(b, adr, diffic, height, ex_nonce); + } + //----------------------------------------------------------------------------------------------- + bool core::find_blockchain_supplement(const std::list& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp) + { + return m_blockchain_storage.find_blockchain_supplement(qblock_ids, resp); + } + //----------------------------------------------------------------------------------------------- + bool core::find_blockchain_supplement(const std::list& qblock_ids, std::list > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count) + { + return m_blockchain_storage.find_blockchain_supplement(qblock_ids, blocks, total_height, start_height, max_count); + } + //----------------------------------------------------------------------------------------------- + void core::print_blockchain(uint64_t start_index, uint64_t end_index) + { + m_blockchain_storage.print_blockchain(start_index, end_index); + } + //----------------------------------------------------------------------------------------------- + void core::print_blockchain_index() + { + m_blockchain_storage.print_blockchain_index(); + } + //----------------------------------------------------------------------------------------------- + void core::print_blockchain_outs(const std::string& file) + { + m_blockchain_storage.print_blockchain_outs(file); + } + //----------------------------------------------------------------------------------------------- + bool core::get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res) + { + return m_blockchain_storage.get_random_outs_for_amounts(req, res); + } + //----------------------------------------------------------------------------------------------- + bool core::get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector& indexs) + { + return m_blockchain_storage.get_tx_outputs_gindexs(tx_id, indexs); + } + //----------------------------------------------------------------------------------------------- + void core::pause_mine() + { + m_miner.pause(); + } + //----------------------------------------------------------------------------------------------- + void core::resume_mine() + { + m_miner.resume(); + } + //----------------------------------------------------------------------------------------------- + bool core::handle_block_found(block& b) + { + block_verification_context bvc = boost::value_initialized(); + m_miner.pause(); + m_blockchain_storage.add_new_block(b, bvc); + //anyway - update miner template + update_miner_block_template(); + m_miner.resume(); + + + CHECK_AND_ASSERT_MES(!bvc.m_verifivation_failed, false, "mined block failed verification"); + if(bvc.m_added_to_main_chain) + { + cryptonote_connection_context exclude_context = boost::value_initialized(); + NOTIFY_NEW_BLOCK::request arg = AUTO_VAL_INIT(arg); + arg.hop = 0; + arg.current_blockchain_height = m_blockchain_storage.get_current_blockchain_height(); + std::list missed_txs; + std::list txs; + m_blockchain_storage.get_transactions(b.tx_hashes, txs, missed_txs); + if(missed_txs.size() && m_blockchain_storage.get_block_id_by_height(get_block_height(b)) != get_block_hash(b)) + { + LOG_PRINT_L0("Block found but, seems that reorganize just happened after that, do not relay this block"); + return true; + } + CHECK_AND_ASSERT_MES(txs.size() == b.tx_hashes.size() && !missed_txs.size(), false, "cant find some transactions in found block:" << get_block_hash(b) << " txs.size()=" << txs.size() + << ", b.tx_hashes.size()=" << b.tx_hashes.size() << ", missed_txs.size()" << missed_txs.size()); + + block_to_blob(b, arg.b.block); + //pack transactions + BOOST_FOREACH(auto& tx, txs) + arg.b.txs.push_back(t_serializable_object_to_blob(tx)); + + m_pprotocol->relay_block(arg, exclude_context); + } + return bvc.m_added_to_main_chain; + } + //----------------------------------------------------------------------------------------------- + void core::on_synchronized() + { + m_miner.on_synchronized(); + } + bool core::get_backward_blocks_sizes(uint64_t from_height, std::vector& sizes, size_t count) + { + return m_blockchain_storage.get_backward_blocks_sizes(from_height, sizes, count); + } + //----------------------------------------------------------------------------------------------- + bool core::add_new_block(const block& b, block_verification_context& bvc) + { + return m_blockchain_storage.add_new_block(b, bvc); + } + //----------------------------------------------------------------------------------------------- + bool core::handle_incoming_block(const blobdata& block_blob, block_verification_context& bvc, bool update_miner_blocktemplate) + { + bvc = boost::value_initialized(); + if(block_blob.size() > get_max_block_size()) + { + LOG_PRINT_L0("WRONG BLOCK BLOB, too big size " << block_blob.size() << ", rejected"); + bvc.m_verifivation_failed = true; + return false; + } + + + block b = AUTO_VAL_INIT(b); + if(!parse_and_validate_block_from_blob(block_blob, b)) + { + LOG_PRINT_L0("Failed to parse and validate new block"); + bvc.m_verifivation_failed = true; + return false; + } + add_new_block(b, bvc); + if(update_miner_blocktemplate && bvc.m_added_to_main_chain) + update_miner_block_template(); + return true; + } + //----------------------------------------------------------------------------------------------- + crypto::hash core::get_tail_id() + { + return m_blockchain_storage.get_tail_id(); + } + //----------------------------------------------------------------------------------------------- + size_t core::get_pool_transactions_count() + { + return m_mempool.get_transactions_count(); + } + //----------------------------------------------------------------------------------------------- + bool core::have_block(const crypto::hash& id) + { + return m_blockchain_storage.have_block(id); + } + //----------------------------------------------------------------------------------------------- + bool core::parse_tx_from_blob(transaction& tx, crypto::hash& tx_hash, crypto::hash& tx_prefix_hash, const blobdata& blob) + { + return parse_and_validate_tx_from_blob(blob, tx, tx_hash, tx_prefix_hash); + } + //----------------------------------------------------------------------------------------------- + bool core::check_tx_syntax(const transaction& tx) + { + return true; + } + //----------------------------------------------------------------------------------------------- + bool core::get_pool_transactions(std::list& txs) + { + return m_mempool.get_transactions(txs); + } + //----------------------------------------------------------------------------------------------- + bool core::get_short_chain_history(std::list& ids) + { + return m_blockchain_storage.get_short_chain_history(ids); + } + //----------------------------------------------------------------------------------------------- + bool core::handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NOTIFY_RESPONSE_GET_OBJECTS::request& rsp, cryptonote_connection_context& context) + { + return m_blockchain_storage.handle_get_objects(arg, rsp); + } + //----------------------------------------------------------------------------------------------- + crypto::hash core::get_block_id_by_height(uint64_t height) + { + return m_blockchain_storage.get_block_id_by_height(height); + } + //----------------------------------------------------------------------------------------------- + bool core::get_block_by_hash(const crypto::hash &h, block &blk) { + return m_blockchain_storage.get_block_by_hash(h, blk); + } + //----------------------------------------------------------------------------------------------- + void core::get_all_known_block_ids(std::list &main, std::list &alt, std::list &invalid) { + m_blockchain_storage.get_all_known_block_ids(main, alt, invalid); + } + //----------------------------------------------------------------------------------------------- + std::string core::print_pool(bool short_format) + { + return m_mempool.print_pool(short_format); + } + //----------------------------------------------------------------------------------------------- + bool core::update_miner_block_template() + { + m_miner.on_block_chain_update(); + return true; + } + //----------------------------------------------------------------------------------------------- + bool core::on_idle() + { + if(!m_starter_message_showed) + { + LOG_PRINT_L0(ENDL << "**********************************************************************" << ENDL + << "The daemon will start synchronizing with the network. It may take up to several hours." << ENDL + << ENDL + << "You can set the level of process detailization by using command \"set_log \", where is either 0 (no details), 1 (current block height synchronized), or 2 (all details)." << ENDL + << ENDL + << "Use \"help\" command to see the list of available commands." << ENDL + << ENDL + << "Note: in case you need to interrupt the process, use \"exit\" command. Otherwise, the current progress won't be saved." << ENDL + << "**********************************************************************"); + m_starter_message_showed = true; + } + + m_store_blockchain_interval.do_call(boost::bind(&blockchain_storage::store_blockchain, &m_blockchain_storage)); + m_miner.on_idle(); + return true; + } + //----------------------------------------------------------------------------------------------- +} diff --git a/src/cryptonote_core/cryptonote_core.h b/src/cryptonote_core/cryptonote_core.h new file mode 100644 index 000000000..c298451e8 --- /dev/null +++ b/src/cryptonote_core/cryptonote_core.h @@ -0,0 +1,130 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include +#include + +#include "p2p/net_node_common.h" +#include "cryptonote_protocol/cryptonote_protocol_handler_common.h" +#include "storages/portable_storage_template_helper.h" +#include "tx_pool.h" +#include "blockchain_storage.h" +#include "miner.h" +#include "connection_context.h" +#include "cryptonote_core/cryptonote_stat_info.h" +#include "warnings.h" +#include "crypto/hash.h" + +PUSH_WARNINGS +DISABLE_VS_WARNINGS(4355) + +namespace cryptonote +{ + /************************************************************************/ + /* */ + /************************************************************************/ + class core: public i_miner_handler + { + public: + core(i_cryptonote_protocol* pprotocol); + bool handle_get_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, NOTIFY_RESPONSE_GET_OBJECTS::request& rsp, cryptonote_connection_context& context); + bool on_idle(); + bool handle_incoming_tx(const blobdata& tx_blob, tx_verification_context& tvc, bool keeped_by_block); + bool handle_incoming_block(const blobdata& block_blob, block_verification_context& bvc, bool update_miner_blocktemplate = true); + i_cryptonote_protocol* get_protocol(){return m_pprotocol;} + + //-------------------- i_miner_handler ----------------------- + virtual bool handle_block_found( block& b); + virtual bool get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, const blobdata& ex_nonce); + + + miner& get_miner(){return m_miner;} + static void init_options(boost::program_options::options_description& desc); + bool init(const boost::program_options::variables_map& vm); + bool set_genesis_block(const block& b); + bool deinit(); + uint64_t get_current_blockchain_height(); + bool get_blockchain_top(uint64_t& heeight, crypto::hash& top_id); + bool get_blocks(uint64_t start_offset, size_t count, std::list& blocks, std::list& txs); + bool get_blocks(uint64_t start_offset, size_t count, std::list& blocks); + template + bool get_blocks(const t_ids_container& block_ids, t_blocks_container& blocks, t_missed_container& missed_bs) + { + return m_blockchain_storage.get_blocks(block_ids, blocks, missed_bs); + } + crypto::hash get_block_id_by_height(uint64_t height); + bool get_transactions(const std::vector& txs_ids, std::list& txs, std::list& missed_txs); + bool get_transaction(const crypto::hash &h, transaction &tx); + bool get_block_by_hash(const crypto::hash &h, block &blk); + void get_all_known_block_ids(std::list &main, std::list &alt, std::list &invalid); + + bool get_alternative_blocks(std::list& blocks); + size_t get_alternative_blocks_count(); + + void set_cryptonote_protocol(i_cryptonote_protocol* pprotocol); + void set_checkpoints(checkpoints&& chk_pts); + + bool get_pool_transactions(std::list& txs); + size_t get_pool_transactions_count(); + size_t get_blockchain_total_transactions(); + bool get_outs(uint64_t amount, std::list& pkeys); + bool have_block(const crypto::hash& id); + bool get_short_chain_history(std::list& ids); + bool find_blockchain_supplement(const std::list& qblock_ids, NOTIFY_RESPONSE_CHAIN_ENTRY::request& resp); + bool find_blockchain_supplement(const std::list& qblock_ids, std::list > >& blocks, uint64_t& total_height, uint64_t& start_height, size_t max_count); + bool get_stat_info(core_stat_info& st_inf); + bool get_backward_blocks_sizes(uint64_t from_height, std::vector& sizes, size_t count); + bool get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector& indexs); + crypto::hash get_tail_id(); + bool get_random_outs_for_amounts(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res); + void pause_mine(); + void resume_mine(); + blockchain_storage& get_blockchain_storage(){return m_blockchain_storage;} + //debug functions + void print_blockchain(uint64_t start_index, uint64_t end_index); + void print_blockchain_index(); + std::string print_pool(bool short_format); + void print_blockchain_outs(const std::string& file); + void on_synchronized(); + + private: + bool add_new_tx(const transaction& tx, const crypto::hash& tx_hash, const crypto::hash& tx_prefix_hash, size_t blob_size, tx_verification_context& tvc, bool keeped_by_block); + bool add_new_tx(const transaction& tx, tx_verification_context& tvc, bool keeped_by_block); + bool add_new_block(const block& b, block_verification_context& bvc); + bool load_state_data(); + bool parse_tx_from_blob(transaction& tx, crypto::hash& tx_hash, crypto::hash& tx_prefix_hash, const blobdata& blob); + + bool check_tx_syntax(const transaction& tx); + //check correct values, amounts and all lightweight checks not related with database + bool check_tx_semantic(const transaction& tx, bool keeped_by_block); + //check if tx already in memory pool or in main blockchain + + bool is_key_image_spent(const crypto::key_image& key_im); + + bool check_tx_ring_signature(const txin_to_key& tx, const crypto::hash& tx_prefix_hash, const std::vector& sig); + bool is_tx_spendtime_unlocked(uint64_t unlock_time); + bool update_miner_block_template(); + bool handle_command_line(const boost::program_options::variables_map& vm); + bool on_update_blocktemplate_interval(); + bool check_tx_inputs_keyimages_diff(const transaction& tx); + + + tx_memory_pool m_mempool; + blockchain_storage m_blockchain_storage; + i_cryptonote_protocol* m_pprotocol; + critical_section m_incoming_tx_lock; + //m_miner and m_miner_addres are probably temporary here + miner m_miner; + account_public_address m_miner_address; + std::string m_config_folder; + cryptonote_protocol_stub m_protocol_stub; + math_helper::once_a_time_seconds<60*60*12, false> m_store_blockchain_interval; + friend class tx_validate_inputs; + std::atomic m_starter_message_showed; + }; +} + +POP_WARNINGS diff --git a/src/cryptonote_core/cryptonote_format_utils.cpp b/src/cryptonote_core/cryptonote_format_utils.cpp new file mode 100644 index 000000000..fbcadc081 --- /dev/null +++ b/src/cryptonote_core/cryptonote_format_utils.cpp @@ -0,0 +1,710 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "include_base_utils.h" +using namespace epee; + +#include "cryptonote_format_utils.h" +#include +#include "cryptonote_config.h" +#include "miner.h" +#include "crypto/crypto.h" +#include "crypto/hash.h" + +namespace cryptonote +{ + //--------------------------------------------------------------- + void get_transaction_prefix_hash(const transaction_prefix& tx, crypto::hash& h) + { + std::ostringstream s; + binary_archive a(s); + ::serialization::serialize(a, const_cast(tx)); + crypto::cn_fast_hash(s.str().data(), s.str().size(), h); + } + //--------------------------------------------------------------- + crypto::hash get_transaction_prefix_hash(const transaction_prefix& tx) + { + crypto::hash h = null_hash; + get_transaction_prefix_hash(tx, h); + return h; + } + //--------------------------------------------------------------- + bool parse_and_validate_tx_from_blob(const blobdata& tx_blob, transaction& tx) + { + std::stringstream ss; + ss << tx_blob; + binary_archive ba(ss); + bool r = ::serialization::serialize(ba, tx); + CHECK_AND_ASSERT_MES(r, false, "Failed to parse transaction from blob"); + return true; + } + //--------------------------------------------------------------- + bool parse_and_validate_tx_from_blob(const blobdata& tx_blob, transaction& tx, crypto::hash& tx_hash, crypto::hash& tx_prefix_hash) + { + std::stringstream ss; + ss << tx_blob; + binary_archive ba(ss); + bool r = ::serialization::serialize(ba, tx); + CHECK_AND_ASSERT_MES(r, false, "Failed to parse transaction from blob"); + //TODO: validate tx + + crypto::cn_fast_hash(tx_blob.data(), tx_blob.size(), tx_hash); + get_transaction_prefix_hash(tx, tx_prefix_hash); + return true; + } + //--------------------------------------------------------------- + bool construct_miner_tx(uint64_t height, uint64_t already_generated_coins, const account_public_address& miner_address, transaction& tx, uint64_t fee, std::vector& blocks_sizes, size_t current_block_size, size_t max_outs) + { + return construct_miner_tx(height, already_generated_coins, miner_address, tx, fee, blocks_sizes, current_block_size, blobdata(), max_outs); + } + //--------------------------------------------------------------- + bool construct_miner_tx(uint64_t height, uint64_t already_generated_coins, const account_public_address& miner_address, transaction& tx, uint64_t fee, std::vector& blocks_sizes, size_t current_block_size, const blobdata& extra_nonce, size_t max_outs) + { + tx.vin.clear(); + tx.vout.clear(); + tx.extra.clear(); + + keypair txkey = keypair::generate(); + add_tx_pub_key_to_extra(tx, txkey.pub); + if(extra_nonce.size()) + if(!add_tx_extra_nonce(tx, extra_nonce)) + return false; + + txin_gen in; + in.height = height; + + bool block_too_big = false; + uint64_t block_reward = get_block_reward(blocks_sizes, current_block_size, block_too_big, already_generated_coins) + fee; + if(block_too_big) + { + LOG_PRINT_L0("Block is too big"); + return false; + } + + std::vector out_amounts; + decompose_amount_into_digits(block_reward, DEFAULT_FEE, + [&out_amounts](uint64_t a_chunk) { out_amounts.push_back(a_chunk); }, + [&out_amounts](uint64_t a_dust) { out_amounts.push_back(a_dust); }); + + CHECK_AND_ASSERT_MES(1 <= max_outs, false, "max_out must be non-zero"); + while (max_outs < out_amounts.size()) + { + out_amounts[out_amounts.size() - 2] += out_amounts.back(); + out_amounts.resize(out_amounts.size() - 1); + } + + size_t summary_amounts = 0; + for (size_t no = 0; no < out_amounts.size(); no++) + { + crypto::key_derivation derivation = AUTO_VAL_INIT(derivation);; + crypto::public_key out_eph_public_key = AUTO_VAL_INIT(out_eph_public_key); + bool r = crypto::generate_key_derivation(miner_address.m_view_public_key, txkey.sec, derivation); + CHECK_AND_ASSERT_MES(r, false, "while creating outs: failed to generate_key_derivation(" << miner_address.m_view_public_key << ", " << txkey.sec << ")"); + + r = crypto::derive_public_key(derivation, no, miner_address.m_spend_public_key, out_eph_public_key); + CHECK_AND_ASSERT_MES(r, false, "while creating outs: failed to derive_public_key(" << derivation << ", " << no << ", "<< miner_address.m_spend_public_key << ")"); + + txout_to_key tk; + tk.key = out_eph_public_key; + + tx_out out; + summary_amounts += out.amount = out_amounts[no]; + out.target = tk; + tx.vout.push_back(out); + } + + CHECK_AND_ASSERT_MES(summary_amounts == block_reward, false, "Failed to construct miner tx, summary_amounts = " << summary_amounts << " not equal block_reward = " << block_reward); + + tx.version = CURRENT_TRANSACTION_VERSION; + //lock + tx.unlock_time = height + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW; + tx.vin.push_back(in); + //LOG_PRINT("MINER_TX generated ok, block_reward=" << print_money(block_reward) << "(" << print_money(block_reward - fee) << "+" << print_money(fee) + // << "), current_block_size=" << current_block_size << ", already_generated_coins=" << already_generated_coins << ", tx_id=" << get_transaction_hash(tx), LOG_LEVEL_2); + return true; + } + //--------------------------------------------------------------- + bool generate_key_image_helper(const account_keys& ack, const crypto::public_key& tx_public_key, size_t real_output_index, keypair& in_ephemeral, crypto::key_image& ki) + { + crypto::key_derivation recv_derivation = AUTO_VAL_INIT(recv_derivation); + bool r = crypto::generate_key_derivation(tx_public_key, ack.m_view_secret_key, recv_derivation); + CHECK_AND_ASSERT_MES(r, false, "key image helper: failed to generate_key_derivation(" << tx_public_key << ", " << ack.m_view_secret_key << ")"); + + r = crypto::derive_public_key(recv_derivation, real_output_index, ack.m_account_address.m_spend_public_key, in_ephemeral.pub); + CHECK_AND_ASSERT_MES(r, false, "key image helper: failed to derive_public_key(" << recv_derivation << ", " << real_output_index << ", " << ack.m_account_address.m_spend_public_key << ")"); + + crypto::derive_secret_key(recv_derivation, real_output_index, ack.m_spend_secret_key, in_ephemeral.sec); + + crypto::generate_key_image(in_ephemeral.pub, in_ephemeral.sec, ki); + return true; + } + //--------------------------------------------------------------- + uint64_t power_integral(uint64_t a, uint64_t b) + { + if(b == 0) + return 1; + uint64_t total = a; + for(uint64_t i = 1; i != b; i++) + total *= a; + return total; + } + //--------------------------------------------------------------- + bool parse_amount(uint64_t& amount, const std::string& str_amount_) + { + std::vector pars; + std::string str_amount = str_amount_; + boost::algorithm::trim(str_amount); + if(!str_amount.size()) + return false; + if(str_amount[0] == '-') + return false; + + boost::split(pars, str_amount, boost::is_any_of("."), boost::token_compress_on ); + if(pars.size() > 2 || pars.size() < 1) + return false; + uint64_t left = 0; + if(!string_tools::get_xtype_from_string(left, pars[0])) + return false; + amount = left * power_integral(10, CRYPTONOTE_DISPLAY_DECIMAL_POINT); + if(pars.size() == 2) + { + uint64_t right = 0; + if(pars[1].size() > 8 ) + return false; + if(pars[1].size() < 8 ) + pars[1].append(8 - pars[1].size(), '0'); + if(!string_tools::get_xtype_from_string(right, pars[1])) + return false; + amount += right; + } + return true; + } + //--------------------------------------------------------------- + bool get_tx_fee(const transaction& tx, uint64_t & fee) + { + uint64_t amount_in = 0; + uint64_t amount_out = 0; + BOOST_FOREACH(auto& in, tx.vin) + { + CHECK_AND_ASSERT_MES(in.type() == typeid(txin_to_key), 0, "unexpected type id in transaction"); + amount_in += boost::get(in).amount; + } + BOOST_FOREACH(auto& o, tx.vout) + amount_out += o.amount; + + CHECK_AND_ASSERT_MES(amount_in >= amount_out, false, "transaction spend (" <(&tx.extra[i+1]); + i += 1 + sizeof(crypto::public_key); + tx_extra_tag_pubkey_found = true; + continue; + }else if(tx.extra[i] == TX_EXTRA_NONCE) + { + //CHECK_AND_ASSERT_MES(is_coinbase(tx), false, "Failed to parse transaction extra (TX_EXTRA_NONCE can be only in coinbase) in tx " << get_transaction_hash(tx)); + CHECK_AND_ASSERT_MES(!tx_extra_extra_nonce_found, false, "Failed to parse transaction extra (duplicate TX_EXTRA_NONCE entry) in tx " << get_transaction_hash(tx)); + CHECK_AND_ASSERT_MES(tx.extra.size()-1-i >= 1, false, "Failed to parse transaction extra (TX_EXTRA_NONCE have not enough bytes) in tx " << get_transaction_hash(tx)); + ++i; + CHECK_AND_ASSERT_MES(tx.extra.size()-1-i >= tx.extra[i], false, "Failed to parse transaction extra (TX_EXTRA_NONCE have wrong bytes counter) in tx " << get_transaction_hash(tx)); + tx_extra_extra_nonce_found = true; + i += tx.extra[i];//actually don't need to extract it now, just skip + } + else if(!tx.extra[i]) + { + padding_started = true; + continue; + } + ++i; + } + return true; + } + //--------------------------------------------------------------- + bool add_tx_pub_key_to_extra(transaction& tx, const crypto::public_key& tx_pub_key) + { + tx.extra.resize(tx.extra.size() + 1 + sizeof(crypto::public_key)); + tx.extra[tx.extra.size() - 1 - sizeof(crypto::public_key)] = TX_EXTRA_TAG_PUBKEY; + *reinterpret_cast(&tx.extra[tx.extra.size() - sizeof(crypto::public_key)]) = tx_pub_key; + return true; + } + //--------------------------------------------------------------- + bool add_tx_extra_nonce(transaction& tx, const blobdata& extra_nonce) + { + CHECK_AND_ASSERT_MES(extra_nonce.size() <=255, false, "extra nonce could be 255 bytes max"); + size_t start_pos = tx.extra.size(); + tx.extra.resize(tx.extra.size() + 2 + extra_nonce.size()); + //write tag + tx.extra[start_pos] = TX_EXTRA_NONCE; + //write len + ++start_pos; + tx.extra[start_pos] = static_cast(extra_nonce.size()); + //write data + ++start_pos; + memcpy(&tx.extra[start_pos], extra_nonce.data(), extra_nonce.size()); + return true; + } + //--------------------------------------------------------------- + bool construct_tx(const account_keys& sender_account_keys, const std::vector& sources, const std::vector& destinations, transaction& tx, uint64_t unlock_time) + { + tx.vin.clear(); + tx.vout.clear(); + tx.signatures.clear(); + tx.extra.clear(); + + tx.version = CURRENT_TRANSACTION_VERSION; + tx.unlock_time = unlock_time; + + keypair txkey = keypair::generate(); + add_tx_pub_key_to_extra(tx, txkey.pub); + + struct input_generation_context_data + { + keypair in_ephemeral; + }; + std::vector in_contexts; + + + uint64_t summary_inputs_money = 0; + //fill inputs + BOOST_FOREACH(const tx_source_entry& src_entr, sources) + { + if(src_entr.real_output >= src_entr.outputs.size()) + { + LOG_ERROR("real_output index (" << src_entr.real_output << ")bigger than output_keys.size()=" << src_entr.outputs.size()); + return false; + } + summary_inputs_money += src_entr.amount; + + //key_derivation recv_derivation; + in_contexts.push_back(input_generation_context_data()); + keypair& in_ephemeral = in_contexts.back().in_ephemeral; + crypto::key_image img; + if(!generate_key_image_helper(sender_account_keys, src_entr.real_out_tx_key, src_entr.real_output_in_tx_index, in_ephemeral, img)) + return false; + + //check that derivated key is equal with real output key + if( !(in_ephemeral.pub == src_entr.outputs[src_entr.real_output].second) ) + { + LOG_ERROR("derived public key missmatch with output public key! "<< ENDL << "derived_key:" + << string_tools::pod_to_hex(in_ephemeral.pub) << ENDL << "real output_public_key:" + << string_tools::pod_to_hex(src_entr.outputs[src_entr.real_output].second) ); + return false; + } + + //put key image into tx input + txin_to_key input_to_key; + input_to_key.amount = src_entr.amount; + input_to_key.k_image = img; + + //fill outputs array and use relative offsets + BOOST_FOREACH(const tx_source_entry::output_entry& out_entry, src_entr.outputs) + input_to_key.key_offsets.push_back(out_entry.first); + + input_to_key.key_offsets = absolute_output_offsets_to_relative(input_to_key.key_offsets); + tx.vin.push_back(input_to_key); + } + + // "Shuffle" outs + std::vector shuffled_dsts(destinations); + std::sort(shuffled_dsts.begin(), shuffled_dsts.end(), [](const tx_destination_entry& de1, const tx_destination_entry& de2) { return de1.amount < de2.amount; } ); + + uint64_t summary_outs_money = 0; + //fill outputs + size_t output_index = 0; + BOOST_FOREACH(const tx_destination_entry& dst_entr, shuffled_dsts) + { + CHECK_AND_ASSERT_MES(dst_entr.amount > 0, false, "Destination with wrong amount: " << dst_entr.amount); + crypto::key_derivation derivation; + crypto::public_key out_eph_public_key; + bool r = crypto::generate_key_derivation(dst_entr.addr.m_view_public_key, txkey.sec, derivation); + CHECK_AND_ASSERT_MES(r, false, "at creation outs: failed to generate_key_derivation(" << dst_entr.addr.m_view_public_key << ", " << txkey.sec << ")"); + + r = crypto::derive_public_key(derivation, output_index, dst_entr.addr.m_spend_public_key, out_eph_public_key); + CHECK_AND_ASSERT_MES(r, false, "at creation outs: failed to derive_public_key(" << derivation << ", " << output_index << ", "<< dst_entr.addr.m_spend_public_key << ")"); + + tx_out out; + out.amount = dst_entr.amount; + txout_to_key tk; + tk.key = out_eph_public_key; + out.target = tk; + tx.vout.push_back(out); + output_index++; + summary_outs_money += dst_entr.amount; + } + + //check money + if(summary_outs_money > summary_inputs_money ) + { + LOG_ERROR("Transaction inputs money ("<< summary_inputs_money << ") less than outputs money (" << summary_outs_money << ")"); + return false; + } + + + //generate ring signatures + crypto::hash tx_prefix_hash; + get_transaction_prefix_hash(tx, tx_prefix_hash); + + std::stringstream ss_ring_s; + size_t i = 0; + BOOST_FOREACH(const tx_source_entry& src_entr, sources) + { + ss_ring_s << "pub_keys:" << ENDL; + std::vector keys_ptrs; + BOOST_FOREACH(const tx_source_entry::output_entry& o, src_entr.outputs) + { + keys_ptrs.push_back(&o.second); + ss_ring_s << o.second << ENDL; + } + + tx.signatures.push_back(std::vector()); + std::vector& sigs = tx.signatures.back(); + sigs.resize(src_entr.outputs.size()); + crypto::generate_ring_signature(tx_prefix_hash, boost::get(tx.vin[i]).k_image, keys_ptrs, in_contexts[i].in_ephemeral.sec, src_entr.real_output, sigs.data()); + ss_ring_s << "signatures:" << ENDL; + std::for_each(sigs.begin(), sigs.end(), [&](const crypto::signature& s){ss_ring_s << s << ENDL;}); + ss_ring_s << "prefix_hash:" << tx_prefix_hash << ENDL << "in_ephemeral_key: " << in_contexts[i].in_ephemeral.sec << ENDL << "real_output: " << src_entr.real_output; + i++; + } + + LOG_PRINT2("construct_tx.log", "transaction_created: " << get_transaction_hash(tx) << ENDL << obj_to_json_str(tx) << ENDL << ss_ring_s.str() , LOG_LEVEL_3); + + return true; + } + //--------------------------------------------------------------- + bool get_inputs_money_amount(const transaction& tx, uint64_t& money) + { + money = 0; + BOOST_FOREACH(const auto& in, tx.vin) + { + CHECKED_GET_SPECIFIC_VARIANT(in, const txin_to_key, tokey_in, false); + money += tokey_in.amount; + } + return true; + } + //--------------------------------------------------------------- + uint64_t get_block_height(const block& b) + { + CHECK_AND_ASSERT_MES(b.miner_tx.vin.size() == 1, 0, "wrong miner tx in block: " << get_block_hash(b) << ", b.miner_tx.vin.size() != 1"); + CHECKED_GET_SPECIFIC_VARIANT(b.miner_tx.vin[0], const txin_gen, coinbase_in, 0); + return coinbase_in.height; + } + //--------------------------------------------------------------- + bool check_inputs_types_supported(const transaction& tx) + { + BOOST_FOREACH(const auto& in, tx.vin) + { + CHECK_AND_ASSERT_MES(in.type() == typeid(txin_to_key), false, "wrong variant type: " + << in.type().name() << ", expected " << typeid(txin_to_key).name() + << ", in transaction id=" << get_transaction_hash(tx)); + + } + return true; + } + //----------------------------------------------------------------------------------------------- + bool check_outs_valid(const transaction& tx) + { + BOOST_FOREACH(const tx_out& out, tx.vout) + { + CHECK_AND_ASSERT_MES(out.target.type() == typeid(txout_to_key), false, "wrong variant type: " + << out.target.type().name() << ", expected " << typeid(txout_to_key).name() + << ", in transaction id=" << get_transaction_hash(tx)); + + CHECK_AND_NO_ASSERT_MES(0 < out.amount, false, "zero amount ouput in transaction id=" << get_transaction_hash(tx)); + + if(!check_key(boost::get(out.target).key)) + return false; + } + return true; + } + //----------------------------------------------------------------------------------------------- + bool check_money_overflow(const transaction& tx) + { + return check_inputs_overflow(tx) && check_outs_overflow(tx); + } + //--------------------------------------------------------------- + bool check_inputs_overflow(const transaction& tx) + { + uint64_t money = 0; + BOOST_FOREACH(const auto& in, tx.vin) + { + CHECKED_GET_SPECIFIC_VARIANT(in, const txin_to_key, tokey_in, false); + if(money > tokey_in.amount + money) + return false; + money += tokey_in.amount; + } + return true; + } + //--------------------------------------------------------------- + bool check_outs_overflow(const transaction& tx) + { + uint64_t money = 0; + BOOST_FOREACH(const auto& o, tx.vout) + { + if(money > o.amount + money) + return false; + money += o.amount; + } + return true; + } + //--------------------------------------------------------------- + uint64_t get_outs_money_amount(const transaction& tx) + { + uint64_t outputs_amount = 0; + BOOST_FOREACH(const auto& o, tx.vout) + outputs_amount += o.amount; + return outputs_amount; + } + //--------------------------------------------------------------- + std::string short_hash_str(const crypto::hash& h) + { + std::string res = string_tools::pod_to_hex(h); + CHECK_AND_ASSERT_MES(res.size() == 64, res, "wrong hash256 with string_tools::pod_to_hex conversion"); + auto erased_pos = res.erase(8, 48); + res.insert(8, "...."); + return res; + } + //--------------------------------------------------------------- + bool is_out_to_acc(const account_keys& acc, const txout_to_key& out_key, const crypto::public_key& tx_pub_key, size_t output_index) + { + crypto::key_derivation derivation; + generate_key_derivation(tx_pub_key, acc.m_view_secret_key, derivation); + crypto::public_key pk; + derive_public_key(derivation, output_index, acc.m_account_address.m_spend_public_key, pk); + return pk == out_key.key; + } + //--------------------------------------------------------------- + bool lookup_acc_outs(const account_keys& acc, const transaction& tx, std::vector& outs, uint64_t& money_transfered) + { + return lookup_acc_outs(acc, tx, get_tx_pub_key_from_extra(tx), outs, money_transfered); + } + //--------------------------------------------------------------- + bool lookup_acc_outs(const account_keys& acc, const transaction& tx, const crypto::public_key& tx_pub_key, std::vector& outs, uint64_t& money_transfered) + { + money_transfered = 0; + size_t i = 0; + BOOST_FOREACH(const tx_out& o, tx.vout) + { + CHECK_AND_ASSERT_MES(o.target.type() == typeid(txout_to_key), false, "wrong type id in transaction out" ); + if(is_out_to_acc(acc, boost::get(o.target), tx_pub_key, i)) + { + outs.push_back(i); + money_transfered += o.amount; + } + i++; + } + return true; + } + //--------------------------------------------------------------- + void get_blob_hash(const blobdata& blob, crypto::hash& res) + { + cn_fast_hash(blob.data(), blob.size(), res); + } + //--------------------------------------------------------------- + std::string print_money(uint64_t amount) + { + std::string s = std::to_string(amount); + if(s.size() < CRYPTONOTE_DISPLAY_DECIMAL_POINT+1) + { + s.insert(0, CRYPTONOTE_DISPLAY_DECIMAL_POINT+1 - s.size(), '0'); + } + s.insert(s.size() - CRYPTONOTE_DISPLAY_DECIMAL_POINT, "."); + return s; + } + //--------------------------------------------------------------- + crypto::hash get_blob_hash(const blobdata& blob) + { + crypto::hash h = null_hash; + get_blob_hash(blob, h); + return h; + } + //--------------------------------------------------------------- + crypto::hash get_transaction_hash(const transaction& t) + { + crypto::hash h = null_hash; + size_t blob_size = 0; + get_object_hash(t, h, blob_size); + return h; + } + //--------------------------------------------------------------- + bool get_transaction_hash(const transaction& t, crypto::hash& res) + { + size_t blob_size = 0; + return get_object_hash(t, res, blob_size); + } + //--------------------------------------------------------------- + bool get_transaction_hash(const transaction& t, crypto::hash& res, size_t& blob_size) + { + return get_object_hash(t, res, blob_size); + } + //--------------------------------------------------------------- + blobdata get_block_hashing_blob(const block& b) + { + blobdata blob = t_serializable_object_to_blob(static_cast(b)); + crypto::hash tree_root_hash = get_tx_tree_hash(b); + blob.append((const char*)&tree_root_hash, sizeof(tree_root_hash )); + blob.append(tools::get_varint_data(b.tx_hashes.size()+1)); + return blob; + } + //--------------------------------------------------------------- + bool get_block_hash(const block& b, crypto::hash& res) + { + return get_object_hash(get_block_hashing_blob(b), res); + } + //--------------------------------------------------------------- + crypto::hash get_block_hash(const block& b) + { + crypto::hash p = null_hash; + get_block_hash(b, p); + return p; + } + //--------------------------------------------------------------- + bool generate_genesis_block(block& bl) + { + //genesis block + bl = boost::value_initialized(); + + + account_public_address ac = boost::value_initialized(); + std::vector sz; + construct_miner_tx(0, 0, ac, bl.miner_tx, 0, sz, 0, 1); // zero fee in genesis + blobdata txb = tx_to_blob(bl.miner_tx); + std::string hex_tx_represent = string_tools::buff_to_hex_nodelimer(txb); + + //hard code coinbase tx in genesis block, because "tru" generating tx use random, but genesis should be always the same + std::string genesis_coinbase_tx_hex = "010a01ff0001ffffffffffff0f029b2e4c0281c0b02e7c53291a94d1d0cbff8883f8024f5142ee494ffbbd08807121013c086a48c15fb637a96991bc6d53caf77068b5ba6eeb3c82357228c49790584a"; + + blobdata tx_bl; + string_tools::parse_hexstr_to_binbuff(genesis_coinbase_tx_hex, tx_bl); + bool r = parse_and_validate_tx_from_blob(tx_bl, bl.miner_tx); + CHECK_AND_ASSERT_MES(r, false, "failed to parse coinbase tx from hard coded blob"); + bl.major_version = CURRENT_BLOCK_MAJOR_VERSION; + bl.minor_version = CURRENT_BLOCK_MINOR_VERSION; + bl.timestamp = 0; + bl.nonce = 70; + miner::find_nonce_for_given_block(bl, 1, 0); + return true; + } + //--------------------------------------------------------------- + bool get_block_longhash(const block& b, crypto::hash& res, uint64_t height) + { + block b_local = b; //workaround to avoid const errors with do_serialize + blobdata bd = get_block_hashing_blob(b); + crypto::cn_slow_hash(bd.data(), bd.size(), res); + return true; + } + //--------------------------------------------------------------- + std::vector relative_output_offsets_to_absolute(const std::vector& off) + { + std::vector res = off; + for(size_t i = 1; i < res.size(); i++) + res[i] += res[i-1]; + return res; + } + //--------------------------------------------------------------- + std::vector absolute_output_offsets_to_relative(const std::vector& off) + { + std::vector res = off; + if(!off.size()) + return res; + std::sort(res.begin(), res.end());//just to be sure, actually it is already should be sorted + for(size_t i = res.size()-1; i != 0; i--) + res[i] -= res[i-1]; + + return res; + } + //--------------------------------------------------------------- + crypto::hash get_block_longhash(const block& b, uint64_t height) + { + crypto::hash p = null_hash; + get_block_longhash(b, p, height); + return p; + } + //--------------------------------------------------------------- + bool parse_and_validate_block_from_blob(const blobdata& b_blob, block& b) + { + std::stringstream ss; + ss << b_blob; + binary_archive ba(ss); + bool r = ::serialization::serialize(ba, b); + CHECK_AND_ASSERT_MES(r, false, "Failed to parse block from blob"); + return true; + } + //--------------------------------------------------------------- + blobdata block_to_blob(const block& b) + { + return t_serializable_object_to_blob(b); + } + //--------------------------------------------------------------- + bool block_to_blob(const block& b, blobdata& b_blob) + { + return t_serializable_object_to_blob(b, b_blob); + } + //--------------------------------------------------------------- + blobdata tx_to_blob(const transaction& tx) + { + return t_serializable_object_to_blob(tx); + } + //--------------------------------------------------------------- + bool tx_to_blob(const transaction& tx, blobdata& b_blob) + { + return t_serializable_object_to_blob(tx, b_blob); + } + //--------------------------------------------------------------- + void get_tx_tree_hash(const std::vector& tx_hashes, crypto::hash& h) + { + tree_hash(tx_hashes.data(), tx_hashes.size(), h); + } + //--------------------------------------------------------------- + crypto::hash get_tx_tree_hash(const std::vector& tx_hashes) + { + crypto::hash h = null_hash; + get_tx_tree_hash(tx_hashes, h); + return h; + } + //--------------------------------------------------------------- + crypto::hash get_tx_tree_hash(const block& b) + { + std::vector txs_ids; + crypto::hash h = null_hash; + size_t bl_sz = 0; + get_transaction_hash(b.miner_tx, h, bl_sz); + txs_ids.push_back(h); + BOOST_FOREACH(auto& th, b.tx_hashes) + txs_ids.push_back(th); + return get_tx_tree_hash(txs_ids); + } + //--------------------------------------------------------------- +} diff --git a/src/cryptonote_core/cryptonote_format_utils.h b/src/cryptonote_core/cryptonote_format_utils.h new file mode 100644 index 000000000..1c50832b6 --- /dev/null +++ b/src/cryptonote_core/cryptonote_format_utils.h @@ -0,0 +1,191 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once +#include "cryptonote_protocol/cryptonote_protocol_defs.h" +#include "cryptonote_core/cryptonote_basic_impl.h" +#include "account.h" +#include "include_base_utils.h" +#include "crypto/crypto.h" +#include "crypto/hash.h" + + +namespace cryptonote +{ + //--------------------------------------------------------------- + void get_transaction_prefix_hash(const transaction_prefix& tx, crypto::hash& h); + crypto::hash get_transaction_prefix_hash(const transaction_prefix& tx); + bool parse_and_validate_tx_from_blob(const blobdata& tx_blob, transaction& tx, crypto::hash& tx_hash, crypto::hash& tx_prefix_hash); + bool parse_and_validate_tx_from_blob(const blobdata& tx_blob, transaction& tx); + bool construct_miner_tx(uint64_t height, uint64_t already_generated_coins, const account_public_address& miner_address, transaction& tx, uint64_t fee, std::vector& blocks_sizes, size_t current_block_size, const blobdata& extra_nonce, size_t max_outs = 1); + bool construct_miner_tx(uint64_t height, uint64_t already_generated_coins, const account_public_address& miner_address, transaction& tx, uint64_t fee, std::vector& blocks_sizes, size_t current_block_size, size_t max_outs = 1); + + struct tx_source_entry + { + typedef std::pair output_entry; + + std::vector outputs; //index + key + uint64_t real_output; //index in outputs vector of real output_entry + crypto::public_key real_out_tx_key; //incoming real tx public key + size_t real_output_in_tx_index; //index in transaction outputs vector + uint64_t amount; //money + }; + + struct tx_destination_entry + { + uint64_t amount; //money + account_public_address addr; //destination address + + tx_destination_entry() { } + tx_destination_entry(uint64_t a, const account_public_address &ad) : amount(a), addr(ad) { } + }; + + //--------------------------------------------------------------- + bool construct_tx(const account_keys& sender_account_keys, const std::vector& sources, const std::vector& destinations, transaction& tx, uint64_t unlock_time); + bool parse_and_validate_tx_extra(const transaction& tx, crypto::public_key& tx_pub_key); + crypto::public_key get_tx_pub_key_from_extra(const transaction& tx); + bool add_tx_pub_key_to_extra(transaction& tx, const crypto::public_key& tx_pub_key); + bool add_tx_extra_nonce(transaction& tx, const blobdata& extra_nonce); + bool is_out_to_acc(const account_keys& acc, const txout_to_key& out_key, const crypto::public_key& tx_pub_key, size_t output_index); + bool lookup_acc_outs(const account_keys& acc, const transaction& tx, const crypto::public_key& tx_pub_key, std::vector& outs, uint64_t& money_transfered); + bool lookup_acc_outs(const account_keys& acc, const transaction& tx, std::vector& outs, uint64_t& money_transfered); + bool get_tx_fee(const transaction& tx, uint64_t & fee); + uint64_t get_tx_fee(const transaction& tx); + bool generate_key_image_helper(const account_keys& ack, const crypto::public_key& tx_public_key, size_t real_output_index, keypair& in_ephemeral, crypto::key_image& ki); + void get_blob_hash(const blobdata& blob, crypto::hash& res); + crypto::hash get_blob_hash(const blobdata& blob); + std::string short_hash_str(const crypto::hash& h); + + crypto::hash get_transaction_hash(const transaction& t); + bool get_transaction_hash(const transaction& t, crypto::hash& res); + bool get_transaction_hash(const transaction& t, crypto::hash& res, size_t& blob_size); + blobdata get_block_hashing_blob(const block& b); + bool get_block_hash(const block& b, crypto::hash& res); + crypto::hash get_block_hash(const block& b); + bool get_block_longhash(const block& b, crypto::hash& res, uint64_t height); + crypto::hash get_block_longhash(const block& b, uint64_t height); + bool generate_genesis_block(block& bl); + bool parse_and_validate_block_from_blob(const blobdata& b_blob, block& b); + bool get_inputs_money_amount(const transaction& tx, uint64_t& money); + uint64_t get_outs_money_amount(const transaction& tx); + bool check_inputs_types_supported(const transaction& tx); + bool check_outs_valid(const transaction& tx); + blobdata get_block_hashing_blob(const block& b); + bool parse_amount(uint64_t& amount, const std::string& str_amount); + + bool check_money_overflow(const transaction& tx); + bool check_outs_overflow(const transaction& tx); + bool check_inputs_overflow(const transaction& tx); + uint64_t get_block_height(const block& b); + std::vector relative_output_offsets_to_absolute(const std::vector& off); + std::vector absolute_output_offsets_to_relative(const std::vector& off); + std::string print_money(uint64_t amount); + //--------------------------------------------------------------- + template + bool t_serializable_object_to_blob(const t_object& to, blobdata& b_blob) + { + std::stringstream ss; + binary_archive ba(ss); + bool r = ::serialization::serialize(ba, const_cast(to)); + b_blob = ss.str(); + return r; + } + //--------------------------------------------------------------- + template + blobdata t_serializable_object_to_blob(const t_object& to) + { + blobdata b; + t_serializable_object_to_blob(to, b); + return b; + } + //--------------------------------------------------------------- + template + bool get_object_hash(const t_object& o, crypto::hash& res) + { + get_blob_hash(t_serializable_object_to_blob(o), res); + return true; + } + //--------------------------------------------------------------- + template + size_t get_object_blobsize(const t_object& o) + { + blobdata b = t_serializable_object_to_blob(o); + return b.size(); + } + //--------------------------------------------------------------- + template + bool get_object_hash(const t_object& o, crypto::hash& res, size_t& blob_size) + { + blobdata bl = t_serializable_object_to_blob(o); + blob_size = bl.size(); + get_blob_hash(bl, res); + return true; + } + //--------------------------------------------------------------- + template + std::string obj_to_json_str(T& obj) + { + std::stringstream ss; + json_archive ar(ss, true); + bool r = ::serialization::serialize(ar, obj); + CHECK_AND_ASSERT_MES(r, "", "obj_to_json_str failed: serialization::serialize returned false"); + return ss.str(); + } + //--------------------------------------------------------------- + // 62387455827 -> 455827 + 7000000 + 80000000 + 300000000 + 2000000000 + 60000000000, where 455827 <= dust_threshold + template + void decompose_amount_into_digits(uint64_t amount, uint64_t dust_threshold, const chunk_handler_t& chunk_handler, const dust_handler_t& dust_handler) + { + if (0 == amount) + { + chunk_handler(0); + return; + } + + bool is_dust_handled = false; + uint64_t dust = 0; + uint64_t order = 1; + while (0 != amount) + { + uint64_t chunk = (amount % 10) * order; + amount /= 10; + order *= 10; + + if (dust + chunk <= dust_threshold) + { + dust += chunk; + } + else + { + if (!is_dust_handled && 0 != dust) + { + dust_handler(dust); + is_dust_handled = true; + } + if (0 != chunk) + { + chunk_handler(chunk); + } + } + } + + if (!is_dust_handled && 0 != dust) + { + dust_handler(dust); + } + } + //--------------------------------------------------------------- + blobdata block_to_blob(const block& b); + bool block_to_blob(const block& b, blobdata& b_blob); + blobdata tx_to_blob(const transaction& b); + bool tx_to_blob(const transaction& b, blobdata& b_blob); + void get_tx_tree_hash(const std::vector& tx_hashes, crypto::hash& h); + crypto::hash get_tx_tree_hash(const std::vector& tx_hashes); + crypto::hash get_tx_tree_hash(const block& b); + +#define CHECKED_GET_SPECIFIC_VARIANT(variant_var, specific_type, variable_name, fail_return_val) \ + CHECK_AND_ASSERT_MES(variant_var.type() == typeid(specific_type), fail_return_val, "wrong variant type: " << variant_var.type().name() << ", expected " << typeid(specific_type).name()); \ + specific_type& variable_name = boost::get(variant_var); + +} diff --git a/src/cryptonote_core/cryptonote_stat_info.h b/src/cryptonote_core/cryptonote_stat_info.h new file mode 100644 index 000000000..9d406748c --- /dev/null +++ b/src/cryptonote_core/cryptonote_stat_info.h @@ -0,0 +1,27 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once +#include "serialization/keyvalue_serialization.h" + + +namespace cryptonote +{ + struct core_stat_info + { + uint64_t tx_pool_size; + uint64_t blockchain_height; + uint64_t mining_speed; + uint64_t alternative_blocks; + std::string top_block_id_str; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(tx_pool_size) + KV_SERIALIZE(blockchain_height) + KV_SERIALIZE(mining_speed) + KV_SERIALIZE(alternative_blocks) + KV_SERIALIZE(top_block_id_str) + END_KV_SERIALIZE_MAP() + }; +} diff --git a/src/cryptonote_core/difficulty.cpp b/src/cryptonote_core/difficulty.cpp new file mode 100644 index 000000000..052f46662 --- /dev/null +++ b/src/cryptonote_core/difficulty.cpp @@ -0,0 +1,111 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include +#include +#include +#include +#include + +#include "common/int-util.h" +#include "crypto/hash.h" +#include "cryptonote_config.h" +#include "difficulty.h" + +namespace cryptonote { + + using std::size_t; + using std::uint64_t; + using std::vector; + +#if defined(_MSC_VER) +#include +#include + + static inline void mul(uint64_t a, uint64_t b, uint64_t &low, uint64_t &high) { + low = UnsignedMultiply128(a, b, &high); + } + +#else + + static inline void mul(uint64_t a, uint64_t b, uint64_t &low, uint64_t &high) { + typedef unsigned __int128 uint128_t; + uint128_t res = (uint128_t) a * (uint128_t) b; + low = (uint64_t) res; + high = (uint64_t) (res >> 64); + } + +#endif + + static inline bool cadd(uint64_t a, uint64_t b) { + return a + b < a; + } + + static inline bool cadc(uint64_t a, uint64_t b, bool c) { + return a + b < a || (c && a + b == (uint64_t) -1); + } + + bool check_hash(const crypto::hash &hash, difficulty_type difficulty) { + uint64_t low, high, top, cur; + // First check the highest word, this will most likely fail for a random hash. + mul(swap64le(((const uint64_t *) &hash)[3]), difficulty, top, high); + if (high != 0) { + return false; + } + mul(swap64le(((const uint64_t *) &hash)[0]), difficulty, low, cur); + mul(swap64le(((const uint64_t *) &hash)[1]), difficulty, low, high); + bool carry = cadd(cur, low); + cur = high; + mul(swap64le(((const uint64_t *) &hash)[2]), difficulty, low, high); + carry = cadc(cur, low, carry); + carry = cadc(high, top, carry); + return !carry; + } + + difficulty_type next_difficulty(vector timestamps, vector cumulative_difficulties, size_t target_seconds) { + //cutoff DIFFICULTY_LAG + if(timestamps.size() > DIFFICULTY_WINDOW) + { + timestamps.resize(DIFFICULTY_WINDOW); + cumulative_difficulties.resize(DIFFICULTY_WINDOW); + } + + + size_t length = timestamps.size(); + assert(length == cumulative_difficulties.size()); + if (length <= 1) { + return 1; + } + static_assert(DIFFICULTY_WINDOW >= 2, "Window is too small"); + assert(length <= DIFFICULTY_WINDOW); + sort(timestamps.begin(), timestamps.end()); + size_t cut_begin, cut_end; + static_assert(2 * DIFFICULTY_CUT <= DIFFICULTY_WINDOW - 2, "Cut length is too large"); + if (length <= DIFFICULTY_WINDOW - 2 * DIFFICULTY_CUT) { + cut_begin = 0; + cut_end = length; + } else { + cut_begin = (length - (DIFFICULTY_WINDOW - 2 * DIFFICULTY_CUT) + 1) / 2; + cut_end = cut_begin + (DIFFICULTY_WINDOW - 2 * DIFFICULTY_CUT); + } + assert(/*cut_begin >= 0 &&*/ cut_begin + 2 <= cut_end && cut_end <= length); + uint64_t time_span = timestamps[cut_end - 1] - timestamps[cut_begin]; + if (time_span == 0) { + time_span = 1; + } + difficulty_type total_work = cumulative_difficulties[cut_end - 1] - cumulative_difficulties[cut_begin]; + assert(total_work > 0); + uint64_t low, high; + mul(total_work, target_seconds, low, high); + if (high != 0 || low + time_span - 1 < low) { + return 0; + } + return (low + time_span - 1) / time_span; + } + + difficulty_type next_difficulty(vector timestamps, vector cumulative_difficulties) + { + return next_difficulty(std::move(timestamps), std::move(cumulative_difficulties), DIFFICULTY_TARGET); + } +} diff --git a/src/cryptonote_core/difficulty.h b/src/cryptonote_core/difficulty.h new file mode 100644 index 000000000..aad1e27ca --- /dev/null +++ b/src/cryptonote_core/difficulty.h @@ -0,0 +1,19 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include +#include + +#include "crypto/hash.h" + +namespace cryptonote +{ + typedef std::uint64_t difficulty_type; + + bool check_hash(const crypto::hash &hash, difficulty_type difficulty); + difficulty_type next_difficulty(std::vector timestamps, std::vector cumulative_difficulties); + difficulty_type next_difficulty(std::vector timestamps, std::vector cumulative_difficulties, size_t target_seconds); +} diff --git a/src/cryptonote_core/miner.cpp b/src/cryptonote_core/miner.cpp new file mode 100644 index 000000000..a882e1899 --- /dev/null +++ b/src/cryptonote_core/miner.cpp @@ -0,0 +1,356 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#include +#include +#include +#include +#include +#include +#include "misc_language.h" +#include "include_base_utils.h" +#include "cryptonote_basic_impl.h" +#include "cryptonote_format_utils.h" +#include "file_io_utils.h" +#include "common/command_line.h" +#include "string_coding.h" +#include "storages/portable_storage_template_helper.h" + +using namespace epee; + +#include "miner.h" + + + +namespace cryptonote +{ + + namespace + { + const command_line::arg_descriptor arg_extra_messages = {"extra-messages-file", "Specify file for extra messages to include into coinbase transactions", "", true}; + const command_line::arg_descriptor arg_start_mining = {"start-mining", "Specify wallet address to mining for", "", true}; + const command_line::arg_descriptor arg_mining_threads = {"mining-threads", "Specify mining threads count", 0, true}; + } + + + miner::miner(i_miner_handler* phandler):m_stop(1), + m_template(boost::value_initialized()), + m_template_no(0), + m_diffic(0), + m_thread_index(0), + m_phandler(phandler), + m_height(0), + m_pausers_count(0), + m_threads_total(0), + m_starter_nonce(0), + m_last_hr_merge_time(0), + m_hashes(0), + m_do_print_hashrate(false), + m_do_mining(false) + { + + } + //----------------------------------------------------------------------------------------------------- + miner::~miner() + { + stop(); + } + //----------------------------------------------------------------------------------------------------- + bool miner::set_block_template(const block& bl, const difficulty_type& di, uint64_t height) + { + CRITICAL_REGION_LOCAL(m_template_lock); + m_template = bl; + m_diffic = di; + m_height = height; + ++m_template_no; + m_starter_nonce = crypto::rand(); + return true; + } + //----------------------------------------------------------------------------------------------------- + bool miner::on_block_chain_update() + { + if(!is_mining()) + return true; + + return request_block_template(); + } + //----------------------------------------------------------------------------------------------------- + bool miner::request_block_template() + { + block bl = AUTO_VAL_INIT(bl); + difficulty_type di = AUTO_VAL_INIT(di); + uint64_t height = AUTO_VAL_INIT(height); + cryptonote::blobdata extra_nonce; + if(m_extra_messages.size() && m_config.current_extra_message_index < m_extra_messages.size()) + { + extra_nonce = m_extra_messages[m_config.current_extra_message_index]; + } + + if(!m_phandler->get_block_template(bl, m_mine_address, di, height, extra_nonce)) + { + LOG_ERROR("Failed to get_block_template(), stopping mining"); + return false; + } + set_block_template(bl, di, height); + return true; + } + //----------------------------------------------------------------------------------------------------- + bool miner::on_idle() + { + m_update_block_template_interval.do_call([&](){ + if(is_mining())request_block_template(); + return true; + }); + + m_update_merge_hr_interval.do_call([&](){ + merge_hr(); + return true; + }); + + return true; + } + //----------------------------------------------------------------------------------------------------- + void miner::do_print_hashrate(bool do_hr) + { + m_do_print_hashrate = do_hr; + } + //----------------------------------------------------------------------------------------------------- + void miner::merge_hr() + { + if(m_last_hr_merge_time && is_mining()) + { + m_current_hash_rate = m_hashes * 1000 / ((misc_utils::get_tick_count() - m_last_hr_merge_time + 1)); + CRITICAL_REGION_LOCAL(m_last_hash_rates_lock); + m_last_hash_rates.push_back(m_current_hash_rate); + if(m_last_hash_rates.size() > 19) + m_last_hash_rates.pop_front(); + if(m_do_print_hashrate) + { + uint64_t total_hr = std::accumulate(m_last_hash_rates.begin(), m_last_hash_rates.end(), 0); + float hr = static_cast(total_hr)/static_cast(m_last_hash_rates.size()); + std::cout << "hashrate: " << std::setprecision(4) << std::fixed << hr << ENDL; + } + } + m_last_hr_merge_time = misc_utils::get_tick_count(); + m_hashes = 0; + } + //----------------------------------------------------------------------------------------------------- + void miner::init_options(boost::program_options::options_description& desc) + { + command_line::add_arg(desc, arg_extra_messages); + command_line::add_arg(desc, arg_start_mining); + command_line::add_arg(desc, arg_mining_threads); + } + //----------------------------------------------------------------------------------------------------- + bool miner::init(const boost::program_options::variables_map& vm) + { + if(command_line::has_arg(vm, arg_extra_messages)) + { + std::string buff; + bool r = file_io_utils::load_file_to_string(command_line::get_arg(vm, arg_extra_messages), buff); + CHECK_AND_ASSERT_MES(r, false, "Failed to load file with extra messages: " << command_line::get_arg(vm, arg_extra_messages)); + std::vector extra_vec; + boost::split(extra_vec, buff, boost::is_any_of("\n"), boost::token_compress_on ); + m_extra_messages.resize(extra_vec.size()); + for(size_t i = 0; i != extra_vec.size(); i++) + { + string_tools::trim(extra_vec[i]); + if(!extra_vec[i].size()) + continue; + std::string buff = string_encoding::base64_decode(extra_vec[i]); + if(buff != "0") + m_extra_messages[i] = buff; + } + m_config_folder_path = boost::filesystem::path(command_line::get_arg(vm, arg_extra_messages)).parent_path().string(); + m_config = AUTO_VAL_INIT(m_config); + epee::serialization::load_t_from_json_file(m_config, m_config_folder_path + "/" + MINER_CONFIG_FILE_NAME); + LOG_PRINT_L0("Loaded " << m_extra_messages.size() << " extra messages, current index " << m_config.current_extra_message_index); + } + + if(command_line::has_arg(vm, arg_start_mining)) + { + if(!cryptonote::get_account_address_from_str(m_mine_address, command_line::get_arg(vm, arg_start_mining))) + { + LOG_ERROR("Target account address " << command_line::get_arg(vm, arg_start_mining) << " has wrong format, starting daemon canceled"); + return false; + } + m_threads_total = 1; + m_do_mining = true; + if(command_line::has_arg(vm, arg_mining_threads)) + { + m_threads_total = command_line::get_arg(vm, arg_mining_threads); + } + } + + return true; + } + //----------------------------------------------------------------------------------------------------- + bool miner::is_mining() + { + return !m_stop; + } + //----------------------------------------------------------------------------------------------------- + bool miner::start(const account_public_address& adr, size_t threads_count) + { + m_mine_address = adr; + m_threads_total = static_cast(threads_count); + m_starter_nonce = crypto::rand(); + CRITICAL_REGION_LOCAL(m_threads_lock); + if(is_mining()) + { + LOG_ERROR("Starting miner but it's already started"); + return false; + } + + if(!m_threads.empty()) + { + LOG_ERROR("Unable to start miner because there are active mining threads"); + return false; + } + + if(!m_template_no) + request_block_template();//lets update block template + + boost::interprocess::ipcdetail::atomic_write32(&m_stop, 0); + boost::interprocess::ipcdetail::atomic_write32(&m_thread_index, 0); + + for(size_t i = 0; i != threads_count; i++) + m_threads.push_back(boost::thread(boost::bind(&miner::worker_thread, this))); + + LOG_PRINT_L0("Mining has started with " << threads_count << " threads, good luck!" ) + return true; + } + //----------------------------------------------------------------------------------------------------- + uint64_t miner::get_speed() + { + return m_current_hash_rate; + } + //----------------------------------------------------------------------------------------------------- + void miner::send_stop_signal() + { + boost::interprocess::ipcdetail::atomic_write32(&m_stop, 1); + } + //----------------------------------------------------------------------------------------------------- + bool miner::stop() + { + send_stop_signal(); + CRITICAL_REGION_LOCAL(m_threads_lock); + + BOOST_FOREACH(boost::thread& th, m_threads) + th.join(); + + m_threads.clear(); + LOG_PRINT_L0("Mining has been stopped, " << m_threads.size() << " finished" ); + return true; + } + //----------------------------------------------------------------------------------------------------- + bool miner::find_nonce_for_given_block(block& bl, const difficulty_type& diffic, uint64_t height) + { + for(; bl.nonce != std::numeric_limits::max(); bl.nonce++) + { + crypto::hash h; + get_block_longhash(bl, h, height); + + if(check_hash(h, diffic)) + { + return true; + } + } + return false; + } + //----------------------------------------------------------------------------------------------------- + void miner::on_synchronized() + { + if(m_do_mining) + { + start(m_mine_address, m_threads_total); + } + } + //----------------------------------------------------------------------------------------------------- + void miner::pause() + { + CRITICAL_REGION_LOCAL(m_miners_count_lock); + ++m_pausers_count; + if(m_pausers_count == 1 && is_mining()) + LOG_PRINT_L2("MINING PAUSED"); + } + //----------------------------------------------------------------------------------------------------- + void miner::resume() + { + CRITICAL_REGION_LOCAL(m_miners_count_lock); + --m_pausers_count; + if(m_pausers_count < 0) + { + m_pausers_count = 0; + LOG_PRINT_RED_L0("Unexpected miner::resume() called"); + } + if(!m_pausers_count && is_mining()) + LOG_PRINT_L2("MINING RESUMED"); + } + //----------------------------------------------------------------------------------------------------- + bool miner::worker_thread() + { + uint32_t th_local_index = boost::interprocess::ipcdetail::atomic_inc32(&m_thread_index); + LOG_PRINT_L0("Miner thread was started ["<< th_local_index << "]"); + log_space::log_singletone::set_thread_log_prefix(std::string("[miner ") + std::to_string(th_local_index) + "]"); + uint32_t nonce = m_starter_nonce + th_local_index; + uint64_t height = 0; + difficulty_type local_diff = 0; + uint32_t local_template_ver = 0; + block b; + while(!m_stop) + { + if(m_pausers_count)//anti split workaround + { + misc_utils::sleep_no_w(100); + continue; + } + + if(local_template_ver != m_template_no) + { + + CRITICAL_REGION_BEGIN(m_template_lock); + b = m_template; + local_diff = m_diffic; + height = m_height; + CRITICAL_REGION_END(); + local_template_ver = m_template_no; + nonce = m_starter_nonce + th_local_index; + } + + if(!local_template_ver)//no any set_block_template call + { + LOG_PRINT_L2("Block template not set yet"); + epee::misc_utils::sleep_no_w(1000); + continue; + } + + b.nonce = nonce; + crypto::hash h; + get_block_longhash(b, h, height); + + if(check_hash(h, local_diff)) + { + //we lucky! + ++m_config.current_extra_message_index; + LOG_PRINT_GREEN("Found block for difficulty: " << local_diff, LOG_LEVEL_0); + if(!m_phandler->handle_block_found(b)) + { + --m_config.current_extra_message_index; + }else + { + //success update, lets update config + epee::serialization::store_t_to_json_file(m_config, m_config_folder_path + "/" + MINER_CONFIG_FILE_NAME); + } + } + nonce+=m_threads_total; + ++m_hashes; + } + LOG_PRINT_L0("Miner thread stopped ["<< th_local_index << "]"); + return true; + } + //----------------------------------------------------------------------------------------------------- +} + diff --git a/src/cryptonote_core/miner.h b/src/cryptonote_core/miner.h new file mode 100644 index 000000000..03f09509e --- /dev/null +++ b/src/cryptonote_core/miner.h @@ -0,0 +1,99 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include +#include +#include +#include "cryptonote_basic.h" +#include "difficulty.h" +#include "math_helper.h" + + +namespace cryptonote +{ + + struct i_miner_handler + { + virtual bool handle_block_found(block& b) = 0; + virtual bool get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, const blobdata& ex_nonce) = 0; + protected: + ~i_miner_handler(){}; + }; + + /************************************************************************/ + /* */ + /************************************************************************/ + class miner + { + public: + miner(i_miner_handler* phandler); + ~miner(); + bool init(const boost::program_options::variables_map& vm); + static void init_options(boost::program_options::options_description& desc); + bool set_block_template(const block& bl, const difficulty_type& diffic, uint64_t height); + bool on_block_chain_update(); + bool start(const account_public_address& adr, size_t threads_count); + uint64_t get_speed(); + void send_stop_signal(); + bool stop(); + bool is_mining(); + bool on_idle(); + void on_synchronized(); + //synchronous analog (for fast calls) + static bool find_nonce_for_given_block(block& bl, const difficulty_type& diffic, uint64_t height); + void pause(); + void resume(); + void do_print_hashrate(bool do_hr); + + private: + bool worker_thread(); + bool request_block_template(); + void merge_hr(); + + struct miner_config + { + uint64_t current_extra_message_index; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(current_extra_message_index) + END_KV_SERIALIZE_MAP() + }; + + + volatile uint32_t m_stop; + ::critical_section m_template_lock; + block m_template; + std::atomic m_template_no; + std::atomic m_starter_nonce; + difficulty_type m_diffic; + uint64_t m_height; + volatile uint32_t m_thread_index; + volatile uint32_t m_threads_total; + std::atomic m_pausers_count; + ::critical_section m_miners_count_lock; + + std::list m_threads; + ::critical_section m_threads_lock; + i_miner_handler* m_phandler; + account_public_address m_mine_address; + math_helper::once_a_time_seconds<5> m_update_block_template_interval; + math_helper::once_a_time_seconds<2> m_update_merge_hr_interval; + std::vector m_extra_messages; + miner_config m_config; + std::string m_config_folder_path; + std::atomic m_last_hr_merge_time; + std::atomic m_hashes; + std::atomic m_current_hash_rate; + critical_section m_last_hash_rates_lock; + std::list m_last_hash_rates; + bool m_do_print_hashrate; + bool m_do_mining; + + }; +} + + + diff --git a/src/cryptonote_core/tx_extra.h b/src/cryptonote_core/tx_extra.h new file mode 100644 index 000000000..7e27cbc7f --- /dev/null +++ b/src/cryptonote_core/tx_extra.h @@ -0,0 +1,11 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + + + +#define TX_EXTRA_PADDING_MAX_COUNT 40 +#define TX_EXTRA_TAG_PUBKEY 0x01 +#define TX_EXTRA_NONCE 0x02 diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp new file mode 100644 index 000000000..3a1799675 --- /dev/null +++ b/src/cryptonote_core/tx_pool.cpp @@ -0,0 +1,410 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include +#include + +#include "tx_pool.h" +#include "cryptonote_format_utils.h" +#include "cryptonote_boost_serialization.h" +#include "cryptonote_config.h" +#include "blockchain_storage.h" +#include "common/boost_serialization_helper.h" +#include "misc_language.h" +#include "warnings.h" +#include "crypto/hash.h" + +DISABLE_VS_WARNINGS(4244 4345 4503) //'boost::foreach_detail_::or_' : decorated name length exceeded, name was truncated + +namespace cryptonote +{ + //--------------------------------------------------------------------------------- + tx_memory_pool::tx_memory_pool(blockchain_storage& bchs): m_blockchain(bchs) + { + + } + //--------------------------------------------------------------------------------- + bool tx_memory_pool::add_tx(const transaction &tx, /*const crypto::hash& tx_prefix_hash,*/ const crypto::hash &id, size_t blob_size, tx_verification_context& tvc, bool kept_by_block) + { + + + if(!check_inputs_types_supported(tx)) + { + tvc.m_verifivation_failed = true; + return false; + } + + uint64_t inputs_amount = 0; + if(!get_inputs_money_amount(tx, inputs_amount)) + { + tvc.m_verifivation_failed = true; + return false; + } + + uint64_t outputs_amount = get_outs_money_amount(tx); + + if(outputs_amount >= inputs_amount) + { + LOG_PRINT_L0("transaction use more money then it has: use " << outputs_amount << ", have " << inputs_amount); + tvc.m_verifivation_failed = true; + return false; + } + + //check key images for transaction if it is not kept by block + if(!kept_by_block) + { + if(have_tx_keyimges_as_spent(tx)) + { + LOG_ERROR("Transaction with id= "<< id << " used already spent key images"); + tvc.m_verifivation_failed = true; + return false; + } + } + + + crypto::hash max_used_block_id = null_hash; + uint64_t max_used_block_height = 0; + bool ch_inp_res = m_blockchain.check_tx_inputs(tx, max_used_block_height, max_used_block_id); + CRITICAL_REGION_LOCAL(m_transactions_lock); + if(!ch_inp_res) + { + if(kept_by_block) + { + //anyway add this transaction to pool, because it related to block + auto txd_p = m_transactions.insert(transactions_container::value_type(id, tx_details())); + CHECK_AND_ASSERT_MES(txd_p.second, false, "transaction already exists at inserting in memory pool"); + txd_p.first->second.blob_size = blob_size; + txd_p.first->second.tx = tx; + txd_p.first->second.fee = inputs_amount - outputs_amount; + txd_p.first->second.max_used_block_id = null_hash; + txd_p.first->second.max_used_block_height = 0; + txd_p.first->second.kept_by_block = kept_by_block; + tvc.m_verifivation_impossible = true; + tvc.m_added_to_pool = true; + }else + { + LOG_PRINT_L0("tx used wrong inputs, rejected"); + tvc.m_verifivation_failed = true; + return false; + } + }else + { + //update transactions container + auto txd_p = m_transactions.insert(transactions_container::value_type(id, tx_details())); + CHECK_AND_ASSERT_MES(txd_p.second, false, "intrnal error: transaction already exists at inserting in memorypool"); + txd_p.first->second.blob_size = blob_size; + txd_p.first->second.tx = tx; + txd_p.first->second.kept_by_block = kept_by_block; + txd_p.first->second.fee = inputs_amount - outputs_amount; + txd_p.first->second.max_used_block_id = max_used_block_id; + txd_p.first->second.max_used_block_height = max_used_block_height; + txd_p.first->second.last_failed_height = 0; + txd_p.first->second.last_failed_id = null_hash; + tvc.m_added_to_pool = true; + + if(txd_p.first->second.fee > 0) + tvc.m_should_be_relayed = true; + } + + tvc.m_verifivation_failed = true; + //update image_keys container, here should everything goes ok. + BOOST_FOREACH(const auto& in, tx.vin) + { + CHECKED_GET_SPECIFIC_VARIANT(in, const txin_to_key, txin, false); + std::unordered_set& kei_image_set = m_spent_key_images[txin.k_image]; + CHECK_AND_ASSERT_MES(kept_by_block || kei_image_set.size() == 0, false, "internal error: keeped_by_block=" << kept_by_block + << ", kei_image_set.size()=" << kei_image_set.size() << ENDL << "txin.k_image=" << txin.k_image << ENDL + << "tx_id=" << id ); + auto ins_res = kei_image_set.insert(id); + CHECK_AND_ASSERT_MES(ins_res.second, false, "internal error: try to insert duplicate iterator in key_image set"); + } + + tvc.m_verifivation_failed = false; + //succeed + return true; + } + //--------------------------------------------------------------------------------- + bool tx_memory_pool::add_tx(const transaction &tx, tx_verification_context& tvc, bool keeped_by_block) + { + crypto::hash h = null_hash; + size_t blob_size = 0; + get_transaction_hash(tx, h, blob_size); + return add_tx(tx, h, blob_size, tvc, keeped_by_block); + } + //--------------------------------------------------------------------------------- + bool tx_memory_pool::remove_transaction_keyimages(const transaction& tx) + { + CRITICAL_REGION_LOCAL(m_transactions_lock); + BOOST_FOREACH(const txin_v& vi, tx.vin) + { + CHECKED_GET_SPECIFIC_VARIANT(vi, const txin_to_key, txin, false); + auto it = m_spent_key_images.find(txin.k_image); + CHECK_AND_ASSERT_MES(it != m_spent_key_images.end(), false, "failed to find transaction input in key images. img=" << txin.k_image << ENDL + << "transaction id = " << get_transaction_hash(tx)); + std::unordered_set& key_image_set = it->second; + CHECK_AND_ASSERT_MES(key_image_set.size(), false, "empty key_image set, img=" << txin.k_image << ENDL + << "transaction id = " << get_transaction_hash(tx)); + + auto it_in_set = key_image_set.find(get_transaction_hash(tx)); + CHECK_AND_ASSERT_MES(key_image_set.size(), false, "transaction id not found in key_image set, img=" << txin.k_image << ENDL + << "transaction id = " << get_transaction_hash(tx)); + key_image_set.erase(it_in_set); + if(!key_image_set.size()) + { + //it is now empty hash container for this key_image + m_spent_key_images.erase(it); + } + + } + return true; + } + //--------------------------------------------------------------------------------- + bool tx_memory_pool::take_tx(const crypto::hash &id, transaction &tx, size_t& blob_size, uint64_t& fee) + { + CRITICAL_REGION_LOCAL(m_transactions_lock); + auto it = m_transactions.find(id); + if(it == m_transactions.end()) + return false; + + tx = it->second.tx; + blob_size = it->second.blob_size; + fee = it->second.fee; + remove_transaction_keyimages(it->second.tx); + m_transactions.erase(it); + return true; + } + //--------------------------------------------------------------------------------- + size_t tx_memory_pool::get_transactions_count() + { + CRITICAL_REGION_LOCAL(m_transactions_lock); + return m_transactions.size(); + } + //--------------------------------------------------------------------------------- + bool tx_memory_pool::get_transactions(std::list& txs) + { + CRITICAL_REGION_LOCAL(m_transactions_lock); + BOOST_FOREACH(const auto& tx_vt, m_transactions) + txs.push_back(tx_vt.second.tx); + + return true; + } + //--------------------------------------------------------------------------------- + bool tx_memory_pool::get_transaction(const crypto::hash& id, transaction& tx) + { + CRITICAL_REGION_LOCAL(m_transactions_lock); + auto it = m_transactions.find(id); + if(it == m_transactions.end()) + return false; + tx = it->second.tx; + return true; + } + //--------------------------------------------------------------------------------- + bool tx_memory_pool::on_blockchain_inc(uint64_t new_block_height, const crypto::hash& top_block_id) + { + return true; + } + //--------------------------------------------------------------------------------- + bool tx_memory_pool::on_blockchain_dec(uint64_t new_block_height, const crypto::hash& top_block_id) + { + return true; + } + //--------------------------------------------------------------------------------- + bool tx_memory_pool::have_tx(const crypto::hash &id) + { + CRITICAL_REGION_LOCAL(m_transactions_lock); + if(m_transactions.count(id)) + return true; + return false; + } + //--------------------------------------------------------------------------------- + bool tx_memory_pool::have_tx_keyimges_as_spent(const transaction& tx) + { + CRITICAL_REGION_LOCAL(m_transactions_lock); + BOOST_FOREACH(const auto& in, tx.vin) + { + CHECKED_GET_SPECIFIC_VARIANT(in, const txin_to_key, tokey_in, true);//should never fail + if(have_tx_keyimg_as_spent(tokey_in.k_image)) + return true; + } + return false; + } + //--------------------------------------------------------------------------------- + bool tx_memory_pool::have_tx_keyimg_as_spent(const crypto::key_image& key_im) + { + CRITICAL_REGION_LOCAL(m_transactions_lock); + return m_spent_key_images.end() != m_spent_key_images.find(key_im); + } + //--------------------------------------------------------------------------------- + void tx_memory_pool::lock() + { + m_transactions_lock.lock(); + } + //--------------------------------------------------------------------------------- + void tx_memory_pool::unlock() + { + m_transactions_lock.unlock(); + } + //--------------------------------------------------------------------------------- + bool tx_memory_pool::is_transaction_ready_to_go(tx_details& txd) + { + //not the best implementation at this time, sorry :( + //check is ring_signature already checked ? + if(txd.max_used_block_id == null_hash) + {//not checked, lets try to check + + if(txd.last_failed_id != null_hash && m_blockchain.get_current_blockchain_height() > txd.last_failed_height && txd.last_failed_id == m_blockchain.get_block_id_by_height(txd.last_failed_height)) + return false;//we already sure that this tx is broken for this height + + if(!m_blockchain.check_tx_inputs(txd.tx, txd.max_used_block_height, txd.max_used_block_id)) + { + txd.last_failed_height = m_blockchain.get_current_blockchain_height()-1; + txd.last_failed_id = m_blockchain.get_block_id_by_height(txd.last_failed_height); + return false; + } + }else + { + if(txd.max_used_block_height >= m_blockchain.get_current_blockchain_height()) + return false; + if(m_blockchain.get_block_id_by_height(txd.max_used_block_height) != txd.max_used_block_id) + { + //if we already failed on this height and id, skip actual ring signature check + if(txd.last_failed_id == m_blockchain.get_block_id_by_height(txd.last_failed_height)) + return false; + //check ring signature again, it is possible (with very small chance) that this transaction become again valid + if(!m_blockchain.check_tx_inputs(txd.tx, txd.max_used_block_height, txd.max_used_block_id)) + { + txd.last_failed_height = m_blockchain.get_current_blockchain_height()-1; + txd.last_failed_id = m_blockchain.get_block_id_by_height(txd.last_failed_height); + return false; + } + } + } + //if we here, transaction seems valid, but, anyway, check for key_images collisions with blockchain, just to be sure + if(m_blockchain.have_tx_keyimges_as_spent(txd.tx)) + return false; + + //transaction is ok. + return true; + } + //--------------------------------------------------------------------------------- + bool tx_memory_pool::have_key_images(const std::unordered_set& k_images, const transaction& tx) + { + for(size_t i = 0; i!= tx.vin.size(); i++) + { + CHECKED_GET_SPECIFIC_VARIANT(tx.vin[i], const txin_to_key, itk, false); + if(k_images.count(itk.k_image)) + return true; + } + return false; + } + //--------------------------------------------------------------------------------- + bool tx_memory_pool::append_key_images(std::unordered_set& k_images, const transaction& tx) + { + for(size_t i = 0; i!= tx.vin.size(); i++) + { + CHECKED_GET_SPECIFIC_VARIANT(tx.vin[i], const txin_to_key, itk, false); + auto i_res = k_images.insert(itk.k_image); + CHECK_AND_ASSERT_MES(i_res.second, false, "internal error: key images pool cache - inserted duplicate image in set: " << itk.k_image); + } + return true; + } + //--------------------------------------------------------------------------------- + std::string tx_memory_pool::print_pool(bool short_format) + { + std::stringstream ss; + CRITICAL_REGION_LOCAL(m_transactions_lock); + BOOST_FOREACH(transactions_container::value_type& txe, m_transactions) + { + if(short_format) + { + tx_details& txd = txe.second; + ss << "id: " << txe.first << ENDL + << "blob_size: " << txd.blob_size << ENDL + << "fee: " << txd.fee << ENDL + << "kept_by_block: " << txd.kept_by_block << ENDL + << "max_used_block_height: " << txd.max_used_block_height << ENDL + << "max_used_block_id: " << txd.max_used_block_id << ENDL + << "last_failed_height: " << txd.last_failed_height << ENDL + << "last_failed_id: " << txd.last_failed_id << ENDL; + }else + { + tx_details& txd = txe.second; + ss << "id: " << txe.first << ENDL + << obj_to_json_str(txd.tx) << ENDL + << "blob_size: " << txd.blob_size << ENDL + << "fee: " << txd.fee << ENDL + << "kept_by_block: " << txd.kept_by_block << ENDL + << "max_used_block_height: " << txd.max_used_block_height << ENDL + << "max_used_block_id: " << txd.max_used_block_id << ENDL + << "last_failed_height: " << txd.last_failed_height << ENDL + << "last_failed_id: " << txd.last_failed_id << ENDL; + } + + } + return ss.str(); + } + //--------------------------------------------------------------------------------- + bool tx_memory_pool::fill_block_template(block& bl, size_t& cumulative_sizes, size_t max_comulative_sz, uint64_t& fee) + { + CRITICAL_REGION_LOCAL(m_transactions_lock); + + fee = 0; + std::unordered_set k_images; + + BOOST_FOREACH(transactions_container::value_type& tx, m_transactions) + { + if(cumulative_sizes + tx.second.blob_size > max_comulative_sz) + continue; + + if(!is_transaction_ready_to_go(tx.second)) + continue; + + if(have_key_images(k_images, tx.second.tx)) + continue; + + bl.tx_hashes.push_back(tx.first); + cumulative_sizes += tx.second.blob_size; + fee += tx.second.fee; + append_key_images(k_images, tx.second.tx); + + if(cumulative_sizes >= max_comulative_sz) + break; + } + + return true; + } + //--------------------------------------------------------------------------------- + bool tx_memory_pool::init(const std::string& config_folder) + { + m_config_folder = config_folder; + std::string state_file_path = config_folder + "/" + CRYPTONOTE_POOLDATA_FILENAME; + boost::system::error_code ec; + if(!boost::filesystem::exists(state_file_path, ec)) + return true; + bool res = tools::unserialize_obj_from_file(*this, state_file_path); + if(!res) + { + LOG_PRINT_L0("Failed to load memory pool from file " << state_file_path); + } + return res; + } + + //--------------------------------------------------------------------------------- + bool tx_memory_pool::deinit() + { + if (!tools::create_directories_if_necessary(m_config_folder)) + { + LOG_PRINT_L0("Failed to create data directory: " << m_config_folder); + return false; + } + + std::string state_file_path = m_config_folder + "/" + CRYPTONOTE_POOLDATA_FILENAME; + bool res = tools::serialize_obj_to_file(*this, state_file_path); + if(!res) + { + LOG_PRINT_L0("Failed to serialize memory pool to file " << state_file_path); + } + return true; + } +} diff --git a/src/cryptonote_core/tx_pool.h b/src/cryptonote_core/tx_pool.h new file mode 100644 index 000000000..1dff7ee1c --- /dev/null +++ b/src/cryptonote_core/tx_pool.h @@ -0,0 +1,168 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once +#include "include_base_utils.h" +using namespace epee; + + +#include +#include +#include +#include +#include + +#include "string_tools.h" +#include "syncobj.h" +#include "cryptonote_basic_impl.h" +#include "verification_context.h" +#include "crypto/hash.h" + + +namespace cryptonote +{ + class blockchain_storage; + /************************************************************************/ + /* */ + /************************************************************************/ + + class tx_memory_pool: boost::noncopyable + { + public: + tx_memory_pool(blockchain_storage& bchs); + bool add_tx(const transaction &tx, const crypto::hash &id, size_t blob_size, tx_verification_context& tvc, bool keeped_by_block); + bool add_tx(const transaction &tx, tx_verification_context& tvc, bool keeped_by_block); + //gets tx and remove it from pool + bool take_tx(const crypto::hash &id, transaction &tx, size_t& blob_size, uint64_t& fee); + + bool have_tx(const crypto::hash &id); + bool have_tx_keyimg_as_spent(const crypto::key_image& key_im); + bool have_tx_keyimges_as_spent(const transaction& tx); + + bool on_blockchain_inc(uint64_t new_block_height, const crypto::hash& top_block_id); + bool on_blockchain_dec(uint64_t new_block_height, const crypto::hash& top_block_id); + + void lock(); + void unlock(); + + // load/store operations + bool init(const std::string& config_folder); + bool deinit(); + bool fill_block_template(block& bl, size_t& cumulative_sizes, size_t max_comulative_sz, uint64_t& fee); + bool get_transactions(std::list& txs); + bool get_transaction(const crypto::hash& h, transaction& tx); + size_t get_transactions_count(); + bool remove_transaction_keyimages(const transaction& tx); + bool have_key_images(const std::unordered_set& kic, const transaction& tx); + bool append_key_images(std::unordered_set& kic, const transaction& tx); + std::string print_pool(bool short_format); + + /*bool flush_pool(const std::strig& folder); + bool inflate_pool(const std::strig& folder);*/ + +#define CURRENT_MEMPOOL_ARCHIVE_VER 7 + + template + void serialize(archive_t & a, const unsigned int version) + { + if(version < CURRENT_MEMPOOL_ARCHIVE_VER ) + return; + CRITICAL_REGION_LOCAL(m_transactions_lock); + a & m_transactions; + a & m_spent_key_images; + } + + struct tx_details + { + transaction tx; + size_t blob_size; + uint64_t fee; + crypto::hash max_used_block_id; + uint64_t max_used_block_height; + bool kept_by_block; + // + uint64_t last_failed_height; + crypto::hash last_failed_id; + }; + + private: + bool is_transaction_ready_to_go(tx_details& txd); + typedef std::unordered_map transactions_container; + typedef std::unordered_map > key_images_container; + + epee::critical_section m_transactions_lock; + transactions_container m_transactions; + key_images_container m_spent_key_images; + + //transactions_container m_alternative_transactions; + + std::string m_config_folder; + blockchain_storage& m_blockchain; + /************************************************************************/ + /* */ + /************************************************************************/ + /*class inputs_visitor: public boost::static_visitor + { + key_images_container& m_spent_keys; + public: + inputs_visitor(key_images_container& spent_keys): m_spent_keys(spent_keys) + {} + bool operator()(const txin_to_key& tx) const + { + auto pr = m_spent_keys.insert(tx.k_image); + CHECK_AND_ASSERT_MES(pr.second, false, "Tried to insert transaction with input seems already spent, input: " << epee::string_tools::pod_to_hex(tx.k_image)); + return true; + } + bool operator()(const txin_gen& tx) const + { + CHECK_AND_ASSERT_MES(false, false, "coinbase transaction in memory pool"); + return false; + } + bool operator()(const txin_to_script& tx) const {return false;} + bool operator()(const txin_to_scripthash& tx) const {return false;} + }; */ + /************************************************************************/ + /* */ + /************************************************************************/ + class amount_visitor: public boost::static_visitor + { + public: + uint64_t operator()(const txin_to_key& tx) const + { + return tx.amount; + } + uint64_t operator()(const txin_gen& tx) const + { + CHECK_AND_ASSERT_MES(false, false, "coinbase transaction in memory pool"); + return 0; + } + uint64_t operator()(const txin_to_script& tx) const {return 0;} + uint64_t operator()(const txin_to_scripthash& tx) const {return 0;} + }; + + }; +} + +namespace boost +{ + namespace serialization + { + template + void serialize(archive_t & ar, cryptonote::tx_memory_pool::tx_details& td, const unsigned int version) + { + ar & td.blob_size; + ar & td.fee; + ar & td.tx; + ar & td.max_used_block_height; + ar & td.max_used_block_id; + ar & td.last_failed_height; + ar & td.last_failed_id; + + } + } +} +BOOST_CLASS_VERSION(cryptonote::tx_memory_pool, CURRENT_MEMPOOL_ARCHIVE_VER) + + + diff --git a/src/cryptonote_core/verification_context.h b/src/cryptonote_core/verification_context.h new file mode 100644 index 000000000..210cc2d5b --- /dev/null +++ b/src/cryptonote_core/verification_context.h @@ -0,0 +1,27 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#pragma once +namespace cryptonote +{ + /************************************************************************/ + /* */ + /************************************************************************/ + struct tx_verification_context + { + bool m_should_be_relayed; + bool m_verifivation_failed; //bad tx, should drop connection + bool m_verifivation_impossible; //the transaction is related with an alternative blockchain + bool m_added_to_pool; + }; + + struct block_verification_context + { + bool m_added_to_main_chain; + bool m_verifivation_failed; //bad block, should drop connection + bool m_marked_as_orphaned; + bool m_already_exists; + }; +} diff --git a/src/cryptonote_protocol/blobdatatype.h b/src/cryptonote_protocol/blobdatatype.h new file mode 100644 index 000000000..23111f048 --- /dev/null +++ b/src/cryptonote_protocol/blobdatatype.h @@ -0,0 +1,10 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +namespace cryptonote +{ + typedef std::string blobdata; +} diff --git a/src/cryptonote_protocol/cryptonote_protocol_defs.h b/src/cryptonote_protocol/cryptonote_protocol_defs.h new file mode 100644 index 000000000..d646a7f6f --- /dev/null +++ b/src/cryptonote_protocol/cryptonote_protocol_defs.h @@ -0,0 +1,152 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include +#include "serialization/keyvalue_serialization.h" +#include "cryptonote_core/cryptonote_basic.h" +#include "cryptonote_protocol/blobdatatype.h" +namespace cryptonote +{ + + +#define BC_COMMANDS_POOL_BASE 2000 + + + /************************************************************************/ + /* */ + /************************************************************************/ + struct block_complete_entry + { + blobdata block; + std::list txs; + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(block) + KV_SERIALIZE(txs) + END_KV_SERIALIZE_MAP() + }; + + + /************************************************************************/ + /* */ + /************************************************************************/ + struct NOTIFY_NEW_BLOCK + { + const static int ID = BC_COMMANDS_POOL_BASE + 1; + + struct request + { + block_complete_entry b; + uint64_t current_blockchain_height; + uint32_t hop; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(b) + KV_SERIALIZE(current_blockchain_height) + KV_SERIALIZE(hop) + END_KV_SERIALIZE_MAP() + }; + }; + + /************************************************************************/ + /* */ + /************************************************************************/ + struct NOTIFY_NEW_TRANSACTIONS + { + const static int ID = BC_COMMANDS_POOL_BASE + 2; + + struct request + { + std::list txs; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(txs) + END_KV_SERIALIZE_MAP() + }; + }; + /************************************************************************/ + /* */ + /************************************************************************/ + struct NOTIFY_REQUEST_GET_OBJECTS + { + const static int ID = BC_COMMANDS_POOL_BASE + 3; + + struct request + { + std::list txs; + std::list blocks; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_CONTAINER_POD_AS_BLOB(txs) + KV_SERIALIZE_CONTAINER_POD_AS_BLOB(blocks) + END_KV_SERIALIZE_MAP() + }; + }; + + struct NOTIFY_RESPONSE_GET_OBJECTS + { + const static int ID = BC_COMMANDS_POOL_BASE + 4; + + struct request + { + std::list txs; + std::list blocks; + std::list missed_ids; + uint64_t current_blockchain_height; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(txs) + KV_SERIALIZE(blocks) + KV_SERIALIZE_CONTAINER_POD_AS_BLOB(missed_ids) + KV_SERIALIZE(current_blockchain_height) + END_KV_SERIALIZE_MAP() + }; + }; + + + struct CORE_SYNC_DATA + { + uint64_t current_height; + crypto::hash top_id; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(current_height) + KV_SERIALIZE_VAL_POD_AS_BLOB(top_id) + END_KV_SERIALIZE_MAP() + }; + + struct NOTIFY_REQUEST_CHAIN + { + const static int ID = BC_COMMANDS_POOL_BASE + 6; + + struct request + { + std::list block_ids; /*IDs of the first 10 blocks are sequential, next goes with pow(2,n) offset, like 2, 4, 8, 16, 32, 64 and so on, and the last one is always genesis block */ + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_CONTAINER_POD_AS_BLOB(block_ids) + END_KV_SERIALIZE_MAP() + }; + }; + + struct NOTIFY_RESPONSE_CHAIN_ENTRY + { + const static int ID = BC_COMMANDS_POOL_BASE + 7; + + struct request + { + uint64_t start_height; + uint64_t total_height; + std::list m_block_ids; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(start_height) + KV_SERIALIZE(total_height) + KV_SERIALIZE_CONTAINER_POD_AS_BLOB(m_block_ids) + END_KV_SERIALIZE_MAP() + }; + }; + +} diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.h b/src/cryptonote_protocol/cryptonote_protocol_handler.h new file mode 100644 index 000000000..f599cf40f --- /dev/null +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.h @@ -0,0 +1,107 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include + +#include "storages/levin_abstract_invoke2.h" +#include "warnings.h" +#include "cryptonote_protocol_defs.h" +#include "cryptonote_protocol_handler_common.h" +#include "cryptonote_core/connection_context.h" +#include "cryptonote_core/cryptonote_stat_info.h" +#include "cryptonote_core/verification_context.h" + +PUSH_WARNINGS +DISABLE_VS_WARNINGS(4355) + +namespace cryptonote +{ + + template + class t_cryptonote_protocol_handler: public i_cryptonote_protocol + { + public: + typedef cryptonote_connection_context connection_context; + typedef core_stat_info stat_info; + typedef t_cryptonote_protocol_handler cryptonote_protocol_handler; + typedef CORE_SYNC_DATA payload_type; + + t_cryptonote_protocol_handler(t_core& rcore, nodetool::i_p2p_endpoint* p_net_layout); + + BEGIN_INVOKE_MAP2(cryptonote_protocol_handler) + HANDLE_NOTIFY_T2(NOTIFY_NEW_BLOCK, &cryptonote_protocol_handler::handle_notify_new_block) + HANDLE_NOTIFY_T2(NOTIFY_NEW_TRANSACTIONS, &cryptonote_protocol_handler::handle_notify_new_transactions) + HANDLE_NOTIFY_T2(NOTIFY_REQUEST_GET_OBJECTS, &cryptonote_protocol_handler::handle_request_get_objects) + HANDLE_NOTIFY_T2(NOTIFY_RESPONSE_GET_OBJECTS, &cryptonote_protocol_handler::handle_response_get_objects) + HANDLE_NOTIFY_T2(NOTIFY_REQUEST_CHAIN, &cryptonote_protocol_handler::handle_request_chain) + HANDLE_NOTIFY_T2(NOTIFY_RESPONSE_CHAIN_ENTRY, &cryptonote_protocol_handler::handle_response_chain_entry) + END_INVOKE_MAP2() + + bool on_idle(); + bool init(const boost::program_options::variables_map& vm); + bool deinit(); + void set_p2p_endpoint(nodetool::i_p2p_endpoint* p2p); + //bool process_handshake_data(const blobdata& data, cryptonote_connection_context& context); + bool process_payload_sync_data(const CORE_SYNC_DATA& hshd, cryptonote_connection_context& context, bool is_inital); + bool get_payload_sync_data(blobdata& data); + bool get_payload_sync_data(CORE_SYNC_DATA& hshd); + bool get_stat_info(core_stat_info& stat_inf); + bool on_callback(cryptonote_connection_context& context); + t_core& get_core(){return m_core;} + + + private: + //----------------- commands handlers ---------------------------------------------- + int handle_notify_new_block(int command, NOTIFY_NEW_BLOCK::request& arg, cryptonote_connection_context& context); + int handle_notify_new_transactions(int command, NOTIFY_NEW_TRANSACTIONS::request& arg, cryptonote_connection_context& context); + int handle_request_get_objects(int command, NOTIFY_REQUEST_GET_OBJECTS::request& arg, cryptonote_connection_context& context); + int handle_response_get_objects(int command, NOTIFY_RESPONSE_GET_OBJECTS::request& arg, cryptonote_connection_context& context); + int handle_request_chain(int command, NOTIFY_REQUEST_CHAIN::request& arg, cryptonote_connection_context& context); +// int handle_request_chain_entry(int command, NOTIFY_REQUEST_CHAIN_ENTRY::request& arg, cryptonote_connection_context& context); + int handle_response_chain_entry(int command, NOTIFY_RESPONSE_CHAIN_ENTRY::request& arg, cryptonote_connection_context& context); + + + //----------------- i_bc_protocol_layout --------------------------------------- + virtual bool relay_block(NOTIFY_NEW_BLOCK::request& arg, cryptonote_connection_context& exclude_context); + virtual bool relay_transactions(NOTIFY_NEW_TRANSACTIONS::request& arg, cryptonote_connection_context& exclude_context); + //---------------------------------------------------------------------------------- + //bool get_payload_sync_data(HANDSHAKE_DATA::request& hshd, cryptonote_connection_context& context); + bool request_missing_objects(cryptonote_connection_context& context, bool check_having_blocks); + size_t get_synchronizing_connections_count(); + bool on_connection_synchronized(); + t_core& m_core; + + nodetool::p2p_endpoint_stub m_p2p_stub; + nodetool::i_p2p_endpoint* m_p2p; + std::atomic m_syncronized_connections_count; + //std::atomic m_syncronizing_connections_count; + std::atomic m_welcome_showed; + + + template + bool post_notify(typename t_parametr::request& arg, cryptonote_connection_context& context) + { + LOG_PRINT_L2("[" << net_utils::print_connection_context_short(context) << "] post " << typeid(t_parametr).name() << " -->"); + std::string blob; + epee::serialization::store_t_to_binary(arg, blob); + return m_p2p->invoke_notify_to_peer(t_parametr::ID, blob, context); + } + + template + bool relay_post_notify(typename t_parametr::request& arg, cryptonote_connection_context& exlude_context) + { + LOG_PRINT_L2("[" << net_utils::print_connection_context_short(exlude_context) << "] post relay " << typeid(t_parametr).name() << " -->"); + std::string arg_buff; + epee::serialization::store_t_to_binary(arg, arg_buff); + return m_p2p->relay_notify_to_all(t_parametr::ID, arg_buff, exlude_context); + } + }; +} + + +#include "cryptonote_protocol_handler.inl" + +POP_WARNINGS diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl new file mode 100644 index 000000000..9c9668071 --- /dev/null +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -0,0 +1,474 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include +#include "cryptonote_core/cryptonote_format_utils.h" +#include "profile_tools.h" +namespace cryptonote +{ + + //----------------------------------------------------------------------------------------------------------------------- + template + t_cryptonote_protocol_handler::t_cryptonote_protocol_handler(t_core& rcore, nodetool::i_p2p_endpoint* p_net_layout):m_core(rcore), + m_p2p(p_net_layout), + m_syncronized_connections_count(0), + m_welcome_showed(false) + + { + if(!m_p2p) + m_p2p = &m_p2p_stub; + } + //----------------------------------------------------------------------------------------------------------------------- + template + bool t_cryptonote_protocol_handler::init(const boost::program_options::variables_map& vm) + { + return true; + } + //------------------------------------------------------------------------------------------------------------------------ + template + bool t_cryptonote_protocol_handler::deinit() + { + + + return true; + } + //------------------------------------------------------------------------------------------------------------------------ + template + void t_cryptonote_protocol_handler::set_p2p_endpoint(nodetool::i_p2p_endpoint* p2p) + { + if(p2p) + m_p2p = p2p; + else + m_p2p = &m_p2p_stub; + } + //------------------------------------------------------------------------------------------------------------------------ + template + bool t_cryptonote_protocol_handler::on_callback(cryptonote_connection_context& context) + { + LOG_PRINT_CCONTEXT_L2("callback fired"); + CHECK_AND_ASSERT_MES_CC( context.m_callback_request_count > 0, false, "false callback fired, but context.m_callback_request_count=" << context.m_callback_request_count); + --context.m_callback_request_count; + + if(context.m_state == cryptonote_connection_context::state_synchronizing) + { + NOTIFY_REQUEST_CHAIN::request r = boost::value_initialized(); + m_core.get_short_chain_history(r.block_ids); + LOG_PRINT_CCONTEXT_L2("-->>NOTIFY_REQUEST_CHAIN: m_block_ids.size()=" << r.block_ids.size() ); + post_notify(r, context); + } + + return true; + } + //------------------------------------------------------------------------------------------------------------------------ + template + bool t_cryptonote_protocol_handler::get_stat_info(core_stat_info& stat_inf) + { + return m_core.get_stat_info(stat_inf); + } + //------------------------------------------------------------------------------------------------------------------------ + template + bool t_cryptonote_protocol_handler::process_payload_sync_data(const CORE_SYNC_DATA& hshd, cryptonote_connection_context& context, bool is_inital) + { + if(context.m_state == cryptonote_connection_context::state_befor_handshake && !is_inital) + return true; + + if(context.m_state == cryptonote_connection_context::state_synchronizing) + return true; + + if(m_core.have_block(hshd.top_id)) + { + context.m_state = cryptonote_connection_context::state_normal; + if(is_inital) + on_connection_synchronized(); + return true; + } + + LOG_PRINT_CCONTEXT_BLUE("Sync data returned unknown top block " << "["<< m_core.get_current_blockchain_height() << "->" << hshd.current_height << "] " << hshd.top_id << ", set SYNCHRONIZATION mode", LOG_LEVEL_0); + context.m_state = cryptonote_connection_context::state_synchronizing; + context.m_remote_blockchain_height = hshd.current_height; + //let the socket to send response to handshake, but request callback, to let send request data after response + LOG_PRINT_CCONTEXT_L2("requesting callback"); + ++context.m_callback_request_count; + m_p2p->request_callback(context); + return true; + } + //------------------------------------------------------------------------------------------------------------------------ + /* template + bool t_cryptonote_protocol_handler::process_handshake_data(const blobdata& data, cryptonote_connection_context& context) + { + CORE_SYNC_DATA hsd = boost::value_initialized(); + StorageNamed::load_struct_from_storage_buff(hsd, data); + return process_handshake_data(hsd, context); + }*/ + //------------------------------------------------------------------------------------------------------------------------ + template + bool t_cryptonote_protocol_handler::get_payload_sync_data(CORE_SYNC_DATA& hshd) + { + m_core.get_blockchain_top(hshd.current_height, hshd.top_id); + hshd.current_height +=1; + return true; + } + //------------------------------------------------------------------------------------------------------------------------ + template + bool t_cryptonote_protocol_handler::get_payload_sync_data(blobdata& data) + { + CORE_SYNC_DATA hsd = boost::value_initialized(); + get_payload_sync_data(hsd); + epee::serialization::store_t_to_binary(hsd, data); + return true; + } + //------------------------------------------------------------------------------------------------------------------------ + template + int t_cryptonote_protocol_handler::handle_notify_new_block(int command, NOTIFY_NEW_BLOCK::request& arg, cryptonote_connection_context& context) + { + LOG_PRINT_CCONTEXT_L2("NOTIFY_NEW_BLOCK (hop " << arg.hop << ")"); + if(context.m_state != cryptonote_connection_context::state_normal) + return 1; + + for(auto tx_blob_it = arg.b.txs.begin(); tx_blob_it!=arg.b.txs.end();tx_blob_it++) + { + cryptonote::tx_verification_context tvc = AUTO_VAL_INIT(tvc); + m_core.handle_incoming_tx(*tx_blob_it, tvc, true); + if(tvc.m_verifivation_failed) + { + LOG_PRINT_CCONTEXT_L0("Block verification failed: transaction verification failed, dropping connection"); + m_p2p->drop_connection(context); + return 1; + } + } + + + block_verification_context bvc = boost::value_initialized(); + m_core.pause_mine(); + m_core.handle_incoming_block(arg.b.block, bvc); + m_core.resume_mine(); + if(bvc.m_verifivation_failed) + { + LOG_PRINT_CCONTEXT_L0("Block verification failed, dropping connection"); + m_p2p->drop_connection(context); + return 1; + } + if(bvc.m_added_to_main_chain) + { + ++arg.hop; + //TODO: Add here announce protocol usage + relay_block(arg, context); + }else if(bvc.m_marked_as_orphaned) + { + context.m_state = cryptonote_connection_context::state_synchronizing; + NOTIFY_REQUEST_CHAIN::request r = boost::value_initialized(); + m_core.get_short_chain_history(r.block_ids); + LOG_PRINT_CCONTEXT_L2("-->>NOTIFY_REQUEST_CHAIN: m_block_ids.size()=" << r.block_ids.size() ); + post_notify(r, context); + } + + return 1; + } + //------------------------------------------------------------------------------------------------------------------------ + template + int t_cryptonote_protocol_handler::handle_notify_new_transactions(int command, NOTIFY_NEW_TRANSACTIONS::request& arg, cryptonote_connection_context& context) + { + LOG_PRINT_CCONTEXT_L2("NOTIFY_NEW_TRANSACTIONS"); + if(context.m_state != cryptonote_connection_context::state_normal) + return 1; + + for(auto tx_blob_it = arg.txs.begin(); tx_blob_it!=arg.txs.end();) + { + cryptonote::tx_verification_context tvc = AUTO_VAL_INIT(tvc); + m_core.handle_incoming_tx(*tx_blob_it, tvc, false); + if(tvc.m_verifivation_failed) + { + LOG_PRINT_CCONTEXT_L0("Tx verification failed, dropping connection"); + m_p2p->drop_connection(context); + return 1; + } + if(tvc.m_should_be_relayed) + ++tx_blob_it; + else + arg.txs.erase(tx_blob_it++); + } + + if(arg.txs.size()) + { + //TODO: add announce usage here + relay_transactions(arg, context); + } + + return true; + } + //------------------------------------------------------------------------------------------------------------------------ + template + int t_cryptonote_protocol_handler::handle_request_get_objects(int command, NOTIFY_REQUEST_GET_OBJECTS::request& arg, cryptonote_connection_context& context) + { + LOG_PRINT_CCONTEXT_L2("NOTIFY_REQUEST_GET_OBJECTS"); + NOTIFY_RESPONSE_GET_OBJECTS::request rsp; + if(!m_core.handle_get_objects(arg, rsp, context)) + { + LOG_ERROR_CCONTEXT("failed to handle request NOTIFY_REQUEST_GET_OBJECTS, dropping connection"); + m_p2p->drop_connection(context); + } + LOG_PRINT_CCONTEXT_L2("-->>NOTIFY_RESPONSE_GET_OBJECTS: blocks.size()=" << rsp.blocks.size() << ", txs.size()=" << rsp.txs.size() + << ", rsp.m_current_blockchain_height=" << rsp.current_blockchain_height << ", missed_ids.size()=" << rsp.missed_ids.size()); + post_notify(rsp, context); + return 1; + } + //------------------------------------------------------------------------------------------------------------------------ + template + int t_cryptonote_protocol_handler::handle_response_get_objects(int command, NOTIFY_RESPONSE_GET_OBJECTS::request& arg, cryptonote_connection_context& context) + { + LOG_PRINT_CCONTEXT_L2("NOTIFY_RESPONSE_GET_OBJECTS"); + if(context.m_last_response_height > arg.current_blockchain_height) + { + LOG_ERROR_CCONTEXT("sent wrong NOTIFY_HAVE_OBJECTS: arg.m_current_blockchain_height=" << arg.current_blockchain_height + << " < m_last_response_height=" << context.m_last_response_height << ", dropping connection"); + m_p2p->drop_connection(context); + return 1; + } + + context.m_remote_blockchain_height = arg.current_blockchain_height; + + BOOST_FOREACH(const block_complete_entry& block_entry, arg.blocks) + { + block b; + if(!parse_and_validate_block_from_blob(block_entry.block, b)) + { + LOG_ERROR_CCONTEXT("sent wrong block: failed to parse and validate block: \r\n" + << string_tools::buff_to_hex_nodelimer(block_entry.block) << "\r\n dropping connection"); + m_p2p->drop_connection(context); + return 1; + } + + auto req_it = context.m_requested_objects.find(get_block_hash(b)); + if(req_it == context.m_requested_objects.end()) + { + LOG_ERROR_CCONTEXT("sent wrong NOTIFY_RESPONSE_GET_OBJECTS: block with id=" << string_tools::pod_to_hex(get_blob_hash(block_entry.block)) + << " wasn't requested, dropping connection"); + m_p2p->drop_connection(context); + return 1; + } + if(b.tx_hashes.size() != block_entry.txs.size()) + { + LOG_ERROR_CCONTEXT("sent wrong NOTIFY_RESPONSE_GET_OBJECTS: block with id=" << string_tools::pod_to_hex(get_blob_hash(block_entry.block)) + << ", tx_hashes.size()=" << b.tx_hashes.size() << " mismatch with block_complete_entry.m_txs.size()=" << block_entry.txs.size() << ", dropping connection"); + m_p2p->drop_connection(context); + return 1; + } + + context.m_requested_objects.erase(req_it); + } + + if(context.m_requested_objects.size()) + { + LOG_PRINT_CCONTEXT_RED("returned not all requested objects (context.m_requested_objects.size()=" + << context.m_requested_objects.size() << "), dropping connection", LOG_LEVEL_0); + m_p2p->drop_connection(context); + return 1; + } + + { + m_core.pause_mine(); + misc_utils::auto_scope_leave_caller scope_exit_handler = misc_utils::create_scope_leave_handler( + boost::bind(&t_core::resume_mine, &m_core)); + + BOOST_FOREACH(const block_complete_entry& block_entry, arg.blocks) + { + //process transactions + TIME_MEASURE_START(transactions_process_time); + BOOST_FOREACH(auto& tx_blob, block_entry.txs) + { + tx_verification_context tvc = AUTO_VAL_INIT(tvc); + m_core.handle_incoming_tx(tx_blob, tvc, true); + if(tvc.m_verifivation_failed) + { + LOG_ERROR_CCONTEXT("transaction verification failed on NOTIFY_RESPONSE_GET_OBJECTS, \r\ntx_id = " + << string_tools::pod_to_hex(get_blob_hash(tx_blob)) << ", dropping connection"); + m_p2p->drop_connection(context); + return 1; + } + } + TIME_MEASURE_FINISH(transactions_process_time); + + //process block + TIME_MEASURE_START(block_process_time); + block_verification_context bvc = boost::value_initialized(); + + m_core.handle_incoming_block(block_entry.block, bvc, false); + + if(bvc.m_verifivation_failed) + { + LOG_PRINT_CCONTEXT_L0("Block verification failed, dropping connection"); + m_p2p->drop_connection(context); + return 1; + } + if(bvc.m_marked_as_orphaned) + { + LOG_PRINT_CCONTEXT_L0("Block received at sync phase was marked as orphaned, dropping connection"); + m_p2p->drop_connection(context); + return 1; + } + + TIME_MEASURE_FINISH(block_process_time); + LOG_PRINT_CCONTEXT_L2("Block process time: " << block_process_time + transactions_process_time << "(" << transactions_process_time << "/" << block_process_time << ")ms"); + } + } + + request_missing_objects(context, true); + return 1; + } + //------------------------------------------------------------------------------------------------------------------------ + template + bool t_cryptonote_protocol_handler::on_idle() + { + return m_core.on_idle(); + } + //------------------------------------------------------------------------------------------------------------------------ + template + int t_cryptonote_protocol_handler::handle_request_chain(int command, NOTIFY_REQUEST_CHAIN::request& arg, cryptonote_connection_context& context) + { + LOG_PRINT_CCONTEXT_L2("NOTIFY_REQUEST_CHAIN: m_block_ids.size()=" << arg.block_ids.size()); + NOTIFY_RESPONSE_CHAIN_ENTRY::request r; + if(!m_core.find_blockchain_supplement(arg.block_ids, r)) + { + LOG_ERROR_CCONTEXT("Failed to handle NOTIFY_REQUEST_CHAIN."); + return 1; + } + LOG_PRINT_CCONTEXT_L2("-->>NOTIFY_RESPONSE_CHAIN_ENTRY: m_start_height=" << r.start_height << ", m_total_height=" << r.total_height << ", m_block_ids.size()=" << r.m_block_ids.size()); + post_notify(r, context); + return 1; + } + //------------------------------------------------------------------------------------------------------------------------ + template + bool t_cryptonote_protocol_handler::request_missing_objects(cryptonote_connection_context& context, bool check_having_blocks) + { + if(context.m_needed_objects.size()) + { + //we know objects that we need, request this objects + NOTIFY_REQUEST_GET_OBJECTS::request req; + size_t count = 0; + auto it = context.m_needed_objects.begin(); + + while(it != context.m_needed_objects.end() && count < BLOCKS_SYNCHRONIZING_DEFAULT_COUNT) + { + if( !(check_having_blocks && m_core.have_block(*it))) + { + req.blocks.push_back(*it); + ++count; + context.m_requested_objects.insert(*it); + } + context.m_needed_objects.erase(it++); + } + LOG_PRINT_CCONTEXT_L2("-->>NOTIFY_REQUEST_GET_OBJECTS: blocks.size()=" << req.blocks.size() << ", txs.size()=" << req.txs.size()); + post_notify(req, context); + }else if(context.m_last_response_height < context.m_remote_blockchain_height-1) + {//we have to fetch more objects ids, request blockchain entry + + NOTIFY_REQUEST_CHAIN::request r = boost::value_initialized(); + m_core.get_short_chain_history(r.block_ids); + LOG_PRINT_CCONTEXT_L2("-->>NOTIFY_REQUEST_CHAIN: m_block_ids.size()=" << r.block_ids.size() ); + post_notify(r, context); + }else + { + CHECK_AND_ASSERT_MES(context.m_last_response_height == context.m_remote_blockchain_height-1 + && !context.m_needed_objects.size() + && !context.m_requested_objects.size(), false, "request_missing_blocks final condition failed!" + << "\r\nm_last_response_height=" << context.m_last_response_height + << "\r\nm_remote_blockchain_height=" << context.m_remote_blockchain_height + << "\r\nm_needed_objects.size()=" << context.m_needed_objects.size() + << "\r\nm_requested_objects.size()=" << context.m_requested_objects.size() + << "\r\non connection [" << net_utils::print_connection_context_short(context)<< "]"); + + context.m_state = cryptonote_connection_context::state_normal; + LOG_PRINT_CCONTEXT_GREEN(" SYNCHRONIZED OK", LOG_LEVEL_0); + if( true/*get_synchronizing_connections_count() == 0 && !m_welcome_showed*/) + { + on_connection_synchronized(); + } + } + return true; + } + //------------------------------------------------------------------------------------------------------------------------ + template + bool t_cryptonote_protocol_handler::on_connection_synchronized() + { + bool val_expected = false; + if(m_welcome_showed.compare_exchange_strong(val_expected, true)) + { + LOG_PRINT_L0(ENDL << "**********************************************************************" << ENDL + << "You are now synchronized with the network. You may now start simplewallet." << ENDL + << ENDL + << "Please note, that the blockchain will be saved only after you quit the daemon with \"exit\" command or if you use \"save\" command." << ENDL + << "Otherwise, you will possibly need to synchronize the blockchain again." << ENDL + << ENDL + << "Use \"help\" command to see the list of available commands." << ENDL + << "**********************************************************************"); + m_core.on_synchronized(); + } + return true; + } + //------------------------------------------------------------------------------------------------------------------------ + template + size_t t_cryptonote_protocol_handler::get_synchronizing_connections_count() + { + size_t count = 0; + m_p2p->for_each_connection([&](cryptonote_connection_context& context)->bool{ + if(context.m_state == cryptonote_connection_context::state_synchronizing) + ++count; + return true; + }); + return count; + } + //------------------------------------------------------------------------------------------------------------------------ + template + int t_cryptonote_protocol_handler::handle_response_chain_entry(int command, NOTIFY_RESPONSE_CHAIN_ENTRY::request& arg, cryptonote_connection_context& context) + { + LOG_PRINT_CCONTEXT_L2("NOTIFY_RESPONSE_CHAIN_ENTRY: m_block_ids.size()=" << arg.m_block_ids.size() + << ", m_start_height=" << arg.start_height << ", m_total_height=" << arg.total_height); + + if(!arg.m_block_ids.size()) + { + LOG_ERROR_CCONTEXT("sent empty m_block_ids, dropping connection"); + m_p2p->drop_connection(context); + return 1; + } + + if(!m_core.have_block(arg.m_block_ids.front())) + { + LOG_ERROR_CCONTEXT("sent m_block_ids starting from unknown id: " + << string_tools::pod_to_hex(arg.m_block_ids.front()) << " , dropping connection"); + m_p2p->drop_connection(context); + return 1; + } + + context.m_remote_blockchain_height = arg.total_height; + context.m_last_response_height = arg.start_height + arg.m_block_ids.size()-1; + if(context.m_last_response_height > context.m_remote_blockchain_height) + { + LOG_ERROR_CCONTEXT("sent wrong NOTIFY_RESPONSE_CHAIN_ENTRY, with \r\nm_total_height=" << arg.total_height + << "\r\nm_start_height=" << arg.start_height + << "\r\nm_block_ids.size()=" << arg.m_block_ids.size()); + m_p2p->drop_connection(context); + } + + BOOST_FOREACH(auto& bl_id, arg.m_block_ids) + { + if(!m_core.have_block(bl_id)) + context.m_needed_objects.push_back(bl_id); + } + + request_missing_objects(context, false); + return 1; + } + //------------------------------------------------------------------------------------------------------------------------ + template + bool t_cryptonote_protocol_handler::relay_block(NOTIFY_NEW_BLOCK::request& arg, cryptonote_connection_context& exclude_context) + { + return relay_post_notify(arg, exclude_context); + } + //------------------------------------------------------------------------------------------------------------------------ + template + bool t_cryptonote_protocol_handler::relay_transactions(NOTIFY_NEW_TRANSACTIONS::request& arg, cryptonote_connection_context& exclude_context) + { + return relay_post_notify(arg, exclude_context); + } +} diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler_common.h b/src/cryptonote_protocol/cryptonote_protocol_handler_common.h new file mode 100644 index 000000000..f1ced5050 --- /dev/null +++ b/src/cryptonote_protocol/cryptonote_protocol_handler_common.h @@ -0,0 +1,37 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include "p2p/net_node_common.h" +#include "cryptonote_protocol/cryptonote_protocol_defs.h" +#include "cryptonote_core/connection_context.h" +namespace cryptonote +{ + /************************************************************************/ + /* */ + /************************************************************************/ + struct i_cryptonote_protocol + { + virtual bool relay_block(NOTIFY_NEW_BLOCK::request& arg, cryptonote_connection_context& exclude_context)=0; + virtual bool relay_transactions(NOTIFY_NEW_TRANSACTIONS::request& arg, cryptonote_connection_context& exclude_context)=0; + //virtual bool request_objects(NOTIFY_REQUEST_GET_OBJECTS::request& arg, cryptonote_connection_context& context)=0; + }; + + /************************************************************************/ + /* */ + /************************************************************************/ + struct cryptonote_protocol_stub: public i_cryptonote_protocol + { + virtual bool relay_block(NOTIFY_NEW_BLOCK::request& arg, cryptonote_connection_context& exclude_context) + { + return false; + } + virtual bool relay_transactions(NOTIFY_NEW_TRANSACTIONS::request& arg, cryptonote_connection_context& exclude_context) + { + return false; + } + + }; +} diff --git a/src/daemon/daemon.cpp b/src/daemon/daemon.cpp new file mode 100644 index 000000000..d1ac3714b --- /dev/null +++ b/src/daemon/daemon.cpp @@ -0,0 +1,234 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +// node.cpp : Defines the entry point for the console application. +// + + +#include "include_base_utils.h" +#include "version.h" + +using namespace epee; + +#include + +#include "crypto/hash.h" +#include "console_handler.h" +#include "p2p/net_node.h" +#include "cryptonote_core/checkpoints_create.h" +#include "cryptonote_core/cryptonote_core.h" +#include "rpc/core_rpc_server.h" +#include "cryptonote_protocol/cryptonote_protocol_handler.h" +#include "daemon_commands_handler.h" +#include "version.h" + +#if defined(WIN32) +#include +#endif + +namespace po = boost::program_options; + + +BOOST_CLASS_VERSION(nodetool::node_server >, 1); + +namespace +{ + const command_line::arg_descriptor arg_config_file = {"config-file", "Specify configuration file", std::string(CRYPTONOTE_NAME ".conf")}; + const command_line::arg_descriptor arg_os_version = {"os-version", ""}; + const command_line::arg_descriptor arg_log_file = {"log-file", "", ""}; + const command_line::arg_descriptor arg_log_level = {"log-level", "", LOG_LEVEL_0}; +} + +bool command_line_preprocessor(const boost::program_options::variables_map& vm); + +int main(int argc, char* argv[]) +{ + + string_tools::set_module_name_and_folder(argv[0]); +#ifdef WIN32 + _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ); +#endif + log_space::get_set_log_detalisation_level(true, LOG_LEVEL_0); + log_space::log_singletone::add_logger(LOGGER_CONSOLE, NULL, NULL); + LOG_PRINT_L0("Starting..."); + + TRY_ENTRY(); + + po::options_description desc_cmd_only("Command line options"); + po::options_description desc_cmd_sett("Command line options and settings options"); + + command_line::add_arg(desc_cmd_only, command_line::arg_help); + command_line::add_arg(desc_cmd_only, command_line::arg_version); + command_line::add_arg(desc_cmd_only, arg_os_version); + // tools::get_default_data_dir() can't be called during static initialization + command_line::add_arg(desc_cmd_only, command_line::arg_data_dir, tools::get_default_data_dir()); + command_line::add_arg(desc_cmd_only, arg_config_file); + + command_line::add_arg(desc_cmd_sett, arg_log_file); + command_line::add_arg(desc_cmd_sett, arg_log_level); + + cryptonote::core::init_options(desc_cmd_sett); + cryptonote::core_rpc_server::init_options(desc_cmd_sett); + nodetool::node_server >::init_options(desc_cmd_sett); + cryptonote::miner::init_options(desc_cmd_sett); + + po::options_description desc_options("Allowed options"); + desc_options.add(desc_cmd_only).add(desc_cmd_sett); + + po::variables_map vm; + bool r = command_line::handle_error_helper(desc_options, [&]() + { + po::store(po::parse_command_line(argc, argv, desc_options), vm); + + if (command_line::get_arg(vm, command_line::arg_help)) + { + std::cout << CRYPTONOTE_NAME << " v" << PROJECT_VERSION_LONG << ENDL << ENDL; + std::cout << desc_options << std::endl; + return false; + } + + std::string data_dir = command_line::get_arg(vm, command_line::arg_data_dir); + std::string config = command_line::get_arg(vm, arg_config_file); + + boost::filesystem::path data_dir_path(data_dir); + boost::filesystem::path config_path(config); + if (!config_path.has_parent_path()) + { + config_path = data_dir_path / config_path; + } + + boost::system::error_code ec; + if (boost::filesystem::exists(config_path, ec)) + { + po::store(po::parse_config_file(config_path.string().c_str(), desc_cmd_sett), vm); + } + po::notify(vm); + + return true; + }); + if (!r) + return 1; + + //set up logging options + boost::filesystem::path log_file_path(command_line::get_arg(vm, arg_log_file)); + if (log_file_path.empty()) + log_file_path = log_space::log_singletone::get_default_log_file(); + std::string log_dir; + log_dir = log_file_path.has_parent_path() ? log_file_path.parent_path().string() : log_space::log_singletone::get_default_log_folder(); + + log_space::log_singletone::add_logger(LOGGER_FILE, log_file_path.filename().string().c_str(), log_dir.c_str()); + + if (command_line_preprocessor(vm)) + { + return 0; + } + + LOG_PRINT("Module folder: " << argv[0], LOG_LEVEL_0); + + bool res = true; + cryptonote::checkpoints checkpoints; + res = cryptonote::create_checkpoints(checkpoints); + CHECK_AND_ASSERT_MES(res, 1, "Failed to initialize checkpoints"); + + //create objects and link them + cryptonote::core ccore(NULL); + ccore.set_checkpoints(std::move(checkpoints)); + cryptonote::t_cryptonote_protocol_handler cprotocol(ccore, NULL); + nodetool::node_server > p2psrv(cprotocol); + cryptonote::core_rpc_server rpc_server(ccore, p2psrv); + cprotocol.set_p2p_endpoint(&p2psrv); + ccore.set_cryptonote_protocol(&cprotocol); + daemon_cmmands_handler dch(p2psrv); + + //initialize objects + LOG_PRINT_L0("Initializing p2p server..."); + res = p2psrv.init(vm); + CHECK_AND_ASSERT_MES(res, 1, "Failed to initialize p2p server."); + LOG_PRINT_L0("P2p server initialized OK"); + + LOG_PRINT_L0("Initializing cryptonote protocol..."); + res = cprotocol.init(vm); + CHECK_AND_ASSERT_MES(res, 1, "Failed to initialize cryptonote protocol."); + LOG_PRINT_L0("Cryptonote protocol initialized OK"); + + LOG_PRINT_L0("Initializing core rpc server..."); + res = rpc_server.init(vm); + CHECK_AND_ASSERT_MES(res, 1, "Failed to initialize core rpc server."); + LOG_PRINT_GREEN("Core rpc server initialized OK on port: " << rpc_server.get_binded_port(), LOG_LEVEL_0); + + //initialize core here + LOG_PRINT_L0("Initializing core..."); + res = ccore.init(vm); + CHECK_AND_ASSERT_MES(res, 1, "Failed to initialize core"); + LOG_PRINT_L0("Core initialized OK"); + + // start components + dch.start_handling(); + + LOG_PRINT_L0("Starting core rpc server..."); + res = rpc_server.run(2, false); + CHECK_AND_ASSERT_MES(res, 1, "Failed to initialize core rpc server."); + LOG_PRINT_L0("Core rpc server started ok"); + + LOG_PRINT_L0("Starting p2p net loop..."); + p2psrv.run(); + LOG_PRINT_L0("p2p net loop stopped"); + + //stop components + LOG_PRINT_L0("Stopping core rpc server..."); + rpc_server.send_stop_signal(); + rpc_server.timed_wait_server_stop(5000); + + //deinitialize components + LOG_PRINT_L0("Deinitializing core..."); + ccore.deinit(); + LOG_PRINT_L0("Deinitializing rpc server ..."); + rpc_server.deinit(); + LOG_PRINT_L0("Deinitializing cryptonote_protocol..."); + cprotocol.deinit(); + LOG_PRINT_L0("Deinitializing p2p..."); + p2psrv.deinit(); + + + ccore.set_cryptonote_protocol(NULL); + cprotocol.set_p2p_endpoint(NULL); + + LOG_PRINT("Node stopped.", LOG_LEVEL_0); + return 0; + + CATCH_ENTRY_L0("main", 1); +} + +bool command_line_preprocessor(const boost::program_options::variables_map& vm) +{ + bool exit = false; + if (command_line::get_arg(vm, command_line::arg_version)) + { + std::cout << CRYPTONOTE_NAME << PROJECT_VERSION_LONG << ENDL; + exit = true; + } + if (command_line::get_arg(vm, arg_os_version)) + { + std::cout << "OS: " << tools::get_os_version_string() << ENDL; + exit = true; + } + + if (exit) + { + return true; + } + + int new_log_level = command_line::get_arg(vm, arg_log_level); + if(new_log_level < LOG_LEVEL_MIN || new_log_level > LOG_LEVEL_MAX) + { + LOG_PRINT_L0("Wrong log level value: "); + } + else if (log_space::get_set_log_detalisation_level(false) != new_log_level) + { + log_space::get_set_log_detalisation_level(true, new_log_level); + LOG_PRINT_L0("LOG_LEVEL set to " << new_log_level); + } + + return false; +} diff --git a/src/daemon/daemon_commands_handler.h b/src/daemon/daemon_commands_handler.h new file mode 100644 index 000000000..2edd5d414 --- /dev/null +++ b/src/daemon/daemon_commands_handler.h @@ -0,0 +1,291 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include + +#include "console_handler.h" +#include "p2p/net_node.h" +#include "cryptonote_protocol/cryptonote_protocol_handler.h" +#include "common/util.h" +#include "crypto/hash.h" + + +class daemon_cmmands_handler +{ + nodetool::node_server >& m_srv; +public: + daemon_cmmands_handler(nodetool::node_server >& srv):m_srv(srv) + { + m_cmd_binder.set_handler("help", boost::bind(&daemon_cmmands_handler::help, this, _1), "Show this help"); + m_cmd_binder.set_handler("print_pl", boost::bind(&daemon_cmmands_handler::print_pl, this, _1), "Print peer list"); + m_cmd_binder.set_handler("print_cn", boost::bind(&daemon_cmmands_handler::print_cn, this, _1), "Print connections"); + m_cmd_binder.set_handler("print_bc", boost::bind(&daemon_cmmands_handler::print_bc, this, _1), "Print blockchain info in a given blocks range, print_bc []"); + //m_cmd_binder.set_handler("print_bci", boost::bind(&daemon_cmmands_handler::print_bci, this, _1)); + //m_cmd_binder.set_handler("print_bc_outs", boost::bind(&daemon_cmmands_handler::print_bc_outs, this, _1)); + m_cmd_binder.set_handler("print_block", boost::bind(&daemon_cmmands_handler::print_block, this, _1), "Print block, print_block | "); + m_cmd_binder.set_handler("print_tx", boost::bind(&daemon_cmmands_handler::print_tx, this, _1), "Print transaction, print_tx "); + m_cmd_binder.set_handler("start_mining", boost::bind(&daemon_cmmands_handler::start_mining, this, _1), "Start mining for specified address, start_mining [threads=1]"); + m_cmd_binder.set_handler("stop_mining", boost::bind(&daemon_cmmands_handler::stop_mining, this, _1), "Stop mining"); + m_cmd_binder.set_handler("print_pool", boost::bind(&daemon_cmmands_handler::print_pool, this, _1), "Print transaction pool (long format)"); + m_cmd_binder.set_handler("print_pool_sh", boost::bind(&daemon_cmmands_handler::print_pool_sh, this, _1), "Print transaction pool (short format)"); + m_cmd_binder.set_handler("show_hr", boost::bind(&daemon_cmmands_handler::show_hr, this, _1), "Start showing hash rate"); + m_cmd_binder.set_handler("hide_hr", boost::bind(&daemon_cmmands_handler::hide_hr, this, _1), "Stop showing hash rate"); + m_cmd_binder.set_handler("save", boost::bind(&daemon_cmmands_handler::save, this, _1), "Save blockchain"); + } + + bool start_handling() + { + m_cmd_binder.start_handling(&m_srv, ""); + return true; + } + +private: + epee::srv_console_handlers_binder > > m_cmd_binder; + + //-------------------------------------------------------------------------------- + std::string get_commands_str() + { + std::stringstream ss; + ss << "Commands: " << ENDL; + std::string usage = m_cmd_binder.get_usage(); + boost::replace_all(usage, "\n", "\n "); + usage.insert(0, " "); + ss << usage << ENDL; + return ss.str(); + } + //-------------------------------------------------------------------------------- + bool help(const std::vector& args) + { + std::cout << get_commands_str() << ENDL; + return true; + } + //-------------------------------------------------------------------------------- + bool print_pl(const std::vector& args) + { + m_srv.log_peerlist(); + return true; + } + //-------------------------------------------------------------------------------- + bool save(const std::vector& args) + { + m_srv.get_payload_object().get_core().get_blockchain_storage().store_blockchain(); + return true; + } + //-------------------------------------------------------------------------------- + bool show_hr(const std::vector& args) + { + m_srv.get_payload_object().get_core().get_miner().do_print_hashrate(true); + return true; + } + //-------------------------------------------------------------------------------- + bool hide_hr(const std::vector& args) + { + m_srv.get_payload_object().get_core().get_miner().do_print_hashrate(false); + return true; + } + //-------------------------------------------------------------------------------- + bool print_bc_outs(const std::vector& args) + { + if(args.size() != 1) + { + std::cout << "need file path as parameter" << ENDL; + return true; + } + m_srv.get_payload_object().get_core().print_blockchain_outs(args[0]); + return true; + } + //-------------------------------------------------------------------------------- + bool print_cn(const std::vector& args) + { + m_srv.log_connections(); + return true; + } + //-------------------------------------------------------------------------------- + bool print_bc(const std::vector& args) + { + if(!args.size()) + { + std::cout << "need block index parameter" << ENDL; + return false; + } + uint64_t start_index = 0; + uint64_t end_block_parametr = m_srv.get_payload_object().get_core().get_current_blockchain_height(); + if(!string_tools::get_xtype_from_string(start_index, args[0])) + { + std::cout << "wrong starter block index parameter" << ENDL; + return false; + } + if(args.size() >1 && !string_tools::get_xtype_from_string(end_block_parametr, args[1])) + { + std::cout << "wrong end block index parameter" << ENDL; + return false; + } + + m_srv.get_payload_object().get_core().print_blockchain(start_index, end_block_parametr); + return true; + } + //-------------------------------------------------------------------------------- + bool print_bci(const std::vector& args) + { + m_srv.get_payload_object().get_core().print_blockchain_index(); + return true; + } + //-------------------------------------------------------------------------------- + template + static bool print_as_json(T& obj) + { + std::cout << cryptonote::obj_to_json_str(obj) << ENDL; + return true; + } + //-------------------------------------------------------------------------------- + bool print_block_by_height(uint64_t height) + { + std::list blocks; + m_srv.get_payload_object().get_core().get_blocks(height, 1, blocks); + + if (1 == blocks.size()) + { + cryptonote::block& block = blocks.front(); + std::cout << "block_id: " << get_block_hash(block) << ENDL; + print_as_json(block); + } + else + { + uint64_t current_height; + crypto::hash top_id; + m_srv.get_payload_object().get_core().get_blockchain_top(current_height, top_id); + std::cout << "block wasn't found. Current block chain height: " << current_height << ", requested: " << height << std::endl; + return false; + } + + return true; + } + //-------------------------------------------------------------------------------- + bool print_block_by_hash(const std::string& arg) + { + crypto::hash block_hash; + if (!parse_hash256(arg, block_hash)) + { + return false; + } + + std::list block_ids; + block_ids.push_back(block_hash); + std::list blocks; + std::list missed_ids; + m_srv.get_payload_object().get_core().get_blocks(block_ids, blocks, missed_ids); + + if (1 == blocks.size()) + { + cryptonote::block block = blocks.front(); + print_as_json(block); + } + else + { + std::cout << "block wasn't found: " << arg << std::endl; + return false; + } + + return true; + } + //-------------------------------------------------------------------------------- + bool print_block(const std::vector& args) + { + if (args.empty()) + { + std::cout << "expected: print_block ( | )" << std::endl; + return true; + } + + const std::string& arg = args.front(); + try + { + uint64_t height = boost::lexical_cast(arg); + print_block_by_height(height); + } + catch (boost::bad_lexical_cast&) + { + print_block_by_hash(arg); + } + + return true; + } + //-------------------------------------------------------------------------------- + bool print_tx(const std::vector& args) + { + if (args.empty()) + { + std::cout << "expected: print_tx " << std::endl; + return true; + } + + const std::string& str_hash = args.front(); + crypto::hash tx_hash; + if (!parse_hash256(str_hash, tx_hash)) + { + return true; + } + + std::vector tx_ids; + tx_ids.push_back(tx_hash); + std::list txs; + std::list missed_ids; + m_srv.get_payload_object().get_core().get_transactions(tx_ids, txs, missed_ids); + + if (1 == txs.size()) + { + cryptonote::transaction tx = txs.front(); + print_as_json(tx); + } + else + { + std::cout << "transaction wasn't found: <" << str_hash << '>' << std::endl; + } + + return true; + } + //-------------------------------------------------------------------------------- + bool print_pool(const std::vector& args) + { + LOG_PRINT_L0("Pool state: " << ENDL << m_srv.get_payload_object().get_core().print_pool(false)); + return true; + } + //-------------------------------------------------------------------------------- + bool print_pool_sh(const std::vector& args) + { + LOG_PRINT_L0("Pool state: " << ENDL << m_srv.get_payload_object().get_core().print_pool(true)); + return true; + } //-------------------------------------------------------------------------------- + bool start_mining(const std::vector& args) + { + if(!args.size()) + { + std::cout << "target account address for mining is not set" << std::endl; + return true; + } + + cryptonote::account_public_address adr; + if(!cryptonote::get_account_address_from_str(adr, args.front())) + { + std::cout << "target account address has wrong format" << std::endl; + return true; + } + size_t threads_count = 1; + if(args.size() > 1) + { + string_tools::get_xtype_from_string(threads_count, args[1]); + } + + m_srv.get_payload_object().get_core().get_miner().start(adr, threads_count); + return true; + } + //-------------------------------------------------------------------------------- + bool stop_mining(const std::vector& args) + { + m_srv.get_payload_object().get_core().get_miner().stop(); + return true; + } +}; diff --git a/src/miner/simpleminer.cpp b/src/miner/simpleminer.cpp new file mode 100644 index 000000000..a9f0ceab0 --- /dev/null +++ b/src/miner/simpleminer.cpp @@ -0,0 +1,209 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#include "common/command_line.h" +#include "misc_log_ex.h" +#include "simpleminer.h" +#include "target_helper.h" +#include "net/http_server_handlers_map2.h" +#include "simpleminer_protocol_defs.h" +#include "storages/http_abstract_invoke.h" +#include "string_tools.h" +#include "cryptonote_core/account.h" +#include "cryptonote_core/cryptonote_format_utils.h" + +using namespace epee; +namespace po = boost::program_options; + +int main(int argc, char** argv) +{ + string_tools::set_module_name_and_folder(argv[0]); + + //set up logging options + log_space::get_set_log_detalisation_level(true, LOG_LEVEL_4); + log_space::log_singletone::add_logger(LOGGER_CONSOLE, NULL, NULL); + log_space::log_singletone::add_logger(LOGGER_FILE, + log_space::log_singletone::get_default_log_file().c_str(), + log_space::log_singletone::get_default_log_folder().c_str()); + + po::options_description desc("Allowed options"); + command_line::add_arg(desc, command_line::arg_help); + mining::simpleminer::init_options(desc); + + //uint32_t t = mining::get_target_for_difficulty(700000); + + po::variables_map vm; + bool r = command_line::handle_error_helper(desc, [&]() + { + po::store(po::parse_command_line(argc, argv, desc), vm); + po::notify(vm); + + if (command_line::get_arg(vm, command_line::arg_help)) + { + std::cout << desc << std::endl; + return false; + } + + return true; + }); + if (!r) + return 1; + + mining::simpleminer miner; + r = miner.init(vm); + r = r && miner.run(); // Never returns... + + return 0; +} + + + +namespace mining +{ + const command_line::arg_descriptor arg_pool_addr = {"pool-addr", ""}; + const command_line::arg_descriptor arg_login = {"login", ""}; + const command_line::arg_descriptor arg_pass = {"pass", ""}; + + //----------------------------------------------------------------------------------------------------- + void simpleminer::init_options(boost::program_options::options_description& desc) + { + command_line::add_arg(desc, arg_pool_addr); + command_line::add_arg(desc, arg_login); + command_line::add_arg(desc, arg_pass); + } + bool simpleminer::init(const boost::program_options::variables_map& vm) + { + std::string pool_addr = command_line::get_arg(vm, arg_pool_addr); + //parse ip and address + std::string::size_type p = pool_addr.find(':'); + CHECK_AND_ASSERT_MES(p != std::string::npos && (p + 1 != pool_addr.size()), false, "Wrong srv address syntax"); + m_pool_ip = pool_addr.substr(0, p); + m_pool_port = pool_addr.substr(p + 1, pool_addr.size()); + m_login = command_line::get_arg(vm, arg_login); + m_pass = command_line::get_arg(vm, arg_pass); + return true; + } + + bool simpleminer::text_job_details_to_native_job_details(const job_details& job, simpleminer::job_details_native& native_details) + { + bool r = epee::string_tools::parse_hexstr_to_binbuff(job.blob, native_details.blob); + CHECK_AND_ASSERT_MES(r, false, "wrong buffer sent from pool server"); + r = epee::string_tools::parse_tpod_from_hex_string(job.target, native_details.target); + CHECK_AND_ASSERT_MES(r, false, "wrong buffer sent from pool server"); + native_details.job_id = job.job_id; + return true; + } + + bool simpleminer::run() + { + std::string pool_session_id; + simpleminer::job_details_native job = AUTO_VAL_INIT(job); + uint64_t last_job_ticks = 0; + + while(true) + { + //----------------- + last_job_ticks = epee::misc_utils::get_tick_count(); + if(!m_http_client.is_connected()) + { + LOG_PRINT_L0("Connecting " << m_pool_ip << ":" << m_pool_port << "...."); + if(!m_http_client.connect(m_pool_ip, m_pool_port, 1000)) + { + LOG_PRINT_L0("Failed to connect " << m_pool_ip << ":" << m_pool_port << ", sleep...."); + epee::misc_utils::sleep_no_w(1000); + continue; + } + //DO AUTH + LOG_PRINT_L0("Connected " << m_pool_ip << ":" << m_pool_port << " OK"); + COMMAND_RPC_LOGIN::request req = AUTO_VAL_INIT(req); + req.login = m_login; + req.pass = m_pass; + COMMAND_RPC_LOGIN::response resp = AUTO_VAL_INIT(resp); + if(!epee::net_utils::invoke_http_json_rpc("/", req, resp, m_http_client)) + { + LOG_PRINT_L0("Failed to invoke login " << m_pool_ip << ":" << m_pool_port << ", disconnect and sleep...."); + m_http_client.disconnect(); + epee::misc_utils::sleep_no_w(1000); + continue; + } + if(resp.status != "OK" || resp.id.empty()) + { + LOG_PRINT_L0("Failed to login " << m_pool_ip << ":" << m_pool_port << ", disconnect and sleep...."); + m_http_client.disconnect(); + epee::misc_utils::sleep_no_w(1000); + continue; + } + pool_session_id = resp.id; + //78 + if(!text_job_details_to_native_job_details(resp.job, job)) + { + LOG_PRINT_L0("Failed to text_job_details_to_native_job_details(), disconnect and sleep...."); + m_http_client.disconnect(); + epee::misc_utils::sleep_no_w(1000); + continue; + } + last_job_ticks = epee::misc_utils::get_tick_count(); + + } + while(epee::misc_utils::get_tick_count() - last_job_ticks < 20000) + { + //uint32_t c = (*((uint32_t*)&job.blob.data()[39])); + ++(*((uint32_t*)&job.blob.data()[39])); + crypto::hash h = cryptonote::null_hash; + crypto::cn_slow_hash(job.blob.data(), job.blob.size(), h); + if( ((uint32_t*)&h)[7] < job.target ) + { + //found! + + COMMAND_RPC_SUBMITSHARE::request submit_request = AUTO_VAL_INIT(submit_request); + COMMAND_RPC_SUBMITSHARE::response submit_response = AUTO_VAL_INIT(submit_response); + submit_request.id = pool_session_id; + submit_request.job_id = job.job_id; + submit_request.nonce = epee::string_tools::pod_to_hex((*((uint32_t*)&job.blob.data()[78]))); + LOG_PRINT_L0("Share found: nonce=" << submit_request.nonce << " for job=" << job.job_id << ", submitting..."); + if(!epee::net_utils::invoke_http_json_rpc("/", submit_request, submit_response, m_http_client)) + { + LOG_PRINT_L0("Failed to submit share! disconnect and sleep...."); + m_http_client.disconnect(); + epee::misc_utils::sleep_no_w(1000); + break; + } + if(submit_response.status != "OK") + { + LOG_PRINT_L0("Failed to submit share! (submitted share rejected) disconnect and sleep...."); + m_http_client.disconnect(); + epee::misc_utils::sleep_no_w(1000); + break; + } + LOG_PRINT_GREEN("Share submitted successfully!", LOG_LEVEL_0); + break; + } + } + //get next job + COMMAND_RPC_GETJOB::request getjob_request = AUTO_VAL_INIT(getjob_request); + COMMAND_RPC_GETJOB::response getjob_response = AUTO_VAL_INIT(getjob_response); + getjob_request.id = pool_session_id; + LOG_PRINT_L0("Getting next job..."); + if(!epee::net_utils::invoke_http_json_rpc("/", getjob_request, getjob_response, m_http_client)) + { + LOG_PRINT_L0("Can't get new job! Disconnect and sleep...."); + m_http_client.disconnect(); + epee::misc_utils::sleep_no_w(1000); + break; + } + if(!text_job_details_to_native_job_details(getjob_response, job)) + { + LOG_PRINT_L0("Failed to text_job_details_to_native_job_details(), disconnect and sleep...."); + m_http_client.disconnect(); + epee::misc_utils::sleep_no_w(1000); + continue; + } + last_job_ticks = epee::misc_utils::get_tick_count(); + } + + return true; + + } +} diff --git a/src/miner/simpleminer.h b/src/miner/simpleminer.h new file mode 100644 index 000000000..803ea12fb --- /dev/null +++ b/src/miner/simpleminer.h @@ -0,0 +1,33 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once +#include "net/http_client.h" +#include "cryptonote_protocol/blobdatatype.h" +#include "simpleminer_protocol_defs.h" +namespace mining +{ + class simpleminer + { + public: + static void init_options(boost::program_options::options_description& desc); + bool init(const boost::program_options::variables_map& vm); + bool run(); + private: + struct job_details_native + { + cryptonote::blobdata blob; + uint32_t target; + std::string job_id; + }; + + static bool text_job_details_to_native_job_details(const job_details& job, job_details_native& native_details); + + std::string m_pool_ip; + std::string m_pool_port; + std::string m_login; + std::string m_pass; + epee::net_utils::http::http_simple_client m_http_client; + }; +} diff --git a/src/miner/simpleminer_protocol_defs.h b/src/miner/simpleminer_protocol_defs.h new file mode 100644 index 000000000..9b70f8cbf --- /dev/null +++ b/src/miner/simpleminer_protocol_defs.h @@ -0,0 +1,104 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once +#include "cryptonote_protocol/cryptonote_protocol_defs.h" +#include "cryptonote_core/cryptonote_basic.h" +#include "crypto/hash.h" +#include "net/rpc_method_name.h" + +namespace mining +{ + //----------------------------------------------- +#define CORE_RPC_STATUS_OK "OK" + + + struct job_details + { + std::string blob; + std::string target; + std::string job_id; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(blob) + KV_SERIALIZE(target) + KV_SERIALIZE(job_id) + END_KV_SERIALIZE_MAP() + }; + + + struct COMMAND_RPC_LOGIN + { + RPC_METHOD_NAME("login"); + + struct request + { + std::string login; + std::string pass; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(login) + KV_SERIALIZE(pass) + END_KV_SERIALIZE_MAP() + }; + + + struct response + { + std::string status; + std::string id; + job_details job; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(status) + KV_SERIALIZE(id) + KV_SERIALIZE(job) + END_KV_SERIALIZE_MAP() + }; + }; + + struct COMMAND_RPC_GETJOB + { + RPC_METHOD_NAME("getjob"); + + struct request + { + std::string id; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(id) + END_KV_SERIALIZE_MAP() + }; + + typedef job_details response; + }; + + struct COMMAND_RPC_SUBMITSHARE + { + RPC_METHOD_NAME("submit"); + + struct request + { + std::string id; + std::string nonce; + std::string job_id; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(id) + KV_SERIALIZE(nonce) + KV_SERIALIZE(job_id) + END_KV_SERIALIZE_MAP() + }; + + struct response + { + std::string status; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(status) + END_KV_SERIALIZE_MAP() + }; + }; +} + diff --git a/src/miner/target_helper.h b/src/miner/target_helper.h new file mode 100644 index 000000000..5ac6eed14 --- /dev/null +++ b/src/miner/target_helper.h @@ -0,0 +1,18 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once +#include "cryptonote_core/difficulty.h" + + +namespace mining +{ + inline uint32_t get_target_for_difficulty(cryptonote::difficulty_type difficulty) + { + if(!difficulty) + return 0xffffffff; + return 0xffffffff/static_cast(difficulty); + } + +} diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h new file mode 100644 index 000000000..32249a6d1 --- /dev/null +++ b/src/p2p/net_node.h @@ -0,0 +1,204 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "warnings.h" +#include "net/levin_server_cp2.h" +#include "p2p_protocol_defs.h" +#include "storages/levin_abstract_invoke2.h" +#include "net_peerlist.h" +#include "p2p_networks.h" +#include "math_helper.h" +#include "net_node_common.h" + +using namespace epee; + +PUSH_WARNINGS +DISABLE_VS_WARNINGS(4355) + +namespace nodetool +{ + template + struct p2p_connection_context_t: base_type //t_payload_net_handler::connection_context //public net_utils::connection_context_base + { + peerid_type peer_id; + }; + + template + class node_server: public levin::levin_commands_handler >, + public i_p2p_endpoint + { + struct by_conn_id{}; + struct by_peer_id{}; + struct by_addr{}; + + typedef p2p_connection_context_t p2p_connection_context; + + typedef COMMAND_HANDSHAKE_T COMMAND_HANDSHAKE; + typedef COMMAND_TIMED_SYNC_T COMMAND_TIMED_SYNC; + + public: + typedef t_payload_net_handler payload_net_handler; + // Some code + node_server(t_payload_net_handler& payload_handler):m_payload_handler(payload_handler), m_allow_local_ip(false), m_hide_my_port(false) + {} + + static void init_options(boost::program_options::options_description& desc); + + bool run(); + bool init(const boost::program_options::variables_map& vm); + bool deinit(); + bool send_stop_signal(); + uint32_t get_this_peer_port(){return m_listenning_port;} + t_payload_net_handler& get_payload_object(); + + template + void serialize(Archive &a, const t_version_type ver) + { + a & m_peerlist; + a & m_config.m_peer_id; + } + // debug functions + bool log_peerlist(); + bool log_connections(); + virtual uint64_t get_connections_count(); + size_t get_outgoing_connections_count(); + peerlist_manager& get_peerlist_manager(){return m_peerlist;} + private: + typedef COMMAND_REQUEST_STAT_INFO_T COMMAND_REQUEST_STAT_INFO; + + CHAIN_LEVIN_INVOKE_MAP2(p2p_connection_context); //move levin_commands_handler interface invoke(...) callbacks into invoke map + CHAIN_LEVIN_NOTIFY_MAP2(p2p_connection_context); //move levin_commands_handler interface notify(...) callbacks into nothing + + BEGIN_INVOKE_MAP2(node_server) + HANDLE_INVOKE_T2(COMMAND_HANDSHAKE, &node_server::handle_handshake) + HANDLE_INVOKE_T2(COMMAND_TIMED_SYNC, &node_server::handle_timed_sync) + HANDLE_INVOKE_T2(COMMAND_PING, &node_server::handle_ping) +#ifdef ALLOW_DEBUG_COMMANDS + HANDLE_INVOKE_T2(COMMAND_REQUEST_STAT_INFO, &node_server::handle_get_stat_info) + HANDLE_INVOKE_T2(COMMAND_REQUEST_NETWORK_STATE, &node_server::handle_get_network_state) + HANDLE_INVOKE_T2(COMMAND_REQUEST_PEER_ID, &node_server::handle_get_peer_id) +#endif + CHAIN_INVOKE_MAP_TO_OBJ_FORCE_CONTEXT(m_payload_handler, typename t_payload_net_handler::connection_context&) + END_INVOKE_MAP2() + + //----------------- commands handlers ---------------------------------------------- + int handle_handshake(int command, typename COMMAND_HANDSHAKE::request& arg, typename COMMAND_HANDSHAKE::response& rsp, p2p_connection_context& context); + int handle_timed_sync(int command, typename COMMAND_TIMED_SYNC::request& arg, typename COMMAND_TIMED_SYNC::response& rsp, p2p_connection_context& context); + int handle_ping(int command, COMMAND_PING::request& arg, COMMAND_PING::response& rsp, p2p_connection_context& context); +#ifdef ALLOW_DEBUG_COMMANDS + int handle_get_stat_info(int command, typename COMMAND_REQUEST_STAT_INFO::request& arg, typename COMMAND_REQUEST_STAT_INFO::response& rsp, p2p_connection_context& context); + int handle_get_network_state(int command, COMMAND_REQUEST_NETWORK_STATE::request& arg, COMMAND_REQUEST_NETWORK_STATE::response& rsp, p2p_connection_context& context); + int handle_get_peer_id(int command, COMMAND_REQUEST_PEER_ID::request& arg, COMMAND_REQUEST_PEER_ID::response& rsp, p2p_connection_context& context); +#endif + bool init_config(); + bool make_default_config(); + bool store_config(); + bool check_trust(const proof_of_trust& tr); + + + //----------------- levin_commands_handler ------------------------------------------------------------- + virtual void on_connection_new(p2p_connection_context& context); + virtual void on_connection_close(p2p_connection_context& context); + virtual void callback(p2p_connection_context& context); + //----------------- i_p2p_endpoint ------------------------------------------------------------- + virtual bool relay_notify_to_all(int command, const std::string& data_buff, const epee::net_utils::connection_context_base& context); + virtual bool invoke_command_to_peer(int command, const std::string& req_buff, std::string& resp_buff, const epee::net_utils::connection_context_base& context); + virtual bool invoke_notify_to_peer(int command, const std::string& req_buff, const epee::net_utils::connection_context_base& context); + virtual bool drop_connection(const epee::net_utils::connection_context_base& context); + virtual void request_callback(const epee::net_utils::connection_context_base& context); + virtual void for_each_connection(std::function f); + //----------------------------------------------------------------------------------------------- + bool parse_peer_from_string(nodetool::net_address& pe, const std::string& node_addr); + bool handle_command_line(const boost::program_options::variables_map& vm); + bool idle_worker(); + bool handle_remote_peerlist(const std::list& peerlist, time_t local_time, const net_utils::connection_context_base& context); + bool get_local_node_data(basic_node_data& node_data); + //bool get_local_handshake_data(handshake_data& hshd); + + bool merge_peerlist_with_local(const std::list& bs); + bool fix_time_delta(std::list& local_peerlist, time_t local_time, int64_t& delta); + + bool connections_maker(); + bool peer_sync_idle_maker(); + bool do_handshake_with_peer(peerid_type& pi, p2p_connection_context& context, bool just_take_peerlist = false); + bool do_peer_timed_sync(const net_utils::connection_context_base& context, peerid_type peer_id); + + bool make_new_connection_from_peerlist(bool use_white_list); + bool try_to_connect_and_handshake_with_new_peer(const net_address& na, bool just_take_peerlist = false, uint64_t last_seen_stamp = 0, bool white = true); + size_t get_random_index_with_fixed_probability(size_t max_index); + bool is_peer_used(const peerlist_entry& peer); + bool is_addr_connected(const net_address& peer); + template + bool try_ping(basic_node_data& node_data, p2p_connection_context& context, t_callback cb); + bool make_expected_connections_count(bool white_list, size_t expected_connections); + + //debug functions + std::string print_connections_container(); + + + typedef net_utils::boosted_tcp_server > net_server; + + struct config + { + network_config m_net_config; + uint64_t m_peer_id; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(m_net_config) + KV_SERIALIZE(m_peer_id) + END_KV_SERIALIZE_MAP() + }; + + config m_config; + std::string m_config_folder; + + bool m_have_address; + bool m_first_connection_maker_call; + uint32_t m_listenning_port; + uint32_t m_external_port; + uint32_t m_ip_address; + bool m_allow_local_ip; + bool m_hide_my_port; + + //critical_section m_connections_lock; + //connections_indexed_container m_connections; + + t_payload_net_handler& m_payload_handler; + peerlist_manager m_peerlist; + + math_helper::once_a_time_seconds m_peer_handshake_idle_maker_interval; + math_helper::once_a_time_seconds<1> m_connections_maker_interval; + math_helper::once_a_time_seconds<60*30, false> m_peerlist_store_interval; + + std::string m_bind_ip; + std::string m_port; +#ifdef ALLOW_DEBUG_COMMANDS + uint64_t m_last_stat_request_time; +#endif + std::list m_priority_peers; + std::vector m_seed_nodes; + std::list m_command_line_peers; + uint64_t m_peer_livetime; + //keep connections to initiate some interactions + net_server m_net_server; + }; +} + +#include "net_node.inl" + +POP_WARNINGS diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl new file mode 100644 index 000000000..a5e534f8a --- /dev/null +++ b/src/p2p/net_node.inl @@ -0,0 +1,1020 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include "version.h" +#include "string_tools.h" +#include "common/command_line.h" +#include "common/util.h" +#include "net/net_helper.h" +#include "math_helper.h" +#include "p2p_protocol_defs.h" +#include "net_peerlist_boost_serialization.h" +#include "net/local_ip.h" +#include "crypto/crypto.h" +#include "storages/levin_abstract_invoke2.h" +#define NET_MAKE_IP(b1,b2,b3,b4) ((LPARAM)(((DWORD)(b1)<<24)+((DWORD)(b2)<<16)+((DWORD)(b3)<<8)+((DWORD)(b4)))) + + +namespace nodetool +{ + namespace + { + const command_line::arg_descriptor arg_p2p_bind_ip = {"p2p-bind-ip", "Interface for p2p network protocol", "0.0.0.0"}; + const command_line::arg_descriptor arg_p2p_bind_port = {"p2p-bind-port", "Port for p2p network protocol", boost::to_string(P2P_DEFAULT_PORT)}; + const command_line::arg_descriptor arg_p2p_external_port = {"p2p-external-port", "External port for p2p network protocol (if port forwarding used with NAT)", 0}; + const command_line::arg_descriptor arg_p2p_allow_local_ip = {"allow-local-ip", "Allow local ip add to peer list, mostly in debug purposes"}; + const command_line::arg_descriptor > arg_p2p_add_peer = {"add-peer", "Manually add peer to local peerlist"}; + const command_line::arg_descriptor > arg_p2p_add_priority_node = {"add-priority-node", "Specify list of peers to connect to and attempt to keep the connection open"}; + const command_line::arg_descriptor > arg_p2p_seed_node = {"seed-node", "Connect to a node to retrieve peer addresses, and disconnect"}; + const command_line::arg_descriptor arg_p2p_hide_my_port = {"hide-my-port", "Do not announce yourself as peerlist candidate", false, true}; } + + //----------------------------------------------------------------------------------- + template + void node_server::init_options(boost::program_options::options_description& desc) + { + command_line::add_arg(desc, arg_p2p_bind_ip); + command_line::add_arg(desc, arg_p2p_bind_port); + command_line::add_arg(desc, arg_p2p_external_port); + command_line::add_arg(desc, arg_p2p_allow_local_ip); + command_line::add_arg(desc, arg_p2p_add_peer); + command_line::add_arg(desc, arg_p2p_add_priority_node); + command_line::add_arg(desc, arg_p2p_seed_node); + command_line::add_arg(desc, arg_p2p_hide_my_port); } + //----------------------------------------------------------------------------------- + template + bool node_server::init_config() + { + // + TRY_ENTRY(); + std::string state_file_path = m_config_folder + "/" + P2P_NET_DATA_FILENAME; + std::ifstream p2p_data; + p2p_data.open( state_file_path , std::ios_base::binary | std::ios_base::in); + if(!p2p_data.fail()) + { + boost::archive::binary_iarchive a(p2p_data); + a >> *this; + }else + { + make_default_config(); + } + + //at this moment we have hardcoded config + m_config.m_net_config.handshake_interval = P2P_DEFAULT_HANDSHAKE_INTERVAL; + m_config.m_net_config.connections_count = P2P_DEFAULT_CONNECTIONS_COUNT; + m_config.m_net_config.packet_max_size = P2P_DEFAULT_PACKET_MAX_SIZE; //20 MB limit + m_config.m_net_config.config_id = 0; // initial config + m_config.m_net_config.connection_timeout = P2P_DEFAULT_CONNECTION_TIMEOUT; + m_config.m_net_config.ping_connection_timeout = P2P_DEFAULT_PING_CONNECTION_TIMEOUT; + m_config.m_net_config.send_peerlist_sz = P2P_DEFAULT_PEERS_IN_HANDSHAKE; + + m_first_connection_maker_call = true; + CATCH_ENTRY_L0("node_server::init_config", false); + return true; + } + //----------------------------------------------------------------------------------- + template + void node_server::for_each_connection(std::function f) + { + m_net_server.get_config_object().foreach_connection([&](p2p_connection_context& cntx){ + return f(cntx); + }); + } + //----------------------------------------------------------------------------------- + template + bool node_server::make_default_config() + { + m_config.m_peer_id = crypto::rand(); + return true; + } + //----------------------------------------------------------------------------------- + template + bool node_server::parse_peer_from_string(nodetool::net_address& pe, const std::string& node_addr) + { + return string_tools::parse_peer_from_string(pe.ip, pe.port, node_addr); + } + //----------------------------------------------------------------------------------- + template + bool node_server::handle_command_line(const boost::program_options::variables_map& vm) + { + m_bind_ip = command_line::get_arg(vm, arg_p2p_bind_ip); + m_port = command_line::get_arg(vm, arg_p2p_bind_port); + m_external_port = command_line::get_arg(vm, arg_p2p_external_port); + m_allow_local_ip = command_line::get_arg(vm, arg_p2p_allow_local_ip); + + if (command_line::has_arg(vm, arg_p2p_add_peer)) + { + std::vector perrs = command_line::get_arg(vm, arg_p2p_add_peer); + for(const std::string& pr_str: perrs) + { + nodetool::peerlist_entry pe = AUTO_VAL_INIT(pe); + pe.id = crypto::rand(); + bool r = parse_peer_from_string(pe.adr, pr_str); + CHECK_AND_ASSERT_MES(r, false, "Failed to parse address from string: " << pr_str); + m_command_line_peers.push_back(pe); + } + } + + if (command_line::has_arg(vm, arg_p2p_add_priority_node)) + { + std::vector perrs = command_line::get_arg(vm, arg_p2p_add_priority_node); + for(const std::string& pr_str: perrs) + { + nodetool::net_address na = AUTO_VAL_INIT(na); + bool r = parse_peer_from_string(na, pr_str); + CHECK_AND_ASSERT_MES(r, false, "Failed to parse address from string: " << pr_str); + m_priority_peers.push_back(na); + } + } + if (command_line::has_arg(vm, arg_p2p_seed_node)) + { + std::vector seed_perrs = command_line::get_arg(vm, arg_p2p_seed_node); + for(const std::string& pr_str: seed_perrs) + { + nodetool::net_address na = AUTO_VAL_INIT(na); + bool r = parse_peer_from_string(na, pr_str); + CHECK_AND_ASSERT_MES(r, false, "Failed to parse seed address from string: " << pr_str); + m_seed_nodes.push_back(na); + } + } + if(command_line::has_arg(vm, arg_p2p_hide_my_port)) + m_hide_my_port = true; return true; + } + //----------------------------------------------------------------------------------- +#define ADD_HARDCODED_SEED_NODE(addr_str) { nodetool::net_address na = AUTO_VAL_INIT(na);bool r = parse_peer_from_string(na, addr_str); \ + CHECK_AND_ASSERT_MES(r, false, "Failed to parse seed address from string: " << addr_str); m_seed_nodes.push_back(na); } + + + template + bool node_server::init(const boost::program_options::variables_map& vm) + { + + ADD_HARDCODED_SEED_NODE("85.25.201.95:8080"); + ADD_HARDCODED_SEED_NODE("85.25.196.145:8080"); + ADD_HARDCODED_SEED_NODE("85.25.196.146:8080"); + ADD_HARDCODED_SEED_NODE("85.25.196.144:8080"); + ADD_HARDCODED_SEED_NODE("5.199.168.138:8080"); + ADD_HARDCODED_SEED_NODE("62.75.236.152:8080"); + ADD_HARDCODED_SEED_NODE("85.25.194.245:8080"); + ADD_HARDCODED_SEED_NODE("95.211.224.160:8080"); + ADD_HARDCODED_SEED_NODE("144.76.200.44:8080"); + + bool res = handle_command_line(vm); + CHECK_AND_ASSERT_MES(res, false, "Failed to handle command line"); + m_config_folder = command_line::get_arg(vm, command_line::arg_data_dir); + + res = init_config(); + CHECK_AND_ASSERT_MES(res, false, "Failed to init config."); + + res = m_peerlist.init(m_allow_local_ip); + CHECK_AND_ASSERT_MES(res, false, "Failed to init peerlist."); + + + for(auto& p: m_command_line_peers) + m_peerlist.append_with_peer_white(p); + + //only in case if we really sure that we have external visible ip + m_have_address = true; + m_ip_address = 0; + m_last_stat_request_time = 0; + + //configure self + m_net_server.set_threads_prefix("P2P"); + m_net_server.get_config_object().m_pcommands_handler = this; + m_net_server.get_config_object().m_invoke_timeout = P2P_DEFAULT_INVOKE_TIMEOUT; + + //try to bind + LOG_PRINT_L0("Binding on " << m_bind_ip << ":" << m_port); + res = m_net_server.init_server(m_port, m_bind_ip); + CHECK_AND_ASSERT_MES(res, false, "Failed to bind server"); + + m_listenning_port = m_net_server.get_binded_port(); + LOG_PRINT_GREEN("Net service binded on " << m_bind_ip << ":" << m_listenning_port, LOG_LEVEL_0); + if(m_external_port) + LOG_PRINT_L0("External port defined as " << m_external_port); + return res; + } + //----------------------------------------------------------------------------------- + template + typename node_server::payload_net_handler& node_server::get_payload_object() + { + return m_payload_handler; + } + //----------------------------------------------------------------------------------- + template + bool node_server::run() + { + //here you can set worker threads count + int thrds_count = 10; + + m_net_server.add_idle_handler(boost::bind(&node_server::idle_worker, this), 1000); + m_net_server.add_idle_handler(boost::bind(&t_payload_net_handler::on_idle, &m_payload_handler), 1000); + + //go to loop + LOG_PRINT("Run net_service loop( " << thrds_count << " threads)...", LOG_LEVEL_0); + if(!m_net_server.run_server(thrds_count)) + { + LOG_ERROR("Failed to run net tcp server!"); + } + + LOG_PRINT("net_service loop stopped.", LOG_LEVEL_0); + return true; + } + + //----------------------------------------------------------------------------------- + template + uint64_t node_server::get_connections_count() + { + return m_net_server.get_config_object().get_connections_count(); + } + //----------------------------------------------------------------------------------- + template + bool node_server::deinit() + { + m_peerlist.deinit(); + m_net_server.deinit_server(); + return store_config(); + } + //----------------------------------------------------------------------------------- + template + bool node_server::store_config() + { + + TRY_ENTRY(); + if (!tools::create_directories_if_necessary(m_config_folder)) + { + LOG_PRINT_L0("Failed to create data directory: " << m_config_folder); + return false; + } + + std::string state_file_path = m_config_folder + "/" + P2P_NET_DATA_FILENAME; + std::ofstream p2p_data; + p2p_data.open( state_file_path , std::ios_base::binary | std::ios_base::out| std::ios::trunc); + if(p2p_data.fail()) + { + LOG_PRINT_L0("Failed to save config to file " << state_file_path); + return false; + }; + + boost::archive::binary_oarchive a(p2p_data); + a << *this; + return true; + CATCH_ENTRY_L0("blockchain_storage::save", false); + + return true; + } + //----------------------------------------------------------------------------------- + template + bool node_server::send_stop_signal() + { + m_net_server.send_stop_signal(); + LOG_PRINT_L0("[node] Stop signal sent"); + return true; + } + //----------------------------------------------------------------------------------- + + + template + bool node_server::do_handshake_with_peer(peerid_type& pi, p2p_connection_context& context_, bool just_take_peerlist) + { + typename COMMAND_HANDSHAKE::request arg; + typename COMMAND_HANDSHAKE::response rsp; + get_local_node_data(arg.node_data); + m_payload_handler.get_payload_sync_data(arg.payload_data); + + simple_event ev; + std::atomic hsh_result(false); + + bool r = net_utils::async_invoke_remote_command2(context_.m_connection_id, COMMAND_HANDSHAKE::ID, arg, m_net_server.get_config_object(), + [this, &pi, &ev, &hsh_result, &just_take_peerlist](int code, const typename COMMAND_HANDSHAKE::response& rsp, p2p_connection_context& context) + { + misc_utils::auto_scope_leave_caller scope_exit_handler = misc_utils::create_scope_leave_handler([&](){ev.rise();}); + + if(code < 0) + { + LOG_PRINT_CC_RED(context, "COMMAND_HANDSHAKE invoke failed. (" << code << ", " << levin::get_err_descr(code) << ")", LOG_LEVEL_1); + return; + } + + if(rsp.node_data.network_id != BYTECOIN_NETWORK) + { + LOG_ERROR_CCONTEXT("COMMAND_HANDSHAKE Failed, wrong network! (" << string_tools::get_str_from_guid_a(rsp.node_data.network_id) << "), closing connection."); + return; + } + + if(!handle_remote_peerlist(rsp.local_peerlist, rsp.node_data.local_time, context)) + { + LOG_ERROR_CCONTEXT("COMMAND_HANDSHAKE: failed to handle_remote_peerlist(...), closing connection."); + return; + } + hsh_result = true; + if(!just_take_peerlist) + { + if(!m_payload_handler.process_payload_sync_data(rsp.payload_data, context, true)) + { + LOG_ERROR_CCONTEXT("COMMAND_HANDSHAKE invoked, but process_payload_sync_data returned false, dropping connection."); + hsh_result = false; + return; + } + + pi = context.peer_id = rsp.node_data.peer_id; + m_peerlist.set_peer_just_seen(rsp.node_data.peer_id, context.m_remote_ip, context.m_remote_port); + + if(rsp.node_data.peer_id == m_config.m_peer_id) + { + LOG_PRINT_CCONTEXT_L2("Connection to self detected, dropping connection"); + hsh_result = false; + return; + } + LOG_PRINT_CCONTEXT_L0(" COMMAND_HANDSHAKE INVOKED OK"); + }else + { + LOG_PRINT_CCONTEXT_L0(" COMMAND_HANDSHAKE(AND CLOSE) INVOKED OK"); + } + }, P2P_DEFAULT_HANDSHAKE_INVOKE_TIMEOUT); + + if(r) + { + ev.wait(); + } + + if(!hsh_result) + { + LOG_PRINT_CC_L0(context_, "COMMAND_HANDSHAKE Failed"); + m_net_server.get_config_object().close(context_.m_connection_id); + } + + return hsh_result; + } + //----------------------------------------------------------------------------------- + template + bool node_server::do_peer_timed_sync(const net_utils::connection_context_base& context_, peerid_type peer_id) + { + typename COMMAND_TIMED_SYNC::request arg = AUTO_VAL_INIT(arg); + m_payload_handler.get_payload_sync_data(arg.payload_data); + + bool r = net_utils::async_invoke_remote_command2(context_.m_connection_id, COMMAND_TIMED_SYNC::ID, arg, m_net_server.get_config_object(), + [this](int code, const typename COMMAND_TIMED_SYNC::response& rsp, p2p_connection_context& context) + { + if(code < 0) + { + LOG_PRINT_CC_RED(context, "COMMAND_TIMED_SYNC invoke failed. (" << code << ", " << levin::get_err_descr(code) << ")", LOG_LEVEL_1); + return; + } + + if(!handle_remote_peerlist(rsp.local_peerlist, rsp.local_time, context)) + { + LOG_ERROR_CCONTEXT("COMMAND_TIMED_SYNC: failed to handle_remote_peerlist(...), closing connection."); + m_net_server.get_config_object().close(context.m_connection_id ); + } + if(!context.m_is_income) + m_peerlist.set_peer_just_seen(context.peer_id, context.m_remote_ip, context.m_remote_port); + m_payload_handler.process_payload_sync_data(rsp.payload_data, context, false); + }); + + if(!r) + { + LOG_PRINT_CC_L2(context_, "COMMAND_TIMED_SYNC Failed"); + return false; + } + return true; + } + //----------------------------------------------------------------------------------- + template + size_t node_server::get_random_index_with_fixed_probability(size_t max_index) + { + //divide by zero workaround + if(!max_index) + return 0; + + size_t x = crypto::rand()%(max_index+1); + size_t res = (x*x*x)/(max_index*max_index); //parabola \/ + LOG_PRINT_L3("Random connection index=" << res << "(x="<< x << ", max_index=" << max_index << ")"); + return res; + } + //----------------------------------------------------------------------------------- + template + bool node_server::is_peer_used(const peerlist_entry& peer) + { + + if(m_config.m_peer_id == peer.id) + return true;//dont make connections to ourself + + bool used = false; + m_net_server.get_config_object().foreach_connection([&](const p2p_connection_context& cntxt) + { + if(cntxt.peer_id == peer.id || (!cntxt.m_is_income && peer.adr.ip == cntxt.m_remote_ip && peer.adr.port == cntxt.m_remote_port)) + { + used = true; + return false;//stop enumerating + } + return true; + }); + + return used; + } + //----------------------------------------------------------------------------------- + template + bool node_server::is_addr_connected(const net_address& peer) + { + bool connected = false; + m_net_server.get_config_object().foreach_connection([&](const p2p_connection_context& cntxt) + { + if(!cntxt.m_is_income && peer.ip == cntxt.m_remote_ip && peer.port == cntxt.m_remote_port) + { + connected = true; + return false;//stop enumerating + } + return true; + }); + + return connected; + } + + //----------------------------------------------------------------------------------- + template + bool node_server::try_to_connect_and_handshake_with_new_peer(const net_address& na, bool just_take_peerlist, uint64_t last_seen_stamp, bool white) + { + LOG_PRINT_L0("Connecting to " << string_tools::get_ip_string_from_int32(na.ip) << ":" << string_tools::num_to_string_fast(na.port) << "(white=" << white << ", last_seen: " << (last_seen_stamp?misc_utils::get_time_interval_string(time(NULL) - last_seen_stamp):"never" ) << ")..."); + + typename net_server::t_connection_context con = AUTO_VAL_INIT(con); + bool res = m_net_server.connect(string_tools::get_ip_string_from_int32(na.ip), + string_tools::num_to_string_fast(na.port), + m_config.m_net_config.connection_timeout, + con); + if(!res) + { + LOG_PRINT_L0("Connect failed to " + << string_tools::get_ip_string_from_int32(na.ip) + << ":" << string_tools::num_to_string_fast(na.port) + /*<< ", try " << try_count*/); + //m_peerlist.set_peer_unreachable(pe); + return false; + } + peerid_type pi = AUTO_VAL_INIT(pi); + res = do_handshake_with_peer(pi, con, just_take_peerlist); + if(!res) + { + LOG_PRINT_CC_L0(con, "Failed to HANDSHAKE with peer " + << string_tools::get_ip_string_from_int32(na.ip) + << ":" << string_tools::num_to_string_fast(na.port) + /*<< ", try " << try_count*/); + return false; + } + if(just_take_peerlist) + { + m_net_server.get_config_object().close(con.m_connection_id); + LOG_PRINT_CC_GREEN(con, "CONNECTION HANDSHAKED OK AND CLOSED.", LOG_LEVEL_2); + return true; + } + + peerlist_entry pe_local = AUTO_VAL_INIT(pe_local); + pe_local.adr = na; + pe_local.id = pi; + time(&pe_local.last_seen); + m_peerlist.append_with_peer_white(pe_local); + //update last seen and push it to peerlist manager + + LOG_PRINT_CC_GREEN(con, "CONNECTION HANDSHAKED OK.", LOG_LEVEL_2); + return true; + } + //----------------------------------------------------------------------------------- + template + bool node_server::make_new_connection_from_peerlist(bool use_white_list) + { + size_t local_peers_count = use_white_list ? m_peerlist.get_white_peers_count():m_peerlist.get_gray_peers_count(); + if(!local_peers_count) + return false;//no peers + + size_t max_random_index = std::min(local_peers_count -1, 20); + + std::set tried_peers; + + size_t try_count = 0; + size_t rand_count = 0; + while(rand_count < (max_random_index+1)*3 && try_count < 10 && !m_net_server.is_stop_signal_sent()) + { + ++rand_count; + size_t random_index = get_random_index_with_fixed_probability(max_random_index); + CHECK_AND_ASSERT_MES(random_index < local_peers_count, false, "random_starter_index < peers_local.size() failed!!"); + + if(tried_peers.count(random_index)) + continue; + + tried_peers.insert(random_index); + peerlist_entry pe = AUTO_VAL_INIT(pe); + bool r = use_white_list ? m_peerlist.get_white_peer_by_index(pe, random_index):m_peerlist.get_gray_peer_by_index(pe, random_index); + CHECK_AND_ASSERT_MES(r, false, "Failed to get random peer from peerlist(white:" << use_white_list << ")"); + + ++try_count; + + if(is_peer_used(pe)) + continue; + + LOG_PRINT_L1("Selected peer: " << pe.id << " " << string_tools::get_ip_string_from_int32(pe.adr.ip) << ":" << boost::lexical_cast(pe.adr.port) << "[white=" << use_white_list << "] last_seen: " << (pe.last_seen ? misc_utils::get_time_interval_string(time(NULL) - pe.last_seen) : "never")); + + if(!try_to_connect_and_handshake_with_new_peer(pe.adr, false, pe.last_seen, use_white_list)) + continue; + + return true; + } + return false; + } + //----------------------------------------------------------------------------------- + template + bool node_server::connections_maker() + { + if(!m_peerlist.get_white_peers_count() && m_seed_nodes.size()) + { + size_t try_count = 0; + size_t current_index = crypto::rand()%m_seed_nodes.size(); + while(true) + { + if(m_net_server.is_stop_signal_sent()) + return false; + + if(try_to_connect_and_handshake_with_new_peer(m_seed_nodes[current_index], true)) + break; + if(++try_count > m_seed_nodes.size()) + { + LOG_PRINT_RED_L0("Failed to connect to any of seed peers, continuing without seeds"); + break; + } + if(++current_index > m_seed_nodes.size()) + current_index = 0; + } + } + + for(const net_address& na: m_priority_peers) + { + if(m_net_server.is_stop_signal_sent()) + return false; + + if(is_addr_connected(na)) + continue; + try_to_connect_and_handshake_with_new_peer(na); + } + + size_t expected_white_connections = (m_config.m_net_config.connections_count*P2P_DEFAULT_WHITELIST_CONNECTIONS_PERCENT)/100; + + size_t conn_count = get_outgoing_connections_count(); + if(conn_count < m_config.m_net_config.connections_count) + { + if(conn_count < expected_white_connections) + { + //start from white list + if(!make_expected_connections_count(true, expected_white_connections)) + return false; + //and then do grey list + if(!make_expected_connections_count(false, m_config.m_net_config.connections_count)) + return false; + }else + { + //start from grey list + if(!make_expected_connections_count(false, m_config.m_net_config.connections_count)) + return false; + //and then do white list + if(!make_expected_connections_count(true, m_config.m_net_config.connections_count)) + return false; + } + } + + return true; + } + //----------------------------------------------------------------------------------- + template + bool node_server::make_expected_connections_count(bool white_list, size_t expected_connections) + { + size_t conn_count = get_outgoing_connections_count(); + //add new connections from white peers + while(conn_count < expected_connections) + { + if(m_net_server.is_stop_signal_sent()) + return false; + + if(!make_new_connection_from_peerlist(white_list)) + break; + conn_count = get_outgoing_connections_count(); + } + return true; + } + + //----------------------------------------------------------------------------------- + template + size_t node_server::get_outgoing_connections_count() + { + size_t count = 0; + m_net_server.get_config_object().foreach_connection([&](const p2p_connection_context& cntxt) + { + if(!cntxt.m_is_income) + ++count; + return true; + }); + + return count; + } + //----------------------------------------------------------------------------------- + template + bool node_server::idle_worker() + { + m_peer_handshake_idle_maker_interval.do_call(boost::bind(&node_server::peer_sync_idle_maker, this)); + m_connections_maker_interval.do_call(boost::bind(&node_server::connections_maker, this)); + m_peerlist_store_interval.do_call(boost::bind(&node_server::store_config, this)); + return true; + } + //----------------------------------------------------------------------------------- + template + bool node_server::peer_sync_idle_maker() + { + LOG_PRINT_L2("STARTED PEERLIST IDLE HANDSHAKE"); + typedef std::list > local_connects_type; + local_connects_type cncts; + m_net_server.get_config_object().foreach_connection([&](const p2p_connection_context& cntxt) + { + if(cntxt.peer_id) + cncts.push_back(local_connects_type::value_type(cntxt, cntxt.peer_id));//do idle sync only with handshaked connections + return true; + }); + + std::for_each(cncts.begin(), cncts.end(), [&](const typename local_connects_type::value_type& vl){do_peer_timed_sync(vl.first, vl.second);}); + + LOG_PRINT_L2("FINISHED PEERLIST IDLE HANDSHAKE"); + return true; + } + //----------------------------------------------------------------------------------- + template + bool node_server::fix_time_delta(std::list& local_peerlist, time_t local_time, int64_t& delta) + { + //fix time delta + time_t now = 0; + time(&now); + delta = now - local_time; + + BOOST_FOREACH(peerlist_entry& be, local_peerlist) + { + if(be.last_seen > local_time) + { + LOG_PRINT_RED_L0("FOUND FUTURE peerlist for entry " << string_tools::get_ip_string_from_int32(be.adr.ip) << ":" << be.adr.port << " last_seen: " << be.last_seen << ", local_time(on remote node):" << local_time); + return false; + } + be.last_seen += delta; + } + return true; + } + //----------------------------------------------------------------------------------- + template + bool node_server::handle_remote_peerlist(const std::list& peerlist, time_t local_time, const net_utils::connection_context_base& context) + { + int64_t delta = 0; + std::list peerlist_ = peerlist; + if(!fix_time_delta(peerlist_, local_time, delta)) + return false; + LOG_PRINT_CCONTEXT_L2("REMOTE PEERLIST: TIME_DELTA: " << delta << ", remote peerlist size=" << peerlist_.size()); + LOG_PRINT_CCONTEXT_L3("REMOTE PEERLIST: " << print_peerlist_to_string(peerlist_)); + return m_peerlist.merge_peerlist(peerlist_); + } + //----------------------------------------------------------------------------------- + template + bool node_server::get_local_node_data(basic_node_data& node_data) + { + time(&node_data.local_time); + node_data.peer_id = m_config.m_peer_id; + if(!m_hide_my_port) + node_data.my_port = m_external_port ? m_external_port : m_listenning_port; + else + node_data.my_port = 0; + node_data.network_id = BYTECOIN_NETWORK; + return true; + } + //----------------------------------------------------------------------------------- +#ifdef ALLOW_DEBUG_COMMANDS + template + bool node_server::check_trust(const proof_of_trust& tr) + { + uint64_t local_time = time(NULL); + uint64_t time_delata = local_time > tr.time ? local_time - tr.time: tr.time - local_time; + if(time_delata > 24*60*60 ) + { + LOG_ERROR("check_trust failed to check time conditions, local_time=" << local_time << ", proof_time=" << tr.time); + return false; + } + if(m_last_stat_request_time >= tr.time ) + { + LOG_ERROR("check_trust failed to check time conditions, last_stat_request_time=" << m_last_stat_request_time << ", proof_time=" << tr.time); + return false; + } + if(m_config.m_peer_id != tr.peer_id) + { + LOG_ERROR("check_trust failed: peer_id mismatch (passed " << tr.peer_id << ", expected " << m_config.m_peer_id<< ")"); + return false; + } + crypto::public_key pk = AUTO_VAL_INIT(pk); + string_tools::hex_to_pod(P2P_STAT_TRUSTED_PUB_KEY, pk); + crypto::hash h = tools::get_proof_of_trust_hash(tr); + if(!crypto::check_signature(h, pk, tr.sign)) + { + LOG_ERROR("check_trust failed: sign check failed"); + return false; + } + //update last request time + m_last_stat_request_time = tr.time; + return true; + } + //----------------------------------------------------------------------------------- + template + int node_server::handle_get_stat_info(int command, typename COMMAND_REQUEST_STAT_INFO::request& arg, typename COMMAND_REQUEST_STAT_INFO::response& rsp, p2p_connection_context& context) + { + if(!check_trust(arg.tr)) + { + drop_connection(context); + return 1; + } + rsp.connections_count = m_net_server.get_config_object().get_connections_count(); + rsp.incoming_connections_count = rsp.connections_count - get_outgoing_connections_count(); + rsp.version = PROJECT_VERSION_LONG; + rsp.os_version = tools::get_os_version_string(); + m_payload_handler.get_stat_info(rsp.payload_info); + return 1; + } + //----------------------------------------------------------------------------------- + template + int node_server::handle_get_network_state(int command, COMMAND_REQUEST_NETWORK_STATE::request& arg, COMMAND_REQUEST_NETWORK_STATE::response& rsp, p2p_connection_context& context) + { + if(!check_trust(arg.tr)) + { + drop_connection(context); + return 1; + } + m_net_server.get_config_object().foreach_connection([&](const p2p_connection_context& cntxt) + { + connection_entry ce; + ce.adr.ip = cntxt.m_remote_ip; + ce.adr.port = cntxt.m_remote_port; + ce.id = cntxt.peer_id; + ce.is_income = cntxt.m_is_income; + rsp.connections_list.push_back(ce); + return true; + }); + + m_peerlist.get_peerlist_full(rsp.local_peerlist_gray, rsp.local_peerlist_white); + rsp.my_id = m_config.m_peer_id; + rsp.local_time = time(NULL); + return 1; + } + //----------------------------------------------------------------------------------- + template + int node_server::handle_get_peer_id(int command, COMMAND_REQUEST_PEER_ID::request& arg, COMMAND_REQUEST_PEER_ID::response& rsp, p2p_connection_context& context) + { + rsp.my_id = m_config.m_peer_id; + return 1; + } +#endif + //----------------------------------------------------------------------------------- + template + void node_server::request_callback(const epee::net_utils::connection_context_base& context) + { + m_net_server.get_config_object().request_callback(context.m_connection_id); + } + //----------------------------------------------------------------------------------- + template + bool node_server::relay_notify_to_all(int command, const std::string& data_buff, const epee::net_utils::connection_context_base& context) + { + std::list connections; + m_net_server.get_config_object().foreach_connection([&](const p2p_connection_context& cntxt) + { + if(cntxt.peer_id && context.m_connection_id != cntxt.m_connection_id) + connections.push_back(cntxt.m_connection_id); + return true; + }); + + BOOST_FOREACH(const auto& c_id, connections) + { + m_net_server.get_config_object().notify(command, data_buff, c_id); + } + return true; + } + //----------------------------------------------------------------------------------- + template + void node_server::callback(p2p_connection_context& context) + { + m_payload_handler.on_callback(context); + } + //----------------------------------------------------------------------------------- + template + bool node_server::invoke_notify_to_peer(int command, const std::string& req_buff, const epee::net_utils::connection_context_base& context) + { + int res = m_net_server.get_config_object().notify(command, req_buff, context.m_connection_id); + return res > 0; + } + //----------------------------------------------------------------------------------- + template + bool node_server::invoke_command_to_peer(int command, const std::string& req_buff, std::string& resp_buff, const epee::net_utils::connection_context_base& context) + { + int res = m_net_server.get_config_object().invoke(command, req_buff, resp_buff, context.m_connection_id); + return res > 0; + } + //----------------------------------------------------------------------------------- + template + bool node_server::drop_connection(const epee::net_utils::connection_context_base& context) + { + m_net_server.get_config_object().close(context.m_connection_id); + return true; + } + //----------------------------------------------------------------------------------- + template template + bool node_server::try_ping(basic_node_data& node_data, p2p_connection_context& context, t_callback cb) + { + if(!node_data.my_port) + return false; + + uint32_t actual_ip = context.m_remote_ip; + if(!m_peerlist.is_ip_allowed(actual_ip)) + return false; + std::string ip = string_tools::get_ip_string_from_int32(actual_ip); + std::string port = string_tools::num_to_string_fast(node_data.my_port); + peerid_type pr = node_data.peer_id; + bool r = m_net_server.connect_async(ip, port, m_config.m_net_config.ping_connection_timeout, [cb, /*context,*/ ip, port, pr, this]( + const typename net_server::t_connection_context& ping_context, + const boost::system::error_code& ec)->bool + { + if(ec) + { + LOG_PRINT_CC_L2(ping_context, "back ping connect failed to " << ip << ":" << port); + return false; + } + COMMAND_PING::request req; + COMMAND_PING::response rsp; + //vc2010 workaround + /*std::string ip_ = ip; + std::string port_=port; + peerid_type pr_ = pr; + auto cb_ = cb;*/ + bool inv_call_res = net_utils::async_invoke_remote_command2(ping_context.m_connection_id, COMMAND_PING::ID, req, m_net_server.get_config_object(), + [=](int code, const COMMAND_PING::response& rsp, p2p_connection_context& context) + { + if(code <= 0) + { + LOG_PRINT_CC_L2(ping_context, "Failed to invoke COMMAND_PING to " << ip << ":" << port << "(" << code << ", " << levin::get_err_descr(code) << ")"); + return; + } + + if(rsp.status != PING_OK_RESPONSE_STATUS_TEXT || pr != rsp.peer_id) + { + LOG_PRINT_CC_L2(ping_context, "back ping invoke wrong response \"" << rsp.status << "\" from" << ip << ":" << port << ", hsh_peer_id=" << pr << ", rsp.peer_id=" << rsp.peer_id); + return; + } + m_net_server.get_config_object().close(ping_context.m_connection_id); + cb(); + }); + + if(!inv_call_res) + { + LOG_PRINT_CC_L2(ping_context, "back ping invoke failed to " << ip << ":" << port); + m_net_server.get_config_object().close(ping_context.m_connection_id); + return false; + } + return true; + }); + if(!r) + { + LOG_ERROR("Failed to call connect_async, network error."); + } + return r; + } + //----------------------------------------------------------------------------------- + template + int node_server::handle_timed_sync(int command, typename COMMAND_TIMED_SYNC::request& arg, typename COMMAND_TIMED_SYNC::response& rsp, p2p_connection_context& context) + { + if(!m_payload_handler.process_payload_sync_data(arg.payload_data, context, false)) + { + LOG_ERROR_CCONTEXT("Failed to process_payload_sync_data(), dropping connection"); + drop_connection(context); + return 1; + } + + //fill response + rsp.local_time = time(NULL); + m_peerlist.get_peerlist_head(rsp.local_peerlist); + m_payload_handler.get_payload_sync_data(rsp.payload_data); + LOG_PRINT_CCONTEXT_L2("COMMAND_TIMED_SYNC"); + return 1; + } + //----------------------------------------------------------------------------------- + template + int node_server::handle_handshake(int command, typename COMMAND_HANDSHAKE::request& arg, typename COMMAND_HANDSHAKE::response& rsp, p2p_connection_context& context) + { + if(arg.node_data.network_id != BYTECOIN_NETWORK) + { + + LOG_PRINT_CCONTEXT_L0("WRONG NETWORK AGENT CONNECTED! id=" << string_tools::get_str_from_guid_a(arg.node_data.network_id)); + drop_connection(context); + return 1; + } + + if(!context.m_is_income) + { + LOG_ERROR_CCONTEXT("COMMAND_HANDSHAKE came not from incoming connection"); + drop_connection(context); + return 1; + } + + if(context.peer_id) + { + LOG_ERROR_CCONTEXT("COMMAND_HANDSHAKE came, but seems that connection already have associated peer_id (double COMMAND_HANDSHAKE?)"); + drop_connection(context); + return 1; + } + + if(!m_payload_handler.process_payload_sync_data(arg.payload_data, context, true)) + { + LOG_ERROR_CCONTEXT("COMMAND_HANDSHAKE came, but process_payload_sync_data returned false, dropping connection."); + drop_connection(context); + return 1; + } + //associate peer_id with this connection + context.peer_id = arg.node_data.peer_id; + + if(arg.node_data.peer_id != m_config.m_peer_id && arg.node_data.my_port) + { + peerid_type peer_id_l = arg.node_data.peer_id; + boost::uint32_t port_l = arg.node_data.my_port; + //try ping to be sure that we can add this peer to peer_list + try_ping(arg.node_data, context, [peer_id_l, port_l, context, this]() + { + //called only(!) if success pinged, update local peerlist + peerlist_entry pe; + pe.adr.ip = context.m_remote_ip; + pe.adr.port = port_l; + time(&pe.last_seen); + pe.id = peer_id_l; + this->m_peerlist.append_with_peer_white(pe); + LOG_PRINT_CCONTEXT_L2("PING SUCCESS " << string_tools::get_ip_string_from_int32(context.m_remote_ip) << ":" << port_l); + }); + } + + //fill response + m_peerlist.get_peerlist_head(rsp.local_peerlist); + get_local_node_data(rsp.node_data); + m_payload_handler.get_payload_sync_data(rsp.payload_data); + LOG_PRINT_CCONTEXT_GREEN("COMMAND_HANDSHAKE", LOG_LEVEL_1); + return 1; + } + //----------------------------------------------------------------------------------- + template + int node_server::handle_ping(int command, COMMAND_PING::request& arg, COMMAND_PING::response& rsp, p2p_connection_context& context) + { + LOG_PRINT_CCONTEXT_L2("COMMAND_PING"); + rsp.status = PING_OK_RESPONSE_STATUS_TEXT; + rsp.peer_id = m_config.m_peer_id; + return 1; + } + //----------------------------------------------------------------------------------- + template + bool node_server::log_peerlist() + { + std::list pl_wite; + std::list pl_gray; + m_peerlist.get_peerlist_full(pl_gray, pl_wite); + LOG_PRINT_L0(ENDL << "Peerlist white:" << ENDL << print_peerlist_to_string(pl_wite) << ENDL << "Peerlist gray:" << ENDL << print_peerlist_to_string(pl_gray) ); + return true; + } + //----------------------------------------------------------------------------------- + template + bool node_server::log_connections() + { + LOG_PRINT_L0("Connections: \r\n" << print_connections_container() ); + return true; + } + //----------------------------------------------------------------------------------- + template + std::string node_server::print_connections_container() + { + + std::stringstream ss; + m_net_server.get_config_object().foreach_connection([&](const p2p_connection_context& cntxt) + { + ss << string_tools::get_ip_string_from_int32(cntxt.m_remote_ip) << ":" << cntxt.m_remote_port + << " \t\tpeer_id " << cntxt.peer_id + << " \t\tconn_id " << string_tools::get_str_from_guid_a(cntxt.m_connection_id) << (cntxt.m_is_income ? " INC":" OUT") + << std::endl; + return true; + }); + std::string s = ss.str(); + return s; + } + //----------------------------------------------------------------------------------- + template + void node_server::on_connection_new(p2p_connection_context& context) + { + LOG_PRINT_L2("["<< net_utils::print_connection_context(context) << "] NEW CONNECTION"); + } + //----------------------------------------------------------------------------------- + template + void node_server::on_connection_close(p2p_connection_context& context) + { + LOG_PRINT_L2("["<< net_utils::print_connection_context(context) << "] CLOSE CONNECTION"); + } + //----------------------------------------------------------------------------------- +} diff --git a/src/p2p/net_node_common.h b/src/p2p/net_node_common.h new file mode 100644 index 000000000..db8b63095 --- /dev/null +++ b/src/p2p/net_node_common.h @@ -0,0 +1,62 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include +#include "net/net_utils_base.h" + + +namespace nodetool +{ + + typedef boost::uuids::uuid uuid; + typedef boost::uuids::uuid net_connection_id; + + template + struct i_p2p_endpoint + { + virtual bool relay_notify_to_all(int command, const std::string& data_buff, const epee::net_utils::connection_context_base& context)=0; + virtual bool invoke_command_to_peer(int command, const std::string& req_buff, std::string& resp_buff, const epee::net_utils::connection_context_base& context)=0; + virtual bool invoke_notify_to_peer(int command, const std::string& req_buff, const epee::net_utils::connection_context_base& context)=0; + virtual bool drop_connection(const epee::net_utils::connection_context_base& context)=0; + virtual void request_callback(const epee::net_utils::connection_context_base& context)=0; + virtual uint64_t get_connections_count()=0; + virtual void for_each_connection(std::function f)=0; + }; + + template + struct p2p_endpoint_stub: public i_p2p_endpoint + { + virtual bool relay_notify_to_all(int command, const std::string& data_buff, const epee::net_utils::connection_context_base& context) + { + return false; + } + virtual bool invoke_command_to_peer(int command, const std::string& req_buff, std::string& resp_buff, const epee::net_utils::connection_context_base& context) + { + return false; + } + virtual bool invoke_notify_to_peer(int command, const std::string& req_buff, const epee::net_utils::connection_context_base& context) + { + return true; + } + virtual bool drop_connection(const epee::net_utils::connection_context_base& context) + { + return false; + } + virtual void request_callback(const epee::net_utils::connection_context_base& context) + { + + } + virtual void for_each_connection(std::function f) + { + + } + + virtual uint64_t get_connections_count() + { + return false; + } + }; +} diff --git a/src/p2p/net_peerlist.h b/src/p2p/net_peerlist.h new file mode 100644 index 000000000..65dfd011a --- /dev/null +++ b/src/p2p/net_peerlist.h @@ -0,0 +1,375 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include +#include +#include +#include +//#include +//#include +#include +#include +#include + +#include +#include +#include +#include + + +#include "syncobj.h" +#include "net/local_ip.h" +#include "p2p_protocol_defs.h" +#include "cryptonote_config.h" +#include "net_peerlist_boost_serialization.h" + + + +namespace nodetool +{ + + + /************************************************************************/ + /* */ + /************************************************************************/ + class peerlist_manager + { + public: + bool init(bool allow_local_ip); + bool deinit(); + size_t get_white_peers_count(){CRITICAL_REGION_LOCAL(m_peerlist_lock); return m_peers_white.size();} + size_t get_gray_peers_count(){CRITICAL_REGION_LOCAL(m_peerlist_lock); return m_peers_gray.size();} + bool merge_peerlist(const std::list& outer_bs); + bool get_peerlist_head(std::list& bs_head, uint32_t depth = P2P_DEFAULT_PEERS_IN_HANDSHAKE); + bool get_peerlist_full(std::list& pl_gray, std::list& pl_white); + bool get_white_peer_by_index(peerlist_entry& p, size_t i); + bool get_gray_peer_by_index(peerlist_entry& p, size_t i); + bool append_with_peer_white(const peerlist_entry& pr); + bool append_with_peer_gray(const peerlist_entry& pr); + bool set_peer_just_seen(peerid_type peer, boost::uint32_t ip, boost::uint32_t port); + bool set_peer_just_seen(peerid_type peer, const net_address& addr); + bool set_peer_unreachable(const peerlist_entry& pr); + bool is_ip_allowed(uint32_t ip); + void trim_white_peerlist(); + void trim_gray_peerlist(); + + + private: + struct by_time{}; + struct by_id{}; + struct by_addr{}; + + struct modify_all_but_id + { + modify_all_but_id(const peerlist_entry& ple):m_ple(ple){} + void operator()(peerlist_entry& e) + { + e.id = m_ple.id; + } + private: + const peerlist_entry& m_ple; + }; + + struct modify_all + { + modify_all(const peerlist_entry& ple):m_ple(ple){} + void operator()(peerlist_entry& e) + { + e = m_ple; + } + private: + const peerlist_entry& m_ple; + }; + + struct modify_last_seen + { + modify_last_seen(time_t last_seen):m_last_seen(last_seen){} + void operator()(peerlist_entry& e) + { + e.last_seen = m_last_seen; + } + private: + time_t m_last_seen; + }; + + + typedef boost::multi_index_container< + peerlist_entry, + boost::multi_index::indexed_by< + // access by peerlist_entry::net_adress + boost::multi_index::ordered_unique, boost::multi_index::member >, + // sort by peerlist_entry::last_seen< + boost::multi_index::ordered_non_unique, boost::multi_index::member > + > + > peers_indexed; + + typedef boost::multi_index_container< + peerlist_entry, + boost::multi_index::indexed_by< + // access by peerlist_entry::id< + boost::multi_index::ordered_unique, boost::multi_index::member >, + // access by peerlist_entry::net_adress + boost::multi_index::ordered_unique, boost::multi_index::member >, + // sort by peerlist_entry::last_seen< + boost::multi_index::ordered_non_unique, boost::multi_index::member > + > + > peers_indexed_old; + public: + + template + void serialize(Archive &a, const t_version_type ver) + { + if(ver < 3) + return; + CRITICAL_REGION_LOCAL(m_peerlist_lock); + if(ver < 4) + { + //loading data from old storage + peers_indexed_old pio; + a & pio; + peers_indexed_from_old(pio, m_peers_white); + return; + } + a & m_peers_white; + a & m_peers_gray; + } + + private: + bool peers_indexed_from_old(const peers_indexed_old& pio, peers_indexed& pi); + + friend class boost::serialization::access; + epee::critical_section m_peerlist_lock; + std::string m_config_folder; + bool m_allow_local_ip; + + + peers_indexed m_peers_gray; + peers_indexed m_peers_white; + }; + //-------------------------------------------------------------------------------------------------- + inline + bool peerlist_manager::init(bool allow_local_ip) + { + m_allow_local_ip = allow_local_ip; + return true; + } + //-------------------------------------------------------------------------------------------------- + inline + bool peerlist_manager::deinit() + { + return true; + } + //-------------------------------------------------------------------------------------------------- + inline + bool peerlist_manager::peers_indexed_from_old(const peers_indexed_old& pio, peers_indexed& pi) + { + for(auto x: pio) + { + auto by_addr_it = pi.get().find(x.adr); + if(by_addr_it == pi.get().end()) + { + pi.insert(x); + } + } + + return true; + } + //-------------------------------------------------------------------------------------------------- + inline void peerlist_manager::trim_white_peerlist() + { + while(m_peers_gray.size() > P2P_LOCAL_GRAY_PEERLIST_LIMIT) + { + peers_indexed::index::type& sorted_index=m_peers_gray.get(); + sorted_index.erase(sorted_index.begin()); + } + } + //-------------------------------------------------------------------------------------------------- + inline void peerlist_manager::trim_gray_peerlist() + { + while(m_peers_white.size() > P2P_LOCAL_WHITE_PEERLIST_LIMIT) + { + peers_indexed::index::type& sorted_index=m_peers_white.get(); + sorted_index.erase(sorted_index.begin()); + } + } + //-------------------------------------------------------------------------------------------------- + inline + bool peerlist_manager::merge_peerlist(const std::list& outer_bs) + { + CRITICAL_REGION_LOCAL(m_peerlist_lock); + BOOST_FOREACH(const peerlist_entry& be, outer_bs) + { + append_with_peer_gray(be); + } + // delete extra elements + trim_gray_peerlist(); + return true; + } + //-------------------------------------------------------------------------------------------------- + inline + bool peerlist_manager::get_white_peer_by_index(peerlist_entry& p, size_t i) + { + CRITICAL_REGION_LOCAL(m_peerlist_lock); + if(i >= m_peers_white.size()) + return false; + + peers_indexed::index::type& by_time_index = m_peers_white.get(); + p = *epee::misc_utils::move_it_backward(--by_time_index.end(), i); + return true; + } + //-------------------------------------------------------------------------------------------------- + inline + bool peerlist_manager::get_gray_peer_by_index(peerlist_entry& p, size_t i) + { + CRITICAL_REGION_LOCAL(m_peerlist_lock); + if(i >= m_peers_gray.size()) + return false; + + peers_indexed::index::type& by_time_index = m_peers_gray.get(); + p = *epee::misc_utils::move_it_backward(--by_time_index.end(), i); + return true; + } + //-------------------------------------------------------------------------------------------------- + inline + bool peerlist_manager::is_ip_allowed(uint32_t ip) + { + //never allow loopback ip + if(epee::net_utils::is_ip_loopback(ip)) + return false; + + if(!m_allow_local_ip && epee::net_utils::is_ip_local(ip)) + return false; + + return true; + } + //-------------------------------------------------------------------------------------------------- + inline + bool peerlist_manager::get_peerlist_head(std::list& bs_head, uint32_t depth) + { + + CRITICAL_REGION_LOCAL(m_peerlist_lock); + peers_indexed::index::type& by_time_index=m_peers_white.get(); + uint32_t cnt = 0; + BOOST_REVERSE_FOREACH(const peers_indexed::value_type& vl, by_time_index) + { + if(!vl.last_seen) + continue; + bs_head.push_back(vl); + if(cnt++ > depth) + break; + } + return true; + } + //-------------------------------------------------------------------------------------------------- + inline + bool peerlist_manager::get_peerlist_full(std::list& pl_gray, std::list& pl_white) + { + CRITICAL_REGION_LOCAL(m_peerlist_lock); + peers_indexed::index::type& by_time_index_gr=m_peers_gray.get(); + BOOST_REVERSE_FOREACH(const peers_indexed::value_type& vl, by_time_index_gr) + { + pl_gray.push_back(vl); + } + + peers_indexed::index::type& by_time_index_wt=m_peers_white.get(); + BOOST_REVERSE_FOREACH(const peers_indexed::value_type& vl, by_time_index_wt) + { + pl_white.push_back(vl); + } + + return true; + } + //-------------------------------------------------------------------------------------------------- + inline + bool peerlist_manager::set_peer_just_seen(peerid_type peer, boost::uint32_t ip, boost::uint32_t port) + { + net_address addr; + addr.ip = ip; + addr.port = port; + return set_peer_just_seen(peer, addr); + } + //-------------------------------------------------------------------------------------------------- + inline + bool peerlist_manager::set_peer_just_seen(peerid_type peer, const net_address& addr) + { + TRY_ENTRY(); + CRITICAL_REGION_LOCAL(m_peerlist_lock); + //find in white list + peerlist_entry ple; + ple.adr = addr; + ple.id = peer; + ple.last_seen = time(NULL); + return append_with_peer_white(ple); + CATCH_ENTRY_L0("peerlist_manager::set_peer_just_seen()", false); + } + //-------------------------------------------------------------------------------------------------- + inline + bool peerlist_manager::append_with_peer_white(const peerlist_entry& ple) + { + TRY_ENTRY(); + if(!is_ip_allowed(ple.adr.ip)) + return true; + + CRITICAL_REGION_LOCAL(m_peerlist_lock); + //find in white list + auto by_addr_it_wt = m_peers_white.get().find(ple.adr); + if(by_addr_it_wt == m_peers_white.get().end()) + { + //put new record into white list + m_peers_white.insert(ple); + trim_white_peerlist(); + }else + { + //update record in white list + m_peers_white.replace(by_addr_it_wt, ple); + } + //remove from gray list, if need + auto by_addr_it_gr = m_peers_gray.get().find(ple.adr); + if(by_addr_it_gr != m_peers_gray.get().end()) + { + m_peers_gray.erase(by_addr_it_gr); + } + return true; + CATCH_ENTRY_L0("peerlist_manager::append_with_peer_white()", false); + } + //-------------------------------------------------------------------------------------------------- + inline + bool peerlist_manager::append_with_peer_gray(const peerlist_entry& ple) + { + TRY_ENTRY(); + if(!is_ip_allowed(ple.adr.ip)) + return true; + + if(ple.adr.port != 8080) + assert(false); + + + CRITICAL_REGION_LOCAL(m_peerlist_lock); + //find in white list + auto by_addr_it_wt = m_peers_white.get().find(ple.adr); + if(by_addr_it_wt != m_peers_white.get().end()) + return true; + + //update gray list + auto by_addr_it_gr = m_peers_gray.get().find(ple.adr); + if(by_addr_it_gr == m_peers_gray.get().end()) + { + //put new record into white list + m_peers_gray.insert(ple); + trim_gray_peerlist(); + }else + { + //update record in white list + m_peers_gray.replace(by_addr_it_gr, ple); + } + return true; + CATCH_ENTRY_L0("peerlist_manager::append_with_peer_gray()", false); + return true; + } + //-------------------------------------------------------------------------------------------------- +} + +BOOST_CLASS_VERSION(nodetool::peerlist_manager, 4) diff --git a/src/p2p/net_peerlist_boost_serialization.h b/src/p2p/net_peerlist_boost_serialization.h new file mode 100644 index 000000000..23a253f25 --- /dev/null +++ b/src/p2p/net_peerlist_boost_serialization.h @@ -0,0 +1,28 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +namespace boost +{ + namespace serialization + { + //BOOST_CLASS_VERSION(odetool::net_adress, 1) + template + inline void serialize(Archive &a, nodetool::net_address& na, const ver_type ver) + { + a & na.ip; + a & na.port; + } + + + template + inline void serialize(Archive &a, nodetool::peerlist_entry& pl, const ver_type ver) + { + a & pl.adr; + a & pl.id; + a & pl.last_seen; + } + } +} diff --git a/src/p2p/p2p_networks.h b/src/p2p/p2p_networks.h new file mode 100644 index 000000000..3fa409006 --- /dev/null +++ b/src/p2p/p2p_networks.h @@ -0,0 +1,10 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +namespace nodetool +{ + const static boost::uuids::uuid BYTECOIN_NETWORK = { { 0x11 ,0x10, 0x01, 0x11 , 0x11, 0x00 , 0x01, 0x01, 0x10, 0x11, 0x00, 0x12, 0x10, 0x11, 0x01, 0x10} }; //Bender's nightmare +} diff --git a/src/p2p/p2p_protocol_defs.h b/src/p2p/p2p_protocol_defs.h new file mode 100644 index 000000000..7ae6d08f3 --- /dev/null +++ b/src/p2p/p2p_protocol_defs.h @@ -0,0 +1,315 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include +#include "serialization/keyvalue_serialization.h" +#include "misc_language.h" +#include "cryptonote_config.h" +#include "crypto/crypto.h" + +namespace nodetool +{ + typedef boost::uuids::uuid uuid; + typedef uint64_t peerid_type; + +#pragma pack (push, 1) + + struct net_address + { + boost::uint32_t ip; + boost::uint32_t port; + }; + + struct peerlist_entry + { + net_address adr; + peerid_type id; + time_t last_seen; + }; + + struct connection_entry + { + net_address adr; + peerid_type id; + bool is_income; + }; + +#pragma pack(pop) + + inline + bool operator < (const net_address& a, const net_address& b) + { + return epee::misc_utils::is_less_as_pod(a, b); + } + + inline + bool operator == (const net_address& a, const net_address& b) + { + return memcmp(&a, &b, sizeof(a)) == 0; + } + inline + std::string print_peerlist_to_string(const std::list& pl) + { + time_t now_time = 0; + time(&now_time); + std::stringstream ss; + ss << std::setfill ('0') << std::setw (8) << std::hex << std::noshowbase; + BOOST_FOREACH(const peerlist_entry& pe, pl) + { + ss << pe.id << "\t" << epee::string_tools::get_ip_string_from_int32(pe.adr.ip) << ":" << boost::lexical_cast(pe.adr.port) << " \tlast_seen: " << epee::misc_utils::get_time_interval_string(now_time - pe.last_seen) << std::endl; + } + return ss.str(); + } + + + struct network_config + { + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(connections_count) + KV_SERIALIZE(handshake_interval) + KV_SERIALIZE(packet_max_size) + KV_SERIALIZE(config_id) + END_KV_SERIALIZE_MAP() + + boost::uint32_t connections_count; + boost::uint32_t connection_timeout; + boost::uint32_t ping_connection_timeout; + boost::uint32_t handshake_interval; + boost::uint32_t packet_max_size; + boost::uint32_t config_id; + boost::uint32_t send_peerlist_sz; + }; + + struct basic_node_data + { + uuid network_id; + time_t local_time; + uint32_t my_port; + peerid_type peer_id; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_VAL_POD_AS_BLOB(network_id) + KV_SERIALIZE(peer_id) + KV_SERIALIZE(local_time) + KV_SERIALIZE(my_port) + END_KV_SERIALIZE_MAP() + }; + + +#define P2P_COMMANDS_POOL_BASE 1000 + + /************************************************************************/ + /* */ + /************************************************************************/ + template + struct COMMAND_HANDSHAKE_T + { + const static int ID = P2P_COMMANDS_POOL_BASE + 1; + + struct request + { + basic_node_data node_data; + t_playload_type payload_data; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(node_data) + KV_SERIALIZE(payload_data) + END_KV_SERIALIZE_MAP() + }; + + struct response + { + basic_node_data node_data; + t_playload_type payload_data; + std::list local_peerlist; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(node_data) + KV_SERIALIZE(payload_data) + KV_SERIALIZE_CONTAINER_POD_AS_BLOB(local_peerlist) + END_KV_SERIALIZE_MAP() + }; + }; + + + /************************************************************************/ + /* */ + /************************************************************************/ + template + struct COMMAND_TIMED_SYNC_T + { + const static int ID = P2P_COMMANDS_POOL_BASE + 2; + + struct request + { + t_playload_type payload_data; + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(payload_data) + END_KV_SERIALIZE_MAP() + }; + + struct response + { + time_t local_time; + t_playload_type payload_data; + std::list local_peerlist; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(local_time) + KV_SERIALIZE(payload_data) + KV_SERIALIZE_CONTAINER_POD_AS_BLOB(local_peerlist) + END_KV_SERIALIZE_MAP() + }; + }; + + /************************************************************************/ + /* */ + /************************************************************************/ + + struct COMMAND_PING + { + /* + Used to make "callback" connection, to be sure that opponent node + have accessible connection point. Only other nodes can add peer to peerlist, + and ONLY in case when peer has accepted connection and answered to ping. + */ + const static int ID = P2P_COMMANDS_POOL_BASE + 3; + +#define PING_OK_RESPONSE_STATUS_TEXT "OK" + + struct request + { + /*actually we don't need to send any real data*/ + + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + + struct response + { + std::string status; + peerid_type peer_id; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(status) + KV_SERIALIZE(peer_id) + END_KV_SERIALIZE_MAP() + }; + }; + + +#ifdef ALLOW_DEBUG_COMMANDS + //These commands are considered as insecure, and made in debug purposes for a limited lifetime. + //Anyone who feel unsafe with this commands can disable the ALLOW_GET_STAT_COMMAND macro. + + struct proof_of_trust + { + peerid_type peer_id; + uint64_t time; + crypto::signature sign; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(peer_id) + KV_SERIALIZE(time) + KV_SERIALIZE_VAL_POD_AS_BLOB(sign) + END_KV_SERIALIZE_MAP() + }; + + + template + struct COMMAND_REQUEST_STAT_INFO_T + { + const static int ID = P2P_COMMANDS_POOL_BASE + 4; + + struct request + { + proof_of_trust tr; + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(tr) + END_KV_SERIALIZE_MAP() + }; + + struct response + { + std::string version; + std::string os_version; + uint64_t connections_count; + uint64_t incoming_connections_count; + payload_stat_info payload_info; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(version) + KV_SERIALIZE(os_version) + KV_SERIALIZE(connections_count) + KV_SERIALIZE(incoming_connections_count) + KV_SERIALIZE(payload_info) + END_KV_SERIALIZE_MAP() + }; + }; + + + /************************************************************************/ + /* */ + /************************************************************************/ + struct COMMAND_REQUEST_NETWORK_STATE + { + const static int ID = P2P_COMMANDS_POOL_BASE + 5; + + struct request + { + proof_of_trust tr; + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(tr) + END_KV_SERIALIZE_MAP() + }; + + struct response + { + std::list local_peerlist_white; + std::list local_peerlist_gray; + std::list connections_list; + peerid_type my_id; + uint64_t local_time; + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_CONTAINER_POD_AS_BLOB(local_peerlist_white) + KV_SERIALIZE_CONTAINER_POD_AS_BLOB(local_peerlist_gray) + KV_SERIALIZE_CONTAINER_POD_AS_BLOB(connections_list) + KV_SERIALIZE(my_id) + KV_SERIALIZE(local_time) + END_KV_SERIALIZE_MAP() + }; + }; + + /************************************************************************/ + /* */ + /************************************************************************/ + struct COMMAND_REQUEST_PEER_ID + { + const static int ID = P2P_COMMANDS_POOL_BASE + 6; + + struct request + { + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + + struct response + { + peerid_type my_id; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(my_id) + END_KV_SERIALIZE_MAP() + }; + }; + +#endif + + +} + + + diff --git a/src/p2p/stdafx.h b/src/p2p/stdafx.h new file mode 100644 index 000000000..1cc72f811 --- /dev/null +++ b/src/p2p/stdafx.h @@ -0,0 +1,28 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#pragma once + +#include "targetver.h" + + +#if !defined(__GNUC__) +#define _CRTDBG_MAP_ALLOC +#include +#include +#endif + + + +#include + + +#define BOOST_FILESYSTEM_VERSION 3 +#define ENABLE_RELEASE_LOGGING +#include "log_opt_defs.h" +#include "misc_log_ex.h" + + + diff --git a/src/platform/mingw/alloca.h b/src/platform/mingw/alloca.h new file mode 100644 index 000000000..9e18ae9e2 --- /dev/null +++ b/src/platform/mingw/alloca.h @@ -0,0 +1,7 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include diff --git a/src/platform/msc/alloca.h b/src/platform/msc/alloca.h new file mode 100644 index 000000000..4e8c89ea3 --- /dev/null +++ b/src/platform/msc/alloca.h @@ -0,0 +1,7 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#define alloca(size) _alloca(size) diff --git a/src/platform/msc/inline_c.h b/src/platform/msc/inline_c.h new file mode 100644 index 000000000..c2ba06b61 --- /dev/null +++ b/src/platform/msc/inline_c.h @@ -0,0 +1,9 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#ifndef __cplusplus +#define inline __inline +#endif diff --git a/src/platform/msc/stdbool.h b/src/platform/msc/stdbool.h new file mode 100644 index 000000000..2e1a06732 --- /dev/null +++ b/src/platform/msc/stdbool.h @@ -0,0 +1,13 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#if !defined(__cplusplus) + +typedef int bool; +#define true 1 +#define false 0 + +#endif diff --git a/src/platform/msc/sys/param.h b/src/platform/msc/sys/param.h new file mode 100644 index 000000000..b44de84b4 --- /dev/null +++ b/src/platform/msc/sys/param.h @@ -0,0 +1,10 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#define LITTLE_ENDIAN 1234 +#define BIG_ENDIAN 4321 +#define PDP_ENDIAN 3412 +#define BYTE_ORDER LITTLE_ENDIAN diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp new file mode 100644 index 000000000..984d9d8cd --- /dev/null +++ b/src/rpc/core_rpc_server.cpp @@ -0,0 +1,391 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + + +#include +#include "include_base_utils.h" +using namespace epee; + +#include "core_rpc_server.h" +#include "common/command_line.h" +#include "cryptonote_core/cryptonote_format_utils.h" +#include "cryptonote_core/account.h" +#include "misc_language.h" +#include "crypto/hash.h" +#include "core_rpc_server_error_codes.h" + +namespace cryptonote +{ + namespace + { + const command_line::arg_descriptor arg_rpc_bind_ip = {"rpc-bind-ip", "", "127.0.0.1"}; + const command_line::arg_descriptor arg_rpc_bind_port = {"rpc-bind-port", "", std::to_string(RPC_DEFAULT_PORT)}; + } + + //----------------------------------------------------------------------------------- + void core_rpc_server::init_options(boost::program_options::options_description& desc) + { + command_line::add_arg(desc, arg_rpc_bind_ip); + command_line::add_arg(desc, arg_rpc_bind_port); + } + //------------------------------------------------------------------------------------------------------------------------------ + core_rpc_server::core_rpc_server(core& cr, nodetool::node_server >& p2p):m_core(cr), m_p2p(p2p) + {} + //------------------------------------------------------------------------------------------------------------------------------ + bool core_rpc_server::handle_command_line(const boost::program_options::variables_map& vm) + { + m_bind_ip = command_line::get_arg(vm, arg_rpc_bind_ip); + m_port = command_line::get_arg(vm, arg_rpc_bind_port); + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ + bool core_rpc_server::init(const boost::program_options::variables_map& vm) + { + m_net_server.set_threads_prefix("RPC"); + bool r = handle_command_line(vm); + CHECK_AND_ASSERT_MES(r, false, "Failed to process command line in core_rpc_server"); + return epee::http_server_impl_base::init(m_port, m_bind_ip); + } + //------------------------------------------------------------------------------------------------------------------------------ + bool core_rpc_server::on_get_height(const COMMAND_RPC_GET_HEIGHT::request& req, COMMAND_RPC_GET_HEIGHT::response& res) + { + res.height = m_core.get_current_blockchain_height(); + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ + bool core_rpc_server::on_get_info(const COMMAND_RPC_GET_INFO::request& req, COMMAND_RPC_GET_INFO::response& res) + { + res.height = m_core.get_current_blockchain_height(); + res.difficulty = m_core.get_blockchain_storage().get_difficulty_for_next_block(); + res.tx_count = m_core.get_blockchain_storage().get_total_transactions() - res.height; //without coinbase + res.tx_pool_size = m_core.get_pool_transactions_count(); + res.alt_blocks_count = m_core.get_blockchain_storage().get_alternative_blocks_count(); + uint64_t total_conn = m_p2p.get_connections_count(); + res.outgoing_connections_count = m_p2p.get_outgoing_connections_count(); + res.incoming_connections_count = total_conn - res.outgoing_connections_count; + res.white_peerlist_size = m_p2p.get_peerlist_manager().get_white_peers_count(); + res.grey_peerlist_size = m_p2p.get_peerlist_manager().get_gray_peers_count(); + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ + bool core_rpc_server::on_get_known_block_ids(const COMMAND_RPC_GET_KNOWN_BLOCK_IDS::request& req, COMMAND_RPC_GET_KNOWN_BLOCK_IDS::response& res) + { + std::list main, alt, invalid; + m_core.get_all_known_block_ids(main, alt, invalid); + BOOST_FOREACH(crypto::hash &h, main) + res.main.push_back(string_tools::pod_to_hex(h)); + + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ + bool core_rpc_server::on_get_blocks(const COMMAND_RPC_GET_BLOCKS_FAST::request& req, COMMAND_RPC_GET_BLOCKS_FAST::response& res) + { + std::list > > bs; + if(!m_core.find_blockchain_supplement(req.block_ids, bs, res.current_height, res.start_height, COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT)) + { + res.status = "Failed"; + return false; + } + + BOOST_FOREACH(auto& b, bs) + { + res.blocks.resize(res.blocks.size()+1); + res.blocks.back().block = block_to_blob(b.first); + BOOST_FOREACH(auto& t, b.second) + { + res.blocks.back().txs.push_back(tx_to_blob(t)); + } + } + + res.status = CORE_RPC_STATUS_OK; + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ + bool core_rpc_server::on_get_random_outs(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res) + { + res.status = "Failed"; + if(!m_core.get_random_outs_for_amounts(req, res)) + { + return true; + } + + res.status = CORE_RPC_STATUS_OK; + std::stringstream ss; + typedef COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount outs_for_amount; + typedef COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry out_entry; + std::for_each(res.outs.begin(), res.outs.end(), [&](outs_for_amount& ofa) + { + ss << "[" << ofa.amount << "]:"; + CHECK_AND_ASSERT_MES(ofa.outs.size(), ;, "internal error: ofa.outs.size() is empty for amount " << ofa.amount); + std::for_each(ofa.outs.begin(), ofa.outs.end(), [&](out_entry& oe) + { + ss << oe.global_amount_index << " "; + }); + ss << ENDL; + }); + std::string s = ss.str(); + LOG_PRINT_L2("COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS: " << ENDL << s); + res.status = CORE_RPC_STATUS_OK; + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ + bool core_rpc_server::on_get_indexes(const COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::request& req, COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::response& res) + { + bool r = m_core.get_tx_outputs_gindexs(req.txid, res.o_indexes); + if(!r) + { + res.status = "Failed"; + return true; + } + res.status = CORE_RPC_STATUS_OK; + LOG_PRINT_L2("COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES: [" << res.o_indexes.size() << "]"); + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ + bool core_rpc_server::on_get_transactions(const COMMAND_RPC_GET_TRANSACTIONS::request& req, COMMAND_RPC_GET_TRANSACTIONS::response& res) + { + std::vector vh; + BOOST_FOREACH(const auto& tx_hex_str, req.txs_hashes) + { + blobdata b; + if(!string_tools::parse_hexstr_to_binbuff(tx_hex_str, b)) + { + res.status = "Failed to parse hex representation of transaction hash"; + return true; + } + if(b.size() != sizeof(crypto::hash)) + { + res.status = "Failed, size of data mismatch"; + } + vh.push_back(*reinterpret_cast(b.data())); + } + std::list missed_txs; + std::list txs; + bool r = m_core.get_transactions(vh, txs, missed_txs); + if(!r) + { + res.status = "Failed"; + return true; + } + + BOOST_FOREACH(auto& tx, txs) + { + blobdata blob = t_serializable_object_to_blob(tx); + res.txs_as_hex.push_back(string_tools::buff_to_hex_nodelimer(blob)); + } + + BOOST_FOREACH(const auto& miss_tx, missed_txs) + { + res.missed_tx.push_back(string_tools::pod_to_hex(miss_tx)); + } + + res.status = CORE_RPC_STATUS_OK; + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ + /*bool core_rpc_server::on_get_outputs(const COMMAND_RPC_GET_OUTPUTS::request& req, COMMAND_RPC_GET_OUTPUTS::response& res) + { + return true; + }*/ + //------------------------------------------------------------------------------------------------------------------------------ + bool core_rpc_server::on_send_raw_tx(const COMMAND_RPC_SEND_RAW_TX::request& req, COMMAND_RPC_SEND_RAW_TX::response& res) + { + std::string tx_blob; + if(!string_tools::parse_hexstr_to_binbuff(req.tx_as_hex, tx_blob)) + { + LOG_PRINT_L0("[on_send_raw_tx]: Failed to parse tx from hexbuff: " << req.tx_as_hex); + res.status = "Failed"; + return true; + } + + cryptonote_connection_context fake_context = AUTO_VAL_INIT(fake_context); + tx_verification_context tvc = AUTO_VAL_INIT(tvc); + if(!m_core.handle_incoming_tx(tx_blob, tvc, false)) + { + LOG_PRINT_L0("[on_send_raw_tx]: Failed to process tx"); + res.status = "Failed"; + return true; + } + + if(tvc.m_verifivation_failed) + { + LOG_PRINT_L0("[on_send_raw_tx]: tx verification failed"); + res.status = "Failed"; + return true; + } + + if(!tvc.m_should_be_relayed) + { + LOG_PRINT_L0("[on_send_raw_tx]: tx accepted, but not relayed"); + res.status = "Not relayed"; + return true; + } + + + NOTIFY_NEW_TRANSACTIONS::request r; + r.txs.push_back(tx_blob); + m_core.get_protocol()->relay_transactions(r, fake_context); + //TODO: make sure that tx has reached other nodes here, probably wait to receive reflections from other nodes + res.status = CORE_RPC_STATUS_OK; + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ + bool core_rpc_server::on_start_mining(const COMMAND_RPC_START_MINING::request& req, COMMAND_RPC_START_MINING::response& res) + { + account_public_address adr; + if(!get_account_address_from_str(adr, req.miner_address)) + { + res.status = "Failed, wrong address"; + return true; + } + + if(!m_core.get_miner().start(adr, static_cast(req.threads_count))) + { + res.status = "Failed, mining not started"; + return true; + } + res.status = CORE_RPC_STATUS_OK; + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ + bool core_rpc_server::on_stop_mining(const COMMAND_RPC_STOP_MINING::request& req, COMMAND_RPC_STOP_MINING::response& res) + { + + if(!m_core.get_miner().stop()) + { + res.status = "Failed, mining not stopped"; + return true; + } + res.status = CORE_RPC_STATUS_OK; + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ + bool core_rpc_server::on_getblockcount(const COMMAND_RPC_GETBLOCKCOUNT::request& req, COMMAND_RPC_GETBLOCKCOUNT::response& res) + { + res = m_core.get_current_blockchain_height(); + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ + bool core_rpc_server::on_getblockhash(const COMMAND_RPC_GETBLOCKHASH::request& req, COMMAND_RPC_GETBLOCKHASH::response& res, epee::json_rpc::error& error_resp) + { + if(req.size() != 1) + { + error_resp.code = CORE_RPC_ERROR_CODE_WRONG_PARAM; + error_resp.message = "Wrong parameters, expected height"; + return false; + } + uint64_t h = req[0]; + if(m_core.get_current_blockchain_height() <= h) + { + error_resp.code = CORE_RPC_ERROR_CODE_TOO_BIG_HEIGHT; + error_resp.message = std::string("To big height: ") + std::to_string(h) + ", current blockchain height = " + std::to_string(m_core.get_current_blockchain_height()); + } + res = string_tools::pod_to_hex(m_core.get_block_id_by_height(h)); + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ + uint64_t slow_memmem(void* start_buff, size_t buflen,void* pat,size_t patlen) + { + void* buf = start_buff; + void* end=(char*)buf+buflen-patlen; + while((buf=memchr(buf,((char*)pat)[0],buflen))) + { + if(buf>end) + return 0; + if(memcmp(buf,pat,patlen)==0) + return (char*)buf - (char*)start_buff; + buf=(char*)buf+1; + } + return 0; + } + //------------------------------------------------------------------------------------------------------------------------------ + bool core_rpc_server::on_getblocktemplate(const COMMAND_RPC_GETBLOCKTEMPLATE::request& req, COMMAND_RPC_GETBLOCKTEMPLATE::response& res, epee::json_rpc::error& error_resp) + { + if(req.reserve_size > 255) + { + error_resp.code = CORE_RPC_ERROR_CODE_TOO_BIG_RESERVE_SIZE; + error_resp.message = "To big reserved size, maximum 255"; + return false; + } + + cryptonote::account_public_address acc = AUTO_VAL_INIT(acc); + + if(!req.wallet_address.size() || !cryptonote::get_account_address_from_str(acc, req.wallet_address)) + { + error_resp.code = CORE_RPC_ERROR_CODE_WRONG_WALLET_ADDRESS; + error_resp.message = "Failed to parse wallet address"; + return false; + } + + block b = AUTO_VAL_INIT(b); + cryptonote::blobdata blob_reserve; + blob_reserve.resize(req.reserve_size, 0); + if(!m_core.get_block_template(b, acc, res.difficulty, res.height, blob_reserve)) + { + error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR; + error_resp.message = "Internal error: failed to create block template"; + LOG_ERROR("Failed to create block template"); + return false; + } + blobdata block_blob = t_serializable_object_to_blob(b); + crypto::public_key tx_pub_key = null_pkey; + cryptonote::parse_and_validate_tx_extra(b.miner_tx, tx_pub_key); + if(tx_pub_key == null_pkey) + { + error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR; + error_resp.message = "Internal error: failed to create block template"; + LOG_ERROR("Failed to tx pub key in coinbase extra"); + return false; + } + res.reserved_offset = slow_memmem((void*)block_blob.data(), block_blob.size(), &tx_pub_key, sizeof(tx_pub_key)); + if(!res.reserved_offset) + { + error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR; + error_resp.message = "Internal error: failed to create block template"; + LOG_ERROR("Failed to find tx pub key in blockblob"); + return false; + } + res.reserved_offset += sizeof(tx_pub_key) + 3; //3 bytes: tag for TX_EXTRA_TAG_PUBKEY(1 byte), tag for TX_EXTRA_NONCE(1 byte), counter in TX_EXTRA_NONCE(1 byte) + if(res.reserved_offset + req.reserve_size > block_blob.size()) + { + error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR; + error_resp.message = "Internal error: failed to create block template"; + LOG_ERROR("Failed to calculate offset for "); + return false; + } + res.blocktemplate_blob = string_tools::buff_to_hex_nodelimer(block_blob); + + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ + bool core_rpc_server::on_submitblock(const COMMAND_RPC_SUBMITBLOCK::request& req, COMMAND_RPC_SUBMITBLOCK::response& res, epee::json_rpc::error& error_resp) + { + if(req.size()!=1) + { + error_resp.code = CORE_RPC_ERROR_CODE_WRONG_PARAM; + error_resp.message = "Wrong param"; + return false; + } + blobdata blockblob; + if(!string_tools::parse_hexstr_to_binbuff(req[0], blockblob)) + { + error_resp.code = CORE_RPC_ERROR_CODE_WRONG_BLOCKBLOB; + error_resp.message = "Wrong block blob"; + return false; + } + cryptonote::block_verification_context bvc = AUTO_VAL_INIT(bvc); + m_core.handle_incoming_block(blockblob, bvc); + if(!bvc.m_added_to_main_chain) + { + error_resp.code = CORE_RPC_ERROR_CODE_BLOCK_NOT_ACCEPTED; + error_resp.message = "Block not accepted"; + return false; + } + res.status = "OK"; + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ + + +} diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h new file mode 100644 index 000000000..4425a1ce5 --- /dev/null +++ b/src/rpc/core_rpc_server.h @@ -0,0 +1,75 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + +#include +#include + +#include "net/http_server_impl_base.h" +#include "core_rpc_server_commands_defs.h" +#include "cryptonote_core/cryptonote_core.h" +#include "p2p/net_node.h" +#include "cryptonote_protocol/cryptonote_protocol_handler.h" + +namespace cryptonote +{ + /************************************************************************/ + /* */ + /************************************************************************/ + class core_rpc_server: public epee::http_server_impl_base + { + public: + core_rpc_server(core& cr, nodetool::node_server >& p2p); + + static void init_options(boost::program_options::options_description& desc); + bool init(const boost::program_options::variables_map& vm); + private: + + CHAIN_HTTP_TO_MAP2(); //forward http requests to uri map + + BEGIN_URI_MAP2() + MAP_URI_AUTO_JON2("/getheight", on_get_height, COMMAND_RPC_GET_HEIGHT) + MAP_URI_AUTO_JON2("/getknownblockids", on_get_known_block_ids, COMMAND_RPC_GET_KNOWN_BLOCK_IDS) + MAP_URI_AUTO_BIN2("/getblocks.bin", on_get_blocks, COMMAND_RPC_GET_BLOCKS_FAST) + MAP_URI_AUTO_BIN2("/get_o_indexes.bin", on_get_indexes, COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES) + MAP_URI_AUTO_BIN2("/getrandom_outs.bin", on_get_random_outs, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS) + MAP_URI_AUTO_JON2("/gettransactions", on_get_transactions, COMMAND_RPC_GET_TRANSACTIONS) + MAP_URI_AUTO_JON2("/sendrawtransaction", on_send_raw_tx, COMMAND_RPC_SEND_RAW_TX) + MAP_URI_AUTO_JON2("/start_mining", on_start_mining, COMMAND_RPC_START_MINING) + MAP_URI_AUTO_JON2("/stop_mining", on_stop_mining, COMMAND_RPC_STOP_MINING) + MAP_URI_AUTO_JON2("/getinfo", on_get_info, COMMAND_RPC_GET_INFO) + BEGIN_JSON_RPC_MAP("/json_rpc") + MAP_JON_RPC("getblockcount", on_getblockcount, COMMAND_RPC_GETBLOCKCOUNT) + MAP_JON_RPC_WE("on_getblockhash", on_getblockhash, COMMAND_RPC_GETBLOCKHASH) + MAP_JON_RPC_WE("getblocktemplate",on_getblocktemplate, COMMAND_RPC_GETBLOCKTEMPLATE) + MAP_JON_RPC_WE("submitblock", on_submitblock, COMMAND_RPC_SUBMITBLOCK) + END_JSON_RPC_MAP() + END_URI_MAP2() + + bool on_get_height(const COMMAND_RPC_GET_HEIGHT::request& req, COMMAND_RPC_GET_HEIGHT::response& res); + bool on_get_known_block_ids(const COMMAND_RPC_GET_KNOWN_BLOCK_IDS::request& req, COMMAND_RPC_GET_KNOWN_BLOCK_IDS::response& res); + bool on_get_blocks(const COMMAND_RPC_GET_BLOCKS_FAST::request& req, COMMAND_RPC_GET_BLOCKS_FAST::response& res); + bool on_get_transactions(const COMMAND_RPC_GET_TRANSACTIONS::request& req, COMMAND_RPC_GET_TRANSACTIONS::response& res); + bool on_get_indexes(const COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::request& req, COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::response& res); + bool on_send_raw_tx(const COMMAND_RPC_SEND_RAW_TX::request& req, COMMAND_RPC_SEND_RAW_TX::response& res); + bool on_start_mining(const COMMAND_RPC_START_MINING::request& req, COMMAND_RPC_START_MINING::response& res); + bool on_stop_mining(const COMMAND_RPC_STOP_MINING::request& req, COMMAND_RPC_STOP_MINING::response& res); + bool on_get_random_outs(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res); + bool on_get_info(const COMMAND_RPC_GET_INFO::request& req, COMMAND_RPC_GET_INFO::response& res); + + //json_rpc + bool on_getblockcount(const COMMAND_RPC_GETBLOCKCOUNT::request& req, COMMAND_RPC_GETBLOCKCOUNT::response& res); + bool on_getblockhash(const COMMAND_RPC_GETBLOCKHASH::request& req, COMMAND_RPC_GETBLOCKHASH::response& res, epee::json_rpc::error& error_resp); + bool on_getblocktemplate(const COMMAND_RPC_GETBLOCKTEMPLATE::request& req, COMMAND_RPC_GETBLOCKTEMPLATE::response& res, epee::json_rpc::error& error_resp); + bool on_submitblock(const COMMAND_RPC_SUBMITBLOCK::request& req, COMMAND_RPC_SUBMITBLOCK::response& res, epee::json_rpc::error& error_resp); + //----------------------- + bool handle_command_line(const boost::program_options::variables_map& vm); + + core& m_core; + nodetool::node_server >& m_p2p; + std::string m_port; + std::string m_bind_ip; + }; +} diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h new file mode 100644 index 000000000..fbb7171a1 --- /dev/null +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -0,0 +1,343 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once +#include "cryptonote_protocol/cryptonote_protocol_defs.h" +#include "cryptonote_core/cryptonote_basic.h" +#include "crypto/hash.h" + +namespace cryptonote +{ + //----------------------------------------------- +#define CORE_RPC_STATUS_OK "OK" + + struct COMMAND_RPC_GET_HEIGHT + { + struct request + { + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + + struct response + { + uint64_t height; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(height) + END_KV_SERIALIZE_MAP() + }; + }; + + struct COMMAND_RPC_GET_KNOWN_BLOCK_IDS + { + struct request + { + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + + struct response + { + std::list main; + std::list alt; + std::list invalid; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(main) + KV_SERIALIZE(alt) + KV_SERIALIZE(invalid) + END_KV_SERIALIZE_MAP() + }; + }; + + struct COMMAND_RPC_GET_BLOCKS_FAST + { + + struct request + { + std::list block_ids; //*first 10 blocks id goes sequential, next goes in pow(2,n) offset, like 2, 4, 8, 16, 32, 64 and so on, and the last one is always genesis block */ + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_CONTAINER_POD_AS_BLOB(block_ids) + END_KV_SERIALIZE_MAP() + }; + + struct response + { + std::list blocks; + uint64_t start_height; + uint64_t current_height; + std::string status; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(blocks) + KV_SERIALIZE(start_height) + KV_SERIALIZE(current_height) + KV_SERIALIZE(status) + END_KV_SERIALIZE_MAP() + }; + }; + //----------------------------------------------- + struct COMMAND_RPC_GET_TRANSACTIONS + { + struct request + { + std::list txs_hashes; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(txs_hashes) + END_KV_SERIALIZE_MAP() + }; + + + struct response + { + std::list txs_as_hex; //transactions blobs as hex + std::list missed_tx; //not found transactions + std::string status; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(txs_as_hex) + KV_SERIALIZE(missed_tx) + KV_SERIALIZE(status) + END_KV_SERIALIZE_MAP() + }; + }; + + //----------------------------------------------- + struct COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES + { + struct request + { + crypto::hash txid; + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_VAL_POD_AS_BLOB(txid) + END_KV_SERIALIZE_MAP() + }; + + + struct response + { + std::vector o_indexes; + std::string status; + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(o_indexes) + KV_SERIALIZE(status) + END_KV_SERIALIZE_MAP() + }; + }; + //----------------------------------------------- + struct COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS + { + struct request + { + std::list amounts; + uint64_t outs_count; + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(amounts) + KV_SERIALIZE(outs_count) + END_KV_SERIALIZE_MAP() + }; + +#pragma pack (push, 1) + struct out_entry + { + uint64_t global_amount_index; + crypto::public_key out_key; + }; +#pragma pack(pop) + + struct outs_for_amount + { + uint64_t amount; + std::list outs; + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(amount) + KV_SERIALIZE_CONTAINER_POD_AS_BLOB(outs) + END_KV_SERIALIZE_MAP() + }; + + struct response + { + std::vector outs; + std::string status; + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(outs) + KV_SERIALIZE(status) + END_KV_SERIALIZE_MAP() + }; + }; + //----------------------------------------------- + struct COMMAND_RPC_SEND_RAW_TX + { + struct request + { + std::string tx_as_hex; + + request() {} + explicit request(const transaction &); + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(tx_as_hex) + END_KV_SERIALIZE_MAP() + }; + + + struct response + { + std::string status; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(status) + END_KV_SERIALIZE_MAP() + }; + }; + //----------------------------------------------- + struct COMMAND_RPC_START_MINING + { + struct request + { + std::string miner_address; + uint64_t threads_count; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(miner_address) + KV_SERIALIZE(threads_count) + END_KV_SERIALIZE_MAP() + }; + + struct response + { + std::string status; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(status) + END_KV_SERIALIZE_MAP() + }; + }; + //----------------------------------------------- + struct COMMAND_RPC_GET_INFO + { + struct request + { + + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + + struct response + { + std::string status; + uint64_t height; + uint64_t difficulty; + uint64_t tx_count; + uint64_t tx_pool_size; + uint64_t alt_blocks_count; + uint64_t outgoing_connections_count; + uint64_t incoming_connections_count; + uint64_t white_peerlist_size; + uint64_t grey_peerlist_size; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(status) + KV_SERIALIZE(height) + KV_SERIALIZE(difficulty) + KV_SERIALIZE(tx_count) + KV_SERIALIZE(tx_pool_size) + KV_SERIALIZE(alt_blocks_count) + KV_SERIALIZE(outgoing_connections_count) + KV_SERIALIZE(incoming_connections_count) + KV_SERIALIZE(white_peerlist_size) + KV_SERIALIZE(grey_peerlist_size) + END_KV_SERIALIZE_MAP() + }; + }; + + + //----------------------------------------------- + struct COMMAND_RPC_STOP_MINING + { + struct request + { + + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + + + struct response + { + std::string status; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(status) + END_KV_SERIALIZE_MAP() + }; + }; + + + // + struct COMMAND_RPC_GETBLOCKCOUNT + { + typedef std::list request; + + typedef uint64_t response; + }; + + struct COMMAND_RPC_GETBLOCKHASH + { + typedef std::vector request; + + typedef std::string response; + }; + + + struct COMMAND_RPC_GETBLOCKTEMPLATE + { + struct request + { + uint64_t reserve_size; //max 255 bytes + std::string wallet_address; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(reserve_size) + KV_SERIALIZE(wallet_address) + END_KV_SERIALIZE_MAP() + }; + + struct response + { + uint64_t difficulty; + uint64_t height; + uint64_t reserved_offset; + blobdata blocktemplate_blob; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(difficulty) + KV_SERIALIZE(height) + KV_SERIALIZE(reserved_offset) + KV_SERIALIZE(blocktemplate_blob) + END_KV_SERIALIZE_MAP() + }; + }; + + struct COMMAND_RPC_SUBMITBLOCK + { + typedef std::vector request; + + struct response + { + std::string status; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(status) + END_KV_SERIALIZE_MAP() + }; + }; + + + +} + diff --git a/src/rpc/core_rpc_server_error_codes.h b/src/rpc/core_rpc_server_error_codes.h new file mode 100644 index 000000000..5e3296d05 --- /dev/null +++ b/src/rpc/core_rpc_server_error_codes.h @@ -0,0 +1,14 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once + + +#define CORE_RPC_ERROR_CODE_WRONG_PARAM -1 +#define CORE_RPC_ERROR_CODE_TOO_BIG_HEIGHT -2 +#define CORE_RPC_ERROR_CODE_TOO_BIG_RESERVE_SIZE -3 +#define CORE_RPC_ERROR_CODE_WRONG_WALLET_ADDRESS -4 +#define CORE_RPC_ERROR_CODE_INTERNAL_ERROR -5 +#define CORE_RPC_ERROR_CODE_WRONG_BLOCKBLOB -6 +#define CORE_RPC_ERROR_CODE_BLOCK_NOT_ACCEPTED -7 diff --git a/src/serialization/binary_archive.h b/src/serialization/binary_archive.h new file mode 100644 index 000000000..f28e45c0c --- /dev/null +++ b/src/serialization/binary_archive.h @@ -0,0 +1,169 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +/* binary_archive.h + * + * Portable (low-endian) binary archive */ +#pragma once + +#include +#include +#include +#include + +#include "common/varint.h" +#include "warnings.h" + +PUSH_WARNINGS +DISABLE_VS_WARNINGS(4244) + +//TODO: fix size_t warning in x32 platform + +template +struct binary_archive_base +{ + typedef Stream stream_type; + typedef binary_archive_base base_type; + typedef boost::mpl::bool_ is_saving; + + typedef uint8_t variant_tag_type; + + explicit binary_archive_base(stream_type &s) : stream_(s) { } + + void tag(const char *) { } + void begin_object() { } + void end_object() { } + void begin_variant() { } + void end_variant() { } + stream_type &stream() { return stream_; } +protected: + stream_type &stream_; +}; + +template +struct binary_archive; + +template <> +struct binary_archive : public binary_archive_base +{ + explicit binary_archive(stream_type &s) : base_type(s) { + stream_type::streampos pos = stream_.tellg(); + stream_.seekg(0, std::ios_base::end); + eof_pos_ = stream_.tellg(); + stream_.seekg(pos); + } + + template + void serialize_int(T &v) + { + serialize_uint(*(typename boost::make_unsigned::type *)&v); + } + + template + void serialize_uint(T &v, size_t width = sizeof(T)) + { + T ret = 0; + unsigned shift = 0; + for (size_t i = 0; i < width; i++) { + //std::cerr << "tell: " << stream_.tellg() << " value: " << ret << std::endl; + char c; + stream_.get(c); + T b = (unsigned char)c; + ret += (b << shift); + shift += 8; + } + v = ret; + } + void serialize_blob(void *buf, size_t len, const char *delimiter="") { stream_.read((char *)buf, len); } + + template + void serialize_varint(T &v) + { + serialize_uvarint(*(typename boost::make_unsigned::type *)(&v)); + } + + template + void serialize_uvarint(T &v) + { + typedef std::istreambuf_iterator it; + tools::read_varint(it(stream_), it(), v); // XXX handle failure + } + void begin_array(size_t &s) + { + serialize_varint(s); + } + void begin_array() { } + + void delimit_array() { } + void end_array() { } + + void begin_string(const char *delimiter="\"") { } + void end_string(const char *delimiter="\"") { } + + void read_variant_tag(variant_tag_type &t) { + serialize_int(t); + } + + size_t remaining_bytes() { + if (!stream_.good()) + return 0; + //std::cerr << "tell: " << stream_.tellg() << std::endl; + assert(stream_.tellg() <= eof_pos_); + return eof_pos_ - stream_.tellg(); + } +protected: + std::streamoff eof_pos_; +}; + +template <> +struct binary_archive : public binary_archive_base +{ + explicit binary_archive(stream_type &s) : base_type(s) { } + + template + void serialize_int(T v) + { + serialize_uint(static_cast::type>(v)); + } + template + void serialize_uint(T v) + { + for (size_t i = 0; i < sizeof(T); i++) { + stream_.put((char)(v & 0xff)); + if (1 < sizeof(T)) { + v >>= 8; + } + } + } + void serialize_blob(void *buf, size_t len, const char *delimiter="") { stream_.write((char *)buf, len); } + + template + void serialize_varint(T &v) + { + serialize_uvarint(*(typename boost::make_unsigned::type *)(&v)); + } + + template + void serialize_uvarint(T &v) + { + typedef std::ostreambuf_iterator it; + tools::write_varint(it(stream_), v); + } + void begin_array(size_t s) + { + serialize_varint(s); + } + void begin_array() { } + void delimit_array() { } + void end_array() { } + + void begin_string(const char *delimiter="\"") { } + void end_string(const char *delimiter="\"") { } + + void write_variant_tag(variant_tag_type t) { + serialize_int(t); + } +}; + +POP_WARNINGS diff --git a/src/serialization/binary_utils.h b/src/serialization/binary_utils.h new file mode 100644 index 000000000..d06e8a22a --- /dev/null +++ b/src/serialization/binary_utils.h @@ -0,0 +1,28 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include +#include "binary_archive.h" + +namespace serialization { + +template +bool parse_binary(const std::string &blob, T &v) +{ + std::istringstream istr(blob); + binary_archive iar(istr); + return ::serialization::serialize(iar, v); +} + +template +bool dump_binary(T& v, std::string& blob) +{ + std::stringstream ostr; + binary_archive oar(ostr); + bool success = ::serialization::serialize(oar, v); + blob = ostr.str(); + return success && ostr.good(); +}; + +} // namespace serialization diff --git a/src/serialization/crypto.h b/src/serialization/crypto.h new file mode 100644 index 000000000..89d3c9885 --- /dev/null +++ b/src/serialization/crypto.h @@ -0,0 +1,62 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "serialization.h" +#include "debug_archive.h" +#include "crypto/chacha8.h" +#include "crypto/crypto.h" +#include "crypto/hash.h" + +// read +template