Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Telnet server: alternative to ESPAsyncTCP #1799

Merged
merged 15 commits into from
Jul 12, 2019
4 changes: 4 additions & 0 deletions code/espurna/config/general.h
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,10 @@
#define TELNET_PORT 23 // Port to listen to telnet clients
#define TELNET_MAX_CLIENTS 1 // Max number of concurrent telnet clients

#ifndef TELNET_SERVER
#define TELNET_SERVER TELNET_SERVER_ASYNC // Can be either TELNET_SERVER_ASYNC (using ESPAsyncTCP) or TELNET_SERVER_WIFISERVER (using WiFiServer)
#endif

//------------------------------------------------------------------------------
// TERMINAL
//------------------------------------------------------------------------------
Expand Down
6 changes: 6 additions & 0 deletions code/espurna/config/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -343,3 +343,9 @@
#define MAGNITUDE_PH 31

#define MAGNITUDE_MAX 32

//------------------------------------------------------------------------------
// Telnet server
//------------------------------------------------------------------------------
#define TELNET_SERVER_ASYNC 0
#define TELNET_SERVER_WIFISERVER 1
173 changes: 122 additions & 51 deletions code/espurna/telnet.ino
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,16 @@ Parts of the code have been borrowed from Thomas Sarlandie's NetServer

#if TELNET_SUPPORT

#include <ESPAsyncTCP.h>
#if TELNET_SERVER == TELNET_SERVER_WIFISERVER
#include <ESP8266WiFi.h>
WiFiServer _telnetServer = WiFiServer(TELNET_PORT);
std::unique_ptr<WiFiClient> _telnetClients[TELNET_MAX_CLIENTS];
#else
#include <ESPAsyncTCP.h>
AsyncServer _telnetServer = AsyncServer(TELNET_PORT);
std::unique_ptr<AsyncClient> _telnetClients[TELNET_MAX_CLIENTS];
#endif

AsyncServer * _telnetServer;
AsyncClient * _telnetClients[TELNET_MAX_CLIENTS];
bool _telnetFirst = true;

