turn off debug_printf
[clinton/Smoothieware.git] / src / libs / Network / uip / telnetd / telnetd.cpp
CommitLineData
d4ee6ee2
JM
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 uIP TCP/IP stack
30 *
31 * $Id: telnetd.c,v 1.2 2006/06/07 09:43:54 adam Exp $
32 *
33 */
34
35#include "uip.h"
36#include "telnetd.h"
37#include "shell.h"
38
39#include <string.h>
40#include <stdlib.h>
41#include <stdio.h>
42
43#define ISO_nl 0x0a
44#define ISO_cr 0x0d
45
46#define STATE_NORMAL 0
47#define STATE_IAC 1
48#define STATE_WILL 2
49#define STATE_WONT 3
50#define STATE_DO 4
51#define STATE_DONT 5
52#define STATE_CLOSE 6
53
54#define TELNET_IAC 255
55#define TELNET_WILL 251
56#define TELNET_WONT 252
57#define TELNET_DO 253
58#define TELNET_DONT 254
59
60#define TELNET_LINEMODE 0x22
61#define TELNET_GA 0x03
62#define TELNET_X_PROMPT 0x55
63
75eae806
JM
64#define DEBUG_PRINTF(...)
65//#define DEBUG_PRINTF printf
d4ee6ee2
JM
66
67static char *alloc_line(int size)
68{
69 return (char *)malloc(size);
70}
71
72static void dealloc_line(char *line)
73{
74 free(line);
75}
76
77void Telnetd::close()
78{
79 state = STATE_CLOSE;
80}
81
82int Telnetd::sendline(char *line)
83{
84 int i;
85 for (i = 0; i < TELNETD_CONF_NUMLINES; ++i) {
86 if (lines[i] == NULL) {
87 lines[i] = line;
88 return i;
89 }
90 }
91 if (i == TELNETD_CONF_NUMLINES) {
92 dealloc_line(line);
93 }
94 return TELNETD_CONF_NUMLINES;
95}
96
97void Telnetd::output_prompt(const char *str)
98{
99 if(prompt) output(str);
100}
101
102int Telnetd::output(const char *str)
103{
104 if(state == STATE_CLOSE) return -1;
105
106 unsigned chunk = 256; // small chunk size so we don't allocate huge blocks, and must be less than mss
107 unsigned len = strlen(str);
108 char *line;
109 if (len < chunk) {
110 // can be sent in one tcp buffer
111 line = alloc_line(len + 1);
112 if (line != NULL) {
113 strcpy(line, str);
114 return sendline(line);
115 }else{
116 // out of memory treat like full
117 return TELNETD_CONF_NUMLINES;
118 }
119 } else {
120 // need to split line over multiple send lines
121 int size = chunk; // size to copy
122 int off = 0;
123 int n= 0;
124 while (len >= chunk) {
125 line = alloc_line(chunk + 1);
126 if (line != NULL) {
127 memcpy(line, str + off, size);
128 line[size] = 0;
129 n= sendline(line);
130 len -= size;
131 off += size;
132 }else{
133 // out of memory treat like full
134 return TELNETD_CONF_NUMLINES;
135 }
136 }
137 if (len > 0) {
138 // send rest
139 line = alloc_line(len + 1);
140 if (line != NULL) {
141 strcpy(line, str + off);
142 n= sendline(line);
143 }else{
144 // out of memory treat like full
145 return TELNETD_CONF_NUMLINES;
146 }
147 }
148 return n;
149 }
150}
151
152// check if we can queue or if queue is full
153int Telnetd::can_output()
154{
155 if(state == STATE_CLOSE) return -1;
156
157 int i;
158 int cnt = 0;
159 for (i = 0; i < TELNETD_CONF_NUMLINES; ++i) {
160 if (lines[i] == NULL) cnt++;
161 }
162 return cnt < 4 ? 0 : 1;
163}
164
165void Telnetd::acked(void)
166{
167 while (numsent > 0) {
168 dealloc_line(lines[0]);
169 for (int i = 1; i < TELNETD_CONF_NUMLINES; ++i) {
170 lines[i - 1] = lines[i];
171 }
172 lines[TELNETD_CONF_NUMLINES - 1] = NULL;
173 --numsent;
174 }
175}
176
177void Telnetd::senddata(void)
178{
179 // NOTE this sends as many lines as it can fit in one tcp frame
180 // we need to keep the lines under the size of the tcp frame
181 char *bufptr, *lineptr;
182 int buflen, linelen;
183
184 bufptr = (char *)uip_appdata;
185 buflen = 0;
186 for (numsent = 0; numsent < TELNETD_CONF_NUMLINES && lines[numsent] != NULL ; ++numsent) {
187 lineptr = lines[numsent];
188 linelen = strlen(lineptr);
189 if (buflen + linelen < uip_mss()) {
190 memcpy(bufptr, lineptr, linelen);
191 bufptr += linelen;
192 buflen += linelen;
193 } else {
194 break;
195 }
196 }
197 uip_send(uip_appdata, buflen);
198}
199
200void Telnetd::get_char(u8_t c)
201{
202 if (c == ISO_cr) {
203 return;
204 }
205
206 buf[(int)bufptr] = c;
207 if (buf[(int)bufptr] == ISO_nl || bufptr == sizeof(buf) - 1) {
208 if (bufptr > 0) {
209 buf[(int)bufptr] = 0;
210 }
211 shell->input(buf);
212 bufptr = 0;
213
214 } else {
215 ++bufptr;
216 }
217}
218
219// static void sendopt(u8_t option, u8_t value)
220// {
221// char *line;
222// line = alloc_line(4);
223// if (line != NULL) {
224// line[0] = TELNET_IAC;
225// line[1] = option;
226// line[2] = value;
227// line[3] = 0;
228// sendline(line);
229// }
230// }
231
232void Telnetd::newdata(void)
233{
234 u16_t len;
235 u8_t c;
236 char *dataptr;
237
238 len = uip_datalen();
239 dataptr = (char *)uip_appdata;
240
241 while (len > 0 && bufptr < sizeof(buf)) {
242 c = *dataptr;
243 ++dataptr;
244 --len;
245 switch (state) {
246 case STATE_IAC:
247 if (c == TELNET_IAC) {
248 get_char(c);
249 state = STATE_NORMAL;
250 } else {
251 switch (c) {
252 case TELNET_WILL:
253 state = STATE_WILL;
254 break;
255 case TELNET_WONT:
256 state = STATE_WONT;
257 break;
258 case TELNET_DO:
259 state = STATE_DO;
260 break;
261 case TELNET_DONT:
262 state = STATE_DONT;
263 break;
264 default:
265 state = STATE_NORMAL;
266 break;
267 }
268 }
269 break;
270 case STATE_WILL:
271 if (c == TELNET_LINEMODE) {
272 //sendopt(TELNET_DO, c);
273 }
274 state = STATE_NORMAL;
275 break;
276
277 case STATE_WONT:
278 /* Reply with a DONT */
279 //sendopt(TELNET_DONT, c);
280 state = STATE_NORMAL;
281 break;
282 case STATE_DO:
283 if (c == TELNET_X_PROMPT) {
284 prompt= true;
285 }else if (c == TELNET_GA) {
286 // enable prompt if telnet client running
287 prompt= true;
d4ee6ee2
JM
288 }else{
289 /* Reply with a WONT */
290 //sendopt(TELNET_WONT, c);
291 }
292 state = STATE_NORMAL;
293 break;
294 case STATE_DONT:
295 if (c == TELNET_X_PROMPT) {
296 prompt= false;
297 }else{
298 /* Reply with a WONT */
299 //sendopt(TELNET_WONT, c);
300 }
301 state = STATE_NORMAL;
302 break;
303 case STATE_NORMAL:
304 if (c == TELNET_IAC) {
305 state = STATE_IAC;
306 } else {
307 get_char(c);
308 }
309 break;
310 }
311 }
312
313 // if the command queue is getting too big we stop TCP
314 if(shell->queue_size() > 20) {
315 DEBUG_PRINTF("Telnet: stopped: %d\n", shell->queue_size());
316 uip_stop();
317 }
318}
319
320void Telnetd::poll()
321{
322 if(first_time) {
323 first_time= false;
324 shell->start();
325 senddata();
326 }
327}
328
329Telnetd::Telnetd()
330{
331 DEBUG_PRINTF("Telnetd: ctor %p\n", this);
332 for (int i = 0; i < TELNETD_CONF_NUMLINES; ++i) {
333 lines[i] = NULL;
334 }
335
336 first_time= true;
337 bufptr = 0;
338 state = STATE_NORMAL;
339 prompt= false;
340 shell= new Shell(this);
341}
342
343Telnetd::~Telnetd()
344{
345 DEBUG_PRINTF("Telnetd: dtor %p\n", this);
346 for (int i = 0; i < TELNETD_CONF_NUMLINES; ++i) {
347 if (lines[i] != NULL) dealloc_line(lines[i]);
348 }
349 delete shell;
350}
351
352// static
353void Telnetd::appcall(void)
354{
355 Telnetd *instance= reinterpret_cast<Telnetd *>(uip_conn->appstate);
356
357 if (uip_connected()) {
358 // create a new telnet class instance
359 instance= new Telnetd;
360 DEBUG_PRINTF("Telnetd new instance: %p\n", instance);
361 uip_conn->appstate= instance; // and store it in the appstate of the connection
362 instance->rport= uip_conn->rport;
363 }
364
365 if (uip_closed() || uip_aborted() || uip_timedout()) {
366 DEBUG_PRINTF("Telnetd: closed: %p\n", instance);
367 if(instance != NULL) {
368 delete instance;
369 uip_conn->appstate= NULL;
370 }
371 return;
372 }
373
374 // sanity check
375 if(instance == NULL || instance->rport != uip_conn->rport) {
376 DEBUG_PRINTF("Telnetd: ERROR Null instance or rport is wrong: %p - %u, %d\n", instance, HTONS(uip_conn->rport), uip_flags);
377 uip_abort();
378 return;
379 }
380
381 if (instance->state == STATE_CLOSE) {
382 uip_close();
383 }
384
385
386 if (uip_acked()) {
387 instance->acked();
388 }
389
390 if (uip_newdata()) {
391 instance->newdata();
392 }
393
394 if (uip_rexmit() || uip_newdata() || uip_acked() || uip_connected() || uip_poll()) {
395 instance->senddata();
396 }
397
398 if(uip_poll() && uip_stopped(uip_conn) && instance->shell->queue_size() < 5) {
399 DEBUG_PRINTF("restarted %d - %p\n", instance->shell->queue_size(), instance);
400 uip_restart();
401 }
402
403 if(uip_poll()) {
404 instance->poll();
405 }
406}
407
408// static
409void Telnetd::init(void)
410{
411 uip_listen(HTONS(23));
412}