24#include <winpr/sysinfo.h>
25#include <winpr/path.h>
26#include <winpr/string.h>
28#include <freerdp/freerdp.h>
29#include <freerdp/streamdump.h>
30#include <freerdp/transport_io.h>
32#include "streamdump.h"
34#define TAG FREERDP_TAG("streamdump")
36struct stream_dump_context
39 size_t writeDumpOffset;
40 size_t readDumpOffset;
43 CONNECTION_STATE state;
49static UINT32 crc32b(
const BYTE* data,
size_t length)
51 UINT32 crc = 0xFFFFFFFFul;
53 for (
size_t x = 0; x < length; x++)
55 const UINT32 d = data[x] & 0xFFul;
57 for (
int j = 7; j >= 0; j--)
62 crc = (crc >> 1ul) ^ mask;
68#if !defined(BUILD_TESTING_INTERNAL)
71 BOOL stream_dump_read_line(FILE* fp,
wStream* s, UINT64* pts,
size_t* pOffset, UINT32* flags)
80 if (!fp || !s || !flags)
85 if (_fseeki64(fp, WINPR_ASSERTING_INT_CAST(int64_t, *pOffset), SEEK_SET) < 0)
89 r = fread(&ts, 1,
sizeof(ts), fp);
92 r = fread(&received, 1,
sizeof(received), fp);
93 if (r !=
sizeof(received))
95 r = fread(&crc32, 1,
sizeof(crc32), fp);
96 if (r !=
sizeof(crc32))
98 r = fread(&size, 1,
sizeof(size), fp);
99 if (r !=
sizeof(size))
102 *flags = STREAM_MSG_SRV_RX;
104 *flags = STREAM_MSG_SRV_TX;
107 const size_t usize = WINPR_ASSERTING_INT_CAST(
size_t, size);
108 if (!Stream_EnsureRemainingCapacity(s, usize))
110 r = fread(Stream_Pointer(s), 1, usize, fp);
113 if (crc32 != crc32b(Stream_ConstPointer(s), usize))
115 Stream_Seek(s, usize);
120 INT64 tmp = _ftelli64(fp);
123 *pOffset = (size_t)tmp;
131 Stream_SealLength(s);
135#if !defined(BUILD_TESTING_INTERNAL)
138 BOOL stream_dump_write_line(FILE* fp, UINT32 flags,
wStream* s)
141 const UINT64 t = GetTickCount64();
142 const BYTE* data = Stream_Buffer(s);
143 const size_t usize = Stream_Length(s);
144 const uint64_t size = (uint64_t)usize;
150 const UINT32 crc32 = crc32b(data, usize);
151 const BYTE received = flags & STREAM_MSG_SRV_RX;
152 size_t r = fwrite(&t, 1,
sizeof(t), fp);
155 r = fwrite(&received, 1,
sizeof(received), fp);
156 if (r !=
sizeof(received))
158 r = fwrite(&crc32, 1,
sizeof(crc32), fp);
159 if (r !=
sizeof(crc32))
161 r = fwrite(&size, 1,
sizeof(size), fp);
162 if (r !=
sizeof(size))
164 r = fwrite(data, 1, usize, fp);
174static FILE* stream_dump_get_file(
const rdpSettings* settings,
const char* mode)
176 const char* cfolder =
nullptr;
177 char* file =
nullptr;
180 if (!settings || !mode)
185 file = GetKnownSubPath(KNOWN_PATH_TEMP,
"freerdp-transport-dump");
187 file = _strdup(cfolder);
192 fp = winpr_fopen(file, mode);
198SSIZE_T stream_dump_append(
const rdpContext* context, UINT32 flags,
wStream* s,
size_t* offset)
202 const UINT32 mask = STREAM_MSG_SRV_RX | STREAM_MSG_SRV_TX;
203 CONNECTION_STATE state = freerdp_get_state(context);
206 if (!context || !s || !offset)
209 if ((flags & STREAM_MSG_SRV_RX) && (flags & STREAM_MSG_SRV_TX))
212 if ((flags & mask) == 0)
215 if (state < context->dump->state)
218 fp = stream_dump_get_file(context->settings,
"ab");
222 r = _fseeki64(fp, WINPR_ASSERTING_INT_CAST(int64_t, *offset), SEEK_SET);
226 if (!stream_dump_write_line(fp, flags, s))
229 const int64_t rt = _ftelli64(fp);
235 rc = WINPR_ASSERTING_INT_CAST(SSIZE_T, rt);
237 *offset = (size_t)rc;
245SSIZE_T stream_dump_get(
const rdpContext* context, UINT32* flags,
wStream* s,
size_t* offset,
252 if (!context || !s || !offset)
254 fp = stream_dump_get_file(context->settings,
"rb");
257 r = _fseeki64(fp, WINPR_ASSERTING_INT_CAST(int64_t, *offset), SEEK_SET);
261 if (!stream_dump_read_line(fp, s, pts, offset, flags))
265 const int64_t rt = _ftelli64(fp);
268 rc = WINPR_ASSERTING_INT_CAST(SSIZE_T, rt);
277static int stream_dump_transport_write(rdpTransport* transport,
wStream* s)
280 rdpContext* ctx = transport_get_context(transport);
283 WINPR_ASSERT(ctx->dump);
286 r = stream_dump_append(ctx, ctx->dump->isServer ? STREAM_MSG_SRV_TX : STREAM_MSG_SRV_RX, s,
287 &ctx->dump->writeDumpOffset);
291 WINPR_ASSERT(ctx->dump->io.WritePdu);
292 return ctx->dump->io.WritePdu(transport, s);
295static int stream_dump_transport_read(rdpTransport* transport,
wStream* s)
298 rdpContext* ctx = transport_get_context(transport);
301 WINPR_ASSERT(ctx->dump);
304 WINPR_ASSERT(ctx->dump->io.ReadPdu);
305 rc = ctx->dump->io.ReadPdu(transport, s);
309 stream_dump_append(ctx, ctx->dump->isServer ? STREAM_MSG_SRV_RX : STREAM_MSG_SRV_TX, s,
310 &ctx->dump->readDumpOffset);
317static BOOL stream_dump_register_write_handlers(rdpContext* context)
319 rdpTransportIo dump = WINPR_C_ARRAY_INIT;
320 const rdpTransportIo* dfl = freerdp_get_io_callbacks(context);
329 WINPR_ASSERT(context->dump);
330 context->dump->io.ReadPdu = dfl->ReadPdu;
331 context->dump->io.WritePdu = dfl->WritePdu;
334 dump.WritePdu = stream_dump_transport_write;
335 dump.ReadPdu = stream_dump_transport_read;
336 return freerdp_set_io_callbacks(context, &dump);
339static int stream_dump_replay_transport_write(rdpTransport* transport,
wStream* s)
341 rdpContext* ctx = transport_get_context(transport);
347 size = Stream_Length(s);
348 WLog_Print(ctx->dump->log, WLOG_TRACE,
"replay write %" PRIuz, size);
354static int stream_dump_replay_transport_read(rdpTransport* transport,
wStream* s)
356 rdpContext* ctx = transport_get_context(transport);
364 WINPR_ASSERT(ctx->dump);
367 const size_t start = Stream_GetPosition(s);
370 if (!Stream_SetPosition(s, start))
372 if (stream_dump_get(ctx, &flags, s, &ctx->dump->replayOffset, &ts) < 0)
374 }
while (flags & STREAM_MSG_SRV_RX);
376 if (!ctx->dump->nodelay)
378 if ((ctx->dump->replayTime > 0) && (ts > ctx->dump->replayTime))
379 slp = ts - ctx->dump->replayTime;
381 ctx->dump->replayTime = ts;
383 size = Stream_Length(s);
384 Stream_ResetPosition(s);
385 WLog_Print(ctx->dump->log, WLOG_TRACE,
"replay read %" PRIuz, size);
389 uint64_t duration = slp;
392 const DWORD actual = (DWORD)MIN(duration, UINT32_MAX);
395 }
while (duration > 0);
401static int stream_dump_replay_transport_tcp_connect(WINPR_ATTR_UNUSED rdpContext* context,
402 WINPR_ATTR_UNUSED rdpSettings* settings,
403 WINPR_ATTR_UNUSED
const char* hostname,
404 WINPR_ATTR_UNUSED
int port,
405 WINPR_ATTR_UNUSED DWORD timeout)
407 WINPR_ASSERT(context);
408 WINPR_ASSERT(settings);
409 WINPR_ASSERT(hostname);
415 WINPR_ATTR_UNUSED rdpTransport* transport, WINPR_ATTR_UNUSED
const char* hostname,
416 WINPR_ATTR_UNUSED
int port, WINPR_ATTR_UNUSED DWORD timeout)
418 WINPR_ASSERT(transport);
419 WINPR_ASSERT(hostname);
424static BOOL stream_dump_replay_transport_tls_connect(WINPR_ATTR_UNUSED rdpTransport* transport)
426 WINPR_ASSERT(transport);
430static BOOL stream_dump_replay_transport_accept(WINPR_ATTR_UNUSED rdpTransport* transport)
432 WINPR_ASSERT(transport);
436static BOOL stream_dump_register_read_handlers(rdpContext* context)
438 const rdpTransportIo* dfl = freerdp_get_io_callbacks(context);
444 rdpTransportIo dump = *dfl;
447 WINPR_ASSERT(context->dump);
448 context->dump->nodelay =
450 context->dump->io.ReadPdu = dfl->ReadPdu;
451 context->dump->io.WritePdu = dfl->WritePdu;
454 dump.WritePdu = stream_dump_replay_transport_write;
455 dump.ReadPdu = stream_dump_replay_transport_read;
456 dump.TCPConnect = stream_dump_replay_transport_tcp_connect;
457 dump.TLSAccept = stream_dump_replay_transport_accept;
458 dump.TLSConnect = stream_dump_replay_transport_tls_connect;
459 dump.ConnectLayer = stream_dump_replay_transport_connect_layer;
460 if (!freerdp_set_io_callbacks(context, &dump))
462 return freerdp_io_callback_set_event(context, TRUE);
465BOOL stream_dump_register_handlers(rdpContext* context, CONNECTION_STATE state, BOOL isServer)
467 WINPR_ASSERT(context);
468 WINPR_ASSERT(context->dump);
469 context->dump->state = state;
470 context->dump->isServer = isServer;
471 if (!stream_dump_register_write_handlers(context))
473 return stream_dump_register_read_handlers(context);
476void stream_dump_free(rdpStreamDumpContext* dump)
481rdpStreamDumpContext* stream_dump_new(
void)
483 rdpStreamDumpContext* dump = calloc(1,
sizeof(rdpStreamDumpContext));
486 dump->log = WLog_Get(TAG);
WINPR_ATTR_NODISCARD FREERDP_API const char * freerdp_settings_get_string(const rdpSettings *settings, FreeRDP_Settings_Keys_String id)
Returns a immutable string settings value.
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_get_bool(const rdpSettings *settings, FreeRDP_Settings_Keys_Bool id)
Returns a boolean settings value.