Coverage Report

Created: 2024-05-20 06:11

/src/FreeRDP/libfreerdp/core/streamdump.c
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * FreeRDP: A Remote Desktop Protocol Implementation
3
 *
4
 * RDP session stream dump interface
5
 *
6
 * Copyright 2022 Armin Novak
7
 * Copyright 2022 Thincast Technologies GmbH
8
 *
9
 * Licensed under the Apache License, Version 2.0 (the "License");
10
 * you may not use this file except in compliance with the License.
11
 * You may obtain a copy of the License at
12
 *
13
 *     http://www.apache.org/licenses/LICENSE-2.0
14
 *
15
 * Unless required by applicable law or agreed to in writing, software
16
 * distributed under the License is distributed on an "AS IS" BASIS,
17
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18
 * See the License for the specific language governing permissions and
19
 * limitations under the License.
20
 */
21
22
#include <time.h>
23
24
#include <winpr/sysinfo.h>
25
#include <winpr/path.h>
26
#include <winpr/string.h>
27
28
#include <freerdp/freerdp.h>
29
#include <freerdp/streamdump.h>
30
#include <freerdp/transport_io.h>
31
32
#include "streamdump.h"
33
34
struct stream_dump_context
35
{
36
  rdpTransportIo io;
37
  size_t writeDumpOffset;
38
  size_t readDumpOffset;
39
  size_t replayOffset;
40
  UINT64 replayTime;
41
  CONNECTION_STATE state;
42
  BOOL isServer;
43
  BOOL nodelay;
44
};
45
46
static UINT32 crc32b(const BYTE* data, size_t length)
47
0
{
48
0
  UINT32 crc = 0xFFFFFFFF;
49
50
0
  for (size_t x = 0; x < length; x++)
51
0
  {
52
0
    const UINT32 d = data[x] & 0xFF;
53
0
    crc = crc ^ d;
54
0
    for (int j = 7; j >= 0; j--)
55
0
    {
56
0
      UINT32 mask = ~(crc & 1);
57
0
      crc = (crc >> 1) ^ (0xEDB88320 & mask);
58
0
    }
59
0
  }
60
0
  return ~crc;
61
0
}
62
63
#if !defined(BUILD_TESTING)
64
static
65
#endif
66
    BOOL
67
    stream_dump_read_line(FILE* fp, wStream* s, UINT64* pts, size_t* pOffset, UINT32* flags)
68
0
{
69
0
  BOOL rc = FALSE;
70
0
  UINT64 ts = 0;
71
0
  UINT64 size = 0;
72
0
  size_t r = 0;
73
0
  UINT32 crc32 = 0;
74
0
  BYTE received = 0;
75
76
0
  if (!fp || !s || !flags)
77
0
    return FALSE;
78
79
0
  if (pOffset)
80
0
    _fseeki64(fp, *pOffset, SEEK_SET);
81
82
0
  r = fread(&ts, 1, sizeof(ts), fp);
83
0
  if (r != sizeof(ts))
84
0
    goto fail;
85
0
  r = fread(&received, 1, sizeof(received), fp);
86
0
  if (r != sizeof(received))
87
0
    goto fail;
88
0
  r = fread(&crc32, 1, sizeof(crc32), fp);
89
0
  if (r != sizeof(crc32))
90
0
    goto fail;
91
0
  r = fread(&size, 1, sizeof(size), fp);
92
0
  if (r != sizeof(size))
93
0
    goto fail;
94
0
  if (received)
95
0
    *flags = STREAM_MSG_SRV_RX;
96
0
  else
97
0
    *flags = STREAM_MSG_SRV_TX;
98
0
  if (!Stream_EnsureRemainingCapacity(s, size))
99
0
    goto fail;
100
0
  r = fread(Stream_Pointer(s), 1, size, fp);
101
0
  if (r != size)
102
0
    goto fail;
103
0
  if (crc32 != crc32b(Stream_ConstPointer(s), size))
104
0
    goto fail;
105
0
  Stream_Seek(s, size);
106
107
0
  if (pOffset)
108
0
  {
109
0
    INT64 tmp = _ftelli64(fp);
110
0
    if (tmp < 0)
111
0
      goto fail;
112
0
    *pOffset = (size_t)tmp;
113
0
  }
114
115
0
  if (pts)
116
0
    *pts = ts;
117
0
  rc = TRUE;
118
119
0
fail:
120
0
  Stream_SealLength(s);
121
0
  return rc;
122
0
}
123
124
#if !defined(BUILD_TESTING)
125
static
126
#endif
127
    BOOL
