Merge branch 'gateway-http-bugfix'
authorChristian Plattner <ccpp@gmx.at>
Fri, 9 Dec 2016 19:43:02 +0000 (20:43 +0100)
committerChristian Plattner <ccpp@gmx.at>
Fri, 9 Dec 2016 19:43:02 +0000 (20:43 +0100)
Conflicts:
client/common/cmdline.c
include/freerdp/settings.h
libfreerdp/common/settings.c
libfreerdp/core/settings.c
libfreerdp/core/tcp.c
libfreerdp/core/transport.c

1  2 
client/common/cmdline.c
include/freerdp/settings.h
libfreerdp/common/settings.c
libfreerdp/core/CMakeLists.txt
libfreerdp/core/gateway/rdg.c
libfreerdp/core/gateway/rpc.c
libfreerdp/core/proxy.c
libfreerdp/core/settings.c
libfreerdp/core/transport.c

@@@ -78,8 -80,8 +80,9 @@@ static COMMAND_LINE_ARGUMENT_A args[] 
        { "gu", COMMAND_LINE_VALUE_REQUIRED, "[<domain>\\]<user> or <user>[@<domain>]", NULL, NULL, -1, NULL, "Gateway username" },
        { "gp", COMMAND_LINE_VALUE_REQUIRED, "<password>", NULL, NULL, -1, NULL, "Gateway password" },
        { "gd", COMMAND_LINE_VALUE_REQUIRED, "<domain>", NULL, NULL, -1, NULL, "Gateway domain" },
-       { "gateway-usage-method", COMMAND_LINE_VALUE_REQUIRED, "<direct|detect>", NULL, NULL, -1, NULL, "Gateway usage method" },
+       { "gt", COMMAND_LINE_VALUE_REQUIRED, "<rpc|http|auto>", NULL, NULL, -1, NULL, "Gateway transport type" },
+       { "gateway-usage-method", COMMAND_LINE_VALUE_REQUIRED, "<direct|detect>", NULL, NULL, -1, "gum", "Gateway usage method" },
 +      { "http-proxy", COMMAND_LINE_VALUE_REQUIRED, "<host>:<port>", NULL, NULL, -1, NULL, "HTTP Proxy" },
        { "load-balance-info", COMMAND_LINE_VALUE_REQUIRED, "<info string>", NULL, NULL, -1, NULL, "Load balance info" },
        { "app", COMMAND_LINE_VALUE_REQUIRED, "<executable path> or <||alias>", NULL, NULL, -1, NULL, "Remote application program" },
        { "app-name", COMMAND_LINE_VALUE_REQUIRED, "<app name>", NULL, NULL, -1, NULL, "Remote application name for user interface" },
@@@ -272,23 -293,13 +294,23 @@@ BOOL freerdp_client_print_command_line_
        printf("Multimedia Redirection: /multimedia:sys:alsa\n");
        printf("USB Device Redirection: /usb:id,dev:054c:0268\n");
        printf("\n");
 +
 +      printf("For Gateways, the https_proxy environment variable is respected:\n");
 +#ifdef _WIN32
 +      printf("    set HTTPS_PROXY=http://proxy.contoso.com:3128/\n");
 +#else
 +      printf("    export https_proxy=http://proxy.contoso.com:3128/\n");
 +#endif
 +      printf("    xfreerdp /g:rdp.contoso.com ...\n");
 +      printf("\n");
 +
        printf("More documentation is coming, in the meantime consult source files\n");
        printf("\n");
-       return 1;
+       return TRUE;
  }
  
- int freerdp_client_command_line_pre_filter(void* context, int index, int argc, LPCSTR* argv)
+ static int freerdp_client_command_line_pre_filter(void* context, int index,
+         int argc, LPCSTR* argv)
  {
        if (index == 1)
        {
@@@ -1473,41 -1774,12 +1785,35 @@@ int freerdp_client_settings_parse_comma
  
                        settings->GatewayEnabled = TRUE;
                        settings->GatewayUseSameCredentials = TRUE;
-                       freerdp_set_gateway_usage_method(settings, TSC_PROXY_MODE_DETECT);
+                       freerdp_set_gateway_usage_method(settings, TSC_PROXY_MODE_DIRECT);
                }
 +              CommandLineSwitchCase(arg, "http-proxy")
 +              {
 +                      if (arg->Flags & COMMAND_LINE_VALUE_PRESENT)
 +                      {
 +                              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");
 +                              }
 +                      }
 +              }
                CommandLineSwitchCase(arg, "gu")
                {
-                       char* user;
-                       char* domain;
-                       freerdp_parse_username(arg->Value, &user, &domain);
-                       settings->GatewayUsername = user;
-                       settings->GatewayDomain = domain;
+                       if (!(gwUser = _strdup(arg->Value)))
+                               return COMMAND_LINE_ERROR_MEMORY;
  
                        settings->GatewayUseSameCredentials = FALSE;
                }
@@@ -690,9 -683,9 +683,12 @@@ typedef struct _RDPDR_PARALLEL RDPDR_PA
  #define FreeRDP_GatewayUseSameCredentials                     1991
  #define FreeRDP_GatewayEnabled                                        1992
  #define FreeRDP_GatewayBypassLocal                            1993
- #define FreeRDP_HTTPProxyEnabled                              2995
- #define FreeRDP_HTTPProxyHostname                             2996
- #define FreeRDP_HTTPProxyPort                                 2997
+ #define FreeRDP_GatewayRpcTransport                           1994
+ #define FreeRDP_GatewayHttpTransport                          1995
+ #define FreeRDP_GatewayUdpTransport                           1996
++#define FreeRDP_HTTPProxyEnabled                              2015
++#define FreeRDP_HTTPProxyHostname                             2016
++#define FreeRDP_HTTPProxyPort                                 2017
  #define FreeRDP_RemoteApplicationMode                         2112
  #define FreeRDP_RemoteApplicationName                         2113
  #define FreeRDP_RemoteApplicationIcon                         2114
@@@ -1127,14 -1139,12 +1142,17 @@@ struct rdp_setting
        ALIGN64 BOOL GatewayUseSameCredentials; /* 1991 */
        ALIGN64 BOOL GatewayEnabled; /* 1992 */
        ALIGN64 BOOL GatewayBypassLocal; /* 1993 */
-       UINT64 padding2048[2048 - 1994]; /* 1994 */
+       ALIGN64 BOOL GatewayRpcTransport; /* 1994 */
+       ALIGN64 BOOL GatewayHttpTransport; /* 1995 */
+       ALIGN64 BOOL GatewayUdpTransport; /* 1996 */
+       UINT64 padding2048[2048 - 1997]; /* 1997 */
        UINT64 padding2112[2112 - 2048]; /* 2048 */
  
 +      /* HTTP Proxy */
 +      ALIGN64 BOOL HTTPProxyEnabled;  /* 1995 */
 +      ALIGN64 char* HTTPProxyHostname;        /* 1996 */
 +      ALIGN64 UINT32 HTTPProxyPort;   /* 1997 */
 +
        /**
         * RemoteApp
         */
