DeterministicESPAsyncWebServer v6.27.1
Zero-allocation, bounded-execution async HTTP server for ESP32
Loading...
Searching...
No Matches
ssh_channel.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_channel.cpp
6 * @brief SSH connection protocol - multiplexed session channels (RFC 4254).
7 *
8 * The channel table is owned here; inbound messages are routed to a channel by the
9 * recipient channel id they carry, and the local channel id is the channel's slot
10 * index in its connection's pool (unique per connection, which is all RFC 4254
11 * requires). Other layers go through these functions, never the table.
12 */
13
16#include <string.h>
17
19
20// All SSH channel-layer callbacks, owned by one instance (internal linkage): the channel-data
21// sink and the local/remote port-forward hooks. Grouped so it is one named owner, unreachable
22// from any other translation unit. (The ssh_chan[][] table is the shared cross-TU substrate.)
32static SshChannelCtx s_chcb;
33
35{
36 s_chcb.data_cb = cb;
37}
38
43
48
53
58
63
64void ssh_channel_init(uint8_t i)
65{
66 if (i >= MAX_SSH_CONNS)
67 return;
68 memset(ssh_chan[i], 0, sizeof(ssh_chan[i])); // reset every channel for this connection
69}
70
71// ---------------------------------------------------------------------------
72// Wire helpers
73// ---------------------------------------------------------------------------
74
75static uint32_t rd_u32(const uint8_t *p)
76{
77 return ((uint32_t)p[0] << 24) | ((uint32_t)p[1] << 16) | ((uint32_t)p[2] << 8) | (uint32_t)p[3];
78}
79
80static void wr_u32(uint8_t *p, uint32_t v)
81{
82 p[0] = (uint8_t)(v >> 24);
83 p[1] = (uint8_t)(v >> 16);
84 p[2] = (uint8_t)(v >> 8);
85 p[3] = (uint8_t)v;
86}
87
88// Read an SSH string: out points at the data, *slen its length. Advances *off.
89static bool rd_string(const uint8_t *p, size_t len, size_t *off, const uint8_t **out, uint32_t *slen)
90{
91 if (*off + 4 > len)
92 return false;
93 uint32_t n = rd_u32(p + *off);
94 *off += 4;
95 if (*off + n > len)
96 return false;
97 *out = p + *off;
98 *slen = n;
99 *off += n;
100 return true;
101}
102
103// ---------------------------------------------------------------------------
104// Channel table (owned here)
105// ---------------------------------------------------------------------------
106
107// The open channel @p id on connection @p i, or nullptr. local id == slot index.
108static SshChannel *chan_by_id(uint8_t i, uint32_t id)
109{
110 if (i >= MAX_SSH_CONNS || id >= DETWS_SSH_MAX_CHANNELS || !ssh_chan[i][id].open)
111 return nullptr;
112 return &ssh_chan[i][id];
113}
114
115// A pending server-initiated channel @p id on connection @p i (awaiting the client's
116// CONFIRMATION / FAILURE), or nullptr.
117static SshChannel *chan_pending_by_id(uint8_t i, uint32_t id)
118{
119 if (i >= MAX_SSH_CONNS || id >= DETWS_SSH_MAX_CHANNELS || !ssh_chan[i][id].pending)
120 return nullptr;
121 return &ssh_chan[i][id];
122}
123
124// First free channel slot on connection @p i, or -1 if the pool is full. A pending
125// (opened-but-unconfirmed) channel is in use just like an open one.
126static int chan_alloc(uint8_t i)
127{
128 for (int c = 0; c < DETWS_SSH_MAX_CHANNELS; c++)
129 if (!ssh_chan[i][c].open && !ssh_chan[i][c].pending)
130 return c;
131 return -1;
132}
133
134// ---------------------------------------------------------------------------
135// CHANNEL_OPEN → CONFIRMATION / FAILURE
136// ---------------------------------------------------------------------------
137
138// CHANNEL_OPEN_FAILURE: byte || recipient(sender) || reason || desc || lang.
139// reason: 1 admin-prohibited, 2 connect-failed, 3 unknown-type, 4 resource-shortage.
140static int build_open_failure(uint8_t *out, size_t cap, uint32_t sender, uint32_t reason, size_t *out_len)
141{
142 if (cap < 17)
143 return -1;
145 wr_u32(out + 1, sender);
146 wr_u32(out + 5, reason);
147 wr_u32(out + 9, 0); // empty description
148 wr_u32(out + 13, 0); // empty language
149 *out_len = 17;
150 return 0;
151}
152
153// CHANNEL_OPEN_CONFIRMATION: byte || recipient(peer) || sender(local) || window || max.
154static int build_open_confirm(const SshChannel *c, uint8_t *out, size_t cap, size_t *out_len)
155{
156 if (cap < 17)
157 return -1;
159 wr_u32(out + 1, c->peer_id);
160 wr_u32(out + 5, c->local_id);
161 wr_u32(out + 9, c->local_window);
162 wr_u32(out + 13, SSH_CHAN_MAX_PACKET);
163 *out_len = 17;
164 return 0;
165}
166
167// ---------------------------------------------------------------------------
168// GLOBAL_REQUEST (RFC 4254 §4; §7.1 tcpip-forward / cancel-tcpip-forward)
169// ---------------------------------------------------------------------------
170
171int ssh_global_request_handle(uint8_t i, const uint8_t *payload, size_t len, uint8_t *out, size_t *out_len, size_t cap)
172{
173 *out_len = 0;
174 if (i >= MAX_SSH_CONNS || len < 1 || payload[0] != SSH_MSG_GLOBAL_REQUEST)
175 return -1;
176
177 size_t off = 1;
178 const uint8_t *name;
179 uint32_t name_len;
180 if (!rd_string(payload, len, &off, &name, &name_len))
181 return -1;
182 if (off >= len)
183 return -1;
184 bool want_reply = payload[off++] != 0;
185
186 bool is_fwd = (name_len == 13 && memcmp(name, "tcpip-forward", 13) == 0);
187 bool is_cancel = (name_len == 20 && memcmp(name, "cancel-tcpip-forward", 20) == 0);
188
189 if (is_fwd || is_cancel)
190 {
191 // Request-specific data: bind address (string) followed by bind port (uint32).
192 const uint8_t *addr;
193 uint32_t addr_len;
194 if (!rd_string(payload, len, &off, &addr, &addr_len) || off + 4 > len)
195 return -1;
196 uint16_t bind_port = (uint16_t)rd_u32(payload + off);
197
198 // The owner allocates (or cancels) the real listener; -1 means "refused".
199 int bound = -1;
200 if (is_fwd && s_chcb.rfwd_open_cb)
201 bound = s_chcb.rfwd_open_cb(i, (const char *)addr, addr_len, bind_port);
202 else if (is_cancel && s_chcb.rfwd_cancel_cb)
203 bound = s_chcb.rfwd_cancel_cb(i, (const char *)addr, addr_len, bind_port);
204
205 if (bound < 0)
206 {
207 if (want_reply) // refused: no owner, policy denied, or the table is full
208 {
209 if (cap < 1)
210 return -1;
212 *out_len = 1;
213 }
214 return 0;
215 }
216
217 if (want_reply)
218 {
219 // A tcpip-forward that requested port 0 echoes the allocated port
220 // (RFC 4254 §7.1); a specific port and cancel reply bare success.
221 if (is_fwd && bind_port == 0)
222 {
223 if (cap < 5)
224 return -1;
226 wr_u32(out + 1, (uint32_t)(uint16_t)bound);
227 *out_len = 5;
228 }
229 else
230 {
231 if (cap < 1)
232 return -1;
234 *out_len = 1;
235 }
236 }
237 return 0;
238 }
239
240 // Any other global request is unrecognized: RFC 4254 §4 -> REQUEST_FAILURE when the
241 // client wants a reply, otherwise silently ignored. (Never UNIMPLEMENTED: the
242 // GLOBAL_REQUEST message type is known; only this request name is not.)
243 if (want_reply)
244 {
245 if (cap < 1)
246 return -1;
248 *out_len = 1;
249 }
250 return 0;
251}
252
253// ---------------------------------------------------------------------------
254// Server-initiated CHANNEL_OPEN (forwarded-tcpip, ssh -R) + its CONFIRM / FAILURE
255// ---------------------------------------------------------------------------
256
257int ssh_channel_open_forwarded(uint8_t i, const char *conn_addr, uint16_t conn_port, const char *orig_addr,
258 uint16_t orig_port, uint8_t *out, size_t *out_len, size_t cap)
259{
260 *out_len = 0;
261 if (i >= MAX_SSH_CONNS || !conn_addr || !orig_addr)
262 return -1;
263 int slot = chan_alloc(i);
264 if (slot < 0)
265 return -1; // channel pool full
266
267 const char *type = "forwarded-tcpip";
268 size_t tl = 15, ca = strnlen(conn_addr, cap), oa = strnlen(orig_addr, cap);
269 // byte || string(type) || u32 sender || u32 window || u32 maxpkt || string(conn_addr)
270 // || u32 conn_port || string(orig_addr) || u32 orig_port (RFC 4254 §7.2)
271 size_t need = 1 + (4 + tl) + 12 + (4 + ca) + 4 + (4 + oa) + 4;
272 if (cap < need)
273 return -1;
274
275 size_t off = 0;
276 out[off++] = SSH_MSG_CHANNEL_OPEN;
277 wr_u32(out + off, (uint32_t)tl);
278 memcpy(out + off + 4, type, tl);
279 off += 4 + tl;
280 wr_u32(out + off, (uint32_t)slot); // our sender channel id
281 wr_u32(out + off + 4, SSH_CHAN_WINDOW);
282 wr_u32(out + off + 8, SSH_CHAN_MAX_PACKET);
283 off += 12;
284 wr_u32(out + off, (uint32_t)ca);
285 memcpy(out + off + 4, conn_addr, ca);
286 off += 4 + ca;
287 wr_u32(out + off, conn_port);
288 off += 4;
289 wr_u32(out + off, (uint32_t)oa);
290 memcpy(out + off + 4, orig_addr, oa);
291 off += 4 + oa;
292 wr_u32(out + off, orig_port);
293 off += 4;
294
295 SshChannel *c = &ssh_chan[i][slot];
296 c->open = false;
297 c->pending = true; // awaiting the client's CHANNEL_OPEN_CONFIRMATION
299 c->local_id = (uint32_t)slot;
300 c->peer_id = 0;
302 c->peer_window = 0;
303 c->peer_max_pkt = 0;
304
305 *out_len = off;
306 return slot;
307}
308
309int ssh_channel_handle_open_confirm(uint8_t i, const uint8_t *payload, size_t len)
310{
311 // byte || recipient(our local id) || sender(peer id) || window || max packet.
312 if (i >= MAX_SSH_CONNS || len < 17 || payload[0] != SSH_MSG_CHANNEL_OPEN_CONFIRM)
313 return -1;
314 SshChannel *c = chan_pending_by_id(i, rd_u32(payload + 1));
315 if (!c)
316 return -1;
317 c->peer_id = rd_u32(payload + 5);
318 c->peer_window = rd_u32(payload + 9);
319 c->peer_max_pkt = rd_u32(payload + 13);
320 c->pending = false;
321 c->open = true;
322 if (s_chcb.forward_confirm_cb)
323 s_chcb.forward_confirm_cb(i, c->local_id, true);
324 return 0;
325}
326
327int ssh_channel_handle_open_failure(uint8_t i, const uint8_t *payload, size_t len)
328{
329 // byte || recipient(our local id) || reason || desc || lang.
330 if (i >= MAX_SSH_CONNS || len < 5 || payload[0] != SSH_MSG_CHANNEL_OPEN_FAILURE)
331 return -1;
332 SshChannel *c = chan_pending_by_id(i, rd_u32(payload + 1));
333 if (!c)
334 return -1;
335 uint32_t ch = c->local_id;
336 c->pending = false;
337 c->open = false; // free the slot; the client refused the forward
338 if (s_chcb.forward_confirm_cb)
339 s_chcb.forward_confirm_cb(i, ch, false);
340 return 0;
341}
342
343int ssh_channel_handle_open(uint8_t i, const uint8_t *payload, size_t len, uint8_t *out, size_t *out_len, size_t cap)
344{
345 if (i >= MAX_SSH_CONNS || len < 1 || payload[0] != SSH_MSG_CHANNEL_OPEN)
346 return -1;
347
348 size_t off = 1;
349 const uint8_t *type;
350 uint32_t type_len;
351 if (!rd_string(payload, len, &off, &type, &type_len))
352 return -1;
353 if (off + 12 > len)
354 return -1;
355 uint32_t sender = rd_u32(payload + off);
356 uint32_t init_window = rd_u32(payload + off + 4);
357 uint32_t max_pkt = rd_u32(payload + off + 8);
358 off += 12;
359
360 bool is_session = (type_len == 7 && memcmp(type, "session", 7) == 0);
361 bool is_dtcpip = (type_len == 12 && memcmp(type, "direct-tcpip", 12) == 0);
362 if (!is_session && !is_dtcpip)
363 return build_open_failure(out, cap, sender, 3u, out_len); // unknown channel type
364
365 // direct-tcpip data: host(string) port(u32) orig_host(string) orig_port(u32).
366 const uint8_t *fhost = nullptr;
367 uint32_t fhost_len = 0;
368 uint16_t fport = 0;
369 if (is_dtcpip)
370 {
371 if (!s_chcb.forward_open_cb)
372 return build_open_failure(out, cap, sender, 1u, out_len); // forwarding off: prohibited
373 if (!rd_string(payload, len, &off, &fhost, &fhost_len) || off + 4 > len)
374 return -1;
375 fport = (uint16_t)rd_u32(payload + off); // orig host/port follow but are advisory
376 }
377
378 int slot = chan_alloc(i);
379 if (slot < 0)
380 return build_open_failure(out, cap, sender, 4u, out_len); // pool full
381
382 SshChannel *c = &ssh_chan[i][slot];
383 c->open = true;
385 c->local_id = (uint32_t)slot;
386 c->peer_id = sender;
388 c->peer_window = init_window;
389 c->peer_max_pkt = max_pkt;
390
391 if (is_dtcpip)
392 {
393 // The owner does the actual TCP connect (no I/O in this codec); on refusal
394 // free the channel and fail closed.
395 if (s_chcb.forward_open_cb(i, c->local_id, (const char *)fhost, fhost_len, fport) < 0)
396 {
397 c->open = false;
398 return build_open_failure(out, cap, sender, 2u, out_len); // connect failed
399 }
400 }
401 return build_open_confirm(c, out, cap, out_len);
402}
403
404// ---------------------------------------------------------------------------
405// CHANNEL_REQUEST → SUCCESS / FAILURE
406// ---------------------------------------------------------------------------
407
408int ssh_channel_handle_request(uint8_t i, const uint8_t *payload, size_t len, uint8_t *out, size_t *out_len, size_t cap)
409{
410 *out_len = 0;
411 if (i >= MAX_SSH_CONNS || len < 1 || payload[0] != SSH_MSG_CHANNEL_REQUEST)
412 return -1;
413
414 size_t off = 1;
415 if (off + 4 > len)
416 return -1;
417 uint32_t recipient = rd_u32(payload + off); // our channel id
418 off += 4;
419 const uint8_t *rtype;
420 uint32_t rtype_len;
421 if (!rd_string(payload, len, &off, &rtype, &rtype_len))
422 return -1;
423 if (off >= len)
424 return -1;
425 bool want_reply = payload[off++] != 0;
426
427 SshChannel *c = chan_by_id(i, recipient);
428 if (!c)
429 return -1;
430
431 bool accept =
432 (rtype_len == 5 && memcmp(rtype, "shell", 5) == 0) || (rtype_len == 4 && memcmp(rtype, "exec", 4) == 0) ||
433 (rtype_len == 7 && memcmp(rtype, "pty-req", 7) == 0) || (rtype_len == 3 && memcmp(rtype, "env", 3) == 0);
434
435 if (!want_reply)
436 return 0;
437
438 if (cap < 5)
439 return -1;
441 wr_u32(out + 1, c->peer_id);
442 *out_len = 5;
443 return 0;
444}
445
446// ---------------------------------------------------------------------------
447// CHANNEL_DATA (inbound) + flow control
448// ---------------------------------------------------------------------------
449
450int ssh_channel_handle_data(uint8_t i, const uint8_t *payload, size_t len, uint8_t *out, size_t *out_len, size_t cap)
451{
452 *out_len = 0;
453 if (i >= MAX_SSH_CONNS || len < 1 || payload[0] != SSH_MSG_CHANNEL_DATA)
454 return -1;
455
456 size_t off = 1;
457 if (off + 4 > len)
458 return -1;
459 uint32_t recipient = rd_u32(payload + off);
460 off += 4;
461 const uint8_t *data;
462 uint32_t dlen;
463 if (!rd_string(payload, len, &off, &data, &dlen))
464 return -1;
465
466 SshChannel *c = chan_by_id(i, recipient);
467 if (!c)
468 return -1;
469 if (dlen > c->local_window)
470 return -1; // peer overran the advertised window (RFC 4254 §5.2)
471 c->local_window -= dlen;
472
473 if (dlen > 0)
474 {
475 if (c->type != SshChanType::SSH_CHAN_SESSION) // forwarded TCP bytes (ssh -L / -R) -> the forward owner
476 {
477 if (s_chcb.forward_data_cb)
478 s_chcb.forward_data_cb(i, c->local_id, data, dlen);
479 }
480 else if (s_chcb.data_cb) // session bytes -> the application
481 {
482 s_chcb.data_cb(i, c->local_id, data, dlen);
483 }
484 }
485
486 // Replenish the window once it drops below half.
487 if (c->local_window < SSH_CHAN_WINDOW / 2)
488 {
489 uint32_t add = SSH_CHAN_WINDOW - c->local_window;
490 if (cap >= 9)
491 {
493 wr_u32(out + 1, c->peer_id);
494 wr_u32(out + 5, add);
495 *out_len = 9;
496 c->local_window += add;
497 }
498 }
499 return 0;
500}
501
502// ---------------------------------------------------------------------------
503// CHANNEL_DATA (outbound)
504// ---------------------------------------------------------------------------
505
506int ssh_channel_build_data(uint8_t i, uint32_t channel, const uint8_t *data, size_t len, uint8_t *out, size_t *out_len,
507 size_t cap)
508{
509 SshChannel *c = (i < MAX_SSH_CONNS) ? chan_by_id(i, channel) : nullptr;
510 if (!c)
511 return -1;
512 if (len > c->peer_window || len > c->peer_max_pkt)
513 return -1; // would exceed the client's window / packet size
514 if (cap < 1 + 4 + 4 + len)
515 return -1;
516
517 out[0] = SSH_MSG_CHANNEL_DATA;
518 wr_u32(out + 1, c->peer_id);
519 wr_u32(out + 5, (uint32_t)len);
520 memcpy(out + 9, data, len);
521 *out_len = 9 + len;
522 c->peer_window -= (uint32_t)len;
523 return 0;
524}
525
526// ---------------------------------------------------------------------------
527// WINDOW_ADJUST (inbound)
528// ---------------------------------------------------------------------------
529
530int ssh_channel_handle_window_adjust(uint8_t i, const uint8_t *payload, size_t len)
531{
532 if (i >= MAX_SSH_CONNS || len < 9 || payload[0] != SSH_MSG_CHANNEL_WINDOW_ADJUST)
533 return -1;
534 SshChannel *c = chan_by_id(i, rd_u32(payload + 1));
535 if (!c)
536 return -1;
537 uint32_t add = rd_u32(payload + 5);
538 // Saturate rather than overflow the 32-bit window.
539 uint32_t w = c->peer_window;
540 c->peer_window = (w + add < w) ? 0xFFFFFFFFu : (w + add);
541 return 0;
542}
543
544// ---------------------------------------------------------------------------
545// EOF + CLOSE
546// ---------------------------------------------------------------------------
547
548// Frame EOF + CLOSE for an open channel and mark it closed (shared by the inbound
549// handler and the app/teardown path).
550static int build_close_chan(SshChannel *c, uint8_t *out, size_t *out_len, size_t cap)
551{
552 if (!c || cap < 10)
553 return -1;
554 uint32_t peer = c->peer_id;
555 out[0] = SSH_MSG_CHANNEL_EOF;
556 wr_u32(out + 1, peer);
557 out[5] = SSH_MSG_CHANNEL_CLOSE;
558 wr_u32(out + 6, peer);
559 *out_len = 10;
560 c->open = false;
561 return 0;
562}
563
564int ssh_channel_build_close(uint8_t i, uint32_t channel, uint8_t *out, size_t *out_len, size_t cap)
565{
566 if (i >= MAX_SSH_CONNS)
567 return -1;
568 return build_close_chan(chan_by_id(i, channel), out, out_len, cap);
569}
570
571int ssh_channel_handle_close(uint8_t i, const uint8_t *payload, size_t len, uint8_t *out, size_t *out_len, size_t cap)
572{
573 *out_len = 0;
574 if (i >= MAX_SSH_CONNS || len < 5 || payload[0] != SSH_MSG_CHANNEL_CLOSE)
575 return -1;
576 return build_close_chan(chan_by_id(i, rd_u32(payload + 1)), out, out_len, cap);
577}
#define MAX_SSH_CONNS
Maximum simultaneous SSH connections.
#define SSH_CHAN_WINDOW
Initial receive window the SSH server advertises (RFC 4254 §5.1).
#define SSH_CHAN_MAX_PACKET
Maximum SSH channel data payload the server accepts per message.
#define DETWS_SSH_MAX_CHANNELS
Maximum concurrent SSH channels per connection (RFC 4254 multiplexing).
void ssh_channel_set_forward_data_cb(SshForwardDataCb cb)
Install the direct-tcpip forward inbound-data callback.
int ssh_channel_open_forwarded(uint8_t i, const char *conn_addr, uint16_t conn_port, const char *orig_addr, uint16_t orig_port, uint8_t *out, size_t *out_len, size_t cap)
Open a server-initiated "forwarded-tcpip" channel (RFC 4254 §7.2, ssh -R).
int ssh_channel_handle_data(uint8_t i, const uint8_t *payload, size_t len, uint8_t *out, size_t *out_len, size_t cap)
Handle SSH_MSG_CHANNEL_DATA: bounds-check, update the window, and invoke the data callback....
int ssh_channel_build_data(uint8_t i, uint32_t channel, const uint8_t *data, size_t len, uint8_t *out, size_t *out_len, size_t cap)
Build an SSH_MSG_CHANNEL_DATA message carrying data to the client on channel channel (a local channel...
int ssh_global_request_handle(uint8_t i, const uint8_t *payload, size_t len, uint8_t *out, size_t *out_len, size_t cap)
Handle SSH_MSG_GLOBAL_REQUEST (RFC 4254 §4).
void ssh_channel_set_forward_open_cb(SshForwardOpenCb cb)
Install the direct-tcpip forward open-policy callback (opt-in).
void ssh_channel_set_forward_confirm_cb(SshForwardConfirmCb cb)
Install the forwarded-tcpip open-confirmation callback (opt-in, ssh -R).
int ssh_channel_build_close(uint8_t i, uint32_t channel, uint8_t *out, size_t *out_len, size_t cap)
Build SSH_MSG_CHANNEL_EOF + SSH_MSG_CHANNEL_CLOSE for channel channel and mark it closed.
void ssh_channel_set_rforward_cancel_cb(SshRemoteForwardCancelCb cb)
Install the remote-forward (ssh -R) cancel callback (opt-in).
int ssh_channel_handle_window_adjust(uint8_t i, const uint8_t *payload, size_t len)
Handle SSH_MSG_CHANNEL_WINDOW_ADJUST (grows the peer window).
void ssh_channel_set_data_cb(SshChannelDataCb cb)
Install the inbound-data callback (session channels).
void ssh_channel_init(uint8_t i)
Reset channel state for slot i.
int ssh_channel_handle_close(uint8_t i, const uint8_t *payload, size_t len, uint8_t *out, size_t *out_len, size_t cap)
Handle an inbound SSH_MSG_CHANNEL_CLOSE: route to the recipient channel, reply with EOF + CLOSE,...
int ssh_channel_handle_open_failure(uint8_t i, const uint8_t *payload, size_t len)
Handle SSH_MSG_CHANNEL_OPEN_FAILURE for a channel we opened (ssh -R).
int ssh_channel_handle_request(uint8_t i, const uint8_t *payload, size_t len, uint8_t *out, size_t *out_len, size_t cap)
Handle SSH_MSG_CHANNEL_REQUEST.
int ssh_channel_handle_open(uint8_t i, const uint8_t *payload, size_t len, uint8_t *out, size_t *out_len, size_t cap)
Handle SSH_MSG_CHANNEL_OPEN and emit CHANNEL_OPEN_CONFIRMATION.
SshChannel ssh_chan[MAX_SSH_CONNS][DETWS_SSH_MAX_CHANNELS]
Channel pool: DETWS_SSH_MAX_CHANNELS channels per SSH connection (BSS). Owned by this layer; src/ cod...
int ssh_channel_handle_open_confirm(uint8_t i, const uint8_t *payload, size_t len)
Handle SSH_MSG_CHANNEL_OPEN_CONFIRMATION for a channel we opened (ssh -R).
void ssh_channel_set_rforward_open_cb(SshRemoteForwardOpenCb cb)
Install the remote-forward (ssh -R) open-policy callback (opt-in).
SSH connection protocol - multiplexed "session" channels (RFC 4254).
int(* SshForwardOpenCb)(uint8_t slot, uint32_t channel, const char *host, size_t host_len, uint16_t port)
"direct-tcpip" forward request: a client asked the server to open a TCP connection to host : port (ss...
Definition ssh_channel.h:75
void(* SshChannelDataCb)(uint8_t slot, uint32_t channel, const uint8_t *data, size_t len)
Application callback for inbound channel data (raw bytes), tagged with the channel id it arrived on.
Definition ssh_channel.h:59
void(* SshForwardDataCb)(uint8_t slot, uint32_t channel, const uint8_t *data, size_t len)
Inbound data on a direct-tcpip channel (the owner writes it to the forwarded TCP socket)....
Definition ssh_channel.h:79
@ SSH_CHAN_DIRECT_TCPIP
"direct-tcpip" - client-initiated TCP forward (ssh -L)
@ SSH_CHAN_SESSION
"session" - shell / exec / data
@ SSH_CHAN_FORWARDED_TCPIP
"forwarded-tcpip" - server-initiated TCP forward (ssh -R)
void(* SshForwardConfirmCb)(uint8_t slot, uint32_t channel, bool ok)
Result of the client's reply to a server-initiated forwarded-tcpip channel: ok = true on CHANNEL_OPEN...
int(* SshRemoteForwardCancelCb)(uint8_t slot, const char *bind_addr, size_t addr_len, uint16_t bind_port)
"cancel-tcpip-forward" request (RFC 4254 §7.1): drop a remote forward.
int(* SshRemoteForwardOpenCb)(uint8_t slot, const char *bind_addr, size_t addr_len, uint16_t bind_port)
"tcpip-forward" remote-forward request (ssh -R): the client asks the server to listen on bind_addr : ...
Definition ssh_channel.h:97
SSH binary packet protocol: framing, AES-256-CTR encryption, HMAC-SHA2-256 integrity,...
#define SSH_MSG_CHANNEL_SUCCESS
Definition ssh_packet.h:121
#define SSH_MSG_CHANNEL_EOF
Definition ssh_packet.h:118
#define SSH_MSG_CHANNEL_DATA
Definition ssh_packet.h:117
#define SSH_MSG_CHANNEL_FAILURE
Definition ssh_packet.h:122
#define SSH_MSG_CHANNEL_OPEN_CONFIRM
Definition ssh_packet.h:114
#define SSH_MSG_CHANNEL_OPEN
Definition ssh_packet.h:113
#define SSH_MSG_CHANNEL_REQUEST
Definition ssh_packet.h:120
#define SSH_MSG_CHANNEL_OPEN_FAILURE
Definition ssh_packet.h:115
#define SSH_MSG_REQUEST_SUCCESS
Definition ssh_packet.h:111
#define SSH_MSG_CHANNEL_CLOSE
Definition ssh_packet.h:119
#define SSH_MSG_REQUEST_FAILURE
Definition ssh_packet.h:112
#define SSH_MSG_GLOBAL_REQUEST
Definition ssh_packet.h:110
#define SSH_MSG_CHANNEL_WINDOW_ADJUST
Definition ssh_packet.h:116
SshForwardOpenCb forward_open_cb
SshRemoteForwardCancelCb rfwd_cancel_cb
SshForwardConfirmCb forward_confirm_cb
SshChannelDataCb data_cb
SshForwardDataCb forward_data_cb
SshRemoteForwardOpenCb rfwd_open_cb
Per-connection channel state.
Definition ssh_channel.h:41
bool open
True once the channel is confirmed open both ways.
Definition ssh_channel.h:42
uint32_t peer_max_pkt
Client's maximum packet size.
Definition ssh_channel.h:49
uint32_t peer_window
Bytes we may still send to the client.
Definition ssh_channel.h:48
SshChanType type
session, direct-tcpip, or forwarded-tcpip.
Definition ssh_channel.h:44
bool pending
True for a server-initiated channel we opened, awaiting the client's confirmation.
Definition ssh_channel.h:43
uint32_t local_id
Our channel id (== slot index).
Definition ssh_channel.h:45
uint32_t local_window
Bytes we may still receive before WINDOW_ADJUST.
Definition ssh_channel.h:47
uint32_t peer_id
Client's channel id.
Definition ssh_channel.h:46