11#if DETWS_SSH_PORT_FORWARD
41 SshForwardPolicyCb policy =
nullptr;
43static SshFwdCtx s_fwd;
47static const int kFwdBurst = 4;
49static int fwd_find_free()
52 if (!s_fwd.fwd[i].active)
57static SshFwd *fwd_lookup(uint8_t ssh_slot, uint32_t channel)
60 if (s_fwd.fwd[i].active && s_fwd.fwd[i].ssh_slot == ssh_slot && s_fwd.fwd[i].channel == channel)
99static SshRFwdCtx s_rfwd;
101static int rbind_find_free()
104 if (!s_rfwd.rbind[i].active)
108static SshRFwdBind *rbind_by_listener(uint8_t listener_idx)
111 if (s_rfwd.rbind[i].active && s_rfwd.rbind[i].listener_idx == listener_idx)
112 return &s_rfwd.rbind[i];
115static SshRFwdBind *rbind_find(uint8_t ssh_slot, uint16_t port)
118 if (s_rfwd.rbind[i].active && s_rfwd.rbind[i].ssh_slot == ssh_slot && s_rfwd.rbind[i].bind_port == port)
119 return &s_rfwd.rbind[i];
122static int rbridge_find_free()
125 if (!s_rfwd.rbridge[i].active)
129static SshRFwdBridge *rbridge_by_conn(uint8_t conn_slot)
132 if (s_rfwd.rbridge[i].active && s_rfwd.rbridge[i].conn_slot == conn_slot)
133 return &s_rfwd.rbridge[i];
136static SshRFwdBridge *rbridge_by_channel(uint8_t ssh_slot, uint32_t channel)
139 if (s_rfwd.rbridge[i].active && s_rfwd.rbridge[i].ssh_slot == ssh_slot && s_rfwd.rbridge[i].channel == channel)
140 return &s_rfwd.rbridge[i];
148static void rbridge_pump_to_client(SshRFwdBridge *br)
154 for (
int burst = 0; burst < 4; burst++)
156 size_t avail = det_conn_available(br->conn_slot);
158 if (avail == 0 || win == 0 || !c->
open)
160 size_t budget = avail;
165 if (budget >
sizeof(buf))
166 budget =
sizeof(buf);
167 size_t n = det_conn_read(br->conn_slot, buf, budget);
177static int on_forward_open(uint8_t ssh_slot, uint32_t channel,
const char *host,
size_t host_len, uint16_t port)
179 int idx = fwd_find_free();
183 if (host_len == 0 || host_len >=
sizeof(hbuf))
185 memcpy(hbuf, host, host_len);
187 if (s_fwd.policy && !s_fwd.policy(hbuf, port))
192 s_fwd.fwd[idx].active =
true;
193 s_fwd.fwd[idx].ssh_slot = ssh_slot;
194 s_fwd.fwd[idx].channel = channel;
195 s_fwd.fwd[idx].cid = cid;
201static void on_forward_data(uint8_t ssh_slot, uint32_t channel,
const uint8_t *data,
size_t len)
203 SshFwd *f = fwd_lookup(ssh_slot, channel);
209 SshRFwdBridge *br = rbridge_by_channel(ssh_slot, channel);
210 if (br && br->confirmed)
223static int on_rforward_open(uint8_t ssh_slot,
const char *addr,
size_t addr_len, uint16_t bind_port)
227 if (rbind_find(ssh_slot, bind_port))
229 int bi = rbind_find_free();
246 s_rfwd.rbind[bi].active =
true;
247 s_rfwd.rbind[bi].ssh_slot = ssh_slot;
248 s_rfwd.rbind[bi].listener_idx = (uint8_t)li;
249 s_rfwd.rbind[bi].bind_port = bind_port;
250 size_t al = addr_len <
sizeof(s_rfwd.rbind[bi].bind_addr) - 1 ? addr_len : sizeof(s_rfwd.rbind[bi].bind_addr) - 1;
251 memcpy(s_rfwd.rbind[bi].bind_addr, addr, al);
252 s_rfwd.rbind[bi].bind_addr[al] = 0;
257static int on_rforward_cancel(uint8_t ssh_slot,
const char *addr,
size_t addr_len, uint16_t bind_port)
261 SshRFwdBind *b = rbind_find(ssh_slot, bind_port);
270static void on_forward_confirm(uint8_t ssh_slot, uint32_t channel,
bool ok)
272 SshRFwdBridge *br = rbridge_by_channel(ssh_slot, channel);
276 br->confirmed =
true;
288static void rfwd_on_accept(uint8_t conn_slot)
290 SshRFwdBind *b = rbind_by_listener(
conn_pool[conn_slot].listener_id);
296 int idx = rbridge_find_free();
311 int ch =
ssh_conn_open_forwarded(b->ssh_slot, b->bind_addr[0] ? b->bind_addr :
"0.0.0.0", b->bind_port, orig, 0);
317 s_rfwd.rbridge[idx].active =
true;
318 s_rfwd.rbridge[idx].confirmed =
false;
319 s_rfwd.rbridge[idx].ssh_slot = b->ssh_slot;
320 s_rfwd.rbridge[idx].conn_slot = conn_slot;
321 s_rfwd.rbridge[idx].channel = (uint32_t)ch;
324static void rfwd_on_data(uint8_t conn_slot)
326 SshRFwdBridge *br = rbridge_by_conn(conn_slot);
327 if (br && br->confirmed)
328 rbridge_pump_to_client(br);
331static void rfwd_on_close(uint8_t conn_slot)
333 SshRFwdBridge *br = rbridge_by_conn(conn_slot);
340static void rfwd_on_poll(uint8_t conn_slot)
344 if (!det_conn_active(conn_slot))
346 SshRFwdBridge *br = rbridge_by_conn(conn_slot);
347 if (!br || !br->confirmed)
356 rbridge_pump_to_client(br);
359static const ProtoHandler s_rfwd_handler = {rfwd_on_accept, rfwd_on_data, rfwd_on_close, rfwd_on_poll};
361void ssh_forward_set_policy_cb(SshForwardPolicyCb cb)
366void ssh_forward_begin()
369 s_fwd.fwd[i].active =
false;
371 s_rfwd.rbind[i].active =
false;
373 s_rfwd.rbridge[i].active =
false;
384void ssh_forward_pump(uint8_t ssh_slot)
389 SshFwd *f = &s_fwd.fwd[i];
390 if (!f->active || f->ssh_slot != ssh_slot)
409 for (
int burst = 0; burst < kFwdBurst; burst++)
413 if (avail == 0 || win == 0)
415 size_t budget = avail;
420 if (budget >
sizeof(buf))
421 budget =
sizeof(buf);
439void ssh_forward_reset(uint8_t ssh_slot)
443 if (s_fwd.fwd[i].active && s_fwd.fwd[i].ssh_slot == ssh_slot)
446 s_fwd.fwd[i].active =
false;
451 if (s_rfwd.rbind[i].active && s_rfwd.rbind[i].ssh_slot == ssh_slot)
454 s_rfwd.rbind[i].active =
false;
457 if (s_rfwd.rbridge[i].active && s_rfwd.rbridge[i].ssh_slot == ssh_slot)
460 s_rfwd.rbridge[i].active =
false;
#define MAX_LISTENERS
Maximum number of simultaneously active listener ports.
@ PROTO_SSH_RFWD
SSH remote-forward listener (ssh -R): accepts bridge to a forwarded-tcpip channel.
#define DETWS_SSH_RFWD_BRIDGE_MAX
Maximum concurrent bridged connections across all remote forwards.
#define DETWS_SSH_FWD_CHUNK
Max bytes moved per forward channel per poll, target -> client (<= SSH_PKT_BUF_SIZE).
#define DETWS_SSH_FWD_MAX
Maximum concurrent forwarded TCP connections (must be <= DETWS_CLIENT_CONNS).
#define DETWS_SSH_FWD_CONNECT_MS
Blocking connect timeout (ms) when opening a forward target.
#define DETWS_SSH_FWD_HOST_MAX
Maximum forward target hostname length including null terminator.
#define DETWS_SSH_RFWD_MAX
Maximum concurrent remote-forward listeners (ssh -R / tcpip-forward).
#define DETWS_SSH_MAX_CHANNELS
Maximum concurrent SSH channels per connection (RFC 4254 multiplexing).
size_t det_client_available(int)
Wire bytes currently buffered and ready to read.
bool det_client_is_closed(int)
True once the peer closed (FIN) or the connection errored.
int det_client_open(const char *, uint16_t, uint32_t)
Resolve host (dotted-quad fast path, else DNS) and connect to host : port, blocking up to timeout_ms.
size_t det_client_read(int, uint8_t *, size_t)
Drain up to cap buffered wire bytes into buf; returns the count.
void det_client_close(int)
Tear down the connection (marshaled) and return the slot to the pool.
bool det_client_send(int, const void *, size_t)
Queue len wire bytes for transmission (marshaled tcp_write + output).
Layer 4 outbound TCP client transport - the client-side peer of the (server) transport in tcp....
size_t det_ip_format(const DetIp *ip, char *out, size_t cap)
Format ip into out as its RFC 5952 canonical text.
Layer 3 (Network) - a family-tagged IP address (IPv4 or IPv6) with RFC-faithful text parsing,...
#define DET_IP_STR_MAX
Longest text an det_ip_format can produce, including the NUL (RFC 5952 v4-mapped).
int32_t listener_add_dynamic(uint8_t idx, uint16_t port, ConnProto proto)
Add / stop a listener from a running task (thread-safe variant).
Listener listener_pool[MAX_LISTENERS]
Static pool of listener contexts. Defined in listener.cpp.
void listener_stop_dynamic(uint8_t idx)
Layer 4 (Listener) - per-port TCP listener abstraction.
Layer 5 (Session) - per-protocol connection handler dispatch table.
void proto_register(ConnProto proto, const ProtoHandler *h)
Register h for protocol proto (replaces any previous handler).
void ssh_channel_set_forward_data_cb(SshForwardDataCb cb)
Install the direct-tcpip forward inbound-data callback.
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).
void ssh_channel_set_rforward_cancel_cb(SshRemoteForwardCancelCb cb)
Install the remote-forward (ssh -R) cancel callback (opt-in).
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...
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 ssh_conn_close_channel(uint8_t ssh_slot, uint32_t channel)
Close an SSH channel from the server side: frame CHANNEL_EOF and CHANNEL_CLOSE as two binary packets ...
int ssh_conn_open_forwarded(uint8_t ssh_slot, const char *conn_addr, uint16_t conn_port, const char *orig_addr, uint16_t orig_port)
Open a server-initiated "forwarded-tcpip" channel to the client (ssh -R): build the CHANNEL_OPEN (RFC...
int ssh_conn_send(uint8_t ssh_slot, uint32_t channel, const uint8_t *data, size_t len)
Send application data to the client over an SSH channel.
Glue between the TCP transport (conn_pool) and the SSH protocol stack.
SSH direct-tcpip port-forwarding owner (the ssh -L target side).
A v4 or v6 address in network (big-endian) byte order.
Per-protocol connection event/poll callbacks (Layer 5 dispatch vtable).
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.
bool det_conn_send(uint8_t slot, const void *data, u16_t len)
Send len bytes on connection slot (copies data; TLS-aware).
void det_conn_flush(uint8_t slot)
Flush queued bytes / finish the send on slot (TLS-aware).
void det_conn_close(uint8_t slot)
Close connection slot gracefully (tcp_close), aborting if the FIN cannot be queued....
TcpConn conn_pool[CONN_POOL_SLOTS]
Static pool of connection contexts. Defined in tcp.cpp. Sized CONN_POOL_SLOTS: MAX_CONNS TCP slots pl...
bool det_conn_remote_addr(uint8_t slot, DetIp *out)
The connected peer's address as a family-tagged DetIp (IPv4 or IPv6).
Layer 4 (Transport) - TCP connection pool, ring buffers, and lwIP integration.