Contiki 3.x
telnetd.c
1 /*
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  *
31  *
32  */
33 
34 #include <string.h>
35 
36 #include "sys/cc.h"
37 #include "contiki-lib.h"
38 #include "contiki-net.h"
39 #include "lib/petsciiconv.h"
40 #include "shell.h"
41 
42 #include "telnetd.h"
43 
44 #define ISO_nl 0x0a
45 #define ISO_cr 0x0d
46 
47 PROCESS(telnetd_process, "Telnet server");
48 
49 AUTOSTART_PROCESSES(&telnetd_process);
50 
51 #ifndef TELNETD_CONF_LINELEN
52 #define TELNETD_CONF_LINELEN 80
53 #endif
54 #ifndef TELNETD_CONF_NUMLINES
55 #define TELNETD_CONF_NUMLINES 25
56 #endif
57 
58 #ifdef TELNETD_CONF_REJECT
59 extern char telnetd_reject_text[];
60 #else
61 static char telnetd_reject_text[] =
62  "Too many connections, please try again later.";
63 #endif
64 
65 #ifndef TELNETD_CONF_MAX_IDLE_TIME
66 #define TELNETD_CONF_MAX_IDLE_TIME (CLOCK_SECOND * 30)
67 #endif
68 
69 struct telnetd_state {
70  char buf[TELNETD_CONF_LINELEN + 1];
71  char bufptr;
72  uint16_t numsent;
73  uint8_t state;
74 #define STATE_NORMAL 0
75 #define STATE_IAC 1
76 #define STATE_WILL 2
77 #define STATE_WONT 3
78 #define STATE_DO 4
79 #define STATE_DONT 5
80 #define STATE_CLOSE 6
81 #if TELNETD_CONF_MAX_IDLE_TIME
82  struct timer silence_timer;
83 #endif /* TELNETD_CONF_MAX_IDLE_TIME */
84 };
85 static struct telnetd_state s;
86 
87 #define TELNET_IAC 255
88 #define TELNET_WILL 251
89 #define TELNET_WONT 252
90 #define TELNET_DO 253
91 #define TELNET_DONT 254
92 
93 #define DEBUG 0
94 #if DEBUG
95 #include <stdio.h>
96 #define PRINTF(...) printf(__VA_ARGS__)
97 #else
98 #define PRINTF(...)
99 #endif
100 
101 struct telnetd_buf {
102  char bufmem[TELNETD_CONF_NUMLINES * TELNETD_CONF_LINELEN];
103  int ptr;
104  int size;
105 };
106 
107 static struct telnetd_buf buf;
108 
109 static uint8_t connected;
110 
111 /*---------------------------------------------------------------------------*/
112 static void
113 buf_init(struct telnetd_buf *buf)
114 {
115  buf->ptr = 0;
116  buf->size = TELNETD_CONF_NUMLINES * TELNETD_CONF_LINELEN;
117 }
118 /*---------------------------------------------------------------------------*/
119 static int
120 buf_append(struct telnetd_buf *buf, const char *data, int len)
121 {
122  int copylen;
123 
124  PRINTF("buf_append len %d (%d) '%.*s'\n", len, buf->ptr, len, data);
125  copylen = MIN(len, buf->size - buf->ptr);
126  memcpy(&buf->bufmem[buf->ptr], data, copylen);
127  petsciiconv_toascii(&buf->bufmem[buf->ptr], copylen);
128  buf->ptr += copylen;
129 
130  return copylen;
131 }
132 /*---------------------------------------------------------------------------*/
133 static void
134 buf_copyto(struct telnetd_buf *buf, char *to, int len)
135 {
136  memcpy(to, &buf->bufmem[0], len);
137 }
138 /*---------------------------------------------------------------------------*/
139 static void
140 buf_pop(struct telnetd_buf *buf, int len)
141 {
142  int poplen;
143 
144  PRINTF("buf_pop len %d (%d)\n", len, buf->ptr);
145  poplen = MIN(len, buf->ptr);
146  memcpy(&buf->bufmem[0], &buf->bufmem[poplen], buf->ptr - poplen);
147  buf->ptr -= poplen;
148 }
149 /*---------------------------------------------------------------------------*/
150 static int
151 buf_len(struct telnetd_buf *buf)
152 {
153  return buf->ptr;
154 }
155 /*---------------------------------------------------------------------------*/
156 void
157 telnetd_quit(void)
158 {
159  shell_quit();
160 #if TELNETD_CONF_GUI
161  telnetd_gui_quit();
162 #endif /* TELNETD_CONF_GUI */
163  process_exit(&telnetd_process);
164  LOADER_UNLOAD();
165 }
166 /*---------------------------------------------------------------------------*/
167 void
168 shell_prompt(char *str)
169 {
170  buf_append(&buf, str, (int)strlen(str));
171 }
172 /*---------------------------------------------------------------------------*/
173 void
174 shell_default_output(const char *str1, int len1, const char *str2, int len2)
175 {
176  static const char crnl[2] = {ISO_cr, ISO_nl};
177 
178  if(len1 > 0 && str1[len1 - 1] == '\n') {
179  --len1;
180  }
181  if(len2 > 0 && str2[len2 - 1] == '\n') {
182  --len2;
183  }
184 
185  /* PRINTF("shell_default_output: %.*s %.*s\n", len1, str1, len2, str2);*/
186 
187 #if TELNETD_CONF_GUI
188  telnetd_gui_output(str1, len1, str2, len2);
189 #endif /* TELNETD_CONF_GUI */
190  buf_append(&buf, str1, len1);
191  buf_append(&buf, str2, len2);
192  buf_append(&buf, crnl, sizeof(crnl));
193 }
194 /*---------------------------------------------------------------------------*/
195 void
196 shell_exit(void)
197 {
198  s.state = STATE_CLOSE;
199 }
200 /*---------------------------------------------------------------------------*/
201 PROCESS_THREAD(telnetd_process, ev, data)
202 {
203  PROCESS_BEGIN();
204 
205  shell_init();
206 
207 #if TELNETD_CONF_GUI
208  telnetd_gui_init();
209 #endif /* TELNETD_CONF_GUI */
210 
211  petsciiconv_toascii(telnetd_reject_text, strlen(telnetd_reject_text));
212 
213  tcp_listen(UIP_HTONS(23));
214 
215  while(1) {
217  if(ev == tcpip_event) {
218  telnetd_appcall(data);
219  } else if(ev == PROCESS_EVENT_EXIT) {
220  telnetd_quit();
221  } else {
222 #if TELNETD_CONF_GUI
223  telnetd_gui_eventhandler(ev, data);
224 #endif /* TELNETD_CONF_GUI */
225  }
226  }
227 
228  PROCESS_END();
229 }
230 /*---------------------------------------------------------------------------*/
231 static void
232 acked(void)
233 {
234  buf_pop(&buf, s.numsent);
235 }
236 /*---------------------------------------------------------------------------*/
237 static void
238 senddata(void)
239 {
240  int len;
241  len = MIN(buf_len(&buf), uip_mss());
242  PRINTF("senddata len %d\n", len);
243  buf_copyto(&buf, uip_appdata, len);
244  uip_send(uip_appdata, len);
245  s.numsent = len;
246 }
247 /*---------------------------------------------------------------------------*/
248 static void
249 get_char(uint8_t c)
250 {
251  PRINTF("telnetd: get_char '%c' %d %d\n", c, c, s.bufptr);
252 
253  if(c == 0) {
254  return;
255  }
256 
257  if(c != ISO_nl && c != ISO_cr) {
258  s.buf[(int)s.bufptr] = c;
259  ++s.bufptr;
260  }
261  if(((c == ISO_nl || c == ISO_cr) && s.bufptr > 0) ||
262  s.bufptr == sizeof(s.buf)) {
263  if(s.bufptr < sizeof(s.buf)) {
264  s.buf[(int)s.bufptr] = 0;
265  }
266  petsciiconv_topetscii(s.buf, TELNETD_CONF_LINELEN);
267  PRINTF("telnetd: get_char '%.*s'\n", s.bufptr, s.buf);
268  shell_input(s.buf, s.bufptr);
269  s.bufptr = 0;
270  }
271 }
272 /*---------------------------------------------------------------------------*/
273 static void
274 sendopt(uint8_t option, uint8_t value)
275 {
276  char line[4];
277  line[0] = (char)TELNET_IAC;
278  line[1] = option;
279  line[2] = value;
280  line[3] = 0;
281  petsciiconv_topetscii(line, 4);
282  buf_append(&buf, line, 4);
283 }
284 /*---------------------------------------------------------------------------*/
285 static void
286 newdata(void)
287 {
288  uint16_t len;
289  uint8_t c;
290  uint8_t *ptr;
291 
292  len = uip_datalen();
293  PRINTF("newdata len %d '%.*s'\n", len, len, (char *)uip_appdata);
294 
295  ptr = uip_appdata;
296  while(len > 0 && s.bufptr < sizeof(s.buf)) {
297  c = *ptr;
298  PRINTF("newdata char '%c' %d %d state %d\n", c, c, len, s.state);
299  ++ptr;
300  --len;
301  switch(s.state) {
302  case STATE_IAC:
303  if(c == TELNET_IAC) {
304  get_char(c);
305  s.state = STATE_NORMAL;
306  } else {
307  switch(c) {
308  case TELNET_WILL:
309  s.state = STATE_WILL;
310  break;
311  case TELNET_WONT:
312  s.state = STATE_WONT;
313  break;
314  case TELNET_DO:
315  s.state = STATE_DO;
316  break;
317  case TELNET_DONT:
318  s.state = STATE_DONT;
319  break;
320  default:
321  s.state = STATE_NORMAL;
322  break;
323  }
324  }
325  break;
326  case STATE_WILL:
327  /* Reply with a DONT */
328  sendopt(TELNET_DONT, c);
329  s.state = STATE_NORMAL;
330  break;
331 
332  case STATE_WONT:
333  /* Reply with a DONT */
334  sendopt(TELNET_DONT, c);
335  s.state = STATE_NORMAL;
336  break;
337  case STATE_DO:
338  /* Reply with a WONT */
339  sendopt(TELNET_WONT, c);
340  s.state = STATE_NORMAL;
341  break;
342  case STATE_DONT:
343  /* Reply with a WONT */
344  sendopt(TELNET_WONT, c);
345  s.state = STATE_NORMAL;
346  break;
347  case STATE_NORMAL:
348  if(c == TELNET_IAC) {
349  s.state = STATE_IAC;
350  } else {
351  get_char(c);
352  }
353  break;
354  }
355  }
356 }
357 /*---------------------------------------------------------------------------*/
358 void
359 telnetd_appcall(void *ts)
360 {
361  if(uip_connected()) {
362  if(!connected) {
363  buf_init(&buf);
364  s.bufptr = 0;
365  s.state = STATE_NORMAL;
366  connected = 1;
367  shell_start();
368 #if TELNETD_CONF_MAX_IDLE_TIME
369  timer_set(&s.silence_timer, TELNETD_CONF_MAX_IDLE_TIME);
370 #endif /* TELNETD_CONF_MAX_IDLE_TIME */
371  ts = (char *)0;
372  } else {
373  uip_send(telnetd_reject_text, strlen(telnetd_reject_text));
374  ts = (char *)1;
375  }
376  tcp_markconn(uip_conn, ts);
377  }
378 
379  if(!ts) {
380  if(s.state == STATE_CLOSE) {
381  s.state = STATE_NORMAL;
382  uip_close();
383  return;
384  }
385  if(uip_closed() ||
386  uip_aborted() ||
387  uip_timedout()) {
388  shell_stop();
389  connected = 0;
390  }
391  if(uip_acked()) {
392 #if TELNETD_CONF_MAX_IDLE_TIME
393  timer_set(&s.silence_timer, TELNETD_CONF_MAX_IDLE_TIME);
394 #endif /* TELNETD_CONF_MAX_IDLE_TIME */
395  acked();
396  }
397  if(uip_newdata()) {
398 #if TELNETD_CONF_MAX_IDLE_TIME
399  timer_set(&s.silence_timer, TELNETD_CONF_MAX_IDLE_TIME);
400 #endif /* TELNETD_CONF_MAX_IDLE_TIME */
401  newdata();
402  }
403  if(uip_rexmit() ||
404  uip_newdata() ||
405  uip_acked() ||
406  uip_connected() ||
407  uip_poll()) {
408  senddata();
409 #if TELNETD_CONF_MAX_IDLE_TIME
410  if(s.numsent > 0) {
411  timer_set(&s.silence_timer, TELNETD_CONF_MAX_IDLE_TIME);
412  }
413 #endif /* TELNETD_CONF_MAX_IDLE_TIME */
414  }
415 #if TELNETD_CONF_MAX_IDLE_TIME
416  if(uip_poll()) {
417  if(timer_expired(&s.silence_timer)) {
418  uip_close();
419  tcp_markconn(uip_conn, NULL);
420  }
421  }
422 #endif /* TELNETD_CONF_MAX_IDLE_TIME */
423  }
424 }
425 /*---------------------------------------------------------------------------*/
426 void
427 telnetd_init(void)
428 {
429  process_start(&telnetd_process, NULL);
430 }
431 /*---------------------------------------------------------------------------*/
#define uip_acked()
Has previously sent data been acknowledged?
Definition: uip.h:750
Representation of a uIP TCP connection.
Definition: uip.h:1353
void shell_prompt(char *str)
Print a prompt.
Definition: serial-shell.c:82
static void newdata(void)
Definition: resolv.c:787
process_event_t tcpip_event
The uIP event.
Definition: tcpip.c:80
#define UIP_HTONS(n)
Convert 16-bit quantity from host byte order to network byte order.
Definition: uip.h:1239
Default definitions of C compiler quirk work-arounds.
Main header file for the Contiki shell
void timer_set(struct timer *t, clock_time_t interval)
Set a timer.
Definition: timer.c:64
PETSCII/ASCII conversion functions.
#define uip_closed()
Has the connection been closed by the other end?
Definition: uip.h:772
#define PROCESS_END()
Define the end of a process.
Definition: process.h:131
#define PROCESS(name, strname)
Declare a process.
Definition: process.h:307
void tcp_listen(uint16_t port)
Open a TCP port.
Definition: tcpip.c:261
#define PROCESS_THREAD(name, ev, data)
Define the body of a process.
Definition: process.h:273
A timer.
Definition: timer.h:86
uip_appdata
Pointer to the application data in the packet buffer.
Definition: tcp_loader.c:74
void shell_init(void)
Initialize the shell.
Definition: shell.c:501
void process_exit(struct process *p)
Cause a process to exit.
Definition: process.c:202
void shell_quit(void)
Quit the shell.
Definition: shell.c:576
void shell_default_output(const char *text1, int len1, const char *text2, int len2)
Print a line of output from the shell.
Definition: serial-shell.c:58
void shell_stop(void)
Stop the shell.
Definition: shell.c:570
#define uip_aborted()
Has the connection been aborted by the other end?
Definition: uip.h:782
#define LOADER_UNLOAD()
Unload a program from memory.
Definition: loader.h:104
#define uip_datalen()
The length of any incoming data that is currently available (if available) in the uip_appdata buffer...
Definition: uip.h:652
#define NULL
The null pointer.
void shell_input(char *commandline, int commandline_len)
Send a line of input to the shell.
Definition: shell.c:359
CCIF void uip_send(const void *data, int len)
Send data on the current connection.
Definition: uip.c:1960
#define uip_mss()
Get the current maximum segment size that can be sent on the current connection.
Definition: uip.h:839
#define uip_connected()
Has the connection just been connected?
Definition: uip.h:762
void process_start(struct process *p, process_data_t data)
Start a process.
Definition: process.c:99
void shell_exit(void)
Request shell exit.
Definition: serial-shell.c:89
#define uip_rexmit()
Do we need to retransmit previously data?
Definition: uip.h:804
#define uip_close()
Close the current connection.
Definition: uip.h:672
#define uip_poll()
Is the connection being polled by uIP?
Definition: uip.h:818
#define uip_timedout()
Has the connection timed out?
Definition: uip.h:792
#define PROCESS_WAIT_EVENT()
Wait for an event to be posted to the process.
Definition: process.h:141
#define uip_newdata()
Is new incoming data available?
Definition: uip.h:739
void shell_start(void)
Start the shell.
Definition: shell.c:562
int timer_expired(struct timer *t)
Check if a timer has expired.
Definition: timer.c:122
#define PROCESS_BEGIN()
Define the beginning of a process.
Definition: process.h:120