128
    stream_dump_write_line(FILE* fp, UINT32 flags, wStream* s)
129
0
{
130
0
  BOOL rc = FALSE;
131
0
  const UINT64 t = GetTickCount64();
132
0
  const BYTE* data = Stream_Buffer(s);
133
0
  const UINT64 size = Stream_Length(s);
134
135
0
  if (!fp || !s)
136
0
    return FALSE;
137
138
0
  {
139
0
    const UINT32 crc32 = crc32b(data, size);
140
0
    const BYTE received = flags & STREAM_MSG_SRV_RX;
141
0
    size_t r = fwrite(&t, 1, sizeof(t), fp);
142
0
    if (r != sizeof(t))
143
0
      goto fail;
144
0
    r = fwrite(&received, 1, sizeof(received), fp);
145
0
    if (r != sizeof(received))
146
0
      goto fail;
147
0
    r = fwrite(&crc32, 1, sizeof(crc32), fp);
148
0
    if (r != sizeof(crc32))
149
0
      goto fail;
150
0
    r = fwrite(&size, 1, sizeof(size), fp);
151
0
    if (r != sizeof(size))
152
0
      goto fail;
153
0
    r = fwrite(data, 1, size, fp);
154
0
    if (r != size)
155
0
      goto fail;
156
0
  }
157
158
0
  rc = TRUE;
159
0
fail:
160
0
  return rc;
161
0
}
162
163
static FILE* stream_dump_get_file(const rdpSettings* settings, const char* mode)
164
0
{
165
0
  const char* cfolder = NULL;
166
0
  char* file = NULL;
167
0
  FILE* fp = NULL;
168
169
0
  if (!settings || !mode)
170
0
    return NULL;
171
172
0
  cfolder = freerdp_settings_get_string(settings, FreeRDP_TransportDumpFile);
173
0
  if (!cfolder)
174
0
    file = GetKnownSubPath(KNOWN_PATH_TEMP, "freerdp-transport-dump");
175
0
  else
176
0
    file = _strdup(cfolder);
177
178
0
  if (!file)
179
0
    goto fail;
180
181
0
  fp = winpr_fopen(file, mode);
182
0
fail:
183
0
  free(file);
184
0
  return fp;
185
0
}
186
187
SSIZE_T stream_dump_append(const rdpContext* context, UINT32 flags, wStream* s, size_t* offset)
188
0
{
189
0
  SSIZE_T rc = -1;
190
0
  FILE* fp = NULL;
191
0
  const UINT32 mask = STREAM_MSG_SRV_RX | STREAM_MSG_SRV_TX;
192
0
  CONNECTION_STATE state = freerdp_get_state(context);
193
0
  int r = 0;
194
195
0
  if (!context || !s || !offset)
196
0
    return -1;
197
198
0
  if ((flags & STREAM_MSG_SRV_RX) && (flags & STREAM_MSG_SRV_TX))
199
0
    return -1;
200
201
0
  if ((flags & mask) == 0)
202
0
    return -1;
203
204
0
  if (state < context->dump->state)
205
0
    return 0;
206
207
0
  fp = stream_dump_get_file(context->settings, "ab");
208
0
  if (!fp)
209
0
    return -1;
210
211
0
  r = _fseeki64(fp, *offset, SEEK_SET);
212
0
  if (r < 0)
213
0
    goto fail;
214
215
0
  if (!stream_dump_write_line(fp, flags, s))
216
0
    goto fail;
217
0
  rc = _ftelli64(fp);
218
0
  if (rc < 0)
219
0
    goto fail;
220
0
  *offset = (size_t)rc;
221
0
fail:
222
0
  if (fp)
223
0
    fclose(fp);
224
0
  return rc;
225
0
}
226
227
SSIZE_T stream_dump_get(const rdpContext* context, UINT32* flags, wStream* s, size_t* offset,
228
                        UINT64* pts)
