Fork of the espurna firmware for `mhsw` switches
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

399 lines
12 KiB

7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
  1. /*
  2. TELNET MODULE
  3. Copyright (C) 2017-2019 by Xose Pérez <xose dot perez at gmail dot com>
  4. Parts of the code have been borrowed from Thomas Sarlandie's NetServer
  5. (https://github.com/sarfata/kbox-firmware/tree/master/src/esp)
  6. */
  7. #if TELNET_SUPPORT
  8. #define TELNET_IAC 0xFF
  9. #define TELNET_XEOF 0xEC
  10. #if TELNET_SERVER == TELNET_SERVER_WIFISERVER
  11. #include <ESP8266WiFi.h>
  12. WiFiServer _telnetServer = WiFiServer(TELNET_PORT);
  13. std::unique_ptr<WiFiClient> _telnetClients[TELNET_MAX_CLIENTS];
  14. #else
  15. #include <ESPAsyncTCP.h>
  16. AsyncServer _telnetServer = AsyncServer(TELNET_PORT);
  17. std::unique_ptr<AsyncClient> _telnetClients[TELNET_MAX_CLIENTS];
  18. #endif
  19. bool _telnetAuth = TELNET_AUTHENTICATION;
  20. bool _telnetClientsAuth[TELNET_MAX_CLIENTS];
  21. // -----------------------------------------------------------------------------
  22. // Private methods
  23. // -----------------------------------------------------------------------------
  24. #if WEB_SUPPORT
  25. bool _telnetWebSocketOnKeyCheck(const char * key, JsonVariant& value) {
  26. return (strncmp(key, "telnet", 6) == 0);
  27. }
  28. void _telnetWebSocketOnConnected(JsonObject& root) {
  29. root["telnetSTA"] = getSetting("telnetSTA", TELNET_STA).toInt() == 1;
  30. root["telnetAuth"] = getSetting("telnetAuth", TELNET_AUTHENTICATION).toInt() == 1;
  31. }
  32. #endif
  33. #if TELNET_REVERSE_SUPPORT
  34. void _telnetReverse(const char * host, uint16_t port) {
  35. DEBUG_MSG_P(PSTR("[TELNET] Connecting to reverse telnet on %s:%d\n"), host, port);
  36. unsigned char i;
  37. for (i = 0; i < TELNET_MAX_CLIENTS; i++) {
  38. if (!_telnetClients[i] || !_telnetClients[i]->connected()) {
  39. #if TELNET_SERVER == TELNET_SERVER_WIFISERVER
  40. _telnetClients[i] = std::make_unique<WiFiClient>();
  41. #else // TELNET_SERVER_ASYNC
  42. _telnetClients[i] = std::make_unique<AsyncClient>();
  43. #endif
  44. if (_telnetClients[i]->connect(host, port)) {
  45. return _telnetNotifyConnected(i);
  46. } else {
  47. DEBUG_MSG_P(PSTR("[TELNET] Error connecting reverse telnet\n"));
  48. return _telnetDisconnect(i);
  49. }
  50. }
  51. }
  52. //no free/disconnected spot so reject
  53. if (i == TELNET_MAX_CLIENTS) {
  54. DEBUG_MSG_P(PSTR("[TELNET] Failed too connect - too many clients connected\n"));
  55. }
  56. }
  57. #if MQTT_SUPPORT
  58. void _telnetReverseMQTTCallback(unsigned int type, const char * topic, const char * payload) {
  59. if (type == MQTT_CONNECT_EVENT) {
  60. mqttSubscribe(MQTT_TOPIC_TELNET_REVERSE);
  61. } else if (type == MQTT_MESSAGE_EVENT) {
  62. String t = mqttMagnitude((char *) topic);
  63. if (t.equals(MQTT_TOPIC_TELNET_REVERSE)) {
  64. String pl = String(payload);
  65. int col = pl.indexOf(':');
  66. if (col != -1) {
  67. String host = pl.substring(0, col);
  68. uint16_t port = pl.substring(col + 1).toInt();
  69. _telnetReverse(host.c_str(), port);
  70. } else {
  71. DEBUG_MSG_P(PSTR("[TELNET] Incorrect reverse telnet value given, use the form \"host:ip\""));
  72. }
  73. }
  74. }
  75. }
  76. #endif
  77. #endif
  78. void _telnetDisconnect(unsigned char clientId) {
  79. // ref: we are called from onDisconnect, async is already stopped
  80. #if TELNET_SERVER == TELNET_SERVER_WIFISERVER
  81. _telnetClients[clientId]->stop();
  82. #endif
  83. _telnetClients[clientId] = nullptr;
  84. wifiReconnectCheck();
  85. DEBUG_MSG_P(PSTR("[TELNET] Client #%d disconnected\n"), clientId);
  86. }
  87. bool _telnetWrite(unsigned char clientId, const char *data, size_t len) {
  88. if (_telnetClients[clientId] && _telnetClients[clientId]->connected()) {
  89. return (_telnetClients[clientId]->write(data, len) > 0);
  90. }
  91. return false;
  92. }
  93. unsigned char _telnetWrite(const char *data, size_t len) {
  94. unsigned char count = 0;
  95. for (unsigned char i = 0; i < TELNET_MAX_CLIENTS; i++) {
  96. // Do not send broadcast messages to unauthenticated clients
  97. if (_telnetAuth && !_telnetClientsAuth[i]) {
  98. continue;
  99. }
  100. if (_telnetWrite(i, data, len)) ++count;
  101. }
  102. return count;
  103. }
  104. unsigned char _telnetWrite(const char *data) {
  105. return _telnetWrite(data, strlen(data));
  106. }
  107. bool _telnetWrite(unsigned char clientId, const char * message) {
  108. return _telnetWrite(clientId, message, strlen(message));
  109. }
  110. void _telnetData(unsigned char clientId, void *data, size_t len) {
  111. // Capture close connection
  112. char * p = (char *) data;
  113. if ((len >= 2) && (p[0] == TELNET_IAC)) {
  114. // C-d is sent as two bytes (sometimes repeating)
  115. if (p[1] == TELNET_XEOF) {
  116. _telnetDisconnect(clientId);
  117. }
  118. return; // Ignore telnet negotiation
  119. }
  120. if ((strncmp(p, "close", 5) == 0) || (strncmp(p, "quit", 4) == 0)) {
  121. _telnetDisconnect(clientId);
  122. return;
  123. }
  124. // Password prompt (disable on CORE variant)
  125. #ifdef ESPURNA_CORE
  126. const bool authenticated = true;
  127. #else
  128. const bool authenticated = _telnetClientsAuth[clientId];
  129. #endif
  130. if (_telnetAuth && !authenticated) {
  131. String password = getAdminPass();
  132. if (strncmp(p, password.c_str(), password.length()) == 0) {
  133. DEBUG_MSG_P(PSTR("[TELNET] Client #%d authenticated\n"), clientId);
  134. _telnetWrite(clientId, "Password correct, welcome!\n");
  135. _telnetClientsAuth[clientId] = true;
  136. } else {
  137. _telnetWrite(clientId, "Password (try again): ");
  138. }
  139. return;
  140. }
  141. // Inject command
  142. #if TERMINAL_SUPPORT
  143. terminalInject(data, len);
  144. #endif
  145. }
  146. void _telnetNotifyConnected(unsigned char i) {
  147. DEBUG_MSG_P(PSTR("[TELNET] Client #%u connected\n"), i);
  148. #if TELNET_SERVER == TELNET_SERVER_ASYNC
  149. _telnetClients[i]->onAck([i](void *s, AsyncClient *c, size_t len, uint32_t time) {
  150. }, 0);
  151. _telnetClients[i]->onData([i](void *s, AsyncClient *c, void *data, size_t len) {
  152. _telnetData(i, data, len);
  153. }, 0);
  154. _telnetClients[i]->onDisconnect([i](void *s, AsyncClient *c) {
  155. _telnetDisconnect(i);
  156. }, 0);
  157. _telnetClients[i]->onError([i](void *s, AsyncClient *c, int8_t error) {
  158. DEBUG_MSG_P(PSTR("[TELNET] Error %s (%d) on client #%u\n"), c->errorToString(error), error, i);
  159. }, 0);
  160. _telnetClients[i]->onTimeout([i](void *s, AsyncClient *c, uint32_t time) {
  161. DEBUG_MSG_P(PSTR("[TELNET] Timeout on client #%u at %lu\n"), i, time);
  162. c->close();
  163. }, 0);
  164. #endif
  165. // If there is no terminal support automatically dump info and crash data
  166. #if TERMINAL_SUPPORT == 0
  167. info();
  168. wifiDebug();
  169. crashDump();
  170. crashClear();
  171. #endif
  172. #ifdef ESPURNA_CORE
  173. _telnetClientsAuth[i] = true;
  174. #else
  175. _telnetClientsAuth[i] = !_telnetAuth;
  176. if (_telnetAuth) {
  177. if (getAdminPass().length()) {
  178. _telnetWrite(i, "Password: ");
  179. } else {
  180. _telnetClientsAuth[i] = true;
  181. }
  182. }
  183. #endif
  184. wifiReconnectCheck();
  185. }
  186. #if TELNET_SERVER == TELNET_SERVER_WIFISERVER
  187. void _telnetLoop() {
  188. if (_telnetServer.hasClient()) {
  189. int i;
  190. for (i = 0; i < TELNET_MAX_CLIENTS; i++) {
  191. if (!_telnetClients[i] || !_telnetClients[i]->connected()) {
  192. _telnetClients[i] = std::unique_ptr<WiFiClient>(new WiFiClient(_telnetServer.available()));
  193. if (_telnetClients[i]->localIP() != WiFi.softAPIP()) {
  194. // Telnet is always available for the ESPurna Core image
  195. #ifdef ESPURNA_CORE
  196. bool telnetSTA = true;
  197. #else
  198. bool telnetSTA = getSetting("telnetSTA", TELNET_STA).toInt() == 1;
  199. #endif
  200. if (!telnetSTA) {
  201. DEBUG_MSG_P(PSTR("[TELNET] Rejecting - Only local connections\n"));
  202. _telnetDisconnect(i);
  203. return;
  204. }
  205. }
  206. _telnetNotifyConnected(i);
  207. break;
  208. }
  209. }
  210. //no free/disconnected spot so reject
  211. if (i == TELNET_MAX_CLIENTS) {
  212. DEBUG_MSG_P(PSTR("[TELNET] Rejecting - Too many connections\n"));
  213. _telnetServer.available().stop();
  214. return;
  215. }
  216. }
  217. for (int i = 0; i < TELNET_MAX_CLIENTS; i++) {
  218. if (_telnetClients[i]) {
  219. // Handle client timeouts
  220. if (!_telnetClients[i]->connected()) {
  221. _telnetDisconnect(i);
  222. } else {
  223. // Read data from clients
  224. while (_telnetClients[i] && _telnetClients[i]->available()) {
  225. char data[TERMINAL_BUFFER_SIZE];
  226. size_t len = _telnetClients[i]->available();
  227. unsigned int r = _telnetClients[i]->readBytes(data, min(sizeof(data), len));
  228. _telnetData(i, data, r);
  229. }
  230. }
  231. }
  232. }
  233. }
  234. #else // TELNET_SERVER_ASYNC
  235. void _telnetNewClient(AsyncClient* client) {
  236. if (client->localIP() != WiFi.softAPIP()) {
  237. // Telnet is always available for the ESPurna Core image
  238. #ifdef ESPURNA_CORE
  239. bool telnetSTA = true;
  240. #else
  241. bool telnetSTA = getSetting("telnetSTA", TELNET_STA).toInt() == 1;
  242. #endif
  243. if (!telnetSTA) {
  244. DEBUG_MSG_P(PSTR("[TELNET] Rejecting - Only local connections\n"));
  245. client->onDisconnect([](void *s, AsyncClient *c) {
  246. delete c;
  247. });
  248. client->close(true);
  249. return;
  250. }
  251. }
  252. for (unsigned char i = 0; i < TELNET_MAX_CLIENTS; i++) {
  253. if (!_telnetClients[i] || !_telnetClients[i]->connected()) {
  254. _telnetClients[i] = std::unique_ptr<AsyncClient>(client);
  255. _telnetNotifyConnected(i);
  256. return;
  257. }
  258. }
  259. DEBUG_MSG_P(PSTR("[TELNET] Rejecting - Too many connections\n"));
  260. client->onDisconnect([](void *s, AsyncClient *c) {
  261. delete c;
  262. });
  263. client->close(true);
  264. }
  265. #endif // TELNET_SERVER == TELNET_SERVER_WIFISERVER
  266. // -----------------------------------------------------------------------------
  267. // Public API
  268. // -----------------------------------------------------------------------------
  269. bool telnetConnected() {
  270. for (unsigned char i = 0; i < TELNET_MAX_CLIENTS; i++) {
  271. if (_telnetClients[i] && _telnetClients[i]->connected()) return true;
  272. }
  273. return false;
  274. }
  275. unsigned char telnetWrite(unsigned char ch) {
  276. char data[1] = {ch};
  277. return _telnetWrite(data, 1);
  278. }
  279. void _telnetConfigure() {
  280. _telnetAuth = getSetting("telnetAuth", TELNET_AUTHENTICATION).toInt() == 1;
  281. }
  282. void telnetSetup() {
  283. #if TELNET_SERVER == TELNET_SERVER_WIFISERVER
  284. espurnaRegisterLoop(_telnetLoop);
  285. _telnetServer.setNoDelay(true);
  286. _telnetServer.begin();
  287. #else
  288. _telnetServer.onClient([](void *s, AsyncClient* c) {
  289. _telnetNewClient(c);
  290. }, 0);
  291. _telnetServer.begin();
  292. #endif
  293. #if WEB_SUPPORT
  294. wsRegister()
  295. .onVisible([](JsonObject& root) { root["telnetVisible"] = 1; })
  296. .onConnected(_telnetWebSocketOnConnected)
  297. .onKeyCheck(_telnetWebSocketOnKeyCheck);
  298. #endif
  299. #if TELNET_REVERSE_SUPPORT
  300. #if MQTT_SUPPORT
  301. mqttRegister(_telnetReverseMQTTCallback);
  302. #endif
  303. #if TERMINAL_SUPPORT
  304. terminalRegisterCommand(F("TELNET.REVERSE"), [](Embedis* e) {
  305. if (e->argc < 3) {
  306. terminalError(F("Wrong arguments. Usage: TELNET.REVERSE <host> <port>"));
  307. return;
  308. }
  309. String host = String(e->argv[1]);
  310. uint16_t port = String(e->argv[2]).toInt();
  311. terminalOK();
  312. _telnetReverse(host.c_str(), port);
  313. });
  314. #endif
  315. #endif
  316. espurnaRegisterReload(_telnetConfigure);
  317. _telnetConfigure();
  318. DEBUG_MSG_P(PSTR("[TELNET] %s server, Listening on port %d\n"),
  319. (TELNET_SERVER == TELNET_SERVER_WIFISERVER) ? "Sync" : "Async",
  320. TELNET_PORT);
  321. }
  322. #endif // TELNET_SUPPORT