diff --git a/src/Makefile.test.include b/src/Makefile.test.include index b3084307e1..32afc7163e 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -319,6 +319,7 @@ test_fuzz_fuzz_SOURCES = \ test/fuzz/strprintf.cpp \ test/fuzz/system.cpp \ test/fuzz/timedata.cpp \ + test/fuzz/torcontrol.cpp \ test/fuzz/transaction.cpp \ test/fuzz/tx_in.cpp \ test/fuzz/tx_out.cpp \ diff --git a/src/test/fuzz/torcontrol.cpp b/src/test/fuzz/torcontrol.cpp new file mode 100644 index 0000000000..b7a42ea7f4 --- /dev/null +++ b/src/test/fuzz/torcontrol.cpp @@ -0,0 +1,79 @@ +// Copyright (c) 2020 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include +#include +#include +#include + +#include +#include +#include + +class DummyTorControlConnection : public TorControlConnection +{ +public: + DummyTorControlConnection() : TorControlConnection{nullptr} + { + } + + bool Connect(const std::string&, const ConnectionCB&, const ConnectionCB&) + { + return true; + } + + void Disconnect() + { + } + + bool Command(const std::string&, const ReplyHandlerCB&) + { + return true; + } +}; + +void initialize_torcontrol() +{ + static const auto testing_setup = MakeNoLogFileContext<>(); +} + +FUZZ_TARGET_INIT(torcontrol, initialize_torcontrol) +{ + FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; + + TorController tor_controller; + while (fuzzed_data_provider.ConsumeBool()) { + TorControlReply tor_control_reply; + CallOneOf( + fuzzed_data_provider, + [&] { + tor_control_reply.code = 250; + }, + [&] { + tor_control_reply.code = 510; + }, + [&] { + tor_control_reply.code = fuzzed_data_provider.ConsumeIntegral(); + }); + tor_control_reply.lines = ConsumeRandomLengthStringVector(fuzzed_data_provider); + if (tor_control_reply.lines.empty()) { + break; + } + DummyTorControlConnection dummy_tor_control_connection; + CallOneOf( + fuzzed_data_provider, + [&] { + tor_controller.add_onion_cb(dummy_tor_control_connection, tor_control_reply); + }, + [&] { + tor_controller.auth_cb(dummy_tor_control_connection, tor_control_reply); + }, + [&] { + tor_controller.authchallenge_cb(dummy_tor_control_connection, tor_control_reply); + }, + [&] { + tor_controller.protocolinfo_cb(dummy_tor_control_connection, tor_control_reply); + }); + } +} diff --git a/src/torcontrol.cpp b/src/torcontrol.cpp index c60506fb5e..ca9eef212f 100644 --- a/src/torcontrol.cpp +++ b/src/torcontrol.cpp @@ -52,77 +52,6 @@ static const int MAX_LINE_LENGTH = 100000; /****** Low-level TorControlConnection ********/ -/** Reply from Tor, can be single or multi-line */ -class TorControlReply -{ -public: - TorControlReply() { Clear(); } - - int code; - std::vector lines; - - void Clear() - { - code = 0; - lines.clear(); - } -}; - -/** Low-level handling for Tor control connection. - * Speaks the SMTP-like protocol as defined in torspec/control-spec.txt - */ -class TorControlConnection -{ -public: - typedef std::function ConnectionCB; - typedef std::function ReplyHandlerCB; - - /** Create a new TorControlConnection. - */ - explicit TorControlConnection(struct event_base *base); - ~TorControlConnection(); - - /** - * Connect to a Tor control port. - * tor_control_center is address of the form host:port. - * connected is the handler that is called when connection is successfully established. - * disconnected is a handler that is called when the connection is broken. - * Return true on success. - */ - bool Connect(const std::string& tor_control_center, const ConnectionCB& connected, const ConnectionCB& disconnected); - - /** - * Disconnect from Tor control port. - */ - void Disconnect(); - - /** Send a command, register a handler for the reply. - * A trailing CRLF is automatically added. - * Return true on success. - */ - bool Command(const std::string &cmd, const ReplyHandlerCB& reply_handler); - - /** Response handlers for async replies */ - boost::signals2::signal async_handler; -private: - /** Callback when ready for use */ - std::function connected; - /** Callback when connection lost */ - std::function disconnected; - /** Libevent event base */ - struct event_base *base; - /** Connection to control socket */ - struct bufferevent *b_conn; - /** Message being received */ - TorControlReply message; - /** Response handlers */ - std::deque reply_handlers; - - /** Libevent handlers: internal */ - static void readcb(struct bufferevent *bev, void *ctx); - static void eventcb(struct bufferevent *bev, short what, void *ctx); -}; - TorControlConnection::TorControlConnection(struct event_base *_base): base(_base), b_conn(nullptr) { @@ -366,55 +295,6 @@ std::map ParseTorReplyMapping(const std::string &s) return mapping; } -/****** Bitcoin specific TorController implementation ********/ - -/** Controller that connects to Tor control socket, authenticate, then create - * and maintain an ephemeral onion service. - */ -class TorController -{ -public: - TorController(struct event_base* base, const std::string& tor_control_center, const CService& target); - ~TorController(); - - /** Get name of file to store private key in */ - fs::path GetPrivateKeyFile(); - - /** Reconnect, after getting disconnected */ - void Reconnect(); -private: - struct event_base* base; - const std::string m_tor_control_center; - TorControlConnection conn; - std::string private_key; - std::string service_id; - bool reconnect; - struct event *reconnect_ev; - float reconnect_timeout; - CService service; - const CService m_target; - /** Cookie for SAFECOOKIE auth */ - std::vector cookie; - /** ClientNonce for SAFECOOKIE auth */ - std::vector clientNonce; - - /** Callback for ADD_ONION result */ - void add_onion_cb(TorControlConnection& conn, const TorControlReply& reply); - /** Callback for AUTHENTICATE result */ - void auth_cb(TorControlConnection& conn, const TorControlReply& reply); - /** Callback for AUTHCHALLENGE result */ - void authchallenge_cb(TorControlConnection& conn, const TorControlReply& reply); - /** Callback for PROTOCOLINFO result */ - void protocolinfo_cb(TorControlConnection& conn, const TorControlReply& reply); - /** Callback after successful connection */ - void connected_cb(TorControlConnection& conn); - /** Callback after connection lost or failed connection attempt */ - void disconnected_cb(TorControlConnection& conn); - - /** Callback for reconnect timer */ - static void reconnect_cb(evutil_socket_t fd, short what, void *arg); -}; - TorController::TorController(struct event_base* _base, const std::string& tor_control_center, const CService& target): base(_base), m_tor_control_center(tor_control_center), conn(base), reconnect(true), reconnect_ev(0), diff --git a/src/torcontrol.h b/src/torcontrol.h index ad5dada39b..f5dc9d413f 100644 --- a/src/torcontrol.h +++ b/src/torcontrol.h @@ -8,7 +8,19 @@ #ifndef BITCOIN_TORCONTROL_H #define BITCOIN_TORCONTROL_H +#include +#include + +#include + +#include +#include + +#include +#include +#include #include +#include class CService; @@ -21,4 +33,128 @@ void StopTorControl(); CService DefaultOnionServiceTarget(); +/** Reply from Tor, can be single or multi-line */ +class TorControlReply +{ +public: + TorControlReply() { Clear(); } + + int code; + std::vector lines; + + void Clear() + { + code = 0; + lines.clear(); + } +}; + +/** Low-level handling for Tor control connection. + * Speaks the SMTP-like protocol as defined in torspec/control-spec.txt + */ +class TorControlConnection +{ +public: + typedef std::function ConnectionCB; + typedef std::function ReplyHandlerCB; + + /** Create a new TorControlConnection. + */ + explicit TorControlConnection(struct event_base *base); + ~TorControlConnection(); + + /** + * Connect to a Tor control port. + * tor_control_center is address of the form host:port. + * connected is the handler that is called when connection is successfully established. + * disconnected is a handler that is called when the connection is broken. + * Return true on success. + */ + bool Connect(const std::string& tor_control_center, const ConnectionCB& connected, const ConnectionCB& disconnected); + + /** + * Disconnect from Tor control port. + */ + void Disconnect(); + + /** Send a command, register a handler for the reply. + * A trailing CRLF is automatically added. + * Return true on success. + */ + bool Command(const std::string &cmd, const ReplyHandlerCB& reply_handler); + + /** Response handlers for async replies */ + boost::signals2::signal async_handler; +private: + /** Callback when ready for use */ + std::function connected; + /** Callback when connection lost */ + std::function disconnected; + /** Libevent event base */ + struct event_base *base; + /** Connection to control socket */ + struct bufferevent *b_conn; + /** Message being received */ + TorControlReply message; + /** Response handlers */ + std::deque reply_handlers; + + /** Libevent handlers: internal */ + static void readcb(struct bufferevent *bev, void *ctx); + static void eventcb(struct bufferevent *bev, short what, void *ctx); +}; + +/****** Bitcoin specific TorController implementation ********/ + +/** Controller that connects to Tor control socket, authenticate, then create + * and maintain an ephemeral onion service. + */ +class TorController +{ +public: + TorController(struct event_base* base, const std::string& tor_control_center, const CService& target); + TorController() : conn{nullptr} { + // Used for testing only. + } + ~TorController(); + + /** Get name of file to store private key in */ + fs::path GetPrivateKeyFile(); + + /** Reconnect, after getting disconnected */ + void Reconnect(); +private: + struct event_base* base; + const std::string m_tor_control_center; + TorControlConnection conn; + std::string private_key; + std::string service_id; + bool reconnect; + struct event *reconnect_ev = nullptr; + float reconnect_timeout; + CService service; + const CService m_target; + /** Cookie for SAFECOOKIE auth */ + std::vector cookie; + /** ClientNonce for SAFECOOKIE auth */ + std::vector clientNonce; + +public: + /** Callback for ADD_ONION result */ + void add_onion_cb(TorControlConnection& conn, const TorControlReply& reply); + /** Callback for AUTHENTICATE result */ + void auth_cb(TorControlConnection& conn, const TorControlReply& reply); + /** Callback for AUTHCHALLENGE result */ + void authchallenge_cb(TorControlConnection& conn, const TorControlReply& reply); + /** Callback for PROTOCOLINFO result */ + void protocolinfo_cb(TorControlConnection& conn, const TorControlReply& reply); + /** Callback after successful connection */ + void connected_cb(TorControlConnection& conn); + /** Callback after connection lost or failed connection attempt */ + void disconnected_cb(TorControlConnection& conn); + + /** Callback for reconnect timer */ + static void reconnect_cb(evutil_socket_t fd, short what, void *arg); +}; + #endif /* BITCOIN_TORCONTROL_H */