FreeRDP
Loading...
Searching...
No Matches
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
28struct 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
42enum
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
51state_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 = nullptr;
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
95static BOOL multitransport_request_send(rdpMultitransport* multi, UINT32 reqId, UINT16 reqProto,
96 const BYTE* cookie)
97{
98 WINPR_ASSERT(multi);
99 UINT16 sec_flags = 0;
100 wStream* s = rdp_message_channel_pdu_init(multi->rdp, &sec_flags);
101 if (!s)
102 return FALSE;
103
104 if (!Stream_EnsureRemainingCapacity(s, 24))
105 {
106 Stream_Release(s);
107 return FALSE;
108 }
109
110 Stream_Write_UINT32(s, reqId); /* requestId (4 bytes) */
111 Stream_Write_UINT16(s, reqProto); /* requestedProtocol (2 bytes) */
112 Stream_Zero(s, 2); /* reserved (2 bytes) */
113 Stream_Write(s, cookie, RDPUDP_COOKIE_LEN); /* securityCookie (16 bytes) */
114
115 return rdp_send_message_channel_pdu(multi->rdp, s, sec_flags | SEC_TRANSPORT_REQ);
116}
117
118state_run_t multitransport_server_request(rdpMultitransport* multi, UINT16 reqProto)
119{
120 WINPR_ASSERT(multi);
121
122 /* TODO: move this static variable to the listener */
123 static UINT32 reqId = 0;
124
125 if (reqProto == INITIATE_REQUEST_PROTOCOL_UDPFECR)
126 {
127 multi->reliableReqId = reqId++;
128 if (winpr_RAND(multi->reliableCookie, sizeof(multi->reliableCookie)) < 0)
129 return STATE_RUN_FAILED;
130
131 return multitransport_request_send(multi, multi->reliableReqId, reqProto,
132 multi->reliableCookie)
133 ? STATE_RUN_SUCCESS
134 : STATE_RUN_FAILED;
135 }
136
137 WLog_ERR(TAG, "only reliable transport is supported");
138 return STATE_RUN_CONTINUE;
139}
140
141BOOL multitransport_client_send_response(rdpMultitransport* multi, UINT32 reqId, HRESULT hr)
142{
143 WINPR_ASSERT(multi);
144
145 UINT16 sec_flags = 0;
146 wStream* s = rdp_message_channel_pdu_init(multi->rdp, &sec_flags);
147 if (!s)
148 return FALSE;
149
150 if (!Stream_EnsureRemainingCapacity(s, 28))
151 {
152 Stream_Release(s);
153 return FALSE;
154 }
155
156 Stream_Write_UINT32(s, reqId); /* requestId (4 bytes) */
157
158 /* [MS-RDPBCGR] 2.2.15.2 Client Initiate Multitransport Response PDU defines this as 4byte
159 * UNSIGNED but https://learn.microsoft.com/en-us/windows/win32/learnwin32/error-codes-in-com
160 * defines this as signed... assume the spec is (implicitly) assuming twos complement. */
161 Stream_Write_INT32(s, hr); /* HResult (4 bytes) */
162 return rdp_send_message_channel_pdu(multi->rdp, s, sec_flags | SEC_TRANSPORT_RSP);
163}
164
165state_run_t multitransport_recv_response(rdpMultitransport* multi, wStream* s)
166{
167 WINPR_ASSERT(multi && multi->rdp);
168 WINPR_ASSERT(s);
169
170 rdpSettings* settings = multi->rdp->settings;
171 WINPR_ASSERT(settings);
172
173 if (!settings->ServerMode)
174 {
175 WLog_ERR(TAG, "client is not expecting a multi-transport resp packet");
176 return STATE_RUN_FAILED;
177 }
178
179 if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
180 return STATE_RUN_FAILED;
181
182 UINT32 requestId = 0;
183 UINT32 hr = 0;
184
185 Stream_Read_UINT32(s, requestId); /* requestId (4 bytes) */
186 Stream_Read_UINT32(s, hr); /* hrResponse (4 bytes) */
187
188 state_run_t res = STATE_RUN_SUCCESS;
189 IFCALLRET(multi->MtResponse, res, multi, requestId, hr);
190 return res;
191}
192
193static state_run_t multitransport_no_udp(rdpMultitransport* multi, UINT32 reqId,
194 WINPR_ATTR_UNUSED UINT16 reqProto,
195 WINPR_ATTR_UNUSED const BYTE* cookie)
196{
197 return multitransport_client_send_response(multi, reqId, E_ABORT) ? STATE_RUN_SUCCESS
198 : STATE_RUN_FAILED;
199}
200
201static state_run_t multitransport_server_handle_response(rdpMultitransport* multi,
202 WINPR_ATTR_UNUSED UINT32 reqId,
203 WINPR_ATTR_UNUSED UINT32 hrResponse)
204{
205 rdpRdp* rdp = multi->rdp;
206
207 if (!rdp_server_transition_to_state(rdp, CONNECTION_STATE_CAPABILITIES_EXCHANGE_DEMAND_ACTIVE))
208 return STATE_RUN_FAILED;
209
210 return STATE_RUN_CONTINUE;
211}
212
213rdpMultitransport* multitransport_new(rdpRdp* rdp, WINPR_ATTR_UNUSED UINT16 protocol)
214{
215 WINPR_ASSERT(rdp);
216
217 rdpSettings* settings = rdp->settings;
218 WINPR_ASSERT(settings);
219
220 rdpMultitransport* multi = calloc(1, sizeof(rdpMultitransport));
221 if (!multi)
222 return nullptr;
223
224 if (settings->ServerMode)
225 {
226 multi->MtResponse = multitransport_server_handle_response;
227 }
228 else
229 {
230 multi->MtRequest = multitransport_no_udp;
231 }
232
233 multi->rdp = rdp;
234 return multi;
235}
236
237void multitransport_free(rdpMultitransport* multitransport)
238{
239 free(multitransport);
240}