FreeRDP
multitransport.c
1 
20 #include <winpr/assert.h>
21 #include <freerdp/config.h>
22 #include <freerdp/log.h>
23 
24 #include "settings.h"
25 #include "rdp.h"
26 #include "multitransport.h"
27 
28 struct rdp_multitransport
29 {
30  rdpRdp* rdp;
31 
32  MultiTransportRequestCb MtRequest;
33  MultiTransportResponseCb MtResponse;
34 
35  /* server-side data */
36  UINT32 reliableReqId;
37 
38  BYTE reliableCookie[RDPUDP_COOKIE_LEN];
39  BYTE reliableCookieHash[RDPUDP_COOKIE_HASHLEN];
40 };
41 
42 enum
43 {
44  RDPTUNNEL_ACTION_CREATEREQUEST = 0x00,
45  RDPTUNNEL_ACTION_CREATERESPONSE = 0x01,
46  RDPTUNNEL_ACTION_DATA = 0x02
47 };
48 
49 #define TAG FREERDP_TAG("core.multitransport")
50 
51 state_run_t multitransport_recv_request(rdpMultitransport* multi, wStream* s)
52 {
53  WINPR_ASSERT(multi);
54  rdpSettings* settings = multi->rdp->settings;
55 
56  if (settings->ServerMode)
57  {
58  WLog_ERR(TAG, "not expecting a multi-transport request in server mode");
59  return STATE_RUN_FAILED;
60  }
61 
62  if (!Stream_CheckAndLogRequiredLength(TAG, s, 24))
63  return STATE_RUN_FAILED;
64 
65  UINT32 requestId = 0;
66  UINT16 requestedProto = 0;
67  UINT16 reserved = 0;
68  const BYTE* cookie = NULL;
69 
70  Stream_Read_UINT32(s, requestId); /* requestId (4 bytes) */
71  Stream_Read_UINT16(s, requestedProto); /* requestedProtocol (2 bytes) */
72  Stream_Read_UINT16(s, reserved); /* reserved (2 bytes) */
73  cookie = Stream_ConstPointer(s);
74  Stream_Seek(s, RDPUDP_COOKIE_LEN); /* securityCookie (16 bytes) */
75  if (reserved != 0)
76  {
77  /*
78  * If the reserved filed is not 0 the request PDU seems to contain some extra data.
79  * If the reserved value is 1, then two bytes of 0 (probably a version field)
80  * are followed by a JSON payload (not null terminated, until the end of the packet.
81  * There seems to be no dedicated length field)
82  *
83  * for now just ignore all that
84  */
85  WLog_WARN(TAG,
86  "reserved is %" PRIu16 " instead of 0, skipping %" PRIuz "bytes of unknown data",
87  reserved, Stream_GetRemainingLength(s));
88  (void)Stream_SafeSeek(s, Stream_GetRemainingLength(s));
89  }
90 
91  WINPR_ASSERT(multi->MtRequest);
92  return multi->MtRequest(multi, requestId, requestedProto, cookie);
93 }
94 
95 static BOOL multitransport_request_send(rdpMultitransport* multi, UINT32 reqId, UINT16 reqProto,
96  const BYTE* cookie)
97 {
98  WINPR_ASSERT(multi);
99  wStream* s = rdp_message_channel_pdu_init(multi->rdp);
100  if (!s)
101  return FALSE;
102 
103  if (!Stream_EnsureRemainingCapacity(s, 24))
104  {
105  Stream_Release(s);
106  return FALSE;
107  }
108 
109  Stream_Write_UINT32(s, reqId); /* requestId (4 bytes) */
110  Stream_Write_UINT16(s, reqProto); /* requestedProtocol (2 bytes) */
111  Stream_Zero(s, 2); /* reserved (2 bytes) */
112  Stream_Write(s, cookie, RDPUDP_COOKIE_LEN); /* securityCookie (16 bytes) */
113 
114  return rdp_send_message_channel_pdu(multi->rdp, s, SEC_TRANSPORT_REQ);
115 }
116 
117 state_run_t multitransport_server_request(rdpMultitransport* multi, UINT16 reqProto)
118 {
119  WINPR_ASSERT(multi);
120 
121  /* TODO: move this static variable to the listener */
122  static UINT32 reqId = 0;
123 
124  if (reqProto == INITIATE_REQUEST_PROTOCOL_UDPFECR)
125  {
126  multi->reliableReqId = reqId++;
127  winpr_RAND(multi->reliableCookie, sizeof(multi->reliableCookie));
128 
129  return multitransport_request_send(multi, multi->reliableReqId, reqProto,
130  multi->reliableCookie)
131  ? STATE_RUN_SUCCESS
132  : STATE_RUN_FAILED;
133  }
134 
135  WLog_ERR(TAG, "only reliable transport is supported");
136  return STATE_RUN_CONTINUE;
137 }
138 
139 BOOL multitransport_client_send_response(rdpMultitransport* multi, UINT32 reqId, HRESULT hr)
140 {
141  WINPR_ASSERT(multi);
142 
143  wStream* s = rdp_message_channel_pdu_init(multi->rdp);
144  if (!s)
145  return FALSE;
146 
147  if (!Stream_EnsureRemainingCapacity(s, 28))
148  {
149  Stream_Release(s);
150  return FALSE;
151  }
152 
153  Stream_Write_UINT32(s, reqId); /* requestId (4 bytes) */
154 
155  /* [MS-RDPBCGR] 2.2.15.2 Client Initiate Multitransport Response PDU defines this as 4byte
156  * UNSIGNED but https://learn.microsoft.com/en-us/windows/win32/learnwin32/error-codes-in-com
157  * defines this as signed... assume the spec is (implicitly) assuming twos complement. */
158  Stream_Write_INT32(s, hr); /* HResult (4 bytes) */
159  return rdp_send_message_channel_pdu(multi->rdp, s, SEC_TRANSPORT_RSP);
160 }
161 
162 state_run_t multitransport_recv_response(rdpMultitransport* multi, wStream* s)
163 {
164  WINPR_ASSERT(multi && multi->rdp);
165  WINPR_ASSERT(s);
166 
167  rdpSettings* settings = multi->rdp->settings;
168  WINPR_ASSERT(settings);
169 
170  if (!settings->ServerMode)
171  {
172  WLog_ERR(TAG, "client is not expecting a multi-transport resp packet");
173  return STATE_RUN_FAILED;
174  }
175 
176  if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
177  return STATE_RUN_FAILED;
178 
179  UINT32 requestId = 0;
180  HRESULT hr = 0;
181 
182  Stream_Read_UINT32(s, requestId); /* requestId (4 bytes) */
183  Stream_Read_UINT32(s, hr); /* hrResponse (4 bytes) */
184 
185  return IFCALLRESULT(STATE_RUN_SUCCESS, multi->MtResponse, multi, requestId, hr);
186 }
187 
188 static state_run_t multitransport_no_udp(rdpMultitransport* multi, UINT32 reqId, UINT16 reqProto,
189  const BYTE* cookie)
190 {
191  return multitransport_client_send_response(multi, reqId, E_ABORT) ? STATE_RUN_SUCCESS
192  : STATE_RUN_FAILED;
193 }
194 
195 static state_run_t multitransport_server_handle_response(rdpMultitransport* multi, UINT32 reqId,
196  UINT32 hrResponse)
197 {
198  rdpRdp* rdp = multi->rdp;
199 
200  if (!rdp_server_transition_to_state(rdp, CONNECTION_STATE_CAPABILITIES_EXCHANGE_DEMAND_ACTIVE))
201  return STATE_RUN_FAILED;
202 
203  return STATE_RUN_CONTINUE;
204 }
205 
206 rdpMultitransport* multitransport_new(rdpRdp* rdp, UINT16 protocol)
207 {
208  WINPR_ASSERT(rdp);
209 
210  rdpSettings* settings = rdp->settings;
211  WINPR_ASSERT(settings);
212 
213  rdpMultitransport* multi = calloc(1, sizeof(rdpMultitransport));
214  if (!multi)
215  return NULL;
216 
217  if (settings->ServerMode)
218  {
219  multi->MtResponse = multitransport_server_handle_response;
220  }
221  else
222  {
223  multi->MtRequest = multitransport_no_udp;
224  }
225 
226  multi->rdp = rdp;
227  return multi;
228 }
229 
230 void multitransport_free(rdpMultitransport* multitransport)
231 {
232  free(multitransport);
233 }