Blame libfreerdp/core/proxy.c

Packit Service fa4841
/**
Packit Service fa4841
 * FreeRDP: A Remote Desktop Protocol Implementation
Packit Service fa4841
 * HTTP Proxy support
Packit Service fa4841
 *
Packit Service fa4841
 * Copyright 2016 Christian Plattner <ccpp@gmx.at>
Packit Service fa4841
 *
Packit Service fa4841
 * Licensed under the Apache License, Version 2.0 (the "License");
Packit Service fa4841
 * you may not use this file except in compliance with the License.
Packit Service fa4841
 * You may obtain a copy of the License at
Packit Service fa4841
 *
Packit Service fa4841
 *     http://www.apache.org/licenses/LICENSE-2.0
Packit Service fa4841
 *
Packit Service fa4841
 * Unless required by applicable law or agreed to in writing, software
Packit Service fa4841
 * distributed under the License is distributed on an "AS IS" BASIS,
Packit Service fa4841
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Packit Service fa4841
 * See the License for the specific language governing permissions and
Packit Service fa4841
 * limitations under the License.
Packit Service fa4841
 */
Packit Service fa4841
Packit Service fa4841
#include <ctype.h>
Packit Service fa4841
#include <errno.h>
Packit Service fa4841
Packit Service fa4841
#include "proxy.h"
Packit Service fa4841
#include "freerdp/settings.h"
Packit Service fa4841
#include "tcp.h"
Packit Service fa4841
Packit Service fa4841
#include "winpr/environment.h" /* For GetEnvironmentVariableA */
Packit Service fa4841
Packit Service fa4841
#define CRLF "\r\n"
Packit Service fa4841
#define TAG FREERDP_TAG("core.proxy")
Packit Service fa4841
Packit Service fa4841
/* SOCKS Proxy auth methods by rfc1928 */
Packit Service fa4841
enum
Packit Service fa4841
{
Packit Service fa4841
	AUTH_M_NO_AUTH = 0,
Packit Service fa4841
	AUTH_M_GSSAPI = 1,
Packit Service fa4841
	AUTH_M_USR_PASS = 2
Packit Service fa4841
};
Packit Service fa4841
Packit Service fa4841
enum
Packit Service fa4841
{
Packit Service fa4841
	SOCKS_CMD_CONNECT = 1,
Packit Service fa4841
	SOCKS_CMD_BIND = 2,
Packit Service fa4841
	SOCKS_CMD_UDP_ASSOCIATE = 3
Packit Service fa4841
};
Packit Service fa4841
Packit Service fa4841
enum
Packit Service fa4841
{
Packit Service fa4841
	SOCKS_ADDR_IPV4 = 1,
Packit Service fa4841
	SOCKS_ADDR_FQDN = 3,
Packit Service fa4841
	SOCKS_ADDR_IPV6 = 4,
Packit Service fa4841
};
Packit Service fa4841
Packit Service fa4841
/* CONN REQ replies in enum. order */
Packit Service fa4841
static const char* rplstat[] = { "succeeded",
Packit Service fa4841
	                             "general SOCKS server failure",
Packit Service fa4841
	                             "connection not allowed by ruleset",
Packit Service fa4841
	                             "Network unreachable",
Packit Service fa4841
	                             "Host unreachable",
Packit Service fa4841
	                             "Connection refused",
Packit Service fa4841
	                             "TTL expired",
Packit Service fa4841
	                             "Command not supported",
Packit Service fa4841
	                             "Address type not supported" };
Packit Service fa4841
Packit Service fa4841
static BOOL http_proxy_connect(BIO* bufferedBio, const char* hostname, UINT16 port);
Packit Service fa4841
static BOOL socks_proxy_connect(BIO* bufferedBio, const char* proxyUsername,
Packit Service fa4841
                                const char* proxyPassword, const char* hostname, UINT16 port);
Packit Service fa4841
static void proxy_read_environment(rdpSettings* settings, char* envname);
Packit Service fa4841
static BOOL proxy_parse_uri(rdpSettings* settings, const char* uri);
Packit Service fa4841
Packit Service fa4841
BOOL proxy_prepare(rdpSettings* settings, const char** lpPeerHostname, UINT16* lpPeerPort,
Packit Service fa4841
                   const char** lpProxyUsername, const char** lpProxyPassword)
