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")
36 struct stream_dump_context
39 size_t writeDumpOffset;
40 size_t readDumpOffset;
43 CONNECTION_STATE state;
49 static UINT32 crc32b(
const BYTE* data,
size_t length)
51 UINT32 crc = 0xFFFFFFFF;
53 for (
size_t x = 0; x < length; x++)
55 const UINT32 d = data[x] & 0xFF;
57 for (
int j = 7; j >= 0; j--)
59 UINT32 mask = ~(crc & 1);
60 crc = (crc >> 1) ^ (0xEDB88320 & mask);
66 #if !defined(BUILD_TESTING_INTERNAL)
70 stream_dump_read_line(FILE* fp,
wStream* s, UINT64* pts,
size_t* pOffset, UINT32* flags)
79 if (!fp || !s || !flags)
83 (void)_fseeki64(fp, *pOffset, SEEK_SET);
85 r = fread(&ts, 1,
sizeof(ts), fp);
88 r = fread(&received, 1,
sizeof(received), fp);
89 if (r !=
sizeof(received))
91 r = fread(&crc32, 1,
sizeof(crc32), fp);
92 if (r !=
sizeof(crc32))
94 r = fread(&size, 1,
sizeof(size), fp);
95 if (r !=
sizeof(size))
98 *flags = STREAM_MSG_SRV_RX;
100 *flags = STREAM_MSG_SRV_TX;
101 if (!Stream_EnsureRemainingCapacity(s, size))
103 r = fread(Stream_Pointer(s), 1, size, fp);
106 if (crc32 != crc32b(Stream_ConstPointer(s), size))
108 Stream_Seek(s, size);
112 INT64 tmp = _ftelli64(fp);
115 *pOffset = (size_t)tmp;
123 Stream_SealLength(s);
127 #if !defined(BUILD_TESTING_INTERNAL)
131 stream_dump_write_line(FILE* fp, UINT32 flags,
wStream* s)
134 const UINT64 t = GetTickCount64();
135 const BYTE* data = Stream_Buffer(s);
136 const UINT64 size = Stream_Length(s);
142 const UINT32 crc32 = crc32b(data, size);
143 const BYTE received = flags & STREAM_MSG_SRV_RX;
144 size_t r = fwrite(&t, 1,
sizeof(t), fp);
147 r = fwrite(&received, 1,
sizeof(received), fp);
148 if (r !=
sizeof(received))
150 r = fwrite(&crc32, 1,
sizeof(crc32), fp);
151 if (r !=
sizeof(crc32))
153 r = fwrite(&size, 1,
sizeof(size), fp);
154 if (r !=
sizeof(size))
156 r = fwrite(data, 1, size, fp);
166 static FILE* stream_dump_get_file(
const rdpSettings* settings,
const char* mode)
168 const char* cfolder = NULL;
172 if (!settings || !mode)
177 file = GetKnownSubPath(KNOWN_PATH_TEMP,
"freerdp-transport-dump");
179 file = _strdup(cfolder);
184 fp = winpr_fopen(file, mode);
190 SSIZE_T stream_dump_append(
const rdpContext* context, UINT32 flags,
wStream* s,
size_t* offset)
194 const UINT32 mask = STREAM_MSG_SRV_RX | STREAM_MSG_SRV_TX;
195 CONNECTION_STATE state = freerdp_get_state(context);
198 if (!context || !s || !offset)
201 if ((flags & STREAM_MSG_SRV_RX) && (flags & STREAM_MSG_SRV_TX))
204 if ((flags & mask) == 0)
207 if (state < context->dump->state)
210 fp = stream_dump_get_file(context->settings,
"ab");
214 r = _fseeki64(fp, *offset, SEEK_SET);
218 if (!stream_dump_write_line(fp, flags, s))
223 *offset = (size_t)rc;
230 SSIZE_T stream_dump_get(
const rdpContext* context, UINT32* flags,
wStream* s,
size_t* offset,
237 if (!context || !s || !offset)
239 fp = stream_dump_get_file(context->settings,
"rb");
242 r = _fseeki64(fp, *offset, SEEK_SET);
246 if (!stream_dump_read_line(fp, s, pts, offset, flags))
256 static int stream_dump_transport_write(rdpTransport* transport,
wStream* s)
259 rdpContext* ctx = transport_get_context(transport);
262 WINPR_ASSERT(ctx->dump);
265 r = stream_dump_append(ctx, ctx->dump->isServer ? STREAM_MSG_SRV_TX : STREAM_MSG_SRV_RX, s,
266 &ctx->dump->writeDumpOffset);
270 WINPR_ASSERT(ctx->dump->io.WritePdu);
271 return ctx->dump->io.WritePdu(transport, s);
274 static int stream_dump_transport_read(rdpTransport* transport,
wStream* s)
277 rdpContext* ctx = transport_get_context(transport);
280 WINPR_ASSERT(ctx->dump);
283 WINPR_ASSERT(ctx->dump->io.ReadPdu);
284 rc = ctx->dump->io.ReadPdu(transport, s);
288 stream_dump_append(ctx, ctx->dump->isServer ? STREAM_MSG_SRV_RX : STREAM_MSG_SRV_TX, s,
289 &ctx->dump->readDumpOffset);
296 static BOOL stream_dump_register_write_handlers(rdpContext* context)
298 rdpTransportIo dump = { 0 };
299 const rdpTransportIo* dfl = freerdp_get_io_callbacks(context);
308 WINPR_ASSERT(context->dump);
309 context->dump->io.ReadPdu = dfl->ReadPdu;
310 context->dump->io.WritePdu = dfl->WritePdu;
313 dump.WritePdu = stream_dump_transport_write;
314 dump.ReadPdu = stream_dump_transport_read;
315 return freerdp_set_io_callbacks(context, &dump);
318 static int stream_dump_replay_transport_write(rdpTransport* transport,
wStream* s)
320 rdpContext* ctx = transport_get_context(transport);
326 size = Stream_Length(s);
327 WLog_Print(ctx->dump->log, WLOG_TRACE,
"replay write %" PRIuz, size);
333 static int stream_dump_replay_transport_read(rdpTransport* transport,
wStream* s)
335 rdpContext* ctx = transport_get_context(transport);
343 WINPR_ASSERT(ctx->dump);
346 const size_t start = Stream_GetPosition(s);
349 Stream_SetPosition(s, start);
350 if (stream_dump_get(ctx, &flags, s, &ctx->dump->replayOffset, &ts) < 0)
352 }
while (flags & STREAM_MSG_SRV_RX);
354 if (!ctx->dump->nodelay)
356 if ((ctx->dump->replayTime > 0) && (ts > ctx->dump->replayTime))
357 slp = ts - ctx->dump->replayTime;
359 ctx->dump->replayTime = ts;
361 size = Stream_Length(s);
362 Stream_SetPosition(s, 0);
363 WLog_Print(ctx->dump->log, WLOG_TRACE,
"replay read %" PRIuz, size);
367 size_t duration = slp;
370 const DWORD actual = (DWORD)MIN(duration, UINT32_MAX);
373 }
while (duration > 0);
379 static int stream_dump_replay_transport_tcp_connect(rdpContext* context, rdpSettings* settings,
380 const char* hostname,
int port, DWORD timeout)
382 WINPR_ASSERT(context);
383 WINPR_ASSERT(settings);
384 WINPR_ASSERT(hostname);
389 static rdpTransportLayer* stream_dump_replay_transport_connect_layer(rdpTransport* transport,
390 const char* hostname,
int port,
393 WINPR_ASSERT(transport);
394 WINPR_ASSERT(hostname);
399 static BOOL stream_dump_replay_transport_tls_connect(rdpTransport* transport)
401 WINPR_ASSERT(transport);
405 static BOOL stream_dump_replay_transport_accept(rdpTransport* transport)
407 WINPR_ASSERT(transport);
411 static BOOL stream_dump_register_read_handlers(rdpContext* context)
413 const rdpTransportIo* dfl = freerdp_get_io_callbacks(context);
419 rdpTransportIo dump = *dfl;
422 WINPR_ASSERT(context->dump);
423 context->dump->nodelay =
425 context->dump->io.ReadPdu = dfl->ReadPdu;
426 context->dump->io.WritePdu = dfl->WritePdu;
429 dump.WritePdu = stream_dump_transport_write;
430 dump.ReadPdu = stream_dump_transport_read;
433 dump.WritePdu = stream_dump_replay_transport_write;
434 dump.ReadPdu = stream_dump_replay_transport_read;
435 dump.TCPConnect = stream_dump_replay_transport_tcp_connect;
436 dump.TLSAccept = stream_dump_replay_transport_accept;
437 dump.TLSConnect = stream_dump_replay_transport_tls_connect;
438 dump.ConnectLayer = stream_dump_replay_transport_connect_layer;
439 if (!freerdp_set_io_callbacks(context, &dump))
441 return freerdp_io_callback_set_event(context, TRUE);
444 BOOL stream_dump_register_handlers(rdpContext* context, CONNECTION_STATE state, BOOL isServer)
446 WINPR_ASSERT(context);
447 WINPR_ASSERT(context->dump);
448 context->dump->state = state;
449 context->dump->isServer = isServer;
450 if (!stream_dump_register_write_handlers(context))
452 return stream_dump_register_read_handlers(context);
455 void stream_dump_free(rdpStreamDumpContext* dump)
460 rdpStreamDumpContext* stream_dump_new(
void)
462 rdpStreamDumpContext* dump = calloc(1,
sizeof(rdpStreamDumpContext));
465 dump->log = WLog_Get(TAG);
FREERDP_API BOOL freerdp_settings_get_bool(const rdpSettings *settings, FreeRDP_Settings_Keys_Bool id)
Returns a boolean settings value.
FREERDP_API const char * freerdp_settings_get_string(const rdpSettings *settings, FreeRDP_Settings_Keys_String id)
Returns a immutable string settings value.