telnet.cpp
1 #include "telnet.h" 2 3 #if CERATINA_TELNET_ENABLED 4 5 #include "../console/remote.h" 6 #include <led.h> 7 8 #include <Arduino.h> 9 #include <ESPTelnet.h> 10 #include <EscapeCodes.h> 11 12 //------------------------------------------ 13 // Telnet transport 14 //------------------------------------------ 15 static ESPTelnet telnet_inst; 16 static EscapeCodes ansi; 17 static bool is_started = false; 18 static String client_ip_str; 19 20 static char ring_buf[config::telnet::RING_SIZE]; 21 static char wbuf[config::telnet::WRITE_BUF]; 22 static char line[config::shell::BUF_IN]; 23 24 static void telnet_flush(const char *data, size_t len, void *ctx) { 25 (void)ctx; 26 telnet_inst.write((const uint8_t *)data, len); 27 } 28 29 static console::remote::Shell shell( 30 ring_buf, config::telnet::RING_SIZE, 31 wbuf, config::telnet::WRITE_BUF, 32 line, config::shell::BUF_IN, 33 telnet_flush, nullptr 34 ); 35 36 //------------------------------------------ 37 // Connection callbacks 38 //------------------------------------------ 39 static void on_connect(String ip) { 40 client_ip_str = ip; 41 Serial.printf("[telnet] client connected from %s\n", ip.c_str()); 42 LED.set(colors::Cyan); 43 shell.reset(); 44 telnet_inst.print(ansi.cls()); 45 shell.send_motd("Telnet"); 46 shell.send_prompt(); 47 } 48 49 static void on_disconnect(String ip) { 50 shell.save_history(); 51 Serial.printf("[telnet] client disconnected (%s)\n", ip.c_str()); 52 client_ip_str = ""; 53 LED.set(colors::Green); 54 } 55 56 static void on_reconnect(String ip) { 57 Serial.printf("[telnet] client reconnected from %s\n", ip.c_str()); 58 } 59 60 static void on_connection_attempt(String ip) { 61 Serial.printf("[telnet] rejected connection from %s (session active: %s)\n", 62 ip.c_str(), client_ip_str.c_str()); 63 } 64 65 static void on_input(String input) { 66 for (size_t i = 0; i < input.length(); i++) { 67 char ch = input[i]; 68 if (ch == '\r') continue; 69 shell.push_input(ch); 70 } 71 } 72 73 void networking::telnet::initialize() { 74 if (is_started) return; 75 76 //------------------------------------------ 77 // Public API 78 //------------------------------------------ 79 telnet_inst.onConnect(on_connect); 80 telnet_inst.onDisconnect(on_disconnect); 81 telnet_inst.onReconnect(on_reconnect); 82 telnet_inst.onConnectionAttempt(on_connection_attempt); 83 telnet_inst.onInputReceived(on_input); 84 telnet_inst.setLineMode(false); 85 telnet_inst.setKeepAliveInterval(config::telnet::KEEPALIVE_MS); 86 87 if (!telnet_inst.begin(config::telnet::PORT, false)) { 88 Serial.println(F("[telnet] failed to start")); 89 return; 90 } 91 92 is_started = true; 93 Serial.printf("[telnet] listening on port %d\n", config::telnet::PORT); 94 } 95 96 void networking::telnet::service() { 97 if (!is_started) return; 98 telnet_inst.loop(); 99 if (!telnet_inst.isConnected()) return; 100 shell.service(); 101 } 102 103 bool networking::telnet::isConnected() { 104 return is_started && telnet_inst.isConnected(); 105 } 106 107 const char *networking::telnet::clientIP() { 108 return client_ip_str.c_str(); 109 } 110 111 void networking::telnet::disconnect() { 112 if (is_started && telnet_inst.isConnected()) 113 telnet_inst.disconnectClient(); 114 } 115 116 #else 117 118 void networking::telnet::initialize() {} 119 void networking::telnet::service() {} 120 bool networking::telnet::isConnected() { return false; } 121 const char *networking::telnet::clientIP() { return ""; } 122 void networking::telnet::disconnect() {} 123 124 #endif 125 126 #ifdef PIO_UNIT_TESTING 127 128 #include "telnet.h" 129 #include <testing/utils.h> 130 131 #include <Arduino.h> 132 133 static void test_telnet_config(void) { 134 GIVEN("telnet is enabled"); 135 THEN("the configuration is valid"); 136 137 #if CERATINA_TELNET_ENABLED 138 TEST_ASSERT_GREATER_THAN_UINT16_MESSAGE(0, config::telnet::PORT, 139 "device: telnet port must be > 0"); 140 TEST_ASSERT_GREATER_THAN_UINT16_MESSAGE(0, config::telnet::RING_SIZE, 141 "device: ring buffer must be > 0"); 142 TEST_ASSERT_GREATER_THAN_UINT16_MESSAGE(0, config::telnet::WRITE_BUF, 143 "device: write buffer must be > 0"); 144 145 TEST_PRINTF("telnet enabled on port %d", config::telnet::PORT); 146 #else 147 TEST_IGNORE_MESSAGE("telnet not enabled"); 148 #endif 149 } 150 151 void networking::telnet::test(void) { 152 MODULE("Telnet"); 153 RUN_TEST(test_telnet_config); 154 } 155 156 #endif