Fork of Smoothie to port to mbed non-LPC targets.
Fork of Smoothie by
Diff: libs/Network/uip/telnetd/telnetd.cpp
- Revision:
- 2:1df0b61d3b5a
diff -r ab59fc9af055 -r 1df0b61d3b5a libs/Network/uip/telnetd/telnetd.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/Network/uip/telnetd/telnetd.cpp Fri Feb 28 18:52:52 2014 -0800 @@ -0,0 +1,413 @@ +/* + * Copyright (c) 2003, Adam Dunkels. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * This file is part of the uIP TCP/IP stack + * + * $Id: telnetd.c,v 1.2 2006/06/07 09:43:54 adam Exp $ + * + */ + +#include "uip.h" +#include "telnetd.h" +#include "shell.h" + +#include <string.h> +#include <stdlib.h> +#include <stdio.h> + +#define ISO_nl 0x0a +#define ISO_cr 0x0d + +#define STATE_NORMAL 0 +#define STATE_IAC 1 +#define STATE_WILL 2 +#define STATE_WONT 3 +#define STATE_DO 4 +#define STATE_DONT 5 +#define STATE_CLOSE 6 + +#define TELNET_IAC 255 +#define TELNET_WILL 251 +#define TELNET_WONT 252 +#define TELNET_DO 253 +#define TELNET_DONT 254 + +#define TELNET_LINEMODE 0x22 +#define TELNET_GA 0x03 +#define TELNET_X_PROMPT 0x55 + +//#define DEBUG_PRINTF(...) +#define DEBUG_PRINTF printf + +static char *alloc_line(int size) +{ + return (char *)malloc(size); +} + +static void dealloc_line(char *line) +{ + free(line); +} + +void Telnetd::close() +{ + state = STATE_CLOSE; +} + +int Telnetd::sendline(char *line) +{ + int i; + for (i = 0; i < TELNETD_CONF_NUMLINES; ++i) { + if (lines[i] == NULL) { + lines[i] = line; + return i; + } + } + if (i == TELNETD_CONF_NUMLINES) { + dealloc_line(line); + } + return TELNETD_CONF_NUMLINES; +} + +void Telnetd::output_prompt(const char *str) +{ + if(prompt) output(str); +} + +int Telnetd::output(const char *str) +{ + if(state == STATE_CLOSE) return -1; + + unsigned chunk = 256; // small chunk size so we don't allocate huge blocks, and must be less than mss + unsigned len = strlen(str); + char *line; + if (len < chunk) { + // can be sent in one tcp buffer + line = alloc_line(len + 1); + if (line != NULL) { + strcpy(line, str); + return sendline(line); + }else{ + // out of memory treat like full + return TELNETD_CONF_NUMLINES; + } + } else { + // need to split line over multiple send lines + int size = chunk; // size to copy + int off = 0; + int n= 0; + while (len >= chunk) { + line = alloc_line(chunk + 1); + if (line != NULL) { + memcpy(line, str + off, size); + line[size] = 0; + n= sendline(line); + len -= size; + off += size; + }else{ + // out of memory treat like full + return TELNETD_CONF_NUMLINES; + } + } + if (len > 0) { + // send rest + line = alloc_line(len + 1); + if (line != NULL) { + strcpy(line, str + off); + n= sendline(line); + }else{ + // out of memory treat like full + return TELNETD_CONF_NUMLINES; + } + } + return n; + } +} + +// check if we can queue or if queue is full +int Telnetd::can_output() +{ + if(state == STATE_CLOSE) return -1; + + int i; + int cnt = 0; + for (i = 0; i < TELNETD_CONF_NUMLINES; ++i) { + if (lines[i] == NULL) cnt++; + } + return cnt < 4 ? 0 : 1; +} + +void Telnetd::acked(void) +{ + while (numsent > 0) { + dealloc_line(lines[0]); + for (int i = 1; i < TELNETD_CONF_NUMLINES; ++i) { + lines[i - 1] = lines[i]; + } + lines[TELNETD_CONF_NUMLINES - 1] = NULL; + --numsent; + } +} + +void Telnetd::senddata(void) +{ + // NOTE this sends as many lines as it can fit in one tcp frame + // we need to keep the lines under the size of the tcp frame + char *bufptr, *lineptr; + int buflen, linelen; + + bufptr = (char *)uip_appdata; + buflen = 0; + for (numsent = 0; numsent < TELNETD_CONF_NUMLINES && lines[numsent] != NULL ; ++numsent) { + lineptr = lines[numsent]; + linelen = strlen(lineptr); + if (buflen + linelen < uip_mss()) { + memcpy(bufptr, lineptr, linelen); + bufptr += linelen; + buflen += linelen; + } else { + break; + } + } + uip_send(uip_appdata, buflen); +} + +void Telnetd::get_char(u8_t c) +{ + if (c == ISO_cr) { + return; + } + + buf[(int)bufptr] = c; + if (buf[(int)bufptr] == ISO_nl || bufptr == sizeof(buf) - 1) { + if (bufptr > 0) { + buf[(int)bufptr] = 0; + } + shell->input(buf); + bufptr = 0; + + } else { + ++bufptr; + } +} + +// static void sendopt(u8_t option, u8_t value) +// { +// char *line; +// line = alloc_line(4); +// if (line != NULL) { +// line[0] = TELNET_IAC; +// line[1] = option; +// line[2] = value; +// line[3] = 0; +// sendline(line); +// } +// } + +void Telnetd::newdata(void) +{ + u16_t len; + u8_t c; + char *dataptr; + + len = uip_datalen(); + dataptr = (char *)uip_appdata; + + while (len > 0 && bufptr < sizeof(buf)) { + c = *dataptr; + ++dataptr; + --len; + switch (state) { + case STATE_IAC: + if (c == TELNET_IAC) { + get_char(c); + state = STATE_NORMAL; + } else { + switch (c) { + case TELNET_WILL: + state = STATE_WILL; + break; + case TELNET_WONT: + state = STATE_WONT; + break; + case TELNET_DO: + state = STATE_DO; + break; + case TELNET_DONT: + state = STATE_DONT; + break; + default: + state = STATE_NORMAL; + break; + } + } + break; + case STATE_WILL: + if (c == TELNET_LINEMODE) { + //sendopt(TELNET_DO, c); + } + state = STATE_NORMAL; + break; + + case STATE_WONT: + /* Reply with a DONT */ + //sendopt(TELNET_DONT, c); + state = STATE_NORMAL; + break; + case STATE_DO: + if (c == TELNET_X_PROMPT) { + prompt= true; + }else if (c == TELNET_GA) { + // enable prompt if telnet client running + prompt= true; + shell->setConsole(); // tell shell we are a console, as this is sent be telnet clients + }else{ + /* Reply with a WONT */ + //sendopt(TELNET_WONT, c); + } + state = STATE_NORMAL; + break; + case STATE_DONT: + if (c == TELNET_X_PROMPT) { + prompt= false; + }else{ + /* Reply with a WONT */ + //sendopt(TELNET_WONT, c); + } + state = STATE_NORMAL; + break; + case STATE_NORMAL: + if (c == TELNET_IAC) { + state = STATE_IAC; + } else { + get_char(c); + } + break; + } + } + + // if the command queue is getting too big we stop TCP + if(shell->queue_size() > 20) { + DEBUG_PRINTF("Telnet: stopped: %d\n", shell->queue_size()); + uip_stop(); + } +} + +void Telnetd::poll() +{ + if(first_time) { + first_time= false; + shell->start(); + senddata(); + } +} + +Telnetd::Telnetd() +{ + DEBUG_PRINTF("Telnetd: ctor %p\n", this); + for (int i = 0; i < TELNETD_CONF_NUMLINES; ++i) { + lines[i] = NULL; + } + + first_time= true; + bufptr = 0; + state = STATE_NORMAL; + prompt= false; + shell= new Shell(this); +} + +Telnetd::~Telnetd() +{ + DEBUG_PRINTF("Telnetd: dtor %p\n", this); + for (int i = 0; i < TELNETD_CONF_NUMLINES; ++i) { + if (lines[i] != NULL) dealloc_line(lines[i]); + } + delete shell; +} + +// static +void Telnetd::appcall(void) +{ + Telnetd *instance= reinterpret_cast<Telnetd *>(uip_conn->appstate); + + if (uip_connected()) { + // create a new telnet class instance + instance= new Telnetd; + DEBUG_PRINTF("Telnetd new instance: %p\n", instance); + uip_conn->appstate= instance; // and store it in the appstate of the connection + instance->rport= uip_conn->rport; + } + + if (uip_closed() || uip_aborted() || uip_timedout()) { + DEBUG_PRINTF("Telnetd: closed: %p\n", instance); + if(instance != NULL) { + delete instance; + uip_conn->appstate= NULL; + } + return; + } + + // sanity check + if(instance == NULL || instance->rport != uip_conn->rport) { + DEBUG_PRINTF("Telnetd: ERROR Null instance or rport is wrong: %p - %u, %d\n", instance, HTONS(uip_conn->rport), uip_flags); + uip_abort(); + return; + } + + if (instance->state == STATE_CLOSE) { + uip_close(); + } + + + if (uip_acked()) { + instance->acked(); + } + + if (uip_newdata()) { + instance->newdata(); + } + + if (uip_rexmit() || uip_newdata() || uip_acked() || uip_connected() || uip_poll()) { + instance->senddata(); + } + + if(uip_poll() && uip_stopped(uip_conn) && instance->shell->queue_size() < 5) { + DEBUG_PRINTF("restarted %d - %p\n", instance->shell->queue_size(), instance); + uip_restart(); + } + + if(uip_poll()) { + instance->poll(); + } +} + +// static +void Telnetd::init(void) +{ + uip_listen(HTONS(23)); +}