27#include <winpr/wtypes.h>
29#include <winpr/stream.h>
30#include <winpr/synch.h>
31#include <winpr/sysinfo.h>
32#include <winpr/winsock.h>
33#include <winpr/wlog.h>
36#include <sys/socket.h>
37#include <netinet/in.h>
43#define TAG "com.freerdp.core.test.rdpudp"
46#define FLAG_SYN 0x0001
47#define FLAG_ACK 0x0004
48#define FLAG_SYNEX 0x1000
53#define V3_ACKVEC 0x008
55#define V3_OVERHEADSIZE 0x040
56#define V3_DELAYACKINFO 0x100
58#define V3_TYPE_STANDARD 0
59#define V3_TYPE_DUMMY 8
61#define SYN_LENGTH 1232
62#define FIRST_SEQUENCE 100
69 (void)fprintf(stderr, "%s:%d: check failed: %s\n", __FILE__, __LINE__, #cond); \
97 struct sockaddr_in serverAddr;
102 V3Packet seen[MAX_SEEN];
110static BOOL on_receive(
void* custom,
const BYTE* data,
size_t length)
112 TestCtx* ctx = custom;
113 if (!Stream_EnsureRemainingCapacity(ctx->delivered, length))
115 Stream_Write(ctx->delivered, data, length);
119static BOOL server_open(TestCtx* ctx)
121 ctx->server = _socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
122 if (ctx->server == INVALID_SOCKET)
125 struct sockaddr_in addr = WINPR_C_ARRAY_INIT;
126 addr.sin_family = AF_INET;
127 addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
129 if (_bind(ctx->server, (
const struct sockaddr*)&addr,
sizeof(addr)) != 0)
132 socklen_t length =
sizeof(ctx->serverAddr);
133 if (_getsockname(ctx->server, (
struct sockaddr*)&ctx->serverAddr, &length) != 0)
137 return _ioctlsocket(ctx->server, FIONBIO, &arg) == 0;
141static size_t server_recv(TestCtx* ctx, BYTE* buffer,
size_t size, UINT32 timeoutMs)
143 const UINT64 deadline = GetTickCount64() + timeoutMs;
146 const int rc = _recv(ctx->server, (
char*)buffer, (
int)size, 0);
149 if (GetTickCount64() >= deadline)
155static BOOL server_send_raw(TestCtx* ctx,
const BYTE* data,
size_t length)
157 return _send(ctx->server, (
const char*)data, (
int)length, 0) == (int)length;
161static BOOL pump(TestCtx* ctx)
164 return rdpudp_check(ctx->udp);
171static void write_uint24(
wStream* s, UINT32 value)
173 Stream_Write_UINT8(s, (BYTE)(value & 0xFF));
174 Stream_Write_UINT8(s, (BYTE)((value >> 8) & 0xFF));
175 Stream_Write_UINT8(s, (BYTE)((value >> 16) & 0xFF));
186 UINT16 delayedAckTimeout;
193 size_t payloadLength;
198static BOOL server_send_v3(TestCtx* ctx,
const V3Out* out)
200 BYTE wire[1500] = WINPR_C_ARRAY_INIT;
201 wStream sbuffer = WINPR_C_ARRAY_INIT;
202 wStream* s = Stream_StaticInit(&sbuffer, wire,
sizeof(wire));
207 if (out->hasDelayAck)
208 flags |= V3_DELAYACKINFO;
215 Stream_Write_UINT16(s, (UINT16)(flags | (15u << 12)));
218 Stream_Write_UINT16(s, out->ack);
220 Stream_Write_UINT8(s, 0);
221 Stream_Write_UINT8(s, 0);
223 if (out->hasDelayAck)
225 Stream_Write_UINT8(s, out->maxDelayedAcks);
226 Stream_Write_UINT16(s, out->delayedAckTimeout);
229 Stream_Write_UINT16(s, out->aoa);
232 Stream_Write_UINT16(s, out->dataSeq);
233 Stream_Write_UINT16(s, out->channelSeq);
234 Stream_Write(s, out->payload, out->payloadLength);
237 const size_t layout = Stream_GetPosition(s) - 1;
238 BYTE shortLength = 7;
241 shortLength = (BYTE)layout;
242 Stream_Zero(s, 7 - layout);
244 const size_t length = Stream_GetPosition(s);
245 const BYTE type = out->dummy ? V3_TYPE_DUMMY : V3_TYPE_STANDARD;
246 wire[0] = (BYTE)((shortLength << 5) | (type << 1));
247 const BYTE eighth = wire[7];
250 return server_send_raw(ctx, wire, length);
253static BOOL send_data(TestCtx* ctx, UINT16 dataSeq, UINT16 channelSeq,
const char* text)
255 const V3Out out = { .hasData = TRUE,
257 .channelSeq = channelSeq,
258 .payload = (
const BYTE*)text,
259 .payloadLength = strlen(text) };
260 return server_send_v3(ctx, &out);
263static BOOL send_aoa(TestCtx* ctx, UINT16 aoa)
265 const V3Out out = { .hasAoA = TRUE, .aoa = aoa };
266 return server_send_v3(ctx, &out);
269static BOOL send_ack(TestCtx* ctx, UINT16 ack)
271 const V3Out out = { .hasAck = TRUE, .ack = ack };
272 return server_send_v3(ctx, &out);
275static BOOL parse_v3(
const BYTE* wire,
size_t length, V3Packet* packet)
277 BYTE buffer[1500] = WINPR_C_ARRAY_INIT;
278 if ((length < 8) || (length >
sizeof(buffer)))
280 memcpy(buffer, wire, length);
281 const BYTE eighth = buffer[7];
282 buffer[7] = buffer[0];
285 memset(packet, 0,
sizeof(*packet));
286 const BYTE prefix = buffer[0];
287 const BYTE shortLength = prefix >> 5;
289 length = 1ull + shortLength;
291 wStream sbuffer = WINPR_C_ARRAY_INIT;
292 wStream* s = Stream_StaticConstInit(&sbuffer, buffer + 1, length - 1);
293 if (!Stream_CheckAndLogRequiredLength(TAG, s, 2))
295 const UINT16 header = Stream_Get_UINT16(s);
296 packet->flags = header & 0x0FFF;
297 packet->logWindow = (BYTE)(header >> 12);
299 if (packet->flags & V3_ACK)
301 if (!Stream_CheckAndLogRequiredLength(TAG, s, 7))
303 packet->ack = Stream_Get_UINT16(s);
305 const size_t delayed = Stream_Get_UINT8(s) & 0x0F;
306 if (!Stream_SafeSeek(s, delayed))
309 if ((packet->flags & V3_OVERHEADSIZE) && !Stream_SafeSeek(s, 1))
311 if ((packet->flags & V3_DELAYACKINFO) && !Stream_SafeSeek(s, 3))
313 if (packet->flags & V3_AOA)
315 if (!Stream_CheckAndLogRequiredLength(TAG, s, 2))
317 packet->hasAoA = TRUE;
318 packet->aoa = Stream_Get_UINT16(s);
320 if (packet->flags & V3_DATA)
322 if (!Stream_CheckAndLogRequiredLength(TAG, s, 2))
324 packet->dataSeq = Stream_Get_UINT16(s);
326 if (packet->flags & V3_ACKVEC)
328 if (!Stream_CheckAndLogRequiredLength(TAG, s, 3))
330 packet->vecBase = Stream_Get_UINT16(s);
331 const BYTE control = Stream_Get_UINT8(s);
332 if ((control & 0x80) && !Stream_SafeSeek(s, 4))
334 packet->vecLength = control & 0x7F;
335 if (!Stream_CheckAndLogRequiredLength(TAG, s, packet->vecLength))
337 Stream_Read(s, packet->vec, packet->vecLength);
339 if (packet->flags & V3_DATA)
341 if (!Stream_CheckAndLogRequiredLength(TAG, s, 2))
343 packet->hasData = TRUE;
344 packet->channelSeq = Stream_Get_UINT16(s);
345 packet->payloadLength = Stream_GetRemainingLength(s);
346 if (packet->payloadLength >
sizeof(packet->payload))
347 packet->payloadLength =
sizeof(packet->payload);
348 Stream_Read(s, packet->payload, packet->payloadLength);
354static BOOL drain(TestCtx* ctx, UINT32 waitMs)
356 BYTE buffer[1500] = WINPR_C_ARRAY_INIT;
359 while ((length = server_recv(ctx, buffer,
sizeof(buffer), waitMs)) > 0)
361 V3Packet packet = WINPR_C_ARRAY_INIT;
362 if (!parse_v3(buffer, length, &packet))
364 if (ctx->seenCount == MAX_SEEN)
366 memmove(&ctx->seen[0], &ctx->seen[1],
sizeof(ctx->seen[0]) * (MAX_SEEN - 1));
369 ctx->seen[ctx->seenCount++] = packet;
375static BOOL collect_data(TestCtx* ctx,
size_t want, UINT32 timeoutMs)
377 BYTE buffer[1500] = WINPR_C_ARRAY_INIT;
382 const size_t length = server_recv(ctx, buffer,
sizeof(buffer), timeoutMs);
385 if (ctx->seenCount == MAX_SEEN)
387 V3Packet* packet = &ctx->seen[ctx->seenCount++];
388 if (!parse_v3(buffer, length, packet))
397static const V3Packet* last_ack(
const TestCtx* ctx)
399 for (
size_t x = ctx->seenCount; x > 0; x--)
401 const V3Packet* packet = &ctx->seen[x - 1];
402 if (packet->flags & (V3_ACK | V3_ACKVEC))
408static size_t count_data(
const TestCtx* ctx)
411 for (
size_t x = 0; x < ctx->seenCount; x++)
413 if (ctx->seen[x].hasData)
419static const V3Packet* first_data(
const TestCtx* ctx)
421 for (
size_t x = 0; x < ctx->seenCount; x++)
423 if (ctx->seen[x].hasData)
424 return &ctx->seen[x];
429static BOOL delivered_is(
const TestCtx* ctx,
const char* expected)
431 const size_t length = strlen(expected);
432 return (Stream_GetPosition(ctx->delivered) == length) &&
433 (memcmp(Stream_Buffer(ctx->delivered), expected, length) == 0);
437static BOOL delivered_tail_is(
const TestCtx* ctx,
size_t offset,
const char* expected)
439 const size_t length = strlen(expected);
440 return (Stream_GetPosition(ctx->delivered) == offset + length) &&
441 (memcmp(Stream_Buffer(ctx->delivered) + offset, expected, length) == 0);
448static const BYTE cookieHash[32] = { 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
449 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
450 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
451 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA };
457 UINT32 initialSequence;
462static void ctx_free(TestCtx* ctx)
464 rdpudp_free(ctx->udp);
465 if (ctx->server != INVALID_SOCKET)
466 closesocket(ctx->server);
467 Stream_Free(ctx->delivered, TRUE);
468 memset(ctx, 0,
sizeof(*ctx));
469 ctx->server = INVALID_SOCKET;
473static BOOL ctx_start(TestCtx* ctx, BOOL offerV3, SynInfo* syn)
475 memset(ctx, 0,
sizeof(*ctx));
476 ctx->server = INVALID_SOCKET;
477 ctx->delivered = Stream_New(
nullptr, 1024);
478 if (!ctx->delivered || !server_open(ctx))
481 ctx->udp = rdpudp_new(WLog_Get(TAG), offerV3 ? cookieHash : nullptr, on_receive, ctx);
484 if (!rdpudp_connect(ctx->udp, (
const struct sockaddr*)&ctx->serverAddr,
485 sizeof(ctx->serverAddr)))
488 BYTE buffer[1500] = WINPR_C_ARRAY_INIT;
489 struct sockaddr_in from = WINPR_C_ARRAY_INIT;
490 int fromLength =
sizeof(from);
491 const UINT64 deadline = GetTickCount64() + 1000;
493 while ((rc = _recvfrom(ctx->server, (
char*)buffer,
sizeof(buffer), 0, (
struct sockaddr*)&from,
496 if (GetTickCount64() >= deadline)
500 if (_connect(ctx->server, (
const struct sockaddr*)&from, fromLength) != 0)
503 wStream sbuffer = WINPR_C_ARRAY_INIT;
504 wStream* s = Stream_StaticConstInit(&sbuffer, buffer, (
size_t)rc);
505 if (!Stream_CheckAndLogRequiredLength(TAG, s, 20))
507 syn->length = (size_t)rc;
509 syn->flags = Stream_Get_UINT16_BE(s);
510 syn->initialSequence = Stream_Get_UINT32_BE(s);
512 syn->version = Stream_Get_UINT16_BE(s);
513 if (Stream_GetRemainingLength(s) >=
sizeof(syn->cookie))
514 Stream_Read(s, syn->cookie,
sizeof(syn->cookie));
518static BOOL send_syn_ack(TestCtx* ctx, UINT32 acknowledged, UINT16 version)
520 BYTE wire[20] = WINPR_C_ARRAY_INIT;
521 wStream sbuffer = WINPR_C_ARRAY_INIT;
522 wStream* s = Stream_StaticInit(&sbuffer, wire,
sizeof(wire));
523 Stream_Write_UINT32_BE(s, acknowledged);
524 Stream_Write_UINT16_BE(s, 64);
525 Stream_Write_UINT16_BE(s, FLAG_SYN | FLAG_ACK | FLAG_SYNEX);
526 Stream_Write_UINT32_BE(s, 0x1000);
527 Stream_Write_UINT16_BE(s, SYN_LENGTH);
528 Stream_Write_UINT16_BE(s, SYN_LENGTH);
529 Stream_Write_UINT16_BE(s, 0x0001);
530 Stream_Write_UINT16_BE(s, version);
531 return server_send_raw(ctx, wire,
sizeof(wire));
535static BOOL ctx_connect(TestCtx* ctx)
537 SynInfo syn = WINPR_C_ARRAY_INIT;
538 if (!ctx_start(ctx, TRUE, &syn))
540 if (!send_syn_ack(ctx, syn.initialSequence, RDPUDP_PROTOCOL_VERSION_3))
542 for (
size_t x = 0; x < 100; x++)
546 if (rdpudp_get_state(ctx->udp) == RDPUDP_STATE_ESTABLISHED)
547 return rdpudp_get_version(ctx->udp) == RDPUDP_PROTOCOL_VERSION_3;
555static BOOL stream_ahead(TestCtx* ctx, UINT16* dataSeq, UINT16* channel,
size_t count)
559 const size_t batch = (count > 64) ? 64 : count;
560 for (
size_t x = 0; x < batch; x++)
562 if (!send_data(ctx, *dataSeq, *channel,
"."))
567 if (!pump(ctx) || !drain(ctx, 0))
578static BOOL syn_offers_version_3_with_the_cookie_hash(
void)
581 TestCtx ctx = WINPR_C_ARRAY_INIT;
582 SynInfo syn = WINPR_C_ARRAY_INIT;
583 CHECK(ctx_start(&ctx, TRUE, &syn));
585 CHECK(syn.length == SYN_LENGTH);
586 CHECK((syn.flags & (FLAG_SYN | FLAG_SYNEX)) == (FLAG_SYN | FLAG_SYNEX));
587 CHECK(syn.version == RDPUDP_PROTOCOL_VERSION_3);
588 CHECK(memcmp(syn.cookie, cookieHash,
sizeof(cookieHash)) == 0);
595static BOOL syn_offers_version_2_without_a_cookie_hash(
void)
598 TestCtx ctx = WINPR_C_ARRAY_INIT;
599 SynInfo syn = WINPR_C_ARRAY_INIT;
600 CHECK(ctx_start(&ctx, FALSE, &syn));
601 CHECK(syn.length == SYN_LENGTH);
602 CHECK(syn.version == RDPUDP_PROTOCOL_VERSION_2);
613static BOOL dummy_packets_are_acknowledged_but_not_delivered(
void)
616 TestCtx ctx = WINPR_C_ARRAY_INIT;
617 CHECK(ctx_connect(&ctx));
619 const V3Out dummy = { .dummy = TRUE,
621 .dataSeq = FIRST_SEQUENCE,
623 .payload = (
const BYTE*)
"junk",
624 .payloadLength = 4 };
625 CHECK(server_send_v3(&ctx, &dummy));
627 CHECK(delivered_is(&ctx,
""));
630 CHECK(send_data(&ctx, FIRST_SEQUENCE + 1, 1,
"payload"));
632 CHECK(delivered_is(&ctx,
"payload"));
634 CHECK(drain(&ctx, 20));
635 const V3Packet* ack = last_ack(&ctx);
636 CHECK(ack && (ack->flags & V3_ACK));
637 CHECK(ack->ack == FIRST_SEQUENCE + 1);
647static BOOL retransmission_is_reordered_back_into_the_stream(
void)
650 TestCtx ctx = WINPR_C_ARRAY_INIT;
651 CHECK(ctx_connect(&ctx));
652 CHECK(send_data(&ctx, 100, 1,
"a"));
653 CHECK(send_data(&ctx, 101, 3,
"c"));
654 CHECK(send_data(&ctx, 102, 4,
"d"));
656 CHECK(delivered_is(&ctx,
"a"));
658 CHECK(send_data(&ctx, 103, 2,
"b"));
660 CHECK(delivered_is(&ctx,
"abcd"));
669static BOOL ack_of_acks_advances_the_window_without_touching_the_stream(
void)
672 TestCtx ctx = WINPR_C_ARRAY_INIT;
673 CHECK(ctx_connect(&ctx));
674 CHECK(send_data(&ctx, 100, 1,
"a"));
676 CHECK(send_data(&ctx, 102, 2,
"b"));
678 CHECK(delivered_is(&ctx,
"ab"));
679 CHECK(drain(&ctx, 20));
680 const V3Packet* ack = last_ack(&ctx);
681 CHECK(ack && (ack->flags & V3_ACKVEC) && (ack->vecBase == 101));
683 CHECK(send_aoa(&ctx, 102));
685 CHECK(drain(&ctx, 20));
686 ack = last_ack(&ctx);
687 CHECK(ack && (ack->flags & V3_ACK));
688 CHECK(ack->ack == 102);
689 CHECK(delivered_is(&ctx,
"ab"));
696static BOOL send_data_aoa(TestCtx* ctx, UINT16 dataSeq, UINT16 channelSeq,
const char* text,
699 const V3Out out = { .hasAoA = TRUE,
703 .channelSeq = channelSeq,
704 .payload = (
const BYTE*)text,
705 .payloadLength = strlen(text) };
706 return server_send_v3(ctx, &out);
712static BOOL ack_of_acks_past_its_own_packet_keeps_the_chunk(
void)
715 TestCtx ctx = WINPR_C_ARRAY_INIT;
716 CHECK(ctx_connect(&ctx));
717 CHECK(send_data(&ctx, 100, 1,
"a"));
718 CHECK(send_data_aoa(&ctx, 101, 2,
"b", 102));
719 CHECK(send_data_aoa(&ctx, 102, 3,
"c", 102));
721 CHECK(delivered_is(&ctx,
"abc"));
722 CHECK(drain(&ctx, 20));
723 const V3Packet* ack = last_ack(&ctx);
724 CHECK(ack && (ack->flags & V3_ACK) && (ack->ack == 102));
734static BOOL ack_of_acks_past_its_own_packet_gets_no_ack_vector(
void)
737 TestCtx ctx = WINPR_C_ARRAY_INIT;
738 CHECK(ctx_connect(&ctx));
739 CHECK(send_data(&ctx, 100, 1,
"a"));
741 CHECK(drain(&ctx, 20));
743 CHECK(send_data_aoa(&ctx, 101, 2,
"b", 102));
744 CHECK(send_data_aoa(&ctx, 102, 3,
"c", 102));
746 CHECK(delivered_is(&ctx,
"abc"));
747 CHECK(drain(&ctx, 20));
748 for (
size_t x = 0; x < ctx.seenCount; x++)
749 CHECK(!(ctx.seen[x].flags & V3_ACKVEC));
750 const V3Packet* ack = last_ack(&ctx);
751 CHECK(ack && (ack->flags & V3_ACK) && (ack->ack == 102));
760static BOOL ack_of_acks_far_past_its_own_packet_keeps_the_chunk(
void)
763 TestCtx ctx = WINPR_C_ARRAY_INIT;
764 CHECK(ctx_connect(&ctx));
765 CHECK(send_data(&ctx, 100, 1,
"a"));
766 CHECK(send_data_aoa(&ctx, 101, 2,
"b", 104));
767 CHECK(send_data(&ctx, 104, 3,
"c"));
769 CHECK(delivered_is(&ctx,
"abc"));
770 CHECK(drain(&ctx, 20));
771 const V3Packet* ack = last_ack(&ctx);
772 CHECK(ack && (ack->flags & V3_ACK) && (ack->ack == 104));
781static BOOL late_packet_behind_the_window_fills_the_hole(
void)
784 TestCtx ctx = WINPR_C_ARRAY_INIT;
785 CHECK(ctx_connect(&ctx));
786 CHECK(send_data(&ctx, 100, 1,
"a"));
788 CHECK(send_data(&ctx, 102, 3,
"c"));
789 CHECK(send_aoa(&ctx, 102));
791 CHECK(delivered_is(&ctx,
"a"));
793 CHECK(send_data(&ctx, 101, 2,
"b"));
795 CHECK(delivered_is(&ctx,
"abc"));
798 CHECK(send_data(&ctx, 103, 2,
"b"));
799 CHECK(send_data(&ctx, 104, 4,
"d"));
801 CHECK(delivered_is(&ctx,
"abcd"));
810static BOOL retransmitted_packets_are_acknowledged_again(
void)
813 TestCtx ctx = WINPR_C_ARRAY_INIT;
814 CHECK(ctx_connect(&ctx));
815 CHECK(send_data(&ctx, 100, 1,
"first"));
817 CHECK(drain(&ctx, 20));
818 CHECK(last_ack(&ctx));
820 CHECK(send_data(&ctx, 100, 1,
"first"));
822 CHECK(delivered_is(&ctx,
"first"));
823 CHECK(drain(&ctx, 20));
825 const V3Packet* ack = last_ack(&ctx);
826 CHECK(ack && (ack->flags & V3_ACKVEC) && (ack->vecBase == 100) && (ack->vecLength == 1) &&
827 (ack->vec[0] == (0x80 | 0x40 | 1)));
837static BOOL ack_vector_is_not_lost_to_outgoing_data(
void)
840 TestCtx ctx = WINPR_C_ARRAY_INIT;
841 CHECK(ctx_connect(&ctx));
843 const V3Out first = { .hasDelayAck = TRUE,
845 .delayedAckTimeout = 200,
849 .payload = (
const BYTE*)
"first",
850 .payloadLength = 5 };
851 CHECK(server_send_v3(&ctx, &first));
855 CHECK(drain(&ctx, 20));
856 CHECK(last_ack(&ctx));
859 CHECK(send_data(&ctx, 100, 1,
"first"));
861 CHECK(rdpudp_send(ctx.udp, (
const BYTE*)
"x", 1));
862 CHECK(drain(&ctx, 20));
863 const V3Packet* sent = first_data(&ctx);
864 CHECK(sent && !(sent->flags & V3_ACK));
869 CHECK(drain(&ctx, 20));
871 for (
size_t x = 0; x < ctx.seenCount; x++)
873 const V3Packet* p = &ctx.seen[x];
874 if ((p->flags & V3_ACKVEC) && (p->vecBase == 100) && (p->vecLength == 1) &&
875 (p->vec[0] == (0x80 | 0x40 | 1)))
879 CHECK(delivered_is(&ctx,
"first"));
888static BOOL retransmitted_channel_data_is_not_delivered_twice(
void)
891 TestCtx ctx = WINPR_C_ARRAY_INIT;
892 CHECK(ctx_connect(&ctx));
893 CHECK(send_data(&ctx, 100, 1,
"a"));
894 CHECK(send_data(&ctx, 101, 2,
"b"));
895 CHECK(send_data(&ctx, 102, 2,
"b"));
896 CHECK(send_data(&ctx, 103, 3,
"c"));
898 CHECK(delivered_is(&ctx,
"abc"));
905static BOOL gap_produces_an_ack_vector_rather_than_a_cumulative_ack(
void)
908 TestCtx ctx = WINPR_C_ARRAY_INIT;
909 CHECK(ctx_connect(&ctx));
910 CHECK(send_data(&ctx, 100, 1,
"a"));
911 CHECK(send_data(&ctx, 102, 3,
"c"));
913 CHECK(drain(&ctx, 20));
914 const V3Packet* ack = last_ack(&ctx);
916 CHECK((ack->flags & V3_ACKVEC) && !(ack->flags & V3_ACK));
917 CHECK(ack->vecBase == 101);
919 CHECK((ack->vecLength == 1) && (ack->vec[0] == 0x02));
926static BOOL ack_advertises_the_largest_window(
void)
929 TestCtx ctx = WINPR_C_ARRAY_INIT;
930 CHECK(ctx_connect(&ctx));
931 CHECK(send_data(&ctx, 100, 1,
"a"));
933 CHECK(drain(&ctx, 20));
934 const V3Packet* ack = last_ack(&ctx);
935 CHECK(ack && (ack->flags & V3_ACK) && (ack->ack == 100));
937 CHECK(ack->logWindow == 15);
946static BOOL channel_sequence_wraps_past_zero(
void)
949 TestCtx ctx = WINPR_C_ARRAY_INIT;
950 CHECK(ctx_connect(&ctx));
951 UINT16 dataSeq = FIRST_SEQUENCE;
953 CHECK(stream_ahead(&ctx, &dataSeq, &channel, 65534));
954 CHECK(channel == 65535);
955 const size_t before = Stream_GetPosition(ctx.delivered);
956 CHECK(before == 65534);
958 CHECK(send_data(&ctx, dataSeq, 65535,
"f"));
959 CHECK(send_data(&ctx, (UINT16)(dataSeq + 1), 1,
"a"));
961 CHECK(delivered_tail_is(&ctx, before,
"fa"));
963 CHECK(send_data(&ctx, (UINT16)(dataSeq + 2), 2,
"b"));
965 CHECK(delivered_tail_is(&ctx, before,
"fab"));
974static BOOL real_server_ack_with_delayed_acks_and_data(
void)
976 static const BYTE raw[] = { 0x00, 0x45, 0xf0, 0x66, 0x00, 0x03, 0x22, 0xe0,
977 0x00, 0x01, 0x54, 0x08, 0xe0, 0x00, 0x02, 0x00,
978 0x17, 0x03, 0x03, 0x00, 0x62, 0x56, 0x2c, 0x9f };
980 TestCtx ctx = WINPR_C_ARRAY_INIT;
981 CHECK(ctx_connect(&ctx));
983 CHECK(send_data(&ctx, 223, 1,
"a"));
984 CHECK(server_send_raw(&ctx, raw,
sizeof(raw)));
986 CHECK(Stream_GetPosition(ctx.delivered) > 6);
987 CHECK(memcmp(Stream_Buffer(ctx.delivered),
"a\x17\x03\x03\x00\x62", 6) == 0);
1001static BOOL ack_of_acks_reports_the_oldest_unacknowledged_packet(
void)
1004 TestCtx ctx = WINPR_C_ARRAY_INIT;
1005 CHECK(ctx_connect(&ctx));
1006 CHECK(rdpudp_send(ctx.udp, (
const BYTE*)
"1", 1));
1007 CHECK(drain(&ctx, 20));
1008 const V3Packet* sent = first_data(&ctx);
1009 CHECK(sent && sent->hasAoA && (sent->aoa == FIRST_SEQUENCE));
1011 CHECK(rdpudp_send(ctx.udp, (
const BYTE*)
"2", 1));
1012 CHECK(drain(&ctx, 20));
1013 sent = first_data(&ctx);
1014 CHECK(sent && !sent->hasAoA);
1016 CHECK(send_ack(&ctx, FIRST_SEQUENCE));
1018 CHECK(rdpudp_send(ctx.udp, (
const BYTE*)
"3", 1));
1019 CHECK(drain(&ctx, 20));
1020 sent = first_data(&ctx);
1021 CHECK(sent && sent->hasAoA && (sent->aoa == FIRST_SEQUENCE + 1));
1030static BOOL acknowledgements_survive_sequence_wraparound(
void)
1033 TestCtx ctx = WINPR_C_ARRAY_INIT;
1034 CHECK(ctx_connect(&ctx));
1037 UINT16 next = FIRST_SEQUENCE;
1038 while (next != 0xFFFE)
1040 const size_t batch = ((size_t)(0xFFFE - next) > 64) ? 64 : (size_t)(0xFFFE - next);
1041 for (
size_t x = 0; x < batch; x++)
1042 CHECK(rdpudp_send(ctx.udp, (
const BYTE*)
".", 1));
1043 CHECK(collect_data(&ctx, batch, 1000));
1044 next = (UINT16)(next + batch);
1045 CHECK(send_ack(&ctx, (UINT16)(next - 1)));
1050 for (
size_t x = 0; x < 4; x++)
1051 CHECK(rdpudp_send(ctx.udp, (
const BYTE*)
"w", 1));
1052 CHECK(collect_data(&ctx, 4, 1000));
1053 CHECK(send_ack(&ctx, 0x0000));
1059 CHECK(drain(&ctx, 20));
1060 CHECK(count_data(&ctx) == 1);
1061 const V3Packet* resent = first_data(&ctx);
1062 CHECK(resent && (resent->channelSeq == 0xFFFE - FIRST_SEQUENCE + 4));
1072static BOOL ack_of_acks_resyncs_after_a_silence(
void)
1075 TestCtx ctx = WINPR_C_ARRAY_INIT;
1076 CHECK(ctx_connect(&ctx));
1077 CHECK(send_data(&ctx, FIRST_SEQUENCE, 1,
"a"));
1079 CHECK(drain(&ctx, 20));
1080 const V3Packet* ack = last_ack(&ctx);
1081 CHECK(ack && (ack->flags & V3_ACK) && (ack->ack == FIRST_SEQUENCE));
1086 CHECK(drain(&ctx, 20));
1087 CHECK(last_ack(&ctx) ==
nullptr);
1091 const V3Out unannounced = { .dummy = TRUE,
1093 .dataSeq = FIRST_SEQUENCE + 1,
1094 .payload = (
const BYTE*)
"x",
1095 .payloadLength = 1 };
1096 CHECK(server_send_v3(&ctx, &unannounced));
1099 CHECK(rdpudp_get_timeout(ctx.udp) > 0);
1100 CHECK(drain(&ctx, 20));
1101 CHECK(last_ack(&ctx) ==
nullptr);
1103 const UINT16 ahead = (UINT16)(FIRST_SEQUENCE + 45000);
1104 const V3Out out = { .hasAoA = TRUE,
1109 .payload = (
const BYTE*)
"b",
1110 .payloadLength = 1 };
1111 CHECK(server_send_v3(&ctx, &out));
1113 CHECK(delivered_is(&ctx,
"ab"));
1114 CHECK(drain(&ctx, 20));
1115 ack = last_ack(&ctx);
1116 CHECK(ack && (ack->flags & V3_ACK) && (ack->ack == ahead));
1127static BOOL packets_past_a_wrap_are_acknowledged_on_their_own(
void)
1130 TestCtx ctx = WINPR_C_ARRAY_INIT;
1131 CHECK(ctx_connect(&ctx));
1132 CHECK(send_data(&ctx, FIRST_SEQUENCE, 1,
"a"));
1134 CHECK(drain(&ctx, 20));
1138 const UINT16 wrapped = (UINT16)(FIRST_SEQUENCE + 1 + 42530);
1139 const size_t total = 100;
1140 char expected[80] =
"a";
1141 for (
size_t x = 0; x < total; x++)
1143 const UINT16 seq = (UINT16)(wrapped + x);
1146 const char text[2] = { (char)(
'A' + (x % 26)),
'\0' };
1147 expected[x + 1] = text[0];
1148 CHECK(send_data_aoa(&ctx, seq, (UINT16)(2 + x), text, (UINT16)(wrapped - 6976)));
1152 const V3Out dummy = { .dummy = TRUE,
1156 .payload = (
const BYTE*)
"x",
1157 .payloadLength = 1 };
1158 CHECK(server_send_v3(&ctx, &dummy));
1164 CHECK(delivered_is(&ctx, expected));
1167 CHECK(drain(&ctx, 50));
1168 BOOL reported[100] = WINPR_C_ARRAY_INIT;
1170 for (
size_t x = 0; x < ctx.seenCount; x++)
1172 const V3Packet* p = &ctx.seen[x];
1173 CHECK(!(p->flags & V3_ACK) || (p->ack != FIRST_SEQUENCE));
1174 if (!(p->flags & V3_ACKVEC))
1177 size_t pos = (UINT16)(p->vecBase - wrapped);
1178 for (
size_t y = 0; y < p->vecLength; y++)
1181 CHECK((p->vec[y] & 0xC0) == 0xC0);
1182 for (
size_t z = 0; z < (p->vec[y] & 0x3F); z++)
1185 reported[pos++] = TRUE;
1189 for (
size_t x = 0; x < total; x++)
1192 CHECK(vectors < total / 4);
1201static BOOL piggybacked_ack_is_not_repeated_after_a_jump(
void)
1204 TestCtx ctx = WINPR_C_ARRAY_INIT;
1205 CHECK(ctx_connect(&ctx));
1207 const V3Out first = { .hasDelayAck = TRUE,
1208 .maxDelayedAcks = 8,
1209 .delayedAckTimeout = 200,
1211 .dataSeq = FIRST_SEQUENCE,
1213 .payload = (
const BYTE*)
"a",
1214 .payloadLength = 1 };
1215 CHECK(server_send_v3(&ctx, &first));
1217 CHECK(rdpudp_send(ctx.udp, (
const BYTE*)
"x", 1));
1218 CHECK(drain(&ctx, 20));
1219 const V3Packet* sent = first_data(&ctx);
1220 CHECK(sent && (sent->flags & V3_ACK) && (sent->ack == FIRST_SEQUENCE));
1221 CHECK(send_ack(&ctx, sent->dataSeq));
1224 const UINT16 wrapped = (UINT16)(FIRST_SEQUENCE + 1 + 42530);
1225 for (
size_t x = 0; x < 10; x++)
1226 CHECK(send_data_aoa(&ctx, (UINT16)(wrapped + x), (UINT16)(2 + x),
"b",
1227 (UINT16)(wrapped - 6976)));
1231 CHECK(drain(&ctx, 20));
1233 BOOL vector = FALSE;
1234 for (
size_t x = 0; x < ctx.seenCount; x++)
1236 const V3Packet* p = &ctx.seen[x];
1237 CHECK(!(p->flags & V3_ACK) || (p->ack != FIRST_SEQUENCE));
1238 if (p->flags & V3_ACKVEC)
1256static const TestCase cases[] = {
1257 {
"syn_offers_version_3_with_the_cookie_hash", syn_offers_version_3_with_the_cookie_hash },
1258 {
"syn_offers_version_2_without_a_cookie_hash", syn_offers_version_2_without_a_cookie_hash },
1259 {
"dummy_packets_are_acknowledged_but_not_delivered",
1260 dummy_packets_are_acknowledged_but_not_delivered },
1261 {
"retransmission_is_reordered_back_into_the_stream",
1262 retransmission_is_reordered_back_into_the_stream },
1263 {
"ack_of_acks_advances_the_window_without_touching_the_stream",
1264 ack_of_acks_advances_the_window_without_touching_the_stream },
1265 {
"ack_of_acks_past_its_own_packet_keeps_the_chunk",
1266 ack_of_acks_past_its_own_packet_keeps_the_chunk },
1267 {
"ack_of_acks_past_its_own_packet_gets_no_ack_vector",
1268 ack_of_acks_past_its_own_packet_gets_no_ack_vector },
1269 {
"ack_of_acks_far_past_its_own_packet_keeps_the_chunk",
1270 ack_of_acks_far_past_its_own_packet_keeps_the_chunk },
1271 {
"late_packet_behind_the_window_fills_the_hole",
1272 late_packet_behind_the_window_fills_the_hole },
1273 {
"retransmitted_packets_are_acknowledged_again",
1274 retransmitted_packets_are_acknowledged_again },
1275 {
"ack_vector_is_not_lost_to_outgoing_data", ack_vector_is_not_lost_to_outgoing_data },
1276 {
"retransmitted_channel_data_is_not_delivered_twice",
1277 retransmitted_channel_data_is_not_delivered_twice },
1278 {
"gap_produces_an_ack_vector_rather_than_a_cumulative_ack",
1279 gap_produces_an_ack_vector_rather_than_a_cumulative_ack },
1280 {
"ack_advertises_the_largest_window", ack_advertises_the_largest_window },
1281 {
"channel_sequence_wraps_past_zero", channel_sequence_wraps_past_zero },
1282 {
"real_server_ack_with_delayed_acks_and_data", real_server_ack_with_delayed_acks_and_data },
1283 {
"ack_of_acks_reports_the_oldest_unacknowledged_packet",
1284 ack_of_acks_reports_the_oldest_unacknowledged_packet },
1285 {
"acknowledgements_survive_sequence_wraparound",
1286 acknowledgements_survive_sequence_wraparound },
1287 {
"ack_of_acks_resyncs_after_a_silence", ack_of_acks_resyncs_after_a_silence },
1288 {
"packets_past_a_wrap_are_acknowledged_on_their_own",
1289 packets_past_a_wrap_are_acknowledged_on_their_own },
1290 {
"piggybacked_ack_is_not_repeated_after_a_jump",
1291 piggybacked_ack_is_not_repeated_after_a_jump },
1294int TestRdpUdp(
int argc,
char* argv[])
1299 WSADATA wsaData = WINPR_C_ARRAY_INIT;
1300 if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
1304 for (
size_t x = 0; x < ARRAYSIZE(cases); x++)
1306 const BOOL ok = cases[x].fn();
1307 (void)printf(
"%s %s\n", ok ?
"ok " :
"FAILED", cases[x].name);