From b1c1549ad1cf1cbf821645dc34e9c22d760d3374 Mon Sep 17 00:00:00 2001 From: Jiri Sasek Date: Fri, 18 Aug 2017 19:17:17 +0200 Subject: [PATCH] SOCKS proxy support --- client/common/cmdline.c | 12 +++- client/common/cmdline.h | 2 +- include/freerdp/settings.h | 1 + libfreerdp/core/proxy.c | 141 +++++++++++++++++++++++++++++++++++++++++++- libfreerdp/core/transport.c | 14 ++++- 5 files changed, 164 insertions(+), 6 deletions(-) diff --git a/client/common/cmdline.c b/client/common/cmdline.c index 801ef95..f356019 100644 --- a/client/common/cmdline.c +++ b/client/common/cmdline.c @@ -1317,6 +1317,8 @@ int freerdp_client_settings_parse_command_line_arguments(rdpSettings* settings, compatibility = freerdp_client_detect_command_line(argc - 1, &argv[1], &flags, allowUnknown); + settings->ProxyHostname = NULL; + if (compatibility) { WLog_WARN(TAG, "Using deprecated command-line interface!"); @@ -1819,6 +1821,9 @@ int freerdp_client_settings_parse_command_line_arguments(rdpSettings* settings, } CommandLineSwitchCase(arg, "proxy") { + /* initial value */ + settings->ProxyType = PROXY_TYPE_HTTP; + if (arg->Flags & COMMAND_LINE_VALUE_PRESENT) { p = strstr(arg->Value, "://"); @@ -1831,9 +1836,13 @@ int freerdp_client_settings_parse_command_line_arguments(rdpSettings* settings, { settings->ProxyType = PROXY_TYPE_HTTP; } + else if (!strcmp("socks", arg->Value)) + { + settings->ProxyType = PROXY_TYPE_SOCKS; + } else { - WLog_ERR(TAG, "Only HTTP proxys supported by now"); + WLog_ERR(TAG, "Only HTTP and SOCKS proxies supported by now"); return COMMAND_LINE_ERROR_UNEXPECTED_VALUE; } @@ -1854,7 +1863,6 @@ int freerdp_client_settings_parse_command_line_arguments(rdpSettings* settings, settings->ProxyHostname = (char*) malloc(length + 1); strncpy(settings->ProxyHostname, arg->Value, length); settings->ProxyHostname[length] = '\0'; - settings->ProxyType = PROXY_TYPE_HTTP; } } else diff --git a/client/common/cmdline.h b/client/common/cmdline.h index 8785ed6..635d028 100644 --- a/client/common/cmdline.h +++ b/client/common/cmdline.h @@ -137,7 +137,7 @@ static COMMAND_LINE_ARGUMENT_A args[] = { "port", COMMAND_LINE_VALUE_REQUIRED, "", NULL, NULL, -1, NULL, "Server port" }, { "print-reconnect-cookie", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueFalse, NULL, -1, NULL, "Print base64 reconnect cookie after connecting" }, { "printer", COMMAND_LINE_VALUE_OPTIONAL, "[,]", NULL, NULL, -1, NULL, "Redirect printer device" }, - { "proxy", COMMAND_LINE_VALUE_REQUIRED, "[://]:", NULL, NULL, -1, NULL, "Proxy (see also environment variable below)" }, + { "proxy", COMMAND_LINE_VALUE_REQUIRED, "[://]:", NULL, NULL, -1, NULL, "Proxy settings: override env.var (see also environment variable below).\n\tProtocol \"socks\" should be given explicitly where \"http[s]\" is default.\n\tNote: socks proxy is not supported by env. variable" }, { "pth", COMMAND_LINE_VALUE_REQUIRED, "", NULL, NULL, -1, "pass-the-hash", "Pass the hash (restricted admin mode)" }, { "pwidth", COMMAND_LINE_VALUE_REQUIRED, "", NULL, NULL, -1, NULL, "Physical width of display (in millimeters)" }, { "reconnect-cookie", COMMAND_LINE_VALUE_REQUIRED, "", NULL, NULL, -1, NULL, "Pass base64 reconnect cookie to the connection" }, diff --git a/include/freerdp/settings.h b/include/freerdp/settings.h index e35364b..0a6f25e 100644 --- a/include/freerdp/settings.h +++ b/include/freerdp/settings.h @@ -471,6 +471,7 @@ typedef struct _RDPDR_PARALLEL RDPDR_PARALLEL; #define PROXY_TYPE_NONE 0 #define PROXY_TYPE_HTTP 1 +#define PROXY_TYPE_SOCKS 2 /* Settings */ diff --git a/libfreerdp/core/proxy.c b/libfreerdp/core/proxy.c index 6c98471..2d20e5b 100644 --- a/libfreerdp/core/proxy.c +++ b/libfreerdp/core/proxy.c @@ -29,7 +29,8 @@ #define CRLF "\r\n" #define TAG FREERDP_TAG("core.proxy") -BOOL http_proxy_connect(BIO* bufferedBio, const char* hostname, UINT16 port); +static BOOL http_proxy_connect(BIO* bufferedBio, const char* hostname, UINT16 port); +static BOOL socks_proxy_connect(BIO* bufferedBio, const char* hostname, UINT16 port); void proxy_read_environment(rdpSettings* settings, char* envname); BOOL proxy_prepare(rdpSettings* settings, const char** lpPeerHostname, UINT16* lpPeerPort, @@ -160,13 +161,16 @@ BOOL proxy_connect(rdpSettings* settings, BIO* bufferedBio, const char* hostname case PROXY_TYPE_HTTP: return http_proxy_connect(bufferedBio, hostname, port); + case PROXY_TYPE_SOCKS: + return socks_proxy_connect(bufferedBio, hostname, port); + default: WLog_ERR(TAG, "Invalid internal proxy configuration"); return FALSE; } } -BOOL http_proxy_connect(BIO* bufferedBio, const char* hostname, UINT16 port) +static BOOL http_proxy_connect(BIO* bufferedBio, const char* hostname, UINT16 port) { int status; wStream* s; @@ -255,3 +259,136 @@ BOOL http_proxy_connect(BIO* bufferedBio, const char* hostname, UINT16 port) return TRUE; } +/* SOCKS Proxy auth methods by rfc1928 */ +#define AUTH_M_NO_AUTH 0 +#define AUTH_M_GSSAPI 1 +#define AUTH_M_USR_PASS 2 + +static BOOL socks_proxy_connect(BIO* bufferedBio, const char* hostname, UINT16 port) +{ + int status; + BYTE buf[280], hostnlen = 0xff & strlen(hostname); + /* CONN REQ replies in enum. order */ + static const char *rplstat[] = { + "succeeded", + "general SOCKS server failure", + "connection not allowed by ruleset", + "Network unreachable", + "Host unreachable", + "Connection refused", + "TTL expired", + "Command not supported", + "Address type not supported" + }; + + /* select auth. method */ + memset(buf, '\0', sizeof(buf)); + buf[0] = 5; /* SOCKS version */ + buf[1] = 1; /* #of methods offered */ + buf[2] = AUTH_M_NO_AUTH; + status = BIO_write(bufferedBio, buf, 3); + if (status != 3) + { + WLog_ERR(TAG, "SOCKS proxy: failed to write AUTH METHOD request"); + return FALSE; + } + + /* Read result SOCK reply */ + memset(buf, '\0', sizeof(buf)); + for(;;) { + status = BIO_read(bufferedBio, buf, sizeof(buf)); + if (status > 0) break; + else if (status < 0) + { + /* Error? */ + if (BIO_should_retry(bufferedBio)) + { + USleep(100); + continue; + } + + WLog_ERR(TAG, "Failed reading AUTH reply from SOCKS proxy (Status %d)", status); + return FALSE; + } + else if (status == 0) + { + /* Error? */ + WLog_ERR(TAG, "Failed reading AUTH reply from SOCKS proxy (BIO_read returned zero)"); + return FALSE; + } + } + + if (buf[0] != 5) + { + WLog_ERR(TAG, "SOCKS Proxy version is not 5 (AUTH)"); + return FALSE; + } + + if (buf[1] != AUTH_M_NO_AUTH) + { + WLog_ERR(TAG, "SOCKS Proxy: (NO AUTH) method was not selected by proxy"); + return FALSE; + } + + /* CONN request */ + memset(buf, '\0', sizeof(buf)); + buf[0] = 5; /* SOCKS version */ + buf[1] = 1; /* command = connect */ + /* 3rd octet is reserved x00 */ + buf[3] = 3; /* addr.type = FQDN */ + buf[4] = hostnlen; /* DST.ADDR */ + memcpy(buf +5, hostname, hostnlen); + /* follows DST.PORT in netw. format */ + buf[hostnlen +5] = 0xff & (port >> 8); + buf[hostnlen +6] = 0xff & port; + status = BIO_write(bufferedBio, buf, hostnlen +7); + if (status != (hostnlen +7)) + { + WLog_ERR(TAG, "SOCKS proxy: failed to write CONN REQ"); + return FALSE; + } + + /* Read result SOCK reply */ + memset(buf, '\0', sizeof(buf)); + for(;;) { + status = BIO_read(bufferedBio, buf, sizeof(buf)); + if (status > 0) break; + else if (status < 0) + { + /* Error? */ + if (BIO_should_retry(bufferedBio)) + { + USleep(100); + continue; + } + + WLog_ERR(TAG, "Failed reading reply from SOCKS proxy (Status %d)", status); + return FALSE; + } + else if (status == 0) + { + /* Error? */ + WLog_ERR(TAG, "Failed reading reply from SOCKS proxy (BIO_read returned zero)"); + return FALSE; + } + } + + if (buf[0] != 5) + { + WLog_ERR(TAG, "SOCKS Proxy version is not 5 (CONN REQ)"); + return FALSE; + } + + if (buf[1] == 0) + { + WLog_INFO(TAG, "Successfully connected to %s:%d", hostname, port); + return TRUE; + } + + if (buf[1] > 0 && buf[1] < 9) + WLog_INFO(TAG, "SOCKS Proxy replied: %s", rplstat[buf[1]]); + else + WLog_INFO(TAG, "SOCKS Proxy replied: %d status not listed in rfc1928", buf[1]); + + return FALSE; +} diff --git a/libfreerdp/core/transport.c b/libfreerdp/core/transport.c index ad44e0a..2ae98f7 100644 --- a/libfreerdp/core/transport.c +++ b/libfreerdp/core/transport.c @@ -52,6 +52,7 @@ #include "fastpath.h" #include "transport.h" #include "rdp.h" +#include "proxy.h" #define TAG FREERDP_TAG("core.transport") @@ -404,7 +405,14 @@ BOOL transport_connect(rdpTransport* transport, const char* hostname, } else { - sockfd = freerdp_tcp_connect(context, settings, hostname, port, timeout); + UINT16 peerPort; + const char *peerHostname; + BOOL isProxyConnection = proxy_prepare(settings, &peerHostname, &peerPort, TRUE); + + if (isProxyConnection) + sockfd = freerdp_tcp_connect(context, settings, peerHostname, peerPort, timeout); + else + sockfd = freerdp_tcp_connect(context, settings, hostname, port, timeout); if (sockfd < 1) return FALSE; @@ -412,6 +420,10 @@ BOOL transport_connect(rdpTransport* transport, const char* hostname, if (!transport_attach(transport, sockfd)) return FALSE; + if (isProxyConnection) + if (!proxy_connect(settings, transport->frontBio, hostname, port)) + return FALSE; + status = TRUE; } -- 2.7.4