Blame libfreerdp/core/proxy.c

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