@@@ -883,10 -981,15 +981,18 @@@ BOOL freerdp_get_param_bool(rdpSettings
                case FreeRDP_GatewayBypassLocal:
                        return settings->GatewayBypassLocal;
  
-                       break;
+               case FreeRDP_GatewayRpcTransport:
+                       return settings->GatewayRpcTransport;
+               case FreeRDP_GatewayHttpTransport:
+                       return settings->GatewayHttpTransport;
+               case FreeRDP_GatewayUdpTransport:
+                       return settings->GatewayUdpTransport;
 +              case FreeRDP_HTTPProxyEnabled:
 +                      return settings->HTTPProxyEnabled;
 +
                case FreeRDP_RemoteApplicationMode:
                        return settings->RemoteApplicationMode;
  
@@@ -1347,10 -1461,18 +1464,22 @@@ int freerdp_set_param_bool(rdpSettings
                        settings->GatewayBypassLocal = param;
                        break;
  
+               case FreeRDP_GatewayRpcTransport:
+                       settings->GatewayRpcTransport = param;
+                       break;
+               case FreeRDP_GatewayHttpTransport:
+                       settings->GatewayHttpTransport = param;
+                       break;
+               case FreeRDP_GatewayUdpTransport:
+                       settings->GatewayUdpTransport = param;
+                       break;
 +              case FreeRDP_HTTPProxyEnabled:
 +                      settings->HTTPProxyEnabled = param;
 +                      break;
 +
                case FreeRDP_RemoteApplicationMode:
                        settings->RemoteApplicationMode = param;
                        break;
Simple merge
index 0000000,474ed85..70fd83f
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,1668 +1,1692 @@@
 -      sockfd = freerdp_tcp_connect(rdg->context, settings, settings->GatewayHostname,
 -                                      settings->GatewayPort, timeout);
+ /**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ * Remote Desktop Gateway (RDG)
+ *
+ * Copyright 2015 Denis Vincent <dvincent@devolutions.net>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ #ifdef HAVE_CONFIG_H
+ #include "config.h"
+ #endif
+ #include <assert.h>
+ #include <winpr/crt.h>
+ #include <winpr/synch.h>
+ #include <winpr/print.h>
+ #include <winpr/stream.h>
+ #include <winpr/winsock.h>
+ #include <freerdp/log.h>
+ #include <freerdp/error.h>
+ #include <freerdp/utils/ringbuffer.h>
+ #include "rdg.h"
++#include "../proxy.h"
+ #include "../rdp.h"
+ #define TAG FREERDP_TAG("core.gateway.rdg")
+ #pragma pack(push, 1)
+ typedef struct rdg_packet_header
+ {
+       UINT16 type;
+       UINT16 reserved;
+       UINT32 packetLength;
+ } RdgPacketHeader;
+ #pragma pack(pop)
+ BOOL rdg_write_packet(rdpRdg* rdg, wStream* sPacket)
+ {
+       int status;
+       wStream* sChunk;
+       char chunkSize[11];
+       sprintf_s(chunkSize, sizeof(chunkSize), "%X\r\n", (unsigned int) Stream_Length(sPacket));
+       sChunk = Stream_New(NULL, strlen(chunkSize) + Stream_Length(sPacket) + 2);
+       if (!sChunk)
+               return FALSE;
+       Stream_Write(sChunk, chunkSize, strlen(chunkSize));
+       Stream_Write(sChunk, Stream_Buffer(sPacket), Stream_Length(sPacket));
+       Stream_Write(sChunk, "\r\n", 2);
+       Stream_SealLength(sChunk);
+       status = tls_write_all(rdg->tlsIn, Stream_Buffer(sChunk), Stream_Length(sChunk));
+       Stream_Free(sChunk, TRUE);
+       if (status < 0)
+               return FALSE;
+       return TRUE;
+ }
+ wStream* rdg_receive_packet(rdpRdg* rdg)
+ {
+       int status;
+       wStream* s;
+       RdgPacketHeader* packet;
+       UINT32 readCount = 0;
+       s = Stream_New(NULL, 1024);
+       if (!s)
+               return NULL;
+       packet = (RdgPacketHeader*) Stream_Buffer(s);
+       while (readCount < sizeof(RdgPacketHeader))
+       {
+               status = BIO_read(rdg->tlsOut->bio, Stream_Pointer(s), sizeof(RdgPacketHeader) - readCount);
+               if (status < 0)
+               {
+                       continue;
+               }
+               readCount += status;
+               Stream_Seek(s, readCount);
+       }
+       if (Stream_Capacity(s) < packet->packetLength)
+       {
+               if (!Stream_EnsureCapacity(s, packet->packetLength))
+               {
+                       Stream_Free(s, TRUE);
+                       return NULL;
+               }
+               packet = (RdgPacketHeader*) Stream_Buffer(s);
+       }
+       while (readCount < packet->packetLength)
+       {
+               status = BIO_read(rdg->tlsOut->bio, Stream_Pointer(s), packet->packetLength - readCount);
+               if (status < 0)
+               {
+                       continue;
+               }
+               readCount += status;
+               Stream_Seek(s, readCount);
+       }
+       Stream_SealLength(s);
+       return s;
+ }
+ BOOL rdg_send_handshake(rdpRdg* rdg)
+ {
+       wStream* s;
+       BOOL status;
+       s = Stream_New(NULL, 14);
+       if (!s)
+               return FALSE;
+       Stream_Write_UINT16(s, PKT_TYPE_HANDSHAKE_REQUEST); /* Type (2 bytes) */
+       Stream_Write_UINT16(s, 0); /* Reserved (2 bytes) */
+       Stream_Write_UINT32(s, 14); /* PacketLength (4 bytes) */
+       Stream_Write_UINT8(s, 1); /* VersionMajor (1 byte) */
+       Stream_Write_UINT8(s, 0); /* VersionMinor (1 byte) */
+       Stream_Write_UINT16(s, 0); /* ClientVersion (2 bytes), must be 0 */
+       Stream_Write_UINT16(s, 0); /* ExtendedAuthentication (2 bytes) */
+       Stream_SealLength(s);
+       status = rdg_write_packet(rdg, s);
+       Stream_Free(s, TRUE);
+       if (status)
+       {
+               rdg->state = RDG_CLIENT_STATE_HANDSHAKE;
+       }
+       return status;
+ }
+ BOOL rdg_send_tunnel_request(rdpRdg* rdg)
+ {
+       wStream* s;
+       BOOL status;
+       s = Stream_New(NULL, 16);
+       if (!s)
+               return FALSE;
+       Stream_Write_UINT16(s, PKT_TYPE_TUNNEL_CREATE); /* Type (2 bytes) */
+       Stream_Write_UINT16(s, 0); /* Reserved (2 bytes) */
+       Stream_Write_UINT32(s, 16); /* PacketLength (4 bytes) */
+       Stream_Write_UINT32(s, HTTP_CAPABILITY_TYPE_QUAR_SOH); /* CapabilityFlags (4 bytes) */
+       Stream_Write_UINT16(s, 0); /* FieldsPresent (2 bytes) */
+       Stream_Write_UINT16(s, 0); /* Reserved (2 bytes), must be 0 */
+       Stream_SealLength(s);
+       status = rdg_write_packet(rdg, s);
+       Stream_Free(s, TRUE);
+       if (status)
+       {
+               rdg->state = RDG_CLIENT_STATE_TUNNEL_CREATE;
+       }
+       return status;
+ }
+ BOOL rdg_send_tunnel_authorization(rdpRdg* rdg)
+ {
+       int i;
+       wStream* s;
+       BOOL status;
+       WCHAR* clientName = NULL;
+       UINT16 clientNameLen;
+       UINT32 packetSize;
+       clientNameLen = ConvertToUnicode(CP_UTF8, 0, rdg->settings->ClientHostname, -1, &clientName, 0);
+       if (!clientName)
+               return FALSE;
+       packetSize = 12 + clientNameLen * sizeof(WCHAR);
+       s = Stream_New(NULL, packetSize);
+       if (!s)
+       {
+               free(clientName);
+               return FALSE;
+       }
+       Stream_Write_UINT16(s, PKT_TYPE_TUNNEL_AUTH); /* Type (2 bytes) */
+       Stream_Write_UINT16(s, 0); /* Reserved (2 bytes) */
+       Stream_Write_UINT32(s, packetSize); /* PacketLength (4 bytes) */
+       Stream_Write_UINT16(s, 0); /* FieldsPresent (2 bytes) */
+       Stream_Write_UINT16(s, clientNameLen * 2); /* Client name string length */
+       for (i = 0; i < clientNameLen; i++)
+               Stream_Write_UINT16(s, clientName[i]);
+       Stream_SealLength(s);
+       status = rdg_write_packet(rdg, s);
+       Stream_Free(s, TRUE);
+       free(clientName);
+       if (status)
+       {
+               rdg->state = RDG_CLIENT_STATE_TUNNEL_AUTHORIZE;
+       }
+       return status;
+ }
+ BOOL rdg_send_channel_create(rdpRdg* rdg)
+ {
+       int i;
+       wStream* s;
+       BOOL status;
+       char* serverName = rdg->settings->ServerHostname;
+       UINT16 serverNameLen = strlen(serverName) + 1;
+       UINT32 packetSize = 16 + serverNameLen * 2;
+       s = Stream_New(NULL, packetSize);
+       if (!s)
+               return FALSE;
+       Stream_Write_UINT16(s, PKT_TYPE_CHANNEL_CREATE); /* Type (2 bytes) */
+       Stream_Write_UINT16(s, 0); /* Reserved (2 bytes) */
+       Stream_Write_UINT32(s, packetSize); /* PacketLength (4 bytes) */
+       Stream_Write_UINT8(s, 1); /* Number of resources. (1 byte) */
+       Stream_Write_UINT8(s, 0); /* Number of alternative resources (1 byte) */
+       Stream_Write_UINT16(s, rdg->settings->ServerPort); /* Resource port (2 bytes) */
+       Stream_Write_UINT16(s, 3); /* Protocol number (2 bytes) */
+       Stream_Write_UINT16(s, serverNameLen * 2);
+       for (i = 0; i < serverNameLen; i++)
+       {
+               Stream_Write_UINT16(s, serverName[i]);
+       }
+       Stream_SealLength(s);
+       status = rdg_write_packet(rdg, s);
+       Stream_Free(s, TRUE);
+       if (status)
+       {
+               rdg->state = RDG_CLIENT_STATE_CHANNEL_CREATE;
+       }
+       return status;
+ }
+ wStream* rdg_build_http_request(rdpRdg* rdg, char* method)
+ {
+       wStream* s;
+       HttpRequest* request = NULL;
+       SecBuffer* ntlmToken = NULL;
+       char* base64NtlmToken = NULL;
+       assert(method != NULL);
+       request = http_request_new();
+       if (!request)
+               return NULL;
+       http_request_set_method(request, method);
+       http_request_set_uri(request, rdg->http->URI);
+       if (!request->Method || !request->URI)
+               return NULL;
+       if (rdg->ntlm)
+       {
+               ntlmToken = rdg->ntlm->outputBuffer;
+               if (ntlmToken)
+                       base64NtlmToken = crypto_base64_encode(ntlmToken->pvBuffer, ntlmToken->cbBuffer);
+               if (base64NtlmToken)
+               {
+                       http_request_set_auth_scheme(request, "NTLM");
+                       http_request_set_auth_param(request, base64NtlmToken);
+                       free(base64NtlmToken);
+                       if (!request->AuthScheme || !request->AuthParam)
+                               return NULL;
+               }
+       }
+       if (rdg->state == RDG_CLIENT_STATE_IN_CHANNEL_AUTHORIZED)
+       {
+               http_request_set_transfer_encoding(request, "chunked");
+       }
+       s = http_request_write(rdg->http, request);
+       http_request_free(request);
+       if (s)
+               Stream_SealLength(s);
+       return s;
+ }
+ BOOL rdg_process_out_channel_response(rdpRdg* rdg, HttpResponse* response)
+ {
+       int status;
+       wStream* s;
+       char* token64 = NULL;
+       int ntlmTokenLength = 0;
+       BYTE* ntlmTokenData = NULL;
+       rdpNtlm* ntlm = rdg->ntlm;
+       if (response->StatusCode != HTTP_STATUS_DENIED)
+       {
+               WLog_DBG(TAG, "RDG not supported");
+               rdg->state = RDG_CLIENT_STATE_NOT_FOUND;
+               return FALSE;
+       }
+       WLog_DBG(TAG, "Out Channel authorization required");
+       if (ListDictionary_Contains(response->Authenticates, "NTLM"))
+       {
+               token64 = ListDictionary_GetItemValue(response->Authenticates, "NTLM");
+               if (!token64)
+               {
+                       return FALSE;
+               }
+               crypto_base64_decode(token64, strlen(token64), &ntlmTokenData, &ntlmTokenLength);
+       }
+       if (ntlmTokenData && ntlmTokenLength)
+       {
+               ntlm->inputBuffer[0].pvBuffer = ntlmTokenData;
+               ntlm->inputBuffer[0].cbBuffer = ntlmTokenLength;
+       }
+       ntlm_authenticate(ntlm);
+       s = rdg_build_http_request(rdg, "RDG_OUT_DATA");
+       if (!s)
+               return FALSE;
+       status = tls_write_all(rdg->tlsOut, Stream_Buffer(s), Stream_Length(s));
+       Stream_Free(s, TRUE);
+       ntlm_free(rdg->ntlm);
+       rdg->ntlm = NULL;
+       if (status < 0)
+               return FALSE;
+       rdg->state = RDG_CLIENT_STATE_OUT_CHANNEL_AUTHORIZE;
+       return TRUE;
+ }
+ BOOL rdg_process_out_channel_authorization(rdpRdg* rdg, HttpResponse* response)
+ {
+       if (response->StatusCode != HTTP_STATUS_OK)
+       {
+               rdg->state = RDG_CLIENT_STATE_CLOSED;
+               return FALSE;
+       }
+       WLog_DBG(TAG, "Out Channel authorization complete");
+       rdg->state = RDG_CLIENT_STATE_OUT_CHANNEL_AUTHORIZED;
+       return TRUE;
+ }
+ BOOL rdg_process_in_channel_response(rdpRdg* rdg, HttpResponse* response)
+ {
+       int status;
+       wStream* s;
+       char* token64 = NULL;
+       int ntlmTokenLength = 0;
+       BYTE* ntlmTokenData = NULL;
+       rdpNtlm* ntlm = rdg->ntlm;
+       WLog_DBG(TAG, "In Channel authorization required");
+       if (ListDictionary_Contains(response->Authenticates, "NTLM"))
+       {
+               token64 = ListDictionary_GetItemValue(response->Authenticates, "NTLM");
+               if (!token64)
+               {
+                       return FALSE;
+               }
+               crypto_base64_decode(token64, strlen(token64), &ntlmTokenData, &ntlmTokenLength);
+       }
+       if (ntlmTokenData && ntlmTokenLength)
+       {
+               ntlm->inputBuffer[0].pvBuffer = ntlmTokenData;
+               ntlm->inputBuffer[0].cbBuffer = ntlmTokenLength;
+       }
+       ntlm_authenticate(ntlm);
+       s = rdg_build_http_request(rdg, "RDG_IN_DATA");
+       if (!s)
+               return FALSE;
+       status = tls_write_all(rdg->tlsIn, Stream_Buffer(s), Stream_Length(s));
+       Stream_Free(s, TRUE);
+       ntlm_free(rdg->ntlm);
+       rdg->ntlm = NULL;
+       if (status < 0)
+               return FALSE;
+       rdg->state = RDG_CLIENT_STATE_IN_CHANNEL_AUTHORIZE;
+       return TRUE;
+ }
+ BOOL rdg_process_in_channel_authorization(rdpRdg* rdg, HttpResponse* response)
+ {
+       wStream* s;
+       int status;
+       if (response->StatusCode != HTTP_STATUS_OK)
+       {
+               rdg->state = RDG_CLIENT_STATE_CLOSED;
+               return FALSE;
+       }
+       WLog_DBG(TAG, "In Channel authorization complete");
+       rdg->state = RDG_CLIENT_STATE_IN_CHANNEL_AUTHORIZED;
+       s = rdg_build_http_request(rdg, "RDG_IN_DATA");
+       if (!s)
+               return FALSE;
+       status = tls_write_all(rdg->tlsIn, Stream_Buffer(s), Stream_Length(s));
+       
+       Stream_Free(s, TRUE);
+       if (status <= 0)
+               return FALSE;
+       return TRUE;
+ }
+ BOOL rdg_process_handshake_response(rdpRdg* rdg, wStream* s)
+ {
+       HRESULT errorCode;
+       WLog_DBG(TAG, "Handshake response received");
+       if (rdg->state != RDG_CLIENT_STATE_HANDSHAKE)
+       {
+               return FALSE;
+       }
+       if (Stream_GetRemainingLength(s) < 12)
+               return FALSE;
+       Stream_Seek(s, 8);
+       Stream_Read_UINT32(s, errorCode);
+       if (FAILED(errorCode))
+       {
+               WLog_DBG(TAG, "Handshake error %x", errorCode);
+               return FALSE;
+       }
+       return rdg_send_tunnel_request(rdg);
+ }
+ BOOL rdg_process_tunnel_response(rdpRdg* rdg, wStream* s)
+ {
+       HRESULT errorCode;
+       WLog_DBG(TAG, "Tunnel response received");
+       if (rdg->state != RDG_CLIENT_STATE_TUNNEL_CREATE)
+       {
+               return FALSE;
+       }
+       if (Stream_GetRemainingLength(s) < 14)
+               return FALSE;
+       Stream_Seek(s, 10);
+       Stream_Read_UINT32(s, errorCode);
+       if (FAILED(errorCode))
+       {
+               WLog_DBG(TAG, "Tunnel creation error %x", errorCode);
+               return FALSE;
+       }
+       return rdg_send_tunnel_authorization(rdg);
+ }
+ BOOL rdg_process_tunnel_authorization_response(rdpRdg* rdg, wStream* s)
+ {
+       HRESULT errorCode;
+       WLog_DBG(TAG, "Tunnel authorization received");
+       if (rdg->state != RDG_CLIENT_STATE_TUNNEL_AUTHORIZE)
+       {
+               return FALSE;
+       }
+       if (Stream_GetRemainingLength(s) < 12)
+               return FALSE;
+       Stream_Seek(s, 8);
+       Stream_Read_UINT32(s, errorCode);
+       if (FAILED(errorCode))
+       {
+               WLog_DBG(TAG, "Tunnel authorization error %x", errorCode);
+               return FALSE;
+       }
+       return rdg_send_channel_create(rdg);
+ }
+ BOOL rdg_process_channel_response(rdpRdg* rdg, wStream* s)
+ {
+       HRESULT errorCode;
+       WLog_DBG(TAG, "Channel response received");
+       if (rdg->state != RDG_CLIENT_STATE_CHANNEL_CREATE)
+       {
+               return FALSE;
+       }
+       if (Stream_GetRemainingLength(s) < 12)
+               return FALSE;
+       Stream_Seek(s, 8);
+       Stream_Read_UINT32(s, errorCode);
+       if (FAILED(errorCode))
+       {
+               WLog_DBG(TAG, "Channel error %x", errorCode);
+               return FALSE;
+       }
+       rdg->state = RDG_CLIENT_STATE_OPENED;
+       return TRUE;
+ }
+ BOOL rdg_process_packet(rdpRdg* rdg, wStream* s)
+ {
+       BOOL status = TRUE;
+       UINT16 type;
+       Stream_SetPosition(s, 0);
+       if (Stream_GetRemainingLength(s) < 2)
+               return FALSE;
+       Stream_Peek_UINT16(s, type);
+       switch (type)
+       {
+               case PKT_TYPE_HANDSHAKE_RESPONSE:
+                       status = rdg_process_handshake_response(rdg, s);
+                       break;
+               case PKT_TYPE_TUNNEL_RESPONSE:
+                       status = rdg_process_tunnel_response(rdg, s);
+                       break;
+               case PKT_TYPE_TUNNEL_AUTH_RESPONSE:
+                       status = rdg_process_tunnel_authorization_response(rdg, s);
+                       break;
+               case PKT_TYPE_CHANNEL_RESPONSE:
+                       status = rdg_process_channel_response(rdg, s);
+                       break;
+               case PKT_TYPE_DATA:
+                       assert(FALSE);
+                       return FALSE;
+       }
+       return status;
+ }
+ BOOL rdg_out_channel_recv(rdpRdg* rdg)
+ {
+       wStream* s;
+       BOOL status = TRUE;
+       HttpResponse* response = NULL;
+       switch (rdg->state)
+       {
+               case RDG_CLIENT_STATE_OUT_CHANNEL_REQUEST:
+                       response = http_response_recv(rdg->tlsOut);
+                       if (!response)
+                       {
+                               return FALSE;
+                       }
+                       status = rdg_process_out_channel_response(rdg, response);
+                       http_response_free(response);
+                       break;
+               case RDG_CLIENT_STATE_OUT_CHANNEL_AUTHORIZE:
+                       response = http_response_recv(rdg->tlsOut);
+                       if (!response)
+                       {
+                               return FALSE;
+                       }
+                       status = rdg_process_out_channel_authorization(rdg, response);
+                       http_response_free(response);
+                       break;
+               default:
+                       s = rdg_receive_packet(rdg);
+                       if (s)
+                       {
+                               status = rdg_process_packet(rdg, s);
+                               Stream_Free(s, TRUE);
+                       }
+       }
+       return status;
+ }
+ BOOL rdg_in_channel_recv(rdpRdg* rdg)
+ {
+       BOOL status = TRUE;
+       HttpResponse* response = NULL;
+       switch (rdg->state)
+       {
+               case RDG_CLIENT_STATE_IN_CHANNEL_REQUEST:
+                       response = http_response_recv(rdg->tlsIn);
+                       if (!response)
+                               return FALSE;
+                       status = rdg_process_in_channel_response(rdg, response);
+                       http_response_free(response);
+                       break;
+               case RDG_CLIENT_STATE_IN_CHANNEL_AUTHORIZE:
+                       response = http_response_recv(rdg->tlsIn);
+                       if (!response)
+                               return FALSE;
+                       status = rdg_process_in_channel_authorization(rdg, response);
+                       http_response_free(response);
+                       break;
+       }
+       return status;
+ }
+ DWORD rdg_get_event_handles(rdpRdg* rdg, HANDLE* events, DWORD count)
+ {
+       DWORD nCount = 0;
+       assert(rdg != NULL);
+       if (events && (nCount < count))
+               events[nCount++] = rdg->readEvent;
+       else
+               return 0;
+       if (rdg->tlsOut && rdg->tlsOut->bio)
+       {
+               if (events && (nCount < count))
+               {
+                       BIO_get_event(rdg->tlsOut->bio, &events[nCount]);
+                       nCount++;
+               }
+               else
+                       return 0;
+       }
+       if (rdg->tlsIn && rdg->tlsIn->bio)
+       {
+               if (events && (nCount < count))
+               {
+                       BIO_get_event(rdg->tlsIn->bio, &events[nCount]);
+                       nCount++;
+               }
+               else
+                       return 0;
+       }
+       return nCount;
+ }
+ BOOL rdg_check_event_handles(rdpRdg* rdg)
+ {
+       HANDLE event = NULL;
+       assert(rdg != NULL);
+       BIO_get_event(rdg->tlsOut->bio, &event);
+       if (WaitForSingleObject(event, 0) == WAIT_OBJECT_0)
+       {
+               return rdg_out_channel_recv(rdg);
+       }
+       BIO_get_event(rdg->tlsIn->bio, &event);
+       if (WaitForSingleObject(event, 0) == WAIT_OBJECT_0)
+       {
+               return rdg_in_channel_recv(rdg);
+       }
+       return TRUE;
+ }
+ BOOL rdg_ncacn_http_ntlm_init(rdpRdg* rdg, rdpTls* tls)
+ {
+       rdpNtlm* ntlm = rdg->ntlm;
+       rdpContext* context = rdg->context;
+       rdpSettings* settings = context->settings;
+       freerdp* instance = context->instance;
+       if (!settings->GatewayPassword || !settings->GatewayUsername || !strlen(settings->GatewayPassword) || !strlen(settings->GatewayUsername))
+       {
+               if (instance->GatewayAuthenticate)
+               {
+                       BOOL proceed = instance->GatewayAuthenticate(instance, &settings->GatewayUsername, &settings->GatewayPassword, &settings->GatewayDomain);
+                       if (!proceed)
+                       {
+                               freerdp_set_last_error(context, FREERDP_ERROR_CONNECT_CANCELLED);
+                               return FALSE;
+                       }
+                       if (settings->GatewayUseSameCredentials)
+                       {
+                               if (settings->GatewayUsername)
+                               {
+                                       free(settings->Username);
+                                       if (!(settings->Username = _strdup(settings->GatewayUsername)))
+                                               return FALSE;
+                               }
+                               if (settings->GatewayDomain)
+                               {
+                                       free(settings->Domain);
+                                       if (!(settings->Domain = _strdup(settings->GatewayDomain)))
+                                               return FALSE;
+                               }
+                               if (settings->GatewayPassword)
+                               {
+                                       free(settings->Password);
+                                       if (!(settings->Password = _strdup(settings->GatewayPassword)))
+                                               return FALSE;
+                               }
+                       }
+               }
+       }
+       if (!ntlm_client_init(ntlm, TRUE, settings->GatewayUsername, settings->GatewayDomain, settings->GatewayPassword, tls->Bindings))
+       {
+               return FALSE;
+       }
+       if (!ntlm_client_make_spn(ntlm, _T("HTTP"), settings->GatewayHostname))
+       {
+               return FALSE;
+       }
+       return TRUE;
+ }
+ BOOL rdg_send_out_channel_request(rdpRdg*rdg)
+ {
+       wStream* s = NULL;
+       int status;
+       rdg->ntlm = ntlm_new();
+       if (!rdg->ntlm)
+               return FALSE;
+       status = rdg_ncacn_http_ntlm_init(rdg, rdg->tlsOut);
+       if (!status)
+               return FALSE;
+       status = ntlm_authenticate(rdg->ntlm);
+       if (!status)
+               return FALSE;
+       s = rdg_build_http_request(rdg, "RDG_OUT_DATA");
+       if (!s)
+               return FALSE;
+       status = tls_write_all(rdg->tlsOut, Stream_Buffer(s), Stream_Length(s));
+       Stream_Free(s, TRUE);
+       if (status < 0)
+               return FALSE;
+       rdg->state = RDG_CLIENT_STATE_OUT_CHANNEL_REQUEST;
+       return TRUE;
+ }
+ BOOL rdg_send_in_channel_request(rdpRdg*rdg)
+ {
+       int status;
+       wStream* s = NULL;
+       rdg->ntlm = ntlm_new();
+       if (!rdg->ntlm)
+               return FALSE;
+       status = rdg_ncacn_http_ntlm_init(rdg, rdg->tlsIn);
+       if (!status)
+               return FALSE;
+       status = ntlm_authenticate(rdg->ntlm);
+       if (!status)
+               return FALSE;
+       s = rdg_build_http_request(rdg, "RDG_IN_DATA");
+       if (!s)
+               return FALSE;
+       status = tls_write_all(rdg->tlsIn, Stream_Buffer(s), Stream_Length(s));
+       Stream_Free(s, TRUE);
+       if (status < 0)
+               return FALSE;
+       rdg->state = RDG_CLIENT_STATE_IN_CHANNEL_REQUEST;
+       return TRUE;
+ }
+ BOOL rdg_tls_out_connect(rdpRdg* rdg, const char* hostname, UINT16 port, int timeout)
+ {
+       int sockfd = 0;
+       int status = 0;
+       BIO* socketBio = NULL;
+       BIO* bufferedBio = NULL;
+       rdpSettings* settings = rdg->settings;
++      const char *peerHostname = settings->GatewayHostname;
++      int peerPort = settings->GatewayPort;
++      BOOL isProxyConnection = FALSE;
+       assert(hostname != NULL);
 -      sockfd = freerdp_tcp_connect(rdg->context, settings, settings->GatewayHostname,
 -                                      settings->GatewayPort, timeout);
++      if (settings->HTTPProxyEnabled) {
++              peerHostname = settings->HTTPProxyHostname;
++              peerPort = settings->HTTPProxyPort;
++              isProxyConnection = TRUE;
++      }
++
++      sockfd = freerdp_tcp_connect(rdg->context, settings, peerHostname,
++                                      peerPort, timeout);
+       if (sockfd < 1)
+       {
+               return FALSE;
+       }
+       socketBio = BIO_new(BIO_s_simple_socket());
+       if (!socketBio)
+       {
+               closesocket(sockfd);
+               return FALSE;
+       }
+       BIO_set_fd(socketBio, sockfd, BIO_CLOSE);
+       bufferedBio = BIO_new(BIO_s_buffered_socket());
+       if (!bufferedBio)
+       {
+               BIO_free(socketBio);
+               return FALSE;
+       }
+       bufferedBio = BIO_push(bufferedBio, socketBio);
+       status = BIO_set_nonblock(bufferedBio, TRUE);
++      if (isProxyConnection) {
++              if (!http_proxy_connect(bufferedBio, settings->GatewayHostname, settings->GatewayPort))
++                      return -1;
++      }
++
+       if (!status)
+       {
+               BIO_free_all(bufferedBio);
+               return FALSE;
+       }
+       rdg->tlsOut->hostname = settings->GatewayHostname;
+       rdg->tlsOut->port = settings->GatewayPort;
+       rdg->tlsOut->isGatewayTransport = TRUE;
+       status = tls_connect(rdg->tlsOut, bufferedBio);
+       if (status < 1)
+       {
+               return FALSE;
+       }
+       return TRUE;
+ }
+ BOOL rdg_tls_in_connect(rdpRdg* rdg, const char* hostname, UINT16 port, int timeout)
+ {
+       int sockfd = 0;
+       int status = 0;
+       BIO* socketBio = NULL;
+       BIO* bufferedBio = NULL;
+       rdpSettings* settings = rdg->settings;
++      const char *peerHostname = settings->GatewayHostname;
++      int peerPort = settings->GatewayPort;
++      BOOL isProxyConnection = FALSE;
+       assert(hostname != NULL);
++      if (settings->HTTPProxyEnabled) {
++              peerHostname = settings->HTTPProxyHostname;
++              peerPort = settings->HTTPProxyPort;
++              isProxyConnection = TRUE;
++      }
++
++      sockfd = freerdp_tcp_connect(rdg->context, settings, peerHostname,
++                                      peerPort, timeout);
+       if (sockfd < 1)
+               return FALSE;
+       socketBio = BIO_new(BIO_s_simple_socket());
+       if (!socketBio)
+       {
+               closesocket(sockfd);
+               return FALSE;
+       }
+       BIO_set_fd(socketBio, sockfd, BIO_CLOSE);
+       bufferedBio = BIO_new(BIO_s_buffered_socket());
+       if (!bufferedBio)
+       {
+               BIO_free(socketBio);
+               return FALSE;
+       }
+       bufferedBio = BIO_push(bufferedBio, socketBio);
+       status = BIO_set_nonblock(bufferedBio, TRUE);
+       if (!status)
+       {
+               BIO_free_all(bufferedBio);
+               return FALSE;
+       }
+       rdg->tlsIn->hostname = settings->GatewayHostname;
+       rdg->tlsIn->port = settings->GatewayPort;
+       rdg->tlsIn->isGatewayTransport = TRUE;
+       status = tls_connect(rdg->tlsIn, bufferedBio);
+       if (status < 1)
+       {
+               return FALSE;
+       }
+       return TRUE;
+ }
+ BOOL rdg_out_channel_connect(rdpRdg* rdg, const char* hostname, UINT16 port, int timeout)
+ {
+       BOOL status;
+       DWORD nCount;
+       HANDLE events[8];
+       assert(hostname != NULL);
+       status = rdg_tls_out_connect(rdg, hostname, port, timeout);
+       if (!status)
+               return FALSE;
+       status = rdg_send_out_channel_request(rdg);
+       if (!status)
+               return FALSE;
+       nCount = rdg_get_event_handles(rdg, events, 8);
+       if (nCount == 0)
+               return FALSE;
+       while (rdg->state <= RDG_CLIENT_STATE_OUT_CHANNEL_AUTHORIZE)
+       {
+               WaitForMultipleObjects(nCount, events, FALSE, 100);
+               status = rdg_check_event_handles(rdg);
+               if (!status)
+               {
+                       rdg->context->rdp->transport->layer = TRANSPORT_LAYER_CLOSED;
+                       return FALSE;
+               }
+       }
+       return TRUE;
+ }
+ BOOL rdg_in_channel_connect(rdpRdg* rdg, const char* hostname, UINT16 port, int timeout)
+ {
+       BOOL status;
+       DWORD nCount;
+       HANDLE events[8];
+       assert(hostname != NULL);
+       status = rdg_tls_in_connect(rdg, hostname, port, timeout);
+       if (!status)
+               return FALSE;
+       status = rdg_send_in_channel_request(rdg);
+       if (!status)
+               return FALSE;
+       nCount = rdg_get_event_handles(rdg, events, 8);
+       if (nCount == 0)
+               return FALSE;
+       while (rdg->state <= RDG_CLIENT_STATE_IN_CHANNEL_AUTHORIZE)
+       {
+               WaitForMultipleObjects(nCount, events, FALSE, 100);
+               status = rdg_check_event_handles(rdg);
+               if (!status)
+               {
+                       rdg->context->rdp->transport->layer = TRANSPORT_LAYER_CLOSED;
+                       return FALSE;
+               }
+       }
+       return TRUE;
+ }
+ BOOL rdg_tunnel_connect(rdpRdg* rdg)
+ {
+       BOOL status;
+       DWORD nCount;
+       HANDLE events[8];
+       rdg_send_handshake(rdg);
+       nCount = rdg_get_event_handles(rdg, events, 8);
+       if (nCount == 0)
+               return FALSE;
+       while (rdg->state < RDG_CLIENT_STATE_OPENED)
+       {
+               WaitForMultipleObjects(nCount, events, FALSE, 100);
+               status = rdg_check_event_handles(rdg);
+               if (!status)
+               {
+                       rdg->context->rdp->transport->layer = TRANSPORT_LAYER_CLOSED;
+                       return FALSE;
+               }
+       }
+       return TRUE;
+ }
+ BOOL rdg_connect(rdpRdg* rdg, const char* hostname, UINT16 port, int timeout)
+ {
+       BOOL status;
+       assert(rdg != NULL);
+       status = rdg_out_channel_connect(rdg, hostname, port, timeout);
+       if (!status)
+               return FALSE;
+       status = rdg_in_channel_connect(rdg, hostname, port, timeout);
+       if (!status)
+               return FALSE;
+       status = rdg_tunnel_connect(rdg);
+       if (!status)
+               return FALSE;
+       return TRUE;
+ }
+ int rdg_write_data_packet(rdpRdg* rdg, BYTE* buf, int size)
+ {
+       int status;
+       wStream* sChunk;
+       int packetSize = size + 10;
+       char chunkSize[11];
+       if (size < 1)
+               return 0;
+       sprintf_s(chunkSize, sizeof(chunkSize), "%X\r\n", packetSize);
+       sChunk = Stream_New(NULL, strlen(chunkSize) + packetSize + 2);
+       if (!sChunk)
+               return -1;
+       Stream_Write(sChunk, chunkSize, strlen(chunkSize));
+       Stream_Write_UINT16(sChunk, PKT_TYPE_DATA);   /* Type */
+       Stream_Write_UINT16(sChunk, 0);   /* Reserved */
+       Stream_Write_UINT32(sChunk, packetSize);   /* Packet length */
+       Stream_Write_UINT16(sChunk, size);   /* Data size */
+       Stream_Write(sChunk, buf, size);   /* Data */
+       Stream_Write(sChunk, "\r\n", 2);
+       Stream_SealLength(sChunk);
+       status = tls_write_all(rdg->tlsIn, Stream_Buffer(sChunk), Stream_Length(sChunk));
+       Stream_Free(sChunk, TRUE);
+       if (status < 0)
+               return -1;
+       return size;
+ }
+ BOOL rdg_process_close_packet(rdpRdg* rdg)
+ {
+       int status;
+       wStream* sChunk;
+       int packetSize = 12;
+       char chunkSize[11];
+     
+       sprintf_s(chunkSize, sizeof(chunkSize), "%X\r\n", packetSize);
+     
+       sChunk = Stream_New(NULL, strlen(chunkSize) + packetSize + 2);
+     
+       if (!sChunk)
+               return FALSE;
+     
+       Stream_Write(sChunk, chunkSize, strlen(chunkSize));
+     
+       Stream_Write_UINT16(sChunk, PKT_TYPE_CLOSE_CHANNEL_RESPONSE);   /* Type */
+       Stream_Write_UINT16(sChunk, 0);   /* Reserved */
+       Stream_Write_UINT32(sChunk, packetSize);   /* Packet length */
+     
+       Stream_Write_UINT32(sChunk, 0);   /* Status code */
+     
+       Stream_Write(sChunk, "\r\n", 2);
+       Stream_SealLength(sChunk);
+     
+       status = tls_write_all(rdg->tlsIn, Stream_Buffer(sChunk), Stream_Length(sChunk));
+       Stream_Free(sChunk, TRUE);
+     
+       return (status < 0 ? FALSE : TRUE);
+ }
+ BOOL rdg_process_keep_alive_packet(rdpRdg* rdg)
+ {
+       int status;
+       wStream* sChunk;
+       int packetSize = 8;
+       char chunkSize[11];
+     
+       sprintf_s(chunkSize, sizeof(chunkSize), "%X\r\n", packetSize);
+     
+       sChunk = Stream_New(NULL, strlen(chunkSize) + packetSize + 2);
+     
+       if (!sChunk)
+               return FALSE;
+     
+       Stream_Write(sChunk, chunkSize, strlen(chunkSize));
+     
+       Stream_Write_UINT16(sChunk, PKT_TYPE_KEEPALIVE);   /* Type */
+       Stream_Write_UINT16(sChunk, 0);   /* Reserved */
+       Stream_Write_UINT32(sChunk, packetSize);   /* Packet length */
+     
+       Stream_Write(sChunk, "\r\n", 2);
+       Stream_SealLength(sChunk);
+     
+       status = tls_write_all(rdg->tlsIn, Stream_Buffer(sChunk), Stream_Length(sChunk));
+       Stream_Free(sChunk, TRUE);
+     
+       return (status < 0 ? FALSE : TRUE);
+ }
+ BOOL rdg_process_unknown_packet(rdpRdg* rdg, int type)
+ {
+       WLog_WARN(TAG, "Unknown Control Packet received: %X", type);
+       return TRUE;
+ }
+ BOOL rdg_process_control_packet(rdpRdg* rdg, int type, int packetLength)
+ {
+       wStream* s = NULL;
+       int readCount = 0;
+       int status;
+       int payloadSize = packetLength - sizeof(RdgPacketHeader);
+       if (payloadSize)
+       {
+               s = Stream_New(NULL, payloadSize);
+               if (!s)
+                       return FALSE;
+               while (readCount < payloadSize)
+               {
+                       status = BIO_read(rdg->tlsOut->bio, Stream_Pointer(s), sizeof(RdgPacketHeader) - readCount);
+                       if (status <= 0)
+                       {
+                               if (!BIO_should_retry(rdg->tlsOut->bio))
+                               {
+                                       Stream_Free(s, TRUE);
+                                       return FALSE;
+                               }
+                               continue;
+                       }
+                       Stream_Seek(s, status);
+                       readCount += status;
+               }
+       }
+       switch (type)
+       {
+               case PKT_TYPE_CLOSE_CHANNEL:
+                       EnterCriticalSection(&rdg->writeSection);
+                       status = rdg_process_close_packet(rdg);
+                       LeaveCriticalSection(&rdg->writeSection);
+                       break;
+               case PKT_TYPE_KEEPALIVE:
+                       EnterCriticalSection(&rdg->writeSection);
+                       status = rdg_process_keep_alive_packet(rdg);
+                       LeaveCriticalSection(&rdg->writeSection);
+                       break;
+             
+               default:
+                       status = rdg_process_unknown_packet(rdg, type);
+                       break;
+       }
+       Stream_Free(s, TRUE);
+       return status;
+ }
+ int rdg_read_data_packet(rdpRdg* rdg, BYTE* buffer, int size)
+ {
+       RdgPacketHeader header;
+       int readCount = 0;
+       int readSize;
+       int status;
+       int pending;
+       if (!rdg->packetRemainingCount)
+       {
+               while (readCount < sizeof(RdgPacketHeader))
+               {
+                       status = BIO_read(rdg->tlsOut->bio, (BYTE*)(&header) + readCount, sizeof(RdgPacketHeader) - readCount);
+                       if (status <= 0)
+                       {
+                               if (!BIO_should_retry(rdg->tlsOut->bio))
+                               {
+                                       return -1;
+                               }
+                               if (!readCount)
+                               {
+                                       return 0;
+                               }
+                               BIO_wait_read(rdg->tlsOut->bio, 50);
+                               continue;
+                       }
+                       readCount += status;
+               }
+               if (header.type != PKT_TYPE_DATA)
+               {
+                       status = rdg_process_control_packet(rdg, header.type, header.packetLength);
+                       if (!status)
+                       {
+                               return -1;
+                       }
+                       return 0;
+               }
+               readCount = 0;
+               while (readCount < 2)
+               {
+                       status = BIO_read(rdg->tlsOut->bio, (BYTE*)(&rdg->packetRemainingCount) + readCount, 2 - readCount);
+                       if (status < 0)
+                       {
+                               if (!BIO_should_retry(rdg->tlsOut->bio))
+                               {
+                                       return -1;
+                               }
+                               BIO_wait_read(rdg->tlsOut->bio, 50);
+                               continue;
+                       }
+                       readCount += status;
+               }
+       }
+       readSize = (rdg->packetRemainingCount < size ? rdg->packetRemainingCount : size);
+       status = BIO_read(rdg->tlsOut->bio, buffer, readSize);
+       if (status <= 0)
+       {
+               if (!BIO_should_retry(rdg->tlsOut->bio))
+               {
+                       return -1;
+               }
+               return 0;
+       }
+       rdg->packetRemainingCount -= status;
+       pending = BIO_pending(rdg->tlsOut->bio);
+       if (pending > 0)
+               SetEvent(rdg->readEvent);
+       else
+               ResetEvent(rdg->readEvent);
+       return status;
+ }
+ long rdg_bio_callback(BIO* bio, int mode, const char* argp, int argi, long argl, long ret)
+ {
+       return 1;
+ }
+ static int rdg_bio_write(BIO* bio, const char* buf, int num)
+ {
+       int status;
+       rdpRdg* rdg = (rdpRdg*) bio->ptr;
+       BIO_clear_flags(bio, BIO_FLAGS_WRITE);
+       EnterCriticalSection(&rdg->writeSection);
+       status = rdg_write_data_packet(rdg, (BYTE*) buf, num);
+       LeaveCriticalSection(&rdg->writeSection);
+       if (status < 0)
+       {
+               BIO_clear_flags(bio, BIO_FLAGS_SHOULD_RETRY);
+               return -1;
+       }
+       else if (status < num)
+       {
+               BIO_set_flags(bio, BIO_FLAGS_WRITE);
+               WSASetLastError(WSAEWOULDBLOCK);
+       }
+       else
+       {
+               BIO_set_flags(bio, BIO_FLAGS_WRITE);
+       }
+       return status;
+ }
+ static int rdg_bio_read(BIO* bio, char* buf, int size)
+ {
+       int status;
+       rdpRdg* rdg = (rdpRdg*) bio->ptr;
+       status = rdg_read_data_packet(rdg, (BYTE*) buf, size);
+       if (status < 0)
+       {
+               BIO_clear_retry_flags(bio);
+               return -1;
+       }
+       else if (status == 0)
+       {
+               BIO_set_retry_read(bio);
+               WSASetLastError(WSAEWOULDBLOCK);
+               return -1;
+       }
+       else
+       {
+               BIO_set_flags(bio, BIO_FLAGS_READ);
+       }
+       return status;
+ }
+ static int rdg_bio_puts(BIO* bio, const char* str)
+ {
+       return -2;
+ }
+ static int rdg_bio_gets(BIO* bio, char* str, int size)
+ {
+       return -2;
+ }
+ static long rdg_bio_ctrl(BIO* bio, int cmd, long arg1, void* arg2)
+ {
+       int status = 0;
+       rdpRdg* rdg = (rdpRdg*)bio->ptr;
+       rdpTls* tlsOut = rdg->tlsOut;
+       rdpTls* tlsIn = rdg->tlsIn;
+       if (cmd == BIO_CTRL_FLUSH)
+       {
+               (void)BIO_flush(tlsOut->bio);
+               (void)BIO_flush(tlsIn->bio);
+               status = 1;
+       }
+       else if (cmd == BIO_C_GET_EVENT)
+       {
+               if (arg2)
+               {
+                       BIO_get_event(rdg->tlsOut->bio, arg2);
+                       status = 1;
+               }
+       }
+       else if (cmd == BIO_C_SET_NONBLOCK)
+       {
+               status = 1;
+       }
+       else if (cmd == BIO_C_READ_BLOCKED)
+       {
+               BIO* bio = tlsOut->bio;
+               status = BIO_read_blocked(bio);
+       }
+       else if (cmd == BIO_C_WRITE_BLOCKED)
+       {
+               BIO* bio = tlsIn->bio;
+               status = BIO_write_blocked(bio);
+       }
+       else if (cmd == BIO_C_WAIT_READ)
+       {
+               int timeout = (int) arg1;
+               BIO* bio = tlsOut->bio;
+               if (BIO_read_blocked(bio))
+                       return BIO_wait_read(bio, timeout);
+               else if (BIO_write_blocked(bio))
+                       return BIO_wait_write(bio, timeout);
+               else
+                       status = 1;
+       }
+       else if (cmd == BIO_C_WAIT_WRITE)
+       {
+               int timeout = (int) arg1;
+               BIO* bio = tlsIn->bio;
+               if (BIO_write_blocked(bio))
+                       status = BIO_wait_write(bio, timeout);
+               else if (BIO_read_blocked(bio))
+                       status = BIO_wait_read(bio, timeout);
+               else
+                       status = 1;
+       }
+       return status;
+ }
+ static int rdg_bio_new(BIO* bio)
+ {
+       bio->init = 1;
+       bio->num = 0;
+       bio->ptr = NULL;
+       bio->flags = BIO_FLAGS_SHOULD_RETRY;
+       return 1;
+ }
+ static int rdg_bio_free(BIO* bio)
+ {
+       return 1;
+ }
+ static BIO_METHOD rdg_bio_methods =
+ {
+       BIO_TYPE_TSG,
+       "RDGateway",
+       rdg_bio_write,
+       rdg_bio_read,
+       rdg_bio_puts,
+       rdg_bio_gets,
+       rdg_bio_ctrl,
+       rdg_bio_new,
+       rdg_bio_free,
+       NULL,
+ };
+ BIO_METHOD* BIO_s_rdg(void)
+ {
+       return &rdg_bio_methods;
+ }
+ rdpRdg* rdg_new(rdpTransport* transport)
+ {
+       rdpRdg* rdg;
+       RPC_CSTR stringUuid;
+       char bracedUuid[40];
+       RPC_STATUS rpcStatus;
+       assert(transport != NULL);
+       rdg = (rdpRdg*) calloc(1, sizeof(rdpRdg));
+       if (rdg)
+       {
+               rdg->state = RDG_CLIENT_STATE_INITIAL;
+               rdg->context = transport->context;
+               rdg->settings = rdg->context->settings;
+               UuidCreate(&rdg->guid);
+               rpcStatus = UuidToStringA(&rdg->guid, &stringUuid);
+               if (rpcStatus == RPC_S_OUT_OF_MEMORY)
+                       goto rdg_alloc_error;
+               sprintf_s(bracedUuid, sizeof(bracedUuid), "{%s}", stringUuid);
+               RpcStringFreeA(&stringUuid);
+               rdg->tlsOut = tls_new(rdg->settings);
+               if (!rdg->tlsOut)
+                       goto rdg_alloc_error;
+               rdg->tlsIn = tls_new(rdg->settings);
+               if (!rdg->tlsIn)
+                       goto rdg_alloc_error;
+               rdg->http = http_context_new();
+               if (!rdg->http)
+                       goto rdg_alloc_error;
+               http_context_set_uri(rdg->http, "/remoteDesktopGateway/");
+               http_context_set_accept(rdg->http, "*/*");
+               http_context_set_cache_control(rdg->http, "no-cache");
+               http_context_set_pragma(rdg->http, "no-cache");
+               http_context_set_connection(rdg->http, "Keep-Alive");
+               http_context_set_user_agent(rdg->http, "MS-RDGateway/1.0");
+               http_context_set_host(rdg->http, rdg->settings->GatewayHostname);
+               http_context_set_rdg_connection_id(rdg->http, bracedUuid);
+               if (!rdg->http->URI || !rdg->http->Accept || !rdg->http->CacheControl ||
+                               !rdg->http->Pragma || !rdg->http->Connection || !rdg->http->UserAgent
+                               || !rdg->http->Host || !rdg->http->RdgConnectionId)
+               {
+                       goto rdg_alloc_error;
+               }
+               rdg->frontBio = BIO_new(BIO_s_rdg());
+               if (!rdg->frontBio)
+                       goto rdg_alloc_error;
+               rdg->frontBio->ptr = rdg;
+               rdg->readEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+               if (!rdg->readEvent)
+                       goto rdg_alloc_error;
+         
+               InitializeCriticalSection(&rdg->writeSection);
+       }
+       return rdg;
+ rdg_alloc_error:
+       rdg_free(rdg);
+       return NULL;
+ }
+ void rdg_free(rdpRdg* rdg)
+ {
+       if (!rdg)
+               return;
+       if (rdg->tlsOut)
+       {
+               tls_free(rdg->tlsOut);
+               rdg->tlsOut = NULL;
+       }
+       if (rdg->tlsIn)
+       {
+               tls_free(rdg->tlsIn);
+               rdg->tlsIn = NULL;
+       }
+       if (rdg->http)
+       {
+               http_context_free(rdg->http);
+               rdg->http = NULL;
+       }
+       if (rdg->ntlm)
+       {
+               ntlm_free(rdg->ntlm);
+               rdg->ntlm = NULL;
+       }
+       if (rdg->readEvent)
+       {
+               CloseHandle(rdg->readEvent);
+               rdg->readEvent = NULL;
+       }
+     
+       DeleteCriticalSection(&rdg->writeSection);
+       free(rdg);
+ }
@@@ -41,6 -36,6 +36,7 @@@
  #include <valgrind/memcheck.h>
  #endif
  
++#include "../proxy.h"
  #include "http.h"
  #include "ntlm.h"
  #include "ncacn_http.h"
@@@ -535,16 -734,191 +735,205 @@@ out_free
        return NULL;
  }
  
