Skip to content

Commit

Permalink
Added support for reverse telnet (#1920)
Browse files Browse the repository at this point in the history
* Added support for reverse telnet

* Fix issues with first command being ignored

* Fix incorrect client creation

* Integrate PR 1927
  • Loading branch information
Niek authored and mcspr committed Oct 9, 2019
1 parent cf4540a commit f3b3556
Show file tree
Hide file tree
Showing 2 changed files with 104 additions and 21 deletions.
8 changes: 8 additions & 0 deletions code/espurna/config/general.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 <port>" and use the MQTT reverse telnet command to connect
#ifndef TELNET_REVERSE_SUPPORT
#define TELNET_REVERSE_SUPPORT 0
#endif

//------------------------------------------------------------------------------
// TERMINAL
//------------------------------------------------------------------------------
Expand Down Expand Up @@ -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"
Expand Down
117 changes: 96 additions & 21 deletions code/espurna/telnet.ino
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,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::make_unique<WiFiClient>();
#else // TELNET_SERVER_ASYNC
_telnetClients[i] = std::make_unique<AsyncClient>();
#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
Expand Down Expand Up @@ -127,6 +179,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();
Expand Down Expand Up @@ -203,7 +277,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);
}
}
Expand Down Expand Up @@ -238,26 +312,6 @@ void _telnetNewClient(AsyncClient* client) {

_telnetClients[i] = std::unique_ptr<AsyncClient>(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;
}
Expand Down Expand Up @@ -312,6 +366,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 <host> <port>"));
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();

Expand Down

0 comments on commit f3b3556

Please sign in to comment.