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.

330 lines
9.7 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
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. #if TELNET_SERVER == TELNET_SERVER_WIFISERVER
  9. #include <ESP8266WiFi.h>
  10. WiFiServer _telnetServer = WiFiServer(TELNET_PORT);
  11. std::unique_ptr<WiFiClient> _telnetClients[TELNET_MAX_CLIENTS];
  12. #else
  13. #include <ESPAsyncTCP.h>
  14. AsyncServer _telnetServer = AsyncServer(TELNET_PORT);
  15. std::unique_ptr<AsyncClient> _telnetClients[TELNET_MAX_CLIENTS];
  16. #endif
  17. bool _telnetFirst = true;
  18. bool _telnetAuth = TELNET_AUTHENTICATION;
  19. bool _telnetClientsAuth[TELNET_MAX_CLIENTS];
  20. // -----------------------------------------------------------------------------
  21. // Private methods
  22. // -----------------------------------------------------------------------------
  23. #if WEB_SUPPORT
  24. bool _telnetWebSocketOnKeyCheck(const char * key, JsonVariant& value) {
  25. return (strncmp(key, "telnet", 6) == 0);
  26. }
  27. void _telnetWebSocketOnConnected(JsonObject& root) {
  28. root["telnetSTA"] = getSetting("telnetSTA", TELNET_STA).toInt() == 1;
  29. root["telnetAuth"] = getSetting("telnetAuth", TELNET_AUTHENTICATION).toInt() == 1;
  30. }
  31. #endif
  32. void _telnetDisconnect(unsigned char clientId) {
  33. // ref: we are called from onDisconnect, async is already stopped
  34. #if TELNET_SERVER == TELNET_SERVER_WIFISERVER
  35. _telnetClients[clientId]->stop();
  36. #endif
  37. _telnetClients[clientId] = nullptr;
  38. wifiReconnectCheck();
  39. DEBUG_MSG_P(PSTR("[TELNET] Client #%d disconnected\n"), clientId);
  40. }
  41. bool _telnetWrite(unsigned char clientId, const char *data, size_t len) {
  42. if (_telnetClients[clientId] && _telnetClients[clientId]->connected()) {
  43. return (_telnetClients[clientId]->write(data, len) > 0);
  44. }
  45. return false;
  46. }
  47. unsigned char _telnetWrite(const char *data, size_t len) {
  48. unsigned char count = 0;
  49. for (unsigned char i = 0; i < TELNET_MAX_CLIENTS; i++) {
  50. // Do not send broadcast messages to unauthenticated clients
  51. if (_telnetAuth && !_telnetClientsAuth[i]) {
  52. continue;
  53. }
  54. if (_telnetWrite(i, data, len)) ++count;
  55. }
  56. return count;
  57. }
  58. unsigned char _telnetWrite(const char *data) {
  59. return _telnetWrite(data, strlen(data));
  60. }
  61. bool _telnetWrite(unsigned char clientId, const char * message) {
  62. return _telnetWrite(clientId, message, strlen(message));
  63. }
  64. void _telnetData(unsigned char clientId, void *data, size_t len) {
  65. // Skip first message since it's always garbage
  66. if (_telnetFirst) {
  67. _telnetFirst = false;
  68. return;
  69. }
  70. // Capture close connection
  71. char * p = (char *) data;
  72. // C-d is sent as two bytes (sometimes repeating)
  73. if (len >= 2) {
  74. if ((p[0] == 0xFF) && (p[1] == 0xEC)) {
  75. _telnetDisconnect(clientId);
  76. return;
  77. }
  78. }
  79. if ((strncmp(p, "close", 5) == 0) || (strncmp(p, "quit", 4) == 0)) {
  80. _telnetDisconnect(clientId);
  81. return;
  82. }
  83. // Password prompt (disable on CORE variant)
  84. #ifdef ESPURNA_CORE
  85. const bool authenticated = true;
  86. #else
  87. const bool authenticated = _telnetClientsAuth[clientId];
  88. #endif
  89. if (_telnetAuth && !authenticated) {
  90. String password = getAdminPass();
  91. if (strncmp(p, password.c_str(), password.length()) == 0) {
  92. DEBUG_MSG_P(PSTR("[TELNET] Client #%d authenticated\n"), clientId);
  93. _telnetWrite(clientId, "Password correct, welcome!\n");
  94. _telnetClientsAuth[clientId] = true;
  95. } else {
  96. _telnetWrite(clientId, "Password (try again): ");
  97. }
  98. return;
  99. }
  100. // Inject command
  101. #if TERMINAL_SUPPORT
  102. terminalInject(data, len);
  103. #endif
  104. }
  105. void _telnetNotifyConnected(unsigned char i) {
  106. DEBUG_MSG_P(PSTR("[TELNET] Client #%u connected\n"), i);
  107. // If there is no terminal support automatically dump info and crash data
  108. #if TERMINAL_SUPPORT == 0
  109. info();
  110. wifiDebug();
  111. crashDump();
  112. crashClear();
  113. #endif
  114. #ifdef ESPURNA_CORE
  115. _telnetClientsAuth[i] = true;
  116. #else
  117. _telnetClientsAuth[i] = !_telnetAuth;
  118. if (_telnetAuth) {
  119. if (getAdminPass().length()) {
  120. _telnetWrite(i, "Password: ");
  121. } else {
  122. _telnetClientsAuth[i] = true;
  123. }
  124. }
  125. #endif
  126. _telnetFirst = true;
  127. wifiReconnectCheck();
  128. }
  129. #if TELNET_SERVER == TELNET_SERVER_WIFISERVER
  130. void _telnetLoop() {
  131. if (_telnetServer.hasClient()) {
  132. int i;
  133. for (i = 0; i < TELNET_MAX_CLIENTS; i++) {
  134. if (!_telnetClients[i] || !_telnetClients[i]->connected()) {
  135. _telnetClients[i] = std::unique_ptr<WiFiClient>(new WiFiClient(_telnetServer.available()));
  136. if (_telnetClients[i]->localIP() != WiFi.softAPIP()) {
  137. // Telnet is always available for the ESPurna Core image
  138. #ifdef ESPURNA_CORE
  139. bool telnetSTA = true;
  140. #else
  141. bool telnetSTA = getSetting("telnetSTA", TELNET_STA).toInt() == 1;
  142. #endif
  143. if (!telnetSTA) {
  144. DEBUG_MSG_P(PSTR("[TELNET] Rejecting - Only local connections\n"));
  145. _telnetDisconnect(i);
  146. return;
  147. }
  148. }
  149. _telnetNotifyConnected(i);
  150. break;
  151. }
  152. }
  153. //no free/disconnected spot so reject
  154. if (i == TELNET_MAX_CLIENTS) {
  155. DEBUG_MSG_P(PSTR("[TELNET] Rejecting - Too many connections\n"));
  156. _telnetServer.available().stop();
  157. return;
  158. }
  159. }
  160. for (int i = 0; i < TELNET_MAX_CLIENTS; i++) {
  161. if (_telnetClients[i]) {
  162. // Handle client timeouts
  163. if (!_telnetClients[i]->connected()) {
  164. _telnetDisconnect(i);
  165. } else {
  166. // Read data from clients
  167. while (_telnetClients[i] && _telnetClients[i]->available()) {
  168. char data[TERMINAL_BUFFER_SIZE];
  169. size_t len = _telnetClients[i]->available();
  170. unsigned int r = _telnetClients[i]->readBytes(data, min(sizeof(data), len));
  171. _telnetData(i, data, r);
  172. }
  173. }
  174. }
  175. }
  176. }
  177. #else // TELNET_SERVER_ASYNC
  178. void _telnetNewClient(AsyncClient* client) {
  179. if (client->localIP() != WiFi.softAPIP()) {
  180. // Telnet is always available for the ESPurna Core image
  181. #ifdef ESPURNA_CORE
  182. bool telnetSTA = true;
  183. #else
  184. bool telnetSTA = getSetting("telnetSTA", TELNET_STA).toInt() == 1;
  185. #endif
  186. if (!telnetSTA) {
  187. DEBUG_MSG_P(PSTR("[TELNET] Rejecting - Only local connections\n"));
  188. client->onDisconnect([](void *s, AsyncClient *c) {
  189. delete c;
  190. });
  191. client->close(true);
  192. return;
  193. }
  194. }
  195. for (unsigned char i = 0; i < TELNET_MAX_CLIENTS; i++) {
  196. if (!_telnetClients[i] || !_telnetClients[i]->connected()) {
  197. _telnetClients[i] = std::unique_ptr<AsyncClient>(client);
  198. _telnetClients[i]->onAck([i](void *s, AsyncClient *c, size_t len, uint32_t time) {
  199. }, 0);
  200. _telnetClients[i]->onData([i](void *s, AsyncClient *c, void *data, size_t len) {
  201. _telnetData(i, data, len);
  202. }, 0);
  203. _telnetClients[i]->onDisconnect([i](void *s, AsyncClient *c) {
  204. _telnetDisconnect(i);
  205. }, 0);
  206. _telnetClients[i]->onError([i](void *s, AsyncClient *c, int8_t error) {
  207. DEBUG_MSG_P(PSTR("[TELNET] Error %s (%d) on client #%u\n"), c->errorToString(error), error, i);
  208. }, 0);
  209. _telnetClients[i]->onTimeout([i](void *s, AsyncClient *c, uint32_t time) {
  210. DEBUG_MSG_P(PSTR("[TELNET] Timeout on client #%u at %lu\n"), i, time);
  211. c->close();
  212. }, 0);
  213. _telnetNotifyConnected(i);
  214. return;
  215. }
  216. }
  217. DEBUG_MSG_P(PSTR("[TELNET] Rejecting - Too many connections\n"));
  218. client->onDisconnect([](void *s, AsyncClient *c) {
  219. delete c;
  220. });
  221. client->close(true);
  222. }
  223. #endif // TELNET_SERVER == TELNET_SERVER_WIFISERVER
  224. // -----------------------------------------------------------------------------
  225. // Public API
  226. // -----------------------------------------------------------------------------
  227. bool telnetConnected() {
  228. for (unsigned char i = 0; i < TELNET_MAX_CLIENTS; i++) {
  229. if (_telnetClients[i] && _telnetClients[i]->connected()) return true;
  230. }
  231. return false;
  232. }
  233. unsigned char telnetWrite(unsigned char ch) {
  234. char data[1] = {ch};
  235. return _telnetWrite(data, 1);
  236. }
  237. void _telnetConfigure() {
  238. _telnetAuth = getSetting("telnetAuth", TELNET_AUTHENTICATION).toInt() == 1;
  239. }
  240. void telnetSetup() {
  241. #if TELNET_SERVER == TELNET_SERVER_WIFISERVER
  242. espurnaRegisterLoop(_telnetLoop);
  243. _telnetServer.setNoDelay(true);
  244. _telnetServer.begin();
  245. #else
  246. _telnetServer.onClient([](void *s, AsyncClient* c) {
  247. _telnetNewClient(c);
  248. }, 0);
  249. _telnetServer.begin();
  250. #endif
  251. #if WEB_SUPPORT
  252. wsRegister()
  253. .onVisible([](JsonObject& root) { root["telnetVisible"] = 1; })
  254. .onConnected(_telnetWebSocketOnConnected)
  255. .onKeyCheck(_telnetWebSocketOnKeyCheck);
  256. #endif
  257. espurnaRegisterReload(_telnetConfigure);
  258. _telnetConfigure();
  259. DEBUG_MSG_P(PSTR("[TELNET] %s server, Listening on port %d\n"),
  260. (TELNET_SERVER == TELNET_SERVER_WIFISERVER) ? "Sync" : "Async",
  261. TELNET_PORT);
  262. }
  263. #endif // TELNET_SUPPORT