DeterministicESPAsyncWebServer v6.27.1
Zero-allocation, bounded-execution async HTTP server for ESP32
Loading...
Searching...
No Matches
ssh_comp.cpp
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_comp.cpp
6 * @brief SSH server-to-client compression owner - per-connection state + activation.
7 */
8
10
11#if DETWS_ENABLE_SSH_ZLIB
12
14#include <string.h>
15
16// The per-connection compressor holds a window-sized work buffer + a window-sized hash chain (tens
17// of KB); the pool does not fit internal DRAM alongside the SSH crypto stack, so it lives in PSRAM
18// (DETWS_SSH_ZLIB_IN_PSRAM). Same mechanism/caveat as the TLS arena and HTTP/2 pool: it needs a
19// framework built with CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY=y (the stock arduino-esp32 core
20// ships it OFF, so EXT_RAM_BSS_ATTR would silently no-op); see tools/psram/README.md.
21#if DETWS_SSH_ZLIB_IN_PSRAM && defined(ARDUINO)
22#include <esp_attr.h> // pulls in sdkconfig.h -> CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY
23#if !defined(CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY)
24#error \
25 "DETWS_SSH_ZLIB_IN_PSRAM needs a framework built with CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY=y. The stock arduino-esp32 core ships it OFF, so EXT_RAM_BSS_ATTR silently no-ops and the compressor pool would overflow internal DRAM. Rebuild the core (tools/psram/README.md) or unset DETWS_SSH_ZLIB_IN_PSRAM."
26#endif
27#if defined(EXT_RAM_BSS_ATTR)
28#define DETWS_SSH_COMP_ATTR EXT_RAM_BSS_ATTR // IDF v5 / arduino-esp32 3.x
29#elif defined(EXT_RAM_ATTR)
30#define DETWS_SSH_COMP_ATTR EXT_RAM_ATTR // IDF v4 / arduino-esp32 2.x
31#else
32#define DETWS_SSH_COMP_ATTR
33#endif
34#else
35#define DETWS_SSH_COMP_ATTR
36#endif
37
38// Per-connection compression state (large buffers -> PSRAM; the flags are trivial).
39struct SshCompState
40{
41 SshDeflate z; ///< streaming compressor (bound to the buffers below).
42 uint8_t work[SSH_ZLIB_WORK_SIZE]; ///< history + input work buffer.
43 uint16_t head[SSH_ZLIB_HASH_SIZE]; ///< hash bucket heads.
44 uint16_t prev[SSH_ZLIB_WORK_SIZE]; ///< hash chain (absolute-position indexed).
45 uint16_t ll_code[288]; ///< fixed lit/length Huffman codes.
46 uint8_t ll_len[288]; ///< their bit lengths.
47 uint16_t d_code[30]; ///< fixed distance Huffman codes.
48 uint8_t d_len[30]; ///< their bit lengths.
49 SshCompAlg s2c_alg; ///< negotiated compression algorithm.
50 bool s2c_active; ///< true once the stream has started.
51};
52
53// All SSH compression state, owned by one instance (internal linkage): the per-connection
54// deflate stream table. One named owner, unreachable from any other translation unit.
55struct SshCompCtx
56{
57 SshCompState comp[MAX_SSH_CONNS];
58};
59static DETWS_SSH_COMP_ATTR SshCompCtx s_ssh_comp;
60
61static void start_stream(SshCompState *c)
62{
63 ssh_deflate_init(&c->z, c->work, c->head, c->prev, c->ll_code, c->ll_len, c->d_code, c->d_len);
64 c->s2c_active = true;
65}
66
67void ssh_comp_reset(uint8_t i)
68{
69 if (i >= MAX_SSH_CONNS)
70 return;
71 s_ssh_comp.comp[i].s2c_alg = SshCompAlg::SSH_COMP_NONE;
72 s_ssh_comp.comp[i].s2c_active = false;
73}
74
75void ssh_comp_set_s2c(uint8_t i, SshCompAlg alg)
76{
77 if (i >= MAX_SSH_CONNS)
78 return;
79 s_ssh_comp.comp[i].s2c_alg = alg;
80}
81
82void ssh_comp_on_newkeys(uint8_t i)
83{
84 if (i >= MAX_SSH_CONNS)
85 return;
86 SshCompState *c = &s_ssh_comp.comp[i];
87 if (c->s2c_alg == SshCompAlg::SSH_COMP_ZLIB && !c->s2c_active)
88 start_stream(c);
89}
90
91void ssh_comp_on_auth_success(uint8_t i)
92{
93 if (i >= MAX_SSH_CONNS)
94 return;
95 SshCompState *c = &s_ssh_comp.comp[i];
96 if (c->s2c_alg == SshCompAlg::SSH_COMP_ZLIB_DELAYED && !c->s2c_active)
97 start_stream(c);
98}
99
100bool ssh_comp_s2c_active(uint8_t i)
101{
102 return i < MAX_SSH_CONNS && s_ssh_comp.comp[i].s2c_active;
103}
104
105int ssh_comp_s2c(uint8_t i, const uint8_t *src, size_t src_len, uint8_t *dst, size_t dst_cap, size_t *out_len)
106{
107 if (i >= MAX_SSH_CONNS || !s_ssh_comp.comp[i].s2c_active)
108 return -1;
109 return ssh_deflate_packet(&s_ssh_comp.comp[i].z, src, src_len, dst, dst_cap, out_len);
110}
111
112#endif // DETWS_ENABLE_SSH_ZLIB
#define MAX_SSH_CONNS
Maximum simultaneous SSH connections.
SSH per-connection compression owner (server-to-client zlib / zlib@openssh.com).
SSH server-to-client compression: a context-takeover DEFLATE stream (no heap).