diff --git a/cmake/OpenSSL.cmake b/cmake/OpenSSL.cmake index bcdea0c6..ea46081c 100644 --- a/cmake/OpenSSL.cmake +++ b/cmake/OpenSSL.cmake @@ -13,6 +13,10 @@ if (WITH_TLS) if (OPENSSL_FOUND) set(TLS_SOURCES src/base/net/stratum/Tls.h src/base/net/stratum/Tls.cpp) include_directories(${OPENSSL_INCLUDE_DIR}) + + if (WITH_HTTP) + set(TLS_SOURCES ${TLS_SOURCES} src/base/net/http/HttpsClient.h src/base/net/http/HttpsClient.cpp) + endif() else() message(FATAL_ERROR "OpenSSL NOT found: use `-DWITH_TLS=OFF` to build without TLS support") endif() diff --git a/src/base/net/http/HttpClient.cpp b/src/base/net/http/HttpClient.cpp index d33daf2c..a512989c 100644 --- a/src/base/net/http/HttpClient.cpp +++ b/src/base/net/http/HttpClient.cpp @@ -43,9 +43,9 @@ static const char *kCRLF = "\r\n"; class WriteBaton : public Baton { public: - inline WriteBaton(const std::stringstream &ss, std::string &&body) : + inline WriteBaton(const std::string &header, std::string &&body) : m_body(body), - m_header(ss.str()) + m_header(header) { bufs[0].len = m_header.size(); bufs[0].base = const_cast(m_header.c_str()); @@ -77,9 +77,9 @@ private: } // namespace xmrig -xmrig::HttpClient::HttpClient(const String &host, uint16_t port, int method, const String &url, IHttpListener *listener, const char *data, size_t size) : +xmrig::HttpClient::HttpClient(int method, const String &url, IHttpListener *listener, const char *data, size_t size) : HttpContext(HTTP_RESPONSE, listener), - m_port(port) + m_port(0) { this->method = method; this->url = url; @@ -89,8 +89,6 @@ xmrig::HttpClient::HttpClient(const String &host, uint16_t port, int method, con } m_dns = new Dns(this); - - status = m_dns->resolve(host); } @@ -100,6 +98,20 @@ xmrig::HttpClient::~HttpClient() } +bool xmrig::HttpClient::connect(const String &host, uint16_t port) +{ + m_port = port; + + return m_dns->resolve(host); +} + + +const xmrig::String &xmrig::HttpClient::host() const +{ + return m_dns->host(); +} + + void xmrig::HttpClient::onResolved(const Dns &dns, int status) { this->status = status; @@ -119,7 +131,7 @@ void xmrig::HttpClient::onResolved(const Dns &dns, int status) } -void xmrig::HttpClient::send() +void xmrig::HttpClient::handshake() { headers.insert({ "Host", m_dns->host().data() }); headers.insert({ "Connection", "close" }); @@ -140,7 +152,21 @@ void xmrig::HttpClient::send() headers.clear(); - WriteBaton *baton = new WriteBaton(ss, std::move(body)); + write(ss.str()); +} + + +void xmrig::HttpClient::read(const char *data, size_t size) +{ + if (parse(data, size) < size) { + close(); + } +} + + +void xmrig::HttpClient::write(const std::string &header) +{ + WriteBaton *baton = new WriteBaton(header, std::move(body)); uv_write(&baton->req, stream(), baton->bufs, baton->count(), WriteBaton::onWrite); } @@ -177,12 +203,7 @@ void xmrig::HttpClient::onConnect(uv_connect_t *req, int status) HttpClient *client = static_cast(tcp->data); if (nread >= 0) { - const size_t size = static_cast(nread); - const size_t parsed = client->parse(buf->base, size); - - if (parsed < size) { - client->close(); - } + client->read(buf->base, static_cast(nread)); } else { if (nread != UV_EOF) { LOG_ERR("[%s:%d] read error: \"%s\"", client->m_dns->host().data(), client->m_port, uv_strerror(static_cast(nread))); @@ -194,5 +215,5 @@ void xmrig::HttpClient::onConnect(uv_connect_t *req, int status) delete [] buf->base; }); - client->send(); + client->handshake(); } diff --git a/src/base/net/http/HttpClient.h b/src/base/net/http/HttpClient.h index c31b9a3e..b92c4733 100644 --- a/src/base/net/http/HttpClient.h +++ b/src/base/net/http/HttpClient.h @@ -41,15 +41,22 @@ class String; class HttpClient : public HttpContext, public IDnsListener { public: - HttpClient(const String &host, uint16_t port, int method, const String &url, IHttpListener *listener, const char *data = nullptr, size_t size = 0); - ~HttpClient(); + HttpClient(int method, const String &url, IHttpListener *listener, const char *data = nullptr, size_t size = 0); + ~HttpClient() override; + + inline uint16_t port() const { return m_port; } + + bool connect(const String &host, uint16_t port); + const String &host() const; protected: - void onResolved(const Dns &dns, int status); + void onResolved(const Dns &dns, int status) override; + + virtual void handshake(); + virtual void read(const char *data, size_t size); + virtual void write(const std::string &header); private: - void send(); - static void onConnect(uv_connect_t *req, int status); Dns *m_dns; diff --git a/src/base/net/http/HttpsClient.cpp b/src/base/net/http/HttpsClient.cpp new file mode 100644 index 00000000..8fd26a2e --- /dev/null +++ b/src/base/net/http/HttpsClient.cpp @@ -0,0 +1,150 @@ +/* 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 2014-2019 heapwolf + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 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 +#include + + +#include "base/net/http/HttpsClient.h" +#include "base/tools/String.h" + + +xmrig::HttpsClient::HttpsClient(int method, const String &url, IHttpListener *listener, const char *data, size_t size) : + HttpClient(method, url, listener, data, size), + m_ready(false), + m_buf(), + 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); +} + + +xmrig::HttpsClient::~HttpsClient() +{ + if (m_ctx) { + SSL_CTX_free(m_ctx); + } + + if (m_ssl) { + SSL_free(m_ssl); + } +} + + +void xmrig::HttpsClient::handshake() +{ + m_ssl = SSL_new(m_ctx); + assert(m_ssl != nullptr); + + if (!m_ssl) { + return; + } + + SSL_set_connect_state(m_ssl); + SSL_set_bio(m_ssl, m_readBio, m_writeBio); + SSL_set_tlsext_host_name(m_ssl, host().data()); + + SSL_do_handshake(m_ssl); + + flush(); +} + + +void xmrig::HttpsClient::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) { + flush(); + } else if (rc == 1) { + X509 *cert = SSL_get_peer_certificate(m_ssl); + if (!verify(cert)) { + X509_free(cert); + return close(); + } + + X509_free(cert); + m_ready = true; + + HttpClient::handshake(); + } + + return; + } + + int bytes_read = 0; + while ((bytes_read = SSL_read(m_ssl, m_buf, sizeof(m_buf))) > 0) { + HttpClient::read(m_buf, static_cast(bytes_read)); + } +} + + +void xmrig::HttpsClient::write(const std::string &header) +{ + SSL_write(m_ssl, (header + body).c_str(), header.size() + body.size()); + flush(); +} + + +bool xmrig::HttpsClient::verify(X509 *cert) const +{ + return cert != nullptr; +} + + +void xmrig::HttpsClient::flush() +{ + uv_buf_t buf; + buf.len = BIO_get_mem_data(m_writeBio, &buf.base); + + if (buf.len == 0) { + return; + } + + bool result = false; + if (uv_is_writable(stream())) { + result = uv_try_write(stream(), &buf, 1) == buf.len; + + if (!result) { + close(); + } + } + + (void) BIO_reset(m_writeBio); +} diff --git a/src/base/net/http/HttpsClient.h b/src/base/net/http/HttpsClient.h new file mode 100644 index 00000000..76b90c66 --- /dev/null +++ b/src/base/net/http/HttpsClient.h @@ -0,0 +1,70 @@ +/* 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 2014-2019 heapwolf + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 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_HTTPSCLIENT_H +#define XMRIG_HTTPSCLIENT_H + + +typedef struct bio_st BIO; +typedef struct ssl_ctx_st SSL_CTX; +typedef struct ssl_st SSL; +typedef struct x509_st X509; + + +#include "base/net/http/HttpClient.h" + + +namespace xmrig { + + +class HttpsClient : public HttpClient +{ +public: + HttpsClient(int method, const String &url, IHttpListener *listener, const char *data = nullptr, size_t size = 0); + ~HttpsClient() override; + +protected: + void handshake() override; + void read(const char *data, size_t size) override; + void write(const std::string &header) override; + +private: + bool verify(X509 *cert) const; + void flush(); + + BIO *m_readBio; + BIO *m_writeBio; + bool m_ready; + char m_buf[1024 * 2]; + SSL *m_ssl; + SSL_CTX *m_ctx; +}; + + +} // namespace xmrig + + +#endif // XMRIG_HTTPSCLIENT_H