From: Roger Dingledine Date: Thu, 24 Feb 2005 10:56:55 +0000 (+0000) Subject: add support for CONNECTing through https proxies. X-Git-Tag: tor-0.1.0.1-rc~208 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6faaac27064ba6bf0c8e0b1fc819004d3bf585ba;p=thirdparty%2Ftor.git add support for CONNECTing through https proxies. not sure if it works. i don't have an https proxy. svn:r3682 --- diff --git a/src/or/config.c b/src/or/config.c index 7fa6fe853c..d55af562d6 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -131,6 +131,7 @@ static config_var_t config_vars[] = { VAR("Group", STRING, Group, NULL), VAR("HashedControlPassword",STRING, HashedControlPassword, NULL), VAR("HttpProxy", STRING, HttpProxy, NULL), + VAR("HttpsProxy", STRING, HttpsProxy, NULL), VAR("HiddenServiceOptions",LINELIST_V, RendConfigLines, NULL), VAR("HiddenServiceDir", LINELIST_S, RendConfigLines, NULL), VAR("HiddenServicePort", LINELIST_S, RendConfigLines, NULL), @@ -1463,6 +1464,17 @@ options_validate(or_options_t *options) } } + if (options->HttpsProxy) { /* parse it now */ + if (parse_addr_port(options->HttpsProxy, NULL, + &options->HttpsProxyAddr, &options->HttpsProxyPort) < 0) { + log(LOG_WARN,"HttpsProxy failed to parse or resolve. Please fix."); + result = -1; + } + if (options->HttpsProxyPort == 0) { /* give it a default */ + options->HttpsProxyPort = 443; + } + } + if (options->HashedControlPassword) { if (decode_hashed_password(NULL, options->HashedControlPassword)<0) { log_fn(LOG_WARN,"Bad HashedControlPassword: wrong length or bad base64"); diff --git a/src/or/connection_or.c b/src/or/connection_or.c index 7286de869e..48cee77c37 100644 --- a/src/or/connection_or.c +++ b/src/or/connection_or.c @@ -47,6 +47,56 @@ int connection_or_reached_eof(connection_t *conn) { return 0; } +/** Read conn's inbuf. If the http response from the proxy is all + * here, make sure it's good news, and begin the tls handshake. If + * it's bad news, close the connection and return -1. Else return 0 + * and hope for better luck next time. + */ +static int +connection_or_read_proxy_response(connection_t *conn) { + + char *headers; + int status_code; + time_t date_header; + int compression; + + switch (fetch_from_buf_http(conn->inbuf, + &headers, MAX_HEADERS_SIZE, + NULL, NULL, 10000)) { + case -1: /* overflow */ + log_fn(LOG_WARN,"Your https proxy sent back an oversized response. Closing."); + return -1; + case 0: + log_fn(LOG_INFO,"https proxy response not all here yet. Waiting."); + return 0; + /* case 1, fall through */ + } + + if (parse_http_response(headers, &status_code, &date_header, + &compression) < 0) { + log_fn(LOG_WARN,"Unparseable headers (connecting to '%s'). Closing.", + conn->address); + tor_free(headers); + return -1; + } + + if (status_code == 200) { + log_fn(LOG_INFO,"https connect successful (to '%s')! Launching tls.", + conn->address); + if (connection_tls_start_handshake(conn, 0) < 0) { + /* TLS handshaking error of some kind. */ + connection_mark_for_close(conn); + return -1; + } + return 0; + } + /* else, bad news on the status code */ + log_fn(LOG_WARN,"The https proxy sent back a bad status code %d. Closing.", + status_code); + connection_mark_for_close(conn); + return -1; +} + /** Handle any new bytes that have come in on connection conn. * If conn is in 'open' state, hand it to * connection_or_process_cells_from_inbuf() @@ -57,9 +107,14 @@ int connection_or_process_inbuf(connection_t *conn) { tor_assert(conn); tor_assert(conn->type == CONN_TYPE_OR); - if (conn->state != OR_CONN_STATE_OPEN) - return 0; /* don't do anything */ - return connection_or_process_cells_from_inbuf(conn); + switch(conn->state) { + case OR_CONN_STATE_PROXY_READING: + return connection_or_read_proxy_response(conn); + case OR_CONN_STATE_OPEN: + return connection_or_process_cells_from_inbuf(conn); + default: + return 0; /* don't do anything */ + } } /** Connection conn has finished writing and has no bytes left on @@ -76,15 +131,22 @@ int connection_or_finished_flushing(connection_t *conn) { assert_connection_ok(conn,0); - if (conn->state != OR_CONN_STATE_OPEN) { - log_fn(LOG_WARN,"BUG: called in unexpected state %d",conn->state); + switch(conn->state) { + case OR_CONN_STATE_PROXY_FLUSHING: + log_fn(LOG_DEBUG,"finished sending CONNECT to proxy."); + conn->state = OR_CONN_STATE_PROXY_READING; + connection_stop_writing(conn); + break; + case OR_CONN_STATE_OPEN: + connection_stop_writing(conn); + break; + default: + log_fn(LOG_WARN,"BUG: called in unexpected state %d.", conn->state); #ifdef TOR_FRAGILE - tor_assert(0); + tor_assert(0); #endif - return -1; + return -1; } - - connection_stop_writing(conn); return 0; } @@ -99,6 +161,20 @@ int connection_or_finished_connecting(connection_t *conn) log_fn(LOG_INFO,"OR connect() to router at %s:%u finished.", conn->address,conn->port); + if (get_options()->HttpsProxy) { + char buf[1024]; + char addrbuf[INET_NTOA_BUF_LEN]; + struct in_addr in; + + in.s_addr = htonl(conn->addr); + tor_inet_ntoa(&in, addrbuf, sizeof(addrbuf)); + tor_snprintf(buf, sizeof(buf), "CONNECT %s:%d HTTP/1.0\r\n\r\n", + addrbuf, conn->port); + connection_write_to_buf(buf, strlen(buf), conn); + conn->state = OR_CONN_STATE_PROXY_FLUSHING; + return 0; + } + if (connection_tls_start_handshake(conn, 0) < 0) { /* TLS handshaking error of some kind. */ connection_mark_for_close(conn); @@ -212,10 +288,11 @@ connection_t *connection_or_connect(uint32_t addr, uint16_t port, const char *id_digest) { connection_t *conn; routerinfo_t *me; + or_options_t *options = get_options(); tor_assert(id_digest); - if (server_mode(get_options()) && (me=router_get_my_routerinfo()) && + if (server_mode(options) && (me=router_get_my_routerinfo()) && !memcmp(me->identity_digest, id_digest,DIGEST_LEN)) { log_fn(LOG_WARN,"Bug: Client asked me to connect to myself! Refusing."); return NULL; @@ -238,9 +315,16 @@ connection_t *connection_or_connect(uint32_t addr, uint16_t port, conn->state = OR_CONN_STATE_CONNECTING; control_event_or_conn_status(conn, OR_CONN_EVENT_LAUNCHED); + if (options->HttpsProxy) { + /* we shouldn't connect directly. use the https proxy instead. */ + addr = options->HttpsProxyAddr; + port = options->HttpsProxyPort; + } + switch (connection_connect(conn, conn->address, addr, port)) { case -1: - router_mark_as_down(conn->identity_digest); + if (!options->HttpsProxy) + router_mark_as_down(conn->identity_digest); control_event_or_conn_status(conn, OR_CONN_EVENT_FAILED); connection_free(conn); return NULL; @@ -252,12 +336,11 @@ connection_t *connection_or_connect(uint32_t addr, uint16_t port, /* case 1: fall through */ } - if (connection_tls_start_handshake(conn, 0) >= 0) - return conn; - - /* failure */ - connection_mark_for_close(conn); - return NULL; + if (connection_or_finished_connecting(conn) < 0) { + /* already marked for close */ + return NULL; + } + return conn; } /** Begin the tls handshake with conn. receiving is 0 if diff --git a/src/or/directory.c b/src/or/directory.c index d1a813c224..b81f38d8ca 100644 --- a/src/or/directory.c +++ b/src/or/directory.c @@ -62,9 +62,6 @@ char rend_publish_string[] = "/tor/rendezvous/publish"; char rend_fetch_url[] = "/tor/rendezvous/"; #endif -#define MAX_HEADERS_SIZE 50000 -#define MAX_BODY_SIZE 500000 - #define ALLOW_DIRECTORY_TIME_SKEW 30*60 /********* END VARIABLES ************/ @@ -497,7 +494,7 @@ parse_http_url(char *headers, char **url) * if the value of the header is not recognized. * Otherwise, return -1. */ -static int +int parse_http_response(const char *headers, int *code, time_t *date, int *compression) { diff --git a/src/or/or.h b/src/or/or.h index 12d07c2985..953d8ed24b 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -148,6 +148,10 @@ #define MAX_HEX_NICKNAME_LEN (HEX_DIGEST_LEN+1) #define MAX_DIR_SIZE 500000 +/* For http parsing */ +#define MAX_HEADERS_SIZE 50000 +#define MAX_BODY_SIZE 500000 + #ifdef TOR_PERF /** How long do we keep DNS cache entries before purging them? */ #define MAX_DNS_ENTRY_AGE (150*60) @@ -220,11 +224,15 @@ typedef enum { #define _OR_CONN_STATE_MIN 1 /** State for a connection to an OR: waiting for connect() to finish. */ #define OR_CONN_STATE_CONNECTING 1 +/** State for a connection to an OR: waiting for proxy command to flush. */ +#define OR_CONN_STATE_PROXY_FLUSHING 2 +/** State for a connection to an OR: waiting for proxy response. */ +#define OR_CONN_STATE_PROXY_READING 3 /** State for a connection to an OR: SSL is handshaking, not done yet. */ -#define OR_CONN_STATE_HANDSHAKING 2 +#define OR_CONN_STATE_HANDSHAKING 4 /** State for a connection to an OR: Ready to send/receive cells. */ -#define OR_CONN_STATE_OPEN 3 -#define _OR_CONN_STATE_MAX 3 +#define OR_CONN_STATE_OPEN 5 +#define _OR_CONN_STATE_MAX 5 #define _EXIT_CONN_STATE_MIN 1 /** State for an exit connection: waiting for response from dns farm. */ @@ -991,6 +999,10 @@ typedef struct { uint32_t HttpProxyAddr; /**< Parsed IPv4 addr for http proxy, if any */ uint16_t HttpProxyPort; /**< Parsed port for http proxy, if any */ + char *HttpsProxy; /**< hostname[:port] to use as https proxy, if any */ + uint32_t HttpsProxyAddr; /**< Parsed IPv4 addr for https proxy, if any */ + uint16_t HttpsProxyPort; /**< Parsed port for https proxy, if any */ + struct config_line_t *DirServers; /**< List of configuration lines * for directory servers. */ char *MyFamily; /**< Declared family for this OR. */ @@ -1360,6 +1372,9 @@ void directory_post_to_dirservers(uint8_t purpose, const char *payload, size_t payload_len); void directory_get_from_dirserver(uint8_t purpose, const char *resource, int retry_if_no_servers); +int parse_http_response(const char *headers, int *code, time_t *date, + int *compression); + int connection_dir_reached_eof(connection_t *conn); int connection_dir_process_inbuf(connection_t *conn); int connection_dir_finished_flushing(connection_t *conn);