- void rpc_client_virtual_connection_free(RpcVirtualConnection* virtualConnection)
+ void rpc_virtual_connection_free(RpcVirtualConnection* connection)
+ {
+       if (!connection)
+               return;
+       rpc_in_channel_free(connection->DefaultInChannel);
+       rpc_in_channel_free(connection->NonDefaultInChannel);
+       rpc_out_channel_free(connection->DefaultOutChannel);
+       rpc_out_channel_free(connection->NonDefaultOutChannel);
+       free(connection);
+ }
+ int rpc_channel_tls_connect(RpcChannel* channel, int timeout)
+ {
+       int sockfd;
+       rdpTls* tls;
+       int tlsStatus;
+       BIO* socketBio;
+       BIO* bufferedBio;
+       rdpRpc* rpc = channel->rpc;
+       rdpContext* context = rpc->context;
+       rdpSettings* settings = context->settings;
++      const char *peerHostname = settings->GatewayHostname;
++      int peerPort = settings->GatewayPort;
++      BOOL isProxyConnection = FALSE;
++
++      if (settings->HTTPProxyEnabled) {
++              peerHostname = settings->HTTPProxyHostname;
++              peerPort = settings->HTTPProxyPort;
++              isProxyConnection = TRUE;
++      }
 -      sockfd = freerdp_tcp_connect(context, settings, settings->GatewayHostname,
 -                                      settings->GatewayPort, timeout);
++      sockfd = freerdp_tcp_connect(context, settings, peerHostname,
++                                      peerPort, timeout);
+       if (sockfd < 1)
+               return -1;
+       socketBio = BIO_new(BIO_s_simple_socket());
+       if (!socketBio)
+               return FALSE;
+       BIO_set_fd(socketBio, sockfd, BIO_CLOSE);
+       bufferedBio = BIO_new(BIO_s_buffered_socket());
+       if (!bufferedBio)
+               return FALSE;
+       bufferedBio = BIO_push(bufferedBio, socketBio);
+       if (!BIO_set_nonblock(bufferedBio, TRUE))
+               return -1;
++      if (isProxyConnection) {
++              if (!http_proxy_connect(bufferedBio, settings->GatewayHostname, settings->GatewayPort))
++                      return -1;
++      }
++
+       channel->bio = bufferedBio;
+       tls = channel->tls = tls_new(settings);
+       if (!tls)
+               return -1;
+       tls->hostname = settings->GatewayHostname;
+       tls->port = settings->GatewayPort;
+       tls->isGatewayTransport = TRUE;
+       tlsStatus = tls_connect(tls, bufferedBio);
+       if (tlsStatus < 1)
+       {
+               if (tlsStatus < 0)
+               {
+                       if (!freerdp_get_last_error(context))
+                               freerdp_set_last_error(context, FREERDP_ERROR_TLS_CONNECT_FAILED);
+               }
+               else
+               {
+                       if (!freerdp_get_last_error(context))
+                               freerdp_set_last_error(context, FREERDP_ERROR_CONNECT_CANCELLED);
+               }
+               return -1;
+       }
+       return 1;
+ }
+ int rpc_in_channel_connect(RpcInChannel* inChannel, int timeout)
+ {
+       rdpRpc* rpc = inChannel->rpc;
+       /* Connect IN Channel */
+       if (rpc_channel_tls_connect((RpcChannel*) inChannel, timeout) < 0)
+               return -1;
+       rpc_in_channel_transition_to_state(inChannel, CLIENT_IN_CHANNEL_STATE_CONNECTED);
+       if (rpc_ncacn_http_ntlm_init(rpc, (RpcChannel*) inChannel) < 0)
+               return -1;
+       /* Send IN Channel Request */
+       if (rpc_ncacn_http_send_in_channel_request(rpc, inChannel) < 0)
+       {
+               WLog_ERR(TAG, "rpc_ncacn_http_send_in_channel_request failure");
+               return -1;
+       }
+       rpc_in_channel_transition_to_state(inChannel, CLIENT_IN_CHANNEL_STATE_SECURITY);
+       return 1;
+ }
+ int rpc_out_channel_connect(RpcOutChannel* outChannel, int timeout)
  {
-       if (virtualConnection)
+       rdpRpc* rpc = outChannel->rpc;
+       /* Connect OUT Channel */
+       if (rpc_channel_tls_connect((RpcChannel*) outChannel, timeout) < 0)
+               return -1;
+       rpc_out_channel_transition_to_state(outChannel, CLIENT_OUT_CHANNEL_STATE_CONNECTED);
+       if (rpc_ncacn_http_ntlm_init(rpc, (RpcChannel*) outChannel) < 0)
+               return FALSE;
+       /* Send OUT Channel Request */
+       if (rpc_ncacn_http_send_out_channel_request(rpc, outChannel, FALSE) < 0)
        {
-               CloseHandle(virtualConnection->DefaultInChannel->Mutex);
-               CloseHandle(virtualConnection->DefaultOutChannel->Mutex);
-               free(virtualConnection->DefaultInChannel);
-               free(virtualConnection->DefaultOutChannel);
-               free(virtualConnection);
+               WLog_ERR(TAG, "rpc_ncacn_http_send_out_channel_request failure");
+               return FALSE;
        }
+       rpc_out_channel_transition_to_state(outChannel, CLIENT_OUT_CHANNEL_STATE_SECURITY);
+       return 1;
+ }
+ int rpc_out_channel_replacement_connect(RpcOutChannel* outChannel, int timeout)
+ {
+       rdpRpc* rpc = outChannel->rpc;
+       /* Connect OUT Channel */
+       if (rpc_channel_tls_connect((RpcChannel*) outChannel, timeout) < 0)
+               return -1;
+       rpc_out_channel_transition_to_state(outChannel, CLIENT_OUT_CHANNEL_STATE_CONNECTED);
+       if (rpc_ncacn_http_ntlm_init(rpc, (RpcChannel*) outChannel) < 0)
+               return FALSE;
+       /* Send OUT Channel Request */
+       if (rpc_ncacn_http_send_out_channel_request(rpc, outChannel, TRUE) < 0)
+       {
+               WLog_ERR(TAG, "rpc_ncacn_http_send_out_channel_request failure");
+               return FALSE;
+       }
+       rpc_out_channel_transition_to_state(outChannel, CLIENT_OUT_CHANNEL_STATE_SECURITY);
+       return 1;
+ }
+ BOOL rpc_connect(rdpRpc* rpc, int timeout)
+ {
+       RpcInChannel* inChannel;
+       RpcOutChannel* outChannel;
+       RpcVirtualConnection* connection;
+       rpc->VirtualConnection = rpc_virtual_connection_new(rpc);
+       if (!rpc->VirtualConnection)
+               return FALSE;
+       connection = rpc->VirtualConnection;
+       inChannel = connection->DefaultInChannel;
+       outChannel = connection->DefaultOutChannel;
+       rpc_virtual_connection_transition_to_state(rpc, connection, VIRTUAL_CONNECTION_STATE_INITIAL);
+       if (rpc_in_channel_connect(inChannel, timeout) < 0)
+               return FALSE;
+       if (rpc_out_channel_connect(outChannel, timeout) < 0)
+               return FALSE;
+       return TRUE;
  }
  
  rdpRpc* rpc_new(rdpTransport* transport)
