diff --git a/CMakeLists.txt b/CMakeLists.txt index 98c208f07..fd2950971 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -504,6 +504,10 @@ if(ANDROID) endif() endif() +if(WITH_HTTP_PROXY) + add_definitions(-DWITH_HTTP_PROXY) +endif() + # Unit Tests include(CTest) diff --git a/client/common/cmdline.c b/client/common/cmdline.c index 7cfd12d35..17987a1bb 100644 --- a/client/common/cmdline.c +++ b/client/common/cmdline.c @@ -72,6 +72,9 @@ COMMAND_LINE_ARGUMENT_A args[] = { "gu", COMMAND_LINE_VALUE_REQUIRED, "[\\] or [@]", NULL, NULL, -1, NULL, "Gateway username" }, { "gp", COMMAND_LINE_VALUE_REQUIRED, "", NULL, NULL, -1, NULL, "Gateway password" }, { "gd", COMMAND_LINE_VALUE_REQUIRED, "", NULL, NULL, -1, NULL, "Gateway domain" }, +#ifdef WITH_HTTP_PROXY + { "http-proxy", COMMAND_LINE_VALUE_REQUIRED, ":", NULL, NULL, -1, NULL, "HTTP Proxy" }, +#endif { "load-balance-info", COMMAND_LINE_VALUE_REQUIRED, "", NULL, NULL, -1, NULL, "Load balance info" }, { "app", COMMAND_LINE_VALUE_REQUIRED, " or <||alias>", NULL, NULL, -1, NULL, "Remote application program" }, { "app-name", COMMAND_LINE_VALUE_REQUIRED, "", NULL, NULL, -1, NULL, "Remote application name for user interface" }, @@ -1349,6 +1352,32 @@ int freerdp_client_settings_parse_command_line_arguments(rdpSettings* settings, settings->GatewayUseSameCredentials = TRUE; settings->GatewayEnabled = TRUE; } +#ifdef WITH_HTTP_PROXY + CommandLineSwitchCase(arg, "http-proxy") + { + if (arg->Flags & COMMAND_LINE_VALUE_PRESENT) + { + /* TODO: parse "http://" URL? */ + p = strchr(arg->Value, ':'); + + if (p) + { + length = (int) (p - arg->Value); + settings->HTTPProxyPort = atoi(&p[1]); + settings->HTTPProxyHostname = (char*) malloc(length + 1); + strncpy(settings->HTTPProxyHostname, arg->Value, length); + settings->HTTPProxyHostname[length] = '\0'; + + settings->HTTPProxyEnabled = TRUE; + } + else + { + /* TODO parse encironment variable here? */ + fprintf(stderr, "Option http-proxy needs argument. Ignored.\n"); + } + } + } +#endif CommandLineSwitchCase(arg, "gu") { char* user; diff --git a/cmake/ConfigOptions.cmake b/cmake/ConfigOptions.cmake index 915d114d4..bf9b0cdfa 100644 --- a/cmake/ConfigOptions.cmake +++ b/cmake/ConfigOptions.cmake @@ -68,6 +68,8 @@ option(STATIC_CHANNELS "Build channels statically" ON) option(WITH_CHANNELS "Build virtual channel plugins" ON) +option(WITH_HTTP_PROXY "Build HTTP proxy support" OFF) + if(WITH_CLIENT AND WITH_CHANNELS) option(WITH_CLIENT_CHANNELS "Build virtual channel plugins" ON) endif() diff --git a/include/freerdp/settings.h b/include/freerdp/settings.h index ffee66a45..c982d994c 100644 --- a/include/freerdp/settings.h +++ b/include/freerdp/settings.h @@ -669,6 +669,11 @@ typedef struct _RDPDR_PARALLEL RDPDR_PARALLEL; #define FreeRDP_GatewayCredentialsSource 1990 #define FreeRDP_GatewayUseSameCredentials 1991 #define FreeRDP_GatewayEnabled 1992 +#ifdef WITH_HTTP_PROXY +#define FreeRDP_HTTPProxyEnabled 2995 +#define FreeRDP_HTTPProxyHostname 2996 +#define FreeRDP_HTTPProxyPort 2997 +#endif #define FreeRDP_RemoteApplicationMode 2112 #define FreeRDP_RemoteApplicationName 2113 #define FreeRDP_RemoteApplicationIcon 2114 @@ -1076,6 +1081,13 @@ struct rdp_settings UINT64 padding2048[2048 - 1993]; /* 1993 */ UINT64 padding2112[2112 - 2048]; /* 2048 */ +#ifdef WITH_HTTP_PROXY + /* HTTP Proxy */ + ALIGN64 BOOL HTTPProxyEnabled; /* 1995 */ + ALIGN64 char* HTTPProxyHostname; /* 1996 */ + ALIGN64 UINT32 HTTPProxyPort; /* 1997 */ +#endif + /** * RemoteApp */ diff --git a/libfreerdp/common/settings.c b/libfreerdp/common/settings.c index d0f373544..42ad8b407 100644 --- a/libfreerdp/common/settings.c +++ b/libfreerdp/common/settings.c @@ -746,6 +746,12 @@ BOOL freerdp_get_param_bool(rdpSettings* settings, int id) return settings->GatewayEnabled; break; +#ifdef WITH_HTTP_PROXY + case FreeRDP_HTTPProxyEnabled: + return settings->HTTPProxyEnabled; + break; +#endif + case FreeRDP_RemoteApplicationMode: return settings->RemoteApplicationMode; break; @@ -1218,6 +1224,12 @@ int freerdp_set_param_bool(rdpSettings* settings, int id, BOOL param) settings->GatewayEnabled = param; break; +#ifdef WITH_HTTP_PROXY + case FreeRDP_HTTPProxyEnabled: + settings->HTTPProxyEnabled = param; + break; +#endif + case FreeRDP_RemoteApplicationMode: settings->RemoteApplicationMode = param; break; @@ -1603,6 +1615,12 @@ UINT32 freerdp_get_param_uint32(rdpSettings* settings, int id) return settings->GatewayCredentialsSource; break; +#ifdef WITH_HTTP_PROXY + case FreeRDP_HTTPProxyPort: + return settings->HTTPProxyPort; + break; +#endif + case FreeRDP_RemoteAppNumIconCaches: return settings->RemoteAppNumIconCaches; break; @@ -1911,6 +1929,12 @@ int freerdp_set_param_uint32(rdpSettings* settings, int id, UINT32 param) settings->GatewayCredentialsSource = param; break; +#ifdef WITH_HTTP_PROXY + case FreeRDP_HTTPProxyPort: + settings->HTTPProxyPort = param; + break; +#endif + case FreeRDP_RemoteAppNumIconCaches: settings->RemoteAppNumIconCaches = param; break; @@ -2233,6 +2257,12 @@ char* freerdp_get_param_string(rdpSettings* settings, int id) return settings->GatewayDomain; break; +#ifdef WITH_HTTP_PROXY + case FreeRDP_HTTPProxyHostname: + return settings->HTTPProxyHostname; + break; +#endif + case FreeRDP_RemoteApplicationName: return settings->RemoteApplicationName; break; @@ -2437,6 +2467,13 @@ int freerdp_set_param_string(rdpSettings* settings, int id, const char* param) settings->GatewayDomain = _strdup(param); break; +#ifdef WITH_HTTP_PROXY + case FreeRDP_HTTPProxyHostname: + free(settings->HTTPProxyHostname); + settings->HTTPProxyHostname = _strdup(param); + break; +#endif + case FreeRDP_RemoteApplicationName: free(settings->RemoteApplicationName); settings->RemoteApplicationName = _strdup(param); diff --git a/libfreerdp/core/settings.c b/libfreerdp/core/settings.c index 13ea60567..5526d50f2 100644 --- a/libfreerdp/core/settings.c +++ b/libfreerdp/core/settings.c @@ -481,6 +481,9 @@ rdpSettings* freerdp_settings_clone(rdpSettings* settings) _settings->GatewayUsername = _strdup(settings->GatewayUsername); /* 1987 */ _settings->GatewayPassword = _strdup(settings->GatewayPassword); /* 1988 */ _settings->GatewayDomain = _strdup(settings->GatewayDomain); /* 1989 */ +#ifdef WITH_HTTP_PROXY + _settings->HTTPProxyHostname = settings->HTTPProxyHostname; /* 199x */ +#endif _settings->RemoteApplicationName = _strdup(settings->RemoteApplicationName); /* 2113 */ _settings->RemoteApplicationIcon = _strdup(settings->RemoteApplicationIcon); /* 2114 */ _settings->RemoteApplicationProgram = _strdup(settings->RemoteApplicationProgram); /* 2115 */ @@ -534,6 +537,9 @@ rdpSettings* freerdp_settings_clone(rdpSettings* settings) _settings->GatewayUsageMethod = settings->GatewayUsageMethod; /* 1984 */ _settings->GatewayPort = settings->GatewayPort; /* 1985 */ _settings->GatewayCredentialsSource = settings->GatewayCredentialsSource; /* 1990 */ +#ifdef WITH_HTTP_PROXY + _settings->HTTPProxyPort = settings->HTTPProxyPort; /* 199x */ +#endif _settings->RemoteApplicationExpandCmdLine = settings->RemoteApplicationExpandCmdLine; /* 2119 */ _settings->RemoteApplicationExpandWorkingDir = settings->RemoteApplicationExpandWorkingDir; /* 2120 */ _settings->RemoteAppNumIconCaches = settings->RemoteAppNumIconCaches; /* 2122 */ @@ -651,6 +657,9 @@ rdpSettings* freerdp_settings_clone(rdpSettings* settings) _settings->PlayRemoteFx = settings->PlayRemoteFx; /* 1857 */ _settings->GatewayUseSameCredentials = settings->GatewayUseSameCredentials; /* 1991 */ _settings->GatewayEnabled = settings->GatewayEnabled; /* 1992 */ +#ifdef WITH_HTTP_PROXY + _settings->HTTPProxyEnabled = settings->HTTPProxyEnabled; /* 1995 */ +#endif _settings->RemoteApplicationMode = settings->RemoteApplicationMode; /* 2112 */ _settings->DisableRemoteAppCapsCheck = settings->DisableRemoteAppCapsCheck; /* 2121 */ _settings->RemoteAppLanguageBarSupported = settings->RemoteAppLanguageBarSupported; /* 2124 */ diff --git a/libfreerdp/core/transport.c b/libfreerdp/core/transport.c index 5aefdb936..ce364642b 100644 --- a/libfreerdp/core/transport.c +++ b/libfreerdp/core/transport.c @@ -327,6 +327,61 @@ BOOL transport_connect_nla(rdpTransport* transport) return TRUE; } +#ifdef WITH_HTTP_PROXY +/* TODO move into core/tcp.c? */ + + +BOOL transport_http_proxy_connect(rdpTransport* transport, const char* hostname, UINT16 port) +{ + int status; + wStream* s; + char str[32]; + + _itoa_s(port, str, sizeof(str), 10); + + s = Stream_New(NULL, 200); + Stream_Write(s, "CONNECT ", 8); + Stream_Write(s, hostname, strlen(hostname)); + Stream_Write_UINT8(s, ':'); + Stream_Write(s, str, strlen(str)); + Stream_Write(s, " HTTP/1.1\r\n\r\nHost: ", 19); + Stream_Write(s, hostname, strlen(hostname)); + Stream_Write_UINT8(s, ':'); + Stream_Write(s, str, strlen(str)); + Stream_Write(s, "\r\n\r\n", 4); + + // Send Host: header? (RFC2817) + + //fprintf(stderr, "Sending CONNECT %.*s:%.*s\n", strlen(hostname), hostname, strlen(str), str); + fprintf(stderr, "Sending \"%.*s\"...\n", (int)Stream_GetPosition(s), Stream_Buffer(s)); + + status = transport_write(transport, s); + if (status < 0) { + fprintf(stderr, "Error writing: status=%d\n", status); + return status; + } + + fprintf(stderr, "Sent!\n"); + + // Read result... + memset(str, 0, sizeof(str)); + status = tcp_read(transport->TcpIn, (BYTE*)str, sizeof(str)-1); + if (status <= 0) { + // Error? + return FALSE; + } + + fprintf(stderr, "Reply: \"%.*s\"\n", status, str); + + // TODO read until "\r\n\r\n" + if (strstr(str, "200") == NULL) { + return FALSE; + } + + return TRUE; +} +#endif + BOOL transport_tsg_connect(rdpTransport* transport, const char* hostname, UINT16 port) { rdpTsg* tsg = tsg_new(transport); @@ -374,6 +429,50 @@ BOOL transport_connect(rdpTransport* transport, const char* hostname, UINT16 por transport->async = settings->AsyncTransport; +#ifdef WITH_HTTP_PROXY + if (transport->settings->HTTPProxyEnabled) + { + status = tcp_connect(transport->TcpIn, settings->HTTPProxyHostname, settings->HTTPProxyPort); + + if (status) { + if (transport->settings->GatewayEnabled) { + transport->layer = TRANSPORT_LAYER_HTTP_PROXY_IN; + status = transport_http_proxy_connect(transport, settings->GatewayHostname, settings->GatewayPort); + + if (status) { + // Connect second channel + transport->TcpOut = tcp_new(settings); + status = tcp_connect(transport->TcpOut, settings->HTTPProxyHostname, settings->HTTPProxyPort); + } + + if (status) { + transport->layer = TRANSPORT_LAYER_HTTP_PROXY_OUT; + status = transport_http_proxy_connect(transport, settings->GatewayHostname, settings->GatewayPort); + } + + if (status) { + transport->layer = TRANSPORT_LAYER_TSG; + transport->SplitInputOutput = TRUE; + status = transport_tsg_connect(transport, hostname, port); + } + } + else { + transport->layer = TRANSPORT_LAYER_TCP; + transport->SplitInputOutput = FALSE; + transport->TcpOut = transport->TcpIn; + + status = tcp_connect(transport->TcpIn, settings->HTTPProxyHostname, settings->HTTPProxyPort); + + if (status) { + status = transport_http_proxy_connect(transport, hostname, port); + } + + } + } + } + else +#endif + if (transport->settings->GatewayEnabled) { transport->layer = TRANSPORT_LAYER_TSG; @@ -555,6 +654,12 @@ int transport_read_layer(rdpTransport* transport, BYTE* data, int bytes) else if (transport->layer == TRANSPORT_LAYER_TSG_TLS) { status = tls_read(transport->TsgTls, data + read, bytes - read); } +#ifdef WITH_HTTP_PROXY + else if (transport->layer == TRANSPORT_LAYER_HTTP_PROXY_IN) + status = tcp_read(transport->TcpIn, data + read, bytes - read); + else if (transport->layer == TRANSPORT_LAYER_HTTP_PROXY_OUT) + status = tcp_read(transport->TcpOut, data + read, bytes - read); +#endif /* blocking means that we can't continue until this is read */ @@ -737,6 +842,12 @@ int transport_write(rdpTransport* transport, wStream* s) status = tsg_write(transport->tsg, Stream_Pointer(s), length); else if (transport->layer == TRANSPORT_LAYER_TSG_TLS) status = tls_write(transport->TsgTls, Stream_Pointer(s), length); +#ifdef WITH_HTTP_PROXY + else if (transport->layer == TRANSPORT_LAYER_HTTP_PROXY_IN) + status = tcp_write(transport->TcpIn, Stream_Pointer(s), length); + else if (transport->layer == TRANSPORT_LAYER_HTTP_PROXY_OUT) + status = tcp_write(transport->TcpOut, Stream_Pointer(s), length); +#endif if (status < 0) break; /* error occurred */ @@ -757,6 +868,12 @@ int transport_write(rdpTransport* transport, wStream* s) tcp_wait_write(transport->TcpOut); else if (transport->layer == TRANSPORT_LAYER_TSG_TLS) tls_wait_write(transport->TsgTls); +#ifdef WITH_HTTP_PROXY + else if (transport->layer == TRANSPORT_LAYER_HTTP_PROXY_IN) + tcp_wait_write(transport->TcpIn); + else if (transport->layer == TRANSPORT_LAYER_HTTP_PROXY_OUT) + tcp_wait_write(transport->TcpOut); +#endif else USleep(transport->SleepInterval); } diff --git a/libfreerdp/core/transport.h b/libfreerdp/core/transport.h index 1ba273f7a..e588f1613 100644 --- a/libfreerdp/core/transport.h +++ b/libfreerdp/core/transport.h @@ -26,7 +26,11 @@ typedef enum TRANSPORT_LAYER_TLS, TRANSPORT_LAYER_TSG, TRANSPORT_LAYER_TSG_TLS, - TRANSPORT_LAYER_CLOSED + TRANSPORT_LAYER_CLOSED, +#ifdef WITH_HTTP_PROXY + TRANSPORT_LAYER_HTTP_PROXY_IN, + TRANSPORT_LAYER_HTTP_PROXY_OUT, +#endif } TRANSPORT_LAYER; typedef struct rdp_transport rdpTransport;