229
0
{
230
0
  SSIZE_T rc = -1;
231
0
  FILE* fp = NULL;
232
0
  int r = 0;
233
234
0
  if (!context || !s || !offset)
235
0
    return -1;
236
0
  fp = stream_dump_get_file(context->settings, "rb");
237
0
  if (!fp)
238
0
    return -1;
239
0
  r = _fseeki64(fp, *offset, SEEK_SET);
240
0
  if (r < 0)
241
0
    goto fail;
242
243
0
  if (!stream_dump_read_line(fp, s, pts, offset, flags))
244
0
    goto fail;
245
246
0
  rc = _ftelli64(fp);
247
0
fail:
248
0
  if (fp)
249
0
    fclose(fp);
250
0
  return rc;
251
0
}
252
253
static int stream_dump_transport_write(rdpTransport* transport, wStream* s)
254
0
{
255
0
  SSIZE_T r = 0;
256
0
  rdpContext* ctx = transport_get_context(transport);
257
258
0
  WINPR_ASSERT(ctx);
259
0
  WINPR_ASSERT(ctx->dump);
260
0
  WINPR_ASSERT(s);
261
262
0
  r = stream_dump_append(ctx, ctx->dump->isServer ? STREAM_MSG_SRV_TX : STREAM_MSG_SRV_RX, s,
263
0
                         &ctx->dump->writeDumpOffset);
264
0
  if (r < 0)
265
0
    return -1;
266
267
0
  WINPR_ASSERT(ctx->dump->io.WritePdu);
268
0
  return ctx->dump->io.WritePdu(transport, s);
269
0
}
270
271
static int stream_dump_transport_read(rdpTransport* transport, wStream* s)
272
0
{
273
0
  int rc = 0;
274
0
  rdpContext* ctx = transport_get_context(transport);
275
276
0
  WINPR_ASSERT(ctx);
277
0
  WINPR_ASSERT(ctx->dump);
278
0
  WINPR_ASSERT(s);
279
280
0
  WINPR_ASSERT(ctx->dump->io.ReadPdu);
281
0
  rc = ctx->dump->io.ReadPdu(transport, s);
282
0
  if (rc > 0)
283
0
  {
284
0
    SSIZE_T r =
285
0
        stream_dump_append(ctx, ctx->dump->isServer ? STREAM_MSG_SRV_RX : STREAM_MSG_SRV_TX, s,
286
0
                           &ctx->dump->readDumpOffset);
287
0
    if (r < 0)
288
0
      return -1;
289
0
  }
290
0
  return rc;
291
0
}
292
293
static BOOL stream_dump_register_write_handlers(rdpContext* context)
294
0
{
295
0
  rdpTransportIo dump;
296
0
  const rdpTransportIo* dfl = freerdp_get_io_callbacks(context);
297
298
0
  if (!freerdp_settings_get_bool(context->settings, FreeRDP_TransportDump))
299
0
    return TRUE;
300
301
0
  WINPR_ASSERT(dfl);
302
0
  dump = *dfl;
303
304
  /* Remember original callbacks for later */
305
0
  WINPR_ASSERT(context->dump);
306
0
  context->dump->io.ReadPdu = dfl->ReadPdu;
307
0
  context->dump->io.WritePdu = dfl->WritePdu;
308
309
  /* Set our dump wrappers */
310
0
  dump.WritePdu = stream_dump_transport_write;
311
0
  dump.ReadPdu = stream_dump_transport_read;
312
0
  return freerdp_set_io_callbacks(context, &dump);
313
0
}
314
315
static int stream_dump_replay_transport_write(rdpTransport* transport, wStream* s)
316
0
{
317
0
  rdpContext* ctx = transport_get_context(transport);
318
0
  size_t size = 0;
319
320
0
  WINPR_ASSERT(ctx);
321
0
  WINPR_ASSERT(s);
322
323
0
  size = Stream_Length(s);
324
0
  WLog_ERR("abc", "replay write %" PRIuz, size);
325
  // TODO: Compare with write file
326
327
0
  return 1;
328
0
}
329
330
static int stream_dump_replay_transport_read(rdpTransport* transport, wStream* s)
331
0
{
332
0
  rdpContext* ctx = transport_get_context(transport);
333
334
0
  size_t size = 0;
335
0
  time_t slp = 0;
336
0
  UINT64 ts = 0;
337
0
  UINT32 flags = 0;
338
339
0
  WINPR_ASSERT(ctx);
340
0
  WINPR_ASSERT(ctx->dump);
341
0
  WINPR_ASSERT(s);
342
343
0
  const size_t start = Stream_GetPosition(s);
344
0
  do
345
0
  {
346
0
    Stream_SetPosition(s, start);
347
0
    if (stream_dump_get(ctx, &flags, s, &ctx->dump->replayOffset, &ts) < 0)
348
0
      return -1;
349
0
  } while (flags & STREAM_MSG_SRV_RX);
350
351
0
  if (!ctx->dump->nodelay)
352
0
  {
353
0
    if ((ctx->dump->replayTime > 0) && (ts > ctx->dump->replayTime))
354
0
      slp = ts - ctx->dump->replayTime;
355
0
  }
356
0
  ctx->dump->replayTime = ts;
357
358
0
  size = Stream_Length(s);
359
0
  Stream_SetPosition(s, 0);
360
0
  WLog_ERR("abc", "replay read %" PRIuz, size);
361
362
0
  if (slp > 0)
363
0
  {
364
0
    size_t duration = slp;
365
0
    do
366
0
    {
367
0
      const DWORD actual = (DWORD)MIN(duration, UINT32_MAX);
368
0
      Sleep(actual);
369
0
      duration -= actual;
370
0
    } while (duration > 0);
371
0
  }
372
373
0
  return 1;
374
0
}
375
376
static int stream_dump_replay_transport_tcp_connect(rdpContext* context, rdpSettings* settings,
377
                                                    const char* hostname, int port, DWORD timeout)
