lbrycrd/src/test/torcontrol_tests.cpp
Jack Grigg 49a199bb51
torcontrol: Handle escapes in Tor QuotedStrings
https://trac.torproject.org/projects/tor/ticket/14999 is tracking an encoding
bug with the Tor control protocol, where many of the QuotedString instances that
Tor outputs are in fact CStrings, but it is not documented which ones are which.

https://spec.torproject.org/control-spec section 2.1.1 provides a future-proofed
rule for handing QuotedStrings, which this commit implements.

This commit merges all six commits from https://github.com/zcash/zcash/pull/2251
2017-05-16 18:22:25 +12:00

200 lines
7 KiB
C++

// Copyright (c) 2017 The Zcash developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
//
#include "test/test_bitcoin.h"
#include "torcontrol.cpp"
#include <boost/test/unit_test.hpp>
BOOST_FIXTURE_TEST_SUITE(torcontrol_tests, BasicTestingSetup)
void CheckSplitTorReplyLine(std::string input, std::string command, std::string args)
{
BOOST_TEST_MESSAGE(std::string("CheckSplitTorReplyLine(") + input + ")");
auto ret = SplitTorReplyLine(input);
BOOST_CHECK_EQUAL(ret.first, command);
BOOST_CHECK_EQUAL(ret.second, args);
}
BOOST_AUTO_TEST_CASE(util_SplitTorReplyLine)
{
// Data we should receive during normal usage
CheckSplitTorReplyLine(
"PROTOCOLINFO PIVERSION",
"PROTOCOLINFO", "PIVERSION");
CheckSplitTorReplyLine(
"AUTH METHODS=COOKIE,SAFECOOKIE COOKIEFILE=\"/home/x/.tor/control_auth_cookie\"",
"AUTH", "METHODS=COOKIE,SAFECOOKIE COOKIEFILE=\"/home/x/.tor/control_auth_cookie\"");
CheckSplitTorReplyLine(
"AUTH METHODS=NULL",
"AUTH", "METHODS=NULL");
CheckSplitTorReplyLine(
"AUTH METHODS=HASHEDPASSWORD",
"AUTH", "METHODS=HASHEDPASSWORD");
CheckSplitTorReplyLine(
"VERSION Tor=\"0.2.9.8 (git-a0df013ea241b026)\"",
"VERSION", "Tor=\"0.2.9.8 (git-a0df013ea241b026)\"");
CheckSplitTorReplyLine(
"AUTHCHALLENGE SERVERHASH=aaaa SERVERNONCE=bbbb",
"AUTHCHALLENGE", "SERVERHASH=aaaa SERVERNONCE=bbbb");
// Other valid inputs
CheckSplitTorReplyLine("COMMAND", "COMMAND", "");
CheckSplitTorReplyLine("COMMAND SOME ARGS", "COMMAND", "SOME ARGS");
// These inputs are valid because PROTOCOLINFO accepts an OtherLine that is
// just an OptArguments, which enables multiple spaces to be present
// between the command and arguments.
CheckSplitTorReplyLine("COMMAND ARGS", "COMMAND", " ARGS");
CheckSplitTorReplyLine("COMMAND EVEN+more ARGS", "COMMAND", " EVEN+more ARGS");
}
void CheckParseTorReplyMapping(std::string input, std::map<std::string,std::string> expected)
{
BOOST_TEST_MESSAGE(std::string("CheckParseTorReplyMapping(") + input + ")");
auto ret = ParseTorReplyMapping(input);
BOOST_CHECK_EQUAL(ret.size(), expected.size());
auto r_it = ret.begin();
auto e_it = expected.begin();
while (r_it != ret.end() && e_it != expected.end()) {
BOOST_CHECK_EQUAL(r_it->first, e_it->first);
BOOST_CHECK_EQUAL(r_it->second, e_it->second);
r_it++;
e_it++;
}
}
BOOST_AUTO_TEST_CASE(util_ParseTorReplyMapping)
{
// Data we should receive during normal usage
CheckParseTorReplyMapping(
"METHODS=COOKIE,SAFECOOKIE COOKIEFILE=\"/home/x/.tor/control_auth_cookie\"", {
{"METHODS", "COOKIE,SAFECOOKIE"},
{"COOKIEFILE", "/home/x/.tor/control_auth_cookie"},
});
CheckParseTorReplyMapping(
"METHODS=NULL", {
{"METHODS", "NULL"},
});
CheckParseTorReplyMapping(
"METHODS=HASHEDPASSWORD", {
{"METHODS", "HASHEDPASSWORD"},
});
CheckParseTorReplyMapping(
"Tor=\"0.2.9.8 (git-a0df013ea241b026)\"", {
{"Tor", "0.2.9.8 (git-a0df013ea241b026)"},
});
CheckParseTorReplyMapping(
"SERVERHASH=aaaa SERVERNONCE=bbbb", {
{"SERVERHASH", "aaaa"},
{"SERVERNONCE", "bbbb"},
});
CheckParseTorReplyMapping(
"ServiceID=exampleonion1234", {
{"ServiceID", "exampleonion1234"},
});
CheckParseTorReplyMapping(
"PrivateKey=RSA1024:BLOB", {
{"PrivateKey", "RSA1024:BLOB"},
});
CheckParseTorReplyMapping(
"ClientAuth=bob:BLOB", {
{"ClientAuth", "bob:BLOB"},
});
// Other valid inputs
CheckParseTorReplyMapping(
"Foo=Bar=Baz Spam=Eggs", {
{"Foo", "Bar=Baz"},
{"Spam", "Eggs"},
});
CheckParseTorReplyMapping(
"Foo=\"Bar=Baz\"", {
{"Foo", "Bar=Baz"},
});
CheckParseTorReplyMapping(
"Foo=\"Bar Baz\"", {
{"Foo", "Bar Baz"},
});
// Escapes
CheckParseTorReplyMapping(
"Foo=\"Bar\\ Baz\"", {
{"Foo", "Bar Baz"},
});
CheckParseTorReplyMapping(
"Foo=\"Bar\\Baz\"", {
{"Foo", "BarBaz"},
});
CheckParseTorReplyMapping(
"Foo=\"Bar\\@Baz\"", {
{"Foo", "Bar@Baz"},
});
CheckParseTorReplyMapping(
"Foo=\"Bar\\\"Baz\" Spam=\"\\\"Eggs\\\"\"", {
{"Foo", "Bar\"Baz"},
{"Spam", "\"Eggs\""},
});
CheckParseTorReplyMapping(
"Foo=\"Bar\\\\Baz\"", {
{"Foo", "Bar\\Baz"},
});
// C escapes
CheckParseTorReplyMapping(
"Foo=\"Bar\\nBaz\\t\" Spam=\"\\rEggs\" Octals=\"\\1a\\11\\17\\18\\81\\377\\378\\400\\2222\" Final=Check", {
{"Foo", "Bar\nBaz\t"},
{"Spam", "\rEggs"},
{"Octals", "\1a\11\17\1" "881\377\37" "8\40" "0\222" "2"},
{"Final", "Check"},
});
CheckParseTorReplyMapping(
"Valid=Mapping Escaped=\"Escape\\\\\"", {
{"Valid", "Mapping"},
{"Escaped", "Escape\\"},
});
CheckParseTorReplyMapping(
"Valid=Mapping Bare=\"Escape\\\"", {});
CheckParseTorReplyMapping(
"OneOctal=\"OneEnd\\1\" TwoOctal=\"TwoEnd\\11\"", {
{"OneOctal", "OneEnd\1"},
{"TwoOctal", "TwoEnd\11"},
});
// Special handling for null case
// (needed because string comparison reads the null as end-of-string)
BOOST_TEST_MESSAGE(std::string("CheckParseTorReplyMapping(Null=\"\\0\")"));
auto ret = ParseTorReplyMapping("Null=\"\\0\"");
BOOST_CHECK_EQUAL(ret.size(), 1);
auto r_it = ret.begin();
BOOST_CHECK_EQUAL(r_it->first, "Null");
BOOST_CHECK_EQUAL(r_it->second.size(), 1);
BOOST_CHECK_EQUAL(r_it->second[0], '\0');
// A more complex valid grammar. PROTOCOLINFO accepts a VersionLine that
// takes a key=value pair followed by an OptArguments, making this valid.
// Because an OptArguments contains no semantic data, there is no point in
// parsing it.
CheckParseTorReplyMapping(
"SOME=args,here MORE optional=arguments here", {
{"SOME", "args,here"},
});
// Inputs that are effectively invalid under the target grammar.
// PROTOCOLINFO accepts an OtherLine that is just an OptArguments, which
// would make these inputs valid. However,
// - This parser is never used in that situation, because the
// SplitTorReplyLine parser enables OtherLine to be skipped.
// - Even if these were valid, an OptArguments contains no semantic data,
// so there is no point in parsing it.
CheckParseTorReplyMapping("ARGS", {});
CheckParseTorReplyMapping("MORE ARGS", {});
CheckParseTorReplyMapping("MORE ARGS", {});
CheckParseTorReplyMapping("EVEN more=ARGS", {});
CheckParseTorReplyMapping("EVEN+more ARGS", {});
}
BOOST_AUTO_TEST_SUITE_END()