Packit Service fa4841
{
Packit Service fa4841
	if (settings->ProxyType == PROXY_TYPE_IGNORE)
Packit Service fa4841
		return FALSE;
Packit Service fa4841
Packit Service fa4841
	/* For TSGateway, find the system HTTPS proxy automatically */
Packit Service fa4841
	if (settings->ProxyType == PROXY_TYPE_NONE)
Packit Service fa4841
		proxy_read_environment(settings, "https_proxy");
Packit Service fa4841
Packit Service fa4841
	if (settings->ProxyType == PROXY_TYPE_NONE)
Packit Service fa4841
		proxy_read_environment(settings, "HTTPS_PROXY");
Packit Service fa4841
Packit Service fa4841
	if (settings->ProxyType != PROXY_TYPE_NONE)
Packit Service fa4841
		proxy_read_environment(settings, "no_proxy");
Packit Service fa4841
Packit Service fa4841
	if (settings->ProxyType != PROXY_TYPE_NONE)
Packit Service fa4841
		proxy_read_environment(settings, "NO_PROXY");
Packit Service fa4841
Packit Service fa4841
	if (settings->ProxyType != PROXY_TYPE_NONE)
Packit Service fa4841
	{
Packit Service fa4841
		*lpPeerHostname = settings->ProxyHostname;
Packit Service fa4841
		*lpPeerPort = settings->ProxyPort;
Packit Service fa4841
		*lpProxyUsername = settings->ProxyUsername;
Packit Service fa4841
		*lpProxyPassword = settings->ProxyPassword;
Packit Service fa4841
		return TRUE;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	return FALSE;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
static BOOL cidr4_match(const struct in_addr* addr, const struct in_addr* net, BYTE bits)
Packit Service fa4841
{
Packit Service fa4841
	uint32_t mask, amask, nmask;
Packit Service fa4841
Packit Service fa4841
	if (bits == 0)
Packit Service fa4841
		return TRUE;
Packit Service fa4841
Packit Service fa4841
	mask = htonl(0xFFFFFFFFu << (32 - bits));
Packit Service fa4841
	amask = addr->s_addr & mask;
Packit Service fa4841
	nmask = net->s_addr & mask;
Packit Service fa4841
	return amask == nmask;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
static BOOL cidr6_match(const struct in6_addr* address, const struct in6_addr* network,
Packit Service fa4841
                        uint8_t bits)
Packit Service fa4841
{
Packit Service fa4841
	const uint32_t* a = (const uint32_t*)address;
Packit Service fa4841
	const uint32_t* n = (const uint32_t*)network;
Packit Service fa4841
	size_t bits_whole, bits_incomplete;
Packit Service fa4841
	bits_whole = bits >> 5;
Packit Service fa4841
	bits_incomplete = bits & 0x1F;
Packit Service fa4841
Packit Service fa4841
	if (bits_whole)
Packit Service fa4841
	{
Packit Service fa4841
		if (memcmp(a, n, bits_whole << 2) != 0)
Packit Service fa4841
			return FALSE;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	if (bits_incomplete)
Packit Service fa4841
	{
Packit Service fa4841
		uint32_t mask = htonl((0xFFFFFFFFu) << (32 - bits_incomplete));
Packit Service fa4841
Packit Service fa4841
		if ((a[bits_whole] ^ n[bits_whole]) & mask)
Packit Service fa4841
			return FALSE;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	return TRUE;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
static BOOL check_no_proxy(rdpSettings* settings, const char* no_proxy)
Packit Service fa4841
{
Packit Service fa4841
	const char* delimiter = ",";
Packit Service fa4841
	BOOL result = FALSE;
Packit Service fa4841
	char* current;
Packit Service fa4841
	char* copy;
Packit Service fa4841
	char* context = NULL;
Packit Service fa4841
	size_t host_len;
Packit Service fa4841
	struct sockaddr_in sa4;
Packit Service fa4841
	struct sockaddr_in6 sa6;
Packit Service fa4841
	BOOL is_ipv4 = FALSE;
Packit Service fa4841
	BOOL is_ipv6 = FALSE;
Packit Service fa4841
Packit Service fa4841
	if (!no_proxy || !settings)
Packit Service fa4841
		return FALSE;
Packit Service fa4841
Packit Service fa4841
	if (inet_pton(AF_INET, settings->ServerHostname, &sa4.sin_addr) == 1)
Packit Service fa4841
		is_ipv4 = TRUE;
Packit Service fa4841
	else if (inet_pton(AF_INET6, settings->ServerHostname, &sa6.sin6_addr) == 1)
Packit Service fa4841
		is_ipv6 = TRUE;
Packit Service fa4841
Packit Service fa4841
	host_len = strlen(settings->ServerHostname);
Packit Service fa4841
	copy = _strdup(no_proxy);
Packit Service fa4841
Packit Service fa4841
	if (!copy)
Packit Service fa4841
		return FALSE;
Packit Service fa4841
Packit Service fa4841
	current = strtok_s(copy, delimiter, &context);
Packit Service fa4841
Packit Service fa4841
	while (current && !result)
Packit Service fa4841
	{
Packit Service fa4841
		const size_t currentlen = strlen(current);
Packit Service fa4841
Packit Service fa4841
		if (currentlen > 0)
Packit Service fa4841
		{
Packit Service fa4841
			WLog_DBG(TAG, "%s => %s (%" PRIdz ")", settings->ServerHostname, current, currentlen);
Packit Service fa4841
Packit Service fa4841
			/* detect left and right "*" wildcard */
Packit Service fa4841
			if (current[0] == '*')
Packit Service fa4841
			{
Packit Service fa4841
				if (host_len >= currentlen)
Packit Service fa4841
				{
Packit Service fa4841
					const size_t offset = host_len + 1 - currentlen;
Packit Service fa4841
					const char* name = settings->ServerHostname + offset;
Packit Service fa4841
Packit Service fa4841
					if (strncmp(current + 1, name, currentlen - 1) == 0)
Packit Service fa4841
						result = TRUE;
Packit Service fa4841
				}
Packit Service fa4841
			}
Packit Service fa4841
			else if (current[currentlen - 1] == '*')
Packit Service fa4841
			{
Packit Service fa4841
				if (strncmp(current, settings->ServerHostname, currentlen - 1) == 0)
Packit Service fa4841
					result = TRUE;
Packit Service fa4841
			}
Packit Service fa4841
			else if (current[0] ==
Packit Service fa4841
			         '.') /* Only compare if the no_proxy variable contains a whole domain. */
Packit Service fa4841
			{
Packit Service fa4841
				if (host_len > currentlen)
Packit Service fa4841
				{
Packit Service fa4841
					const size_t offset = host_len - currentlen;
Packit Service fa4841
					const char* name = settings->ServerHostname + offset;
Packit Service fa4841
Packit Service fa4841
					if (strncmp(current, name, currentlen) == 0)
Packit Service fa4841
						result = TRUE; /* right-aligned match for host names */
Packit Service fa4841
				}
Packit Service fa4841
			}
Packit Service fa4841
			else if (strcmp(current, settings->ServerHostname) == 0)
Packit Service fa4841
				result = TRUE; /* exact match */
Packit Service fa4841
			else if (is_ipv4 || is_ipv6)
Packit Service fa4841
			{
Packit Service fa4841
				char* rangedelim = strchr(current, '/');
Packit Service fa4841
Packit Service fa4841
				/* Check for IP ranges */
Packit Service fa4841
				if (rangedelim != NULL)
Packit Service fa4841
				{
Packit Service fa4841
					const char* range = rangedelim + 1;
Packit Service fa4841
					int sub;
Packit Service fa4841
					int rc = sscanf(range, "%u", &sub);
Packit Service fa4841
Packit Service fa4841
					if ((rc == 1) && (rc >= 0))
Packit Service fa4841
					{
Packit Service fa4841
						*rangedelim = '\0';
Packit Service fa4841
Packit Service fa4841
						if (is_ipv4)
Packit Service fa4841
						{
Packit Service fa4841
							struct sockaddr_in mask;
Packit Service fa4841
Packit Service fa4841
							if (inet_pton(AF_INET, current, &mask.sin_addr))
Packit Service fa4841
								result = cidr4_match(&sa4.sin_addr, &mask.sin_addr, sub);
Packit Service fa4841
						}
Packit Service fa4841
						else if (is_ipv6)
Packit Service fa4841
						{
Packit Service fa4841
							struct sockaddr_in6 mask;
Packit Service fa4841
Packit Service fa4841
							if (inet_pton(AF_INET6, current, &mask.sin6_addr))
Packit Service fa4841
								result = cidr6_match(&sa6.sin6_addr, &mask.sin6_addr, sub);
Packit Service fa4841
						}
Packit Service fa4841
					}
Packit Service fa4841
					else
Packit Service fa4841
						WLog_WARN(TAG, "NO_PROXY invalid entry %s", current);
Packit Service fa4841
				}
Packit Service fa4841
				else if (strncmp(current, settings->ServerHostname, currentlen) == 0)
Packit Service fa4841
					result = TRUE; /* left-aligned match for IPs */
Packit Service fa4841
			}
Packit Service fa4841
		}
Packit Service fa4841
Packit Service fa4841
		current = strtok_s(NULL, delimiter, &context);
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	free(copy);
Packit Service fa4841
	return result;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
void proxy_read_environment(rdpSettings* settings, char* envname)
Packit Service fa4841
{
Packit Service fa4841
	DWORD envlen;
Packit Service fa4841
	char* env;
Packit Service fa4841
	envlen = GetEnvironmentVariableA(envname, NULL, 0);
Packit Service fa4841
Packit Service fa4841
	if (!envlen)
Packit Service fa4841
		return;
Packit Service fa4841
Packit Service fa4841
	env = calloc(1, envlen);
Packit Service fa4841
Packit Service fa4841
	if (!env)
Packit Service fa4841
	{
Packit Service fa4841
		WLog_ERR(TAG, "Not enough memory");
Packit Service fa4841
		return;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	if (GetEnvironmentVariableA(envname, env, envlen) == envlen - 1)
Packit Service fa4841
	{
Packit Service fa4841
		if (_strnicmp("NO_PROXY", envname, 9) == 0)
Packit Service fa4841
		{
Packit Service fa4841
			if (check_no_proxy(settings, env))
Packit Service fa4841
			{
Packit Service fa4841
				WLog_INFO(TAG, "deactivating proxy: %s [%s=%s]", settings->ServerHostname, envname,
Packit Service fa4841
				          env);
Packit Service fa4841
				settings->ProxyType = PROXY_TYPE_NONE;
Packit Service fa4841
			}
Packit Service fa4841
		}
Packit Service fa4841
		else
Packit Service fa4841
		{
Packit Service fa4841
			proxy_parse_uri(settings, env);
Packit Service fa4841
		}
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	free(env);
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
BOOL proxy_parse_uri(rdpSettings* settings, const char* uri)
Packit Service fa4841
{
Packit Service fa4841
	const char *hostname, *pport;
Packit Service fa4841
	const char* protocol;
Packit Service fa4841
	const char* p;
Packit Service fa4841
	UINT16 port;
Packit Service fa4841
	int hostnamelen;
Packit Service fa4841
	p = strstr(uri, "://");
Packit Service fa4841
Packit Service fa4841
	if (p)
Packit Service fa4841
	{
Packit Service fa4841
		if (p == uri + 4 && !strncmp("http", uri, 4))
Packit Service fa4841
		{
Packit Service fa4841
			settings->ProxyType = PROXY_TYPE_HTTP;
Packit Service fa4841
			protocol = "http";
Packit Service fa4841
		}
Packit Service fa4841
		else if (p == uri + 6 && !strncmp("socks5", uri, 6))
Packit Service fa4841
		{
Packit Service fa4841
			settings->ProxyType = PROXY_TYPE_SOCKS;
Packit Service fa4841
			protocol = "socks5";
Packit Service fa4841
		}
Packit Service fa4841
		else
Packit Service fa4841
		{
Packit Service fa4841
			WLog_ERR(TAG, "Only HTTP and SOCKS5 proxies supported by now");
Packit Service fa4841
			return FALSE;
Packit Service fa4841
		}
Packit Service fa4841
Packit Service fa4841
		uri = p + 3;
Packit Service fa4841
	}
Packit Service fa4841
	else
Packit Service fa4841
	{
Packit Service fa4841
		WLog_ERR(TAG, "No scheme in proxy URI");
Packit Service fa4841
		return FALSE;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	hostname = uri;
Packit Service fa4841
	pport = strchr(hostname, ':');
Packit Service fa4841
Packit Service fa4841
	if (pport)
Packit Service fa4841
	{
Packit Service fa4841
		long val;
Packit Service fa4841
		errno = 0;
Packit Service fa4841
		val = strtol(pport + 1, NULL, 0);
Packit Service fa4841
Packit Service fa4841
		if ((errno != 0) || (val <= 0) || (val > UINT16_MAX))
Packit Service fa4841
			return FALSE;
Packit Service fa4841
Packit Service fa4841
		port = val;
Packit Service fa4841
	}
Packit Service fa4841
	else
Packit Service fa4841
	{
Packit Service fa4841
		/* The default is 80. Also for Proxys. */
Packit Service fa4841
		port = 80;
Packit Service fa4841
		pport = strchr(hostname, '/');
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	if (pport)
Packit Service fa4841
	{
Packit Service fa4841
		hostnamelen = pport - hostname;
Packit Service fa4841
	}
Packit Service fa4841
	else
Packit Service fa4841
	{
Packit Service fa4841
		hostnamelen = strlen(hostname);
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	settings->ProxyHostname = calloc(hostnamelen + 1, 1);
Packit Service fa4841
Packit Service fa4841
	if (!settings->ProxyHostname)
Packit Service fa4841
	{
Packit Service fa4841
		WLog_ERR(TAG, "Not enough memory");
Packit Service fa4841
		return FALSE;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	memcpy(settings->ProxyHostname, hostname, hostnamelen);
Packit Service fa4841
	settings->ProxyPort = port;
Packit Service fa4841
	WLog_INFO(TAG, "Parsed proxy configuration: %s://%s:%d", protocol, settings->ProxyHostname,
Packit Service fa4841
	          settings->ProxyPort);
Packit Service fa4841
	return TRUE;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
BOOL proxy_connect(rdpSettings* settings, BIO* bufferedBio, const char* proxyUsername,
Packit Service fa4841
                   const char* proxyPassword, const char* hostname, UINT16 port)
Packit Service fa4841
{
Packit Service fa4841
	switch (settings->ProxyType)
Packit Service fa4841
	{
Packit Service fa4841
		case PROXY_TYPE_NONE:
Packit Service fa4841
		case PROXY_TYPE_IGNORE:
Packit Service fa4841
			return TRUE;
Packit Service fa4841
Packit Service fa4841
		case PROXY_TYPE_HTTP:
Packit Service fa4841
			return http_proxy_connect(bufferedBio, hostname, port);
Packit Service fa4841
Packit Service fa4841
		case PROXY_TYPE_SOCKS:
Packit Service fa4841
			return socks_proxy_connect(bufferedBio, proxyUsername, proxyPassword, hostname, port);
Packit Service fa4841
Packit Service fa4841
		default:
Packit Service fa4841
			WLog_ERR(TAG, "Invalid internal proxy configuration");
Packit Service fa4841
			return FALSE;
Packit Service fa4841
	}
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
static const char* get_response_header(char* response)
Packit Service fa4841
{
Packit Service fa4841
	char* current_pos = strchr(response, '\r');
Packit Service fa4841
	if (!current_pos)
Packit Service fa4841
		current_pos = strchr(response, '\n');
Packit Service fa4841
Packit Service fa4841
	if (current_pos)
Packit Service fa4841
		*current_pos = '\0';
Packit Service fa4841
Packit Service fa4841
	return response;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
static BOOL http_proxy_connect(BIO* bufferedBio, const char* hostname, UINT16 port)
Packit Service fa4841
{
Packit Service fa4841
	int status;
Packit Service fa4841
	wStream* s;
Packit Service fa4841
	char port_str[10], recv_buf[256], *eol;
Packit Service fa4841
	size_t resultsize;
Packit Service fa4841
	_itoa_s(port, port_str, sizeof(port_str), 10);
Packit Service fa4841
	s = Stream_New(NULL, 200);
Packit Service fa4841
	Stream_Write(s, "CONNECT ", 8);
Packit Service fa4841
	Stream_Write(s, hostname, strlen(hostname));
Packit Service fa4841
	Stream_Write_UINT8(s, ':');
Packit Service fa4841
	Stream_Write(s, port_str, strlen(port_str));
Packit Service fa4841
	Stream_Write(s, " HTTP/1.1" CRLF "Host: ", 17);
Packit Service fa4841
	Stream_Write(s, hostname, strlen(hostname));
Packit Service fa4841
	Stream_Write_UINT8(s, ':');
Packit Service fa4841
	Stream_Write(s, port_str, strnlen(port_str, sizeof(port_str)));
Packit Service fa4841
	Stream_Write(s, CRLF CRLF, 4);
Packit Service fa4841
	status = BIO_write(bufferedBio, Stream_Buffer(s), Stream_GetPosition(s));
Packit Service fa4841
Packit Service fa4841
	if ((status < 0) || ((size_t)status != Stream_GetPosition(s)))
Packit Service fa4841
	{
Packit Service fa4841
		Stream_Free(s, TRUE);
Packit Service fa4841
		WLog_ERR(TAG, "HTTP proxy: failed to write CONNECT request");
Packit Service fa4841
		return FALSE;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	Stream_Free(s, TRUE);
Packit Service fa4841
	s = NULL;
Packit Service fa4841
	/* Read result until CR-LF-CR-LF.
Packit Service fa4841
	 * Keep recv_buf a null-terminated string. */
Packit Service fa4841
	memset(recv_buf, '\0', sizeof(recv_buf));
Packit Service fa4841
	resultsize = 0;
Packit Service fa4841
Packit Service fa4841
	while (strstr(recv_buf, CRLF CRLF) == NULL)
Packit Service fa4841
	{
Packit Service fa4841
		if (resultsize >= sizeof(recv_buf) - 1)
Packit Service fa4841
		{
Packit Service fa4841
			WLog_ERR(TAG, "HTTP Reply headers too long: %s", get_response_header(recv_buf));
Packit Service fa4841
			return FALSE;
Packit Service fa4841
		}
Packit Service fa4841
Packit Service fa4841
		status =
Packit Service fa4841
		    BIO_read(bufferedBio, (BYTE*)recv_buf + resultsize, sizeof(recv_buf) - resultsize - 1);
Packit Service fa4841
Packit Service fa4841
		if (status < 0)
Packit Service fa4841
		{
Packit Service fa4841
			/* Error? */
Packit Service fa4841
			if (BIO_should_retry(bufferedBio))
Packit Service fa4841
			{
Packit Service fa4841
				USleep(100);
Packit Service fa4841
				continue;
Packit Service fa4841
			}
Packit Service fa4841
Packit Service fa4841
			WLog_ERR(TAG, "Failed reading reply from HTTP proxy (Status %d)", status);
Packit Service fa4841
			return FALSE;
Packit Service fa4841
		}
Packit Service fa4841
		else if (status == 0)
Packit Service fa4841
		{
Packit Service fa4841
			/* Error? */
Packit Service fa4841
			WLog_ERR(TAG, "Failed reading reply from HTTP proxy (BIO_read returned zero)");
Packit Service fa4841
			return FALSE;
Packit Service fa4841
		}
Packit Service fa4841
Packit Service fa4841
		resultsize += status;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	/* Extract HTTP status line */
Packit Service fa4841
	eol = strchr(recv_buf, '\r');
Packit Service fa4841
Packit Service fa4841
	if (!eol)
Packit Service fa4841
	{
Packit Service fa4841
		/* should never happen */
Packit Service fa4841
		return FALSE;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	*eol = '\0';
Packit Service fa4841
	WLog_INFO(TAG, "HTTP Proxy: %s", recv_buf);
Packit Service fa4841
Packit Service fa4841
	if (strnlen(recv_buf, sizeof(recv_buf)) < 12)
Packit Service fa4841
	{
Packit Service fa4841
		return FALSE;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	recv_buf[7] = 'X';
Packit Service fa4841
Packit Service fa4841
	if (strncmp(recv_buf, "HTTP/1.X 200", 12))
Packit Service fa4841
		return FALSE;
Packit Service fa4841
Packit Service fa4841
	return TRUE;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
static int recv_socks_reply(BIO* bufferedBio, BYTE* buf, int len, char* reason, BYTE checkVer)
Packit Service fa4841
{
Packit Service fa4841
	int status;
Packit Service fa4841
Packit Service fa4841
	for (;;)
Packit Service fa4841
	{
Packit Service fa4841
		status = BIO_read(bufferedBio, buf, len);
Packit Service fa4841
Packit Service fa4841
		if (status > 0)
Packit Service fa4841
		{
Packit Service fa4841
			break;
Packit Service fa4841
		}
Packit Service fa4841
		else if (status < 0)
Packit Service fa4841
		{
Packit Service fa4841
			/* Error? */
Packit Service fa4841
			if (BIO_should_retry(bufferedBio))
Packit Service fa4841
			{
Packit Service fa4841
				USleep(100);
Packit Service fa4841
				continue;
Packit Service fa4841
			}
Packit Service fa4841
Packit Service fa4841
			WLog_ERR(TAG, "Failed reading %s reply from SOCKS proxy (Status %d)", reason, status);
Packit Service fa4841
			return -1;
Packit Service fa4841
		}
Packit Service fa4841
		else // if (status == 0)
Packit Service fa4841
		{
Packit Service fa4841
			/* Error? */
Packit Service fa4841
			WLog_ERR(TAG, "Failed reading %s reply from SOCKS proxy (BIO_read returned zero)",
Packit Service fa4841
			         reason);
Packit Service fa4841
			return -1;
Packit Service fa4841
		}
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	if (status < 2)
Packit Service fa4841
	{
Packit Service fa4841
		WLog_ERR(TAG, "SOCKS Proxy reply packet too short (%s)", reason);
Packit Service fa4841
		return -1;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	if (buf[0] != checkVer)
Packit Service fa4841
	{
Packit Service fa4841
		WLog_ERR(TAG, "SOCKS Proxy version is not 5 (%s)", reason);
Packit Service fa4841
		return -1;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	return status;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
static BOOL socks_proxy_connect(BIO* bufferedBio, const char* proxyUsername,
Packit Service fa4841
                                const char* proxyPassword, const char* hostname, UINT16 port)
Packit Service fa4841
{
Packit Service fa4841
	int status;
Packit Service fa4841
	int nauthMethods = 1, writeLen = 3;
Packit Service fa4841
	BYTE buf[3 + 255 + 255]; /* biggest packet is user/pass auth */
Packit Service fa4841
	size_t hostnlen = strnlen(hostname, 255);
Packit Service fa4841
Packit Service fa4841
	if (proxyUsername && proxyPassword)
Packit Service fa4841
	{
Packit Service fa4841
		nauthMethods++;
Packit Service fa4841
		writeLen++;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	/* select auth. method */
Packit Service fa4841
	buf[0] = 5;            /* SOCKS version */
Packit Service fa4841
	buf[1] = nauthMethods; /* #of methods offered */
Packit Service fa4841
	buf[2] = AUTH_M_NO_AUTH;
Packit Service fa4841
Packit Service fa4841
	if (nauthMethods > 1)
Packit Service fa4841
		buf[3] = AUTH_M_USR_PASS;
Packit Service fa4841
Packit Service fa4841
	status = BIO_write(bufferedBio, buf, writeLen);
Packit Service fa4841
Packit Service fa4841
	if (status != writeLen)
Packit Service fa4841
	{
Packit Service fa4841
		WLog_ERR(TAG, "SOCKS proxy: failed to write AUTH METHOD request");
Packit Service fa4841
		return FALSE;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	status = recv_socks_reply(bufferedBio, buf, 2, "AUTH REQ", 5);
Packit Service fa4841
Packit Service fa4841
	if (status <= 0)
Packit Service fa4841
		return FALSE;
Packit Service fa4841
Packit Service fa4841
	switch (buf[1])
Packit Service fa4841
	{
Packit Service fa4841
		case AUTH_M_NO_AUTH:
Packit Service fa4841
			WLog_DBG(TAG, "SOCKS Proxy: (NO AUTH) method was selected");
Packit Service fa4841
			break;
Packit Service fa4841
Packit Service fa4841
		case AUTH_M_USR_PASS:
Packit Service fa4841
			if (!proxyUsername || !proxyPassword)
Packit Service fa4841
				return FALSE;
Packit Service fa4841
			else
Packit Service fa4841
			{
Packit Service fa4841
				int usernameLen = strnlen(proxyUsername, 255);
Packit Service fa4841
				int userpassLen = strnlen(proxyPassword, 255);
Packit Service fa4841
				BYTE* ptr;
Packit Service fa4841
Packit Service fa4841
				if (nauthMethods < 2)
Packit Service fa4841
				{
Packit Service fa4841
					WLog_ERR(TAG, "SOCKS Proxy: USER/PASS method was not proposed to server");
Packit Service fa4841
					return FALSE;
Packit Service fa4841
				}
Packit Service fa4841
Packit Service fa4841
				/* user/password v1 method */
Packit Service fa4841
				ptr = buf + 2;
Packit Service fa4841
				buf[0] = 1;
Packit Service fa4841
				buf[1] = usernameLen;
Packit Service fa4841
				memcpy(ptr, proxyUsername, usernameLen);
Packit Service fa4841
				ptr += usernameLen;
Packit Service fa4841
				*ptr = userpassLen;
Packit Service fa4841
				ptr++;
Packit Service fa4841
				memcpy(ptr, proxyPassword, userpassLen);
Packit Service fa4841
				status = BIO_write(bufferedBio, buf, 3 + usernameLen + userpassLen);
Packit Service fa4841
Packit Service fa4841
				if (status != 3 + usernameLen + userpassLen)
Packit Service fa4841
				{
Packit Service fa4841
					WLog_ERR(TAG, "SOCKS Proxy: error writing user/password request");
Packit Service fa4841
					return FALSE;
Packit Service fa4841
				}
Packit Service fa4841
Packit Service fa4841
				status = recv_socks_reply(bufferedBio, buf, 2, "AUTH REQ", 1);
Packit Service fa4841
Packit Service fa4841
				if (status < 2)
Packit Service fa4841
					return FALSE;
Packit Service fa4841
Packit Service fa4841
				if (buf[1] != 0x00)
Packit Service fa4841
				{
Packit Service fa4841
					WLog_ERR(TAG, "SOCKS Proxy: invalid user/password");
Packit Service fa4841
					return FALSE;
Packit Service fa4841
				}
Packit Service fa4841
			}
Packit Service fa4841
Packit Service fa4841
			break;
Packit Service fa4841
Packit Service fa4841
		default:
Packit Service fa4841
			WLog_ERR(TAG, "SOCKS Proxy: unknown method 0x%x was selected by proxy", buf[1]);
Packit Service fa4841
			return FALSE;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	/* CONN request */
Packit Service fa4841
	buf[0] = 5;                 /* SOCKS version */
Packit Service fa4841
	buf[1] = SOCKS_CMD_CONNECT; /* command */
Packit Service fa4841
	buf[2] = 0;                 /* 3rd octet is reserved x00 */
Packit Service fa4841
	buf[3] = SOCKS_ADDR_FQDN;   /* addr.type */
Packit Service fa4841
	buf[4] = hostnlen;          /* DST.ADDR */
Packit Service fa4841
	memcpy(buf + 5, hostname, hostnlen);
Packit Service fa4841
	/* follows DST.PORT in netw. format */
Packit Service fa4841
	buf[hostnlen + 5] = (port >> 8) & 0xff;
Packit Service fa4841
	buf[hostnlen + 6] = port & 0xff;
Packit Service fa4841
	status = BIO_write(bufferedBio, buf, hostnlen + 7U);
Packit Service fa4841
Packit Service fa4841
	if ((status < 0) || ((size_t)status != (hostnlen + 7U)))
Packit Service fa4841
	{
Packit Service fa4841
		WLog_ERR(TAG, "SOCKS proxy: failed to write CONN REQ");
Packit Service fa4841
		return FALSE;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	status = recv_socks_reply(bufferedBio, buf, sizeof(buf), "CONN REQ", 5);
Packit Service fa4841
Packit Service fa4841
	if (status < 4)
Packit Service fa4841
		return FALSE;
Packit Service fa4841
Packit Service fa4841
	if (buf[1] == 0)
Packit Service fa4841
	{
Packit Service fa4841
		WLog_INFO(TAG, "Successfully connected to %s:%d", hostname, port);
Packit Service fa4841
		return TRUE;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	if (buf[1] > 0 && buf[1] < 9)
Packit Service fa4841
		WLog_INFO(TAG, "SOCKS Proxy replied: %s", rplstat[buf[1]]);
Packit Service fa4841
	else
Packit Service fa4841
		WLog_INFO(TAG, "SOCKS Proxy replied: %d status not listed in rfc1928", buf[1]);
Packit Service fa4841
Packit Service fa4841
	return FALSE;
Packit Service fa4841
}