4.1.1 Normal Scenario
The RDG client obtains the name of an RDG server by using an out-of-band mechanism. The RDG client establishes a binding handle (a binding handle is specified in [C706] section 2.1) to the RDG server at the well-known endpoint of 443 and 3388.
The RDG server performs the authentication steps specified in section 2.1.
The RDG client then calls the TsProxyCreateTunnel method to create and obtain the tunnel (2) context handle. As part of this call, the client sends current version capabilities to the server.
The RDG server receives the TsProxyCreateTunnel method. The RDG server authenticates the RDG client and uses policies to determine if the RDG client is allowed access to create a tunnel (2). The RDG server then creates a context handle to represent the tunnel (2) and returns this to the RDG client. The server response includes the common capabilities of both the client and the server.
The RDG client makes the TsProxyAuthorizeTunnel method call using the tunnel (2) context handle, optionally passing its health statement.
The RDG server receives TsProxyAuthorizeTunnel method call and verifies the tunnel (2) context handle. The RDG server also performs RPC's verification and uses NAP policies to determine if the client is healthy. Assuming the RDG client is healthy, the RDG server returns success.
If both the client and the server are capable of handling administrative messages, the client can request administrative messages using the TsProxyMakeTunnelCall method. This call is queued up on the server and is completed only when the messages are available.
The RDG client makes the TsProxyCreateChannel method call using the tunnel (2) context handle. The RDG client passes the target server information to the RDG server and obtains the channel context handle from the RDG server.
The RDG server receives the TsProxyCreateChannel method and determines, based on the NAP policy, if the RDG client is allowed to connect to the target server. If the connection is allowed, the RDG server creates a context handle to represent the channel and returns this to the RDG client.
The RDG client makes the TsProxySetupReceivePipe method call.
The RDG server receives the TsProxySetupReceivePipe method and creates an RPC out pipe. The RDG server can now send data on the pipe.
The RDG client and RDG server start sending and receiving data from this point.
The RDG client makes the TsProxyCloseChannel method call to close the channel.
The RDG server receives the TsProxyCloseChannel method and correctly closes the channel.
The RDG client then makes the TsProxyCloseTunnel method call to end the connection.
The RDG server receives the TsProxyCloseTunnel method and destroys the client connection.
For example, the client calls the TsProxyCreateTunnel method on a server named "fourthcoffee.example.com".
Example for the TsProxyCreateTunnel method:
-
HRESULT = {to be filled in by server} TsProxyCreateTunnel( [in, ref] PTSG_PACKET TSGPacket; [out, ref] PTSG_PACKET* TSGPacketResponse = {to be filled in by server}; [out] PTUNNEL_CONTEXT_HANDLE_SERIALIZE* tunnelContext = {to be filled in by server, and saved as m_tunnelcontext by client}; [out] unsigned long* tunnelid = {to be filled in by server and saved as m_tunnelid by client}; );
Where TSG_PACKET is set as follows.
-
typedef struct _TSG_PACKET { unsigned long packetId = TSG_PACKET_TYPE_VERSIONCAPS; TSG_PACKET_TYPE_UNION TSGPacket {= packetVersionCaps}; } TSG_PACKET;
Where TSG_PACKET_VERSIONCAPS is filled in as follows.
-
typedef struct _TSG_PACKET_VERSIONCAPS { TSG_PACKET_HEADER TSGHeader { ComponentId = 0x5452; PacketId = {unused}; } PTSG_PACKET_CAPABILITIES TSGCaps { capabilityType = 1; TSGPacket.tsgCapNap = {1}; } unsigned long numCapabilities = 1; unsigned short majorVersion = 1; unsigned short minorVersion = 1; unsigned short quarantineCapabilities = 0; } TSG_PACKET_VERSIONCAPS;
The RDG server receives this method and returns the following.
-
HRESULT = S_OK TsProxyCreateTunnel( [in, ref] PTSG_PACKET TSGPacket = {unchanged}; [out, ref] PTSG_PACKET* TSGPacketResponse = = {filled in as shown below}; [out] PTUNNEL_CONTEXT_HANDLE_SERIALIZE* tunnelContext = pContextHandleObject; [out] unsigned long* tunnelId = 1; );
Where TSG_PACKET_RESPONSE is set as follows.
-
typedef struct _TSG_PACKET { unsigned long packetId = TSG_PACKET_TYPE_QUARENC_RESPONSE; TSG_PACKET_TYPE_UNION TSGPacket {= packetQuarEncResponse}; } TSG_PACKET;
Where the TSG_PACKET_QUARENC_RESPONSE is set as follows.
-
typedef struct _TSG_PACKET_QUARENC_RESPONSE { unsigned long flags = 0; unsigned long certChainLen = {number of characters in certChainData}; wchar_t* certChainData = {certificate chain data}; GUID nonce = CreateGuid(); PTSG_PACKET_VERSIONCAPS versionCaps { TSG_PACKET_HEADER TSGHeader { ComponentId = 0x5452; PacketId = TSG_PACKET_TYPE_VERSIONCAPS; } PTSG_PACKET_CAPABILITIES TSGCaps { capabilityType = 1; TSGPacket.tsgCapNap = {1}; } unsigned long numCapabilities = 1; unsigned short majorVersion = 1; unsigned short minorVersion = 1; unsigned short quarantineCapabilities = 0; } } TSG_PACKET_QUARENC_RESPONSE;
Example for TsProxyAuthorizeTunnel method.
-
HRESULT = {to be filled in by server} TsProxyAuthorizeTunnel( [in] PTUNNEL_CONTEXT_HANDLE_NOSERIALIZE tunnelContext = m_tunnelcontext; [in, ref] PTSG_PACKET TSGPacket; [out, ref] PTSG_PACKET* TSGPacketResponse = { to be filled in by server}; );
Where TSG_PACKET is set as follows.
-
typedef struct _TSG_PACKET { unsigned long packetId = TSG_PACKET_TYPE_QUARREQUEST; TSG_PACKET_TYPE_UNION TSGPacket {=PTSG_PACKET_QUARREQUEST packetQuarRequest}; } TSG_PACKET;
Where the TSG_PACKET_QUARREQUEST is set as follows.
-
typedef struct _TSG_PACKET_QUARREQUEST { unsigned long flags = 0; wchar_t* machineName = "mymachine"; unsigned long nameLength = 10; byte *data = {statement of health prefixed with Nonce, which is received in response to TsProxyCreateTunnel}; unsigned long dataLen = {Number of bytes in the data field}; } TSG_PACKET_QUARREQUEST;
The RDG server receives this method and returns the following.
-
HRESULT = S_OK TsProxyAuthorizeTunnel( [in] PTUNNEL_CONTEXT_HANDLE_NOSERIALIZE tunnelContext = unchanged; [in, ref] PTSG_PACKET TSGPacket = unchanged; [out, ref] PTSG_PACKET* TSGPacketResponse= filled in as below; );
Where the TSG_PACKET_RESPONSE is set as follows.
-
typedef struct _TSG_PACKET { unsigned long packetId = TSG_PACKET_TYPE_RESPONSE; TSG_PACKET_TYPE_UNION TSGPacket {=PTSG_PACKET_RESPONSE packetResponse}; } TSG_PACKET;
Where the packetResponse is set as follows.
-
typedef struct _TSG_PACKET_RESPONSE { unsigned long flags = TSG_PACKET_TYPE_QUARREQUEST; unsigned long reserved = 0; byte *responseData = NULL; unsigned long responseDataLen = 0; TSG_REDIRECTION_FLAGS redirectionFlags = {0,0,0,0,0,0,0,0}; } TSG_PACKET_RESPONSE;
Example for the TsProxyMakeTunnelCall method.
-
HRESULT = {to be filled in by server} TsProxyMakeTunnelCall( [in] PTUNNEL_CONTEXT_HANDLE_NOSERIALIZE tunnelContext = m_tunnelcontext; [in] unsigned long procId, [in, ref] PTSG_PACKET TSGPacket, [out, ref] PTSG_PACKET* TSGPacketResponse = { to be filled in by server } );
Where the procId and TSGPacket are set as follows.
-
procId = TSG_TUNNEL_CALL_ASYNC_MSG_REQUEST = 0x1 typedef struct _TSG_PACKET { unsigned long packetId = TSG_PACKET_TYPE_MSGREQUEST_PACKET; TSG_PACKET_TYPE_UNION TSGPacket {=PTSG_PACKET_MSG_REQUEST packetMsgRequest}; } TSG_PACKET;
Where the TSG_PACKET_MSG_REQUEST is set as follows.
-
typedef struct _TSG_PACKET_MSG_REQUEST { unsigned long maxMessagesPerBatch = 1; } TSG_PACKET_MSG_REQUEST;
The RDG server receives this method and returns:
-
HRESULT = S_OK TsProxyMakeTunnelCall( [in] PTUNNEL_CONTEXT_HANDLE_NOSERIALIZE tunnelContext = unchanged; [in] unsigned long procId = unchanged, [in, ref] PTSG_PACKET TSGPacket = unchanged, [out, ref] PTSG_PACKET* TSGPacketResponse = { filled in as below } );
Where the TSG_PACKET_RESPONSE is set as follows.
-
typedef struct _TSG_PACKET { unsigned long packetId = TSG_PACKET_TYPE_MESSAGE_PACKET; TSG_PACKET_TYPE_UNION TSGPacket {=PTSG_PACKET_MSG_RESPONSE packetMsgResponse}; } TSG_PACKET;
Where the packetMsgResponse is set as follows.
-
typedef struct _TSG_PACKET_MSG_RESPONSE { unsigned long msgID = 1; unsigned long msgType = TSG_ASYNC_MESSAGE_SERVICE_MESSAGE = 2; long isMsgPresent = 1; [switch_is(msgType)] TSG_PACKET_TYPE_MESSAGE_UNION messagePacket; } TSG_PACKET_MSG_RESPONSE;
Where the messagePacket is set as follows.
-
typedef [switch_type(unsigned long)] union { [case (TSG_ASYNC_MESSAGE_CONSENT_MESSAGE)] PTSG_PACKET_STRING_MESSAGE consentMessage; [case (TSG_ASYNC_MESSAGE_SERVICE_MESSAGE)] PTSG_PACKET_STRING_MESSAGE serviceMessage; [case (TSG_ASYNC_MESSAGE_REAUTH)] PTSG_PACKET_REAUTH_MESSAGE reauthMessage; } TSG_PACKET_TYPE_MESSAGE_UNION;
Where the servicemessage is set as follows.
-
typedef struct _TSG_PACKET_STRING_MESSAGE { long isDisplayMandatory = 1; long isConsentMandatory = 1; [range(0, 65536)] unsigned long msgBytes = 4; [size_is(msgBytes)] wchar_t* msgBuffer = "Test"; } TSG_PACKET_STRING_MESSAGE;
Example for the TsProxyCreateChannel method.
-
HRESULT = {to be filled in by server} TsProxyCreateChannel( [in] PTUNNEL_CONTEXT_HANDLE_NOSERIALIZE tunnelContext = m_tunnelcontext; [in, ref] PTSENDPOINTINFO tsEndPointInfo; [out] PCHANNEL_CONTEXT_HANDLE_SERIALIZE* channelContext = { to be filled in by server}; [out] unsigned long* channelId = { to be filled in by server}; );
Where the tsEndPointInfo is set as follows.
-
typedef struct _tsendpointinfo { RESOURCENAME *resourceNames = "myTsMachine"; unsigned long numResourceNames = 1; RESOURCENAME *alternateResourceNames = NULL; unsigned short numAlternateResourceNames = 0; unsigned long Port = 222101507; }TSENDPOINTINFO;
The RDG server receives this method and returns:
-
HRESULT = S_OK TsProxyCreateChannel( [in] PTUNNEL_CONTEXT_HANDLE_NOSERIALIZE tunnelContext = unchanged; [in, ref] PTSENDPOINTINFO tsEndPointInfo = unchanged; [out] PCHANNEL_CONTEXT_HANDLE_SERIALIZE* channelContext = pServerChannelContextHandle; [out] unsigned long* channelId = 1; );
Example for the TsProxySendToServer method.
-
DWORD = {to be filled in by server} TsProxySendToServer( [in] TSG_SEND_MESSAGE_TSGSendMessage; );
Where the Generic Send Data Message Packet is as follows.
-
m_channelContextHandle = {00 00 00 00 36 41 18 41 dd 2d 84 43 83 63 82 cc b6 ea f3 f9 }; typedef struct _TSG_SEND_MESSAGE { PTUNNEL_CONTEXT_HANDLE_NOSERIALIZE m_channelContextHandle; //as above DWORD totalDataLength = 0x00000008; //buffer1Length+sizeof(buffer1Length) DWORD numBuffers = 0x00000001; //number of buffers that follow is 1 DWORD buffer1Length=0x04; //length of data that follows is 4 bytes PBYTE buffer1 = {04,00,00,03}; //data of 4 bytes } TSG_SEND_MESSAGE;
The RDG server receives this method, verifies m_channelContextHandle, and sends the buffer1Length of buffer1 to the target server and returns the following.
-
DWORD = ERROR_SUCCESS TsProxySendToServer ( [in] TSG_SEND_MESSAGE_TSGSendMessage = unchanged; );
Example for the TsProxySetupReceivePipe method.
-
DWORD = {to be filled in by server} TsProxySetupReceivePipe ( [in, max_is(32767)] byte pRpcMessage[] );
Where an example value of pRpcMessage is as follows.
-
{ 00 00 00 00 EC EC 2E 7D DB E2 E3 4A AE 61 A3 51 DC 53 55 61 }
The RDG server receives this method, sets up the out pipe, streams all necessary data to the RDG client in RPC response PDUs without setting the PFC_LAST_FRAG bit in the pfc_flags field, and when the RDG client calls TsProxyCloseChannel or calls TsProxyCloseTunnel without calling TsProxyCloseChannel, it returns the following return code in an RPC response PDU with PFC_LAST_FRAG bit set in the pfc_flags field.
-
DWORD = ERROR_GRACEFUL_DISCONNECT TsProxySetupReceivePipe ( [in, max_is(32767)] byte pRpcMessage[] = unchanged );
Example for the TsProxyCloseChannel method.
-
HRESULT = {to be filled in by server} TsProxyCloseChannel ( [in, out] PCHANNEL_CONTEXT_HANDLE_NOSERIALIZE* context = m_channelContext; );
The RDG server receives this method and returns:
-
HRESULT = S_OK TsProxyCloseChannel( [in, out] PCHANNEL_CONTEXT_HANDLE_NOSERIALIZE* context = NULL; );
Example for the TsProxyCloseTunnel method.
-
HRESULT = {to be filled in by server} TsProxyCloseTunnel ( [in, out] PTUNNEL_CONTEXT_HANDLE_SERIALIZE* context = m_tunnelContext; );
The RDG server receives this method and returns:
-
HRESULT = S_OK TsProxyCloseTunnel( [in, out] PTUNNEL_CONTEXT_HANDLE_SERIALIZE* context = NULL; );