[common] network: define transport wire protocol

This commit is contained in:
Geoffrey McRae
2026-09-30 14:23:47 +10:00
parent 536516be19
commit 5ba1bda0c8
6 changed files with 1676 additions and 0 deletions

View File

@@ -45,6 +45,8 @@ set(COMMON_SOURCES
src/cpuinfo.c
src/debug.c
src/ll.c
src/network/protocol.c
src/network/wire.c
)
add_library(lg_common STATIC ${COMMON_SOURCES})

View File

@@ -0,0 +1,488 @@
/**
* 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
*/
#ifndef _H_LG_COMMON_NETWORK_PROTOCOL_
#define _H_LG_COMMON_NETWORK_PROTOCOL_
#include "common/network/wire.h"
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/* Encoded as the byte sequence "LGNW" by the little-endian wire writer. */
#define LG_NET_WIRE_MAGIC UINT32_C(0x574E474C)
#define LG_NET_WIRE_VERSION_MAJOR 1U
#define LG_NET_WIRE_VERSION_MINOR 0U
#define LG_NET_ENVELOPE_WIRE_SIZE 64U
#define LG_NET_MAX_PAYLOAD_LENGTH (64U * 1024U * 1024U)
#define LG_NET_MESSAGE_VERSION_CURRENT 1U
#define LG_NET_MESSAGE_VERSION_INITIAL 1U
#define LG_NET_PROTOCOL_VERSION_CURRENT 1U
#define LG_NET_PROTOCOL_VERSION_MIN 1U
#define LG_NET_PROTOCOL_VERSION_MAX LG_NET_PROTOCOL_VERSION_CURRENT
#define LG_NET_MAX_CAPABILITY_RECORDS 64U
#define LG_NET_HELLO_NONCE_LENGTH 32U
#define LG_NET_PASSWORD_SALT_MAX_LENGTH 64U
#define LG_NET_PASSWORD_CHALLENGE_MAX_LENGTH 64U
#define LG_NET_PASSWORD_NONCE_MAX_LENGTH 64U
#define LG_NET_PASSWORD_PROOF_MAX_LENGTH 64U
/* 19527 is 0x4c47 (ASCII "LG") and is currently IANA-unassigned. */
#define LG_NET_DEFAULT_PORT 19527U
#define LG_NET_ALPN "looking-glass/1"
#define LG_NET_ALPN_LENGTH (sizeof(LG_NET_ALPN) - 1U)
#define LG_NET_CORE_VERSION_CURRENT 1U
#define LG_NET_CORE_VERSION_MIN 1U
#define LG_NET_CORE_VERSION_MAX LG_NET_CORE_VERSION_CURRENT
#define LG_NET_RECOVERY_VERSION_CURRENT 1U
#define LG_NET_RECOVERY_VERSION_MIN 1U
#define LG_NET_RECOVERY_VERSION_MAX LG_NET_RECOVERY_VERSION_CURRENT
#define LG_NET_VIDEO_VERSION_CURRENT 1U
#define LG_NET_VIDEO_VERSION_MIN 1U
#define LG_NET_VIDEO_VERSION_MAX LG_NET_VIDEO_VERSION_CURRENT
#define LG_NET_CURSOR_VERSION_CURRENT 1U
#define LG_NET_CURSOR_VERSION_MIN 1U
#define LG_NET_CURSOR_VERSION_MAX LG_NET_CURSOR_VERSION_CURRENT
#define LG_NET_INPUT_VERSION_CURRENT 1U
#define LG_NET_INPUT_VERSION_MIN 1U
#define LG_NET_INPUT_VERSION_MAX LG_NET_INPUT_VERSION_CURRENT
#define LG_NET_AUDIO_VERSION_CURRENT 1U
#define LG_NET_AUDIO_VERSION_MIN 1U
#define LG_NET_AUDIO_VERSION_MAX LG_NET_AUDIO_VERSION_CURRENT
#define LG_NET_CLIPBOARD_VERSION_CURRENT 1U
#define LG_NET_CLIPBOARD_VERSION_MIN 1U
#define LG_NET_CLIPBOARD_VERSION_MAX LG_NET_CLIPBOARD_VERSION_CURRENT
#define LG_NET_FILE_VERSION_CURRENT 1U
#define LG_NET_FILE_VERSION_MIN 1U
#define LG_NET_FILE_VERSION_MAX LG_NET_FILE_VERSION_CURRENT
/* Reserved now so a future USB implementation cannot collide with a service. */
#define LG_NET_USB_VERSION_RESERVED 1U
typedef uint16_t LGNetService;
enum
{
LG_NET_SERVICE_CORE = 1,
LG_NET_SERVICE_RECOVERY = 2,
LG_NET_SERVICE_VIDEO = 3,
LG_NET_SERVICE_CURSOR = 4,
LG_NET_SERVICE_INPUT = 5,
LG_NET_SERVICE_AUDIO = 6,
LG_NET_SERVICE_CLIPBOARD = 7,
LG_NET_SERVICE_FILE = 8,
LG_NET_SERVICE_USB = 9,
};
typedef uint16_t LGNetCoreMessage;
enum
{
LG_NET_CORE_MESSAGE_HELLO = 1,
LG_NET_CORE_MESSAGE_HELLO_ACK = 2,
LG_NET_CORE_MESSAGE_AUTH_CHALLENGE = 3,
LG_NET_CORE_MESSAGE_AUTH_RESPONSE = 4,
LG_NET_CORE_MESSAGE_AUTH_RESULT = 5,
LG_NET_CORE_MESSAGE_CAPABILITY = 6,
LG_NET_CORE_MESSAGE_CAPABILITIES_DONE = 7,
LG_NET_CORE_MESSAGE_SESSION_INFO = 8,
LG_NET_CORE_MESSAGE_STATUS = 9,
LG_NET_CORE_MESSAGE_PING = 10,
LG_NET_CORE_MESSAGE_PONG = 11,
LG_NET_CORE_MESSAGE_GOODBYE = 12,
LG_NET_CORE_MESSAGE_ERROR = 13,
};
typedef uint16_t LGNetRecoveryMessage;
enum
{
LG_NET_RECOVERY_MESSAGE_GET_INFO = 1,
LG_NET_RECOVERY_MESSAGE_INFO = 2,
LG_NET_RECOVERY_MESSAGE_REQUEST = 3,
LG_NET_RECOVERY_MESSAGE_STATUS = 4,
};
typedef uint16_t LGNetVideoMessage;
enum
{
LG_NET_VIDEO_MESSAGE_SUBSCRIBE = 1,
LG_NET_VIDEO_MESSAGE_UNSUBSCRIBE = 2,
LG_NET_VIDEO_MESSAGE_STREAM_CONFIG = 3,
LG_NET_VIDEO_MESSAGE_FRAME = 4,
LG_NET_VIDEO_MESSAGE_FRAME_FRAGMENT = 5,
LG_NET_VIDEO_MESSAGE_KEYFRAME_REQUEST = 6,
LG_NET_VIDEO_MESSAGE_SCHEDULE = 7,
LG_NET_VIDEO_MESSAGE_FEEDBACK = 8,
LG_NET_VIDEO_MESSAGE_STATUS = 9,
};
typedef uint16_t LGNetCursorMessage;
enum
{
LG_NET_CURSOR_MESSAGE_STATE = 1,
LG_NET_CURSOR_MESSAGE_POSITION = 2,
LG_NET_CURSOR_MESSAGE_SHAPE = 3,
LG_NET_CURSOR_MESSAGE_TRANSFORM = 4,
LG_NET_CURSOR_MESSAGE_STATUS = 5,
};
typedef uint16_t LGNetInputMessage;
enum
{
LG_NET_INPUT_MESSAGE_CLAIM = 1,
LG_NET_INPUT_MESSAGE_CLAIM_RESULT = 2,
LG_NET_INPUT_MESSAGE_KEEPALIVE = 3,
LG_NET_INPUT_MESSAGE_RELEASE = 4,
LG_NET_INPUT_MESSAGE_RESET = 5,
LG_NET_INPUT_MESSAGE_MOUSE_RELATIVE = 6,
LG_NET_INPUT_MESSAGE_MOUSE_ABSOLUTE = 7,
LG_NET_INPUT_MESSAGE_KEYBOARD = 8,
LG_NET_INPUT_MESSAGE_STATUS = 9,
LG_NET_INPUT_MESSAGE_KEYBOARD_LEDS = 10,
};
typedef uint16_t LGNetAudioMessage;
enum
{
LG_NET_AUDIO_MESSAGE_SUBSCRIBE = 1,
LG_NET_AUDIO_MESSAGE_STATUS = 2,
LG_NET_AUDIO_MESSAGE_PLAYBACK_START = 3,
LG_NET_AUDIO_MESSAGE_PLAYBACK_STOP = 4,
LG_NET_AUDIO_MESSAGE_PLAYBACK_DATA = 5,
LG_NET_AUDIO_MESSAGE_PLAYBACK_VOLUME = 6,
LG_NET_AUDIO_MESSAGE_PLAYBACK_MUTE = 7,
LG_NET_AUDIO_MESSAGE_CAPTURE_START = 8,
LG_NET_AUDIO_MESSAGE_CAPTURE_STOP = 9,
LG_NET_AUDIO_MESSAGE_CAPTURE_DATA = 10,
LG_NET_AUDIO_MESSAGE_CAPTURE_VOLUME = 11,
LG_NET_AUDIO_MESSAGE_CAPTURE_MUTE = 12,
LG_NET_AUDIO_MESSAGE_CLOCK_FEEDBACK = 13,
LG_NET_AUDIO_MESSAGE_KEEPALIVE = 14,
LG_NET_AUDIO_MESSAGE_RELEASE = 15,
LG_NET_AUDIO_MESSAGE_STATE_BARRIER = 16,
LG_NET_AUDIO_MESSAGE_STATE_ACK = 17,
};
typedef uint16_t LGNetClipboardMessage;
enum
{
LG_NET_CLIPBOARD_MESSAGE_CLAIM = 1,
LG_NET_CLIPBOARD_MESSAGE_CLAIM_RESULT = 2,
LG_NET_CLIPBOARD_MESSAGE_KEEPALIVE = 3,
LG_NET_CLIPBOARD_MESSAGE_RELEASE = 4,
LG_NET_CLIPBOARD_MESSAGE_OFFER = 5,
LG_NET_CLIPBOARD_MESSAGE_CLEAR = 6,
LG_NET_CLIPBOARD_MESSAGE_REQUEST = 7,
LG_NET_CLIPBOARD_MESSAGE_DATA_BEGIN = 8,
LG_NET_CLIPBOARD_MESSAGE_DATA_CHUNK = 9,
LG_NET_CLIPBOARD_MESSAGE_DATA_END = 10,
LG_NET_CLIPBOARD_MESSAGE_DATA_READY = 11,
LG_NET_CLIPBOARD_MESSAGE_CANCEL = 12,
LG_NET_CLIPBOARD_MESSAGE_STATUS = 13,
};
typedef uint16_t LGNetFileMessage;
enum
{
LG_NET_FILE_MESSAGE_OFFER = 1,
LG_NET_FILE_MESSAGE_ACQUIRE = 2,
LG_NET_FILE_MESSAGE_ACQUIRED = 3,
LG_NET_FILE_MESSAGE_RELEASE = 4,
LG_NET_FILE_MESSAGE_REQUEST = 5,
LG_NET_FILE_MESSAGE_ENTRY = 6,
LG_NET_FILE_MESSAGE_DATA_BEGIN = 7,
LG_NET_FILE_MESSAGE_DATA_CHUNK = 8,
LG_NET_FILE_MESSAGE_DATA_END = 9,
LG_NET_FILE_MESSAGE_DATA_READY = 10,
LG_NET_FILE_MESSAGE_CANCEL = 11,
LG_NET_FILE_MESSAGE_STATUS = 12,
};
/* Message IDs are reserved for the future USB service; their payloads are not
* defined by this protocol version. */
typedef uint16_t LGNetUSBMessage;
enum
{
LG_NET_USB_MESSAGE_CAPABILITIES = 1,
LG_NET_USB_MESSAGE_DEVICE = 2,
LG_NET_USB_MESSAGE_CLAIM = 3,
LG_NET_USB_MESSAGE_CONTROL = 4,
LG_NET_USB_MESSAGE_ENDPOINT = 5,
LG_NET_USB_MESSAGE_CANCEL = 6,
LG_NET_USB_MESSAGE_RESET = 7,
LG_NET_USB_MESSAGE_STATUS = 8,
};
typedef uint32_t LGNetEnvelopeFlags;
enum
{
LG_NET_ENVELOPE_RESPONSE = 1U << 0,
LG_NET_ENVELOPE_FINAL = 1U << 1,
LG_NET_ENVELOPE_RELIABLE = 1U << 2,
LG_NET_ENVELOPE_DATAGRAM = 1U << 3,
LG_NET_ENVELOPE_ACK_REQUIRED = 1U << 4,
LG_NET_ENVELOPE_ERROR = 1U << 5,
LG_NET_ENVELOPE_EARLY_DATA = 1U << 6,
};
typedef struct LGNetEnvelope
{
uint16_t wireMajor;
uint16_t wireMinor;
uint16_t headerSize;
LGNetService service;
uint16_t messageType;
uint16_t serviceVersion;
uint16_t messageVersion;
LGNetEnvelopeFlags flags;
uint32_t payloadLength;
uint64_t sessionEpoch;
uint64_t componentEpoch;
uint64_t sequence;
uint64_t requestID;
}
LGNetEnvelope;
typedef struct LGNetPacketView
{
LGNetEnvelope envelope;
const uint8_t * payload;
size_t payloadLength;
size_t wireSize;
}
LGNetPacketView;
typedef uint16_t LGNetRole;
enum
{
LG_NET_ROLE_CLIENT = 1,
LG_NET_ROLE_SERVER = 2,
};
typedef uint16_t LGNetAuthMode;
enum
{
LG_NET_AUTH_NONE = 0,
LG_NET_AUTH_PASSWORD = 1,
};
#define LG_NET_AUTH_MODE_FLAG(mode) (UINT32_C(1) << (mode))
#define LG_NET_AUTH_MODE_DEFAULT LG_NET_AUTH_NONE
#define LG_NET_AUTH_MODES_DEFAULT LG_NET_AUTH_MODE_FLAG(LG_NET_AUTH_NONE)
#define LG_NET_AUTH_MODES_KNOWN \
(LG_NET_AUTH_MODE_FLAG(LG_NET_AUTH_NONE) | \
LG_NET_AUTH_MODE_FLAG(LG_NET_AUTH_PASSWORD))
typedef struct LGNetHello
{
LGNetRole role;
uint16_t protocolMin;
uint16_t protocolMax;
uint32_t authModes;
uint32_t maxPayloadLength;
uint32_t maxDatagramPayload;
uint32_t capabilityCount;
uint8_t nonce[LG_NET_HELLO_NONCE_LENGTH];
}
LGNetHello;
typedef uint32_t LGNetHelloAckFlags;
enum
{
LG_NET_HELLO_ACK_RESUMED = 1U << 0,
};
typedef struct LGNetHelloAck
{
uint16_t protocolVersion;
LGNetAuthMode authMode;
LGNetHelloAckFlags flags;
uint32_t maxPayloadLength;
uint32_t maxDatagramPayload;
uint32_t capabilityCount;
uint64_t sessionEpoch;
uint8_t nonce[LG_NET_HELLO_NONCE_LENGTH];
}
LGNetHelloAck;
#define LG_NET_HELLO_WIRE_SIZE 56U
#define LG_NET_HELLO_ACK_WIRE_SIZE 64U
typedef uint16_t LGNetCapabilityFlags;
enum
{
LG_NET_CAPABILITY_REQUIRED = 1U << 0,
LG_NET_CAPABILITY_SELECTED = 1U << 1,
LG_NET_CAPABILITY_RELIABLE = 1U << 2,
LG_NET_CAPABILITY_DATAGRAM = 1U << 3,
LG_NET_CAPABILITY_BULK = 1U << 4,
LG_NET_CAPABILITY_MULTI_CLIENT = 1U << 5,
};
typedef struct LGNetCapability
{
LGNetService service;
uint16_t versionMin;
uint16_t versionMax;
LGNetCapabilityFlags flags;
uint64_t features;
uint32_t maxPayloadLength;
uint32_t maxDatagramPayload;
uint16_t maxBidirectionalStreams;
uint16_t maxUnidirectionalStreams;
}
LGNetCapability;
#define LG_NET_CAPABILITY_WIRE_SIZE 32U
/* Password proofs are carried by this library but derived by the application.
* A password must never be sent on the wire. The negotiated algorithm defines
* the proof and transcript binding; cryptography is outside this wire codec. */
typedef uint16_t LGNetPasswordAlgorithm;
enum
{
LG_NET_PASSWORD_PBKDF2_HMAC_SHA256 = 1,
};
typedef struct LGNetPasswordChallenge
{
LGNetPasswordAlgorithm algorithm;
uint32_t rounds;
uint16_t saltLength;
uint16_t challengeLength;
uint8_t salt [LG_NET_PASSWORD_SALT_MAX_LENGTH];
uint8_t challenge[LG_NET_PASSWORD_CHALLENGE_MAX_LENGTH];
}
LGNetPasswordChallenge;
typedef struct LGNetPasswordResponse
{
LGNetPasswordAlgorithm algorithm;
uint16_t nonceLength;
uint16_t proofLength;
uint8_t nonce[LG_NET_PASSWORD_NONCE_MAX_LENGTH];
uint8_t proof[LG_NET_PASSWORD_PROOF_MAX_LENGTH];
}
LGNetPasswordResponse;
typedef uint16_t LGNetAuthResultCode;
enum
{
LG_NET_AUTH_RESULT_ACCEPTED = 1,
LG_NET_AUTH_RESULT_REJECTED = 2,
LG_NET_AUTH_RESULT_UNSUPPORTED = 3,
LG_NET_AUTH_RESULT_ERROR = 4,
};
typedef struct LGNetAuthResult
{
LGNetAuthResultCode result;
uint16_t proofLength;
uint32_t retryAfterMs;
uint8_t proof[LG_NET_PASSWORD_PROOF_MAX_LENGTH];
}
LGNetAuthResult;
#define LG_NET_PASSWORD_CHALLENGE_HEADER_SIZE 16U
#define LG_NET_PASSWORD_RESPONSE_HEADER_SIZE 12U
#define LG_NET_AUTH_RESULT_HEADER_SIZE 12U
void lgNetEnvelopeInit(LGNetEnvelope * envelope, LGNetService service,
uint16_t messageType, uint16_t serviceVersion,
uint16_t messageVersion);
bool lgNetEnvelopeEncode(void * data, size_t size,
const LGNetEnvelope * envelope);
LGNetParseResult lgNetEnvelopeDecode(LGNetEnvelope * envelope,
const void * data, size_t size);
bool lgNetPacketSize(const LGNetEnvelope * envelope, size_t * size);
bool lgNetPacketEncode(void * data, size_t size,
const LGNetEnvelope * envelope, const void * payload);
LGNetParseResult lgNetPacketDecode(LGNetPacketView * packet,
const void * data, size_t size);
bool lgNetServiceKnown(LGNetService service);
bool lgNetMessageKnown(LGNetService service, uint16_t messageType);
void lgNetHelloInit(LGNetHello * hello, LGNetRole role);
bool lgNetHelloValid(const LGNetHello * hello);
bool lgNetHelloEncode(void * data, size_t size, const LGNetHello * hello);
LGNetParseResult lgNetHelloDecode(LGNetHello * hello,
const void * data, size_t size);
void lgNetHelloAckInit(LGNetHelloAck * hello, uint64_t sessionEpoch);
bool lgNetHelloAckValid(const LGNetHelloAck * hello);
bool lgNetHelloAckEncode(
void * data, size_t size, const LGNetHelloAck * hello);
LGNetParseResult lgNetHelloAckDecode(LGNetHelloAck * hello,
const void * data, size_t size);
bool lgNetCapabilityValid(const LGNetCapability * capability);
bool lgNetCapabilityEncode(void * data, size_t size,
const LGNetCapability * capability);
LGNetParseResult lgNetCapabilityDecode(LGNetCapability * capability,
const void * data, size_t size);
size_t lgNetPasswordChallengeSize(
const LGNetPasswordChallenge * challenge);
bool lgNetPasswordChallengeValid(
const LGNetPasswordChallenge * challenge);
bool lgNetPasswordChallengeEncode(void * data, size_t size,
const LGNetPasswordChallenge * challenge);
LGNetParseResult lgNetPasswordChallengeDecode(
LGNetPasswordChallenge * challenge, const void * data, size_t size);
size_t lgNetPasswordResponseSize(const LGNetPasswordResponse * response);
bool lgNetPasswordResponseValid(const LGNetPasswordResponse * response);
bool lgNetPasswordResponseEncode(void * data, size_t size,
const LGNetPasswordResponse * response);
LGNetParseResult lgNetPasswordResponseDecode(
LGNetPasswordResponse * response, const void * data, size_t size);
size_t lgNetAuthResultSize(const LGNetAuthResult * result);
bool lgNetAuthResultValid(const LGNetAuthResult * result);
bool lgNetAuthResultEncode(
void * data, size_t size, const LGNetAuthResult * result);
LGNetParseResult lgNetAuthResultDecode(LGNetAuthResult * result,
const void * data, size_t size);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,97 @@
/**
* 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
*/
#ifndef _H_LG_COMMON_NETWORK_WIRE_
#define _H_LG_COMMON_NETWORK_WIRE_
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef enum LGNetParseResult
{
LG_NET_PARSE_OK,
LG_NET_PARSE_TRUNCATED,
LG_NET_PARSE_INVALID_MAGIC,
LG_NET_PARSE_INVALID_VERSION,
LG_NET_PARSE_INVALID_HEADER,
LG_NET_PARSE_INVALID_LENGTH,
LG_NET_PARSE_INVALID_VALUE,
}
LGNetParseResult;
typedef struct LGNetWriter
{
uint8_t * data;
size_t capacity;
size_t offset;
bool failed;
}
LGNetWriter;
typedef struct LGNetReader
{
const uint8_t * data;
size_t size;
size_t offset;
bool failed;
}
LGNetReader;
void lgNetWriterInit(LGNetWriter * writer, void * data, size_t capacity);
size_t lgNetWriterSize(const LGNetWriter * writer);
size_t lgNetWriterRemaining(const LGNetWriter * writer);
bool lgNetWriterValid(const LGNetWriter * writer);
bool lgNetWriterU8(LGNetWriter * writer, uint8_t value);
bool lgNetWriterU16(LGNetWriter * writer, uint16_t value);
bool lgNetWriterU32(LGNetWriter * writer, uint32_t value);
bool lgNetWriterU64(LGNetWriter * writer, uint64_t value);
bool lgNetWriterI32(LGNetWriter * writer, int32_t value);
bool lgNetWriterI64(LGNetWriter * writer, int64_t value);
bool lgNetWriterBytes(
LGNetWriter * writer, const void * data, size_t size);
bool lgNetWriterZero(LGNetWriter * writer, size_t size);
void lgNetReaderInit(
LGNetReader * reader, const void * data, size_t size);
size_t lgNetReaderConsumed(const LGNetReader * reader);
size_t lgNetReaderRemaining(const LGNetReader * reader);
bool lgNetReaderValid(const LGNetReader * reader);
bool lgNetReaderU8(LGNetReader * reader, uint8_t * value);
bool lgNetReaderU16(LGNetReader * reader, uint16_t * value);
bool lgNetReaderU32(LGNetReader * reader, uint32_t * value);
bool lgNetReaderU64(LGNetReader * reader, uint64_t * value);
bool lgNetReaderI32(LGNetReader * reader, int32_t * value);
bool lgNetReaderI64(LGNetReader * reader, int64_t * value);
bool lgNetReaderBytes(LGNetReader * reader, void * data, size_t size);
bool lgNetReaderView(
LGNetReader * reader, const uint8_t ** data, size_t size);
bool lgNetReaderSkip(LGNetReader * reader, size_t size);
bool lgNetReaderZero(LGNetReader * reader, size_t size);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,730 @@
/**
* 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;
}

355
common/src/network/wire.c Normal file
View File

@@ -0,0 +1,355 @@
/**
* 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/wire.h"
#include <string.h>
static bool writerReserve(LGNetWriter * writer, size_t size)
{
if (!writer || writer->failed || writer->offset > writer->capacity ||
size > writer->capacity - writer->offset)
{
if (writer)
writer->failed = true;
return false;
}
return true;
}
static bool readerReserve(LGNetReader * reader, size_t size)
{
if (!reader || reader->failed || reader->offset > reader->size ||
size > reader->size - reader->offset)
{
if (reader)
reader->failed = true;
return false;
}
return true;
}
void lgNetWriterInit(LGNetWriter * writer, void * data, size_t capacity)
{
if (!writer)
return;
writer->data = data;
writer->capacity = capacity;
writer->offset = 0;
writer->failed = capacity && !data;
}
size_t lgNetWriterSize(const LGNetWriter * writer)
{
return writer ? writer->offset : 0;
}
size_t lgNetWriterRemaining(const LGNetWriter * writer)
{
return writer && !writer->failed && writer->offset <= writer->capacity ?
writer->capacity - writer->offset : 0;
}
bool lgNetWriterValid(const LGNetWriter * writer)
{
return writer && !writer->failed && writer->offset <= writer->capacity &&
(!writer->capacity || writer->data);
}
bool lgNetWriterU8(LGNetWriter * writer, uint8_t value)
{
if (!writerReserve(writer, 1))
return false;
writer->data[writer->offset++] = value;
return true;
}
bool lgNetWriterU16(LGNetWriter * writer, uint16_t value)
{
if (!writerReserve(writer, 2))
return false;
uint8_t * data = writer->data + writer->offset;
data[0] = (uint8_t)(value );
data[1] = (uint8_t)(value >> 8);
writer->offset += 2;
return true;
}
bool lgNetWriterU32(LGNetWriter * writer, uint32_t value)
{
if (!writerReserve(writer, 4))
return false;
uint8_t * data = writer->data + writer->offset;
data[0] = (uint8_t)(value );
data[1] = (uint8_t)(value >> 8);
data[2] = (uint8_t)(value >> 16);
data[3] = (uint8_t)(value >> 24);
writer->offset += 4;
return true;
}
bool lgNetWriterU64(LGNetWriter * writer, uint64_t value)
{
if (!writerReserve(writer, 8))
return false;
uint8_t * data = writer->data + writer->offset;
data[0] = (uint8_t)(value );
data[1] = (uint8_t)(value >> 8);
data[2] = (uint8_t)(value >> 16);
data[3] = (uint8_t)(value >> 24);
data[4] = (uint8_t)(value >> 32);
data[5] = (uint8_t)(value >> 40);
data[6] = (uint8_t)(value >> 48);
data[7] = (uint8_t)(value >> 56);
writer->offset += 8;
return true;
}
bool lgNetWriterI32(LGNetWriter * writer, int32_t value)
{
uint32_t bits;
memcpy(&bits, &value, sizeof(bits));
return lgNetWriterU32(writer, bits);
}
bool lgNetWriterI64(LGNetWriter * writer, int64_t value)
{
uint64_t bits;
memcpy(&bits, &value, sizeof(bits));
return lgNetWriterU64(writer, bits);
}
bool lgNetWriterBytes(
LGNetWriter * writer, const void * data, size_t size)
{
if (!writerReserve(writer, size) || (size && !data))
{
if (writer)
writer->failed = true;
return false;
}
if (size)
memcpy(writer->data + writer->offset, data, size);
writer->offset += size;
return true;
}
bool lgNetWriterZero(LGNetWriter * writer, size_t size)
{
if (!writerReserve(writer, size))
return false;
if (size)
memset(writer->data + writer->offset, 0, size);
writer->offset += size;
return true;
}
void lgNetReaderInit(
LGNetReader * reader, const void * data, size_t size)
{
if (!reader)
return;
reader->data = data;
reader->size = size;
reader->offset = 0;
reader->failed = size && !data;
}
size_t lgNetReaderConsumed(const LGNetReader * reader)
{
return reader ? reader->offset : 0;
}
size_t lgNetReaderRemaining(const LGNetReader * reader)
{
return reader && !reader->failed && reader->offset <= reader->size ?
reader->size - reader->offset : 0;
}
bool lgNetReaderValid(const LGNetReader * reader)
{
return reader && !reader->failed && reader->offset <= reader->size &&
(!reader->size || reader->data);
}
bool lgNetReaderU8(LGNetReader * reader, uint8_t * value)
{
if (!readerReserve(reader, 1) || !value)
{
if (reader)
reader->failed = true;
return false;
}
*value = reader->data[reader->offset++];
return true;
}
bool lgNetReaderU16(LGNetReader * reader, uint16_t * value)
{
if (!readerReserve(reader, 2) || !value)
{
if (reader)
reader->failed = true;
return false;
}
const uint8_t * data = reader->data + reader->offset;
*value =
(uint16_t)data[0] |
(uint16_t)data[1] << 8;
reader->offset += 2;
return true;
}
bool lgNetReaderU32(LGNetReader * reader, uint32_t * value)
{
if (!readerReserve(reader, 4) || !value)
{
if (reader)
reader->failed = true;
return false;
}
const uint8_t * data = reader->data + reader->offset;
*value =
(uint32_t)data[0] |
(uint32_t)data[1] << 8 |
(uint32_t)data[2] << 16 |
(uint32_t)data[3] << 24;
reader->offset += 4;
return true;
}
bool lgNetReaderU64(LGNetReader * reader, uint64_t * value)
{
if (!readerReserve(reader, 8) || !value)
{
if (reader)
reader->failed = true;
return false;
}
const uint8_t * data = reader->data + reader->offset;
*value =
(uint64_t)data[0] |
(uint64_t)data[1] << 8 |
(uint64_t)data[2] << 16 |
(uint64_t)data[3] << 24 |
(uint64_t)data[4] << 32 |
(uint64_t)data[5] << 40 |
(uint64_t)data[6] << 48 |
(uint64_t)data[7] << 56;
reader->offset += 8;
return true;
}
bool lgNetReaderI32(LGNetReader * reader, int32_t * value)
{
uint32_t bits;
if (!value || !lgNetReaderU32(reader, &bits))
{
if (reader)
reader->failed = true;
return false;
}
memcpy(value, &bits, sizeof(*value));
return true;
}
bool lgNetReaderI64(LGNetReader * reader, int64_t * value)
{
uint64_t bits;
if (!value || !lgNetReaderU64(reader, &bits))
{
if (reader)
reader->failed = true;
return false;
}
memcpy(value, &bits, sizeof(*value));
return true;
}
bool lgNetReaderBytes(LGNetReader * reader, void * data, size_t size)
{
if (!readerReserve(reader, size) || (size && !data))
{
if (reader)
reader->failed = true;
return false;
}
if (size)
memcpy(data, reader->data + reader->offset, size);
reader->offset += size;
return true;
}
bool lgNetReaderView(
LGNetReader * reader, const uint8_t ** data, size_t size)
{
if (!readerReserve(reader, size) || !data)
{
if (reader)
reader->failed = true;
return false;
}
*data = size ? reader->data + reader->offset : NULL;
reader->offset += size;
return true;
}
bool lgNetReaderSkip(LGNetReader * reader, size_t size)
{
if (!readerReserve(reader, size))
return false;
reader->offset += size;
return true;
}
bool lgNetReaderZero(LGNetReader * reader, size_t size)
{
if (!readerReserve(reader, size))
return false;
for (size_t i = 0; i < size; ++i)
if (reader->data[reader->offset + i])
{
reader->failed = true;
return false;
}
reader->offset += size;
return true;
}

View File

@@ -69,6 +69,8 @@
<ClCompile Include="CClipboardRing.cpp" />
<ClCompile Include="CDebug.cpp" />
<ClCompile Include="CPipeEndpoint.cpp" />
<ClCompile Include="..\..\common\src\network\protocol.c" />
<ClCompile Include="..\..\common\src\network\wire.c" />
<ClCompile Include="RefreshRate.cpp" />
<ClCompile Include="WCCopy.cpp" />
<ClCompile Include="WCCopyAVX2.cpp">
@@ -86,6 +88,8 @@
<ClInclude Include="CDebug.h" />
<ClInclude Include="ClipboardRing.h" />
<ClInclude Include="CPipeEndpoint.h" />
<ClInclude Include="..\..\common\include\common\network\protocol.h" />
<ClInclude Include="..\..\common\include\common\network\wire.h" />
<ClInclude Include="CSRWLock.h" />
<ClInclude Include="DefaultDisplayModes.h" />
<ClInclude Include="InputPipeProtocol.h" />