diff --git a/CMakeLists.txt b/CMakeLists.txt index 62ec419e..b779b74d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,6 +6,7 @@ option(WITH_AEON "CryptoNight-Lite support" ON) option(WITH_SUMO "CryptoNight-Heavy support" ON) option(WITH_HTTPD "HTTP REST API" ON) option(WITH_DEBUG_LOG "Enable debug log output" OFF) +option(WITH_TLS "Enable OpenSSL support" ON) option(BUILD_STATIC "Build static binary" OFF) include (CheckIncludeFile) @@ -155,7 +156,7 @@ else() src/Mem_unix.cpp ) - set(EXTRA_LIBS pthread rt) + set(EXTRA_LIBS pthread rt dl) endif() if (CMAKE_SYSTEM_NAME STREQUAL FreeBSD) @@ -194,6 +195,8 @@ else() endif() endif() +include(cmake/OpenSSL.cmake) + CHECK_INCLUDE_FILE (syslog.h HAVE_SYSLOG_H) if (HAVE_SYSLOG_H) add_definitions(/DHAVE_SYSLOG_H) @@ -233,6 +236,7 @@ if (WITH_HTTPD) message(FATAL_ERROR "microhttpd NOT found: use `-DWITH_HTTPD=OFF` to build without http deamon support") endif() else() + set(HTTPD_SOURCES "") set(MHD_LIBRARY "") add_definitions(/DXMRIG_NO_HTTPD) add_definitions(/DXMRIG_NO_API) @@ -250,5 +254,5 @@ if (WITH_DEBUG_LOG) add_definitions(/DAPP_DEBUG) endif() -add_executable(${PROJECT_NAME} ${HEADERS} ${SOURCES} ${SOURCES_OS} ${SOURCES_CPUID} ${HEADERS_CRYPTO} ${SOURCES_CRYPTO} ${SOURCES_SYSLOG} ${HTTPD_SOURCES}) -target_link_libraries(${PROJECT_NAME} ${UV_LIBRARIES} ${MHD_LIBRARY} ${EXTRA_LIBS} ${CPUID_LIB}) +add_executable(${PROJECT_NAME} ${HEADERS} ${SOURCES} ${SOURCES_OS} ${SOURCES_CPUID} ${HEADERS_CRYPTO} ${SOURCES_CRYPTO} ${SOURCES_SYSLOG} ${HTTPD_SOURCES} ${TLS_SOURCES}) +target_link_libraries(${PROJECT_NAME} ${OPENSSL_LIBRARIES} ${UV_LIBRARIES} ${MHD_LIBRARY} ${EXTRA_LIBS} ${CPUID_LIB}) diff --git a/cmake/OpenSSL.cmake b/cmake/OpenSSL.cmake new file mode 100644 index 00000000..d0c0d164 --- /dev/null +++ b/cmake/OpenSSL.cmake @@ -0,0 +1,22 @@ +if (WITH_TLS) + set(OPENSSL_ROOT_DIR ${XMRIG_DEPS}) + set(OPENSSL_USE_STATIC_LIBS TRUE) + set(OPENSSL_MSVC_STATIC_RT TRUE) + + find_package(OpenSSL) + + if (OPENSSL_FOUND) + set(TLS_SOURCES src/common/net/Tls.h src/common/net/Tls.cpp) + include_directories(${OPENSSL_INCLUDE_DIR}) + else() + message(FATAL_ERROR "OpenSSL NOT found: use `-DWITH_TLS=OFF` to build without TLS support") + endif() + + if (WIN32) + set(EXTRA_LIBS ${EXTRA_LIBS} Crypt32) + endif() +else() + set(TLS_SOURCES "") + set(OPENSSL_LIBRARIES "") + add_definitions(/DXMRIG_NO_TLS) +endif() diff --git a/src/Summary.cpp b/src/Summary.cpp index 76842d5b..de6b1234 100644 --- a/src/Summary.cpp +++ b/src/Summary.cpp @@ -37,24 +37,6 @@ #include "version.h" -static void print_versions(xmrig::Config *config) -{ - char buf[16] = { 0 }; - -# if defined(__clang__) - snprintf(buf, 16, " clang/%d.%d.%d", __clang_major__, __clang_minor__, __clang_patchlevel__); -# elif defined(__GNUC__) - snprintf(buf, 16, " gcc/%d.%d.%d", __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__); -# elif defined(_MSC_VER) - snprintf(buf, 16, " MSVC/%d", MSVC_VERSION); -# endif - - Log::i()->text(config->isColors() ? GREEN_BOLD(" * ") WHITE_BOLD("%-13s") CYAN_BOLD("%s/%s") WHITE_BOLD(" libuv/%s%s") - : " * %-13s%s/%s libuv/%s%s", - "VERSIONS", APP_NAME, APP_VERSION, uv_version_string(), buf); -} - - static void print_memory(xmrig::Config *config) { # ifdef _WIN32 if (config->isColors()) { @@ -71,18 +53,18 @@ static void print_memory(xmrig::Config *config) { static void print_cpu(xmrig::Config *config) { if (config->isColors()) { - Log::i()->text(GREEN_BOLD(" * ") WHITE_BOLD("%-13s") "%s (%d) %sx64 %sAES-NI", + Log::i()->text(GREEN_BOLD(" * ") WHITE_BOLD("%-13s%s (%d)") " %sx64 %sAES", "CPU", Cpu::brand(), Cpu::sockets(), Cpu::isX64() ? "\x1B[1;32m" : "\x1B[1;31m-", Cpu::hasAES() ? "\x1B[1;32m" : "\x1B[1;31m-"); # ifndef XMRIG_NO_LIBCPUID - Log::i()->text(GREEN_BOLD(" * ") WHITE_BOLD("%-13s") "%.1f MB/%.1f MB", "CPU L2/L3", Cpu::l2() / 1024.0, Cpu::l3() / 1024.0); + Log::i()->text(GREEN_BOLD(" * ") WHITE_BOLD("%-13s%.1f MB/%.1f MB"), "CPU L2/L3", Cpu::l2() / 1024.0, Cpu::l3() / 1024.0); # endif } else { - Log::i()->text(" * %-13s%s (%d) %sx64 %sAES-NI", "CPU", Cpu::brand(), Cpu::sockets(), Cpu::isX64() ? "" : "-", Cpu::hasAES() ? "" : "-"); + Log::i()->text(" * %-13s%s (%d) %sx64 %sAES", "CPU", Cpu::brand(), Cpu::sockets(), Cpu::isX64() ? "" : "-", Cpu::hasAES() ? "" : "-"); # ifndef XMRIG_NO_LIBCPUID Log::i()->text(" * %-13s%.1f MB/%.1f MB", "CPU L2/L3", Cpu::l2() / 1024.0, Cpu::l3() / 1024.0); # endif @@ -120,44 +102,6 @@ static void print_threads(xmrig::Config *config) } -static void print_pools(xmrig::Config *config) -{ - const std::vector &pools = config->pools(); - - for (size_t i = 0; i < pools.size(); ++i) { - Log::i()->text(config->isColors() ? GREEN_BOLD(" * ") WHITE_BOLD("POOL #%-7zu") CYAN_BOLD("%s") " variant " WHITE_BOLD("%s") - : " * POOL #%-7d%s variant %s", - i + 1, - pools[i].url(), - pools[i].algorithm().variantName() - ); - } - -# ifdef APP_DEBUG - LOG_NOTICE("POOLS --------------------------------------------------------------------"); - for (const Pool &pool : pools) { - pool.print(); - } - LOG_NOTICE("--------------------------------------------------------------------------"); -# endif -} - - -#ifndef XMRIG_NO_API -static void print_api(xmrig::Config *config) -{ - const int port = config->apiPort(); - if (port == 0) { - return; - } - - Log::i()->text(config->isColors() ? GREEN_BOLD(" * ") WHITE_BOLD("%-13s") CYAN("%s:") CYAN_BOLD("%d") - : " * %-13s%s:%d", - "API BIND", config->isApiIPv6() ? "[::]" : "0.0.0.0", port); -} -#endif - - static void print_commands(xmrig::Config *config) { if (config->isColors()) { @@ -173,15 +117,12 @@ static void print_commands(xmrig::Config *config) void Summary::print(xmrig::Controller *controller) { - print_versions(controller->config()); + controller->config()->printVersions(); print_memory(controller->config()); print_cpu(controller->config()); print_threads(controller->config()); - print_pools(controller->config()); - -# ifndef XMRIG_NO_API - print_api(controller->config()); -# endif + controller->config()->printPools(); + controller->config()->printAPI(); print_commands(controller->config()); } diff --git a/src/common/Platform.cpp b/src/common/Platform.cpp index 52b55987..a95f78e7 100644 --- a/src/common/Platform.cpp +++ b/src/common/Platform.cpp @@ -26,6 +26,12 @@ #include +#ifndef XMRIG_NO_TLS +# include +# include +#endif + + #include "Platform.h" @@ -61,3 +67,23 @@ const char *Platform::defaultConfigName() *m_defaultConfigName = '\0'; return nullptr; } + + +void Platform::init(const char *userAgent) +{ +# ifndef XMRIG_NO_TLS + SSL_library_init(); + SSL_load_error_strings(); + ERR_load_BIO_strings(); + ERR_load_crypto_strings(); + SSL_load_error_strings(); + OpenSSL_add_all_digests(); +# endif + + if (userAgent) { + m_userAgent = userAgent; + } + else { + m_userAgent = createUserAgent(); + } +} diff --git a/src/common/Platform.h b/src/common/Platform.h index 8704604a..5dfb9ff7 100644 --- a/src/common/Platform.h +++ b/src/common/Platform.h @@ -21,8 +21,8 @@ * along with this program. If not, see . */ -#ifndef __PLATFORM_H__ -#define __PLATFORM_H__ +#ifndef XMRIG_PLATFORM_H +#define XMRIG_PLATFORM_H #include @@ -43,9 +43,11 @@ public: static inline const char *userAgent() { return m_userAgent.data(); } private: + static char *createUserAgent(); + static char m_defaultConfigName[520]; static xmrig::c_str m_userAgent; }; -#endif /* __PLATFORM_H__ */ +#endif /* XMRIG_PLATFORM_H */ diff --git a/src/common/Platform_mac.cpp b/src/common/Platform_mac.cpp index b8181cc4..d0c533b0 100644 --- a/src/common/Platform_mac.cpp +++ b/src/common/Platform_mac.cpp @@ -38,7 +38,7 @@ #endif -static inline char *createUserAgent() +char *Platform::createUserAgent() { const size_t max = 160; @@ -65,17 +65,6 @@ bool Platform::setThreadAffinity(uint64_t cpu_id) } -void Platform::init(const char *userAgent) -{ - if (userAgent) { - m_userAgent = userAgent; - } - else { - m_userAgent = createUserAgent(); - } -} - - void Platform::setProcessPriority(int priority) { diff --git a/src/common/Platform_unix.cpp b/src/common/Platform_unix.cpp index 97b32ee8..058920ec 100644 --- a/src/common/Platform_unix.cpp +++ b/src/common/Platform_unix.cpp @@ -52,7 +52,7 @@ typedef cpuset_t cpu_set_t; #endif -static inline char *createUserAgent() +char *Platform::createUserAgent() { const size_t max = 160; @@ -92,23 +92,11 @@ bool Platform::setThreadAffinity(uint64_t cpu_id) } -void Platform::init(const char *userAgent) -{ - if (userAgent) { - m_userAgent = userAgent; - } - else { - m_userAgent = createUserAgent(); - } -} - - void Platform::setProcessPriority(int priority) { } - void Platform::setThreadPriority(int priority) { if (priority == -1) { diff --git a/src/common/Platform_win.cpp b/src/common/Platform_win.cpp index 47f41867..32b850d1 100644 --- a/src/common/Platform_win.cpp +++ b/src/common/Platform_win.cpp @@ -55,7 +55,7 @@ static inline OSVERSIONINFOEX winOsVersion() } -static inline char *createUserAgent() +char *Platform::createUserAgent() { const auto osver = winOsVersion(); const size_t max = 160; @@ -94,17 +94,6 @@ bool Platform::setThreadAffinity(uint64_t cpu_id) } -void Platform::init(const char *userAgent) -{ - if (userAgent) { - m_userAgent = userAgent; - } - else { - m_userAgent = createUserAgent(); - } -} - - void Platform::setProcessPriority(int priority) { if (priority == -1) { diff --git a/src/common/config/CommonConfig.cpp b/src/common/config/CommonConfig.cpp index ca901757..b70d5e3d 100644 --- a/src/common/config/CommonConfig.cpp +++ b/src/common/config/CommonConfig.cpp @@ -29,12 +29,37 @@ #include +#ifndef XMRIG_NO_HTTPD +# include +#endif + + +#ifndef XMRIG_NO_TLS +# include +#endif + + +#ifdef XMRIG_AMD_PROJECT +# if defined(__APPLE__) +# include +# else +# include "3rdparty/CL/cl.h" +# endif +#endif + + +#ifdef XMRIG_NVIDIA_PROJECT +# include "nvidia/cryptonight.h" +#endif + + #include "common/config/CommonConfig.h" #include "common/log/Log.h" #include "donate.h" #include "rapidjson/document.h" #include "rapidjson/filewritestream.h" #include "rapidjson/prettywriter.h" +#include "version.h" xmrig::CommonConfig::CommonConfig() : @@ -69,8 +94,102 @@ xmrig::CommonConfig::CommonConfig() : } -xmrig::CommonConfig::~CommonConfig() +void xmrig::CommonConfig::printAPI() { +# ifndef XMRIG_NO_API + if (apiPort() == 0) { + return; + } + + Log::i()->text(isColors() ? GREEN_BOLD(" * ") WHITE_BOLD("%-13s") CYAN("%s:") CYAN_BOLD("%d") + : " * %-13s%s:%d", + "API BIND", isApiIPv6() ? "[::]" : "0.0.0.0", apiPort()); +# endif +} + + +void xmrig::CommonConfig::printPools() +{ + for (size_t i = 0; i < m_activePools.size(); ++i) { + if (!isColors()) { + Log::i()->text(" * POOL #%-7zu%s variant=%s, TLS=%d", + i + 1, + m_activePools[i].url(), + m_activePools[i].algorithm().variantName(), + static_cast(m_activePools[i].isTLS()) + ); + } + else { + Log::i()->text(GREEN_BOLD(" * ") WHITE_BOLD("POOL #%-7zu") "\x1B[1;%dm%s\x1B[0m variant " WHITE_BOLD("%s"), + i + 1, + m_activePools[i].isTLS() ? 32 : 36, + m_activePools[i].url(), + m_activePools[i].algorithm().variantName() + ); + } + } + +# ifdef APP_DEBUG + LOG_NOTICE("POOLS --------------------------------------------------------------------"); + for (const Pool &pool : m_activePools) { + pool.print(); + } + LOG_NOTICE("--------------------------------------------------------------------------"); +# endif +} + + +void xmrig::CommonConfig::printVersions() +{ + char buf[256] = { 0 }; + +# if defined(__clang__) + snprintf(buf, sizeof buf, "clang/%d.%d.%d", __clang_major__, __clang_minor__, __clang_patchlevel__); +# elif defined(__GNUC__) + snprintf(buf, sizeof buf, "gcc/%d.%d.%d", __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__); +# elif defined(_MSC_VER) + snprintf(buf, sizeof buf, "MSVC/%d", MSVC_VERSION); +# endif + + Log::i()->text(isColors() ? GREEN_BOLD(" * ") WHITE_BOLD("%-13s") CYAN_BOLD("%s/%s") WHITE_BOLD(" %s") + : " * %-13s%s/%s %s", + "ABOUT", APP_NAME, APP_VERSION, buf); + +# if defined(XMRIG_AMD_PROJECT) +# if CL_VERSION_2_0 + const char *ocl = "2.0"; +# elif CL_VERSION_1_2 + const char *ocl = "1.2"; +# elif CL_VERSION_1_1 + const char *ocl = "1.1"; +# elif CL_VERSION_1_0 + const char *ocl = "1.0"; +# else + const char *ocl = "0.0"; +# endif + int length = snprintf(buf, sizeof buf, "OpenCL/%s ", ocl); +# elif defined(XMRIG_NVIDIA_PROJECT) + const int cudaVersion = cuda_get_runtime_version(); + int length = snprintf(buf, sizeof buf, "CUDA/%d.%d ", cudaVersion / 1000, cudaVersion % 100); +# else + memset(buf, 0, 16); + int length = 0; +# endif + +# if !defined(XMRIG_NO_TLS) && defined(OPENSSL_VERSION_TEXT) + { + constexpr const char *v = OPENSSL_VERSION_TEXT + 8; + length += snprintf(buf + length, (sizeof buf) - length, "OpenSSL/%.*s ", static_cast(strchr(v, ' ') - v), v); + } +# endif + +# ifndef XMRIG_NO_HTTPD + length += snprintf(buf + length, (sizeof buf) - length, "microhttpd/%s ", MHD_get_version()); +# endif + + Log::i()->text(isColors() ? GREEN_BOLD(" * ") WHITE_BOLD("%-13slibuv/%s %s") + : " * %-13slibuv/%s %s", + "LIBS", uv_version_string(), buf); } @@ -126,6 +245,12 @@ bool xmrig::CommonConfig::finalize() pool.adjust(m_algorithm); if (pool.isValid() && pool.algorithm().isValid()) { +# ifdef XMRIG_NO_TLS + if (pool.isTLS()) { + continue; + } +# endif + m_activePools.push_back(std::move(pool)); } } @@ -157,6 +282,10 @@ bool xmrig::CommonConfig::parseBoolean(int key, bool enable) m_pools.back().setKeepAlive(enable ? Pool::kKeepAliveTimeout : 0); break; + case TlsKey: /* --tls */ + m_pools.back().setTLS(enable); + break; + # ifndef XMRIG_PROXY_PROJECT case NicehashKey: /* --nicehash */ m_pools.back().setNicehash(enable); @@ -235,6 +364,10 @@ bool xmrig::CommonConfig::parseString(int key, const char *arg) m_pools.back().setRigId(arg); break; + case FingerprintKey: /* --tls-fingerprint */ + m_pools.back().setFingerprint(arg); + break; + case VariantKey: /* --variant */ m_pools.back().algorithm().parseVariant(arg); break; @@ -269,6 +402,7 @@ bool xmrig::CommonConfig::parseString(int key, const char *arg) case SyslogKey: /* --syslog */ case KeepAliveKey: /* --keepalive */ case NicehashKey: /* --nicehash */ + case TlsKey: /* --tls */ case ApiIPv6Key: /* --api-ipv6 */ case DryRunKey: /* --dry-run */ return parseBoolean(key, true); diff --git a/src/common/config/CommonConfig.h b/src/common/config/CommonConfig.h index fa27ea6a..7643a1a5 100644 --- a/src/common/config/CommonConfig.h +++ b/src/common/config/CommonConfig.h @@ -21,8 +21,8 @@ * along with this program. If not, see . */ -#ifndef __COMMONCONFIG_H__ -#define __COMMONCONFIG_H__ +#ifndef XMRIG_COMMONCONFIG_H +#define XMRIG_COMMONCONFIG_H #include @@ -41,7 +41,6 @@ class CommonConfig : public IConfig { public: CommonConfig(); - ~CommonConfig(); inline bool isApiIPv6() const { return m_apiIPv6; } inline bool isApiRestricted() const { return m_apiRestricted; } @@ -68,6 +67,10 @@ public: bool save() override; + void printAPI(); + void printPools(); + void printVersions(); + protected: enum State { NoneState, diff --git a/src/common/config/ConfigLoader.cpp b/src/common/config/ConfigLoader.cpp index cc5d9a49..484c2f8f 100644 --- a/src/common/config/ConfigLoader.cpp +++ b/src/common/config/ConfigLoader.cpp @@ -32,6 +32,11 @@ #endif +#ifndef XMRIG_NO_TLS +# include +#endif + + #include "common/config/ConfigLoader.h" #include "common/config/ConfigWatcher.h" #include "common/interfaces/IConfig.h" @@ -313,6 +318,13 @@ void xmrig::ConfigLoader::showVersion() printf("\nlibuv/%s\n", uv_version_string()); # ifndef XMRIG_NO_HTTPD - printf("libmicrohttpd/%s\n", MHD_get_version()); + printf("microhttpd/%s\n", MHD_get_version()); +# endif + +# if !defined(XMRIG_NO_TLS) && defined(OPENSSL_VERSION_TEXT) + { + constexpr const char *v = OPENSSL_VERSION_TEXT + 8; + printf("OpenSSL/%.*s\n", static_cast(strchr(v, ' ') - v), v); + } # endif } diff --git a/src/common/interfaces/IConfig.h b/src/common/interfaces/IConfig.h index 95a4babf..d3593163 100644 --- a/src/common/interfaces/IConfig.h +++ b/src/common/interfaces/IConfig.h @@ -63,6 +63,8 @@ public: VerboseKey = 1100, VersionKey = 'V', WatchKey = 1105, + TlsKey = 1013, + FingerprintKey = 1014, // xmrig common CPUPriorityKey = 1021, diff --git a/src/common/net/Client.cpp b/src/common/net/Client.cpp index 2a9db444..3a93789b 100644 --- a/src/common/net/Client.cpp +++ b/src/common/net/Client.cpp @@ -29,6 +29,13 @@ #include +#ifndef XMRIG_NO_TLS +# include +# include +# include "common/net/Tls.h" +#endif + + #include "common/interfaces/IClientListener.h" #include "common/log/Log.h" #include "common/net/Client.h" @@ -48,6 +55,17 @@ int64_t Client::m_sequence = 1; xmrig::Storage Client::m_storage; +#ifdef APP_DEBUG +static const char *states[] = { + "unconnected", + "host-lookup", + "connecting", + "connected", + "closing" +}; +#endif + + Client::Client(int id, const char *agent, IClientListener *listener) : m_ipv6(false), m_nicehash(false), @@ -61,6 +79,7 @@ Client::Client(int id, const char *agent, IClientListener *listener) : m_failures(0), m_recvBufPos(0), m_state(UnconnectedState), + m_tls(nullptr), m_expire(0), m_jobs(0), m_keepAlive(0), @@ -92,6 +111,12 @@ Client::~Client() void Client::connect() { +# ifndef XMRIG_NO_TLS + if (m_pool.isTLS()) { + m_tls = new Tls(this); + } +# endif + resolve(m_pool.host()); } @@ -122,6 +147,7 @@ void Client::deleteLater() } + void Client::setPool(const Pool &pool) { if (!pool.isValid()) { @@ -160,6 +186,30 @@ bool Client::disconnect() } +const char *Client::tlsFingerprint() const +{ +# ifndef XMRIG_NO_TLS + if (isTLS() && m_pool.fingerprint() == nullptr) { + return m_tls->fingerprint(); + } +# endif + + return nullptr; +} + + +const char *Client::tlsVersion() const +{ +# ifndef XMRIG_NO_TLS + if (isTLS()) { + return m_tls->version(); + } +# endif + + return nullptr; +} + + int64_t Client::submit(const JobResult &result) { using namespace rapidjson; @@ -245,6 +295,16 @@ bool Client::isCriticalError(const char *message) } +bool Client::isTLS() const +{ +# ifndef XMRIG_NO_TLS + return m_pool.isTLS() && m_tls; +# else + return false; +# endif +} + + bool Client::parseJob(const rapidjson::Value ¶ms, int *code) { if (!params.IsObject()) { @@ -330,6 +390,39 @@ bool Client::parseLogin(const rapidjson::Value &result, int *code) } +bool Client::send(BIO *bio) +{ +# ifndef XMRIG_NO_TLS + uv_buf_t buf; + buf.len = BIO_get_mem_data(bio, &buf.base); + + if (buf.len == 0) { + return true; + } + + LOG_DEBUG("[%s] TLS send (%d bytes)", m_pool.url(), static_cast(buf.len)); + + bool result = false; + if (state() == ConnectedState && uv_is_writable(m_stream)) { + result = uv_try_write(m_stream, &buf, 1) > 0; + + if (!result) { + close(); + } + } + else { + LOG_DEBUG_ERR("[%s] send failed, invalid state: %d", m_pool.url(), m_state); + } + + (void) BIO_reset(bio); + + return result; +# else + return false; +# endif +} + + bool Client::verifyAlgorithm(const xmrig::Algorithm &algorithm) const { # ifdef XMRIG_PROXY_PROJECT @@ -404,16 +497,27 @@ int64_t Client::send(const rapidjson::Document &doc) int64_t Client::send(size_t size) { LOG_DEBUG("[%s] send (%d bytes): \"%s\"", m_pool.url(), size, m_sendBuf); - if (state() != ConnectedState || !uv_is_writable(m_stream)) { - LOG_DEBUG_ERR("[%s] send failed, invalid state: %d", m_pool.url(), m_state); - return -1; + +# ifndef XMRIG_NO_TLS + if (isTLS()) { + if (!m_tls->send(m_sendBuf, size)) { + return -1; + } } + else +# endif + { + if (state() != ConnectedState || !uv_is_writable(m_stream)) { + LOG_DEBUG_ERR("[%s] send failed, invalid state: %d", m_pool.url(), m_state); + return -1; + } - uv_buf_t buf = uv_buf_init(m_sendBuf, (unsigned int) size); + uv_buf_t buf = uv_buf_init(m_sendBuf, (unsigned int) size); - if (uv_try_write(m_stream, &buf, 1) < 0) { - close(); - return -1; + if (uv_try_write(m_stream, &buf, 1) < 0) { + close(); + return -1; + } } m_expire = uv_now(uv_default_loop()) + kResponseTimeout; @@ -463,6 +567,22 @@ void Client::connect(sockaddr *addr) } +void Client::handshake() +{ +# ifndef XMRIG_NO_TLS + if (isTLS()) { + m_expire = uv_now(uv_default_loop()) + kResponseTimeout; + + m_tls->handshake(); + } + else +# endif + { + login(); + } +} + + void Client::login() { using namespace rapidjson; @@ -511,6 +631,13 @@ void Client::onClose() m_socket = nullptr; setState(UnconnectedState); +# ifndef XMRIG_NO_TLS + if (m_tls) { + delete m_tls; + m_tls = nullptr; + } +# endif + reconnect(); } @@ -665,6 +792,35 @@ void Client::ping() } +void Client::read() +{ + char* end; + char* start = m_recvBuf.base; + size_t remaining = m_recvBufPos; + + while ((end = static_cast(memchr(start, '\n', remaining))) != nullptr) { + end++; + size_t len = end - start; + parse(start, len); + + remaining -= len; + start = end; + } + + if (remaining == 0) { + m_recvBufPos = 0; + return; + } + + if (start == m_recvBuf.base) { + return; + } + + memcpy(m_recvBuf.base, start, remaining); + m_recvBufPos = remaining; +} + + void Client::reconnect() { if (!m_listener) { @@ -689,7 +845,7 @@ void Client::reconnect() void Client::setState(SocketState state) { - LOG_DEBUG("[%s] state: %d", m_pool.url(), state); + LOG_DEBUG("[%s] state: \"%s\"", m_pool.url(), states[state]); if (m_state == state) { return; @@ -757,7 +913,7 @@ void Client::onConnect(uv_connect_t *req, int status) uv_read_start(client->m_stream, Client::onAllocBuffer, Client::onRead); delete req; - client->login(); + client->handshake(); } @@ -789,30 +945,18 @@ void Client::onRead(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf) client->m_recvBufPos += nread; - char* end; - char* start = client->m_recvBuf.base; - size_t remaining = client->m_recvBufPos; +# ifndef XMRIG_NO_TLS + if (client->isTLS()) { + LOG_DEBUG("[%s] TLS received (%d bytes)", client->m_pool.url(), static_cast(nread)); - while ((end = static_cast(memchr(start, '\n', remaining))) != nullptr) { - end++; - size_t len = end - start; - client->parse(start, len); - - remaining -= len; - start = end; - } - - if (remaining == 0) { + client->m_tls->read(client->m_recvBuf.base, client->m_recvBufPos); client->m_recvBufPos = 0; - return; } - - if (start == client->m_recvBuf.base) { - return; + else +# endif + { + client->read(); } - - memcpy(client->m_recvBuf.base, start, remaining); - client->m_recvBufPos = remaining; } diff --git a/src/common/net/Client.h b/src/common/net/Client.h index 4be8badb..55105c0b 100644 --- a/src/common/net/Client.h +++ b/src/common/net/Client.h @@ -21,8 +21,8 @@ * along with this program. If not, see . */ -#ifndef __CLIENT_H__ -#define __CLIENT_H__ +#ifndef XMRIG_CLIENT_H +#define XMRIG_CLIENT_H #include @@ -43,6 +43,9 @@ class IClientListener; class JobResult; +typedef struct bio_st BIO; + + class Client { public: @@ -54,12 +57,20 @@ public: ClosingState }; - constexpr static int kResponseTimeout = 20 * 1000; + constexpr static int kResponseTimeout = 20 * 1000; + +# ifndef XMRIG_NO_TLS + constexpr static int kInputBufferSize = 1024 * 16; +# else + constexpr static int kInputBufferSize = 1024 * 2; +# endif Client(int id, const char *agent, IClientListener *listener); ~Client(); bool disconnect(); + const char *tlsFingerprint() const; + const char *tlsVersion() const; int64_t submit(const JobResult &result); void connect(); void connect(const Pool &pool); @@ -80,6 +91,9 @@ public: inline void setRetryPause(int ms) { m_retryPause = ms; } private: + class Tls; + + enum Extensions { NicehashExt = 1, AlgoExt = 2 @@ -87,14 +101,17 @@ private: bool close(); bool isCriticalError(const char *message); + bool isTLS() const; bool parseJob(const rapidjson::Value ¶ms, int *code); bool parseLogin(const rapidjson::Value &result, int *code); + bool send(BIO *bio); bool verifyAlgorithm(const xmrig::Algorithm &algorithm) const; int resolve(const char *host); int64_t send(const rapidjson::Document &doc); int64_t send(size_t size); void connect(const std::vector &ipv4, const std::vector &ipv6); void connect(sockaddr *addr); + void handshake(); void login(); void onClose(); void parse(char *line, size_t len); @@ -102,6 +119,7 @@ private: void parseNotification(const char *method, const rapidjson::Value ¶ms, const rapidjson::Value &error); void parseResponse(int64_t id, const rapidjson::Value &result, const rapidjson::Value &error); void ping(); + void read(); void reconnect(); void setState(SocketState state); void startTimeout(); @@ -120,9 +138,9 @@ private: bool m_ipv6; bool m_nicehash; bool m_quiet; - char m_buf[2048]; + char m_buf[kInputBufferSize]; char m_ip[46]; - char m_sendBuf[768]; + char m_sendBuf[1024]; const char *m_agent; IClientListener *m_listener; int m_extensions; @@ -135,6 +153,7 @@ private: size_t m_recvBufPos; SocketState m_state; std::map m_results; + Tls *m_tls; uint64_t m_expire; uint64_t m_jobs; uint64_t m_keepAlive; @@ -150,4 +169,4 @@ private: }; -#endif /* __CLIENT_H__ */ +#endif /* XMRIG_CLIENT_H */ diff --git a/src/common/net/Pool.cpp b/src/common/net/Pool.cpp index 49f4e54c..141e5115 100644 --- a/src/common/net/Pool.cpp +++ b/src/common/net/Pool.cpp @@ -46,6 +46,7 @@ Pool::Pool() : m_nicehash(false), + m_tls(false), m_keepAlive(0), m_port(kDefaultPort) { @@ -65,6 +66,7 @@ Pool::Pool() : */ Pool::Pool(const char *url) : m_nicehash(false), + m_tls(false), m_keepAlive(0), m_port(kDefaultPort) { @@ -72,8 +74,9 @@ Pool::Pool(const char *url) : } -Pool::Pool(const char *host, uint16_t port, const char *user, const char *password, int keepAlive, bool nicehash) : +Pool::Pool(const char *host, uint16_t port, const char *user, const char *password, int keepAlive, bool nicehash, bool tls) : m_nicehash(nicehash), + m_tls(tls), m_keepAlive(keepAlive), m_port(port), m_host(host), @@ -114,15 +117,17 @@ bool Pool::isCompatible(const xmrig::Algorithm &algorithm) const bool Pool::isEqual(const Pool &other) const { - return (m_nicehash == other.m_nicehash - && m_keepAlive == other.m_keepAlive - && m_port == other.m_port - && m_algorithm == other.m_algorithm - && m_host == other.m_host - && m_password == other.m_password - && m_rigId == other.m_rigId - && m_url == other.m_url - && m_user == other.m_user); + return (m_nicehash == other.m_nicehash + && m_tls == other.m_tls + && m_keepAlive == other.m_keepAlive + && m_port == other.m_port + && m_algorithm == other.m_algorithm + && m_fingerprint == other.m_fingerprint + && m_host == other.m_host + && m_password == other.m_password + && m_rigId == other.m_rigId + && m_url == other.m_url + && m_user == other.m_user); } @@ -134,7 +139,13 @@ bool Pool::parse(const char *url) const char *base = url; if (p) { - if (strncasecmp(url, "stratum+tcp://", 14)) { + if (strncasecmp(url, "stratum+tcp://", 14) == 0) { + m_tls = false; + } + else if (strncasecmp(url, "stratum+ssl://", 14) == 0) { + m_tls = true; + } + else { return false; } @@ -221,6 +232,9 @@ rapidjson::Value Pool::toJSON(rapidjson::Document &doc) const break; } + obj.AddMember("tls", isTLS(), allocator); + obj.AddMember("tls-fingerprint", fingerprint() ? Value(StringRef(fingerprint())).Move() : Value(kNullType).Move(), allocator); + return obj; } diff --git a/src/common/net/Pool.h b/src/common/net/Pool.h index 0641b851..123cc131 100644 --- a/src/common/net/Pool.h +++ b/src/common/net/Pool.h @@ -45,30 +45,35 @@ public: Pool(); Pool(const char *url); Pool(const char *host, - uint16_t port, - const char *user = nullptr, - const char *password = nullptr, - int keepAlive = 0, - bool nicehash = false + uint16_t port, + const char *user = nullptr, + const char *password = nullptr, + int keepAlive = 0, + bool nicehash = false, + bool tls = false ); - inline bool isNicehash() const { return m_nicehash; } - inline bool isValid() const { return !m_host.isNull() && m_port > 0; } - inline const char *host() const { return m_host.data(); } - inline const char *password() const { return !m_password.isNull() ? m_password.data() : kDefaultPassword; } - inline const char *rigId() const { return m_rigId.data(); } - inline const char *url() const { return m_url.data(); } - inline const char *user() const { return !m_user.isNull() ? m_user.data() : kDefaultUser; } - inline const xmrig::Algorithm &algorithm() const { return m_algorithm; } - inline const xmrig::Algorithms &algorithms() const { return m_algorithms; } - inline int keepAlive() const { return m_keepAlive; } - inline uint16_t port() const { return m_port; } - inline void setKeepAlive(int keepAlive) { m_keepAlive = keepAlive >= 0 ? keepAlive : 0; } - inline void setNicehash(bool nicehash) { m_nicehash = nicehash; } - inline void setPassword(const char *password) { m_password = password; } - inline void setRigId(const char *rigId) { m_rigId = rigId; } - inline void setUser(const char *user) { m_user = user; } - inline xmrig::Algorithm &algorithm() { return m_algorithm; } + inline bool isNicehash() const { return m_nicehash; } + inline bool isTLS() const { return m_tls; } + inline bool isValid() const { return !m_host.isNull() && m_port > 0; } + inline const char *fingerprint() const { return m_fingerprint.data(); } + inline const char *host() const { return m_host.data(); } + inline const char *password() const { return !m_password.isNull() ? m_password.data() : kDefaultPassword; } + inline const char *rigId() const { return m_rigId.data(); } + inline const char *url() const { return m_url.data(); } + inline const char *user() const { return !m_user.isNull() ? m_user.data() : kDefaultUser; } + inline const xmrig::Algorithm &algorithm() const { return m_algorithm; } + inline const xmrig::Algorithms &algorithms() const { return m_algorithms; } + inline int keepAlive() const { return m_keepAlive; } + inline uint16_t port() const { return m_port; } + inline void setFingerprint(const char *fingerprint) { m_fingerprint = fingerprint; } + inline void setKeepAlive(int keepAlive) { m_keepAlive = keepAlive >= 0 ? keepAlive : 0; } + inline void setNicehash(bool nicehash) { m_nicehash = nicehash; } + inline void setPassword(const char *password) { m_password = password; } + inline void setRigId(const char *rigId) { m_rigId = rigId; } + inline void setTLS(bool tls) { m_tls = tls; } + inline void setUser(const char *user) { m_user = user; } + inline xmrig::Algorithm &algorithm() { return m_algorithm; } inline bool operator!=(const Pool &other) const { return !isEqual(other); } inline bool operator==(const Pool &other) const { return isEqual(other); } @@ -92,10 +97,12 @@ private: void rebuild(); bool m_nicehash; + bool m_tls; int m_keepAlive; uint16_t m_port; xmrig::Algorithm m_algorithm; xmrig::Algorithms m_algorithms; + xmrig::c_str m_fingerprint; xmrig::c_str m_host; xmrig::c_str m_password; xmrig::c_str m_rigId; diff --git a/src/common/net/Tls.cpp b/src/common/net/Tls.cpp new file mode 100644 index 00000000..182d86ff --- /dev/null +++ b/src/common/net/Tls.cpp @@ -0,0 +1,190 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2018 Lee Clagett + * Copyright 2018 SChernykh + * Copyright 2016-2018 XMRig , + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +#include + + +#include "common/net/Client.h" +#include "common/net/Tls.h" +#include "common/log/Log.h" + + +#ifdef _MSC_VER +# define strncasecmp(x,y,z) _strnicmp(x,y,z) +#endif + + +Client::Tls::Tls(Client *client) : + m_ready(false), + m_buf(), + m_fingerprint(), + m_client(client), + m_ssl(nullptr) +{ + m_ctx = SSL_CTX_new(SSLv23_method()); + assert(m_ctx != nullptr); + + if (!m_ctx) { + return; + } + + m_writeBio = BIO_new(BIO_s_mem()); + m_readBio = BIO_new(BIO_s_mem()); + SSL_CTX_set_options(m_ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3); +} + + +Client::Tls::~Tls() +{ + if (m_ctx) { + SSL_CTX_free(m_ctx); + } + + if (m_ssl) { + SSL_free(m_ssl); + } +} + + +bool Client::Tls::handshake() +{ + m_ssl = SSL_new(m_ctx); + assert(m_ssl != nullptr); + + if (!m_ssl) { + return false; + } + + SSL_set_connect_state(m_ssl); + SSL_set_bio(m_ssl, m_readBio, m_writeBio); + SSL_do_handshake(m_ssl); + + return send(); +} + + +bool Client::Tls::send(const char *data, size_t size) +{ + SSL_write(m_ssl, data, size); + + return send(); +} + + +const char *Client::Tls::fingerprint() const +{ + return m_ready ? m_fingerprint : nullptr; +} + + +const char *Client::Tls::version() const +{ + return m_ready ? SSL_get_version(m_ssl) : nullptr; +} + + +void Client::Tls::read(const char *data, size_t size) +{ + BIO_write(m_readBio, data, size); + + if (!SSL_is_init_finished(m_ssl)) { + const int rc = SSL_connect(m_ssl); + + if (rc < 0 && SSL_get_error(m_ssl, rc) == SSL_ERROR_WANT_READ) { + send(); + } else if (rc == 1) { + X509 *cert = SSL_get_peer_certificate(m_ssl); + if (!verify(cert)) { + X509_free(cert); + m_client->close(); + + return; + } + + X509_free(cert); + m_ready = true; + m_client->login(); + } + + return; + } + + int bytes_read = 0; + while ((bytes_read = SSL_read(m_ssl, m_buf, sizeof(m_buf))) > 0) { + m_client->parse(m_buf, bytes_read); + } +} + + +bool Client::Tls::send() +{ + return m_client->send(m_writeBio); +} + + +bool Client::Tls::verify(X509 *cert) +{ + if (cert == nullptr) { + LOG_ERR("[%s] Failed to get server certificate", m_client->m_pool.url()); + + return false; + } + + if (!verifyFingerprint(cert)) { + LOG_ERR("[%s] Failed to verify server certificate fingerprint", m_client->m_pool.url()); + + const char *fingerprint = m_client->m_pool.fingerprint(); + if (strlen(m_fingerprint) == 64 && fingerprint != nullptr) { + LOG_ERR("\"%s\" was given", m_fingerprint); + LOG_ERR("\"%s\" was configured", fingerprint); + } + + return false; + } + + return true; +} + + +bool Client::Tls::verifyFingerprint(X509 *cert) +{ + const EVP_MD *digest = EVP_get_digestbyname("sha256"); + if (digest == nullptr) { + return false; + } + + unsigned char md[EVP_MAX_MD_SIZE]; + unsigned int dlen; + + if (X509_digest(cert, digest, md, &dlen) != 1) { + return false; + } + + Job::toHex(md, 32, m_fingerprint); + const char *fingerprint = m_client->m_pool.fingerprint(); + + return fingerprint == nullptr || strncasecmp(m_fingerprint, fingerprint, 64) == 0; +} diff --git a/src/common/net/Tls.h b/src/common/net/Tls.h new file mode 100644 index 00000000..6e38f32f --- /dev/null +++ b/src/common/net/Tls.h @@ -0,0 +1,62 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2016-2018 XMRig , + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef XMRIG_TLS_H +#define XMRIG_TLS_H + + +#include + + +#include "common/net/Client.h" + + +class Client::Tls +{ +public: + Tls(Client *client); + ~Tls(); + + bool handshake(); + bool send(const char *data, size_t size); + const char *fingerprint() const; + const char *version() const; + void read(const char *data, size_t size); + +private: + bool send(); + bool verify(X509 *cert); + bool verifyFingerprint(X509 *cert); + + BIO *m_readBio; + BIO *m_writeBio; + bool m_ready; + char m_buf[1024 * 2]; + char m_fingerprint[32 * 2 + 8]; + Client *m_client; + SSL *m_ssl; + SSL_CTX *m_ctx; +}; + + +#endif /* XMRIG_TLS_H */ diff --git a/src/core/ConfigLoader_platform.h b/src/core/ConfigLoader_platform.h index 98724271..c034f3e7 100644 --- a/src/core/ConfigLoader_platform.h +++ b/src/core/ConfigLoader_platform.h @@ -132,6 +132,8 @@ static struct option const options[] = { { "user-agent", 1, nullptr, xmrig::IConfig::UserAgentKey }, { "userpass", 1, nullptr, xmrig::IConfig::UserpassKey }, { "rig-id", 1, nullptr, xmrig::IConfig::RigIdKey }, + { "tls", 0, nullptr, xmrig::IConfig::TlsKey }, + { "tls-fingerprint", 1, nullptr, xmrig::IConfig::FingerprintKey }, { "version", 0, nullptr, xmrig::IConfig::VersionKey }, { nullptr, 0, nullptr, 0 } }; @@ -162,15 +164,17 @@ static struct option const config_options[] = { static struct option const pool_options[] = { - { "url", 1, nullptr, xmrig::IConfig::UrlKey }, - { "pass", 1, nullptr, xmrig::IConfig::PasswordKey }, - { "user", 1, nullptr, xmrig::IConfig::UserKey }, - { "userpass", 1, nullptr, xmrig::IConfig::UserpassKey }, - { "nicehash", 0, nullptr, xmrig::IConfig::NicehashKey }, - { "keepalive", 2, nullptr, xmrig::IConfig::KeepAliveKey }, - { "variant", 1, nullptr, xmrig::IConfig::VariantKey }, - { "rig-id", 1, nullptr, xmrig::IConfig::RigIdKey }, - { nullptr, 0, nullptr, 0 } + { "url", 1, nullptr, xmrig::IConfig::UrlKey }, + { "pass", 1, nullptr, xmrig::IConfig::PasswordKey }, + { "user", 1, nullptr, xmrig::IConfig::UserKey }, + { "userpass", 1, nullptr, xmrig::IConfig::UserpassKey }, + { "nicehash", 0, nullptr, xmrig::IConfig::NicehashKey }, + { "keepalive", 2, nullptr, xmrig::IConfig::KeepAliveKey }, + { "variant", 1, nullptr, xmrig::IConfig::VariantKey }, + { "rig-id", 1, nullptr, xmrig::IConfig::RigIdKey }, + { "tls", 0, nullptr, xmrig::IConfig::TlsKey }, + { "tls-fingerprint", 1, nullptr, xmrig::IConfig::FingerprintKey }, + { nullptr, 0, nullptr, 0 } }; diff --git a/src/net/Network.cpp b/src/net/Network.cpp index 7293a0ac..703e0ccf 100644 --- a/src/net/Network.cpp +++ b/src/net/Network.cpp @@ -101,7 +101,15 @@ void Network::onActive(IStrategy *strategy, Client *client) m_state.setPool(client->host(), client->port(), client->ip()); - LOG_INFO(isColors() ? "\x1B[01;37muse pool \x1B[01;36m%s:%d \x1B[01;30m%s" : "use pool %s:%d %s", client->host(), client->port(), client->ip()); + const char *tlsVersion = client->tlsVersion(); + LOG_INFO(isColors() ? WHITE_BOLD("use pool ") CYAN_BOLD("%s:%d ") GREEN_BOLD("%s") " \x1B[1;30m%s " + : "use pool %s:%d %s %s", + client->host(), client->port(), tlsVersion ? tlsVersion : "", client->ip()); + + const char *fingerprint = client->tlsFingerprint(); + if (fingerprint != nullptr) { + LOG_INFO("\x1B[1;30mfingerprint (SHA-256): \"%s\"", fingerprint); + } }