Added HTTP proxy support

This commit is contained in:
Maxim Biro 2014-12-13 23:44:51 -05:00
parent 7d4489b872
commit 377da127a1
7 changed files with 153 additions and 15 deletions

View File

@ -12,8 +12,8 @@ without warranty of any kind.
Basic Installation
==================
Briefly, the shell commands `./configure; make; make install' should
configure, build, and install this package. The following
Briefly, the shell command `./configure && make && make install'
should configure, build, and install this package. The following
more-detailed instructions are generic; see the `README' file for
instructions specific to this package. Some packages provide this
`INSTALL' file but do not implement all of the features documented

View File

@ -66,8 +66,67 @@ static int connect_sock_to(sock_t sock, IP_Port ip_port, TCP_Proxy_Info *proxy_i
return 1;
}
/* return 1 on success.
* return 0 on failure.
*/
static int proxy_http_generate_connection_request(TCP_Client_Connection *TCP_conn)
{
char one[] = "CONNECT ";
char two[] = " HTTP/1.1\nHost: ";
char three[] = "\r\n\r\n";
static void socks5_generate_handshake(TCP_Client_Connection *TCP_conn)
char ip[INET6_ADDRSTRLEN];
if (!ip_parse_addr(&TCP_conn->ip_port.ip, ip, sizeof(ip))) {
return 0;
}
const uint16_t port = ntohs(TCP_conn->ip_port.port);
const int written = sprintf(TCP_conn->last_packet, "%s%s:%hu%s%s:%hu%s", one, ip, port, two, ip, port, three);
if (written < 0) {
return 0;
}
TCP_conn->last_packet_length = written;
TCP_conn->last_packet_sent = 0;
return 1;
}
/* return 1 on success.
* return 0 if no data received.
* return -1 on failure (connection refused).
*/
static int proxy_http_read_connection_response(TCP_Client_Connection *TCP_conn)
{
char success[] = "200";
uint8_t data[16]; // draining works the best if the length is a power of 2
int ret = read_TCP_packet(TCP_conn->sock, data, sizeof(data) - 1);
if (ret == -1) {
return 0;
}
data[sizeof(data) - 1] = '\0';
if (strstr(data, success)) {
// drain all data
// instead of drainining it byte by byte do it in bigger chunks
// decrementing to 1
size_t step = sizeof(data);
do {
if (ret <= 0) {
step = step % 2 == 0 ? step/2 : 1;
}
ret = read_TCP_packet(TCP_conn->sock, data, step);
} while (ret > 0 || step != 1);
return 1;
}
return -1;
}
static void proxy_socks5_generate_handshake(TCP_Client_Connection *TCP_conn)
{
TCP_conn->last_packet[0] = 5; /* SOCKSv5 */
TCP_conn->last_packet[1] = 1; /* number of authentication methods supported */
@ -95,7 +154,7 @@ static int socks5_read_handshake_response(TCP_Client_Connection *TCP_conn)
return -1;
}
static void socks5_generate_connection_request(TCP_Client_Connection *TCP_conn)
static void proxy_socks5_generate_connection_request(TCP_Client_Connection *TCP_conn)
{
TCP_conn->last_packet[0] = 5; /* SOCKSv5 */
TCP_conn->last_packet[1] = 1; /* command code: establish a TCP/IP stream connection */
@ -125,7 +184,7 @@ static void socks5_generate_connection_request(TCP_Client_Connection *TCP_conn)
* return 0 if no data received.
* return -1 on failure (connection refused).
*/
static int socks5_read_connection_response(TCP_Client_Connection *TCP_conn)
static int proxy_socks5_read_connection_response(TCP_Client_Connection *TCP_conn)
{
if (TCP_conn->ip_port.ip.family == AF_INET) {
uint8_t data[4 + sizeof(IP4) + sizeof(uint16_t)];
@ -578,10 +637,14 @@ TCP_Client_Connection *new_TCP_connection(IP_Port ip_port, const uint8_t *public
encrypt_precompute(temp->public_key, self_secret_key, temp->shared_key);
temp->ip_port = ip_port;
if (proxy_info) {
temp->status = TCP_CLIENT_PROXY_CONNECTING;
if (proxy_info && proxy_info->is_http) {
temp->status = TCP_CLIENT_PROXY_HTTP_CONNECTING;
temp->proxy_info = *proxy_info;
socks5_generate_handshake(temp);
proxy_http_generate_connection_request(temp);
} else if (proxy_info && !proxy_info->is_http) {
temp->status = TCP_CLIENT_PROXY_SOCKS5_CONNECTING;
temp->proxy_info = *proxy_info;
proxy_socks5_generate_handshake(temp);
} else {
temp->status = TCP_CLIENT_CONNECTING;
@ -783,7 +846,23 @@ void do_TCP_connection(TCP_Client_Connection *TCP_connection)
return;
}
if (TCP_connection->status == TCP_CLIENT_PROXY_CONNECTING) {
if (TCP_connection->status == TCP_CLIENT_PROXY_HTTP_CONNECTING) {
if (send_pending_data(TCP_connection) == 0) {
int ret = proxy_http_read_connection_response(TCP_connection);
if (ret == -1) {
TCP_connection->kill_at = 0;
TCP_connection->status = TCP_CLIENT_DISCONNECTED;
}
if (ret == 1) {
generate_handshake(TCP_connection);
TCP_connection->status = TCP_CLIENT_CONNECTING;
}
}
}
if (TCP_connection->status == TCP_CLIENT_PROXY_SOCKS5_CONNECTING) {
if (send_pending_data(TCP_connection) == 0) {
int ret = socks5_read_handshake_response(TCP_connection);
@ -793,15 +872,15 @@ void do_TCP_connection(TCP_Client_Connection *TCP_connection)
}
if (ret == 1) {
socks5_generate_connection_request(TCP_connection);
TCP_connection->status = TCP_CLIENT_PROXY_UNCONFIRMED;
proxy_socks5_generate_connection_request(TCP_connection);
TCP_connection->status = TCP_CLIENT_PROXY_SOCKS5_UNCONFIRMED;
}
}
}
if (TCP_connection->status == TCP_CLIENT_PROXY_UNCONFIRMED) {
if (TCP_connection->status == TCP_CLIENT_PROXY_SOCKS5_UNCONFIRMED) {
if (send_pending_data(TCP_connection) == 0) {
int ret = socks5_read_connection_response(TCP_connection);
int ret = proxy_socks5_read_connection_response(TCP_connection);
if (ret == -1) {
TCP_connection->kill_at = 0;

View File

@ -31,12 +31,14 @@
typedef struct {
IP_Port ip_port;
int is_http; /* HTTP proxy on true, SOCKS5 on false */
} TCP_Proxy_Info;
enum {
TCP_CLIENT_NO_STATUS,
TCP_CLIENT_PROXY_CONNECTING,
TCP_CLIENT_PROXY_UNCONFIRMED,
TCP_CLIENT_PROXY_HTTP_CONNECTING,
TCP_CLIENT_PROXY_SOCKS5_CONNECTING,
TCP_CLIENT_PROXY_SOCKS5_UNCONFIRMED,
TCP_CLIENT_CONNECTING,
TCP_CLIENT_UNCONFIRMED,
TCP_CLIENT_CONFIRMED,

View File

@ -784,6 +784,9 @@ void ipport_unpack(IP_Port *target, const uint8_t *data)
/* ip_ntoa
* converts ip into a string
* uses a static buffer, so mustn't used multiple times in the same output
*
* IPv6 addresses are enclosed into square brackets, i.e. "[IPv6]"
* writes error message into the buffer on error
*/
/* there would be INET6_ADDRSTRLEN, but it might be too short for the error message */
static char addresstext[96];
@ -815,6 +818,38 @@ const char *ip_ntoa(const IP *ip)
return addresstext;
}
/*
* ip_parse_addr
* parses IP structure into an address string
*
* input
* ip: ip of AF_INET or AF_INET6 families
* length: length of the address buffer
* Must be at least INET_ADDRSTRLEN for AF_INET
* and INET6_ADDRSTRLEN for AF_INET6
*
* output
* address: dotted notation (IPv4: quad, IPv6: 16) or colon notation (IPv6)
*
* returns 1 on success, 0 on failure
*/
int ip_parse_addr(const IP *ip, char *address, size_t length)
{
if (!address || !ip) {
return 0;
}
if (ip->family == AF_INET) {
struct in_addr *addr = (struct in_addr *)&ip->ip4;
return inet_ntop(ip->family, addr, address, length) != NULL;
} else if (ip->family == AF_INET6) {
struct in6_addr *addr = (struct in6_addr *)&ip->ip6;
return inet_ntop(ip->family, addr, address, length) != NULL;
}
return 0;
}
/*
* addr_parse_ip
* directly parses the input into an IP structure

View File

@ -180,9 +180,29 @@ IP_Port;
/* ip_ntoa
* converts ip into a string
* uses a static buffer, so mustn't used multiple times in the same output
*
* IPv6 addresses are enclosed into square brackets, i.e. "[IPv6]"
* writes error message into the buffer on error
*/
const char *ip_ntoa(const IP *ip);
/*
* ip_parse_addr
* parses IP structure into an address string
*
* input
* ip: ip of AF_INET or AF_INET6 families
* length: length of the address buffer
* Must be at least INET_ADDRSTRLEN for AF_INET
* and INET6_ADDRSTRLEN for AF_INET6
*
* output
* address: dotted notation (IPv4: quad, IPv6: 16) or colon notation (IPv6)
*
* returns 1 on success, 0 on failure
*/
int ip_parse_addr(const IP *ip, char *address, size_t length);
/*
* addr_parse_ip
* directly parses the input into an IP structure

View File

@ -1021,6 +1021,7 @@ Tox *tox_new(Tox_Options *options)
m_options.ipv6enabled = options->ipv6enabled;
m_options.udp_disabled = options->udp_disabled;
m_options.proxy_enabled = options->proxy_enabled;
m_options.proxy_info.is_http = options->proxy_is_http;
if (m_options.proxy_enabled) {
ip_init(&m_options.proxy_info.ip_port.ip, m_options.ipv6enabled);

View File

@ -892,6 +892,7 @@ typedef struct {
uint8_t proxy_enabled;
char proxy_address[256]; /* Proxy ip or domain in NULL terminated string format. */
uint16_t proxy_port; /* Proxy port: in host byte order. */
uint8_t proxy_is_http; /*1: HTTP proxy, 0: SOCKS5 proxy*/
} Tox_Options;
/*