ProtoCore v0.0.2
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
ssh_inflate.h
Go to the documentation of this file.
1// Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file ssh_inflate.h
6 * @brief SSH client-to-server decompression: a resumable, context-takeover INFLATE (no heap).
7 *
8 * The complement of ssh_zlib (server-to-client DEFLATE). SSH `zlib` / `zlib@openssh.com`
9 * (RFC 4253 sec 6.2) keep one zlib stream alive per direction for the whole session - a persistent
10 * 32 KB sliding window carried across packets ("context takeover"), flushed at each packet boundary.
11 * OpenSSH compresses its outbound (our inbound, client-to-server) traffic with `Z_PARTIAL_FLUSH`,
12 * which ends each packet at a DEFLATE block boundary but NOT on a byte boundary: the last bits of a
13 * packet spill into the next packet's first byte. Decoding it therefore needs a *resumable* inflate
14 * that carries the bit position and the window across `ssh_inflate_packet()` calls.
15 *
16 * The engine keeps that state small by only ever decoding *complete* DEFLATE blocks: after each feed
17 * it retains the few un-decoded tail bytes (the incomplete flush block) plus the bit offset into the
18 * first of them, and re-decodes from that boundary when the next packet arrives - so there is no
19 * mid-block Huffman-table or symbol state to persist. Back-references read from a caller-supplied
20 * 32768-byte circular window (SSH_INFLATE_WINDOW), which holds up to 32 KB of prior output so a match
21 * can reach into earlier packets. All state and buffers are caller-owned; the codec allocates nothing.
22 *
23 * @author Douglas Quigg (dstroy0)
24 * @date 2026
25 */
26
27#ifndef PROTOCORE_SSH_INFLATE_H
28#define PROTOCORE_SSH_INFLATE_H
29
30#include "protocore_config.h"
31#include <stddef.h>
32#include <stdint.h>
33
34#if PC_ENABLE_SSH_ZLIB
35
36/** @brief Sliding-window bytes the inflate needs (the full zlib 32 KB window OpenSSH may reference). */
37#define SSH_INFLATE_WINDOW 32768u
38
39/** @brief Bytes of un-decoded input the engine carries between packets (the flush-block tail). A
40 * well-behaved peer leaves only a handful; the bound also caps a peer that fails to flush cleanly. */
41#define SSH_INFLATE_CARRY 64u
42
43/**
44 * @brief Streaming client-to-server DEFLATE decompressor (one per SSH connection).
45 *
46 * The 32 KB circular @ref window is caller-supplied (it lives in PSRAM alongside the s2c compressor).
47 * ssh_inflate_init() binds it and resets the stream; the small carry/bit state is inline.
48 */
49struct SshInflate
50{
51 uint8_t *window; ///< 32 KB circular back-reference window (SSH_INFLATE_WINDOW bytes).
52 uint32_t wpos; ///< next write position in @ref window (0..SSH_INFLATE_WINDOW-1).
53 uint32_t whist; ///< bytes of valid history in @ref window (caps at SSH_INFLATE_WINDOW).
54 uint8_t carry[SSH_INFLATE_CARRY]; ///< un-decoded tail bytes from the previous packet (flush block).
55 uint8_t carry_len; ///< number of valid bytes in @ref carry.
56 uint8_t bit_off; ///< bits already consumed from carry[0] at the last block boundary (0..7).
57 bool header_seen; ///< true once the leading 2-byte RFC 1950 zlib header was consumed.
58};
59
60/**
61 * @brief Bind a caller-owned 32 KB window to a decompressor and reset it to stream start.
62 * @param z the decompressor to initialize.
63 * @param window back-reference window, >= SSH_INFLATE_WINDOW bytes.
64 */
65void ssh_inflate_init(SshInflate *z, uint8_t *window);
66
67/**
68 * @brief Decompress one inbound packet payload, continuing the session's zlib stream.
69 *
70 * Consumes the 2-byte zlib header on the first call, then decodes every complete DEFLATE block that
71 * @p src (prefixed by any carried tail) makes available, writing the decompressed bytes to @p dst and
72 * into the window. The incomplete trailing flush block is carried to the next call.
73 *
74 * @param z the decompressor.
75 * @param src,src_len one inbound compressed payload.
76 * @param dst,dst_cap output buffer for the decompressed payload.
77 * @param out_len set to the decompressed length on success (may be 0 if a packet carried only flush bits).
78 * @return 0 on success, -1 on a malformed stream, an output overflow, or a carry overflow (peer did not flush).
79 */
80int ssh_inflate_packet(SshInflate *z, const uint8_t *src, size_t src_len, uint8_t *dst, size_t dst_cap,
81 size_t *out_len);
82
83#endif // PC_ENABLE_SSH_ZLIB
84#endif // PROTOCORE_SSH_INFLATE_H
User-facing configuration for ProtoCore.