libfreerdp-core: start event-driven gateway http code
authorMarc-André Moreau <marcandre.moreau@gmail.com>
Tue, 3 Feb 2015 22:17:17 +0000 (17:17 -0500)
committerMarc-André Moreau <marcandre.moreau@gmail.com>
Tue, 3 Feb 2015 22:17:17 +0000 (17:17 -0500)
libfreerdp/core/gateway/http.h
libfreerdp/core/gateway/rpc_client.c
libfreerdp/core/gateway/rpc_client.h
libfreerdp/core/gateway/tsg.c

index d183279..2966eda 100644 (file)
@@ -28,6 +28,7 @@ typedef struct _http_response HttpResponse;
 #include <freerdp/crypto/tls.h>
 
 #include <winpr/stream.h>
+#include <winpr/winhttp.h>
 
 struct _http_context
 {
index a2ece2b..08f99d2 100644 (file)
@@ -29,6 +29,9 @@
 #include <winpr/thread.h>
 #include <winpr/stream.h>
 
+#include "http.h"
+#include "ncacn_http.h"
+
 #include "rpc_bind.h"
 #include "rpc_fault.h"
 #include "rpc_client.h"
@@ -269,7 +272,7 @@ int rpc_client_recv_pdu(rdpRpc* rpc, RPC_PDU* pdu)
                                return -1;
                        }
 
-                       if (rpc_send_rpc_auth_3_pdu(rpc) <= 0)
+                       if (rpc_send_rpc_auth_3_pdu(rpc) < 0)
                        {
                                WLog_ERR(TAG, "rpc_secure_bind: error sending rpc_auth_3 pdu!");
                                return -1;
@@ -525,6 +528,159 @@ int rpc_client_recv(rdpRpc* rpc)
        return 1;
 }
 
