11#if DETWS_ENABLE_TELNET
23 static constexpr uint8_t T_SE = 240;
24 static constexpr uint8_t T_SB = 250;
25 static constexpr uint8_t T_WILL = 251;
26 static constexpr uint8_t T_WONT = 252;
27 static constexpr uint8_t T_DO = 253;
28 static constexpr uint8_t T_DONT = 254;
29 static constexpr uint8_t T_IAC = 255;
30 static constexpr uint8_t OPT_ECHO = 1;
31 static constexpr uint8_t OPT_SGA = 3;
35enum class TelnetState : uint8_t
58 TelnetCommandCb cmd_cb =
nullptr;
60static TelnetCtx s_telnet;
62static TelnetConn *find_conn(uint8_t slot)
65 if (s_telnet.tn[i].used && s_telnet.tn[i].slot == slot)
66 return &s_telnet.tn[i];
70static void raw_send(uint8_t slot,
const void *data,
size_t n)
72 if (!det_conn_active(slot) || n == 0)
82static void send_escaped(uint8_t slot,
const void *data,
size_t n)
84 if (!det_conn_active(slot) || n == 0)
86 const uint8_t *b = (
const uint8_t *)data;
88 for (
size_t i = 0; i < n; i++)
107void telnet_accept(uint8_t slot)
109 TelnetConn *t =
nullptr;
111 if (!s_telnet.tn[i].used)
122 memset(t, 0,
sizeof(*t));
125 t->st = TelnetState::TN_NORMAL;
128 static const uint8_t neg[] = {TelnetByte::T_IAC, TelnetByte::T_WILL, TelnetByte::OPT_ECHO,
129 TelnetByte::T_IAC, TelnetByte::T_WILL, TelnetByte::OPT_SGA};
130 raw_send(slot, neg,
sizeof(neg));
131 raw_send(slot,
"DetWS Telnet ready\r\n> ", 22);
134void telnet_close(uint8_t slot)
136 TelnetConn *t = find_conn(slot);
142static void handle_data(uint8_t slot, TelnetConn *t, uint8_t b)
148 t->line[t->len] =
'\0';
149 raw_send(slot,
"\r\n", 2);
151 s_telnet.cmd_cb(t->line, (uint8_t)(t - s_telnet.tn));
153 raw_send(slot,
"> ", 2);
156 if (b == 0x08 || b == 0x7F)
161 raw_send(slot,
"\b \b", 3);
167 if (t->len <
sizeof(t->line) - 1)
169 t->line[t->len++] = (char)b;
170 send_escaped(slot, &b, 1);
174void telnet_rx(uint8_t slot)
176 TelnetConn *t = find_conn(slot);
181 while (det_conn_read_byte(slot, &b))
185 case TelnetState::TN_NORMAL:
186 if (b == TelnetByte::T_IAC)
187 t->st = TelnetState::TN_IAC;
189 handle_data(slot, t, b);
191 case TelnetState::TN_IAC:
192 if (b == TelnetByte::T_SB)
193 t->st = TelnetState::TN_SB;
194 else if (b == TelnetByte::T_WILL || b == TelnetByte::T_WONT || b == TelnetByte::T_DO ||
195 b == TelnetByte::T_DONT)
198 t->st = TelnetState::TN_OPT;
200 else if (b == TelnetByte::T_IAC)
202 handle_data(slot, t, 0xFF);
203 t->st = TelnetState::TN_NORMAL;
206 t->st = TelnetState::TN_NORMAL;
208 case TelnetState::TN_OPT: {
212 if (t->cmd == TelnetByte::T_DO && b != TelnetByte::OPT_ECHO && b != TelnetByte::OPT_SGA)
213 reply = TelnetByte::T_WONT;
214 else if (t->cmd == TelnetByte::T_WILL)
215 reply = TelnetByte::T_DONT;
218 uint8_t resp[3] = {TelnetByte::T_IAC, reply, b};
219 raw_send(slot, resp, 3);
221 t->st = TelnetState::TN_NORMAL;
224 case TelnetState::TN_SB:
225 if (b == TelnetByte::T_SE)
226 t->st = TelnetState::TN_NORMAL;
236void telnet_on_command(TelnetCommandCb cb)
238 s_telnet.cmd_cb = cb;
241static void broadcast(
const char *s,
size_t n)
244 if (s_telnet.tn[i].used)
245 send_escaped(s_telnet.tn[i].slot, s, n);
248void telnet_print(
const char *s)
254void telnet_println(
const char *s)
258 broadcast(
"\r\n", 2);
261void telnet_printf(
const char *fmt, ...)
266 int n = vsnprintf(buf,
sizeof(buf), fmt, ap);
269 broadcast(buf, (
size_t)(n < (
int)
sizeof(buf) ? n : (int)sizeof(buf) - 1));
272uint8_t telnet_client_count()
276 if (s_telnet.tn[i].used)
283static const ProtoHandler s_telnet_handler = {telnet_accept, telnet_rx, telnet_close,
nullptr};
286 return &s_telnet_handler;
#define MAX_TELNET_CONNS
Maximum simultaneous Telnet connections.
#define TELNET_BUF_SIZE
Stack buffer for one Telnet I/O chunk.
Layer 5 (Session) - per-protocol connection handler dispatch table.
Per-protocol connection event/poll callbacks (Layer 5 dispatch vtable).
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....
Layer 4 (Transport) - TCP connection pool, ring buffers, and lwIP integration.
Layer 6/7 - minimal RFC 854 Telnet server (DETWS_ENABLE_TELNET).