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.

522 lines
15 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
  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. AsyncBufferedClient based on ESPAsyncTCPbuffer, distributed with the ESPAsyncTCP
  7. (https://github.com/me-no-dev/ESPAsyncTCP/blob/master/src/ESPAsyncTCPbuffer.cpp)
  8. Copyright (C) 2019-2020 by Maxim Prokhorov <prokhorov dot max at outlook dot com>
  9. Updated to use WiFiServer and support reverse connections by Niek van der Maas < mail at niekvandermaas dot nl>
  10. */
  11. #include "telnet.h"
  12. #if TELNET_SUPPORT
  13. #include <memory>
  14. #include "board.h"
  15. #include "ws.h"
  16. TTelnetServer _telnetServer(TELNET_PORT);
  17. std::unique_ptr<TTelnetClient> _telnetClients[TELNET_MAX_CLIENTS];
  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", 1 == TELNET_STA);
  29. root["telnetAuth"] = getSetting("telnetAuth", 1 == TELNET_AUTHENTICATION);
  30. }
  31. #endif
  32. #if TELNET_REVERSE_SUPPORT
  33. void _telnetReverse(const char * host, uint16_t port) {
  34. DEBUG_MSG_P(PSTR("[TELNET] Connecting to reverse telnet on %s:%d\n"), host, port);
  35. unsigned char i;
  36. for (i = 0; i < TELNET_MAX_CLIENTS; i++) {
  37. if (!_telnetClients[i] || !_telnetClients[i]->connected()) {
  38. #if TELNET_SERVER == TELNET_SERVER_WIFISERVER
  39. _telnetClients[i] = std::make_unique<TTelnetClient>();
  40. #else // TELNET_SERVER == TELNET_SERVER_ASYNC
  41. _telnetSetupClient(i, new AsyncClient());
  42. #endif
  43. if (_telnetClients[i]->connect(host, port)) {
  44. _telnetNotifyConnected(i);
  45. return;
  46. } else {
  47. DEBUG_MSG_P(PSTR("[TELNET] Error connecting reverse telnet\n"));
  48. _telnetDisconnect(i);
  49. return;
  50. }
  51. }
  52. }
  53. //no free/disconnected spot so reject
  54. if (i == TELNET_MAX_CLIENTS) {
  55. DEBUG_MSG_P(PSTR("[TELNET] Failed too connect - too many clients connected\n"));
  56. }
  57. }
  58. #if MQTT_SUPPORT
  59. void _telnetReverseMQTTCallback(unsigned int type, const char * topic, const char * payload) {
  60. if (type == MQTT_CONNECT_EVENT) {
  61. mqttSubscribe(MQTT_TOPIC_TELNET_REVERSE);
  62. } else if (type == MQTT_MESSAGE_EVENT) {
  63. String t = mqttMagnitude((char *) topic);
  64. if (t.equals(MQTT_TOPIC_TELNET_REVERSE)) {
  65. String pl = String(payload);
  66. int col = pl.indexOf(':');
  67. if (col != -1) {
  68. String host = pl.substring(0, col);
  69. uint16_t port = pl.substring(col + 1).toInt();
  70. _telnetReverse(host.c_str(), port);
  71. } else {
  72. DEBUG_MSG_P(PSTR("[TELNET] Incorrect reverse telnet value given, use the form \"host:ip\""));
  73. }
  74. }
  75. }
  76. }
  77. #endif // MQTT_SUPPORT
  78. #endif // TELNET_REVERSE_SUPPORT
  79. #if TELNET_SERVER == TELNET_SERVER_WIFISERVER
  80. void _telnetDisconnect(unsigned char clientId) {
  81. _telnetClients[clientId]->stop();
  82. _telnetClients[clientId] = nullptr;
  83. wifiReconnectCheck();
  84. DEBUG_MSG_P(PSTR("[TELNET] Client #%d disconnected\n"), clientId);
  85. }
  86. #elif TELNET_SERVER == TELNET_SERVER_ASYNC
  87. void _telnetCleanUp() {
  88. schedule_function([] () {
  89. for (unsigned char clientId=0; clientId < TELNET_MAX_CLIENTS; ++clientId) {
  90. if (!_telnetClients[clientId]->connected()) {
  91. _telnetClients[clientId] = nullptr;
  92. wifiReconnectCheck();
  93. DEBUG_MSG_P(PSTR("[TELNET] Client #%d disconnected\n"), clientId);
  94. }
  95. }
  96. });
  97. }
  98. // just close, clean-up method above will destroy the object later
  99. void _telnetDisconnect(unsigned char clientId) {
  100. _telnetClients[clientId]->close(true);
  101. }
  102. #if TELNET_SERVER_ASYNC_BUFFERED
  103. AsyncBufferedClient::AsyncBufferedClient(AsyncClient* client) : _client(client) {
  104. _client->onAck(_s_onAck, this);
  105. _client->onPoll(_s_onPoll, this);
  106. }
  107. void AsyncBufferedClient::_trySend(AsyncBufferedClient* client) {
  108. while (!client->_buffers.empty()) {
  109. auto& chunk = client->_buffers.front();
  110. if (client->_client->space() >= chunk.size()) {
  111. client->_client->write((const char*)chunk.data(), chunk.size());
  112. client->_buffers.pop_front();
  113. continue;
  114. }
  115. return;
  116. }
  117. }
  118. void AsyncBufferedClient::_s_onAck(void* client_ptr, AsyncClient*, size_t, uint32_t) {
  119. _trySend(reinterpret_cast<AsyncBufferedClient*>(client_ptr));
  120. }
  121. void AsyncBufferedClient::_s_onPoll(void* client_ptr, AsyncClient* client) {
  122. _trySend(reinterpret_cast<AsyncBufferedClient*>(client_ptr));
  123. }
  124. void AsyncBufferedClient::_addBuffer() {
  125. // Note: c++17 emplace returns created object reference
  126. _buffers.emplace_back();
  127. _buffers.back().reserve(TCP_MSS);
  128. }
  129. size_t AsyncBufferedClient::write(const char* data, size_t size) {
  130. if (_buffers.size() > AsyncBufferedClient::BUFFERS_MAX) return 0;
  131. size_t written = 0;
  132. if (_buffers.empty()) {
  133. written = _client->add(data, size);
  134. if (written == size) return size;
  135. }
  136. const size_t full_size = size;
  137. char* data_ptr = const_cast<char*>(data + written);
  138. size -= written;
  139. while (size) {
  140. if (_buffers.empty()) _addBuffer();
  141. auto& current = _buffers.back();
  142. const auto have = current.capacity() - current.size();
  143. if (have >= size) {
  144. current.insert(current.end(), data_ptr, data_ptr + size);
  145. size = 0;
  146. } else {
  147. current.insert(current.end(), data_ptr, data_ptr + have);
  148. _addBuffer();
  149. data_ptr += have;
  150. size -= have;
  151. }
  152. }
  153. return full_size;
  154. }
  155. size_t AsyncBufferedClient::write(char c) {
  156. char _c[1] {c};
  157. return write(_c, 1);
  158. }
  159. void AsyncBufferedClient::flush() {
  160. _client->send();
  161. }
  162. size_t AsyncBufferedClient::available() {
  163. return _client->space();
  164. }
  165. bool AsyncBufferedClient::connect(const char *host, uint16_t port) {
  166. return _client->connect(host, port);
  167. }
  168. void AsyncBufferedClient::close(bool now) {
  169. _client->close(now);
  170. }
  171. bool AsyncBufferedClient::connected() {
  172. return _client->connected();
  173. }
  174. #endif // TELNET_SERVER_ASYNC_BUFFERED
  175. #endif // TELNET_SERVER == TELNET_SERVER_WIFISERVER
  176. size_t _telnetWrite(unsigned char clientId, const char *data, size_t len) {
  177. if (_telnetClients[clientId] && _telnetClients[clientId]->connected()) {
  178. return _telnetClients[clientId]->write(data, len);
  179. }
  180. return 0;
  181. }
  182. size_t _telnetWrite(const char *data, size_t len) {
  183. unsigned char count = 0;
  184. for (unsigned char i = 0; i < TELNET_MAX_CLIENTS; i++) {
  185. // Do not send broadcast messages to unauthenticated clients
  186. if (_telnetAuth && !_telnetClientsAuth[i]) {
  187. continue;
  188. }
  189. if (_telnetWrite(i, data, len)) ++count;
  190. }
  191. return count;
  192. }
  193. size_t _telnetWrite(const char *data) {
  194. return _telnetWrite(data, strlen(data));
  195. }
  196. size_t _telnetWrite(unsigned char clientId, const char * message) {
  197. return _telnetWrite(clientId, message, strlen(message));
  198. }
  199. void _telnetData(unsigned char clientId, char * data, size_t len) {
  200. if ((len >= 2) && (data[0] == TELNET_IAC)) {
  201. // C-d is sent as two bytes (sometimes repeating)
  202. if (data[1] == TELNET_XEOF) {
  203. _telnetDisconnect(clientId);
  204. }
  205. return; // Ignore telnet negotiation
  206. }
  207. if ((strncmp(data, "close", 5) == 0) || (strncmp(data, "quit", 4) == 0)) {
  208. #if TELNET_SERVER == TELNET_SERVER_WIFISERVER
  209. _telnetDisconnect(clientId);
  210. #else
  211. _telnetClients[clientId]->close();
  212. #endif
  213. return;
  214. }
  215. // Password prompt (disable on CORE variant)
  216. const bool authenticated = isEspurnaCore() ? true : _telnetClientsAuth[clientId];
  217. if (_telnetAuth && !authenticated) {
  218. String password = getAdminPass();
  219. if (strncmp(data, password.c_str(), password.length()) == 0) {
  220. DEBUG_MSG_P(PSTR("[TELNET] Client #%d authenticated\n"), clientId);
  221. _telnetWrite(clientId, "Password correct, welcome!\n");
  222. _telnetClientsAuth[clientId] = true;
  223. } else {
  224. _telnetWrite(clientId, "Password (try again): ");
  225. }
  226. return;
  227. }
  228. // Inject command
  229. #if TERMINAL_SUPPORT
  230. terminalInject((void*)data, len);
  231. #endif
  232. }
  233. void _telnetNotifyConnected(unsigned char i) {
  234. DEBUG_MSG_P(PSTR("[TELNET] Client #%u connected\n"), i);
  235. // If there is no terminal support automatically dump info and crash data
  236. #if DEBUG_SUPPORT
  237. #if not TERMINAL_SUPPORT
  238. info();
  239. wifiDebug();
  240. crashDump();
  241. crashClear();
  242. #endif
  243. #endif
  244. if (!isEspurnaCore()) {
  245. _telnetClientsAuth[i] = !_telnetAuth;
  246. if (_telnetAuth) {
  247. if (getAdminPass().length()) {
  248. _telnetWrite(i, "Password: ");
  249. } else {
  250. _telnetClientsAuth[i] = true;
  251. }
  252. }
  253. } else {
  254. _telnetClientsAuth[i] = true;
  255. }
  256. wifiReconnectCheck();
  257. }
  258. #if TELNET_SERVER == TELNET_SERVER_WIFISERVER
  259. void _telnetLoop() {
  260. if (_telnetServer.hasClient()) {
  261. int i;
  262. for (i = 0; i < TELNET_MAX_CLIENTS; i++) {
  263. if (!_telnetClients[i] || !_telnetClients[i]->connected()) {
  264. _telnetClients[i] = std::make_unique<TTelnetClient>(_telnetServer.available());
  265. if (_telnetClients[i]->localIP() != WiFi.softAPIP()) {
  266. // Telnet is always available for the ESPurna Core image
  267. const bool can_connect = isEspurnaCore() ? true : getSetting("telnetSTA", 1 == TELNET_STA);
  268. if (!can_connect) {
  269. DEBUG_MSG_P(PSTR("[TELNET] Rejecting - Only local connections\n"));
  270. _telnetDisconnect(i);
  271. return;
  272. }
  273. }
  274. _telnetNotifyConnected(i);
  275. break;
  276. }
  277. }
  278. //no free/disconnected spot so reject
  279. if (i == TELNET_MAX_CLIENTS) {
  280. DEBUG_MSG_P(PSTR("[TELNET] Rejecting - Too many connections\n"));
  281. _telnetServer.available().stop();
  282. return;
  283. }
  284. }
  285. for (int i = 0; i < TELNET_MAX_CLIENTS; i++) {
  286. if (_telnetClients[i]) {
  287. // Handle client timeouts
  288. if (!_telnetClients[i]->connected()) {
  289. _telnetDisconnect(i);
  290. } else {
  291. // Read data from clients
  292. while (_telnetClients[i] && _telnetClients[i]->available()) {
  293. char data[TERMINAL_BUFFER_SIZE];
  294. size_t len = _telnetClients[i]->available();
  295. unsigned int r = _telnetClients[i]->readBytes(data, min(sizeof(data), len));
  296. _telnetData(i, data, r);
  297. }
  298. }
  299. }
  300. }
  301. }
  302. #elif TELNET_SERVER == TELNET_SERVER_ASYNC
  303. void _telnetSetupClient(unsigned char i, AsyncClient *client) {
  304. client->onError([i](void *s, AsyncClient *client, int8_t error) {
  305. DEBUG_MSG_P(PSTR("[TELNET] Error %s (%d) on client #%u\n"), client->errorToString(error), error, i);
  306. });
  307. client->onData([i](void*, AsyncClient*, void *data, size_t len){
  308. _telnetData(i, reinterpret_cast<char*>(data), len);
  309. });
  310. client->onDisconnect([i](void*, AsyncClient*) {
  311. _telnetCleanUp();
  312. });
  313. // XXX: AsyncClient does not have copy ctor
  314. #if TELNET_SERVER_ASYNC_BUFFERED
  315. _telnetClients[i] = std::make_unique<TTelnetClient>(client);
  316. #else
  317. _telnetClients[i] = std::unique_ptr<TTelnetClient>(client);
  318. #endif // TELNET_SERVER_ASYNC_BUFFERED
  319. }
  320. void _telnetNewClient(AsyncClient* client) {
  321. if (client->localIP() != WiFi.softAPIP()) {
  322. // Telnet is always available for the ESPurna Core image
  323. const bool can_connect = isEspurnaCore() ? true : getSetting("telnetSTA", 1 == TELNET_STA);
  324. if (!can_connect) {
  325. DEBUG_MSG_P(PSTR("[TELNET] Rejecting - Only local connections\n"));
  326. client->onDisconnect([](void *s, AsyncClient *c) {
  327. delete c;
  328. });
  329. client->close(true);
  330. return;
  331. }
  332. }
  333. for (unsigned char i = 0; i < TELNET_MAX_CLIENTS; i++) {
  334. if (!_telnetClients[i] || !_telnetClients[i]->connected()) {
  335. _telnetSetupClient(i, client);
  336. _telnetNotifyConnected(i);
  337. return;
  338. }
  339. }
  340. DEBUG_MSG_P(PSTR("[TELNET] Rejecting - Too many connections\n"));
  341. client->onDisconnect([](void *s, AsyncClient *c) {
  342. delete c;
  343. });
  344. client->close(true);
  345. }
  346. #endif // TELNET_SERVER == TELNET_SERVER_WIFISERVER
  347. // -----------------------------------------------------------------------------
  348. // Public API
  349. // -----------------------------------------------------------------------------
  350. bool telnetConnected() {
  351. for (auto& client : _telnetClients) {
  352. if (client && client->connected()) return true;
  353. }
  354. return false;
  355. }
  356. #if DEBUG_TELNET_SUPPORT
  357. bool telnetDebugSend(const char* prefix, const char* data) {
  358. if (!telnetConnected()) return false;
  359. bool result = false;
  360. if (prefix && (prefix[0] != '\0')) {
  361. result = _telnetWrite(prefix) > 0;
  362. }
  363. return (_telnetWrite(data) > 0) || result;
  364. }
  365. #endif // DEBUG_TELNET_SUPPORT
  366. unsigned char telnetWrite(unsigned char ch) {
  367. char data[1] = {ch};
  368. return _telnetWrite(data, 1);
  369. }
  370. void _telnetConfigure() {
  371. _telnetAuth = getSetting("telnetAuth", 1 == TELNET_AUTHENTICATION);
  372. }
  373. void telnetSetup() {
  374. #if TELNET_SERVER == TELNET_SERVER_WIFISERVER
  375. espurnaRegisterLoop(_telnetLoop);
  376. _telnetServer.setNoDelay(true);
  377. _telnetServer.begin();
  378. #else
  379. _telnetServer.onClient([](void *s, AsyncClient* c) {
  380. _telnetNewClient(c);
  381. }, nullptr);
  382. _telnetServer.begin();
  383. #endif
  384. #if WEB_SUPPORT
  385. wsRegister()
  386. .onVisible([](JsonObject& root) { root["telnetVisible"] = 1; })
  387. .onConnected(_telnetWebSocketOnConnected)
  388. .onKeyCheck(_telnetWebSocketOnKeyCheck);
  389. #endif
  390. #if TELNET_REVERSE_SUPPORT
  391. #if MQTT_SUPPORT
  392. mqttRegister(_telnetReverseMQTTCallback);
  393. #endif
  394. #if TERMINAL_SUPPORT
  395. terminalRegisterCommand(F("TELNET.REVERSE"), [](Embedis* e) {
  396. if (e->argc < 3) {
  397. terminalError(F("Wrong arguments. Usage: TELNET.REVERSE <host> <port>"));
  398. return;
  399. }
  400. String host = String(e->argv[1]);
  401. uint16_t port = String(e->argv[2]).toInt();
  402. terminalOK();
  403. _telnetReverse(host.c_str(), port);
  404. });
  405. #endif
  406. #endif
  407. espurnaRegisterReload(_telnetConfigure);
  408. _telnetConfigure();
  409. DEBUG_MSG_P(PSTR("[TELNET] Listening on port %d\n"), TELNET_PORT);
  410. }
  411. #endif // TELNET_SUPPORT