torcontrol_tests.cpp
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()