From 8aaf28cbfab59a13733965ddcd633dc176eba7ba Mon Sep 17 00:00:00 2001 From: Niek van der Maas Date: Mon, 23 Sep 2019 10:33:35 +0200 Subject: [PATCH 1/4] Added support for reverse telnet --- code/espurna/config/general.h | 8 +++ code/espurna/telnet.ino | 117 ++++++++++++++++++++++++++++------ 2 files changed, 104 insertions(+), 21 deletions(-) diff --git a/code/espurna/config/general.h b/code/espurna/config/general.h index ef8742f830..bbe5c2c83c 100644 --- a/code/espurna/config/general.h +++ b/code/espurna/config/general.h @@ -130,6 +130,13 @@ #define TELNET_SERVER TELNET_SERVER_ASYNC // Can be either TELNET_SERVER_ASYNC (using ESPAsyncTCP) or TELNET_SERVER_WIFISERVER (using WiFiServer) #endif +// Enable this flag to add support for reverse telnet (+800 bytes) +// This is useful to telnet to a device behind a NAT or firewall +// To use this feature, start a listen server on a publicly reachable host with e.g. "ncat -vlp " and use the MQTT reverse telnet command to connect +#ifndef TELNET_REVERSE_SUPPORT +#define TELNET_REVERSE_SUPPORT 0 +#endif + //------------------------------------------------------------------------------ // TERMINAL //------------------------------------------------------------------------------ @@ -1068,6 +1075,7 @@ #define MQTT_TOPIC_IRIN "irin" #define MQTT_TOPIC_IROUT "irout" #define MQTT_TOPIC_OTA "ota" +#define MQTT_TOPIC_TELNET_REVERSE "telnet_reverse" // Light module #define MQTT_TOPIC_CHANNEL "channel" diff --git a/code/espurna/telnet.ino b/code/espurna/telnet.ino index 063575e212..9091f7941c 100644 --- a/code/espurna/telnet.ino +++ b/code/espurna/telnet.ino @@ -42,6 +42,58 @@ void _telnetWebSocketOnConnected(JsonObject& root) { #endif +#if TELNET_REVERSE_SUPPORT + void _telnetReverse(const char * host, uint16_t port) { + DEBUG_MSG_P(PSTR("[TELNET] Connecting to reverse telnet on %s:%d\n"), host, port); + + unsigned char i; + for (i = 0; i < TELNET_MAX_CLIENTS; i++) { + if (!_telnetClients[i] || !_telnetClients[i]->connected()) { + #if TELNET_SERVER == TELNET_SERVER_WIFISERVER + _telnetClients[i] = std::unique_ptr(new WiFiClient()); + #else // TELNET_SERVER_ASYNC + _telnetClients[i] = std::unique_ptr(client); + #endif + + if (_telnetClients[i]->connect(host, port)) { + return _telnetNotifyConnected(i); + } else { + DEBUG_MSG_P(PSTR("[TELNET] Error connecting reverse telnet\n")); + return _telnetDisconnect(i); + } + } + } + + //no free/disconnected spot so reject + if (i == TELNET_MAX_CLIENTS) { + DEBUG_MSG_P(PSTR("[TELNET] Failed too connect - too many clients connected\n")); + } + } + + #if MQTT_SUPPORT + void _telnetReverseMQTTCallback(unsigned int type, const char * topic, const char * payload) { + if (type == MQTT_CONNECT_EVENT) { + mqttSubscribe(MQTT_TOPIC_TELNET_REVERSE); + } else if (type == MQTT_MESSAGE_EVENT) { + String t = mqttMagnitude((char *) topic); + + if (t.equals(MQTT_TOPIC_TELNET_REVERSE)) { + String pl = String(payload); + int col = pl.indexOf(':'); + if (col != -1) { + String host = pl.substring(0, col); + uint16_t port = pl.substring(col + 1).toInt(); + + _telnetReverse(host.c_str(), port); + } else { + DEBUG_MSG_P(PSTR("[TELNET] Incorrect reverse telnet value given, use the form \"host:ip\"")); + } + } + } + } + #endif +#endif + void _telnetDisconnect(unsigned char clientId) { // ref: we are called from onDisconnect, async is already stopped #if TELNET_SERVER == TELNET_SERVER_WIFISERVER @@ -132,6 +184,28 @@ void _telnetNotifyConnected(unsigned char i) { DEBUG_MSG_P(PSTR("[TELNET] Client #%u connected\n"), i); + #if TELNET_SERVER == TELNET_SERVER_ASYNC + _telnetClients[i]->onAck([i](void *s, AsyncClient *c, size_t len, uint32_t time) { + }, 0); + + _telnetClients[i]->onData([i](void *s, AsyncClient *c, void *data, size_t len) { + _telnetData(i, data, len); + }, 0); + + _telnetClients[i]->onDisconnect([i](void *s, AsyncClient *c) { + _telnetDisconnect(i); + }, 0); + + _telnetClients[i]->onError([i](void *s, AsyncClient *c, int8_t error) { + DEBUG_MSG_P(PSTR("[TELNET] Error %s (%d) on client #%u\n"), c->errorToString(error), error, i); + }, 0); + + _telnetClients[i]->onTimeout([i](void *s, AsyncClient *c, uint32_t time) { + DEBUG_MSG_P(PSTR("[TELNET] Timeout on client #%u at %lu\n"), i, time); + c->close(); + }, 0); + #endif + // If there is no terminal support automatically dump info and crash data #if TERMINAL_SUPPORT == 0 info(); @@ -209,7 +283,7 @@ void _telnetLoop() { char data[TERMINAL_BUFFER_SIZE]; size_t len = _telnetClients[i]->available(); unsigned int r = _telnetClients[i]->readBytes(data, min(sizeof(data), len)); - + _telnetData(i, data, r); } } @@ -244,26 +318,6 @@ void _telnetNewClient(AsyncClient* client) { _telnetClients[i] = std::unique_ptr(client); - _telnetClients[i]->onAck([i](void *s, AsyncClient *c, size_t len, uint32_t time) { - }, 0); - - _telnetClients[i]->onData([i](void *s, AsyncClient *c, void *data, size_t len) { - _telnetData(i, data, len); - }, 0); - - _telnetClients[i]->onDisconnect([i](void *s, AsyncClient *c) { - _telnetDisconnect(i); - }, 0); - - _telnetClients[i]->onError([i](void *s, AsyncClient *c, int8_t error) { - DEBUG_MSG_P(PSTR("[TELNET] Error %s (%d) on client #%u\n"), c->errorToString(error), error, i); - }, 0); - - _telnetClients[i]->onTimeout([i](void *s, AsyncClient *c, uint32_t time) { - DEBUG_MSG_P(PSTR("[TELNET] Timeout on client #%u at %lu\n"), i, time); - c->close(); - }, 0); - _telnetNotifyConnected(i); return; } @@ -318,6 +372,27 @@ void telnetSetup() { .onKeyCheck(_telnetWebSocketOnKeyCheck); #endif + #if TELNET_REVERSE_SUPPORT + #if MQTT_SUPPORT + mqttRegister(_telnetReverseMQTTCallback); + #endif + + #if TERMINAL_SUPPORT + terminalRegisterCommand(F("TELNET.REVERSE"), [](Embedis* e) { + if (e->argc < 3) { + terminalError(F("Wrong arguments. Usage: TELNET.REVERSE ")); + return; + } + + String host = String(e->argv[1]); + uint16_t port = String(e->argv[2]).toInt(); + + terminalOK(); + _telnetReverse(host.c_str(), port); + }); + #endif + #endif + espurnaRegisterReload(_telnetConfigure); _telnetConfigure(); From e37b10579cf02fee4de7d48e096128f76c3e480c Mon Sep 17 00:00:00 2001 From: Niek van der Maas Date: Tue, 24 Sep 2019 11:31:14 +0200 Subject: [PATCH 2/4] Fix issues with first command being ignored --- code/espurna/telnet.ino | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/code/espurna/telnet.ino b/code/espurna/telnet.ino index 9091f7941c..9554b86ba0 100644 --- a/code/espurna/telnet.ino +++ b/code/espurna/telnet.ino @@ -20,7 +20,7 @@ Parts of the code have been borrowed from Thomas Sarlandie's NetServer std::unique_ptr _telnetClients[TELNET_MAX_CLIENTS]; #endif -bool _telnetFirst = true; +bool _telnetFirst = false; bool _telnetAuth = TELNET_AUTHENTICATION; bool _telnetClientsAuth[TELNET_MAX_CLIENTS]; @@ -227,7 +227,6 @@ void _telnetNotifyConnected(unsigned char i) { } #endif - _telnetFirst = true; wifiReconnectCheck(); } @@ -258,6 +257,7 @@ void _telnetLoop() { } } + _telnetFirst = true; _telnetNotifyConnected(i); break; @@ -318,6 +318,8 @@ void _telnetNewClient(AsyncClient* client) { _telnetClients[i] = std::unique_ptr(client); + _telnetFirst = true; + _telnetNotifyConnected(i); return; } From d62ecf2b6d8bfa814db8373255d4730699c9845e Mon Sep 17 00:00:00 2001 From: Niek van der Maas Date: Thu, 26 Sep 2019 10:18:31 +0200 Subject: [PATCH 3/4] Fix incorrect client creation --- code/espurna/telnet.ino | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/espurna/telnet.ino b/code/espurna/telnet.ino index 9554b86ba0..802b12b222 100644 --- a/code/espurna/telnet.ino +++ b/code/espurna/telnet.ino @@ -50,9 +50,9 @@ void _telnetWebSocketOnConnected(JsonObject& root) { for (i = 0; i < TELNET_MAX_CLIENTS; i++) { if (!_telnetClients[i] || !_telnetClients[i]->connected()) { #if TELNET_SERVER == TELNET_SERVER_WIFISERVER - _telnetClients[i] = std::unique_ptr(new WiFiClient()); + _telnetClients[i] = std::make_unique(); #else // TELNET_SERVER_ASYNC - _telnetClients[i] = std::unique_ptr(client); + _telnetClients[i] = std::make_unique(); #endif if (_telnetClients[i]->connect(host, port)) { From 6159aabb5413d5e5c95462d2a91e0eea9624cafa Mon Sep 17 00:00:00 2001 From: Niek van der Maas Date: Fri, 27 Sep 2019 10:50:49 +0200 Subject: [PATCH 4/4] Integrate PR 1927 --- code/espurna/telnet.ino | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/code/espurna/telnet.ino b/code/espurna/telnet.ino index 802b12b222..3ab6a09d68 100644 --- a/code/espurna/telnet.ino +++ b/code/espurna/telnet.ino @@ -10,6 +10,9 @@ Parts of the code have been borrowed from Thomas Sarlandie's NetServer #if TELNET_SUPPORT +#define TELNET_IAC 0xFF +#define TELNET_XEOF 0xEC + #if TELNET_SERVER == TELNET_SERVER_WIFISERVER #include WiFiServer _telnetServer = WiFiServer(TELNET_PORT); @@ -20,8 +23,6 @@ Parts of the code have been borrowed from Thomas Sarlandie's NetServer std::unique_ptr _telnetClients[TELNET_MAX_CLIENTS]; #endif -bool _telnetFirst = false; - bool _telnetAuth = TELNET_AUTHENTICATION; bool _telnetClientsAuth[TELNET_MAX_CLIENTS]; @@ -133,21 +134,15 @@ bool _telnetWrite(unsigned char clientId, const char * message) { } void _telnetData(unsigned char clientId, void *data, size_t len) { - // Skip first message since it's always garbage - if (_telnetFirst) { - _telnetFirst = false; - return; - } - // Capture close connection char * p = (char *) data; - // C-d is sent as two bytes (sometimes repeating) - if (len >= 2) { - if ((p[0] == 0xFF) && (p[1] == 0xEC)) { + if ((len >= 2) && (p[0] == TELNET_IAC)) { + // C-d is sent as two bytes (sometimes repeating) + if (p[1] == TELNET_XEOF) { _telnetDisconnect(clientId); - return; } + return; // Ignore telnet negotiation } if ((strncmp(p, "close", 5) == 0) || (strncmp(p, "quit", 4) == 0)) { @@ -257,7 +252,6 @@ void _telnetLoop() { } } - _telnetFirst = true; _telnetNotifyConnected(i); break; @@ -318,8 +312,6 @@ void _telnetNewClient(AsyncClient* client) { _telnetClients[i] = std::unique_ptr(client); - _telnetFirst = true; - _telnetNotifyConnected(i); return; }