From 6ca6836b27316d0ccf58a4119e007851a22f7492 Mon Sep 17 00:00:00 2001 From: fireice-uk Date: Wed, 22 Feb 2017 22:49:55 +0000 Subject: [PATCH] Patched over stak updates from the cpu version --- CMakeLists.txt | 4 +- cli-miner.cpp | 66 +++++++ config.txt | 24 +++ console.cpp | 27 +++ console.h | 2 + crypto/cryptonight_common.cpp | 8 + crypto/soft_aes.c | 8 + donate-level.h | 4 + executor.cpp | 29 ++- executor.h | 10 +- httpd.cpp | 23 +++ jconf.cpp | 50 ++++- jconf.h | 7 + jpsock.cpp | 175 +++++------------ jpsock.h | 18 +- minethd.cpp | 8 + socket.cpp | 358 ++++++++++++++++++++++++++++++++++ socket.h | 57 ++++++ xmr-stak-amd.cbp | 4 + 19 files changed, 740 insertions(+), 142 deletions(-) create mode 100644 socket.cpp create mode 100644 socket.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 0010171..72c9fc8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,6 +13,8 @@ if("${MHTD}" STREQUAL "MHTD-NOTFOUND") message(FATAL_ERROR "libmicrohttpd is required") endif() +find_package(OpenSSL REQUIRED) + #set(CMAKE_VERBOSE_MAKEFILE ON) set(CMAKE_CONFIGURATION_TYPES "RELEASE;STATIC") if("${CMAKE_BUILD_TYPE}" STREQUAL "") @@ -30,6 +32,6 @@ set(EXECUTABLE_OUTPUT_PATH "bin") file(GLOB SOURCES "crypto/*.c" "crypto/*.cpp" "amd_gpu/*.c" "*.cpp") add_executable(xmr-stak-amd ${SOURCES}) -target_link_libraries(xmr-stak-amd pthread microhttpd OpenCL) +target_link_libraries(xmr-stak-amd pthread microhttpd OpenCL crypto ssl) diff --git a/cli-miner.cpp b/cli-miner.cpp index 250d6ed..184d5aa 100644 --- a/cli-miner.cpp +++ b/cli-miner.cpp @@ -11,6 +11,14 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see . + * + * Additional permission under GNU GPL version 3 section 7 + * + * If you modify this Program, or any covered work, by linking or combining + * it with OpenSSL (or a modified version of that library), containing parts + * covered by the terms of OpenSSL License and SSLeay License, the licensors + * of this Program grant you additional permission to convey the resulting work. + * */ #include "executor.h" @@ -24,6 +32,8 @@ #include #include +#include + //Do a press any key for the windows folk. *insert any key joke here* #ifdef _WIN32 void win_exit() @@ -39,9 +49,17 @@ void win_exit() void win_exit() { return; } #endif // _WIN32 +void do_benchmark(); + int main(int argc, char *argv[]) { + SSL_library_init(); + SSL_load_error_strings(); + ERR_load_BIO_strings(); + OpenSSL_add_all_digests(); + const char* sFilename = "config.txt"; + bool benchmark_mode = false; if(argc >= 2) { @@ -53,7 +71,14 @@ int main(int argc, char *argv[]) } if(argc >= 3 && strcasecmp(argv[1], "-c") == 0) + { sFilename = argv[2]; + } + else if(argc >= 3 && strcasecmp(argv[1], "benchmark_mode") == 0) + { + sFilename = argv[2]; + benchmark_mode = true; + } else sFilename = argv[1]; } @@ -70,6 +95,13 @@ int main(int argc, char *argv[]) return 0; } + if(benchmark_mode) + { + do_benchmark(); + win_exit(); + return 0; + } + if(jconf::inst()->GetHttpdPort() != 0) { if (!httpd::inst()->start_daemon()) @@ -92,6 +124,9 @@ int main(int argc, char *argv[]) printer::inst()->print_str("'c' - connection\n"); printer::inst()->print_str("-------------------------------------------------------------------\n"); + if(strlen(jconf::inst()->GetOutputFile()) != 0) + printer::inst()->open_logfile(jconf::inst()->GetOutputFile()); + executor::inst()->ex_start(); int key; @@ -117,3 +152,34 @@ int main(int argc, char *argv[]) return 0; } + +void do_benchmark() +{ + using namespace std::chrono; + std::vector* pvThreads; + + printer::inst()->print_msg(L0, "Running a 60 second benchmark..."); + + uint8_t work[76] = {0}; + minethd::miner_work oWork = minethd::miner_work("", work, sizeof(work), 0, 0, 0); + pvThreads = minethd::thread_starter(oWork); + + uint64_t iStartStamp = time_point_cast(high_resolution_clock::now()).time_since_epoch().count(); + + std::this_thread::sleep_for(std::chrono::seconds(60)); + + oWork = minethd::miner_work(); + minethd::switch_work(oWork); + + double fTotalHps = 0.0; + for (uint32_t i = 0; i < pvThreads->size(); i++) + { + double fHps = pvThreads->at(i)->iHashCount; + fHps /= (pvThreads->at(i)->iTimestamp - iStartStamp) / 1000.0; + + printer::inst()->print_msg(L0, "Thread %u: %.1f H/S", i, fHps); + fTotalHps += fHps; + } + + printer::inst()->print_msg(L0, "Total: %.1f H/S", fTotalHps); +} diff --git a/config.txt b/config.txt index 44dbf2f..0d6e254 100644 --- a/config.txt +++ b/config.txt @@ -24,6 +24,19 @@ */ "platform_index" : 0, +/* + * TLS Settings + * If you need real security, make sure tls_secure_algo is enabled (otherwise MITM attack can downgrade encryption + * to trivially breakable stuff like DES and MD5), and verify the server's fingerprint through a trusted channel. + * + * use_tls - This option will make us connect using Transport Layer Security. + * tls_secure_algo - Use only secure algorithms. This will make us quit with an error if we can't negotiate a secure algo. + * tls_fingerprint - Server's SHA256 fingerprint. If this string is non-empty then we will check the server's cert against it. + */ +"use_tls" : false, +"tls_secure_algo" : true, +"tls_fingerprint" : "", + /* * pool_address - Pool address should be in the form "pool.supportxmr.com:3333". Only stratum pools are supported. * wallet_address - Your wallet, or pool login. @@ -44,9 +57,12 @@ * call_timeout - How long should we wait for a response from the server before we assume it is dead and drop the connection. * retry_time - How long should we wait before another connection attempt. * Both values are in seconds. + * giveup_limit - Limit how many times we try to reconnect to the pool. Zero means no limit. Note that stak miners + * don't mine while the connection is lost, so your computer's power usage goes down to idle. */ "call_timeout" : 10, "retry_time" : 10, +"giveup_limit" : 0, /* * Output control. @@ -71,6 +87,14 @@ */ "h_print_time" : 60, +/* + * Output file + * + * output_file - This option will log all output to a file. + * + */ +"output_file" : "", + /* * Built-in web server * I like checking my hashrate on my phone. Don't you? diff --git a/console.cpp b/console.cpp index 5550359..62b6957 100644 --- a/console.cpp +++ b/console.cpp @@ -11,6 +11,14 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see . + * + * Additional permission under GNU GPL version 3 section 7 + * + * If you modify this Program, or any covered work, by linking or combining + * it with OpenSSL (or a modified version of that library), containing parts + * covered by the terms of OpenSSL License and SSLeay License, the licensors + * of this Program grant you additional permission to convey the resulting work. + * */ #include "console.h" @@ -147,6 +155,13 @@ printer* printer::oInst = nullptr; printer::printer() { verbose_level = LINF; + logfile = nullptr; +} + +bool printer::open_logfile(const char* file) +{ + logfile = fopen(file, "ab+"); + return logfile != nullptr; } void printer::print_msg(verbosity verbose, const char* fmt, ...) @@ -177,12 +192,24 @@ void printer::print_msg(verbosity verbose, const char* fmt, ...) std::unique_lock lck(print_mutex); fputs(buf, stdout); + + if(logfile != nullptr) + { + fputs(buf, logfile); + fflush(logfile); + } } void printer::print_str(const char* str) { std::unique_lock lck(print_mutex); fputs(str, stdout); + + if(logfile != nullptr) + { + fputs(str, logfile); + fflush(logfile); + } } extern "C" void printer_print_msg(const char* fmt, ...) diff --git a/console.h b/console.h index 5c86830..47c3c94 100644 --- a/console.h +++ b/console.h @@ -31,6 +31,7 @@ public: inline void set_verbose_level(size_t level) { verbose_level = (verbosity)level; } void print_msg(verbosity verbose, const char* fmt, ...); void print_str(const char* str); + bool open_logfile(const char* file); private: printer(); @@ -38,4 +39,5 @@ private: std::mutex print_mutex; verbosity verbose_level; + FILE* logfile; }; diff --git a/crypto/cryptonight_common.cpp b/crypto/cryptonight_common.cpp index 533dd6a..2c90d78 100644 --- a/crypto/cryptonight_common.cpp +++ b/crypto/cryptonight_common.cpp @@ -11,6 +11,14 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see . + * + * Additional permission under GNU GPL version 3 section 7 + * + * If you modify this Program, or any covered work, by linking or combining + * it with OpenSSL (or a modified version of that library), containing parts + * covered by the terms of OpenSSL License and SSLeay License, the licensors + * of this Program grant you additional permission to convey the resulting work. + * */ extern "C" diff --git a/crypto/soft_aes.c b/crypto/soft_aes.c index 76c9c1c..b8309cf 100644 --- a/crypto/soft_aes.c +++ b/crypto/soft_aes.c @@ -11,6 +11,14 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see . + * + * Additional permission under GNU GPL version 3 section 7 + * + * If you modify this Program, or any covered work, by linking or combining + * it with OpenSSL (or a modified version of that library), containing parts + * covered by the terms of OpenSSL License and SSLeay License, the licensors + * of this Program grant you additional permission to convey the resulting work. + * */ /* diff --git a/donate-level.h b/donate-level.h index 7b30212..ccae28f 100644 --- a/donate-level.h +++ b/donate-level.h @@ -6,6 +6,10 @@ * Example of how it works for the default setting of 1.0: * You miner will mine into your usual pool for 99 minutes, then switch to the developer's pool for 1.0 minute. * Switching is instant, and only happens after a successful connection, so you never loose any hashes. + * + * If you plan on changing this setting to 0.0 please consider making a one off donation to my wallet: + * 4581HhZkQHgZrZjKeCfCJxZff9E3xCgHGF25zABZz7oR71TnbbgiS7sK9jveE6Dx6uMs2LwszDuvQJgRZQotdpHt1fTdDhk + * */ constexpr double fDevDonationLevel = 1.0 / 100.0; diff --git a/executor.cpp b/executor.cpp index bbab126..fc7ead5 100644 --- a/executor.cpp +++ b/executor.cpp @@ -11,6 +11,14 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see . + * + * Additional permission under GNU GPL version 3 section 7 + * + * If you modify this Program, or any covered work, by linking or combining + * it with OpenSSL (or a modified version of that library), containing parts + * covered by the terms of OpenSSL License and SSLeay License, the licensors + * of this Program grant you additional permission to convey the resulting work. + * */ #include @@ -103,8 +111,17 @@ void executor::ex_clock_thd() void executor::sched_reconnect() { + iReconnectAttempts++; + size_t iLimit = jconf::inst()->GetGiveUpLimit(); + if(iLimit != 0 && iReconnectAttempts > iLimit) + { + printer::inst()->print_msg(L0, "Give up limit reached. Exitting."); + exit(0); + } + long long unsigned int rt = jconf::inst()->GetNetRetry(); - printer::inst()->print_msg(L1, "Pool connection lost. Waiting %lld s before retry.", rt); + printer::inst()->print_msg(L1, "Pool connection lost. Waiting %lld s before retry (attempt %llu).", + rt, int_port(iReconnectAttempts)); auto work = minethd::miner_work(); minethd::switch_work(work); @@ -185,7 +202,10 @@ void executor::on_sock_ready(size_t pool_id) } } else + { + iReconnectAttempts = 0; reset_stats(); + } } void executor::on_sock_error(size_t pool_id, std::string&& sError) @@ -333,7 +353,8 @@ void executor::on_switch_pool(size_t pool_id) // If it fails, it fails, we carry on on the usr pool // as we never receive further events printer::inst()->print_msg(L1, "Connecting to dev pool..."); - if(!pool->connect("donate.xmr-stak.net:3333", error)) + const char* dev_pool_addr = jconf::inst()->GetTlsSetting() ? "donate.xmr-stak.net:6666" : "donate.xmr-stak.net:3333"; + if(!pool->connect(dev_pool_addr, error)) printer::inst()->print_msg(L1, "Error connecting to dev pool. Staying with user pool."); } else @@ -368,8 +389,8 @@ void executor::ex_main() telem = new telemetry(pvThreads->size()); current_pool_id = usr_pool_id; - usr_pool = new jpsock(usr_pool_id); - dev_pool = new jpsock(dev_pool_id); + usr_pool = new jpsock(usr_pool_id, jconf::inst()->GetTlsSetting()); + dev_pool = new jpsock(dev_pool_id, jconf::inst()->GetTlsSetting()); ex_event ev; std::thread clock_thd(&executor::ex_clock_thd, this); diff --git a/executor.h b/executor.h index 81aad08..dc5274b 100644 --- a/executor.h +++ b/executor.h @@ -29,6 +29,10 @@ public: inline void push_event(ex_event&& ev) { oEventQ.push(std::move(ev)); } void push_timed_event(ex_event&& ev, size_t sec); + constexpr static size_t invalid_pool_id = 0; + constexpr static size_t dev_pool_id = 1; + constexpr static size_t usr_pool_id = 2; + private: struct timed_event { @@ -47,10 +51,6 @@ private: // We will divide up this period according to the config setting constexpr static size_t iDevDonatePeriod = 100 * 60; - constexpr static size_t invalid_pool_id = 0; - constexpr static size_t dev_pool_id = 1; - constexpr static size_t usr_pool_id = 2; - std::list lTimedEvents; std::mutex timed_event_mutex; thdq oEventQ; @@ -85,6 +85,8 @@ private: std::promise httpReady; std::mutex httpMutex; + size_t iReconnectAttempts = 0; + struct sck_error_log { std::chrono::system_clock::time_point time; diff --git a/httpd.cpp b/httpd.cpp index 339d15f..a21e4cd 100644 --- a/httpd.cpp +++ b/httpd.cpp @@ -1,3 +1,26 @@ +/* + * 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 + * 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 . + * + * Additional permission under GNU GPL version 3 section 7 + * + * If you modify this Program, or any covered work, by linking or combining + * it with OpenSSL (or a modified version of that library), containing parts + * covered by the terms of OpenSSL License and SSLeay License, the licensors + * of this Program grant you additional permission to convey the resulting work. + * + */ + #include #include #include diff --git a/jconf.cpp b/jconf.cpp index d9a2589..5d22f80 100644 --- a/jconf.cpp +++ b/jconf.cpp @@ -11,6 +11,14 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see . + * + * Additional permission under GNU GPL version 3 section 7 + * + * If you modify this Program, or any covered work, by linking or combining + * it with OpenSSL (or a modified version of that library), containing parts + * covered by the terms of OpenSSL License and SSLeay License, the licensors + * of this Program grant you additional permission to convey the resulting work. + * */ #include "jconf.h" @@ -37,8 +45,10 @@ using namespace rapidjson; /* * This enum needs to match index in oConfigValues, otherwise we will get a runtime error */ -enum configEnum { iGpuThreadNum, aGpuThreadsConf, iPlatformIdx, sPoolAddr, sWalletAddr, - sPoolPwd, iCallTimeout, iNetRetry, iVerboseLevel, iAutohashTime, iHttpdPort, bPreferIpv4 }; +enum configEnum { iGpuThreadNum, aGpuThreadsConf, iPlatformIdx, + bTlsMode, bTlsSecureAlgo, sTlsFingerprint, sPoolAddr, sWalletAddr, sPoolPwd, + iCallTimeout, iNetRetry, iGiveUpLimit, iVerboseLevel, iAutohashTime, + sOutputFile, iHttpdPort, bPreferIpv4 }; struct configVal { configEnum iName; @@ -51,13 +61,18 @@ configVal oConfigValues[] = { { iGpuThreadNum, "gpu_thread_num", kNumberType }, { aGpuThreadsConf, "gpu_threads_conf", kArrayType }, { iPlatformIdx, "platform_index", kNumberType }, + { bTlsMode, "use_tls", kTrueType }, + { bTlsSecureAlgo, "tls_secure_algo", kTrueType }, + { sTlsFingerprint, "tls_fingerprint", kStringType }, { sPoolAddr, "pool_address", kStringType }, { sWalletAddr, "wallet_address", kStringType }, { sPoolPwd, "pool_password", kStringType }, { iCallTimeout, "call_timeout", kNumberType }, { iNetRetry, "retry_time", kNumberType }, + { iGiveUpLimit, "giveup_limit", kNumberType }, { iVerboseLevel, "verbose_level", kNumberType }, { iAutohashTime, "h_print_time", kNumberType }, + { sOutputFile, "output_file", kStringType }, { iHttpdPort, "httpd_port", kNumberType }, { bPreferIpv4, "prefer_ipv4", kTrueType } }; @@ -135,6 +150,21 @@ size_t jconf::GetPlatformIdx() return prv->configValues[iPlatformIdx]->GetUint64(); } +bool jconf::GetTlsSetting() +{ + return prv->configValues[bTlsMode]->GetBool(); +} + +bool jconf::TlsSecureAlgos() +{ + return prv->configValues[bTlsSecureAlgo]->GetBool(); +} + +const char* jconf::GetTlsFingerprint() +{ + return prv->configValues[sTlsFingerprint]->GetString(); +} + const char* jconf::GetPoolAddress() { return prv->configValues[sPoolAddr]->GetString(); @@ -170,6 +200,11 @@ uint64_t jconf::GetNetRetry() return prv->configValues[iNetRetry]->GetUint64(); } +uint64_t jconf::GetGiveUpLimit() +{ + return prv->configValues[iGiveUpLimit]->GetUint64(); +} + uint64_t jconf::GetVerboseLevel() { return prv->configValues[iVerboseLevel]->GetUint64(); @@ -185,6 +220,11 @@ uint16_t jconf::GetHttpdPort() return prv->configValues[iHttpdPort]->GetUint(); } +const char* jconf::GetOutputFile() +{ + return prv->configValues[sOutputFile]->GetString(); +} + bool jconf::check_cpu_features() { constexpr int AESNI_BIT = 1 << 25; @@ -319,10 +359,12 @@ bool jconf::parse_config(const char* sFilename) } } - if(!prv->configValues[iCallTimeout]->IsUint64() || !prv->configValues[iNetRetry]->IsUint64()) + if(!prv->configValues[iCallTimeout]->IsUint64() || + !prv->configValues[iNetRetry]->IsUint64() || + !prv->configValues[iGiveUpLimit]->IsUint64()) { printer::inst()->print_msg(L0, - "Invalid config file. call_timeout and retry_time need to be positive integers."); + "Invalid config file. call_timeout, retry_time and giveup_limit need to be positive integers."); return false; } diff --git a/jconf.h b/jconf.h index d069167..51af4d2 100644 --- a/jconf.h +++ b/jconf.h @@ -25,6 +25,10 @@ public: size_t GetPlatformIdx(); + bool GetTlsSetting(); + bool TlsSecureAlgos(); + const char* GetTlsFingerprint(); + const char* GetPoolAddress(); const char* GetPoolPwd(); const char* GetWalletAddress(); @@ -32,8 +36,11 @@ public: uint64_t GetVerboseLevel(); uint64_t GetAutohashTime(); + const char* GetOutputFile(); + uint64_t GetCallTimeout(); uint64_t GetNetRetry(); + uint64_t GetGiveUpLimit(); uint16_t GetHttpdPort(); diff --git a/jpsock.cpp b/jpsock.cpp index de01009..b072c21 100644 --- a/jpsock.cpp +++ b/jpsock.cpp @@ -11,6 +11,14 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see . + * + * Additional permission under GNU GPL version 3 section 7 + * + * If you modify this Program, or any covered work, by linking or combining + * it with OpenSSL (or a modified version of that library), containing parts + * covered by the terms of OpenSSL License and SSLeay License, the licensors + * of this Program grant you additional permission to convey the resulting work. + * */ #include @@ -23,6 +31,7 @@ #include "rapidjson/document.h" #include "jext.h" #include "socks.h" +#include "socket.h" #define AGENTID_STR "xmr-stak-amd/1.0" @@ -60,10 +69,6 @@ typedef GenericDocument, MemoryPoolAllocator<>, MemoryPoolAllocator<>> Me struct jpsock::opaque_private { - addrinfo *pSockAddr; - addrinfo *pAddrRoot; - SOCKET hSocket; - Value oCallValue; MemoryPoolAllocator<> callAllocator; @@ -79,8 +84,6 @@ struct jpsock::opaque_private jsonDoc(&recvAllocator, jpsock::iJsonMemSize, &parseAllocator), oCallRsp(nullptr) { - hSocket = INVALID_SOCKET; - pSockAddr = nullptr; } }; @@ -90,7 +93,7 @@ struct jpsock::opq_json_val opq_json_val(const Value* val) : val(val) {} }; -jpsock::jpsock(size_t id) : pool_id(id) +jpsock::jpsock(size_t id, bool tls) : pool_id(id) { sock_init(); @@ -100,6 +103,11 @@ jpsock::jpsock(size_t id) : pool_id(id) prv = new opaque_private(bJsonCallMem, bJsonRecvMem, bJsonParseMem); + if(tls) + sck = new tls_socket(this); + else + sck = new plain_socket(this); + oRecvThd = nullptr; bRunning = false; bLoggedIn = false; @@ -123,7 +131,7 @@ std::string&& jpsock::get_call_error() return std::move(prv->oCallRsp.sCallErr); } -inline bool jpsock::set_socket_error(const char* a) +bool jpsock::set_socket_error(const char* a) { if(!bHaveSocketError) { @@ -134,7 +142,7 @@ inline bool jpsock::set_socket_error(const char* a) return false; } -inline bool jpsock::set_socket_error(const char* a, const char* b) +bool jpsock::set_socket_error(const char* a, const char* b) { if(!bHaveSocketError) { @@ -150,6 +158,17 @@ inline bool jpsock::set_socket_error(const char* a, const char* b) return false; } +bool jpsock::set_socket_error(const char* a, size_t len) +{ + if(!bHaveSocketError) + { + bHaveSocketError = true; + sSocketError.assign(a, len); + } + + return false; +} + bool jpsock::set_socket_error_strerr(const char* a) { char sSockErrText[512]; @@ -191,12 +210,8 @@ void jpsock::jpsock_thread() bool jpsock::jpsock_thd_main() { - int ret = ::connect(prv->hSocket, prv->pSockAddr->ai_addr, (int)prv->pSockAddr->ai_addrlen); - freeaddrinfo(prv->pAddrRoot); - prv->pAddrRoot = nullptr; - - if (ret != 0) - return set_socket_error_strerr("CONNECT error: "); + if(!sck->connect()) + return false; executor::inst()->push_event(ex_event(EV_SOCK_READY, pool_id)); @@ -204,18 +219,16 @@ bool jpsock::jpsock_thd_main() size_t datalen = 0; while (true) { - ret = recv(prv->hSocket, buf + datalen, sizeof(buf) - datalen, 0); + int ret = sck->recv(buf + datalen, sizeof(buf) - datalen); - if(ret == 0) - return set_socket_error("RECEIVE error: socket closed"); - if(ret == SOCKET_ERROR || ret < 0) - return set_socket_error("RECEIVE error: "); + if(ret <= 0) + return false; datalen += ret; if (datalen >= sizeof(buf)) { - sock_close(prv->hSocket); + sck->close(false); return set_socket_error("RECEIVE error: data overflow"); } @@ -228,7 +241,7 @@ bool jpsock::jpsock_thd_main() if (!process_line(lnstart, lnlen)) { - sock_close(prv->hSocket); + sck->close(false); return false; } @@ -370,10 +383,12 @@ bool jpsock::process_pool_job(const opq_json_val* params) size_t target_slen = target->GetStringLength(); if(target_slen <= 8) { - uint32_t iTempInt; + uint32_t iTempInt = 0; char sTempStr[] = "00000000"; // Little-endian CPU FTW memcpy(sTempStr, target->GetString(), target_slen); - hex2bin(sTempStr, 8, (unsigned char*)&iTempInt); + if(!hex2bin(sTempStr, 8, (unsigned char*)&iTempInt) || iTempInt == 0) + return set_socket_error("PARSE error: Invalid target"); + oPoolJob.iTarget = iTempInt; } else @@ -390,100 +405,24 @@ bool jpsock::process_pool_job(const opq_json_val* params) bool jpsock::connect(const char* sAddr, std::string& sConnectError) { - if(prv_connect(sAddr)) + bHaveSocketError = false; + sSocketError.clear(); + iJobDiff = 0; + + if(sck->set_hostname(sAddr)) + { + bRunning = true; + oRecvThd = new std::thread(&jpsock::jpsock_thread, this); return true; + } sConnectError = std::move(sSocketError); return false; } -bool jpsock::prv_connect(const char* sAddr) -{ - char sAddrMb[256]; - char *sTmp, *sPort; - - bHaveSocketError = false; - sSocketError.clear(); - iJobDiff = 0; - - size_t ln = strlen(sAddr); - if (ln >= sizeof(sAddrMb)) - return set_socket_error("CONNECT error: Pool address overflow."); - - memcpy(sAddrMb, sAddr, ln); - sAddrMb[ln] = '\0'; - - if ((sTmp = strstr(sAddrMb, "//")) != nullptr) - memmove(sAddrMb, sTmp, strlen(sTmp) + 1); - - if ((sPort = strchr(sAddrMb, ':')) == nullptr) - return set_socket_error("CONNECT error: Pool port number not specified, please use format :."); - - sPort[0] = '\0'; - sPort++; - - addrinfo hints = { 0 }; - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - hints.ai_protocol = IPPROTO_TCP; - - prv->pAddrRoot = nullptr; - int err; - if ((err = getaddrinfo(sAddrMb, sPort, &hints, &prv->pAddrRoot)) != 0) - return set_socket_error_strerr("CONNECT error: GetAddrInfo: ", err); - - addrinfo *ptr = prv->pAddrRoot; - addrinfo *ipv4 = nullptr, *ipv6 = nullptr; - - while (ptr != nullptr) - { - if (ptr->ai_family == AF_INET) - ipv4 = ptr; - if (ptr->ai_family == AF_INET6) - ipv6 = ptr; - ptr = ptr->ai_next; - } - - if (ipv4 == nullptr && ipv6 == nullptr) - { - freeaddrinfo(prv->pAddrRoot); - prv->pAddrRoot = nullptr; - return set_socket_error("CONNECT error: I found some DNS records but no IPv4 or IPv6 addresses."); - } - else if (ipv4 != nullptr && ipv6 == nullptr) - prv->pSockAddr = ipv4; - else if (ipv4 == nullptr && ipv6 != nullptr) - prv->pSockAddr = ipv6; - else if (ipv4 != nullptr && ipv6 != nullptr) - { - if(jconf::inst()->PreferIpv4()) - prv->pSockAddr = ipv4; - else - prv->pSockAddr = ipv6; - } - - prv->hSocket = socket(prv->pSockAddr->ai_family, prv->pSockAddr->ai_socktype, prv->pSockAddr->ai_protocol); - - if (prv->hSocket == INVALID_SOCKET) - { - freeaddrinfo(prv->pAddrRoot); - prv->pAddrRoot = nullptr; - return set_socket_error_strerr("CONNECT error: Socket creation failed "); - } - - bRunning = true; - oRecvThd = new std::thread(&jpsock::jpsock_thread, this); - - return true; -} - void jpsock::disconnect() { - if(prv->hSocket != INVALID_SOCKET) - { - sock_close(prv->hSocket); - prv->hSocket = INVALID_SOCKET; - } + sck->close(false); if(oRecvThd != nullptr) { @@ -491,6 +430,8 @@ void jpsock::disconnect() delete oRecvThd; oRecvThd = nullptr; } + + sck->close(true); } bool jpsock::cmd_ret_wait(const char* sPacket, opq_json_val& poResult) @@ -505,18 +446,10 @@ bool jpsock::cmd_ret_wait(const char* sPacket, opq_json_val& poResult) prv->oCallRsp = call_rsp(&prv->oCallValue); mlock.unlock(); - int pos = 0, slen = strlen(sPacket); - while (pos != slen) + if(!sck->send(sPacket)) { - int ret = send(prv->hSocket, sPacket + pos, slen - pos, 0); - if (ret == SOCKET_ERROR) - { - set_socket_error_strerr("SEND error: "); - disconnect(); //This will join the other thread; - return false; - } - else - pos += ret; + disconnect(); //This will join the other thread; + return false; } //Success is true if the server approves, result is true if there was no socket error diff --git a/jpsock.h b/jpsock.h index ad1545e..0b66866 100644 --- a/jpsock.h +++ b/jpsock.h @@ -19,11 +19,12 @@ We parse it in-situ in the network buffer, after that we copy it to a std::string. Executor will move the buffer via an r-value ref. */ +class base_socket; class jpsock { public: - jpsock(size_t id); + jpsock(size_t id, bool tls); ~jpsock(); bool connect(const char* sAddr, std::string& sConnectError); @@ -51,6 +52,13 @@ public: bool get_current_job(pool_job& job); size_t pool_id; + + bool set_socket_error(const char* a); + bool set_socket_error(const char* a, const char* b); + bool set_socket_error(const char* a, size_t len); + bool set_socket_error_strerr(const char* a); + bool set_socket_error_strerr(const char* a, int res); + private: std::atomic bRunning; std::atomic bLoggedIn; @@ -78,13 +86,6 @@ private: std::string sSocketError; std::atomic bHaveSocketError; - bool set_socket_error(const char* a); - bool set_socket_error(const char* a, const char* b); - bool set_socket_error_strerr(const char* a); - bool set_socket_error_strerr(const char* a, int res); - - bool prv_connect(const char* sAddr); - std::mutex call_mutex; std::condition_variable call_cond; std::thread* oRecvThd; @@ -93,5 +94,6 @@ private: pool_job oCurrentJob; opaque_private* prv; + base_socket* sck; }; diff --git a/minethd.cpp b/minethd.cpp index 7665b33..181421a 100644 --- a/minethd.cpp +++ b/minethd.cpp @@ -11,6 +11,14 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see . + * + * Additional permission under GNU GPL version 3 section 7 + * + * If you modify this Program, or any covered work, by linking or combining + * it with OpenSSL (or a modified version of that library), containing parts + * covered by the terms of OpenSSL License and SSLeay License, the licensors + * of this Program grant you additional permission to convey the resulting work. + * */ #include diff --git a/socket.cpp b/socket.cpp new file mode 100644 index 0000000..b472feb --- /dev/null +++ b/socket.cpp @@ -0,0 +1,358 @@ +/* + * 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 + * 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 . + * + * Additional permission under GNU GPL version 3 section 7 + * + * If you modify this Program, or any covered work, by linking or combining + * it with OpenSSL (or a modified version of that library), containing parts + * covered by the terms of OpenSSL License and SSLeay License, the licensors + * of this Program grant you additional permission to convey the resulting work. + * + */ + +#include "socket.h" +#include "jpsock.h" +#include "jconf.h" +#include "console.h" +#include "executor.h" + +#include +#include +#include + +#ifndef OPENSSL_THREADS +#error OpenSSL was compiled without thread support +#endif + +plain_socket::plain_socket(jpsock* err_callback) : pCallback(err_callback) +{ + hSocket = INVALID_SOCKET; + pSockAddr = nullptr; +} + +bool plain_socket::set_hostname(const char* sAddr) +{ + char sAddrMb[256]; + char *sTmp, *sPort; + + size_t ln = strlen(sAddr); + if (ln >= sizeof(sAddrMb)) + return pCallback->set_socket_error("CONNECT error: Pool address overflow."); + + memcpy(sAddrMb, sAddr, ln); + sAddrMb[ln] = '\0'; + + if ((sTmp = strstr(sAddrMb, "//")) != nullptr) + memmove(sAddrMb, sTmp, strlen(sTmp) + 1); + + if ((sPort = strchr(sAddrMb, ':')) == nullptr) + return pCallback->set_socket_error("CONNECT error: Pool port number not specified, please use format :."); + + sPort[0] = '\0'; + sPort++; + + addrinfo hints = { 0 }; + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + + pAddrRoot = nullptr; + int err; + if ((err = getaddrinfo(sAddrMb, sPort, &hints, &pAddrRoot)) != 0) + return pCallback->set_socket_error_strerr("CONNECT error: GetAddrInfo: ", err); + + addrinfo *ptr = pAddrRoot; + addrinfo *ipv4 = nullptr, *ipv6 = nullptr; + + while (ptr != nullptr) + { + if (ptr->ai_family == AF_INET) + ipv4 = ptr; + if (ptr->ai_family == AF_INET6) + ipv6 = ptr; + ptr = ptr->ai_next; + } + + if (ipv4 == nullptr && ipv6 == nullptr) + { + freeaddrinfo(pAddrRoot); + pAddrRoot = nullptr; + return pCallback->set_socket_error("CONNECT error: I found some DNS records but no IPv4 or IPv6 addresses."); + } + else if (ipv4 != nullptr && ipv6 == nullptr) + pSockAddr = ipv4; + else if (ipv4 == nullptr && ipv6 != nullptr) + pSockAddr = ipv6; + else if (ipv4 != nullptr && ipv6 != nullptr) + { + if(jconf::inst()->PreferIpv4()) + pSockAddr = ipv4; + else + pSockAddr = ipv6; + } + + hSocket = socket(pSockAddr->ai_family, pSockAddr->ai_socktype, pSockAddr->ai_protocol); + + if (hSocket == INVALID_SOCKET) + { + freeaddrinfo(pAddrRoot); + pAddrRoot = nullptr; + return pCallback->set_socket_error_strerr("CONNECT error: Socket creation failed "); + } + + return true; +} + +bool plain_socket::connect() +{ + int ret = ::connect(hSocket, pSockAddr->ai_addr, (int)pSockAddr->ai_addrlen); + + freeaddrinfo(pAddrRoot); + pAddrRoot = nullptr; + + if (ret != 0) + return pCallback->set_socket_error_strerr("CONNECT error: "); + else + return true; +} + +int plain_socket::recv(char* buf, unsigned int len) +{ + int ret = ::recv(hSocket, buf, len, 0); + + if(ret == 0) + pCallback->set_socket_error("RECEIVE error: socket closed"); + if(ret == SOCKET_ERROR || ret < 0) + pCallback->set_socket_error_strerr("RECEIVE error: "); + + return ret; +} + +bool plain_socket::send(const char* buf) +{ + int pos = 0, slen = strlen(buf); + while (pos != slen) + { + int ret = ::send(hSocket, buf + pos, slen - pos, 0); + if (ret == SOCKET_ERROR) + { + pCallback->set_socket_error_strerr("SEND error: "); + return false; + } + else + pos += ret; + } + + return true; +} + +void plain_socket::close(bool free) +{ + if(hSocket != INVALID_SOCKET) + { + sock_close(hSocket); + hSocket = INVALID_SOCKET; + } +} + +tls_socket::tls_socket(jpsock* err_callback) : pCallback(err_callback) +{ +} + +void tls_socket::print_error() +{ + BIO* err_bio = BIO_new(BIO_s_mem()); + ERR_print_errors(err_bio); + + char *buf = nullptr; + size_t len = BIO_get_mem_data(err_bio, &buf); + + pCallback->set_socket_error(buf, len); + + BIO_free(err_bio); +} + +void tls_socket::init_ctx() +{ + const SSL_METHOD* method = SSLv23_method(); + + if(method == nullptr) + return; + + ctx = SSL_CTX_new(method); + if(ctx == nullptr) + return; + + if(jconf::inst()->TlsSecureAlgos()) + { + SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_COMPRESSION); + } +} + +bool tls_socket::set_hostname(const char* sAddr) +{ + if(ctx == nullptr) + { + init_ctx(); + if(ctx == nullptr) + { + print_error(); + return false; + } + } + + if((bio = BIO_new_ssl_connect(ctx)) == nullptr) + { + print_error(); + return false; + } + + if(BIO_set_conn_hostname(bio, sAddr) != 1) + { + print_error(); + return false; + } + + BIO_get_ssl(bio, &ssl); + if(ssl == nullptr) + { + print_error(); + return false; + } + + if(jconf::inst()->TlsSecureAlgos()) + { + if(SSL_set_cipher_list(ssl, "HIGH:!aNULL:!kRSA:!PSK:!SRP:!MD5:!RC4:!SHA1") != 1) + { + print_error(); + return false; + } + } + + return true; +} + +bool tls_socket::connect() +{ + if(BIO_do_connect(bio) != 1) + { + print_error(); + return false; + } + + if(BIO_do_handshake(bio) != 1) + { + print_error(); + return false; + } + + /* Step 1: verify a server certificate was presented during the negotiation */ + X509* cert = SSL_get_peer_certificate(ssl); + if(cert == nullptr) + { + print_error(); + return false; + } + + const EVP_MD* digest; + unsigned char md[EVP_MAX_MD_SIZE]; + unsigned int dlen; + + digest = EVP_get_digestbyname("sha256"); + if(digest == nullptr) + { + print_error(); + return false; + } + + if(X509_digest(cert, digest, md, &dlen) != 1) + { + X509_free(cert); + print_error(); + return false; + } + + if(pCallback->pool_id != executor::dev_pool_id) + { + //Base64 encode digest + BIO *bmem, *b64; + b64 = BIO_new(BIO_f_base64()); + bmem = BIO_new(BIO_s_mem()); + + BIO_puts(bmem, "SHA256:"); + b64 = BIO_push(b64, bmem); + BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); + BIO_write(b64, md, dlen); + BIO_flush(b64); + + const char* conf_md = jconf::inst()->GetTlsFingerprint(); + char *b64_md = nullptr; + size_t b64_len = BIO_get_mem_data(bmem, &b64_md); + + if(strlen(conf_md) == 0) + { + printer::inst()->print_msg(L1, "Server fingerprint: %.*s", (int)b64_len, b64_md); + } + else if(strncmp(b64_md, conf_md, b64_len) != 0) + { + printer::inst()->print_msg(L0, "FINGERPRINT FAILED CHECK: %.*s was given, %s was configured", + (int)b64_len, b64_md, conf_md); + + pCallback->set_socket_error("FINGERPRINT FAILED CHECK"); + BIO_free_all(b64); + X509_free(cert); + return false; + } + + BIO_free_all(b64); + } + + X509_free(cert); + return true; +} + +int tls_socket::recv(char* buf, unsigned int len) +{ + int ret = BIO_read(bio, buf, len); + + if(ret == 0) + pCallback->set_socket_error("RECEIVE error: socket closed"); + if(ret < 0) + print_error(); + + return ret; +} + +bool tls_socket::send(const char* buf) +{ + return BIO_puts(bio, buf) > 0; +} + +void tls_socket::close(bool free) +{ + if(bio == nullptr || ssl == nullptr) + return; + + if(!free) + { + sock_close(BIO_get_fd(bio, nullptr)); + } + else + { + BIO_free_all(bio); + ssl = nullptr; + bio = nullptr; + } +} + diff --git a/socket.h b/socket.h new file mode 100644 index 0000000..94bbf03 --- /dev/null +++ b/socket.h @@ -0,0 +1,57 @@ +#pragma once +#include "socks.h" +class jpsock; + +class base_socket +{ +public: + virtual bool set_hostname(const char* sAddr) = 0; + virtual bool connect() = 0; + virtual int recv(char* buf, unsigned int len) = 0; + virtual bool send(const char* buf) = 0; + virtual void close(bool free) = 0; +}; + +class plain_socket : public base_socket +{ +public: + plain_socket(jpsock* err_callback); + + bool set_hostname(const char* sAddr); + bool connect(); + int recv(char* buf, unsigned int len); + bool send(const char* buf); + void close(bool free); + +private: + jpsock* pCallback; + addrinfo *pSockAddr; + addrinfo *pAddrRoot; + SOCKET hSocket; +}; + +typedef struct ssl_ctx_st SSL_CTX; +typedef struct bio_st BIO; +typedef struct ssl_st SSL; + +class tls_socket : public base_socket +{ +public: + tls_socket(jpsock* err_callback); + + bool set_hostname(const char* sAddr); + bool connect(); + int recv(char* buf, unsigned int len); + bool send(const char* buf); + void close(bool free); + +private: + void init_ctx(); + void print_error(); + + jpsock* pCallback; + + SSL_CTX* ctx = nullptr; + BIO* bio = nullptr; + SSL* ssl = nullptr; +}; diff --git a/xmr-stak-amd.cbp b/xmr-stak-amd.cbp index adc183a..7e971a3 100644 --- a/xmr-stak-amd.cbp +++ b/xmr-stak-amd.cbp @@ -68,6 +68,8 @@ + +