torcontrol_tests.cpp raw

   1  // Copyright (c) 2017 The Zcash developers
   2  // Distributed under the MIT software license, see the accompanying
   3  // file COPYING or http://www.opensource.org/licenses/mit-license.php.
   4  //
   5  
   6  #include <boost/test/unit_test.hpp>
   7  
   8  #include <map>
   9  #include <string>
  10  #include <utility>
  11  
  12  
  13  std::pair<std::string, std::string> SplitTorReplyLine(const std::string& s);
  14  std::map<std::string, std::string> ParseTorReplyMapping(const std::string& s);
  15  
  16  
  17  BOOST_AUTO_TEST_SUITE(torcontrol_tests)
  18  
  19  static void CheckSplitTorReplyLine(std::string input, std::string command, std::string args)
  20  {
  21      auto ret = SplitTorReplyLine(input);
  22      BOOST_CHECK_EQUAL(ret.first, command);
  23      BOOST_CHECK_EQUAL(ret.second, args);
  24  }
  25  
  26  BOOST_AUTO_TEST_CASE(util_SplitTorReplyLine)
  27  {
  28      // Data we should receive during normal usage
  29      CheckSplitTorReplyLine(
  30          "PROTOCOLINFO PIVERSION",
  31          "PROTOCOLINFO", "PIVERSION");
  32      CheckSplitTorReplyLine(
  33          "AUTH METHODS=COOKIE,SAFECOOKIE COOKIEFILE=\"/home/x/.tor/control_auth_cookie\"",
  34          "AUTH", "METHODS=COOKIE,SAFECOOKIE COOKIEFILE=\"/home/x/.tor/control_auth_cookie\"");
  35      CheckSplitTorReplyLine(
  36          "AUTH METHODS=NULL",
  37          "AUTH", "METHODS=NULL");
  38      CheckSplitTorReplyLine(
  39          "AUTH METHODS=HASHEDPASSWORD",
  40          "AUTH", "METHODS=HASHEDPASSWORD");
  41      CheckSplitTorReplyLine(
  42          "VERSION Tor=\"0.2.9.8 (git-a0df013ea241b026)\"",
  43          "VERSION", "Tor=\"0.2.9.8 (git-a0df013ea241b026)\"");
  44      CheckSplitTorReplyLine(
  45          "AUTHCHALLENGE SERVERHASH=aaaa SERVERNONCE=bbbb",
  46          "AUTHCHALLENGE", "SERVERHASH=aaaa SERVERNONCE=bbbb");
  47  
  48      // Other valid inputs
  49      CheckSplitTorReplyLine("COMMAND", "COMMAND", "");
  50      CheckSplitTorReplyLine("COMMAND SOME  ARGS", "COMMAND", "SOME  ARGS");
  51  
  52      // These inputs are valid because PROTOCOLINFO accepts an OtherLine that is
  53      // just an OptArguments, which enables multiple spaces to be present
  54      // between the command and arguments.
  55      CheckSplitTorReplyLine("COMMAND  ARGS", "COMMAND", " ARGS");
  56      CheckSplitTorReplyLine("COMMAND   EVEN+more  ARGS", "COMMAND", "  EVEN+more  ARGS");
  57  }
  58  
  59  static void CheckParseTorReplyMapping(std::string input, std::map<std::string,std::string> expected)
  60  {
  61      auto ret = ParseTorReplyMapping(input);
  62      BOOST_CHECK_EQUAL(ret.size(), expected.size());
  63      auto r_it = ret.begin();
  64      auto e_it = expected.begin();
  65      while (r_it != ret.end() && e_it != expected.end()) {
  66          BOOST_CHECK_EQUAL(r_it->first, e_it->first);
  67          BOOST_CHECK_EQUAL(r_it->second, e_it->second);
  68          r_it++;
  69          e_it++;
  70      }
  71  }
  72  
  73  BOOST_AUTO_TEST_CASE(util_ParseTorReplyMapping)
  74  {
  75      // Data we should receive during normal usage
  76      CheckParseTorReplyMapping(
  77          "METHODS=COOKIE,SAFECOOKIE COOKIEFILE=\"/home/x/.tor/control_auth_cookie\"", {
  78              {"METHODS", "COOKIE,SAFECOOKIE"},
  79              {"COOKIEFILE", "/home/x/.tor/control_auth_cookie"},
  80          });
  81      CheckParseTorReplyMapping(
  82          "METHODS=NULL", {
  83              {"METHODS", "NULL"},
  84          });
  85      CheckParseTorReplyMapping(
  86          "METHODS=HASHEDPASSWORD", {
  87              {"METHODS", "HASHEDPASSWORD"},
  88          });
  89      CheckParseTorReplyMapping(
  90          "Tor=\"0.2.9.8 (git-a0df013ea241b026)\"", {
  91              {"Tor", "0.2.9.8 (git-a0df013ea241b026)"},
  92          });
  93      CheckParseTorReplyMapping(
  94          "SERVERHASH=aaaa SERVERNONCE=bbbb", {
  95              {"SERVERHASH", "aaaa"},
  96              {"SERVERNONCE", "bbbb"},
  97          });
  98      CheckParseTorReplyMapping(
  99          "ServiceID=exampleonion1234", {
 100              {"ServiceID", "exampleonion1234"},
 101          });
 102      CheckParseTorReplyMapping(
 103          "PrivateKey=RSA1024:BLOB", {
 104              {"PrivateKey", "RSA1024:BLOB"},
 105          });
 106      CheckParseTorReplyMapping(
 107          "ClientAuth=bob:BLOB", {
 108              {"ClientAuth", "bob:BLOB"},
 109          });
 110  
 111      // Other valid inputs
 112      CheckParseTorReplyMapping(
 113          "Foo=Bar=Baz Spam=Eggs", {
 114              {"Foo", "Bar=Baz"},
 115              {"Spam", "Eggs"},
 116          });
 117      CheckParseTorReplyMapping(
 118          "Foo=\"Bar=Baz\"", {
 119              {"Foo", "Bar=Baz"},
 120          });
 121      CheckParseTorReplyMapping(
 122          "Foo=\"Bar Baz\"", {
 123              {"Foo", "Bar Baz"},
 124          });
 125  
 126      // Escapes
 127      CheckParseTorReplyMapping(
 128          "Foo=\"Bar\\ Baz\"", {
 129              {"Foo", "Bar Baz"},
 130          });
 131      CheckParseTorReplyMapping(
 132          "Foo=\"Bar\\Baz\"", {
 133              {"Foo", "BarBaz"},
 134          });
 135      CheckParseTorReplyMapping(
 136          "Foo=\"Bar\\@Baz\"", {
 137              {"Foo", "Bar@Baz"},
 138          });
 139      CheckParseTorReplyMapping(
 140          "Foo=\"Bar\\\"Baz\" Spam=\"\\\"Eggs\\\"\"", {
 141              {"Foo", "Bar\"Baz"},
 142              {"Spam", "\"Eggs\""},
 143          });
 144      CheckParseTorReplyMapping(
 145          "Foo=\"Bar\\\\Baz\"", {
 146              {"Foo", "Bar\\Baz"},
 147          });
 148  
 149      // C escapes
 150      CheckParseTorReplyMapping(
 151          "Foo=\"Bar\\nBaz\\t\" Spam=\"\\rEggs\" Octals=\"\\1a\\11\\17\\18\\81\\377\\378\\400\\2222\" Final=Check", {
 152              {"Foo", "Bar\nBaz\t"},
 153              {"Spam", "\rEggs"},
 154              {"Octals", "\1a\11\17\1" "881\377\37" "8\40" "0\222" "2"},
 155              {"Final", "Check"},
 156          });
 157      CheckParseTorReplyMapping(
 158          "Valid=Mapping Escaped=\"Escape\\\\\"", {
 159              {"Valid", "Mapping"},
 160              {"Escaped", "Escape\\"},
 161          });
 162      CheckParseTorReplyMapping(
 163          "Valid=Mapping Bare=\"Escape\\\"", {});
 164      CheckParseTorReplyMapping(
 165          "OneOctal=\"OneEnd\\1\" TwoOctal=\"TwoEnd\\11\"", {
 166              {"OneOctal", "OneEnd\1"},
 167              {"TwoOctal", "TwoEnd\11"},
 168          });
 169  
 170      // Special handling for null case
 171      // (needed because string comparison reads the null as end-of-string)
 172      auto ret = ParseTorReplyMapping("Null=\"\\0\"");
 173      BOOST_CHECK_EQUAL(ret.size(), 1U);
 174      auto r_it = ret.begin();
 175      BOOST_CHECK_EQUAL(r_it->first, "Null");
 176      BOOST_CHECK_EQUAL(r_it->second.size(), 1U);
 177      BOOST_CHECK_EQUAL(r_it->second[0], '\0');
 178  
 179      // A more complex valid grammar. PROTOCOLINFO accepts a VersionLine that
 180      // takes a key=value pair followed by an OptArguments, making this valid.
 181      // Because an OptArguments contains no semantic data, there is no point in
 182      // parsing it.
 183      CheckParseTorReplyMapping(
 184          "SOME=args,here MORE optional=arguments  here", {
 185              {"SOME", "args,here"},
 186          });
 187  
 188      // Inputs that are effectively invalid under the target grammar.
 189      // PROTOCOLINFO accepts an OtherLine that is just an OptArguments, which
 190      // would make these inputs valid. However,
 191      // - This parser is never used in that situation, because the
 192      //   SplitTorReplyLine parser enables OtherLine to be skipped.
 193      // - Even if these were valid, an OptArguments contains no semantic data,
 194      //   so there is no point in parsing it.
 195      CheckParseTorReplyMapping("ARGS", {});
 196      CheckParseTorReplyMapping("MORE ARGS", {});
 197      CheckParseTorReplyMapping("MORE  ARGS", {});
 198      CheckParseTorReplyMapping("EVEN more=ARGS", {});
 199      CheckParseTorReplyMapping("EVEN+more ARGS", {});
 200  }
 201  
 202  BOOST_AUTO_TEST_SUITE_END()
 203