+int rpc_client_out_channel_recv(rdpRpc* rpc)
+{
+       int status = -1;
+       HttpResponse* response;
+       RpcOutChannel* outChannel;
+
+       outChannel = rpc->VirtualConnection->DefaultOutChannel;
+
+       if (outChannel->State < CLIENT_IN_CHANNEL_STATE_OPENED)
+       {
+               response = http_response_recv(rpc->TlsOut);
+
+               if (!response)
+                       return -1;
+
+               if (outChannel->State == CLIENT_OUT_CHANNEL_STATE_SECURITY)
+               {
+                       /* Receive OUT Channel Response */
+
+                       if (rpc_ncacn_http_recv_out_channel_response(rpc, response) < 0)
+                       {
+                               WLog_ERR(TAG, "rpc_ncacn_http_recv_out_channel_response failure");
+                               return -1;
+                       }
+
+                       /* Send OUT Channel Request */
+
+                       if (rpc_ncacn_http_send_out_channel_request(rpc) < 0)
+                       {
+                               WLog_ERR(TAG, "rpc_ncacn_http_send_out_channel_request failure");
+                               return -1;
+                       }
+
+                       rpc_ncacn_http_ntlm_uninit(rpc, TSG_CHANNEL_OUT);
+
+                       rpc_client_out_channel_transition_to_state(outChannel,
+                                       CLIENT_OUT_CHANNEL_STATE_NEGOTIATED);
+
+                       /* Send CONN/A1 PDU over OUT channel */
+
+                       if (rts_send_CONN_A1_pdu(rpc) < 0)
+                       {
+                               WLog_ERR(TAG, "rpc_send_CONN_A1_pdu error!");
+                               return -1;
+                       }
+
+                       rpc_client_out_channel_transition_to_state(outChannel,
+                                       CLIENT_OUT_CHANNEL_STATE_OPENED);
+               }
+
+               http_response_free(response);
+       }
+       else if (rpc->VirtualConnection->State == VIRTUAL_CONNECTION_STATE_OUT_CHANNEL_WAIT)
+       {
+               /* Receive OUT channel response */
+
+               response = http_response_recv(rpc->TlsOut);
+
+               if (!response)
+                       return -1;
+
+               if (response->StatusCode != HTTP_STATUS_OK)
+               {
+                       WLog_ERR(TAG, "error! Status Code: %d", response->StatusCode);
+                       http_response_print(response);
+                       http_response_free(response);
+
+                       if (response->StatusCode == HTTP_STATUS_DENIED)
+                       {
+                               if (!connectErrorCode)
+                                       connectErrorCode = AUTHENTICATIONERROR;
+
+                               if (!freerdp_get_last_error(rpc->context))
+                               {
+                                       freerdp_set_last_error(rpc->context, FREERDP_ERROR_AUTHENTICATION_FAILED);
+                               }
+                       }
+
+                       return -1;
+               }
+
+               http_response_free(response);
+
+               rpc_client_virtual_connection_transition_to_state(rpc,
+                               rpc->VirtualConnection, VIRTUAL_CONNECTION_STATE_WAIT_A3W);
+       }
+       else
+       {
+               status = rpc_client_recv(rpc);
+       }
+
+       return status;
+}
+
+int rpc_client_in_channel_recv(rdpRpc* rpc)
+{
+       int status = -1;
+       HttpResponse* response;
+       RpcInChannel* inChannel;
+       HANDLE InChannelEvent = NULL;
+
+       inChannel = rpc->VirtualConnection->DefaultInChannel;
+       BIO_get_event(rpc->TlsIn->bio, &InChannelEvent);
+
+       if (WaitForSingleObject(InChannelEvent, 0) != WAIT_OBJECT_0)
+               return 1;
+
+       if (inChannel->State < CLIENT_IN_CHANNEL_STATE_OPENED)
+       {
+               response = http_response_recv(rpc->TlsIn);
+
+               if (!response)
+                       return -1;
+
+               if (inChannel->State == CLIENT_IN_CHANNEL_STATE_SECURITY)
+               {
+                       if (rpc_ncacn_http_recv_in_channel_response(rpc, response) < 0)
+                       {
+                               WLog_ERR(TAG, "rpc_ncacn_http_recv_in_channel_response failure");
+                               return -1;
+                       }
+
+                       /* Send IN Channel Request */
+
+                       if (rpc_ncacn_http_send_in_channel_request(rpc) < 0)
+                       {
+                               WLog_ERR(TAG, "rpc_ncacn_http_send_in_channel_request failure");
+                               return -1;
+                       }
+
+                       rpc_ncacn_http_ntlm_uninit(rpc, TSG_CHANNEL_IN);
+
+                       rpc_client_in_channel_transition_to_state(inChannel,
+                                       CLIENT_IN_CHANNEL_STATE_NEGOTIATED);
+
+                       /* Send CONN/B1 PDU over IN channel */
+
+                       if (rts_send_CONN_B1_pdu(rpc) < 0)
+                       {
+                               WLog_ERR(TAG, "rpc_send_CONN_B1_pdu error!");
+                               return -1;
+                       }
+
+                       rpc_client_in_channel_transition_to_state(inChannel,
+                                       CLIENT_IN_CHANNEL_STATE_OPENED);
+
+                       status = 1;
+               }
+       }
+
+       return status;
+}
+
 /**
  * [MS-RPCE] Client Call:
  * http://msdn.microsoft.com/en-us/library/gg593159/
index ddfcdce..ec9760c 100644 (file)
@@ -30,6 +30,9 @@ void rpc_client_call_free(RpcClientCall* client_call);
 int rpc_send_pdu(rdpRpc* rpc, BYTE* buffer, UINT32 length);
 int rpc_client_recv(rdpRpc* rpc);
 
+int rpc_client_in_channel_recv(rdpRpc* rpc);
+int rpc_client_out_channel_recv(rdpRpc* rpc);
+
 int rpc_client_receive_pipe_read(rdpRpc* rpc, BYTE* buffer, size_t length);
 
 int rpc_client_new(rdpRpc* rpc);
index f21415d..db4714d 100644 (file)
@@ -1204,7 +1204,7 @@ int tsg_transition_to_state(rdpTsg* tsg, TSG_STATE state)
 {
        const char* str = "TSG_STATE_UNKNOWN";
 
-       switch (tsg->state)
+       switch (state)
        {
                case TSG_STATE_INITIAL:
                        str = "TSG_STATE_INITIAL";
@@ -1397,14 +1397,22 @@ int tsg_check(rdpTsg* tsg)
 {
        int status;
 
-       status = rpc_client_recv(tsg->rpc);
+       status = rpc_client_in_channel_recv(tsg->rpc);
+
+       if (status < 0)
+               return -1;
+
+       status = rpc_client_out_channel_recv(tsg->rpc);
+
+       if (status < 0)
+               return -1;
 
        return status;
 }
 
 BOOL tsg_connect(rdpTsg* tsg, const char* hostname, UINT16 port)
 {
-       HANDLE ReadEvent;
+       HANDLE events[2];
        rdpRpc* rpc = tsg->rpc;
        rdpSettings* settings = rpc->settings;
 
@@ -1427,19 +1435,18 @@ BOOL tsg_connect(rdpTsg* tsg, const char* hostname, UINT16 port)
                return FALSE;
        }
 
-       ReadEvent = NULL;
-       BIO_get_event(rpc->TlsOut->bio, &ReadEvent);
+       BIO_get_event(rpc->TlsIn->bio, &events[0]);
+       BIO_get_event(rpc->TlsOut->bio, &events[1]);
 
        while (tsg->state != TSG_STATE_PIPE_CREATED)
        {
-               if (WaitForSingleObject(ReadEvent, 100) == WAIT_OBJECT_0)
+               WaitForMultipleObjects(2, events, FALSE, 100);
+
+               if (tsg_check(tsg) < 0)
                {
-                       if (tsg_check(tsg) < 0)
-                       {
-                               WLog_ERR(TAG, "tsg_check failure");
-                               rpc->transport->layer = TRANSPORT_LAYER_CLOSED;
-                               return FALSE;
-                       }
+                       WLog_ERR(TAG, "tsg_check failure");
+                       rpc->transport->layer = TRANSPORT_LAYER_CLOSED;
+                       return FALSE;
                }
        }