diff --git a/src/hasp_debug.cpp b/src/hasp_debug.cpp index b886f1d5..6a2537b2 100644 --- a/src/hasp_debug.cpp +++ b/src/hasp_debug.cpp @@ -54,6 +54,22 @@ Syslog syslog(syslogClient, debugSyslogHost.c_str(), debugSyslogPort, debugAppNa unsigned long debugLastMillis = 0; uint16_t debugTelePeriod = 300; +String debugHaspHeader() +{ + String header((char *)0); + header.reserve(256); + header = F(" _____ _____ _____ _____\r\n" + " | | | _ | __| _ |\r\n" + " | | |__ | __|\r\n" + " |__|__|__|__|_____|__|\r\n" + " Home Automation Switch Plate\r\n"); + char buffer[128]; + snprintf(buffer, sizeof(buffer), PSTR(" Open Hardware edition v%u.%u.%u\r\n"), HASP_VERSION_MAJOR, + HASP_VERSION_MINOR, HASP_VERSION_REVISION); + header += buffer; + return header; +} + void debugStart() { #if defined(ARDUINO_ARCH_ESP32) @@ -64,10 +80,7 @@ void debugStart() Serial.flush(); Serial.println(); - Serial.printf_P(PSTR(" _____ _____ _____ _____\r\n | | | _ | __| _ |\r\n" - " | | |__ | __|\r\n |__|__|__|__|_____|__|\r\n" - " Home Automation Switch Plate\r\n Open Hardware edition v%u.%u.%u\r\n\r\n"), - HASP_VERSION_MAJOR, HASP_VERSION_MINOR, HASP_VERSION_REVISION); + Serial.println(debugHaspHeader()); Serial.flush(); // prepare syslog configuration here (can be anywhere before first call of @@ -129,7 +142,7 @@ void syslogSend(uint8_t log, const char * debugText) case 1: syslog.log(LOG_WARNING, debugText); break; - case2: + case 2: syslog.log(LOG_ERR, debugText); break; default: diff --git a/src/hasp_debug.h b/src/hasp_debug.h index c8ceaf61..5bc288db 100644 --- a/src/hasp_debug.h +++ b/src/hasp_debug.h @@ -3,6 +3,8 @@ #include "ArduinoJson.h" +String debugHaspHeader(void); + void debugPreSetup(JsonObject settings); void debugSetup(JsonObject settings); void debugLoop(void); diff --git a/src/hasp_telnet.cpp b/src/hasp_telnet.cpp index 25a17091..f6132b90 100644 --- a/src/hasp_telnet.cpp +++ b/src/hasp_telnet.cpp @@ -3,6 +3,7 @@ #include "hasp_conf.h" #include "hasp_log.h" +#include "hasp_debug.h" #include "hasp_config.h" #include "hasp_dispatch.h" #include "hasp_telnet.h" @@ -16,11 +17,31 @@ #endif //#define telnetInputMax 128; // Size of user input buffer for user telnet session -static char telnetInputBuffer[128]; - -uint8_t telnetEnabled = true; // Enable telnet debug output -WiFiServer * telnetServer; //(23); +WiFiServer * telnetServer; //(23); WiFiClient * telnetClient; +uint8_t telnetEnabled = true; // Enable telnet debug output +uint8_t telnetLoginAttempt = 0; // Initial attempt +uint8_t telnetLoginState = 0; // Unauthenticated +uint8_t telnetInputIndex = 0; // Empty buffer +char telnetInputBuffer[128]; + +bool telnetExitCommand() +{ + char buffer[128]; + snprintf_P(buffer, sizeof(buffer), PSTR("exit")); + if(strcmp(telnetInputBuffer, buffer) == 0 || telnetLoginAttempt >= 3) { + snprintf_P(buffer, sizeof(buffer), PSTR("TELNET: Closing session from %s"), + telnetClient->remoteIP().toString().c_str()); + debugPrintln(buffer); + telnetClient->stop(); + telnetLoginState = 0; // Unauthenticated + telnetInputIndex = 0; // Empty buffer + telnetLoginAttempt = 0; // Initial attempt + return true; + } else { + return false; + } +} void telnetSetup(const JsonObject & settings) { @@ -28,29 +49,35 @@ void telnetSetup(const JsonObject & settings) if(telnetEnabled) { // Setup telnet server for remote debug output telnetServer = new WiFiServer(23); - telnetClient = new WiFiClient; if(telnetServer) { + telnetClient = new WiFiClient; telnetServer->setNoDelay(true); telnetServer->begin(); - debugPrintln(String(F("TELNET: debug server enabled at telnet:")) + WiFi.localIP().toString()); + debugPrintln(String(F("TELNET: Debug console enabled at telnet://")) + WiFi.localIP().toString()); } else { - errorPrintln(F("TELNET: %sFailed to create telnet server")); + errorPrintln(F("TELNET: %sFailed to start telnet server")); } } } -void telnetLoop(bool isConnected) +void telnetLoop() { // Basic telnet client handling code from: https://gist.github.com/tablatronix/4793677ca748f5f584c95ec4a2b10303 - if(!isConnected) return; - static unsigned long telnetInputIndex = 0; if(telnetServer && telnetServer->hasClient()) { // client is connected if(!*telnetClient || !telnetClient->connected()) { if(telnetClient) { telnetClient->stop(); // client disconnected } - *telnetClient = telnetServer->available(); // ready for new client - telnetInputIndex = 0; // reset input buffer index + *telnetClient = telnetServer->available(); // ready for new client + char buffer[128]; + snprintf_P(buffer, sizeof(buffer), PSTR("TELNET: Client connected from %s"), + telnetClient->remoteIP().toString().c_str()); + debugPrintln(buffer); + snprintf_P(buffer, sizeof(buffer), PSTR("\r\nUsername: ")); + telnetClient->print(buffer); + telnetLoginState = 0; // Unauthenticated + telnetInputIndex = 0; // reset input buffer index + telnetLoginAttempt = 0; // Initial attempt } else { telnetServer->available().stop(); // have client, block new connections } @@ -63,22 +90,76 @@ void telnetLoop(bool isConnected) char telnetInputByte = telnetClient->read(); // Read client byte // debugPrintln(String("telnet in: 0x") + String(telnetInputByte, HEX)); switch(telnetInputByte) { - case 0x3: - case 0x5: + case 0x01: + case 0x03: + case 0x05: case 0xff: + case 0xfe: + case 0xfd: + case 0xfc: + case 0xfb: case 0xf1: + case 0x1f: + case 10: telnetInputIndex = 0; break; - case 10: + case 0x08: // Backspace + if(telnetInputIndex > 0) { + telnetInputIndex--; + } + break; case 13: telnetInputBuffer[telnetInputIndex] = 0; // null terminate our char array - if(telnetInputIndex > 0) dispatchCommand(telnetInputBuffer); + switch(telnetLoginState) { + case 0: { + char buffer[128]; + snprintf_P(buffer, sizeof(buffer), PSTR("Password: %c%c%c"), 0xFF, 0xFB, 0x01); + telnetClient->print(buffer); + snprintf_P(buffer, sizeof(buffer), PSTR("admin")); + telnetLoginState = + strcmp(telnetInputBuffer, buffer) == 0 ? 10 : 99; // Username OK=1 or NOK=99 + break; + } + case 10: + case 99: { + char buffer[128]; + snprintf_P(buffer, sizeof(buffer), PSTR("%c%c%c"), 0xFF, 0xFC, 0x01); + telnetClient->println(buffer); + snprintf_P(buffer, sizeof(buffer), PSTR("haspadmin")); + if(telnetLoginState == 10 && strcmp(telnetInputBuffer, buffer) == 0) { // Authenticated + telnetLoginState = 255; // Authenticated + telnetLoginAttempt = 0; // Initial attempt + telnetClient->println(debugHaspHeader()); + snprintf_P(buffer, sizeof(buffer), PSTR("TELNET: Client login from %s"), + telnetClient->remoteIP().toString().c_str()); + debugPrintln(buffer); + } else { + telnetLoginState = 0; // Unauthorized + telnetLoginAttempt++; // Subsequent attempt + snprintf_P(buffer, sizeof(buffer), PSTR("Authorization failed!\r\n")); + telnetClient->println(buffer); + snprintf_P(buffer, sizeof(buffer), PSTR("TELNET: %%Incorrect login attempt from %s"), + telnetClient->remoteIP().toString().c_str()); + errorPrintln(buffer); + if(telnetLoginAttempt >= 3) { + telnetExitCommand(); + } else { + snprintf_P(buffer, sizeof(buffer), PSTR("Username: ")); + telnetClient->print(buffer); + } + } + break; + } + default: + if(telnetInputIndex > 0 && !telnetExitCommand()) { + dispatchCommand(telnetInputBuffer); + } + } telnetInputIndex = 0; break; - default: - if(telnetInputIndex < - sizeof(telnetInputBuffer)) { // If we have room left in our buffer add the current byte + // If we have room left in our buffer add the current byte + if(telnetInputIndex < sizeof(telnetInputBuffer) - 1 && telnetInputByte >= 0x20) { telnetInputBuffer[telnetInputIndex] = telnetInputByte; telnetInputIndex++; } @@ -87,19 +168,22 @@ void telnetLoop(bool isConnected) } } +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void telnetPrintln(const char * msg) { - if(telnetEnabled && telnetClient && telnetClient->connected()) { + if(telnetEnabled && telnetClient && telnetClient->connected() && telnetLoginState == 255) { telnetClient->println(msg); } } void telnetPrint(const char * msg) { - if(telnetEnabled && telnetClient && telnetClient->connected()) { + if(telnetEnabled && telnetClient && telnetClient->connected() && telnetLoginState == 255) { telnetClient->print(msg); } } +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + bool telnetGetConfig(const JsonObject & settings) { settings[FPSTR(F_CONFIG_ENABLE)] = telnetEnabled; @@ -108,8 +192,6 @@ bool telnetGetConfig(const JsonObject & settings) return true; } -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - bool telnetSetConfig(const JsonObject & settings) { configOutput(settings); diff --git a/src/hasp_telnet.h b/src/hasp_telnet.h index c73d7c44..98d6b19e 100644 --- a/src/hasp_telnet.h +++ b/src/hasp_telnet.h @@ -8,7 +8,7 @@ #include "ArduinoJson.h" void telnetSetup(const JsonObject & settings); -void telnetLoop(bool isConnected); +void telnetLoop(void); void telnetStop(void); void telnetPrint(const char * msg);