Parse reply arguments in the form 'METHODS=COOKIE,SAFECOOKIE COOKIEFILE=".../control_auth_cookie"'. * Returns a map of keys to values, or an empty map if there was an error. * Grammar is implicitly defined in https://spec.torproject.org/control-spec by * the server reply formats for PROTOCOLINFO (S3.21), AUTHCHALLENGE (S3.24), * and ADD_ONION (S3.27). See also sections 2.1 and 2.3. */
| 213 | * and ADD_ONION (S3.27). See also sections 2.1 and 2.3. |
| 214 | */ |
| 215 | std::map<std::string,std::string> ParseTorReplyMapping(const std::string &s) |
| 216 | { |
| 217 | std::map<std::string,std::string> mapping; |
| 218 | size_t ptr=0; |
| 219 | while (ptr < s.size()) { |
| 220 | std::string key, value; |
| 221 | while (ptr < s.size() && s[ptr] != '=' && s[ptr] != ' ') { |
| 222 | key.push_back(s[ptr]); |
| 223 | ++ptr; |
| 224 | } |
| 225 | if (ptr == s.size()) // unexpected end of line |
| 226 | return std::map<std::string,std::string>(); |
| 227 | if (s[ptr] == ' ') // The remaining string is an OptArguments |
| 228 | break; |
| 229 | ++ptr; // skip '=' |
| 230 | if (ptr < s.size() && s[ptr] == '"') { // Quoted string |
| 231 | ++ptr; // skip opening '"' |
| 232 | bool escape_next = false; |
| 233 | while (ptr < s.size() && (escape_next || s[ptr] != '"')) { |
| 234 | // Repeated backslashes must be interpreted as pairs |
| 235 | escape_next = (s[ptr] == '\\' && !escape_next); |
| 236 | value.push_back(s[ptr]); |
| 237 | ++ptr; |
| 238 | } |
| 239 | if (ptr == s.size()) // unexpected end of line |
| 240 | return std::map<std::string,std::string>(); |
| 241 | ++ptr; // skip closing '"' |
| 242 | /** |
| 243 | * Unescape value. Per https://spec.torproject.org/control-spec section 2.1.1: |
| 244 | * |
| 245 | * For future-proofing, controller implementors MAY use the following |
| 246 | * rules to be compatible with buggy Tor implementations and with |
| 247 | * future ones that implement the spec as intended: |
| 248 | * |
| 249 | * Read \n \t \r and \0 ... \377 as C escapes. |
| 250 | * Treat a backslash followed by any other character as that character. |
| 251 | */ |
| 252 | std::string escaped_value; |
| 253 | for (size_t i = 0; i < value.size(); ++i) { |
| 254 | if (value[i] == '\\') { |
| 255 | // This will always be valid, because if the QuotedString |
| 256 | // ended in an odd number of backslashes, then the parser |
| 257 | // would already have returned above, due to a missing |
| 258 | // terminating double-quote. |
| 259 | ++i; |
| 260 | if (value[i] == 'n') { |
| 261 | escaped_value.push_back('\n'); |
| 262 | } else if (value[i] == 't') { |
| 263 | escaped_value.push_back('\t'); |
| 264 | } else if (value[i] == 'r') { |
| 265 | escaped_value.push_back('\r'); |
| 266 | } else if ('0' <= value[i] && value[i] <= '7') { |
| 267 | size_t j; |
| 268 | // Octal escape sequences have a limit of three octal digits, |
| 269 | // but terminate at the first character that is not a valid |
| 270 | // octal digit if encountered sooner. |
| 271 | for (j = 1; j < 3 && (i+j) < value.size() && '0' <= value[i+j] && value[i+j] <= '7'; ++j) {} |
| 272 | // Tor restricts first digit to 0-3 for three-digit octals. |