bool _telnetAuth = TELNET_AUTHENTICATION;
Expand All @@ -38,9 +44,11 @@ void _telnetWebSocketOnSend(JsonObject& root) {
#endif

void _telnetDisconnect(unsigned char clientId) {
_telnetClients[clientId]->free();
delete _telnetClients[clientId];
_telnetClients[clientId] = NULL;
// ref: we are called from onDisconnect, async is already stopped
mcspr marked this conversation as resolved.
Show resolved Hide resolved
#if TELNET_SERVER == TELNET_SERVER_WIFISERVER
_telnetClients[clientId]->stop();
#endif
_telnetClients[clientId] = nullptr;
wifiReconnectCheck();
DEBUG_MSG_P(PSTR("[TELNET] Client #%d disconnected\n"), clientId);
}
Expand Down Expand Up @@ -74,7 +82,6 @@ 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;
Expand All @@ -87,13 +94,13 @@ void _telnetData(unsigned char clientId, void *data, size_t len) {
// C-d is sent as two bytes (sometimes repeating)
if (len >= 2) {
if ((p[0] == 0xFF) && (p[1] == 0xEC)) {
_telnetClients[clientId]->close(true);
_telnetDisconnect(clientId);
return;
}
}

if ((strncmp(p, "close", 5) == 0) || (strncmp(p, "quit", 4) == 0)) {
_telnetClients[clientId]->close();
_telnetDisconnect(clientId);
return;
}

Expand All @@ -108,10 +115,10 @@ void _telnetData(unsigned char clientId, void *data, size_t len) {
String password = getAdminPass();
if (strncmp(p, password.c_str(), password.length()) == 0) {
DEBUG_MSG_P(PSTR("[TELNET] Client #%d authenticated\n"), clientId);
_telnetWrite(clientId, "Welcome!\n");
_telnetWrite(clientId, "Password correct, welcome!\n");
_telnetClientsAuth[clientId] = true;
} else {
_telnetWrite(clientId, "Password: ");
_telnetWrite(clientId, "Password (try again): ");
}
return;
}
Expand All @@ -120,13 +127,93 @@ void _telnetData(unsigned char clientId, void *data, size_t len) {
#if TERMINAL_SUPPORT
terminalInject(data, len);
#endif
}

void _telnetNotifyConnected(unsigned char i) {

DEBUG_MSG_P(PSTR("[TELNET] Client #%u connected\n"), i);

// If there is no terminal support automatically dump info and crash data
#if TERMINAL_SUPPORT == 0
info();
wifiDebug();
crashDump();
crashClear();
#endif

#ifdef ESPURNA_CORE
_telnetClientsAuth[i] = true;
#else
_telnetClientsAuth[i] = !_telnetAuth;
if (_telnetAuth) {
if (getAdminPass().length()) {
_telnetWrite(i, "Password: ");
} else {
_telnetClientsAuth[i] = true;
}
}
#endif

_telnetFirst = true;
wifiReconnectCheck();

}

void _telnetNewClient(AsyncClient *client) {
#if TELNET_SERVER == TELNET_SERVER_WIFISERVER

if (client->localIP() != WiFi.softAPIP()) {
void _telnetLoop() {
if (_telnetServer.hasClient()) {
int i;

for (i = 0; i < TELNET_MAX_CLIENTS; i++) {
if (!_telnetClients[i] || !_telnetClients[i]->connected()) {

_telnetClients[i] = std::unique_ptr<WiFiClient>(new WiFiClient(_telnetServer.available()));

if (_telnetClients[i]->localIP() != WiFi.softAPIP()) {
// Telnet is always available for the ESPurna Core image
#ifdef ESPURNA_CORE
bool telnetSTA = true;
#else
bool telnetSTA = getSetting("telnetSTA", TELNET_STA).toInt() == 1;
#endif

if (!telnetSTA) {
DEBUG_MSG_P(PSTR("[TELNET] Rejecting - Only local connections\n"));
_telnetDisconnect(i);
return;
}
}

_telnetNotifyConnected(i);

break;
}
}

//no free/disconnected spot so reject
if (i == TELNET_MAX_CLIENTS) {
DEBUG_MSG_P(PSTR("[TELNET] Rejecting - Too many connections\n"));
}
}

// Read data from clients
for (int i = 0; i < TELNET_MAX_CLIENTS; i++) {
while (_telnetClients[i] && _telnetClients[i]->available()) {
Niek marked this conversation as resolved.
Show resolved Hide resolved
char data[512];
size_t len = _telnetClients[i]->available();
unsigned int r = _telnetClients[i]->readBytes(data, min(sizeof(data), len));

_telnetData(i, data, r);
}
}
}

#else // TELNET_SERVER_ASYNC

void _telnetNewClient(AsyncClient* client) {

if (client->localIP() != WiFi.softAPIP()) {
// Telnet is always available for the ESPurna Core image
#ifdef ESPURNA_CORE
bool telnetSTA = true;
Expand All @@ -137,76 +224,54 @@ void _telnetNewClient(AsyncClient *client) {
if (!telnetSTA) {
DEBUG_MSG_P(PSTR("[TELNET] Rejecting - Only local connections\n"));
client->onDisconnect([](void *s, AsyncClient *c) {
c->free();
delete c;
});
client->close(true);
return;
}

}

for (unsigned char i = 0; i < TELNET_MAX_CLIENTS; i++) {

if (!_telnetClients[i] || !_telnetClients[i]->connected()) {

_telnetClients[i] = client;
_telnetClients[i] = std::unique_ptr<AsyncClient>(client);

client->onAck([i](void *s, AsyncClient *c, size_t len, uint32_t time) {
_telnetClients[i]->onAck([i](void *s, AsyncClient *c, size_t len, uint32_t time) {
}, 0);

client->onData([i](void *s, AsyncClient *c, void *data, size_t len) {
_telnetClients[i]->onData([i](void *s, AsyncClient *c, void *data, size_t len) {
_telnetData(i, data, len);
}, 0);

client->onDisconnect([i](void *s, AsyncClient *c) {
_telnetClients[i]->onDisconnect([i](void *s, AsyncClient *c) {
_telnetDisconnect(i);
}, 0);

client->onError([i](void *s, AsyncClient *c, int8_t error) {
_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);

client->onTimeout([i](void *s, AsyncClient *c, uint32_t time) {
_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);

DEBUG_MSG_P(PSTR("[TELNET] Client #%u connected\n"), i);

// If there is no terminal support automatically dump info and crash data
#if TERMINAL_SUPPORT == 0
info();
wifiDebug();
crashDump();
crashClear();
#endif

#ifdef ESPURNA_CORE
_telnetClientsAuth[i] = true;
#else
_telnetClientsAuth[i] = !_telnetAuth;
if (_telnetAuth) _telnetWrite(i, "Password: ");
#endif

_telnetFirst = true;
wifiReconnectCheck();

_telnetNotifyConnected(i);
return;

}

}

DEBUG_MSG_P(PSTR("[TELNET] Rejecting - Too many connections\n"));
client->onDisconnect([](void *s, AsyncClient *c) {
c->free();
delete c;
});
client->close(true);

}

#endif // TELNET_SERVER == TELNET_SERVER_WIFISERVER

// -----------------------------------------------------------------------------
// Public API
// -----------------------------------------------------------------------------
Expand All @@ -228,12 +293,16 @@ void _telnetConfigure() {
}

void telnetSetup() {

_telnetServer = new AsyncServer(TELNET_PORT);
_telnetServer->onClient([](void *s, AsyncClient* c) {
_telnetNewClient(c);
}, 0);
_telnetServer->begin();
#if TELNET_SERVER == TELNET_SERVER_WIFISERVER
espurnaRegisterLoop(_telnetLoop);
_telnetServer.setNoDelay(true);
_telnetServer.begin();
#else
_telnetServer.onClient([](void *s, AsyncClient* c) {
_telnetNewClient(c);
}, 0);
_telnetServer.begin();
#endif

#if WEB_SUPPORT
wsOnSendRegister(_telnetWebSocketOnSend);
Expand All @@ -243,7 +312,9 @@ void telnetSetup() {
espurnaRegisterReload(_telnetConfigure);
_telnetConfigure();

DEBUG_MSG_P(PSTR("[TELNET] Listening on port %d\n"), TELNET_PORT);
DEBUG_MSG_P(PSTR("[TELNET] %s server, Listening on port %d\n"),
(TELNET_SERVER == TELNET_SERVER_WIFISERVER) ? "Sync" : "Async",
TELNET_PORT);

}

Expand Down