From e1cfda5caa3bebc6af0cfd5df2fc768e9c56da10 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Tue, 4 Nov 2025 09:42:23 -0500 Subject: [PATCH] HTTP CONNECT: Send Via/Server and Tor-Capabilities headers We want to send these, per prop365, to indicate that we're speaking Tor-flavored HTTP CONNECT, and to indicate our software version and capabilities. --- src/core/or/connection_edge.c | 40 ++++++++++++++++++++++++++++++----- src/core/or/reasons.c | 32 ++++++++++++++-------------- 2 files changed, 51 insertions(+), 21 deletions(-) diff --git a/src/core/or/connection_edge.c b/src/core/or/connection_edge.c index 3b48e26857..94df58628f 100644 --- a/src/core/or/connection_edge.c +++ b/src/core/or/connection_edge.c @@ -2984,9 +2984,22 @@ connection_ap_process_natd(entry_connection_t *conn) return connection_ap_rewrite_and_attach_if_allowed(conn, NULL, NULL); } +#define TOR_CAPABILITIES_HEADER \ + "Tor-Capabilities: \r\n" + +#define HTTP_CONNECT_FIXED_HEADERS \ + TOR_CAPABILITIES_HEADER \ + "Via: tor/1.0 tor-network (tor "VERSION")\r\n" + +#define HTTP_OTHER_FIXED_HEADERS \ + TOR_CAPABILITIES_HEADER \ + "Server: tor/1.0 (tor "VERSION")\r\n" + static const char HTTP_CONNECT_IS_NOT_AN_HTTP_PROXY_MSG[] = "HTTP/1.0 405 Method Not Allowed\r\n" - "Content-Type: text/html; charset=iso-8859-1\r\n\r\n" + "Content-Type: text/html; charset=iso-8859-1\r\n" + HTTP_OTHER_FIXED_HEADERS + "\r\n" "\n" "\n" "This is an HTTP CONNECT tunnel, not a full HTTP Proxy\n" @@ -3052,12 +3065,17 @@ connection_ap_process_http_connect(entry_connection_t *conn) char *command = NULL, *addrport = NULL; char *addr = NULL; size_t bodylen = 0; + const char *fixed_reply_headers = HTTP_OTHER_FIXED_HEADERS; const char *errmsg = NULL; bool close_without_message = false; int rv = 0; bool host_is_localhost = false; + // If true, we already have a full reply, so we shouldn't add + // fixed headers and CRLF. + bool errmsg_is_complete = false; + const int http_status = fetch_from_buf_http(ENTRY_TO_CONN(conn)->inbuf, &headers, 8192, &body, &bodylen, 1024, 0); @@ -3092,21 +3110,24 @@ connection_ap_process_http_connect(entry_connection_t *conn) if (strcasecmp(command, "connect")) { if (host_is_localhost) { errmsg = HTTP_CONNECT_IS_NOT_AN_HTTP_PROXY_MSG; + errmsg_is_complete = true; } else { close_without_message = true; } goto err; } + fixed_reply_headers = HTTP_CONNECT_FIXED_HEADERS; + tor_assert(conn->socks_request); socks_request_t *socks = conn->socks_request; uint16_t port; if (tor_addr_port_split(LOG_WARN, addrport, &addr, &port) < 0) { - errmsg = "HTTP/1.0 400 Bad Request\r\n\r\n"; + errmsg = "HTTP/1.0 400 Bad Request\r\n"; goto err; } if (strlen(addr) >= MAX_SOCKS_ADDR_LEN) { - errmsg = "HTTP/1.0 414 Request-URI Too Long\r\n\r\n"; + errmsg = "HTTP/1.0 414 Request-URI Too Long\r\n"; goto err; } @@ -3140,10 +3161,15 @@ connection_ap_process_http_connect(entry_connection_t *conn) err: if (BUG(errmsg == NULL) && ! close_without_message) - errmsg = "HTTP/1.0 400 Bad Request\r\n\r\n"; + errmsg = "HTTP/1.0 400 Bad Request\r\n"; if (errmsg) { log_info(LD_EDGE, "HTTP tunnel error: saying %s", escaped(errmsg)); connection_buf_add(errmsg, strlen(errmsg), ENTRY_TO_CONN(conn)); + if (!errmsg_is_complete) { + connection_buf_add(fixed_reply_headers, strlen(fixed_reply_headers), + ENTRY_TO_CONN(conn)); + connection_buf_add("\r\n", 2, ENTRY_TO_CONN(conn)); + } } else { log_info(LD_EDGE, "HTTP tunnel error: closing silently"); } @@ -3825,9 +3851,13 @@ connection_ap_handshake_socks_reply(entry_connection_t *conn, char *reply, CONN_TYPE_AP_HTTP_CONNECT_LISTENER) { const char *response = end_reason_to_http_connect_response_line(endreason); if (!response) { - response = "HTTP/1.0 400 Bad Request\r\n\r\n"; + response = "HTTP/1.0 400 Bad Request\r\n"; } connection_buf_add(response, strlen(response), ENTRY_TO_CONN(conn)); + connection_buf_add(HTTP_CONNECT_FIXED_HEADERS, + strlen(HTTP_CONNECT_FIXED_HEADERS), + ENTRY_TO_CONN(conn)); + connection_buf_add("\r\n", 2, ENTRY_TO_CONN(conn)); } else if (conn->socks_request->socks_version == 4) { memset(buf,0,SOCKS4_NETWORK_LEN); buf[1] = (status==SOCKS5_SUCCEEDED ? SOCKS4_GRANTED : SOCKS4_REJECT); diff --git a/src/core/or/reasons.c b/src/core/or/reasons.c index 379f274b27..ba81ba367a 100644 --- a/src/core/or/reasons.c +++ b/src/core/or/reasons.c @@ -464,38 +464,38 @@ end_reason_to_http_connect_response_line(int endreason) /* XXXX these are probably all wrong. Should they all be 502? */ switch (endreason) { case 0: - return "HTTP/1.0 200 OK\r\n\r\n"; + return "HTTP/1.0 200 OK\r\n"; case END_STREAM_REASON_MISC: - return "HTTP/1.0 500 Internal Server Error\r\n\r\n"; + return "HTTP/1.0 500 Internal Server Error\r\n"; case END_STREAM_REASON_RESOLVEFAILED: - return "HTTP/1.0 404 Not Found (resolve failed)\r\n\r\n"; + return "HTTP/1.0 404 Not Found (resolve failed)\r\n"; case END_STREAM_REASON_NOROUTE: - return "HTTP/1.0 404 Not Found (no route)\r\n\r\n"; + return "HTTP/1.0 404 Not Found (no route)\r\n"; case END_STREAM_REASON_CONNECTREFUSED: - return "HTTP/1.0 403 Forbidden (connection refused)\r\n\r\n"; + return "HTTP/1.0 403 Forbidden (connection refused)\r\n"; case END_STREAM_REASON_EXITPOLICY: - return "HTTP/1.0 403 Forbidden (exit policy)\r\n\r\n"; + return "HTTP/1.0 403 Forbidden (exit policy)\r\n"; case END_STREAM_REASON_DESTROY: - return "HTTP/1.0 502 Bad Gateway (destroy cell received)\r\n\r\n"; + return "HTTP/1.0 502 Bad Gateway (destroy cell received)\r\n"; case END_STREAM_REASON_DONE: - return "HTTP/1.0 502 Bad Gateway (unexpected close)\r\n\r\n"; + return "HTTP/1.0 502 Bad Gateway (unexpected close)\r\n"; case END_STREAM_REASON_TIMEOUT: - return "HTTP/1.0 504 Gateway Timeout\r\n\r\n"; + return "HTTP/1.0 504 Gateway Timeout\r\n"; case END_STREAM_REASON_HIBERNATING: - return "HTTP/1.0 502 Bad Gateway (hibernating server)\r\n\r\n"; + return "HTTP/1.0 502 Bad Gateway (hibernating server)\r\n"; case END_STREAM_REASON_INTERNAL: - return "HTTP/1.0 502 Bad Gateway (internal error)\r\n\r\n"; + return "HTTP/1.0 502 Bad Gateway (internal error)\r\n"; case END_STREAM_REASON_RESOURCELIMIT: - return "HTTP/1.0 502 Bad Gateway (resource limit)\r\n\r\n"; + return "HTTP/1.0 502 Bad Gateway (resource limit)\r\n"; case END_STREAM_REASON_CONNRESET: - return "HTTP/1.0 403 Forbidden (connection reset)\r\n\r\n"; + return "HTTP/1.0 403 Forbidden (connection reset)\r\n"; case END_STREAM_REASON_TORPROTOCOL: - return "HTTP/1.0 502 Bad Gateway (tor protocol violation)\r\n\r\n"; + return "HTTP/1.0 502 Bad Gateway (tor protocol violation)\r\n"; case END_STREAM_REASON_ENTRYPOLICY: - return "HTTP/1.0 403 Forbidden (entry policy violation)\r\n\r\n"; + return "HTTP/1.0 403 Forbidden (entry policy violation)\r\n"; case END_STREAM_REASON_NOTDIRECTORY: FALLTHROUGH; default: tor_assert_nonfatal_unreached(); - return "HTTP/1.0 500 Internal Server Error (weird end reason)\r\n\r\n"; + return "HTTP/1.0 500 Internal Server Error (weird end reason)\r\n"; } } -- 2.47.3