index 496bb15,0000000..bd176cc
mode 100644,000000..100644
--- /dev/null
@@@ -1,139 -1,0 +1,143 @@@
 +/**
 + * FreeRDP: A Remote Desktop Protocol Implementation
 + * HTTP Proxy support
 + *
 + * Copyright 2016 Christian Plattner <ccpp@gmx.at>
 + *
 + * Licensed under the Apache License, Version 2.0 (the "License");
 + * you may not use this file except in compliance with the License.
 + * You may obtain a copy of the License at
 + *
 + *     http://www.apache.org/licenses/LICENSE-2.0
 + *
 + * Unless required by applicable law or agreed to in writing, software
 + * distributed under the License is distributed on an "AS IS" BASIS,
 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 + * See the License for the specific language governing permissions and
 + * limitations under the License.
 + */
 +
 +
 +#include "proxy.h"
 +#include "freerdp/settings.h"
 +#include "tcp.h"
 +
 +#include "winpr/environment.h"        /* For GetEnvironmentVariableA */
 +
 +#define CRLF "\r\n"
 +
 +void http_proxy_read_environment(rdpSettings *settings, char *envname)
 +{
 +      char env[256];
 +      DWORD envlen;
 +      char *hostname, *pport;
 +
 +      envlen = GetEnvironmentVariableA(envname, env, sizeof(env));
 +      if(!envlen)
 +              return;
 +
 +      if (strncmp(env, "http://", 7)) {
 +              fprintf(stderr, "Proxy url must have scheme http. Ignoring.\n");
 +              return;
 +      }
 +
 +      settings->HTTPProxyEnabled = TRUE;
 +
 +      hostname = env + 7;
 +      pport = strchr(hostname, ':');
 +      if (pport) {
 +              *pport = '\0';
 +              settings->HTTPProxyPort = atoi(pport+1);
 +      }
 +      else {
 +              /* The default is 80. Also for Proxys. */
 +              settings->HTTPProxyPort = 80;
 +
 +              pport = strchr(hostname, '/');
 +              if(pport)
 +                      *pport = '\0';
 +      }
 +
 +      freerdp_set_param_string(settings, FreeRDP_HTTPProxyHostname, hostname);
 +}
 +
 +BOOL http_proxy_connect(BIO* bufferedBio, const char* hostname, UINT16 port)
 +{
 +      int status;
 +      wStream* s;
 +      char port_str[10], recv_buf[256], *eol;
 +      int resultsize;
 +
 +      _itoa_s(port, port_str, sizeof(port_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, port_str, strlen(port_str));
 +      Stream_Write(s, " HTTP/1.1" CRLF "Host: ", 17);
 +      Stream_Write(s, hostname, strlen(hostname));
 +      Stream_Write_UINT8(s, ':');
 +      Stream_Write(s, port_str, strlen(port_str));
 +      Stream_Write(s, CRLF CRLF, 4);
 +
 +      status = BIO_write(bufferedBio, Stream_Buffer(s), Stream_GetPosition(s));
 +
 +      if (status != Stream_GetPosition(s)) {
 +              fprintf(stderr, "HTTP proxy: failed to write CONNECT request\n");
 +              return FALSE;
 +      }
 +
 +      Stream_Free(s, TRUE);
 +      s = NULL;
 +
 +      /* Read result until CR-LF-CR-LF.
 +       * Keep recv_buf a null-terminated string. */
 +
 +      memset(recv_buf, '\0', sizeof(recv_buf));
 +      resultsize = 0;
 +      while ( strstr(recv_buf, CRLF CRLF) == NULL ) {
 +              if (resultsize >= sizeof(recv_buf)-1) {
 +                      fprintf(stderr, "HTTP Reply headers too long.\n");
 +                      return FALSE;
 +              }
 +
 +              status = BIO_read(bufferedBio, (BYTE*)recv_buf + resultsize, sizeof(recv_buf)-resultsize-1);
 +              if (status < 0) {
 +                      /* Error? */
++                      if (BIO_should_retry(bufferedBio)) {
++                              USleep(100);
++                              continue;
++                      }
 +                      return FALSE;
 +              }
 +              else if (status == 0) {
 +                      /* Error? */
 +                      fprintf(stderr, "BIO_read() returned zero\n");
 +                      return FALSE;
 +              }
 +              resultsize += status;
 +      }
 +
 +      /* Extract HTTP status line */
 +      eol = strchr(recv_buf, '\r');
 +      if (!eol) {
 +              /* should never happen */
 +              return FALSE;
 +      }
 +
 +      *eol = '\0';
 +
 +      fprintf(stderr, "HTTP proxy: %s\n", recv_buf);
 +
 +      if (strlen(recv_buf) < 12) {
 +              return FALSE;
 +      }
 +
 +      recv_buf[7] = 'X';
 +      if (strncmp(recv_buf, "HTTP/1.X 200", 12))
 +              return FALSE;
 +      
 +      return TRUE;
 +}
 +
@@@ -459,61 -625,59 +625,60 @@@ rdpSettings* freerdp_settings_clone(rdp
        if (_settings)
        {
                CopyMemory(_settings, settings, sizeof(rdpSettings));
-               /**
-                 * Generated Code
-                 */
                /* char* values */
-               _settings->ServerHostname = _strdup(settings->ServerHostname); /* 20 */
-               _settings->Username = _strdup(settings->Username); /* 21 */
-               _settings->Password = _strdup(settings->Password); /* 22 */
-               _settings->Domain = _strdup(settings->Domain); /* 23 */
-               _settings->PasswordHash = _strdup(settings->PasswordHash); /* 24 */
+ #define CHECKED_STRDUP(name) if (settings->name && !(_settings->name = _strdup(settings->name))) goto out_fail
+               CHECKED_STRDUP(ServerHostname);  /* 20 */
+               CHECKED_STRDUP(Username); /* 21 */
+               CHECKED_STRDUP(Password); /* 22 */
+               CHECKED_STRDUP(Domain); /* 23 */
+               CHECKED_STRDUP(PasswordHash); /* 24 */
                _settings->ClientHostname = NULL; /* 134 */
                _settings->ClientProductId = NULL; /* 135 */
-               _settings->AlternateShell = _strdup(settings->AlternateShell); /* 640 */
-               _settings->ShellWorkingDirectory = _strdup(settings->ShellWorkingDirectory); /* 641 */
-               _settings->ClientAddress = _strdup(settings->ClientAddress); /* 769 */
-               _settings->ClientDir = _strdup(settings->ClientDir); /* 770 */
-               _settings->DynamicDSTTimeZoneKeyName = _strdup(settings->DynamicDSTTimeZoneKeyName); /* 897 */
-               _settings->RemoteAssistanceSessionId = _strdup(settings->RemoteAssistanceSessionId); /* 1025 */
-               _settings->RemoteAssistancePassStub = _strdup(settings->RemoteAssistancePassStub); /* 1026 */
-               _settings->RemoteAssistancePassword = _strdup(settings->RemoteAssistancePassword); /* 1027 */
-               _settings->RemoteAssistanceRCTicket = _strdup(settings->RemoteAssistanceRCTicket); /* 1028 */
-               _settings->AuthenticationServiceClass = _strdup(settings->AuthenticationServiceClass); /* 1098 */
-               _settings->PreconnectionBlob = _strdup(settings->PreconnectionBlob); /* 1155 */
-               _settings->KerberosKdc = _strdup(settings->KerberosKdc); /* 1344 */
-               _settings->KerberosRealm = _strdup(settings->KerberosRealm); /* 1345 */
-               _settings->CertificateName = _strdup(settings->CertificateName); /* 1409 */
-               _settings->CertificateFile = _strdup(settings->CertificateFile); /* 1410 */
-               _settings->PrivateKeyFile = _strdup(settings->PrivateKeyFile); /* 1411 */
-               _settings->RdpKeyFile = _strdup(settings->RdpKeyFile); /* 1412 */
-               _settings->WindowTitle = _strdup(settings->WindowTitle); /* 1542 */
-               _settings->WmClass = _strdup(settings->WmClass); /* 1549 */
-               _settings->ComputerName = _strdup(settings->ComputerName); /* 1664 */
-               _settings->ConnectionFile = _strdup(settings->ConnectionFile); /* 1728 */
-               _settings->AssistanceFile = _strdup(settings->AssistanceFile); /* 1729 */
-               _settings->HomePath = _strdup(settings->HomePath); /* 1792 */
-               _settings->ConfigPath = _strdup(settings->ConfigPath); /* 1793 */
-               _settings->CurrentPath = _strdup(settings->CurrentPath); /* 1794 */
-               _settings->DumpRemoteFxFile = _strdup(settings->DumpRemoteFxFile); /* 1858 */
-               _settings->PlayRemoteFxFile = _strdup(settings->PlayRemoteFxFile); /* 1859 */
-               _settings->GatewayHostname = _strdup(settings->GatewayHostname); /* 1986 */
-               _settings->GatewayUsername = _strdup(settings->GatewayUsername); /* 1987 */
-               _settings->GatewayPassword = _strdup(settings->GatewayPassword); /* 1988 */
-               _settings->GatewayDomain = _strdup(settings->GatewayDomain); /* 1989 */
-               _settings->HTTPProxyHostname = settings->HTTPProxyHostname; /* 1996 */
-               _settings->RemoteApplicationName = _strdup(settings->RemoteApplicationName); /* 2113 */
-               _settings->RemoteApplicationIcon = _strdup(settings->RemoteApplicationIcon); /* 2114 */
-               _settings->RemoteApplicationProgram = _strdup(settings->RemoteApplicationProgram); /* 2115 */
-               _settings->RemoteApplicationFile = _strdup(settings->RemoteApplicationFile); /* 2116 */
-               _settings->RemoteApplicationGuid = _strdup(settings->RemoteApplicationGuid); /* 2117 */
-               _settings->RemoteApplicationCmdLine = _strdup(settings->RemoteApplicationCmdLine); /* 2118 */
-               _settings->ImeFileName = _strdup(settings->ImeFileName); /* 2628 */
-               _settings->DrivesToRedirect = _strdup(settings->DrivesToRedirect); /* 4290 */
+               CHECKED_STRDUP(AlternateShell); /* 640 */
+               CHECKED_STRDUP(ShellWorkingDirectory); /* 641 */
+               CHECKED_STRDUP(ClientAddress); /* 769 */
+               CHECKED_STRDUP(ClientDir); /* 770 */
+               CHECKED_STRDUP(DynamicDSTTimeZoneKeyName); /* 897 */
+               CHECKED_STRDUP(RemoteAssistanceSessionId); /* 1025 */
+               CHECKED_STRDUP(RemoteAssistancePassStub); /* 1026 */
+               CHECKED_STRDUP(RemoteAssistancePassword); /* 1027 */
+               CHECKED_STRDUP(RemoteAssistanceRCTicket); /* 1028 */
+               CHECKED_STRDUP(AuthenticationServiceClass); /* 1098 */
+               CHECKED_STRDUP(AllowedTlsCiphers); /* 1101 */
+               CHECKED_STRDUP(NtlmSamFile); /* 1103 */
+               CHECKED_STRDUP(PreconnectionBlob); /* 1155 */
+               CHECKED_STRDUP(KerberosKdc); /* 1344 */
+               CHECKED_STRDUP(KerberosRealm); /* 1345 */
+               CHECKED_STRDUP(CertificateName); /* 1409 */
+               CHECKED_STRDUP(CertificateFile); /* 1410 */
+               CHECKED_STRDUP(PrivateKeyFile); /* 1411 */
+               CHECKED_STRDUP(RdpKeyFile); /* 1412 */
+               CHECKED_STRDUP(CertificateContent); /* 1416 */
+               CHECKED_STRDUP(PrivateKeyContent); /* 1417 */
+               CHECKED_STRDUP(RdpKeyContent); /* 1418 */
+               CHECKED_STRDUP(WindowTitle); /* 1542 */
+               CHECKED_STRDUP(WmClass); /* 1549 */
+               CHECKED_STRDUP(ComputerName); /* 1664 */
+               CHECKED_STRDUP(ConnectionFile); /* 1728 */
+               CHECKED_STRDUP(AssistanceFile); /* 1729 */
+               CHECKED_STRDUP(HomePath); /* 1792 */
+               CHECKED_STRDUP(ConfigPath); /* 1793 */
+               CHECKED_STRDUP(CurrentPath); /* 1794 */
+               CHECKED_STRDUP(DumpRemoteFxFile); /* 1858 */
+               CHECKED_STRDUP(PlayRemoteFxFile); /* 1859 */
+               CHECKED_STRDUP(GatewayHostname); /* 1986 */
+               CHECKED_STRDUP(GatewayUsername); /* 1987 */
+               CHECKED_STRDUP(GatewayPassword); /* 1988 */
+               CHECKED_STRDUP(GatewayDomain); /* 1989 */
++              CHECKED_STRDUP(HTTPProxyHostname); /* 2016 */
+               CHECKED_STRDUP(RemoteApplicationName); /* 2113 */
+               CHECKED_STRDUP(RemoteApplicationIcon); /* 2114 */
+               CHECKED_STRDUP(RemoteApplicationProgram); /* 2115 */
+               CHECKED_STRDUP(RemoteApplicationFile); /* 2116 */
+               CHECKED_STRDUP(RemoteApplicationGuid); /* 2117 */
+               CHECKED_STRDUP(RemoteApplicationCmdLine); /* 2118 */
+               CHECKED_STRDUP(ImeFileName); /* 2628 */
+               CHECKED_STRDUP(DrivesToRedirect); /* 4290 */
                /**
                  * Manual Code
                  */
@@@ -479,29 -224,50 +225,57 @@@ BOOL transport_connect(rdpTransport* tr
  
        if (transport->GatewayEnabled)
        {
-               transport->layer = TRANSPORT_LAYER_TSG;
-               transport->SplitInputOutput = TRUE;
-               transport->TcpOut = freerdp_tcp_new(settings);
 +              /* For TSGateway, find the system HTTPS proxy automatically */
 +              if (!transport->settings->HTTPProxyEnabled)
 +                      http_proxy_read_environment(settings, "https_proxy");
 +
 +              if (!transport->settings->HTTPProxyEnabled)
 +                      http_proxy_read_environment(settings, "HTTPS_PROXY");
 +
+               if (!status && settings->GatewayHttpTransport)
+               {
+                       transport->rdg = rdg_new(transport);
  
-               if (!freerdp_tcp_connect(transport->TcpIn, settings->GatewayHostname, settings->GatewayPort, timeout) ||
-                               !freerdp_tcp_set_blocking_mode(transport->TcpIn, FALSE))
-                       return FALSE;
+                       if (!transport->rdg)
+                               return FALSE;
  
-               if (!freerdp_tcp_connect(transport->TcpOut, settings->GatewayHostname, settings->GatewayPort, timeout) ||
-                               !freerdp_tcp_set_blocking_mode(transport->TcpOut, FALSE))
-                       return FALSE;
+                       status = rdg_connect(transport->rdg, hostname, port, timeout);
  
-               if (!transport_tsg_connect(transport, hostname, port))
-                       return FALSE;
+                       if (status)
+                       {
+                               transport->frontBio = transport->rdg->frontBio;
+                               BIO_set_nonblock(transport->frontBio, 0);
+                               transport->layer = TRANSPORT_LAYER_TSG;
+                               status = TRUE;
+                       }
+                       else
+                       {
+                               rdg_free(transport->rdg);
+                               transport->rdg = NULL;
+                       }
+               }
  
-               status = TRUE;
+               if (!status && settings->GatewayRpcTransport)
+               {
+                       transport->tsg = tsg_new(transport);
+                       if (!transport->tsg)
+                               return FALSE;
+                       status = tsg_connect(transport->tsg, hostname, port, timeout);
+                       if (status)
+                       {
+                               transport->frontBio = transport->tsg->bio;
+                               transport->layer = TRANSPORT_LAYER_TSG;
+                               status = TRUE;
+                       }
+                       else
+                       {
+                               tsg_free(transport->tsg);
+                               transport->tsg = NULL;
+                       }
+               }
        }
        else
        {