• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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