diff --git a/INSTALL b/INSTALL index 007e9396..20998407 100644 --- a/INSTALL +++ b/INSTALL @@ -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 diff --git a/toxcore/TCP_client.c b/toxcore/TCP_client.c index 23ab28c0..dfb4005c 100644 --- a/toxcore/TCP_client.c +++ b/toxcore/TCP_client.c @@ -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; diff --git a/toxcore/TCP_client.h b/toxcore/TCP_client.h index 83cb48ba..8a1aeec1 100644 --- a/toxcore/TCP_client.h +++ b/toxcore/TCP_client.h @@ -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, diff --git a/toxcore/network.c b/toxcore/network.c index 35ef5221..5539de6b 100644 --- a/toxcore/network.c +++ b/toxcore/network.c @@ -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 diff --git a/toxcore/network.h b/toxcore/network.h index ccda7ae9..5f9af490 100644 --- a/toxcore/network.h +++ b/toxcore/network.h @@ -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 diff --git a/toxcore/tox.c b/toxcore/tox.c index 66587011..3f3a05b0 100644 --- a/toxcore/tox.c +++ b/toxcore/tox.c @@ -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); diff --git a/toxcore/tox.h b/toxcore/tox.h index 3b72eede..f2c102ab 100644 --- a/toxcore/tox.h +++ b/toxcore/tox.h @@ -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; /*