mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-11-12 14:57:20 +00:00
731 lines
26 KiB
C
731 lines
26 KiB
C
/**
|
|
* Looking Glass
|
|
* Copyright © 2017-2026 The Looking Glass Authors
|
|
* https://looking-glass.io
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License as published by the Free
|
|
* Software Foundation; either version 2 of the License, or (at your option)
|
|
* any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
* more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
|
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
#include "common/network/protocol.h"
|
|
|
|
#include <limits.h>
|
|
#include <string.h>
|
|
|
|
static const LGNetEnvelopeFlags ENVELOPE_FLAGS =
|
|
LG_NET_ENVELOPE_RESPONSE |
|
|
LG_NET_ENVELOPE_FINAL |
|
|
LG_NET_ENVELOPE_RELIABLE |
|
|
LG_NET_ENVELOPE_DATAGRAM |
|
|
LG_NET_ENVELOPE_ACK_REQUIRED |
|
|
LG_NET_ENVELOPE_ERROR |
|
|
LG_NET_ENVELOPE_EARLY_DATA;
|
|
|
|
static const LGNetCapabilityFlags CAPABILITY_FLAGS =
|
|
LG_NET_CAPABILITY_REQUIRED |
|
|
LG_NET_CAPABILITY_SELECTED |
|
|
LG_NET_CAPABILITY_RELIABLE |
|
|
LG_NET_CAPABILITY_DATAGRAM |
|
|
LG_NET_CAPABILITY_BULK |
|
|
LG_NET_CAPABILITY_MULTI_CLIENT;
|
|
|
|
static bool envelopeValid(const LGNetEnvelope * envelope, bool encode)
|
|
{
|
|
if (!envelope || envelope->wireMajor != LG_NET_WIRE_VERSION_MAJOR ||
|
|
envelope->headerSize < LG_NET_ENVELOPE_WIRE_SIZE ||
|
|
(encode && envelope->wireMinor != LG_NET_WIRE_VERSION_MINOR) ||
|
|
(encode && envelope->headerSize != LG_NET_ENVELOPE_WIRE_SIZE) ||
|
|
!envelope->service || !envelope->messageType ||
|
|
!envelope->serviceVersion || !envelope->messageVersion ||
|
|
envelope->payloadLength > LG_NET_MAX_PAYLOAD_LENGTH)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool authModeKnown(LGNetAuthMode mode)
|
|
{
|
|
return mode == LG_NET_AUTH_NONE || mode == LG_NET_AUTH_PASSWORD;
|
|
}
|
|
|
|
static bool passwordAlgorithmKnown(LGNetPasswordAlgorithm algorithm)
|
|
{
|
|
return algorithm == LG_NET_PASSWORD_PBKDF2_HMAC_SHA256;
|
|
}
|
|
|
|
static LGNetParseResult fixedDecodeResult(
|
|
const LGNetReader * reader, size_t expected, size_t actual)
|
|
{
|
|
if (!reader || !lgNetReaderValid(reader))
|
|
return LG_NET_PARSE_INVALID_VALUE;
|
|
if (actual != expected || lgNetReaderConsumed(reader) != expected)
|
|
return LG_NET_PARSE_INVALID_LENGTH;
|
|
return LG_NET_PARSE_OK;
|
|
}
|
|
|
|
void lgNetEnvelopeInit(LGNetEnvelope * envelope, LGNetService service,
|
|
uint16_t messageType, uint16_t serviceVersion,
|
|
uint16_t messageVersion)
|
|
{
|
|
if (!envelope)
|
|
return;
|
|
|
|
memset(envelope, 0, sizeof(*envelope));
|
|
envelope->wireMajor = LG_NET_WIRE_VERSION_MAJOR;
|
|
envelope->wireMinor = LG_NET_WIRE_VERSION_MINOR;
|
|
envelope->headerSize = LG_NET_ENVELOPE_WIRE_SIZE;
|
|
envelope->service = service;
|
|
envelope->messageType = messageType;
|
|
envelope->serviceVersion = serviceVersion;
|
|
envelope->messageVersion = messageVersion;
|
|
}
|
|
|
|
bool lgNetEnvelopeEncode(void * data, size_t size,
|
|
const LGNetEnvelope * envelope)
|
|
{
|
|
if (!data || size < LG_NET_ENVELOPE_WIRE_SIZE ||
|
|
!envelopeValid(envelope, true) ||
|
|
envelope->flags & ~ENVELOPE_FLAGS)
|
|
return false;
|
|
|
|
LGNetWriter writer;
|
|
lgNetWriterInit(&writer, data, size);
|
|
return
|
|
lgNetWriterU32(&writer, LG_NET_WIRE_MAGIC) &&
|
|
lgNetWriterU16(&writer, envelope->wireMajor) &&
|
|
lgNetWriterU16(&writer, envelope->wireMinor) &&
|
|
lgNetWriterU16(&writer, envelope->headerSize) &&
|
|
lgNetWriterU16(&writer, envelope->service) &&
|
|
lgNetWriterU16(&writer, envelope->messageType) &&
|
|
lgNetWriterU16(&writer, envelope->serviceVersion) &&
|
|
lgNetWriterU16(&writer, envelope->messageVersion) &&
|
|
lgNetWriterU16(&writer, 0) &&
|
|
lgNetWriterU32(&writer, envelope->flags) &&
|
|
lgNetWriterU32(&writer, envelope->payloadLength) &&
|
|
lgNetWriterU32(&writer, 0) &&
|
|
lgNetWriterU64(&writer, envelope->sessionEpoch) &&
|
|
lgNetWriterU64(&writer, envelope->componentEpoch) &&
|
|
lgNetWriterU64(&writer, envelope->sequence) &&
|
|
lgNetWriterU64(&writer, envelope->requestID) &&
|
|
lgNetWriterSize(&writer) == LG_NET_ENVELOPE_WIRE_SIZE;
|
|
}
|
|
|
|
LGNetParseResult lgNetEnvelopeDecode(LGNetEnvelope * envelope,
|
|
const void * data, size_t size)
|
|
{
|
|
if (!envelope || !data)
|
|
return LG_NET_PARSE_INVALID_VALUE;
|
|
if (size < LG_NET_ENVELOPE_WIRE_SIZE)
|
|
return LG_NET_PARSE_TRUNCATED;
|
|
|
|
LGNetEnvelope decoded;
|
|
uint32_t magic;
|
|
LGNetReader reader;
|
|
memset(&decoded, 0, sizeof(decoded));
|
|
lgNetReaderInit(&reader, data, LG_NET_ENVELOPE_WIRE_SIZE);
|
|
if (!lgNetReaderU32(&reader, &magic) ||
|
|
!lgNetReaderU16(&reader, &decoded.wireMajor) ||
|
|
!lgNetReaderU16(&reader, &decoded.wireMinor) ||
|
|
!lgNetReaderU16(&reader, &decoded.headerSize) ||
|
|
!lgNetReaderU16(&reader, &decoded.service) ||
|
|
!lgNetReaderU16(&reader, &decoded.messageType) ||
|
|
!lgNetReaderU16(&reader, &decoded.serviceVersion) ||
|
|
!lgNetReaderU16(&reader, &decoded.messageVersion) ||
|
|
!lgNetReaderZero(&reader, 2) ||
|
|
!lgNetReaderU32(&reader, &decoded.flags) ||
|
|
!lgNetReaderU32(&reader, &decoded.payloadLength) ||
|
|
!lgNetReaderZero(&reader, 4) ||
|
|
!lgNetReaderU64(&reader, &decoded.sessionEpoch) ||
|
|
!lgNetReaderU64(&reader, &decoded.componentEpoch) ||
|
|
!lgNetReaderU64(&reader, &decoded.sequence) ||
|
|
!lgNetReaderU64(&reader, &decoded.requestID))
|
|
return LG_NET_PARSE_INVALID_HEADER;
|
|
|
|
if (magic != LG_NET_WIRE_MAGIC)
|
|
return LG_NET_PARSE_INVALID_MAGIC;
|
|
if (decoded.wireMajor != LG_NET_WIRE_VERSION_MAJOR)
|
|
return LG_NET_PARSE_INVALID_VERSION;
|
|
if (decoded.headerSize < LG_NET_ENVELOPE_WIRE_SIZE)
|
|
return LG_NET_PARSE_INVALID_HEADER;
|
|
if (decoded.headerSize > size)
|
|
return LG_NET_PARSE_TRUNCATED;
|
|
if (!envelopeValid(&decoded, false) || decoded.flags & ~ENVELOPE_FLAGS)
|
|
return LG_NET_PARSE_INVALID_VALUE;
|
|
|
|
*envelope = decoded;
|
|
return LG_NET_PARSE_OK;
|
|
}
|
|
|
|
bool lgNetPacketSize(const LGNetEnvelope * envelope, size_t * size)
|
|
{
|
|
if (!size || !envelopeValid(envelope, false) ||
|
|
envelope->headerSize > SIZE_MAX - envelope->payloadLength)
|
|
return false;
|
|
|
|
*size = (size_t)envelope->headerSize + envelope->payloadLength;
|
|
return true;
|
|
}
|
|
|
|
bool lgNetPacketEncode(void * data, size_t size,
|
|
const LGNetEnvelope * envelope, const void * payload)
|
|
{
|
|
uint8_t header[LG_NET_ENVELOPE_WIRE_SIZE];
|
|
size_t wireSize;
|
|
if (!data || !envelope || (envelope->payloadLength && !payload) ||
|
|
!lgNetPacketSize(envelope, &wireSize) || wireSize > size ||
|
|
!lgNetEnvelopeEncode(header, sizeof(header), envelope))
|
|
return false;
|
|
|
|
if (envelope->payloadLength)
|
|
memmove((uint8_t *)data + envelope->headerSize, payload,
|
|
envelope->payloadLength);
|
|
memcpy(data, header, sizeof(header));
|
|
return true;
|
|
}
|
|
|
|
LGNetParseResult lgNetPacketDecode(LGNetPacketView * packet,
|
|
const void * data, size_t size)
|
|
{
|
|
if (!packet)
|
|
return LG_NET_PARSE_INVALID_VALUE;
|
|
|
|
LGNetPacketView decoded;
|
|
memset(&decoded, 0, sizeof(decoded));
|
|
const LGNetParseResult result = lgNetEnvelopeDecode(
|
|
&decoded.envelope, data, size);
|
|
if (result != LG_NET_PARSE_OK)
|
|
return result;
|
|
if (!lgNetPacketSize(&decoded.envelope, &decoded.wireSize))
|
|
return LG_NET_PARSE_INVALID_LENGTH;
|
|
if (decoded.wireSize > size)
|
|
return LG_NET_PARSE_TRUNCATED;
|
|
|
|
decoded.payload = (const uint8_t *)data + decoded.envelope.headerSize;
|
|
decoded.payloadLength = decoded.envelope.payloadLength;
|
|
*packet = decoded;
|
|
return LG_NET_PARSE_OK;
|
|
}
|
|
|
|
bool lgNetServiceKnown(LGNetService service)
|
|
{
|
|
return service >= LG_NET_SERVICE_CORE && service <= LG_NET_SERVICE_USB;
|
|
}
|
|
|
|
bool lgNetMessageKnown(LGNetService service, uint16_t messageType)
|
|
{
|
|
if (!messageType)
|
|
return false;
|
|
|
|
switch (service)
|
|
{
|
|
case LG_NET_SERVICE_CORE:
|
|
return messageType <= LG_NET_CORE_MESSAGE_ERROR;
|
|
|
|
case LG_NET_SERVICE_RECOVERY:
|
|
return messageType <= LG_NET_RECOVERY_MESSAGE_STATUS;
|
|
|
|
case LG_NET_SERVICE_VIDEO:
|
|
return messageType <= LG_NET_VIDEO_MESSAGE_STATUS;
|
|
|
|
case LG_NET_SERVICE_CURSOR:
|
|
return messageType <= LG_NET_CURSOR_MESSAGE_STATUS;
|
|
|
|
case LG_NET_SERVICE_INPUT:
|
|
return messageType <= LG_NET_INPUT_MESSAGE_KEYBOARD_LEDS;
|
|
|
|
case LG_NET_SERVICE_AUDIO:
|
|
return messageType <= LG_NET_AUDIO_MESSAGE_STATE_ACK;
|
|
|
|
case LG_NET_SERVICE_CLIPBOARD:
|
|
return messageType <= LG_NET_CLIPBOARD_MESSAGE_STATUS;
|
|
|
|
case LG_NET_SERVICE_FILE:
|
|
return messageType <= LG_NET_FILE_MESSAGE_STATUS;
|
|
|
|
case LG_NET_SERVICE_USB:
|
|
return messageType <= LG_NET_USB_MESSAGE_STATUS;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void lgNetHelloInit(LGNetHello * hello, LGNetRole role)
|
|
{
|
|
if (!hello)
|
|
return;
|
|
|
|
memset(hello, 0, sizeof(*hello));
|
|
hello->role = role;
|
|
hello->protocolMin = LG_NET_PROTOCOL_VERSION_MIN;
|
|
hello->protocolMax = LG_NET_PROTOCOL_VERSION_CURRENT;
|
|
hello->authModes = LG_NET_AUTH_MODES_DEFAULT;
|
|
hello->maxPayloadLength = LG_NET_MAX_PAYLOAD_LENGTH;
|
|
}
|
|
|
|
bool lgNetHelloValid(const LGNetHello * hello)
|
|
{
|
|
return hello &&
|
|
(hello->role == LG_NET_ROLE_CLIENT || hello->role == LG_NET_ROLE_SERVER) &&
|
|
hello->protocolMin && hello->protocolMin <= hello->protocolMax &&
|
|
hello->authModes &&
|
|
hello->maxPayloadLength &&
|
|
hello->maxPayloadLength <= LG_NET_MAX_PAYLOAD_LENGTH &&
|
|
hello->maxDatagramPayload <= hello->maxPayloadLength &&
|
|
hello->capabilityCount <= LG_NET_MAX_CAPABILITY_RECORDS;
|
|
}
|
|
|
|
bool lgNetHelloEncode(void * data, size_t size, const LGNetHello * hello)
|
|
{
|
|
if (!data || size < LG_NET_HELLO_WIRE_SIZE || !lgNetHelloValid(hello))
|
|
return false;
|
|
|
|
LGNetWriter writer;
|
|
lgNetWriterInit(&writer, data, size);
|
|
return
|
|
lgNetWriterU16(&writer, hello->role) &&
|
|
lgNetWriterU16(&writer, hello->protocolMin) &&
|
|
lgNetWriterU16(&writer, hello->protocolMax) &&
|
|
lgNetWriterU16(&writer, 0) &&
|
|
lgNetWriterU32(&writer, hello->authModes) &&
|
|
lgNetWriterU32(&writer, hello->maxPayloadLength) &&
|
|
lgNetWriterU32(&writer, hello->maxDatagramPayload) &&
|
|
lgNetWriterU32(&writer, hello->capabilityCount) &&
|
|
lgNetWriterBytes(&writer, hello->nonce, sizeof(hello->nonce)) &&
|
|
lgNetWriterSize(&writer) == LG_NET_HELLO_WIRE_SIZE;
|
|
}
|
|
|
|
LGNetParseResult lgNetHelloDecode(LGNetHello * hello,
|
|
const void * data, size_t size)
|
|
{
|
|
if (!hello || !data)
|
|
return LG_NET_PARSE_INVALID_VALUE;
|
|
if (size < LG_NET_HELLO_WIRE_SIZE)
|
|
return LG_NET_PARSE_TRUNCATED;
|
|
|
|
LGNetHello decoded;
|
|
LGNetReader reader;
|
|
memset(&decoded, 0, sizeof(decoded));
|
|
lgNetReaderInit(&reader, data, size);
|
|
if (!lgNetReaderU16(&reader, &decoded.role) ||
|
|
!lgNetReaderU16(&reader, &decoded.protocolMin) ||
|
|
!lgNetReaderU16(&reader, &decoded.protocolMax) ||
|
|
!lgNetReaderZero(&reader, 2) ||
|
|
!lgNetReaderU32(&reader, &decoded.authModes) ||
|
|
!lgNetReaderU32(&reader, &decoded.maxPayloadLength) ||
|
|
!lgNetReaderU32(&reader, &decoded.maxDatagramPayload) ||
|
|
!lgNetReaderU32(&reader, &decoded.capabilityCount) ||
|
|
!lgNetReaderBytes(&reader, decoded.nonce,
|
|
sizeof(decoded.nonce)))
|
|
return LG_NET_PARSE_INVALID_VALUE;
|
|
|
|
const LGNetParseResult result = fixedDecodeResult(
|
|
&reader, LG_NET_HELLO_WIRE_SIZE, size);
|
|
if (result != LG_NET_PARSE_OK)
|
|
return result;
|
|
if (!lgNetHelloValid(&decoded))
|
|
return LG_NET_PARSE_INVALID_VALUE;
|
|
|
|
*hello = decoded;
|
|
return LG_NET_PARSE_OK;
|
|
}
|
|
|
|
void lgNetHelloAckInit(LGNetHelloAck * hello, uint64_t sessionEpoch)
|
|
{
|
|
if (!hello)
|
|
return;
|
|
|
|
memset(hello, 0, sizeof(*hello));
|
|
hello->protocolVersion = LG_NET_PROTOCOL_VERSION_CURRENT;
|
|
hello->authMode = LG_NET_AUTH_MODE_DEFAULT;
|
|
hello->maxPayloadLength = LG_NET_MAX_PAYLOAD_LENGTH;
|
|
hello->sessionEpoch = sessionEpoch;
|
|
}
|
|
|
|
bool lgNetHelloAckValid(const LGNetHelloAck * hello)
|
|
{
|
|
return hello &&
|
|
hello->protocolVersion >= LG_NET_PROTOCOL_VERSION_MIN &&
|
|
hello->protocolVersion <= LG_NET_PROTOCOL_VERSION_MAX &&
|
|
authModeKnown(hello->authMode) &&
|
|
!(hello->flags & ~LG_NET_HELLO_ACK_RESUMED) &&
|
|
hello->maxPayloadLength &&
|
|
hello->maxPayloadLength <= LG_NET_MAX_PAYLOAD_LENGTH &&
|
|
hello->maxDatagramPayload <= hello->maxPayloadLength &&
|
|
hello->capabilityCount <= LG_NET_MAX_CAPABILITY_RECORDS &&
|
|
hello->sessionEpoch;
|
|
}
|
|
|
|
bool lgNetHelloAckEncode(
|
|
void * data, size_t size, const LGNetHelloAck * hello)
|
|
{
|
|
if (!data || size < LG_NET_HELLO_ACK_WIRE_SIZE ||
|
|
!lgNetHelloAckValid(hello))
|
|
return false;
|
|
|
|
LGNetWriter writer;
|
|
lgNetWriterInit(&writer, data, size);
|
|
return
|
|
lgNetWriterU16(&writer, hello->protocolVersion) &&
|
|
lgNetWriterU16(&writer, hello->authMode) &&
|
|
lgNetWriterU32(&writer, hello->flags) &&
|
|
lgNetWriterU32(&writer, hello->maxPayloadLength) &&
|
|
lgNetWriterU32(&writer, hello->maxDatagramPayload) &&
|
|
lgNetWriterU32(&writer, hello->capabilityCount) &&
|
|
lgNetWriterU32(&writer, 0) &&
|
|
lgNetWriterU64(&writer, hello->sessionEpoch) &&
|
|
lgNetWriterBytes(&writer, hello->nonce, sizeof(hello->nonce)) &&
|
|
lgNetWriterSize(&writer) == LG_NET_HELLO_ACK_WIRE_SIZE;
|
|
}
|
|
|
|
LGNetParseResult lgNetHelloAckDecode(LGNetHelloAck * hello,
|
|
const void * data, size_t size)
|
|
{
|
|
if (!hello || !data)
|
|
return LG_NET_PARSE_INVALID_VALUE;
|
|
if (size < LG_NET_HELLO_ACK_WIRE_SIZE)
|
|
return LG_NET_PARSE_TRUNCATED;
|
|
|
|
LGNetHelloAck decoded;
|
|
LGNetReader reader;
|
|
memset(&decoded, 0, sizeof(decoded));
|
|
lgNetReaderInit(&reader, data, size);
|
|
if (!lgNetReaderU16(&reader, &decoded.protocolVersion) ||
|
|
!lgNetReaderU16(&reader, &decoded.authMode) ||
|
|
!lgNetReaderU32(&reader, &decoded.flags) ||
|
|
!lgNetReaderU32(&reader, &decoded.maxPayloadLength) ||
|
|
!lgNetReaderU32(&reader, &decoded.maxDatagramPayload) ||
|
|
!lgNetReaderU32(&reader, &decoded.capabilityCount) ||
|
|
!lgNetReaderZero(&reader, 4) ||
|
|
!lgNetReaderU64(&reader, &decoded.sessionEpoch) ||
|
|
!lgNetReaderBytes(&reader, decoded.nonce,
|
|
sizeof(decoded.nonce)))
|
|
return LG_NET_PARSE_INVALID_VALUE;
|
|
|
|
const LGNetParseResult result = fixedDecodeResult(
|
|
&reader, LG_NET_HELLO_ACK_WIRE_SIZE, size);
|
|
if (result != LG_NET_PARSE_OK)
|
|
return result;
|
|
if (!lgNetHelloAckValid(&decoded))
|
|
return LG_NET_PARSE_INVALID_VALUE;
|
|
|
|
*hello = decoded;
|
|
return LG_NET_PARSE_OK;
|
|
}
|
|
|
|
bool lgNetCapabilityValid(const LGNetCapability * capability)
|
|
{
|
|
if (!capability || !capability->service ||
|
|
!capability->versionMin ||
|
|
capability->versionMin > capability->versionMax ||
|
|
capability->flags & ~CAPABILITY_FLAGS ||
|
|
!capability->maxPayloadLength ||
|
|
capability->maxPayloadLength > LG_NET_MAX_PAYLOAD_LENGTH ||
|
|
capability->maxDatagramPayload > capability->maxPayloadLength)
|
|
return false;
|
|
|
|
if ((capability->flags & LG_NET_CAPABILITY_SELECTED) &&
|
|
capability->versionMin != capability->versionMax)
|
|
return false;
|
|
if (!!(capability->flags & LG_NET_CAPABILITY_DATAGRAM) !=
|
|
!!capability->maxDatagramPayload)
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
bool lgNetCapabilityEncode(void * data, size_t size,
|
|
const LGNetCapability * capability)
|
|
{
|
|
if (!data || size < LG_NET_CAPABILITY_WIRE_SIZE ||
|
|
!lgNetCapabilityValid(capability))
|
|
return false;
|
|
|
|
LGNetWriter writer;
|
|
lgNetWriterInit(&writer, data, size);
|
|
return
|
|
lgNetWriterU16(&writer, capability->service) &&
|
|
lgNetWriterU16(&writer, capability->versionMin) &&
|
|
lgNetWriterU16(&writer, capability->versionMax) &&
|
|
lgNetWriterU16(&writer, capability->flags) &&
|
|
lgNetWriterU64(&writer, capability->features) &&
|
|
lgNetWriterU32(&writer, capability->maxPayloadLength) &&
|
|
lgNetWriterU32(&writer, capability->maxDatagramPayload) &&
|
|
lgNetWriterU16(&writer, capability->maxBidirectionalStreams) &&
|
|
lgNetWriterU16(&writer, capability->maxUnidirectionalStreams) &&
|
|
lgNetWriterU32(&writer, 0) &&
|
|
lgNetWriterSize(&writer) == LG_NET_CAPABILITY_WIRE_SIZE;
|
|
}
|
|
|
|
LGNetParseResult lgNetCapabilityDecode(LGNetCapability * capability,
|
|
const void * data, size_t size)
|
|
{
|
|
if (!capability || !data)
|
|
return LG_NET_PARSE_INVALID_VALUE;
|
|
if (size < LG_NET_CAPABILITY_WIRE_SIZE)
|
|
return LG_NET_PARSE_TRUNCATED;
|
|
|
|
LGNetCapability decoded;
|
|
LGNetReader reader;
|
|
memset(&decoded, 0, sizeof(decoded));
|
|
lgNetReaderInit(&reader, data, size);
|
|
if (!lgNetReaderU16(&reader, &decoded.service) ||
|
|
!lgNetReaderU16(&reader, &decoded.versionMin) ||
|
|
!lgNetReaderU16(&reader, &decoded.versionMax) ||
|
|
!lgNetReaderU16(&reader, &decoded.flags) ||
|
|
!lgNetReaderU64(&reader, &decoded.features) ||
|
|
!lgNetReaderU32(&reader, &decoded.maxPayloadLength) ||
|
|
!lgNetReaderU32(&reader, &decoded.maxDatagramPayload) ||
|
|
!lgNetReaderU16(&reader, &decoded.maxBidirectionalStreams) ||
|
|
!lgNetReaderU16(&reader, &decoded.maxUnidirectionalStreams) ||
|
|
!lgNetReaderZero(&reader, 4))
|
|
return LG_NET_PARSE_INVALID_VALUE;
|
|
|
|
const LGNetParseResult result = fixedDecodeResult(
|
|
&reader, LG_NET_CAPABILITY_WIRE_SIZE, size);
|
|
if (result != LG_NET_PARSE_OK)
|
|
return result;
|
|
if (!lgNetCapabilityValid(&decoded))
|
|
return LG_NET_PARSE_INVALID_VALUE;
|
|
|
|
*capability = decoded;
|
|
return LG_NET_PARSE_OK;
|
|
}
|
|
|
|
size_t lgNetPasswordChallengeSize(
|
|
const LGNetPasswordChallenge * challenge)
|
|
{
|
|
if (!lgNetPasswordChallengeValid(challenge))
|
|
return 0;
|
|
return LG_NET_PASSWORD_CHALLENGE_HEADER_SIZE +
|
|
challenge->saltLength + challenge->challengeLength;
|
|
}
|
|
|
|
bool lgNetPasswordChallengeValid(
|
|
const LGNetPasswordChallenge * challenge)
|
|
{
|
|
return challenge && passwordAlgorithmKnown(challenge->algorithm) &&
|
|
challenge->rounds && challenge->saltLength &&
|
|
challenge->saltLength <= LG_NET_PASSWORD_SALT_MAX_LENGTH &&
|
|
challenge->challengeLength &&
|
|
challenge->challengeLength <= LG_NET_PASSWORD_CHALLENGE_MAX_LENGTH;
|
|
}
|
|
|
|
bool lgNetPasswordChallengeEncode(void * data, size_t size,
|
|
const LGNetPasswordChallenge * challenge)
|
|
{
|
|
const size_t required = lgNetPasswordChallengeSize(challenge);
|
|
if (!data || !required || size < required)
|
|
return false;
|
|
|
|
LGNetWriter writer;
|
|
lgNetWriterInit(&writer, data, size);
|
|
return
|
|
lgNetWriterU16(&writer, challenge->algorithm) &&
|
|
lgNetWriterU16(&writer, 0) &&
|
|
lgNetWriterU32(&writer, challenge->rounds) &&
|
|
lgNetWriterU16(&writer, challenge->saltLength) &&
|
|
lgNetWriterU16(&writer, challenge->challengeLength) &&
|
|
lgNetWriterU32(&writer, 0) &&
|
|
lgNetWriterBytes(&writer, challenge->salt,
|
|
challenge->saltLength) &&
|
|
lgNetWriterBytes(&writer, challenge->challenge,
|
|
challenge->challengeLength) &&
|
|
lgNetWriterSize(&writer) == required;
|
|
}
|
|
|
|
LGNetParseResult lgNetPasswordChallengeDecode(
|
|
LGNetPasswordChallenge * challenge, const void * data, size_t size)
|
|
{
|
|
if (!challenge || !data)
|
|
return LG_NET_PARSE_INVALID_VALUE;
|
|
if (size < LG_NET_PASSWORD_CHALLENGE_HEADER_SIZE)
|
|
return LG_NET_PARSE_TRUNCATED;
|
|
|
|
LGNetPasswordChallenge decoded;
|
|
LGNetReader reader;
|
|
memset(&decoded, 0, sizeof(decoded));
|
|
lgNetReaderInit(&reader, data, size);
|
|
if (!lgNetReaderU16(&reader, &decoded.algorithm) ||
|
|
!lgNetReaderZero(&reader, 2) ||
|
|
!lgNetReaderU32(&reader, &decoded.rounds) ||
|
|
!lgNetReaderU16(&reader, &decoded.saltLength) ||
|
|
!lgNetReaderU16(&reader, &decoded.challengeLength) ||
|
|
!lgNetReaderZero(&reader, 4))
|
|
return LG_NET_PARSE_INVALID_VALUE;
|
|
|
|
if (decoded.saltLength > LG_NET_PASSWORD_SALT_MAX_LENGTH ||
|
|
decoded.challengeLength > LG_NET_PASSWORD_CHALLENGE_MAX_LENGTH)
|
|
return LG_NET_PARSE_INVALID_LENGTH;
|
|
|
|
const size_t expected = LG_NET_PASSWORD_CHALLENGE_HEADER_SIZE +
|
|
decoded.saltLength + decoded.challengeLength;
|
|
if (size < expected)
|
|
return LG_NET_PARSE_TRUNCATED;
|
|
if (size != expected ||
|
|
!lgNetReaderBytes(&reader, decoded.salt, decoded.saltLength) ||
|
|
!lgNetReaderBytes(&reader, decoded.challenge,
|
|
decoded.challengeLength))
|
|
return LG_NET_PARSE_INVALID_LENGTH;
|
|
if (!lgNetPasswordChallengeValid(&decoded))
|
|
return LG_NET_PARSE_INVALID_VALUE;
|
|
|
|
*challenge = decoded;
|
|
return LG_NET_PARSE_OK;
|
|
}
|
|
|
|
size_t lgNetPasswordResponseSize(const LGNetPasswordResponse * response)
|
|
{
|
|
if (!lgNetPasswordResponseValid(response))
|
|
return 0;
|
|
return LG_NET_PASSWORD_RESPONSE_HEADER_SIZE +
|
|
response->nonceLength + response->proofLength;
|
|
}
|
|
|
|
bool lgNetPasswordResponseValid(const LGNetPasswordResponse * response)
|
|
{
|
|
return response && passwordAlgorithmKnown(response->algorithm) &&
|
|
response->nonceLength &&
|
|
response->nonceLength <= LG_NET_PASSWORD_NONCE_MAX_LENGTH &&
|
|
response->proofLength &&
|
|
response->proofLength <= LG_NET_PASSWORD_PROOF_MAX_LENGTH;
|
|
}
|
|
|
|
bool lgNetPasswordResponseEncode(void * data, size_t size,
|
|
const LGNetPasswordResponse * response)
|
|
{
|
|
const size_t required = lgNetPasswordResponseSize(response);
|
|
if (!data || !required || size < required)
|
|
return false;
|
|
|
|
LGNetWriter writer;
|
|
lgNetWriterInit(&writer, data, size);
|
|
return
|
|
lgNetWriterU16(&writer, response->algorithm) &&
|
|
lgNetWriterU16(&writer, response->nonceLength) &&
|
|
lgNetWriterU16(&writer, response->proofLength) &&
|
|
lgNetWriterU16(&writer, 0) &&
|
|
lgNetWriterU32(&writer, 0) &&
|
|
lgNetWriterBytes(&writer, response->nonce,
|
|
response->nonceLength) &&
|
|
lgNetWriterBytes(&writer, response->proof,
|
|
response->proofLength) &&
|
|
lgNetWriterSize(&writer) == required;
|
|
}
|
|
|
|
LGNetParseResult lgNetPasswordResponseDecode(
|
|
LGNetPasswordResponse * response, const void * data, size_t size)
|
|
{
|
|
if (!response || !data)
|
|
return LG_NET_PARSE_INVALID_VALUE;
|
|
if (size < LG_NET_PASSWORD_RESPONSE_HEADER_SIZE)
|
|
return LG_NET_PARSE_TRUNCATED;
|
|
|
|
LGNetPasswordResponse decoded;
|
|
LGNetReader reader;
|
|
memset(&decoded, 0, sizeof(decoded));
|
|
lgNetReaderInit(&reader, data, size);
|
|
if (!lgNetReaderU16(&reader, &decoded.algorithm) ||
|
|
!lgNetReaderU16(&reader, &decoded.nonceLength) ||
|
|
!lgNetReaderU16(&reader, &decoded.proofLength) ||
|
|
!lgNetReaderZero(&reader, 2) ||
|
|
!lgNetReaderZero(&reader, 4))
|
|
return LG_NET_PARSE_INVALID_VALUE;
|
|
|
|
if (decoded.nonceLength > LG_NET_PASSWORD_NONCE_MAX_LENGTH ||
|
|
decoded.proofLength > LG_NET_PASSWORD_PROOF_MAX_LENGTH)
|
|
return LG_NET_PARSE_INVALID_LENGTH;
|
|
|
|
const size_t expected = LG_NET_PASSWORD_RESPONSE_HEADER_SIZE +
|
|
decoded.nonceLength + decoded.proofLength;
|
|
if (size < expected)
|
|
return LG_NET_PARSE_TRUNCATED;
|
|
if (size != expected ||
|
|
!lgNetReaderBytes(&reader, decoded.nonce, decoded.nonceLength) ||
|
|
!lgNetReaderBytes(&reader, decoded.proof, decoded.proofLength))
|
|
return LG_NET_PARSE_INVALID_LENGTH;
|
|
if (!lgNetPasswordResponseValid(&decoded))
|
|
return LG_NET_PARSE_INVALID_VALUE;
|
|
|
|
*response = decoded;
|
|
return LG_NET_PARSE_OK;
|
|
}
|
|
|
|
size_t lgNetAuthResultSize(const LGNetAuthResult * result)
|
|
{
|
|
if (!lgNetAuthResultValid(result))
|
|
return 0;
|
|
return LG_NET_AUTH_RESULT_HEADER_SIZE + result->proofLength;
|
|
}
|
|
|
|
bool lgNetAuthResultValid(const LGNetAuthResult * result)
|
|
{
|
|
return result &&
|
|
result->result >= LG_NET_AUTH_RESULT_ACCEPTED &&
|
|
result->result <= LG_NET_AUTH_RESULT_ERROR &&
|
|
result->proofLength <= LG_NET_PASSWORD_PROOF_MAX_LENGTH;
|
|
}
|
|
|
|
bool lgNetAuthResultEncode(
|
|
void * data, size_t size, const LGNetAuthResult * result)
|
|
{
|
|
const size_t required = lgNetAuthResultSize(result);
|
|
if (!data || !required || size < required)
|
|
return false;
|
|
|
|
LGNetWriter writer;
|
|
lgNetWriterInit(&writer, data, size);
|
|
return
|
|
lgNetWriterU16(&writer, result->result) &&
|
|
lgNetWriterU16(&writer, result->proofLength) &&
|
|
lgNetWriterU32(&writer, result->retryAfterMs) &&
|
|
lgNetWriterU32(&writer, 0) &&
|
|
lgNetWriterBytes(&writer, result->proof,
|
|
result->proofLength) &&
|
|
lgNetWriterSize(&writer) == required;
|
|
}
|
|
|
|
LGNetParseResult lgNetAuthResultDecode(LGNetAuthResult * result,
|
|
const void * data, size_t size)
|
|
{
|
|
if (!result || !data)
|
|
return LG_NET_PARSE_INVALID_VALUE;
|
|
if (size < LG_NET_AUTH_RESULT_HEADER_SIZE)
|
|
return LG_NET_PARSE_TRUNCATED;
|
|
|
|
LGNetAuthResult decoded;
|
|
LGNetReader reader;
|
|
memset(&decoded, 0, sizeof(decoded));
|
|
lgNetReaderInit(&reader, data, size);
|
|
if (!lgNetReaderU16(&reader, &decoded.result) ||
|
|
!lgNetReaderU16(&reader, &decoded.proofLength) ||
|
|
!lgNetReaderU32(&reader, &decoded.retryAfterMs) ||
|
|
!lgNetReaderZero(&reader, 4))
|
|
return LG_NET_PARSE_INVALID_VALUE;
|
|
|
|
if (decoded.proofLength > LG_NET_PASSWORD_PROOF_MAX_LENGTH)
|
|
return LG_NET_PARSE_INVALID_LENGTH;
|
|
|
|
const size_t expected = LG_NET_AUTH_RESULT_HEADER_SIZE +
|
|
decoded.proofLength;
|
|
if (size < expected)
|
|
return LG_NET_PARSE_TRUNCATED;
|
|
if (size != expected ||
|
|
!lgNetReaderBytes(&reader, decoded.proof, decoded.proofLength))
|
|
return LG_NET_PARSE_INVALID_LENGTH;
|
|
if (!lgNetAuthResultValid(&decoded))
|
|
return LG_NET_PARSE_INVALID_VALUE;
|
|
|
|
*result = decoded;
|
|
return LG_NET_PARSE_OK;
|
|
}
|