KasmVNC/common/network/webudp/WuSctp.cpp

170 lines
6.5 KiB
C++
Raw Normal View History

2022-07-26 12:38:14 +02:00
#include "WuSctp.h"
#include <arpa/inet.h>
#include <stdio.h>
#include <string.h>
#include "CRC32.h"
#include "WuBufferOp.h"
#include "WuMath.h"
#include "WuNetwork.h"
int32_t ParseSctpPacket(const uint8_t* buf, size_t len, SctpPacket* packet,
SctpChunk* chunks, size_t maxChunks, size_t* nChunk) {
if (len < 16) {
return 0;
}
int32_t offset = ReadScalarSwapped(buf, &packet->sourcePort);
offset += ReadScalarSwapped(buf + offset, &packet->destionationPort);
offset += ReadScalarSwapped(buf + offset, &packet->verificationTag);
offset += ReadScalarSwapped(buf + offset, &packet->checkSum);
int32_t left = len - offset;
size_t chunkNum = 0;
while (left >= 4 && chunkNum < maxChunks) {
SctpChunk* chunk = &chunks[chunkNum++];
offset += ReadScalarSwapped(buf + offset, &chunk->type);
offset += ReadScalarSwapped(buf + offset, &chunk->flags);
offset += ReadScalarSwapped(buf + offset, &chunk->length);
*nChunk += 1;
if (chunk->type == Sctp_Data) {
auto* p = &chunk->as.data;
size_t chunkOffset = ReadScalarSwapped(buf + offset, &p->tsn);
chunkOffset +=
ReadScalarSwapped(buf + offset + chunkOffset, &p->streamId);
chunkOffset +=
ReadScalarSwapped(buf + offset + chunkOffset, &p->streamSeq);
chunkOffset += ReadScalarSwapped(buf + offset + chunkOffset, &p->protoId);
p->userDataLength = Max(int32_t(chunk->length) - 16, 0);
p->userData = buf + offset + chunkOffset;
} else if (chunk->type == Sctp_Sack) {
auto* sack = &chunk->as.sack;
size_t chunkOffset =
ReadScalarSwapped(buf + offset, &sack->cumulativeTsnAck);
chunkOffset +=
ReadScalarSwapped(buf + offset + chunkOffset, &sack->advRecvWindow);
chunkOffset +=
ReadScalarSwapped(buf + offset + chunkOffset, &sack->numGapAckBlocks);
ReadScalarSwapped(buf + offset + chunkOffset, &sack->numDupTsn);
} else if (chunk->type == Sctp_Heartbeat) {
auto* p = &chunk->as.heartbeat;
size_t chunkOffset = 2; // skip type
uint16_t heartbeatLen;
chunkOffset +=
ReadScalarSwapped(buf + offset + chunkOffset, &heartbeatLen);
p->heartbeatInfoLen = int32_t(heartbeatLen) - 4;
p->heartbeatInfo = buf + offset + chunkOffset;
} else if (chunk->type == Sctp_Init) {
size_t chunkOffset =
ReadScalarSwapped(buf + offset, &chunk->as.init.initiateTag);
chunkOffset += ReadScalarSwapped(buf + offset + chunkOffset,
&chunk->as.init.windowCredit);
chunkOffset += ReadScalarSwapped(buf + offset + chunkOffset,
&chunk->as.init.numOutboundStreams);
chunkOffset += ReadScalarSwapped(buf + offset + chunkOffset,
&chunk->as.init.numInboundStreams);
ReadScalarSwapped(buf + offset + chunkOffset, &chunk->as.init.initialTsn);
}
int32_t valueLength = chunk->length - 4;
int32_t pad = PadSize(valueLength, 4);
offset += valueLength + pad;
left = len - offset;
}
return 1;
}
size_t SerializeSctpPacket(const SctpPacket* packet, const SctpChunk* chunks,
size_t numChunks, uint8_t* dst, size_t dstLen) {
size_t offset = WriteScalar(dst, htons(packet->sourcePort));
offset += WriteScalar(dst + offset, htons(packet->destionationPort));
offset += WriteScalar(dst + offset, htonl(packet->verificationTag));
size_t crcOffset = offset;
offset += WriteScalar(dst + offset, uint32_t(0));
for (size_t i = 0; i < numChunks; i++) {
const SctpChunk* chunk = &chunks[i];
offset += WriteScalar(dst + offset, chunk->type);
offset += WriteScalar(dst + offset, chunk->flags);
offset += WriteScalar(dst + offset, htons(chunk->length));
switch (chunk->type) {
case Sctp_Data: {
auto* dc = &chunk->as.data;
offset += WriteScalar(dst + offset, htonl(dc->tsn));
offset += WriteScalar(dst + offset, htons(dc->streamId));
offset += WriteScalar(dst + offset, htons(dc->streamSeq));
offset += WriteScalar(dst + offset, htonl(dc->protoId));
memcpy(dst + offset, dc->userData, dc->userDataLength);
int32_t pad = PadSize(dc->userDataLength, 4);
offset += dc->userDataLength + pad;
break;
}
case Sctp_InitAck: {
offset += WriteScalar(dst + offset, htonl(chunk->as.init.initiateTag));
offset += WriteScalar(dst + offset, htonl(chunk->as.init.windowCredit));
offset +=
WriteScalar(dst + offset, htons(chunk->as.init.numOutboundStreams));
offset +=
WriteScalar(dst + offset, htons(chunk->as.init.numInboundStreams));
offset += WriteScalar(dst + offset, htonl(chunk->as.init.initialTsn));
offset += WriteScalar(dst + offset, htons(Sctp_StateCookie));
offset += WriteScalar(dst + offset, htons(8));
offset += WriteScalar(dst + offset, htonl(0xB00B1E5));
offset += WriteScalar(dst + offset, htons(Sctp_ForwardTsn));
offset += WriteScalar(dst + offset, htons(4));
break;
}
case Sctp_Sack: {
auto* sack = &chunk->as.sack;
offset += WriteScalar(dst + offset, htonl(sack->cumulativeTsnAck));
offset += WriteScalar(dst + offset, htonl(sack->advRecvWindow));
offset += WriteScalar(dst + offset, htons(sack->numGapAckBlocks));
offset += WriteScalar(dst + offset, htons(sack->numDupTsn));
break;
}
case Sctp_Heartbeat:
case Sctp_HeartbeatAck: {
auto* hb = &chunk->as.heartbeat;
offset += WriteScalar(dst + offset, htons(1));
offset += WriteScalar(dst + offset, htons(hb->heartbeatInfoLen + 4));
memcpy(dst + offset, hb->heartbeatInfo, hb->heartbeatInfoLen);
offset += hb->heartbeatInfoLen + PadSize(hb->heartbeatInfoLen, 4);
break;
}
case Sctp_Shutdown: {
auto* shutdown = &chunk->as.shutdown;
offset += WriteScalar(dst + offset, htonl(shutdown->cumulativeTsnAck));
break;
}
case SctpChunk_ForwardTsn: {
auto* forwardTsn = &chunk->as.forwardTsn;
offset +=
WriteScalar(dst + offset, htonl(forwardTsn->newCumulativeTsn));
break;
}
default:
break;
}
}
uint32_t crc = SctpCRC32(dst, offset);
WriteScalar(dst + crcOffset, htonl(crc));
return offset;
}
int32_t SctpDataChunkLength(int32_t userDataLength) {
return 16 + userDataLength;
}
int32_t SctpChunkLength(int32_t contentLength) { return 4 + contentLength; }