diff --git a/libfreerdp/core/CMakeLists.txt b/libfreerdp/core/CMakeLists.txt index 0f020304a..99b1a1b21 100644 --- a/libfreerdp/core/CMakeLists.txt +++ b/libfreerdp/core/CMakeLists.txt @@ -28,6 +28,8 @@ set(${MODULE_PREFIX}_GATEWAY_DIR "gateway") set(${MODULE_PREFIX}_GATEWAY_SRCS ${${MODULE_PREFIX}_GATEWAY_DIR}/tsg.c ${${MODULE_PREFIX}_GATEWAY_DIR}/tsg.h + ${${MODULE_PREFIX}_GATEWAY_DIR}/rdg.c + ${${MODULE_PREFIX}_GATEWAY_DIR}/rdg.h ${${MODULE_PREFIX}_GATEWAY_DIR}/rpc.c ${${MODULE_PREFIX}_GATEWAY_DIR}/rpc.h ${${MODULE_PREFIX}_GATEWAY_DIR}/rpc_bind.c diff --git a/libfreerdp/core/gateway/http.c b/libfreerdp/core/gateway/http.c index c96255096..40f5104ec 100644 --- a/libfreerdp/core/gateway/http.c +++ b/libfreerdp/core/gateway/http.c @@ -174,6 +174,17 @@ BOOL http_context_set_pragma(HttpContext* context, const char* Pragma) return TRUE; } +BOOL http_context_set_rdg_connection_id(HttpContext* context, const char* RdgConnectionId) +{ + free(context->RdgConnectionId); + context->RdgConnectionId = _strdup(RdgConnectionId); + + if (!context->RdgConnectionId) + return FALSE; + + return TRUE; +} + void http_context_free(HttpContext* context) { if (context) @@ -186,6 +197,7 @@ void http_context_free(HttpContext* context) free(context->CacheControl); free(context->Connection); free(context->Pragma); + free(context->RdgConnectionId); free(context); } } @@ -234,6 +246,17 @@ BOOL http_request_set_auth_param(HttpRequest* request, const char* AuthParam) return TRUE; } +BOOL http_request_set_transfer_encoding(HttpRequest* request, const char* TransferEncoding) +{ + free(request->TransferEncoding); + request->TransferEncoding = _strdup(TransferEncoding); + + if (!request->TransferEncoding) + return FALSE; + + return TRUE; +} + char* http_encode_body_line(char* param, char* value) { char* line; @@ -325,6 +348,26 @@ wStream* http_request_write(HttpContext* context, HttpRequest* request) goto out_free; } + if (context->RdgConnectionId) + { + lines[count] = http_encode_body_line("RDG-Connection-Id", context->RdgConnectionId); + + if (!lines[count]) + goto out_free; + + count++; + } + + if (request->TransferEncoding) + { + lines[count] = http_encode_body_line("Transfer-Encoding", request->TransferEncoding); + + if (!lines[count]) + goto out_free; + + count++; + } + if (request->Authorization) { lines[count] = http_encode_body_line("Authorization", request->Authorization); diff --git a/libfreerdp/core/gateway/http.h b/libfreerdp/core/gateway/http.h index 2966edaf7..87de33620 100644 --- a/libfreerdp/core/gateway/http.h +++ b/libfreerdp/core/gateway/http.h @@ -40,6 +40,7 @@ struct _http_context char* CacheControl; char* Connection; char* Pragma; + char* RdgConnectionId; }; BOOL http_context_set_method(HttpContext* context, const char* Method); @@ -50,6 +51,7 @@ BOOL http_context_set_accept(HttpContext* context, const char* Accept); BOOL http_context_set_cache_control(HttpContext* context, const char* CacheControl); BOOL http_context_set_connection(HttpContext* context, const char* Connection); BOOL http_context_set_pragma(HttpContext* context, const char* Pragma); +BOOL http_context_set_rdg_connection_id(HttpContext* context, const char* RdgConnectionId); HttpContext* http_context_new(void); void http_context_free(HttpContext* context); @@ -63,12 +65,14 @@ struct _http_request char* Authorization; int ContentLength; char* Content; + char* TransferEncoding; }; BOOL http_request_set_method(HttpRequest* request, const char* Method); BOOL http_request_set_uri(HttpRequest* request, const char* URI); BOOL http_request_set_auth_scheme(HttpRequest* request, const char* AuthScheme); BOOL http_request_set_auth_param(HttpRequest* request, const char* AuthParam); +BOOL http_request_set_transfer_encoding(HttpRequest* request, const char* TransferEncoding); wStream* http_request_write(HttpContext* context, HttpRequest* request); diff --git a/libfreerdp/core/gateway/rdg.c b/libfreerdp/core/gateway/rdg.c new file mode 100644 index 000000000..8dfc04dec --- /dev/null +++ b/libfreerdp/core/gateway/rdg.c @@ -0,0 +1,1465 @@ +/** +* FreeRDP: A Remote Desktop Protocol Implementation +* Remote Desktop Gateway (RDG) +* +* Copyright 2015 Denis Vincent +* +* 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 + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include "libfreerdp/core/transport.h" +#include "rdg.h" +#include "../rdp.h" + + +#define TAG FREERDP_TAG("core.gateway.rdg") + +typedef struct rdg_packet_header +{ + UINT16 type; + UINT16 reserved; + UINT32 packetLength; +} RdgPacketHeader; + + +BOOL rdg_write_packet(rdpRdg* rdg, wStream* sPacket) +{ + wStream* sChunk; + char chunkSize[11]; + int status; + + sprintf(chunkSize, "%X\r\n", 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) +{ + wStream* s; + RdgPacketHeader* packet; + UINT32 readCount = 0; + int status; + + s = Stream_New(NULL, 1024); + if (!s) + { + return NULL; + } + packet = (RdgPacketHeader*)s->buffer; + + 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) + { + Stream_EnsureCapacity(s, packet->packetLength); + packet = (RdgPacketHeader*)s->buffer; + } + 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 */ + Stream_Write_UINT16(s, 0); /* Reserved */ + Stream_Write_UINT32(s, 14); /* Packet length */ + + Stream_Write_UINT8(s, 1); /* Version major */ + Stream_Write_UINT8(s, 0); /* Version minor */ + Stream_Write_UINT16(s, 0); /* Client version, must be 0 */ + Stream_Write_UINT16(s, 0); /* Extended authentication */ + Stream_SealLength(s); + + status = rdg_write_packet(rdg, s); + Stream_Free(s, TRUE); + if (status) + { + rdg->state = RDG_CLIENT_STATE_HANDSHAKE; + } + + WLog_WARN(TAG, "Handshake sent"); + 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 */ + Stream_Write_UINT16(s, 0); /* Reserved */ + Stream_Write_UINT32(s, 16); /* Packet length */ + + Stream_Write_UINT32(s, HTTP_CAPABILITY_TYPE_QUAR_SOH); /* Capability flags */ + Stream_Write_UINT16(s, 0); /* Fields present */ + Stream_Write_UINT16(s, 0); /* Reserved, 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; + } + + WLog_WARN(TAG, "Tunnel sent"); + return status; +} + + +BOOL rdg_send_tunnel_authorization(rdpRdg* rdg) +{ + wStream* s; + BOOL status; + char* clientName = rdg->context->settings->ClientHostname; + UINT16 clientNameLen = strlen(clientName) + 1; + UINT32 packetSize = 12 + clientNameLen * 2; + int i; + + s = Stream_New(NULL, packetSize); + if (!s) + { + return FALSE; + } + + Stream_Write_UINT16(s, PKT_TYPE_TUNNEL_AUTH); /* Type */ + Stream_Write_UINT16(s, 0); /* Reserved */ + Stream_Write_UINT32(s, packetSize); /* Packet length */ + + Stream_Write_UINT16(s, 0); /* Fields present */ + 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); + if (status) + { + rdg->state = RDG_CLIENT_STATE_TUNNEL_AUTHORIZE; + } + + WLog_WARN(TAG, "Tunnel authorization sent"); + return status; +} + + +BOOL rdg_send_channel_create(rdpRdg* rdg) +{ + wStream* s; + BOOL status; + char* serverName = rdg->context->settings->ServerHostname; + UINT16 serverNameLen = strlen(serverName) + 1; + UINT32 packetSize = 16 + serverNameLen * 2; + int i; + + s = Stream_New(NULL, packetSize); + if (!s) + { + return FALSE; + } + + Stream_Write_UINT16(s, PKT_TYPE_CHANNEL_CREATE); /* Type */ + Stream_Write_UINT16(s, 0); /* Reserved */ + Stream_Write_UINT32(s, packetSize); /* Packet length */ + + Stream_Write_UINT8(s, 1); /* Number of resources. */ + Stream_Write_UINT8(s, 0); /* Number of alternative resources. */ + Stream_Write_UINT16(s, 3389); /* Port, this seems to be the standard... ? */ + Stream_Write_UINT16(s, 3); /* Protocol number, set according to an example... ? */ + 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); + if (status) + { + rdg->state = RDG_CLIENT_STATE_CHANNEL_CREATE; + } + + WLog_WARN(TAG, "Channel create sent"); + return status; +} + + +wStream* rdg_build_http_request(rdpRdg* rdg, char* method) +{ + wStream* s; + HttpRequest* request = NULL; + SecBuffer* ntlmToken = NULL; + char* base64NtlmToken = NULL; + + assert(rdg != NULL && 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); + 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); + + Stream_SealLength(s); + return s; +} + + +BOOL rdg_process_out_channel_response(rdpRdg* rdg, HttpResponse* response) +{ + wStream* s; + char* token64 = NULL; + int ntlmTokenLength = 0; + BYTE* ntlmTokenData = NULL; + rdpNtlm* ntlm = rdg->ntlm; + int status; + + 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; + } + + rdg->state = RDG_CLIENT_STATE_OUT_CHANNEL_AUTHORIZED; + + return TRUE; +} + + +BOOL rdg_process_in_channel_response(rdpRdg* rdg, HttpResponse* response) +{ + wStream* s; + char* token64 = NULL; + int ntlmTokenLength = 0; + BYTE* ntlmTokenData = NULL; + rdpNtlm* ntlm = rdg->ntlm; + int status; + + 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; + } + + 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); + + return TRUE; +} + + +BOOL rdg_process_handshake_response(rdpRdg* rdg, wStream* s) +{ + HRESULT errorCode; + + WLog_WARN(TAG, "Handshake response received"); + + if (rdg->state != RDG_CLIENT_STATE_HANDSHAKE) + { + return FALSE; + } + + Stream_Seek(s, 8); + Stream_Read_UINT32(s, errorCode); + + if (FAILED(errorCode)) + { + return FALSE; + } + + return rdg_send_tunnel_request(rdg); +} + + +BOOL rdg_process_tunnel_response(rdpRdg* rdg, wStream* s) +{ + HRESULT errorCode; + UINT16 fieldsPresent = 0; + + WLog_WARN(TAG, "Tunnel response received"); + + if (rdg->state != RDG_CLIENT_STATE_TUNNEL_CREATE) + { + return FALSE; + } + + Stream_Seek(s, 10); + Stream_Read_UINT32(s, errorCode); + + if (FAILED(errorCode)) + { + return FALSE; + } + + return rdg_send_tunnel_authorization(rdg); +} + + +BOOL rdg_process_tunnel_authorization_response(rdpRdg* rdg, wStream* s) +{ + HRESULT errorCode; + UINT16 fieldsPresent = 0; + + WLog_WARN(TAG, "Tunnel authorization response received"); + + if (rdg->state != RDG_CLIENT_STATE_TUNNEL_AUTHORIZE) + { + return FALSE; + } + + Stream_Seek(s, 8); + Stream_Read_UINT32(s, errorCode); + + if (FAILED(errorCode)) + { + return FALSE; + } + + return rdg_send_channel_create(rdg); +} + + +BOOL rdg_process_channel_response(rdpRdg* rdg, wStream* s) +{ + HRESULT errorCode; + UINT16 fieldsPresent = 0; + + WLog_WARN(TAG, "Channel create response received"); + + if (rdg->state != RDG_CLIENT_STATE_CHANNEL_CREATE) + { + return FALSE; + } + + Stream_Seek(s, 8); + Stream_Read_UINT32(s, errorCode); + + if (FAILED(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); + Stream_Read_UINT16(s, type); + Stream_SetPosition(s, 0); + + 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); + break; + } + + return status; +} + + +BOOL rdg_out_channel_recv(rdpRdg* rdg) +{ + HttpResponse* response = NULL; + wStream* s; + BOOL status = TRUE; + + 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) +{ + HttpResponse* response = NULL; + BOOL status = TRUE; + + 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; +} + + +UINT32 rdg_get_event_handles(rdpRdg* rdg, HANDLE* events) +{ + UINT32 nCount = 0; + + if (events) + events[nCount] = rdg->readEvent; + nCount++; + + if (rdg->tlsOut) + { + if (events) + BIO_get_event(rdg->tlsOut->bio, &events[nCount]); + nCount++; + } + + if (rdg->tlsIn) + { + if (events) + BIO_get_event(rdg->tlsIn->bio, &events[nCount]); + nCount++; + } + + return nCount; +} + + +BOOL rdg_check_event_handles(rdpRdg* rdg) +{ + HANDLE event = 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) + { + settings->Username = _strdup(settings->GatewayUsername); + settings->Domain = _strdup(settings->GatewayDomain); + settings->Password = _strdup(settings->GatewayPassword); + } + } + } + + 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; + + assert(rdg != NULL); + + 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) +{ + wStream* s = NULL; + int status; + + assert(rdg != 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; + rdpSettings* settings = rdg->context->settings; + BIO* socketBio = NULL; + BIO* bufferedBio = NULL; + + assert(rdg != NULL && hostname != NULL); + + sockfd = freerdp_tcp_connect(settings, settings->GatewayHostname, settings->GatewayPort, 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); + rdg->bioOut = bufferedBio; + status = BIO_set_nonblock(bufferedBio, TRUE); + if (!status) + { + 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; + rdpSettings* settings = rdg->context->settings; + BIO* socketBio = NULL; + BIO* bufferedBio = NULL; + + assert(rdg != NULL && hostname != NULL); + + sockfd = freerdp_tcp_connect(settings, settings->GatewayHostname, settings->GatewayPort, 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); + rdg->bioIn = bufferedBio; + status = BIO_set_nonblock(bufferedBio, TRUE); + if (!status) + { + 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; + UINT32 nCount; + HANDLE events[8]; + + assert(rdg != NULL && 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); + 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; + UINT32 nCount; + HANDLE events[8]; + + assert(rdg != NULL && 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); + 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; + UINT32 nCount; + HANDLE events[8]; + + rdg_send_handshake(rdg); + + nCount = rdg_get_event_handles(rdg, events); + 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; + + 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) +{ + wStream* sChunk; + int packetSize = size + 10; + char chunkSize[11]; + int status; + + if (size < 1) + { + return 0; + } + + sprintf(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; +} + + +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 (rdg->nonBlocking && !readCount) + { + return 0; + } + continue; + } + readCount += status; + } + if (header.type != PKT_TYPE_DATA) + { + /* Add better handling here !!! */ + 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; + } + continue; + } + readCount += status; + } + } + + readCount = 0; + readSize = (rdg->packetRemainingCount < size ? rdg->packetRemainingCount : size); + + status = BIO_read(rdg->tlsOut->bio, buffer + readCount, readSize - readCount); + if (status < 0) + { + if (!BIO_should_retry(rdg->tlsOut->bio)) + { + return -1; + } + } + readCount += status; + + rdg->packetRemainingCount -= readCount; + + pending = BIO_pending(rdg->tlsOut->bio); + if (pending > 0) + { + SetEvent(rdg->readEvent); + } + else + { + ResetEvent(rdg->readEvent); + } + return readSize; +} + + +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; + + status = rdg_write_data_packet(rdg, (BYTE*)buf, num); + + printf("write request %d, get %d\r\n", num, status); + + if (status < 0) + { + BIO_clear_flags(bio, BIO_FLAGS_SHOULD_RETRY); + BIO_clear_flags(bio, BIO_FLAGS_WRITE); + return -1; + } + else if (status < num) + { + BIO_set_flags(bio, BIO_FLAGS_SHOULD_RETRY); + BIO_set_flags(bio, BIO_FLAGS_WRITE); + WSASetLastError(WSAEWOULDBLOCK); + } + else + { + BIO_clear_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); + + printf("read request %d, get %d\r\n", size, status); + + if (status < 0) + { + BIO_clear_flags(bio, BIO_FLAGS_SHOULD_RETRY); + BIO_clear_flags(bio, BIO_FLAGS_READ); + return -1; + } + else if (status < size) + { + BIO_set_flags(bio, BIO_FLAGS_SHOULD_RETRY); + BIO_set_flags(bio, BIO_FLAGS_READ); + WSASetLastError(WSAEWOULDBLOCK); + } + else + { + BIO_clear_flags(bio, BIO_FLAGS_READ); + } + + return status; +} + + +static int rdg_bio_puts(BIO* bio, const char* str) +{ + return 1; +} + + +static int rdg_bio_gets(BIO* bio, char* str, int size) +{ + return 1; +} + + +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) + { + BIO_flush(tlsOut->bio); + BIO_flush(tlsIn->bio); + status = 1; + } + else if (cmd == BIO_C_GET_EVENT) + { + if (arg2) + { + //*((ULONG_PTR*)arg2) = (ULONG_PTR)tsg->rpc->client->PipeEvent; + //status = 1; + BIO_get_event(rdg->tlsOut->bio, arg2); + status = 1; + } + } + else if (cmd == BIO_C_SET_NONBLOCK) + { + //return BIO_ctrl(tlsOut->bio, cmd, arg1, arg2); + + rdg->nonBlocking = arg1; + status = 1; + } + else if (cmd == BIO_C_READ_BLOCKED) + { + //status = BIO_read_blocked(tlsOut->bio); + //status = 0; + } + else if (cmd == BIO_C_WRITE_BLOCKED) + { + //status = BIO_write_blocked(tlsIn->bio); + //status = 0; + } + else if (cmd == BIO_C_WAIT_READ) + { + int timeout = (int)arg1; + return BIO_wait_read(tlsOut->bio, timeout); + + //if (BIO_read_blocked(tlsOut->bio)) + // return BIO_wait_read(tlsOut->bio, timeout); + //else if (BIO_write_blocked(tlsIn->bio)) + // return BIO_wait_write(tlsIn->bio, timeout); + //else + // status = 0; + } + else if (cmd == BIO_C_WAIT_WRITE) + { + //int timeout = (int)arg1; + + //if (BIO_write_blocked(tlsIn->bio)) + // status = BIO_wait_write(tlsIn->bio, timeout); + //else if (BIO_read_blocked(tlsOut->bio)) + // status = BIO_wait_read(tlsOut->bio, timeout); + //else + // status = 0; + } + + 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; +} + + +void rdg_free(rdpRdg* rdg) +{ + if (rdg) + { + tls_free(rdg->tlsOut); + tls_free(rdg->tlsIn); + http_context_free(rdg->http); + ntlm_free(rdg->ntlm); + BIO_free(rdg->frontBio); + if (rdg->readEvent) + { + CloseHandle(rdg->readEvent); + } + + free(rdg); + } +} + + +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; + UuidCreate(&rdg->guid); + rpcStatus = UuidToStringA(&rdg->guid, &stringUuid); + if (rpcStatus == RPC_S_OUT_OF_MEMORY) + { + goto rdg_alloc_error; + } + sprintf(bracedUuid, "{%s}", stringUuid); + RpcStringFreeA(&stringUuid); + + rdg->tlsOut = tls_new(rdg->context->settings); + if (!rdg->tlsOut) + { + goto rdg_alloc_error; + } + rdg->tlsIn = tls_new(rdg->context->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->context->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, FALSE, FALSE, NULL); + if (!rdg->readEvent) + { + goto rdg_alloc_error; + } + } + + return rdg; + +rdg_alloc_error: + rdg_free(rdg); + return NULL; +} + + diff --git a/libfreerdp/core/gateway/rdg.h b/libfreerdp/core/gateway/rdg.h new file mode 100644 index 000000000..78ed3e362 --- /dev/null +++ b/libfreerdp/core/gateway/rdg.h @@ -0,0 +1,161 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Remote Desktop Gateway (RDG) + * + * Copyright 2015 Denis Vincent + * + * 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. + */ + +#ifndef FREERDP_CORE_RDG_H +#define FREERDP_CORE_RDG_H + + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include "http.h" +#include "ntlm.h" + + +/* HTTP channel response fields present flags. */ +#define HTTP_CHANNEL_RESPONSE_FIELD_CHANNELID 0x1 +#define HTTP_CHANNEL_RESPONSE_OPTIONAL 0x2 +#define HTTP_CHANNEL_RESPONSE_FIELD_UDPPORT 0x4 + +/* HTTP extended auth. */ +#define HTTP_EXTENDED_AUTH_NONE 0x0 +#define HTTP_EXTENDED_AUTH_SC 0x1 /* Smart card authentication. */ +#define HTTP_EXTENDED_AUTH_PAA 0x02 /* Pluggable authentication. */ + +/* HTTP packet types. */ +#define PKT_TYPE_HANDSHAKE_REQUEST 0x1 +#define PKT_TYPE_HANDSHAKE_RESPONSE 0x2 +#define PKT_TYPE_EXTENDED_AUTH_MSG 0x3 +#define PKT_TYPE_TUNNEL_CREATE 0x4 +#define PKT_TYPE_TUNNEL_RESPONSE 0x5 +#define PKT_TYPE_TUNNEL_AUTH 0x6 +#define PKT_TYPE_TUNNEL_AUTH_RESPONSE 0x7 +#define PKT_TYPE_CHANNEL_CREATE 0x8 +#define PKT_TYPE_CHANNEL_RESPONSE 0x9 +#define PKT_TYPE_DATA 0xA +#define PKT_TYPE_SERVICE_MESSAGE 0xB +#define PKT_TYPE_REAUTH_MESSAGE 0xC +#define PKT_TYPE_KEEPALIVE 0xD +#define PKT_TYPE_CLOSE_CHANNEL 0x10 +#define PKT_TYPE_CLOSE_CHANNEL_RESPONSE 0x11 + +/* HTTP tunnel auth fields present flags. */ +#define HTTP_TUNNEL_AUTH_FIELD_SOH 0x1 + +/* HTTP tunnel auth response fields present flags. */ +#define HTTP_TUNNEL_AUTH_RESPONSE_FIELD_REDIR_FLAGS 0x1 +#define HTTP_TUNNEL_AUTH_RESPONSE_FIELD_IDLE_TIMEOUT 0x2 +#define HTTP_TUNNEL_AUTH_RESPONSE_FIELD_SOH_RESPONSE 0x4 + +/* HTTP tunnel packet fields present flags. */ +#define HTTP_TUNNEL_PACKET_FIELD_PAA_COOKIE 0x1 +#define HTTP_TUNNEL_PACKET_FIELD_REAUTH 0x2 + +/* HTTP tunnel redir flags. */ +#define HTTP_TUNNEL_REDIR_ENABLE_ALL 0x80000000 +#define HTTP_TUNNEL_REDIR_DISABLE_ALL 0x40000000 +#define HTTP_TUNNEL_REDIR_DISABLE_DRIVE 0x1 +#define HTTP_TUNNEL_REDIR_DISABLE_PRINTER 0x2 +#define HTTP_TUNNEL_REDIR_DISABLE_PORT 0x4 +#define HTTP_TUNNEL_REDIR_DISABLE_CLIPBOARD 0x8 +#define HTTP_TUNNEL_REDIR_DISABLE_PNP 0x10 + +/* HTTP tunnel response fields present flags. */ +#define HTTP_TUNNEL_RESPONSE_FIELD_TUNNEL_ID 0x1 +#define HTTP_TUNNEL_RESPONSE_FIELD_CAPS 0x2 +#define HTTP_TUNNEL_RESPONSE_FIELD_SOH_REQ 0x4 +#define HTTP_TUNNEL_RESPONSE_FIELD_CONSENT_MSG 0x10 + +/* HTTP capability type enumeration. */ +#define HTTP_CAPABILITY_TYPE_QUAR_SOH 0x1 +#define HTTP_CAPABILITY_IDLE_TIMEOUT 0x2 +#define HTTP_CAPABILITY_MESSAGING_CONSENT_SIGN 0x4 +#define HTTP_CAPABILITY_MESSAGING_SERVICE_MSG 0x8 +#define HTTP_CAPABILITY_REAUTH 0x10 +#define HTTP_CAPABILITY_UDP_TRANSPORT 0x20 + + +enum +{ + RDG_CLIENT_STATE_INITIAL, + RDG_CLIENT_STATE_OUT_CHANNEL_REQUEST, + RDG_CLIENT_STATE_OUT_CHANNEL_AUTHORIZE, + RDG_CLIENT_STATE_OUT_CHANNEL_AUTHORIZED, + RDG_CLIENT_STATE_IN_CHANNEL_REQUEST, + RDG_CLIENT_STATE_IN_CHANNEL_AUTHORIZE, + RDG_CLIENT_STATE_IN_CHANNEL_AUTHORIZED, + RDG_CLIENT_STATE_HANDSHAKE, + RDG_CLIENT_STATE_TUNNEL_CREATE, + RDG_CLIENT_STATE_TUNNEL_AUTHORIZE, + RDG_CLIENT_STATE_CHANNEL_CREATE, + RDG_CLIENT_STATE_OPENED, + RDG_CLIENT_STATE_CLOSE, + RDG_CLIENT_STATE_CLOSED, +}; + + +typedef struct rdp_transport rdpTransport; + +typedef struct rdp_rdg rdpRdg; +struct rdp_rdg +{ + rdpContext* context; /* Shortcut to parent context. */ + BIO* bioIn; + BIO* bioOut; + BIO* frontBio; + rdpTls* tlsIn; + rdpTls* tlsOut; + rdpNtlm* ntlm; + HttpContext* http; + HANDLE readEvent; + + UUID guid; + + //UINT32 tunnelId; + //UINT32 negotiatedCapsFlags; + //UUID nonce; + //LPWSTR serverCert; + //LPWSTR consentMsg; + + int state; + int packetRemainingCount; + int nonBlocking; + int timeout; +}; + + +rdpRdg* rdg_new(rdpTransport* transport); +void rdg_free(rdpRdg* rdg); + +BOOL rdg_connect(rdpRdg* rdg, const char* hostname, UINT16 port, int timeout); +UINT32 rdg_get_event_handles(rdpRdg* rdg, HANDLE* events); +BOOL rdg_check_event_handles(rdpRdg* rdg); + + +#endif /* FREERDP_CORE_RDG_H */ diff --git a/libfreerdp/core/transport.c b/libfreerdp/core/transport.c index 696fedeed..474c498f8 100644 --- a/libfreerdp/core/transport.c +++ b/libfreerdp/core/transport.c @@ -207,17 +207,32 @@ BOOL transport_connect(rdpTransport* transport, const char* hostname, UINT16 por if (transport->GatewayEnabled) { - transport->tsg = tsg_new(transport); - - if (!transport->tsg) + /* New RDP 8 gateway test. */ + transport->rdg = rdg_new(transport); + if (!transport->rdg) + { return FALSE; - - if (!tsg_connect(transport->tsg, hostname, port, timeout)) + } + status = rdg_connect(transport->rdg, hostname, port, timeout); + if (!status) + { return FALSE; - - transport->frontBio = transport->tsg->bio; + } + transport->frontBio = transport->rdg->frontBio; + BIO_set_nonblock(transport->frontBio, 0); transport->layer = TRANSPORT_LAYER_TSG; + //transport->tsg = tsg_new(transport); + + //if (!transport->tsg) + // return FALSE; + + //if (!tsg_connect(transport->tsg, hostname, port, timeout)) + // return FALSE; + + //transport->frontBio = transport->tsg->bio; + //transport->layer = TRANSPORT_LAYER_TSG; + status = TRUE; } else @@ -621,7 +636,14 @@ UINT32 transport_get_event_handles(rdpTransport* transport, HANDLE* events) } else { - nCount += tsg_get_event_handles(transport->tsg, events); + if (transport->rdg) + { + nCount += rdg_get_event_handles(transport->rdg, events); + } + else if (transport->tsg) + { + nCount += tsg_get_event_handles(transport->tsg, events); + } } return nCount; diff --git a/libfreerdp/core/transport.h b/libfreerdp/core/transport.h index d9aa126a2..ad6e3f402 100644 --- a/libfreerdp/core/transport.h +++ b/libfreerdp/core/transport.h @@ -35,6 +35,7 @@ typedef struct rdp_transport rdpTransport; #include "nla.h" #include "gateway/tsg.h" +#include "gateway/rdg.h" #include #include @@ -56,6 +57,7 @@ struct rdp_transport { TRANSPORT_LAYER layer; BIO* frontBio; + rdpRdg* rdg; rdpTsg* tsg; rdpTls* tls; rdpContext* context;