blob: ddcf77d6f622e3585a41975ef84af90674cebd97 [file] [log] [blame]
adamdunkels627009a2003-08-21 22:24:19 +00001/*
2 * Copyright (c) 2003, Adam Dunkels.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. The name of the author may not be used to endorse or promote
14 * products derived from this software without specific prior
15 * written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
18 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
23 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
25 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 *
29 * This file is part of the Contiki desktop OS.
30 *
adamdunkelsa3c31032004-09-12 07:25:26 +000031 * $Id: telnetd.c,v 1.10 2004/09/12 07:25:26 adamdunkels Exp $
adamdunkels627009a2003-08-21 22:24:19 +000032 *
33 */
34
adamdunkels627009a2003-08-21 22:24:19 +000035#include "loader.h"
36#include "uip.h"
adamdunkelse2004302003-08-21 22:31:38 +000037#include "petsciiconv.h"
adamdunkels627009a2003-08-21 22:24:19 +000038#include "uip_arp.h"
39#include "resolv.h"
40
adamdunkelscd3d4982003-10-08 10:29:04 +000041#include "memb.h"
42
adamdunkels627009a2003-08-21 22:24:19 +000043#include "shell.h"
44
adamdunkels8e5d9902003-10-14 11:23:04 +000045#include "telnetd.h"
46
47#include "telnetd-conf.h"
48
adamdunkels627009a2003-08-21 22:24:19 +000049#include <string.h>
50
51#define ISO_nl 0x0a
52#define ISO_cr 0x0d
53
54#define XSIZE 36
55#define YSIZE 12
56
adamdunkelsf2f8cb22004-07-04 11:35:07 +000057/*static DISPATCHER_SIGHANDLER(sighandler, s, data);
adamdunkels627009a2003-08-21 22:24:19 +000058
59static struct dispatcher_proc p =
adamdunkelscd3d4982003-10-08 10:29:04 +000060 {DISPATCHER_PROC("Shell server", shell_idle, sighandler,
adamdunkels8e5d9902003-10-14 11:23:04 +000061 telnetd_appcall)};
adamdunkelsf2f8cb22004-07-04 11:35:07 +000062 static ek_id_t id = EK_ID_NONE;*/
63EK_EVENTHANDLER(eventhandler, ev, data);
64EK_PROCESS(p, "Shell server", EK_PRIO_NORMAL,
65 eventhandler, NULL, NULL);
66static ek_id_t id = EK_ID_NONE;
adamdunkels627009a2003-08-21 22:24:19 +000067
adamdunkels8e5d9902003-10-14 11:23:04 +000068MEMB(linemem, TELNETD_CONF_LINELEN, TELNETD_CONF_NUMLINES);
adamdunkelscd3d4982003-10-08 10:29:04 +000069
adamdunkels627009a2003-08-21 22:24:19 +000070
71struct telnetd_state {
adamdunkels8e5d9902003-10-14 11:23:04 +000072 char *lines[TELNETD_CONF_NUMLINES];
73 char buf[TELNETD_CONF_LINELEN];
adamdunkels627009a2003-08-21 22:24:19 +000074 char bufptr;
adamdunkels8e5d9902003-10-14 11:23:04 +000075 u8_t numsent;
adamdunkelscd3d4982003-10-08 10:29:04 +000076 u8_t state;
77#define STATE_NORMAL 0
78#define STATE_IAC 1
79#define STATE_WILL 2
80#define STATE_WONT 3
81#define STATE_DO 4
82#define STATE_DONT 5
83
84#define STATE_CLOSE 6
adamdunkels627009a2003-08-21 22:24:19 +000085};
86static struct telnetd_state s;
87
adamdunkelscd3d4982003-10-08 10:29:04 +000088#define TELNET_IAC 255
89#define TELNET_WILL 251
90#define TELNET_WONT 252
91#define TELNET_DO 253
92#define TELNET_DONT 254
adamdunkels627009a2003-08-21 22:24:19 +000093/*-----------------------------------------------------------------------------------*/
94static char *
95alloc_line(void)
96{
adamdunkelscd3d4982003-10-08 10:29:04 +000097 return memb_alloc(&linemem);
adamdunkels627009a2003-08-21 22:24:19 +000098}
99/*-----------------------------------------------------------------------------------*/
100static void
101dealloc_line(char *line)
102{
adamdunkelscd3d4982003-10-08 10:29:04 +0000103 memb_free(&linemem, line);
adamdunkels627009a2003-08-21 22:24:19 +0000104}
105/*-----------------------------------------------------------------------------------*/
106void
107shell_quit(char *str)
108{
adamdunkelscd3d4982003-10-08 10:29:04 +0000109 s.state = STATE_CLOSE;
110}
111/*-----------------------------------------------------------------------------------*/
112void
adamdunkels8e5d9902003-10-14 11:23:04 +0000113telnetd_quit(void)
adamdunkelscd3d4982003-10-08 10:29:04 +0000114{
adamdunkels8e5d9902003-10-14 11:23:04 +0000115#if TELNETD_CONF_GUI
116 telnetd_gui_quit();
117#endif /* TELNETD_CONF_GUI */
adamdunkelsf2f8cb22004-07-04 11:35:07 +0000118 ek_exit();
adamdunkels627009a2003-08-21 22:24:19 +0000119 id = EK_ID_NONE;
120 LOADER_UNLOAD();
121}
122/*-----------------------------------------------------------------------------------*/
adamdunkelscd3d4982003-10-08 10:29:04 +0000123static void
124sendline(char *line)
125{
126 static unsigned int i;
adamdunkels8e5d9902003-10-14 11:23:04 +0000127
128 for(i = 0; i < TELNETD_CONF_NUMLINES; ++i) {
adamdunkelscd3d4982003-10-08 10:29:04 +0000129 if(s.lines[i] == NULL) {
130 s.lines[i] = line;
131 break;
132 }
133 }
adamdunkels8e5d9902003-10-14 11:23:04 +0000134 if(i == TELNETD_CONF_NUMLINES) {
adamdunkelscd3d4982003-10-08 10:29:04 +0000135 dealloc_line(line);
136 }
137}
138/*-----------------------------------------------------------------------------------*/
139void
140shell_prompt(char *str)
141{
142 char *line;
143 line = alloc_line();
144 if(line != NULL) {
adamdunkels8e5d9902003-10-14 11:23:04 +0000145 strncpy(line, str, TELNETD_CONF_LINELEN);
146 petsciiconv_toascii(line, TELNETD_CONF_LINELEN);
adamdunkelscd3d4982003-10-08 10:29:04 +0000147 sendline(line);
148 }
149}
150/*-----------------------------------------------------------------------------------*/
adamdunkels627009a2003-08-21 22:24:19 +0000151void
152shell_output(char *str1, char *str2)
153{
adamdunkelscd3d4982003-10-08 10:29:04 +0000154 static unsigned len;
adamdunkels627009a2003-08-21 22:24:19 +0000155 char *line;
adamdunkels627009a2003-08-21 22:24:19 +0000156
adamdunkels627009a2003-08-21 22:24:19 +0000157 line = alloc_line();
158 if(line != NULL) {
159 len = strlen(str1);
adamdunkels8e5d9902003-10-14 11:23:04 +0000160 strncpy(line, str1, TELNETD_CONF_LINELEN);
161 if(len < TELNETD_CONF_LINELEN) {
162 strncpy(line + len, str2, TELNETD_CONF_LINELEN - len);
adamdunkels627009a2003-08-21 22:24:19 +0000163 }
164 len = strlen(line);
adamdunkels8e5d9902003-10-14 11:23:04 +0000165 if(len < TELNETD_CONF_LINELEN - 2) {
adamdunkels627009a2003-08-21 22:24:19 +0000166 line[len] = ISO_cr;
167 line[len+1] = ISO_nl;
168 line[len+2] = 0;
169 }
adamdunkels8e5d9902003-10-14 11:23:04 +0000170 petsciiconv_toascii(line, TELNETD_CONF_LINELEN);
adamdunkelscd3d4982003-10-08 10:29:04 +0000171 sendline(line);
adamdunkels627009a2003-08-21 22:24:19 +0000172 }
173}
174/*-----------------------------------------------------------------------------------*/
adamdunkels8bb5cca2003-08-24 22:41:31 +0000175LOADER_INIT_FUNC(telnetd_init, arg)
adamdunkels627009a2003-08-21 22:24:19 +0000176{
adamdunkels8bb5cca2003-08-24 22:41:31 +0000177 arg_free(arg);
178
adamdunkels8e5d9902003-10-14 11:23:04 +0000179 if(id == EK_ID_NONE) {
adamdunkelsf2f8cb22004-07-04 11:35:07 +0000180 id = ek_start(&p);
adamdunkels627009a2003-08-21 22:24:19 +0000181 }
adamdunkels627009a2003-08-21 22:24:19 +0000182}
183/*-----------------------------------------------------------------------------------*/
adamdunkelsf2f8cb22004-07-04 11:35:07 +0000184EK_EVENTHANDLER(eventhandler, ev, data)
adamdunkels627009a2003-08-21 22:24:19 +0000185{
adamdunkelsf2f8cb22004-07-04 11:35:07 +0000186 EK_EVENTHANDLER_ARGS(ev, data);
adamdunkels627009a2003-08-21 22:24:19 +0000187
adamdunkelsf2f8cb22004-07-04 11:35:07 +0000188 if(ev == EK_EVENT_INIT) {
189 tcp_listen(HTONS(23));
190 memb_init(&linemem);
191 shell_init();
192 } else if(ev == tcpip_event) {
193 telnetd_appcall(data);
194 } else if(ev == EK_EVENT_REQUEST_EXIT) {
adamdunkels8e5d9902003-10-14 11:23:04 +0000195 telnetd_quit();
adamdunkels8e5d9902003-10-14 11:23:04 +0000196 } else {
adamdunkelscf428542003-11-27 15:53:00 +0000197#if TELNETD_CONF_GUI
adamdunkelsf2f8cb22004-07-04 11:35:07 +0000198 telnetd_gui_eventhandler(ev, data);
adamdunkels8e5d9902003-10-14 11:23:04 +0000199#endif /* TELNETD_CONF_GUI */
adamdunkels627009a2003-08-21 22:24:19 +0000200 }
201}
202/*-----------------------------------------------------------------------------------*/
203static void
204acked(void)
205{
adamdunkelscf428542003-11-27 15:53:00 +0000206 static unsigned int i;
207
adamdunkels8e5d9902003-10-14 11:23:04 +0000208 while(s.numsent > 0) {
209 dealloc_line(s.lines[0]);
210 for(i = 1; i < TELNETD_CONF_NUMLINES; ++i) {
211 s.lines[i - 1] = s.lines[i];
212 }
213 s.lines[TELNETD_CONF_NUMLINES - 1] = NULL;
214 --s.numsent;
adamdunkels627009a2003-08-21 22:24:19 +0000215 }
216}
217/*-----------------------------------------------------------------------------------*/
218static void
219senddata(void)
220{
adamdunkels8e5d9902003-10-14 11:23:04 +0000221 static char *bufptr, *lineptr;
222 static int buflen, linelen;
223
224 bufptr = uip_appdata;
225 buflen = 0;
226 for(s.numsent = 0; s.numsent < TELNETD_CONF_NUMLINES &&
adamdunkelscf428542003-11-27 15:53:00 +0000227 s.lines[s.numsent] != NULL ; ++s.numsent) {
adamdunkels8e5d9902003-10-14 11:23:04 +0000228 lineptr = s.lines[s.numsent];
229 linelen = strlen(lineptr);
230 if(linelen > TELNETD_CONF_LINELEN) {
231 linelen = TELNETD_CONF_LINELEN;
232 }
233 if(buflen + linelen < uip_mss()) {
adamdunkelscf428542003-11-27 15:53:00 +0000234 memcpy(bufptr, lineptr, linelen);
adamdunkels8e5d9902003-10-14 11:23:04 +0000235 bufptr += linelen;
236 buflen += linelen;
237 } else {
238 break;
239 }
240 }
241 uip_send(uip_appdata, buflen);
242}
243/*-----------------------------------------------------------------------------------*/
244static void
245closed(void)
246{
adamdunkelscf428542003-11-27 15:53:00 +0000247 static unsigned int i;
248
adamdunkels8e5d9902003-10-14 11:23:04 +0000249 for(i = 0; i < TELNETD_CONF_NUMLINES; ++i) {
250 if(s.lines[i] != NULL) {
251 dealloc_line(s.lines[i]);
252 }
adamdunkels627009a2003-08-21 22:24:19 +0000253 }
254}
255/*-----------------------------------------------------------------------------------*/
256static void
adamdunkelscd3d4982003-10-08 10:29:04 +0000257getchar(u8_t c)
258{
259 if(c == ISO_cr) {
260 return;
261 }
262
adamdunkels8e5d9902003-10-14 11:23:04 +0000263 s.buf[(int)s.bufptr] = c;
264 if(s.buf[(int)s.bufptr] == ISO_nl ||
adamdunkelscd3d4982003-10-08 10:29:04 +0000265 s.bufptr == sizeof(s.buf) - 1) {
266 if(s.bufptr > 0) {
adamdunkels8e5d9902003-10-14 11:23:04 +0000267 s.buf[(int)s.bufptr] = 0;
268 petsciiconv_topetscii(s.buf, TELNETD_CONF_LINELEN);
adamdunkelscd3d4982003-10-08 10:29:04 +0000269 }
270 shell_input(s.buf);
271 s.bufptr = 0;
272 } else {
273 ++s.bufptr;
274 }
275}
276/*-----------------------------------------------------------------------------------*/
277static void
278sendopt(u8_t option, u8_t value)
279{
280 char *line;
281 line = alloc_line();
282 if(line != NULL) {
283 line[0] = TELNET_IAC;
284 line[1] = option;
285 line[2] = value;
286 line[3] = 0;
287 sendline(line);
288 }
289}
290/*-----------------------------------------------------------------------------------*/
291static void
adamdunkels627009a2003-08-21 22:24:19 +0000292newdata(void)
293{
294 u16_t len;
adamdunkelscd3d4982003-10-08 10:29:04 +0000295 u8_t c;
296
297
adamdunkels627009a2003-08-21 22:24:19 +0000298 len = uip_datalen();
299
300 while(len > 0 && s.bufptr < sizeof(s.buf)) {
adamdunkelscd3d4982003-10-08 10:29:04 +0000301 c = *uip_appdata;
adamdunkels627009a2003-08-21 22:24:19 +0000302 ++uip_appdata;
303 --len;
adamdunkelscd3d4982003-10-08 10:29:04 +0000304 switch(s.state) {
305 case STATE_IAC:
306 if(c == TELNET_IAC) {
307 getchar(c);
308 s.state = STATE_NORMAL;
309 } else {
310 switch(c) {
311 case TELNET_WILL:
312 s.state = STATE_WILL;
313 break;
314 case TELNET_WONT:
315 s.state = STATE_WONT;
316 break;
317 case TELNET_DO:
318 s.state = STATE_DO;
319 break;
320 case TELNET_DONT:
321 s.state = STATE_DONT;
322 break;
323 default:
324 s.state = STATE_NORMAL;
325 break;
326 }
327 }
328 break;
329 case STATE_WILL:
330 /* Reply with a DONT */
331 sendopt(TELNET_DONT, c);
332 s.state = STATE_NORMAL;
333 break;
334
335 case STATE_WONT:
336 /* Reply with a DONT */
337 sendopt(TELNET_DONT, c);
338 s.state = STATE_NORMAL;
339 break;
340 case STATE_DO:
341 /* Reply with a WONT */
342 sendopt(TELNET_WONT, c);
343 s.state = STATE_NORMAL;
344 break;
345 case STATE_DONT:
346 /* Reply with a WONT */
347 sendopt(TELNET_WONT, c);
348 s.state = STATE_NORMAL;
349 break;
350 case STATE_NORMAL:
351 if(c == TELNET_IAC) {
352 s.state = STATE_IAC;
353 } else {
354 getchar(c);
355 }
356 break;
357 }
358
359
360 }
adamdunkels627009a2003-08-21 22:24:19 +0000361
362}
363/*-----------------------------------------------------------------------------------*/
adamdunkelsf2f8cb22004-07-04 11:35:07 +0000364void
365telnetd_appcall(void *ts)
adamdunkels627009a2003-08-21 22:24:19 +0000366{
adamdunkelscf428542003-11-27 15:53:00 +0000367 static unsigned int i;
adamdunkels627009a2003-08-21 22:24:19 +0000368 if(uip_connected()) {
adamdunkelsf2f8cb22004-07-04 11:35:07 +0000369 tcp_markconn(uip_conn, &s);
adamdunkels8e5d9902003-10-14 11:23:04 +0000370 for(i = 0; i < TELNETD_CONF_NUMLINES; ++i) {
adamdunkels627009a2003-08-21 22:24:19 +0000371 s.lines[i] = NULL;
372 }
373 s.bufptr = 0;
adamdunkelscd3d4982003-10-08 10:29:04 +0000374 s.state = STATE_NORMAL;
adamdunkels627009a2003-08-21 22:24:19 +0000375
adamdunkelscf428542003-11-27 15:53:00 +0000376 shell_start();
adamdunkels627009a2003-08-21 22:24:19 +0000377 }
adamdunkelscd3d4982003-10-08 10:29:04 +0000378
379 if(s.state == STATE_CLOSE) {
380 s.state = STATE_NORMAL;
381 uip_close();
382 return;
383 }
adamdunkels627009a2003-08-21 22:24:19 +0000384
adamdunkels8e5d9902003-10-14 11:23:04 +0000385 if(uip_closed() ||
386 uip_aborted() ||
387 uip_timedout()) {
388 closed();
389 }
adamdunkels627009a2003-08-21 22:24:19 +0000390
391 if(uip_acked()) {
392 acked();
393 }
394
395 if(uip_newdata()) {
396 newdata();
397 }
398
399 if(uip_rexmit() ||
400 uip_newdata() ||
adamdunkels8e5d9902003-10-14 11:23:04 +0000401 uip_acked() ||
402 uip_connected() ||
403 uip_poll()) {
adamdunkels627009a2003-08-21 22:24:19 +0000404 senddata();
405 }
406}
407/*-----------------------------------------------------------------------------------*/