378
0
{
379
0
  WINPR_ASSERT(context);
380
0
  WINPR_ASSERT(settings);
381
0
  WINPR_ASSERT(hostname);
382
383
0
  return 42;
384
0
}
385
386
static BOOL stream_dump_replay_transport_tls_connect(rdpTransport* transport)
387
0
{
388
0
  WINPR_ASSERT(transport);
389
0
  return TRUE;
390
0
}
391
392
static BOOL stream_dump_replay_transport_accept(rdpTransport* transport)
393
0
{
394
0
  WINPR_ASSERT(transport);
395
0
  return TRUE;
396
0
}
397
398
static BOOL stream_dump_register_read_handlers(rdpContext* context)
399
0
{
400
0
  const rdpTransportIo* dfl = freerdp_get_io_callbacks(context);
401
402
0
  if (!freerdp_settings_get_bool(context->settings, FreeRDP_TransportDumpReplay))
403
0
    return TRUE;
404
405
0
  WINPR_ASSERT(dfl);
406
0
  rdpTransportIo dump = *dfl;
407
408
  /* Remember original callbacks for later */
409
0
  WINPR_ASSERT(context->dump);
410
0
  context->dump->nodelay =
411
0
      freerdp_settings_get_bool(context->settings, FreeRDP_TransportDumpReplayNodelay);
412
0
  context->dump->io.ReadPdu = dfl->ReadPdu;
413
0
  context->dump->io.WritePdu = dfl->WritePdu;
414
415
  /* Set our dump wrappers */
416
0
  dump.WritePdu = stream_dump_transport_write;
417
0
  dump.ReadPdu = stream_dump_transport_read;
418
419
  /* Set our dump wrappers */
420
0
  dump.WritePdu = stream_dump_replay_transport_write;
421
0
  dump.ReadPdu = stream_dump_replay_transport_read;
422
0
  dump.TCPConnect = stream_dump_replay_transport_tcp_connect;
423
0
  dump.TLSAccept = stream_dump_replay_transport_accept;
424
0
  dump.TLSConnect = stream_dump_replay_transport_tls_connect;
425
0
  if (!freerdp_set_io_callbacks(context, &dump))
426
0
    return FALSE;
427
0
  return freerdp_io_callback_set_event(context, TRUE);
428
0
}
429
430
BOOL stream_dump_register_handlers(rdpContext* context, CONNECTION_STATE state, BOOL isServer)
431
0
{
432
0
  WINPR_ASSERT(context);
433
0
  WINPR_ASSERT(context->dump);
434
0
  context->dump->state = state;
435
0
  context->dump->isServer = isServer;
436
0
  if (!stream_dump_register_write_handlers(context))
437
0
    return FALSE;
438
0
  return stream_dump_register_read_handlers(context);
439
0
}
440
441
void stream_dump_free(rdpStreamDumpContext* dump)
442
14.9k
{
443
14.9k
  free(dump);
444
14.9k
}
445
446
rdpStreamDumpContext* stream_dump_new(void)
447
14.9k
{
448
14.9k
  rdpStreamDumpContext* dump = calloc(1, sizeof(rdpStreamDumpContext));
449
14.9k
  if (!dump)
450
0
    return NULL;
451
452
14.9k
  return dump;
453
14.9k
}