75static uint32_t rd_u32(
const uint8_t *p)
77 return ((uint32_t)p[0] << 24) | ((uint32_t)p[1] << 16) | ((uint32_t)p[2] << 8) | (uint32_t)p[3];
80static void wr_u32(uint8_t *p, uint32_t v)
82 p[0] = (uint8_t)(v >> 24);
83 p[1] = (uint8_t)(v >> 16);
84 p[2] = (uint8_t)(v >> 8);
89static bool rd_string(
const uint8_t *p,
size_t len,
size_t *off,
const uint8_t **out, uint32_t *slen)
93 uint32_t n = rd_u32(p + *off);
108static SshChannel *chan_by_id(uint8_t i, uint32_t
id)
117static SshChannel *chan_pending_by_id(uint8_t i, uint32_t
id)
126static int chan_alloc(uint8_t i)
140static int build_open_failure(uint8_t *out,
size_t cap, uint32_t sender, uint32_t reason,
size_t *out_len)
145 wr_u32(out + 1, sender);
146 wr_u32(out + 5, reason);
154static int build_open_confirm(
const SshChannel *c, uint8_t *out,
size_t cap,
size_t *out_len)
180 if (!rd_string(payload, len, &off, &name, &name_len))
184 bool want_reply = payload[off++] != 0;
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);
189 if (is_fwd || is_cancel)
194 if (!rd_string(payload, len, &off, &addr, &addr_len) || off + 4 > len)
196 uint16_t bind_port = (uint16_t)rd_u32(payload + off);
201 bound = s_chcb.
rfwd_open_cb(i, (
const char *)addr, addr_len, bind_port);
203 bound = s_chcb.
rfwd_cancel_cb(i, (
const char *)addr, addr_len, bind_port);
221 if (is_fwd && bind_port == 0)
226 wr_u32(out + 1, (uint32_t)(uint16_t)bound);
258 uint16_t orig_port, uint8_t *out,
size_t *out_len,
size_t cap)
263 int slot = chan_alloc(i);
267 const char *type =
"forwarded-tcpip";
268 size_t tl = 15, ca = strnlen(conn_addr, cap), oa = strnlen(orig_addr, cap);
271 size_t need = 1 + (4 + tl) + 12 + (4 + ca) + 4 + (4 + oa) + 4;
277 wr_u32(out + off, (uint32_t)tl);
278 memcpy(out + off + 4, type, tl);
280 wr_u32(out + off, (uint32_t)slot);
284 wr_u32(out + off, (uint32_t)ca);
285 memcpy(out + off + 4, conn_addr, ca);
287 wr_u32(out + off, conn_port);
289 wr_u32(out + off, (uint32_t)oa);
290 memcpy(out + off + 4, orig_addr, oa);
292 wr_u32(out + off, orig_port);
314 SshChannel *c = chan_pending_by_id(i, rd_u32(payload + 1));
317 c->
peer_id = rd_u32(payload + 5);
332 SshChannel *c = chan_pending_by_id(i, rd_u32(payload + 1));
351 if (!rd_string(payload, len, &off, &type, &type_len))
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);
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);
366 const uint8_t *fhost =
nullptr;
367 uint32_t fhost_len = 0;
372 return build_open_failure(out, cap, sender, 1u, out_len);
373 if (!rd_string(payload, len, &off, &fhost, &fhost_len) || off + 4 > len)
375 fport = (uint16_t)rd_u32(payload + off);
378 int slot = chan_alloc(i);
380 return build_open_failure(out, cap, sender, 4u, out_len);
398 return build_open_failure(out, cap, sender, 2u, out_len);
401 return build_open_confirm(c, out, cap, out_len);
417 uint32_t recipient = rd_u32(payload + off);
419 const uint8_t *rtype;
421 if (!rd_string(payload, len, &off, &rtype, &rtype_len))
425 bool want_reply = payload[off++] != 0;
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);
459 uint32_t recipient = rd_u32(payload + off);
463 if (!rd_string(payload, len, &off, &data, &dlen))
494 wr_u32(out + 5, add);
514 if (cap < 1 + 4 + 4 + len)
519 wr_u32(out + 5, (uint32_t)len);
520 memcpy(out + 9, data, len);
534 SshChannel *c = chan_by_id(i, rd_u32(payload + 1));
537 uint32_t add = rd_u32(payload + 5);
540 c->
peer_window = (w + add < w) ? 0xFFFFFFFFu : (w + add);
550static int build_close_chan(
SshChannel *c, uint8_t *out,
size_t *out_len,
size_t cap)
556 wr_u32(out + 1, peer);
558 wr_u32(out + 6, peer);
568 return build_close_chan(chan_by_id(i, channel), out, out_len, cap);
576 return build_close_chan(chan_by_id(i, rd_u32(payload + 1)), out, out_len, cap);
#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...
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.
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)....
@ 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 : ...
SSH binary packet protocol: framing, AES-256-CTR encryption, HMAC-SHA2-256 integrity,...
#define SSH_MSG_CHANNEL_SUCCESS
#define SSH_MSG_CHANNEL_EOF
#define SSH_MSG_CHANNEL_DATA
#define SSH_MSG_CHANNEL_FAILURE
#define SSH_MSG_CHANNEL_OPEN_CONFIRM
#define SSH_MSG_CHANNEL_OPEN
#define SSH_MSG_CHANNEL_REQUEST
#define SSH_MSG_CHANNEL_OPEN_FAILURE
#define SSH_MSG_REQUEST_SUCCESS
#define SSH_MSG_CHANNEL_CLOSE
#define SSH_MSG_REQUEST_FAILURE
#define SSH_MSG_GLOBAL_REQUEST
#define SSH_MSG_CHANNEL_WINDOW_ADJUST
SshForwardOpenCb forward_open_cb
SshRemoteForwardCancelCb rfwd_cancel_cb
SshForwardConfirmCb forward_confirm_cb
SshForwardDataCb forward_data_cb
SshRemoteForwardOpenCb rfwd_open_cb
Per-connection channel state.
bool open
True once the channel is confirmed open both ways.
uint32_t peer_max_pkt
Client's maximum packet size.
uint32_t peer_window
Bytes we may still send to the client.
SshChanType type
session, direct-tcpip, or forwarded-tcpip.
bool pending
True for a server-initiated channel we opened, awaiting the client's confirmation.
uint32_t local_id
Our channel id (== slot index).
uint32_t local_window
Bytes we may still receive before WINDOW_ADJUST.
uint32_t peer_id
Client's channel id.