Line data Source code
1 : /* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. 2 : * Copyright (c) 2007-2021, The Tor Project, Inc. */ 3 : /* See LICENSE for licensing information */ 4 : 5 : /** 6 : * \file reasons.c 7 : * \brief Convert circuit, stream, and orconn error reasons to and/or from 8 : * strings and errno values. 9 : * 10 : * This module is just a bunch of functions full of case statements that 11 : * convert from one representation of our error codes to another. These are 12 : * mainly used in generating log messages, in sending messages to the 13 : * controller in control.c, and in converting errors from one protocol layer 14 : * to another. 15 : **/ 16 : 17 : #include "core/or/or.h" 18 : #include "app/config/config.h" 19 : #include "core/or/reasons.h" 20 : #include "feature/nodelist/node_select.h" 21 : #include "lib/tls/tortls.h" 22 : 23 : /***************************** Edge (stream) reasons **********************/ 24 : 25 : /** Convert the reason for ending a stream <b>reason</b> into the format used 26 : * in STREAM events. Return NULL if the reason is unrecognized. */ 27 : const char * 28 0 : stream_end_reason_to_control_string(int reason) 29 : { 30 0 : reason &= END_STREAM_REASON_MASK; 31 0 : switch (reason) { 32 : case END_STREAM_REASON_MISC: return "MISC"; 33 0 : case END_STREAM_REASON_RESOLVEFAILED: return "RESOLVEFAILED"; 34 0 : case END_STREAM_REASON_CONNECTREFUSED: return "CONNECTREFUSED"; 35 0 : case END_STREAM_REASON_EXITPOLICY: return "EXITPOLICY"; 36 0 : case END_STREAM_REASON_DESTROY: return "DESTROY"; 37 0 : case END_STREAM_REASON_DONE: return "DONE"; 38 0 : case END_STREAM_REASON_TIMEOUT: return "TIMEOUT"; 39 0 : case END_STREAM_REASON_NOROUTE: return "NOROUTE"; 40 0 : case END_STREAM_REASON_HIBERNATING: return "HIBERNATING"; 41 0 : case END_STREAM_REASON_INTERNAL: return "INTERNAL"; 42 0 : case END_STREAM_REASON_RESOURCELIMIT: return "RESOURCELIMIT"; 43 0 : case END_STREAM_REASON_CONNRESET: return "CONNRESET"; 44 0 : case END_STREAM_REASON_TORPROTOCOL: return "TORPROTOCOL"; 45 0 : case END_STREAM_REASON_NOTDIRECTORY: return "NOTDIRECTORY"; 46 : 47 0 : case END_STREAM_REASON_CANT_ATTACH: return "CANT_ATTACH"; 48 0 : case END_STREAM_REASON_NET_UNREACHABLE: return "NET_UNREACHABLE"; 49 0 : case END_STREAM_REASON_SOCKSPROTOCOL: return "SOCKS_PROTOCOL"; 50 : // XXXX Controlspec 51 0 : case END_STREAM_REASON_HTTPPROTOCOL: return "HTTP_PROTOCOL"; 52 : 53 0 : case END_STREAM_REASON_PRIVATE_ADDR: return "PRIVATE_ADDR"; 54 : 55 0 : default: return NULL; 56 : } 57 : } 58 : 59 : /** Translate <b>reason</b>, which came from a relay 'end' cell, 60 : * into a static const string describing why the stream is closing. 61 : * <b>reason</b> is -1 if no reason was provided. 62 : */ 63 : const char * 64 8 : stream_end_reason_to_string(int reason) 65 : { 66 8 : switch (reason) { 67 0 : case -1: 68 0 : log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, 69 : "End cell arrived with length 0. Should be at least 1."); 70 0 : return "MALFORMED"; 71 : case END_STREAM_REASON_MISC: return "misc error"; 72 1 : case END_STREAM_REASON_RESOLVEFAILED: return "resolve failed"; 73 0 : case END_STREAM_REASON_CONNECTREFUSED: return "connection refused"; 74 0 : case END_STREAM_REASON_EXITPOLICY: return "exit policy failed"; 75 0 : case END_STREAM_REASON_DESTROY: return "destroyed"; 76 0 : case END_STREAM_REASON_DONE: return "closed normally"; 77 0 : case END_STREAM_REASON_TIMEOUT: return "gave up (timeout)"; 78 0 : case END_STREAM_REASON_NOROUTE: return "no route to host"; 79 0 : case END_STREAM_REASON_HIBERNATING: return "server is hibernating"; 80 0 : case END_STREAM_REASON_INTERNAL: return "internal error at server"; 81 0 : case END_STREAM_REASON_RESOURCELIMIT: return "server out of resources"; 82 0 : case END_STREAM_REASON_CONNRESET: return "connection reset"; 83 0 : case END_STREAM_REASON_TORPROTOCOL: return "Tor protocol error"; 84 0 : case END_STREAM_REASON_NOTDIRECTORY: return "not a directory"; 85 7 : default: 86 7 : log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, 87 : "Reason for ending (%d) not recognized.",reason); 88 7 : return "unknown"; 89 : } 90 : } 91 : 92 : /** Translate <b>reason</b> (as from a relay 'end' cell) into an 93 : * appropriate SOCKS5 reply code. 94 : * 95 : * A reason of 0 means that we're not actually expecting to send 96 : * this code back to the socks client; we just call it 'succeeded' 97 : * to keep things simple. 98 : */ 99 : socks5_reply_status_t 100 1 : stream_end_reason_to_socks5_response(int reason) 101 : { 102 1 : switch (reason & END_STREAM_REASON_MASK) { 103 : case 0: 104 : return SOCKS5_SUCCEEDED; 105 0 : case END_STREAM_REASON_MISC: 106 0 : return SOCKS5_GENERAL_ERROR; 107 0 : case END_STREAM_REASON_RESOLVEFAILED: 108 0 : return SOCKS5_HOST_UNREACHABLE; 109 0 : case END_STREAM_REASON_CONNECTREFUSED: 110 0 : return SOCKS5_CONNECTION_REFUSED; 111 0 : case END_STREAM_REASON_ENTRYPOLICY: 112 0 : return SOCKS5_NOT_ALLOWED; 113 0 : case END_STREAM_REASON_EXITPOLICY: 114 0 : return SOCKS5_NOT_ALLOWED; 115 0 : case END_STREAM_REASON_DESTROY: 116 0 : return SOCKS5_GENERAL_ERROR; 117 0 : case END_STREAM_REASON_DONE: 118 : /* Note that 'DONE' usually indicates a successful close from the other 119 : * side of the stream... but if we receive it before a connected cell -- 120 : * that is, before we have sent a SOCKS reply -- that means that the 121 : * other side of the circuit closed the connection before telling us it 122 : * was complete. */ 123 0 : return SOCKS5_CONNECTION_REFUSED; 124 0 : case END_STREAM_REASON_TIMEOUT: 125 0 : return SOCKS5_TTL_EXPIRED; 126 0 : case END_STREAM_REASON_NOROUTE: 127 0 : return SOCKS5_HOST_UNREACHABLE; 128 0 : case END_STREAM_REASON_RESOURCELIMIT: 129 0 : return SOCKS5_GENERAL_ERROR; 130 0 : case END_STREAM_REASON_HIBERNATING: 131 0 : return SOCKS5_GENERAL_ERROR; 132 0 : case END_STREAM_REASON_INTERNAL: 133 0 : return SOCKS5_GENERAL_ERROR; 134 0 : case END_STREAM_REASON_CONNRESET: 135 0 : return SOCKS5_CONNECTION_REFUSED; 136 0 : case END_STREAM_REASON_TORPROTOCOL: 137 0 : return SOCKS5_GENERAL_ERROR; 138 : 139 0 : case END_STREAM_REASON_CANT_ATTACH: 140 0 : return SOCKS5_GENERAL_ERROR; 141 0 : case END_STREAM_REASON_NET_UNREACHABLE: 142 0 : return SOCKS5_NET_UNREACHABLE; 143 0 : case END_STREAM_REASON_SOCKSPROTOCOL: 144 0 : return SOCKS5_GENERAL_ERROR; 145 0 : case END_STREAM_REASON_HTTPPROTOCOL: 146 : // LCOV_EXCL_START 147 : tor_assert_nonfatal_unreached(); 148 : return SOCKS5_GENERAL_ERROR; 149 : // LCOV_EXCL_STOP 150 0 : case END_STREAM_REASON_PRIVATE_ADDR: 151 0 : return SOCKS5_GENERAL_ERROR; 152 : 153 0 : default: 154 0 : log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, 155 : "Reason for ending (%d) not recognized; " 156 : "sending generic socks error.", reason); 157 0 : return SOCKS5_GENERAL_ERROR; 158 : } 159 : } 160 : 161 : /* We need to use a few macros to deal with the fact that Windows 162 : * decided that their sockets interface should be a permakludge. 163 : * E_CASE is for errors where windows has both a EFOO and a WSAEFOO 164 : * version, and S_CASE is for errors where windows has only a WSAEFOO 165 : * version. (The E is for 'error', the S is for 'socket'). */ 166 : #ifdef _WIN32 167 : #define E_CASE(s) case s: case WSA ## s 168 : #define S_CASE(s) case WSA ## s 169 : #else 170 : #define E_CASE(s) case s 171 : #define S_CASE(s) case s 172 : #endif /* defined(_WIN32) */ 173 : 174 : /** Given an errno from a failed exit connection, return a reason code 175 : * appropriate for use in a RELAY END cell. */ 176 : uint8_t 177 0 : errno_to_stream_end_reason(int e) 178 : { 179 : /* To add new errors here, find out if they exist on Windows, and if a WSA* 180 : * equivalent exists on windows. Add a case, an S_CASE, or an E_CASE as 181 : * appropriate. */ 182 0 : switch (e) { 183 : case EPIPE: 184 : return END_STREAM_REASON_DONE; 185 0 : E_CASE(EBADF): 186 : E_CASE(EFAULT): 187 : E_CASE(EINVAL): 188 : S_CASE(EISCONN): 189 : S_CASE(ENOTSOCK): 190 : S_CASE(EPROTONOSUPPORT): 191 : S_CASE(EAFNOSUPPORT): 192 : S_CASE(ENOTCONN): 193 0 : return END_STREAM_REASON_INTERNAL; 194 0 : S_CASE(ENETUNREACH): 195 : S_CASE(EHOSTUNREACH): 196 : E_CASE(EACCES): 197 : case EPERM: 198 0 : return END_STREAM_REASON_NOROUTE; 199 0 : S_CASE(ECONNREFUSED): 200 0 : return END_STREAM_REASON_CONNECTREFUSED; 201 0 : S_CASE(ECONNRESET): 202 0 : return END_STREAM_REASON_CONNRESET; 203 0 : S_CASE(ETIMEDOUT): 204 0 : return END_STREAM_REASON_TIMEOUT; 205 0 : S_CASE(ENOBUFS): 206 : case ENOMEM: 207 : case ENFILE: 208 : S_CASE(EADDRINUSE): 209 : S_CASE(EADDRNOTAVAIL): 210 : E_CASE(EMFILE): 211 0 : return END_STREAM_REASON_RESOURCELIMIT; 212 0 : default: 213 0 : log_info(LD_EXIT, "Didn't recognize errno %d (%s); telling the client " 214 : "that we are ending a stream for 'misc' reason.", 215 : e, tor_socket_strerror(e)); 216 0 : return END_STREAM_REASON_MISC; 217 : } 218 : } 219 : 220 : /***************************** ORConn reasons *****************************/ 221 : 222 : /** Convert the reason for ending an OR connection <b>r</b> into the format 223 : * used in ORCONN events. Return "UNKNOWN" if the reason is unrecognized. */ 224 : const char * 225 4 : orconn_end_reason_to_control_string(int r) 226 : { 227 : /* To add new errors here, find out if they exist on Windows, and if a WSA* 228 : * equivalent exists on windows. Add a case, an S_CASE, or an E_CASE as 229 : * appropriate. */ 230 4 : switch (r) { 231 : case END_OR_CONN_REASON_DONE: 232 : return "DONE"; 233 0 : case END_OR_CONN_REASON_REFUSED: 234 0 : return "CONNECTREFUSED"; 235 0 : case END_OR_CONN_REASON_OR_IDENTITY: 236 0 : return "IDENTITY"; 237 0 : case END_OR_CONN_REASON_CONNRESET: 238 0 : return "CONNECTRESET"; 239 0 : case END_OR_CONN_REASON_TIMEOUT: 240 0 : return "TIMEOUT"; 241 0 : case END_OR_CONN_REASON_NO_ROUTE: 242 0 : return "NOROUTE"; 243 0 : case END_OR_CONN_REASON_IO_ERROR: 244 0 : return "IOERROR"; 245 0 : case END_OR_CONN_REASON_RESOURCE_LIMIT: 246 0 : return "RESOURCELIMIT"; 247 0 : case END_OR_CONN_REASON_TLS_ERROR: 248 0 : return "TLS_ERROR"; 249 0 : case END_OR_CONN_REASON_MISC: 250 0 : return "MISC"; 251 0 : case END_OR_CONN_REASON_PT_MISSING: 252 0 : return "PT_MISSING"; 253 0 : case 0: 254 0 : return ""; 255 0 : default: 256 0 : log_warn(LD_BUG, "Unrecognized or_conn reason code %d", r); 257 0 : return "UNKNOWN"; 258 : } 259 : } 260 : 261 : /** Convert a TOR_TLS_* error code into an END_OR_CONN_* reason. */ 262 : int 263 4 : tls_error_to_orconn_end_reason(int e) 264 : { 265 4 : switch (e) { 266 : case TOR_TLS_ERROR_IO: 267 : return END_OR_CONN_REASON_IO_ERROR; 268 : case TOR_TLS_ERROR_CONNREFUSED: 269 : return END_OR_CONN_REASON_REFUSED; 270 : case TOR_TLS_ERROR_CONNRESET: 271 : return END_OR_CONN_REASON_CONNRESET; 272 : case TOR_TLS_ERROR_NO_ROUTE: 273 : return END_OR_CONN_REASON_NO_ROUTE; 274 : case TOR_TLS_ERROR_TIMEOUT: 275 : return END_OR_CONN_REASON_TIMEOUT; 276 : case TOR_TLS_WANTREAD: 277 : case TOR_TLS_WANTWRITE: 278 : case TOR_TLS_CLOSE: 279 : case TOR_TLS_DONE: 280 : return END_OR_CONN_REASON_DONE; 281 : case TOR_TLS_ERROR_MISC: 282 : return END_OR_CONN_REASON_TLS_ERROR; 283 : default: 284 : return END_OR_CONN_REASON_MISC; 285 : } 286 : } 287 : 288 : /** Given an errno from a failed ORConn connection, return a reason code 289 : * appropriate for use in the controller orconn events. */ 290 : int 291 0 : errno_to_orconn_end_reason(int e) 292 : { 293 0 : switch (e) { 294 : case EPIPE: 295 : return END_OR_CONN_REASON_DONE; 296 0 : S_CASE(ENOTCONN): 297 : S_CASE(ENETUNREACH): 298 : S_CASE(ENETDOWN): 299 : S_CASE(EHOSTUNREACH): 300 0 : return END_OR_CONN_REASON_NO_ROUTE; 301 0 : S_CASE(ECONNREFUSED): 302 0 : return END_OR_CONN_REASON_REFUSED; 303 0 : S_CASE(ECONNRESET): 304 0 : return END_OR_CONN_REASON_CONNRESET; 305 0 : S_CASE(ETIMEDOUT): 306 0 : return END_OR_CONN_REASON_TIMEOUT; 307 0 : S_CASE(ENOBUFS): 308 : case ENOMEM: 309 : case ENFILE: 310 : E_CASE(EMFILE): 311 : E_CASE(EACCES): 312 : E_CASE(EBADF): 313 : E_CASE(EFAULT): 314 : E_CASE(EINVAL): 315 0 : return END_OR_CONN_REASON_RESOURCE_LIMIT; 316 0 : default: 317 0 : log_info(LD_OR, "Didn't recognize errno %d (%s).", 318 : e, tor_socket_strerror(e)); 319 0 : return END_OR_CONN_REASON_MISC; 320 : } 321 : } 322 : 323 : /***************************** Circuit reasons *****************************/ 324 : 325 : /** Convert a numeric reason for destroying a circuit into a string for a 326 : * CIRCUIT event. */ 327 : const char * 328 0 : circuit_end_reason_to_control_string(int reason) 329 : { 330 0 : int is_remote = 0; 331 : 332 0 : if (reason >= 0 && reason & END_CIRC_REASON_FLAG_REMOTE) { 333 0 : reason &= ~END_CIRC_REASON_FLAG_REMOTE; 334 0 : is_remote = 1; 335 : } 336 : 337 0 : switch (reason) { 338 : case END_CIRC_AT_ORIGIN: 339 : /* This shouldn't get passed here; it's a catch-all reason. */ 340 : return "ORIGIN"; 341 0 : case END_CIRC_REASON_NONE: 342 : /* This shouldn't get passed here; it's a catch-all reason. */ 343 0 : return "NONE"; 344 0 : case END_CIRC_REASON_TORPROTOCOL: 345 0 : return "TORPROTOCOL"; 346 0 : case END_CIRC_REASON_INTERNAL: 347 0 : return "INTERNAL"; 348 0 : case END_CIRC_REASON_REQUESTED: 349 0 : return "REQUESTED"; 350 0 : case END_CIRC_REASON_HIBERNATING: 351 0 : return "HIBERNATING"; 352 0 : case END_CIRC_REASON_RESOURCELIMIT: 353 0 : return "RESOURCELIMIT"; 354 0 : case END_CIRC_REASON_CONNECTFAILED: 355 0 : return "CONNECTFAILED"; 356 0 : case END_CIRC_REASON_OR_IDENTITY: 357 0 : return "OR_IDENTITY"; 358 0 : case END_CIRC_REASON_CHANNEL_CLOSED: 359 0 : return "CHANNEL_CLOSED"; 360 0 : case END_CIRC_REASON_FINISHED: 361 0 : return "FINISHED"; 362 0 : case END_CIRC_REASON_TIMEOUT: 363 0 : return "TIMEOUT"; 364 0 : case END_CIRC_REASON_DESTROYED: 365 0 : return "DESTROYED"; 366 0 : case END_CIRC_REASON_NOPATH: 367 0 : return "NOPATH"; 368 0 : case END_CIRC_REASON_NOSUCHSERVICE: 369 0 : return "NOSUCHSERVICE"; 370 0 : case END_CIRC_REASON_MEASUREMENT_EXPIRED: 371 0 : return "MEASUREMENT_EXPIRED"; 372 0 : case END_CIRC_REASON_IP_NOW_REDUNDANT: 373 0 : return "IP_NOW_REDUNDANT"; 374 0 : default: 375 0 : if (is_remote) { 376 : /* 377 : * If it's remote, it's not a bug *here*, so don't use LD_BUG, but 378 : * do note that the someone we're talking to is speaking the Tor 379 : * protocol with a weird accent. 380 : */ 381 0 : log_warn(LD_PROTOCOL, 382 : "Remote server sent bogus reason code %d", reason); 383 : } else { 384 0 : log_warn(LD_BUG, 385 : "Unrecognized reason code %d", reason); 386 : } 387 : return NULL; 388 : } 389 : } 390 : 391 : /** Return a string corresponding to a SOCKS4 response code. */ 392 : const char * 393 1 : socks4_response_code_to_string(uint8_t code) 394 : { 395 1 : switch (code) { 396 : case 0x5a: 397 : return "connection accepted"; 398 1 : case 0x5b: 399 1 : return "server rejected connection"; 400 0 : case 0x5c: 401 0 : return "server cannot connect to identd on this client"; 402 0 : case 0x5d: 403 0 : return "user id does not match identd"; 404 0 : default: 405 0 : return "invalid SOCKS 4 response code"; 406 : } 407 : } 408 : 409 : /** Return a string corresponding to a SOCKS5 response code. */ 410 : const char * 411 1 : socks5_response_code_to_string(uint8_t code) 412 : { 413 1 : switch (code) { 414 : case 0x00: 415 : return "connection accepted"; 416 0 : case 0x01: 417 0 : return "general SOCKS server failure"; 418 0 : case 0x02: 419 0 : return "connection not allowed by ruleset"; 420 1 : case 0x03: 421 1 : return "Network unreachable"; 422 0 : case 0x04: 423 0 : return "Host unreachable"; 424 0 : case 0x05: 425 0 : return "Connection refused"; 426 0 : case 0x06: 427 0 : return "TTL expired"; 428 0 : case 0x07: 429 0 : return "Command not supported"; 430 0 : case 0x08: 431 0 : return "Address type not supported"; 432 0 : default: 433 0 : return "unknown reason"; 434 : } 435 : } 436 : 437 : /** Return a string corresponding to a bandwidth_weight_rule_t */ 438 : const char * 439 2746 : bandwidth_weight_rule_to_string(bandwidth_weight_rule_t rule) 440 : { 441 2746 : switch (rule) 442 : { 443 : case NO_WEIGHTING: 444 : return "no weighting"; 445 63 : case WEIGHT_FOR_EXIT: 446 63 : return "weight as exit"; 447 827 : case WEIGHT_FOR_MID: 448 827 : return "weight as middle node"; 449 1847 : case WEIGHT_FOR_GUARD: 450 1847 : return "weight as guard"; 451 9 : case WEIGHT_FOR_DIR: 452 9 : return "weight as directory"; 453 0 : default: 454 0 : return "unknown rule"; 455 : } 456 : } 457 : 458 : /** Given a RELAY_END reason value, convert it to an HTTP response to be 459 : * send over an HTTP tunnel connection. */ 460 : const char * 461 0 : end_reason_to_http_connect_response_line(int endreason) 462 : { 463 0 : endreason &= END_STREAM_REASON_MASK; 464 : /* XXXX these are probably all wrong. Should they all be 502? */ 465 0 : switch (endreason) { 466 : case 0: 467 : return "HTTP/1.0 200 OK\r\n\r\n"; 468 0 : case END_STREAM_REASON_MISC: 469 0 : return "HTTP/1.0 500 Internal Server Error\r\n\r\n"; 470 0 : case END_STREAM_REASON_RESOLVEFAILED: 471 0 : return "HTTP/1.0 404 Not Found (resolve failed)\r\n\r\n"; 472 0 : case END_STREAM_REASON_NOROUTE: 473 0 : return "HTTP/1.0 404 Not Found (no route)\r\n\r\n"; 474 0 : case END_STREAM_REASON_CONNECTREFUSED: 475 0 : return "HTTP/1.0 403 Forbidden (connection refused)\r\n\r\n"; 476 0 : case END_STREAM_REASON_EXITPOLICY: 477 0 : return "HTTP/1.0 403 Forbidden (exit policy)\r\n\r\n"; 478 0 : case END_STREAM_REASON_DESTROY: 479 0 : return "HTTP/1.0 502 Bad Gateway (destroy cell received)\r\n\r\n"; 480 0 : case END_STREAM_REASON_DONE: 481 0 : return "HTTP/1.0 502 Bad Gateway (unexpected close)\r\n\r\n"; 482 0 : case END_STREAM_REASON_TIMEOUT: 483 0 : return "HTTP/1.0 504 Gateway Timeout\r\n\r\n"; 484 0 : case END_STREAM_REASON_HIBERNATING: 485 0 : return "HTTP/1.0 502 Bad Gateway (hibernating server)\r\n\r\n"; 486 0 : case END_STREAM_REASON_INTERNAL: 487 0 : return "HTTP/1.0 502 Bad Gateway (internal error)\r\n\r\n"; 488 0 : case END_STREAM_REASON_RESOURCELIMIT: 489 0 : return "HTTP/1.0 502 Bad Gateway (resource limit)\r\n\r\n"; 490 0 : case END_STREAM_REASON_CONNRESET: 491 0 : return "HTTP/1.0 403 Forbidden (connection reset)\r\n\r\n"; 492 0 : case END_STREAM_REASON_TORPROTOCOL: 493 0 : return "HTTP/1.0 502 Bad Gateway (tor protocol violation)\r\n\r\n"; 494 0 : case END_STREAM_REASON_ENTRYPOLICY: 495 0 : return "HTTP/1.0 403 Forbidden (entry policy violation)\r\n\r\n"; 496 0 : case END_STREAM_REASON_NOTDIRECTORY: FALLTHROUGH; 497 : default: 498 0 : tor_assert_nonfatal_unreached(); 499 0 : return "HTTP/1.0 500 Internal Server Error (weird end reason)\r\n\r\n"; 500 : } 501 : }