1 /*
2 * libwebsockets - small server side websockets and web server implementation
3 *
4 * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
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
25 #include "libwebsockets.h"
26 #include "lws-ssh.h"
27
28 #include <string.h>
29
30 struct per_vhost_data__telnet {
31 struct lws_context *context;
32 struct lws_vhost *vhost;
33 const struct lws_protocols *protocol;
34 struct per_session_data__telnet *live_pss_list;
35 const struct lws_ssh_ops *ops;
36 };
37
38 struct per_session_data__telnet {
39 struct per_session_data__telnet *next;
40 struct per_vhost_data__telnet *vhd;
41 uint32_t rx_tail;
42 void *priv;
43
44 uint32_t initial:1;
45
46 char state;
47 uint8_t cmd;
48 };
49
50 enum {
51 LTS_BINARY_XMIT,
52 LTS_ECHO,
53 LTS_SUPPRESS_GA,
54
55
56 LTSC_SUBOPT_END = 240,
57 LTSC_BREAK = 243,
58 LTSC_SUBOPT_START = 250,
59 LTSC_WILL = 251,
60 LTSC_WONT,
61 LTSC_DO,
62 LTSC_DONT,
63 LTSC_IAC,
64
65 LTST_WAIT_IAC = 0,
66 LTST_GOT_IAC,
67 LTST_WAIT_OPT,
68 };
69
70 static int
telnet_ld(struct per_session_data__telnet * pss,uint8_t c)71 telnet_ld(struct per_session_data__telnet *pss, uint8_t c)
72 {
73 switch (pss->state) {
74 case LTST_WAIT_IAC:
75 if (c == LTSC_IAC) {
76 pss->state = LTST_GOT_IAC;
77 return 0;
78 }
79 return 1;
80
81 case LTST_GOT_IAC:
82 pss->state = LTST_WAIT_IAC;
83
84 switch (c) {
85 case LTSC_BREAK:
86 return 0;
87 case LTSC_WILL:
88 case LTSC_WONT:
89 case LTSC_DO:
90 case LTSC_DONT:
91 pss->cmd = c;
92 pss->state = LTST_WAIT_OPT;
93 return 0;
94 case LTSC_IAC:
95 return 1; /* double IAC */
96 }
97 return 0; /* ignore unknown */
98
99 case LTST_WAIT_OPT:
100 lwsl_notice(" tld: cmd %d: opt %d\n", pss->cmd, c);
101 pss->state = LTST_WAIT_IAC;
102 return 0;
103 }
104
105 return 0;
106 }
107
108 static uint8_t init[] = {
109 LTSC_IAC, LTSC_WILL, 3,
110 LTSC_IAC, LTSC_WILL, 1,
111 LTSC_IAC, LTSC_DONT, 1,
112 LTSC_IAC, LTSC_DO, 0
113 };
114
115 static int
lws_callback_raw_telnet(struct lws * wsi,enum lws_callback_reasons reason,void * user,void * in,size_t len)116 lws_callback_raw_telnet(struct lws *wsi, enum lws_callback_reasons reason,
117 void *user, void *in, size_t len)
118 {
119 struct per_session_data__telnet *pss =
120 (struct per_session_data__telnet *)user, **p;
121 struct per_vhost_data__telnet *vhd =
122 (struct per_vhost_data__telnet *)
123 lws_protocol_vh_priv_get(lws_get_vhost(wsi),
124 lws_get_protocol(wsi));
125 const struct lws_protocol_vhost_options *pvo =
126 (const struct lws_protocol_vhost_options *)in;
127 int n, m;
128 uint8_t buf[LWS_PRE + 800], *pu = in;
129
130 switch ((int)reason) {
131 case LWS_CALLBACK_PROTOCOL_INIT:
132 vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi),
133 lws_get_protocol(wsi),
134 sizeof(struct per_vhost_data__telnet));
135 vhd->context = lws_get_context(wsi);
136 vhd->protocol = lws_get_protocol(wsi);
137 vhd->vhost = lws_get_vhost(wsi);
138
139 while (pvo) {
140 if (!strcmp(pvo->name, "ops"))
141 vhd->ops = (const struct lws_ssh_ops *)pvo->value;
142
143 pvo = pvo->next;
144 }
145
146 if (!vhd->ops) {
147 lwsl_err("telnet pvo \"ops\" is mandatory\n");
148 return -1;
149 }
150 break;
151
152 case LWS_CALLBACK_RAW_ADOPT:
153 pss->next = vhd->live_pss_list;
154 vhd->live_pss_list = pss;
155 pss->vhd = vhd;
156 pss->state = LTST_WAIT_IAC;
157 pss->initial = 0;
158 if (vhd->ops->channel_create)
159 vhd->ops->channel_create(wsi, &pss->priv);
160 lws_callback_on_writable(wsi);
161 break;
162
163 case LWS_CALLBACK_RAW_CLOSE:
164 p = &vhd->live_pss_list;
165
166 while (*p) {
167 if ((*p) == pss) {
168 if (vhd->ops->channel_destroy)
169 vhd->ops->channel_destroy(pss->priv);
170 *p = pss->next;
171 continue;
172 }
173 p = &((*p)->next);
174 }
175 break;
176
177 case LWS_CALLBACK_RAW_RX:
178 n = 0;
179
180 /* this stuff is coming in telnet line discipline, we
181 * have to strip IACs and process IAC repeats */
182
183 while (len--) {
184 if (telnet_ld(pss, *pu))
185 buf[n++] = *pu++;
186 else
187 pu++;
188
189 if (n > 100 || !len)
190 pss->vhd->ops->rx(pss->priv, wsi, buf, (uint32_t)n);
191 }
192 break;
193
194 case LWS_CALLBACK_RAW_WRITEABLE:
195 n = 0;
196 if (!pss->initial) {
197 memcpy(buf + LWS_PRE, init, sizeof(init));
198
199 n = sizeof(init);
200 pss->initial = 1;
201 } else {
202 /* bring any waiting tx into second half of buffer
203 * restrict how much we can send to 1/4 of the buffer,
204 * because we have to apply telnet line discipline...
205 * in the worst case of all 0xff, doubling the size
206 */
207 pu = buf + LWS_PRE + 400;
208 m = (int)pss->vhd->ops->tx(pss->priv, LWS_STDOUT, pu,
209 (size_t)((int)sizeof(buf) - LWS_PRE - n - 401) / 2);
210
211 /*
212 * apply telnet line discipline and copy into place
213 * in output buffer
214 */
215 while (m--) {
216 if (*pu == 0xff)
217 buf[LWS_PRE + n++] = 0xff;
218 buf[LWS_PRE + n++] = *pu++;
219 }
220 }
221 if (n > 0) {
222 m = lws_write(wsi, (unsigned char *)buf + LWS_PRE, (unsigned int)n,
223 LWS_WRITE_HTTP);
224 if (m < 0) {
225 lwsl_err("ERROR %d writing to di socket\n", m);
226 return -1;
227 }
228 }
229
230 if (vhd->ops->tx_waiting(&pss->priv))
231 lws_callback_on_writable(wsi);
232 break;
233
234 case LWS_CALLBACK_SSH_UART_SET_RXFLOW:
235 /*
236 * this is sent to set rxflow state on any connections that
237 * sink on a particular uart. The uart index affected is in len
238 *
239 * More than one protocol may sink to the same uart, and the
240 * protocol may select the uart itself, eg, in the URL used
241 * to set up the connection.
242 */
243 lws_rx_flow_control(wsi, len & 1);
244 break;
245
246 default:
247 break;
248 }
249
250 return 0;
251 }
252
253 const struct lws_protocols protocols_telnet[] = {
254 {
255 "lws-telnetd-base",
256 lws_callback_raw_telnet,
257 sizeof(struct per_session_data__telnet),
258 1024, 0, NULL, 900
259 },
260 { NULL, NULL, 0, 0, 0, NULL, 0 } /* terminator */
261 };
262
263
264