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. */
| 270 | * and ADD_ONION (S3.27). See also sections 2.1 and 2.3. |
| 271 | */ |
| 272 | std::map<std::string,std::string> ParseTorReplyMapping(const std::string &s) |
| 273 | { |
| 274 | std::map<std::string,std::string> mapping; |
| 275 | size_t ptr=0; |
| 276 | while (ptr < s.size()) { |
| 277 | std::string key, value; |
| 278 | while (ptr < s.size() && s[ptr] != '=' && s[ptr] != ' ') { |
| 279 | key.push_back(s[ptr]); |
| 280 | ++ptr; |
| 281 | } |
| 282 | if (ptr == s.size()) // unexpected end of line |
| 283 | return std::map<std::string,std::string>(); |
| 284 | if (s[ptr] == ' ') // The remaining string is an OptArguments |
| 285 | break; |
| 286 | ++ptr; // skip '=' |
| 287 | if (ptr < s.size() && s[ptr] == '"') { // Quoted string |
| 288 | ++ptr; // skip opening '"' |
| 289 | bool escape_next = false; |
| 290 | while (ptr < s.size() && (escape_next || s[ptr] != '"')) { |
| 291 | // Repeated backslashes must be interpreted as pairs |
| 292 | escape_next = (s[ptr] == '\\' && !escape_next); |
| 293 | value.push_back(s[ptr]); |
| 294 | ++ptr; |
| 295 | } |
| 296 | if (ptr == s.size()) // unexpected end of line |
| 297 | return std::map<std::string,std::string>(); |
| 298 | ++ptr; // skip closing '"' |
| 299 | /** |
| 300 | * Unescape value. Per https://spec.torproject.org/control-spec section 2.1.1: |
| 301 | * |
| 302 | * For future-proofing, controller implementors MAY use the following |
| 303 | * rules to be compatible with buggy Tor implementations and with |
| 304 | * future ones that implement the spec as intended: |
| 305 | * |
| 306 | * Read \n \t \r and \0 ... \377 as C escapes. |
| 307 | * Treat a backslash followed by any other character as that character. |
| 308 | */ |
| 309 | std::string escaped_value; |
| 310 | for (size_t i = 0; i < value.size(); ++i) { |
| 311 | if (value[i] == '\\') { |
| 312 | // This will always be valid, because if the QuotedString |
| 313 | // ended in an odd number of backslashes, then the parser |
| 314 | // would already have returned above, due to a missing |
| 315 | // terminating double-quote. |
| 316 | ++i; |
| 317 | if (value[i] == 'n') { |
| 318 | escaped_value.push_back('\n'); |
| 319 | } else if (value[i] == 't') { |
| 320 | escaped_value.push_back('\t'); |
| 321 | } else if (value[i] == 'r') { |
| 322 | escaped_value.push_back('\r'); |
| 323 | } else if ('0' <= value[i] && value[i] <= '7') { |
| 324 | size_t j; |
| 325 | // Octal escape sequences have a limit of three octal digits, |
| 326 | // but terminate at the first character that is not a valid |
| 327 | // octal digit if encountered sooner. |
| 328 | for (j = 1; j < 3 && (i+j) < value.size() && '0' <= value[i+j] && value[i+j] <= '7'; ++j) {} |
| 329 | // Tor restricts first digit to 0-3 for three-digit octals. |