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.

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