From 1a8323ef6f366058803ce6590b8167ec59928872 Mon Sep 17 00:00:00 2001 From: fireice-uk Date: Fri, 27 Jan 2017 22:34:28 +0000 Subject: [PATCH] Inital OpenSSL integration --- cli-miner.cpp | 5 + jpsock.cpp | 153 ++++++------------------- jpsock.h | 16 +-- socket.cpp | 288 +++++++++++++++++++++++++++++++++++++++++++++++ socket.h | 57 ++++++++++ xmr-stak-cpu.cbp | 4 + 6 files changed, 400 insertions(+), 123 deletions(-) create mode 100644 socket.cpp create mode 100644 socket.h diff --git a/cli-miner.cpp b/cli-miner.cpp index 4a8454a..3e68610 100644 --- a/cli-miner.cpp +++ b/cli-miner.cpp @@ -24,6 +24,8 @@ #include #include +#include + //Do a press any key for the windows folk. *insert any key joke here* #ifdef _WIN32 void win_exit() @@ -43,6 +45,9 @@ void do_benchmark(); int main(int argc, char *argv[]) { + SSL_library_init(); + SSL_load_error_strings(); + const char* sFilename = "config.txt"; bool benchmark_mode = false; diff --git a/jpsock.cpp b/jpsock.cpp index 29f91d4..8128764 100644 --- a/jpsock.cpp +++ b/jpsock.cpp @@ -23,6 +23,7 @@ #include "rapidjson/document.h" #include "jext.h" #include "socks.h" +#include "socket.h" #define AGENTID_STR "xmr-stak-cpu/1.0" @@ -60,10 +61,6 @@ typedef GenericDocument, MemoryPoolAllocator<>, MemoryPoolAllocator<>> Me struct jpsock::opaque_private { - addrinfo *pSockAddr; - addrinfo *pAddrRoot; - SOCKET hSocket; - Value oCallValue; MemoryPoolAllocator<> callAllocator; @@ -79,8 +76,6 @@ struct jpsock::opaque_private jsonDoc(&recvAllocator, jpsock::iJsonMemSize, &parseAllocator), oCallRsp(nullptr) { - hSocket = INVALID_SOCKET; - pSockAddr = nullptr; } }; @@ -100,6 +95,9 @@ jpsock::jpsock(size_t id) : pool_id(id) prv = new opaque_private(bJsonCallMem, bJsonRecvMem, bJsonParseMem); + //sck = new plain_socket(this); + sck = new tls_socket(this); + oRecvThd = nullptr; bRunning = false; bLoggedIn = false; @@ -150,6 +148,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 +200,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 +209,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 +231,7 @@ bool jpsock::jpsock_thd_main() if (!process_line(lnstart, lnlen)) { - sock_close(prv->hSocket); + sck->close(false); return false; } @@ -396,100 +399,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) { @@ -497,6 +424,8 @@ void jpsock::disconnect() delete oRecvThd; oRecvThd = nullptr; } + + sck->close(true); } bool jpsock::cmd_ret_wait(const char* sPacket, opq_json_val& poResult) @@ -511,18 +440,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 40270be..75bae26 100644 --- a/jpsock.h +++ b/jpsock.h @@ -19,6 +19,7 @@ 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 { @@ -50,6 +51,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; @@ -77,13 +85,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; @@ -92,5 +93,6 @@ private: pool_job oCurrentJob; opaque_private* prv; + base_socket* sck; }; diff --git a/socket.cpp b/socket.cpp new file mode 100644 index 0000000..86069dd --- /dev/null +++ b/socket.cpp @@ -0,0 +1,288 @@ +#include "socket.h" +#include "jpsock.h" +#include "jconf.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; + + /* Cannot fail ??? */ + //SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, verify_callback); + + /* Cannot fail ??? */ + //SSL_CTX_set_verify_depth(ctx, 4); + + /* Cannot fail ??? */ + SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | 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(SSL_set_cipher_list(ssl, "HIGH:!aNULL:!kRSA:!PSK:!SRP:!MD5:!RC4") != 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) { X509_free(cert); } /* Free immediately */ + + if(cert == nullptr) + { + print_error(); + return false; + } + + /* Step 2: verify the result of chain verification */ + /* Verification performed according to RFC 4158 */ + //res = SSL_get_verify_result(ssl); + //if(!(X509_V_OK == res)) handleFailure(); + + /* Step 3: hostname verification */ + /* An exercise left to the reader */ + + 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) + { + SSL_shutdown(ssl); + } + 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-cpu.cbp b/xmr-stak-cpu.cbp index 3a62cc8..cd0f77d 100644 --- a/xmr-stak-cpu.cbp +++ b/xmr-stak-cpu.cbp @@ -65,6 +65,8 @@ + + @@ -147,6 +149,8 @@ + +