telnet.c 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. /*
  2. * libwebsockets - small server side websockets and web server implementation
  3. *
  4. * Copyright (C) 2010 - 2019 Andy Green <[email protected]>
  5. *
  6. * Permission is hereby granted, free of charge, to any person obtaining a copy
  7. * of this software and associated documentation files (the "Software"), to
  8. * deal in the Software without restriction, including without limitation the
  9. * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  10. * sell copies of the Software, and to permit persons to whom the Software is
  11. * furnished to do so, subject to the following conditions:
  12. *
  13. * The above copyright notice and this permission notice shall be included in
  14. * all copies or substantial portions of the Software.
  15. *
  16. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  21. * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  22. * IN THE SOFTWARE.
  23. */
  24. #include "libwebsockets.h"
  25. #include "lws-ssh.h"
  26. #include <string.h>
  27. struct per_vhost_data__telnet {
  28. struct lws_context *context;
  29. struct lws_vhost *vhost;
  30. const struct lws_protocols *protocol;
  31. struct per_session_data__telnet *live_pss_list;
  32. const struct lws_ssh_ops *ops;
  33. };
  34. struct per_session_data__telnet {
  35. struct per_session_data__telnet *next;
  36. struct per_vhost_data__telnet *vhd;
  37. uint32_t rx_tail;
  38. void *priv;
  39. uint32_t initial:1;
  40. char state;
  41. uint8_t cmd;
  42. };
  43. enum {
  44. LTS_BINARY_XMIT,
  45. LTS_ECHO,
  46. LTS_SUPPRESS_GA,
  47. LTSC_SUBOPT_END = 240,
  48. LTSC_BREAK = 243,
  49. LTSC_SUBOPT_START = 250,
  50. LTSC_WILL = 251,
  51. LTSC_WONT,
  52. LTSC_DO,
  53. LTSC_DONT,
  54. LTSC_IAC,
  55. LTST_WAIT_IAC = 0,
  56. LTST_GOT_IAC,
  57. LTST_WAIT_OPT,
  58. };
  59. static int
  60. telnet_ld(struct per_session_data__telnet *pss, uint8_t c)
  61. {
  62. switch (pss->state) {
  63. case LTST_WAIT_IAC:
  64. if (c == LTSC_IAC) {
  65. pss->state = LTST_GOT_IAC;
  66. return 0;
  67. }
  68. return 1;
  69. case LTST_GOT_IAC:
  70. pss->state = LTST_WAIT_IAC;
  71. switch (c) {
  72. case LTSC_BREAK:
  73. return 0;
  74. case LTSC_WILL:
  75. case LTSC_WONT:
  76. case LTSC_DO:
  77. case LTSC_DONT:
  78. pss->cmd = c;
  79. pss->state = LTST_WAIT_OPT;
  80. return 0;
  81. case LTSC_IAC:
  82. return 1; /* double IAC */
  83. }
  84. return 0; /* ignore unknown */
  85. case LTST_WAIT_OPT:
  86. lwsl_notice(" tld: cmd %d: opt %d\n", pss->cmd, c);
  87. pss->state = LTST_WAIT_IAC;
  88. return 0;
  89. }
  90. return 0;
  91. }
  92. static uint8_t init[] = {
  93. LTSC_IAC, LTSC_WILL, 3,
  94. LTSC_IAC, LTSC_WILL, 1,
  95. LTSC_IAC, LTSC_DONT, 1,
  96. LTSC_IAC, LTSC_DO, 0
  97. };
  98. static int
  99. lws_callback_raw_telnet(struct lws *wsi, enum lws_callback_reasons reason,
  100. void *user, void *in, size_t len)
  101. {
  102. struct per_session_data__telnet *pss =
  103. (struct per_session_data__telnet *)user, **p;
  104. struct per_vhost_data__telnet *vhd =
  105. (struct per_vhost_data__telnet *)
  106. lws_protocol_vh_priv_get(lws_get_vhost(wsi),
  107. lws_get_protocol(wsi));
  108. const struct lws_protocol_vhost_options *pvo =
  109. (const struct lws_protocol_vhost_options *)in;
  110. int n, m;
  111. uint8_t buf[LWS_PRE + 800], *pu = in;
  112. switch ((int)reason) {
  113. case LWS_CALLBACK_PROTOCOL_INIT:
  114. vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi),
  115. lws_get_protocol(wsi),
  116. sizeof(struct per_vhost_data__telnet));
  117. vhd->context = lws_get_context(wsi);
  118. vhd->protocol = lws_get_protocol(wsi);
  119. vhd->vhost = lws_get_vhost(wsi);
  120. while (pvo) {
  121. if (!strcmp(pvo->name, "ops"))
  122. vhd->ops = (const struct lws_ssh_ops *)pvo->value;
  123. pvo = pvo->next;
  124. }
  125. if (!vhd->ops) {
  126. lwsl_err("telnet pvo \"ops\" is mandatory\n");
  127. return -1;
  128. }
  129. break;
  130. case LWS_CALLBACK_RAW_ADOPT:
  131. pss->next = vhd->live_pss_list;
  132. vhd->live_pss_list = pss;
  133. pss->vhd = vhd;
  134. pss->state = LTST_WAIT_IAC;
  135. pss->initial = 0;
  136. if (vhd->ops->channel_create)
  137. vhd->ops->channel_create(wsi, &pss->priv);
  138. lws_callback_on_writable(wsi);
  139. break;
  140. case LWS_CALLBACK_RAW_CLOSE:
  141. p = &vhd->live_pss_list;
  142. while (*p) {
  143. if ((*p) == pss) {
  144. if (vhd->ops->channel_destroy)
  145. vhd->ops->channel_destroy(pss->priv);
  146. *p = pss->next;
  147. continue;
  148. }
  149. p = &((*p)->next);
  150. }
  151. break;
  152. case LWS_CALLBACK_RAW_RX:
  153. n = 0;
  154. /* this stuff is coming in telnet line discipline, we
  155. * have to strip IACs and process IAC repeats */
  156. while (len--) {
  157. if (telnet_ld(pss, *pu))
  158. buf[n++] = *pu++;
  159. else
  160. pu++;
  161. if (n > 100 || !len)
  162. pss->vhd->ops->rx(pss->priv, wsi, buf, n);
  163. }
  164. break;
  165. case LWS_CALLBACK_RAW_WRITEABLE:
  166. n = 0;
  167. if (!pss->initial) {
  168. memcpy(buf + LWS_PRE, init, sizeof(init));
  169. n = sizeof(init);
  170. pss->initial = 1;
  171. } else {
  172. /* bring any waiting tx into second half of buffer
  173. * restrict how much we can send to 1/4 of the buffer,
  174. * because we have to apply telnet line discipline...
  175. * in the worst case of all 0xff, doubling the size
  176. */
  177. pu = buf + LWS_PRE + 400;
  178. m = (int)pss->vhd->ops->tx(pss->priv, LWS_STDOUT, pu,
  179. ((int)sizeof(buf) - LWS_PRE - n - 401) / 2);
  180. /*
  181. * apply telnet line discipline and copy into place
  182. * in output buffer
  183. */
  184. while (m--) {
  185. if (*pu == 0xff)
  186. buf[LWS_PRE + n++] = 0xff;
  187. buf[LWS_PRE + n++] = *pu++;
  188. }
  189. }
  190. if (n > 0) {
  191. m = lws_write(wsi, (unsigned char *)buf + LWS_PRE, n,
  192. LWS_WRITE_HTTP);
  193. if (m < 0) {
  194. lwsl_err("ERROR %d writing to di socket\n", m);
  195. return -1;
  196. }
  197. }
  198. if (vhd->ops->tx_waiting(&pss->priv))
  199. lws_callback_on_writable(wsi);
  200. break;
  201. case LWS_CALLBACK_SSH_UART_SET_RXFLOW:
  202. /*
  203. * this is sent to set rxflow state on any connections that
  204. * sink on a particular uart. The uart index affected is in len
  205. *
  206. * More than one protocol may sink to the same uart, and the
  207. * protocol may select the uart itself, eg, in the URL used
  208. * to set up the connection.
  209. */
  210. lws_rx_flow_control(wsi, len & 1);
  211. break;
  212. default:
  213. break;
  214. }
  215. return 0;
  216. }
  217. const struct lws_protocols protocols_telnet[] = {
  218. {
  219. "lws-telnetd-base",
  220. lws_callback_raw_telnet,
  221. sizeof(struct per_session_data__telnet),
  222. 1024, 0, NULL, 900
  223. },
  224. { NULL, NULL, 0, 0, 0, NULL, 0 } /* terminator */
  225. };