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