summaryrefslogtreecommitdiff
path: root/nuttx/examples/nsh/nsh_telnetd.c
diff options
context:
space:
mode:
authorpatacongo <patacongo@42af7a65-404d-4744-a932-0658087f49c3>2007-12-02 18:18:59 +0000
committerpatacongo <patacongo@42af7a65-404d-4744-a932-0658087f49c3>2007-12-02 18:18:59 +0000
commit3fd3ce2c6946e6162a2895f7e031231f64ebf6f3 (patch)
treebef21208f8dd5756a663dc937dde70fd43280be4 /nuttx/examples/nsh/nsh_telnetd.c
parenteb4d3300acdff3332ca4ad06ff1a5ae3bbc1449d (diff)
downloadnuttx-3fd3ce2c6946e6162a2895f7e031231f64ebf6f3.tar.gz
nuttx-3fd3ce2c6946e6162a2895f7e031231f64ebf6f3.tar.bz2
nuttx-3fd3ce2c6946e6162a2895f7e031231f64ebf6f3.zip
Add TELNET front end to NSH
git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@421 42af7a65-404d-4744-a932-0658087f49c3
Diffstat (limited to 'nuttx/examples/nsh/nsh_telnetd.c')
-rw-r--r--nuttx/examples/nsh/nsh_telnetd.c565
1 files changed, 565 insertions, 0 deletions
diff --git a/nuttx/examples/nsh/nsh_telnetd.c b/nuttx/examples/nsh/nsh_telnetd.c
new file mode 100644
index 000000000..f90f535ba
--- /dev/null
+++ b/nuttx/examples/nsh/nsh_telnetd.c
@@ -0,0 +1,565 @@
+/****************************************************************************
+ * examples/nsh/nsh_telnetd.c
+ *
+ * Copyright (C) 2007 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <spudmonkey@racsa.co.cr>
+ *
+ * This is a leverage of similar logic from uIP:
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ * 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. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``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 INSTITUTE OR CONTRIBUTORS 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.
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <pthread.h>
+#include <debug.h>
+
+#include <net/uip/uip-lib.h>
+
+#include "nsh.h"
+
+/****************************************************************************
+ * Definitions
+ ****************************************************************************/
+
+#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
+
+/* Configurable settings */
+
+#ifndef CONFIG_EXAMPLES_NSH_IOBUFFER_SIZE
+# define CONFIG_EXAMPLES_NSH_IOBUFFER_SIZE 512
+#endif
+
+#ifndef CONFIG_EXAMPLES_NSH_CMD_SIZE
+# define CONFIG_EXAMPLES_NSH_CMD_SIZE 40
+#endif
+
+/* As threads are created to handle each request, a stack must be allocated
+ * for the thread. Use a default if the user provided no stacksize.
+ */
+
+#ifndef CONFIG_EXAMPLES_NSH_STACKSIZE
+# define CONFIG_EXAMPLES_NSH_STACKSIZE 4096
+#endif
+
+/* Enabled dumping of all input/output buffers */
+
+#undef CONFIG_EXAMPLES_NSH_TELNETD_DUMPBUFFER
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct telnetd_s
+{
+ int tn_sockfd;
+ char tn_iobuffer[CONFIG_EXAMPLES_NSH_IOBUFFER_SIZE];
+ char tn_cmd[CONFIG_EXAMPLES_NSH_CMD_SIZE];
+ uint8 tn_bufndx;
+ uint8 tn_state;
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nsh_dumpbuffer
+ *
+ * Description:
+ * Dump a buffer of data (debug only)
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_EXAMPLES_NSH_TELNETD_DUMPBUFFER
+static void nsh_dumpbuffer(const char *msg, const char *buffer, ssize_t nbytes)
+{
+#ifdef CONFIG_DEBUG
+ char line[128];
+ int ch;
+ int i;
+ int j;
+
+ dbg("%s:\n", msg);
+ for (i = 0; i < nbytes; i += 16)
+ {
+ sprintf(line, "%04x: ", i);
+
+ for ( j = 0; j < 16; j++)
+ {
+ if (i + j < nbytes)
+ {
+ sprintf(&line[strlen(line)], "%02x ", buffer[i+j] );
+ }
+ else
+ {
+ strcpy(&line[strlen(line)], " ");
+ }
+ }
+
+ for ( j = 0; j < 16; j++)
+ {
+ if (i + j < nbytes)
+ {
+ ch = buffer[i+j];
+ sprintf(&line[strlen(line)], "%c", ch >= 0x20 && ch <= 0x7e ? ch : '.');
+ }
+ }
+ dbg("%s\n", line);
+ }
+#endif
+}
+#else
+# define nsh_dumpbuffer(msg,buffer,nbytes)
+#endif
+
+/****************************************************************************
+ * Name: nsh_putchar
+ *
+ * Description:
+ * Add another parsed character to the TELNET command string
+ *
+ ****************************************************************************/
+
+static void nsh_putchar(struct telnetd_s *pstate, uint8 ch)
+{
+ /* Ignore carriage returns */
+
+ if (ch == ISO_cr)
+ {
+ return;
+ }
+
+ /* Add all other characters to the cmd buffer */
+
+ pstate->tn_cmd[pstate->tn_bufndx] = ch;
+
+ /* If a newline was added or if the buffer is full, then process it now */
+
+ if (ch == ISO_nl || pstate->tn_bufndx == (CONFIG_EXAMPLES_NSH_CMD_SIZE - 1))
+ {
+ if (pstate->tn_bufndx > 0)
+ {
+ pstate->tn_cmd[pstate->tn_bufndx] = '\0';
+ }
+
+ nsh_dumpbuffer("TELNET CMD", pstate->tn_cmd, strlen(pstate->tn_cmd));
+ nsh_parse((void*)pstate, pstate->tn_cmd);
+ pstate->tn_bufndx = 0;
+ }
+ else
+ {
+ pstate->tn_bufndx++;
+ vdbg("Add '%c', bufndx=%d\n", ch, pstate->tn_bufndx);
+ }
+}
+
+/****************************************************************************
+ * Name: nsh_sendopt
+ *
+ * Description:
+ *
+ ****************************************************************************/
+
+static void nsh_sendopt(struct telnetd_s *pstate, uint8 option, uint8 value)
+{
+ uint8 optbuf[4];
+ optbuf[0] = TELNET_IAC;
+ optbuf[1] = option;
+ optbuf[2] = value;
+ optbuf[3] = 0;
+
+ nsh_dumpbuffer("Send optbuf", optbuf, 4);
+ if (send(pstate->tn_sockfd, optbuf, 4, 0) < 0)
+ {
+ dbg("[%d] Failed to send TELNET_IAC\n", pstate->tn_sockfd);
+ }
+}
+
+/****************************************************************************
+ * Name: nsh_receive
+ *
+ * Description:
+ * Process a received TELENET buffer
+ *
+ ****************************************************************************/
+
+static int nsh_receive(struct telnetd_s *pstate, size_t len)
+{
+ char *ptr = pstate->tn_iobuffer;
+ uint8 ch;
+
+ while (len > 0)
+ {
+ ch = *ptr++;
+ len--;
+
+ vdbg("ch=%02x state=%d\n", ch, pstate->tn_state);
+ switch (pstate->tn_state)
+ {
+ case STATE_IAC:
+ if (ch == TELNET_IAC)
+ {
+ nsh_putchar(pstate, ch);
+ pstate->tn_state = STATE_NORMAL;
+ }
+ else
+ {
+ switch (ch)
+ {
+ case TELNET_WILL:
+ pstate->tn_state = STATE_WILL;
+ break;
+
+ case TELNET_WONT:
+ pstate->tn_state = STATE_WONT;
+ break;
+
+ case TELNET_DO:
+ pstate->tn_state = STATE_DO;
+ break;
+
+ case TELNET_DONT:
+ pstate->tn_state = STATE_DONT;
+ break;
+
+ default:
+ pstate->tn_state = STATE_NORMAL;
+ break;
+ }
+ }
+ break;
+
+ case STATE_WILL:
+ /* Reply with a DONT */
+
+ nsh_sendopt(pstate, TELNET_DONT, ch);
+ pstate->tn_state = STATE_NORMAL;
+ break;
+
+ case STATE_WONT:
+ /* Reply with a DONT */
+
+ nsh_sendopt(pstate, TELNET_DONT, ch);
+ pstate->tn_state = STATE_NORMAL;
+ break;
+
+ case STATE_DO:
+ /* Reply with a WONT */
+
+ nsh_sendopt(pstate, TELNET_WONT, ch);
+ pstate->tn_state = STATE_NORMAL;
+ break;
+
+ case STATE_DONT:
+ /* Reply with a WONT */
+
+ nsh_sendopt(pstate, TELNET_WONT, ch);
+ pstate->tn_state = STATE_NORMAL;
+ break;
+
+ case STATE_NORMAL:
+ if (ch == TELNET_IAC)
+ {
+ pstate->tn_state = STATE_IAC;
+ }
+ else
+ {
+ nsh_putchar(pstate, ch);
+ }
+ break;
+ }
+ }
+ return OK;
+}
+
+/****************************************************************************
+ * Name: nsh_prompt
+ *
+ * Description:
+ * Print a prompt to the shell window.
+ *
+ * This function can be used by the shell back-end to print out a prompt
+ * to the shell window.
+ *
+ ****************************************************************************/
+
+static void nsh_prompt(struct telnetd_s *pstate, const char *str)
+{
+ int len = strlen(str);
+
+ strncpy(pstate->tn_iobuffer, str, len);
+ nsh_dumpbuffer("Shell prompt", pstate->tn_iobuffer, len);
+ if (send(pstate->tn_sockfd, pstate->tn_iobuffer, len, 0) < 0)
+ {
+ dbg("[%d] Failed to send prompt\n", pstate->tn_sockfd);
+ }
+}
+
+/****************************************************************************
+ * Name: nsh_connection
+ *
+ * Description:
+ * Each time a new connection to port 23 is made, a new thread is created
+ * that begins at this entry point. There should be exactly one argument
+ * and it should be the socket descriptor (+1).
+ *
+ ****************************************************************************/
+
+static void *nsh_connection(void *arg)
+{
+ struct telnetd_s *pstate = (struct telnetd_s *)malloc(sizeof(struct telnetd_s));
+ int sockfd = (int)arg;
+ int ret = ERROR;
+
+ dbg("[%d] Started\n", sockfd);
+
+ /* Verify that the state structure was successfully allocated */
+
+ if (pstate)
+ {
+ /* Initialize the thread state structure */
+
+ memset(pstate, 0, sizeof(struct telnetd_s));
+ pstate->tn_sockfd = sockfd;
+ pstate->tn_state = STATE_NORMAL;
+
+ /* Loop processing each TELNET command */
+
+ do
+ {
+ /* Display the prompt string */
+
+ nsh_prompt(pstate, g_nshprompt);
+
+ /* Read a buffer of data from the TELNET client */
+
+ ret = recv(pstate->tn_sockfd, pstate->tn_iobuffer, CONFIG_EXAMPLES_NSH_IOBUFFER_SIZE, 0);
+ if (ret > 0)
+ {
+
+ /* Process the received TELNET data */
+
+ nsh_dumpbuffer("Received buffer", pstate->tn_iobuffer, ret);
+ ret = nsh_receive(pstate, ret);
+ }
+ }
+ while (ret >= 0 && pstate->tn_state != STATE_CLOSE);
+ dbg("[%d] ret=%d tn_state=%d\n", sockfd, ret, pstate->tn_state);
+
+ /* End of command processing -- Clean up and exit */
+
+ free(pstate);
+ }
+
+ /* Exit the task */
+
+ dbg("[%d] Exitting\n", sockfd);
+ close(sockfd);
+ pthread_exit(NULL);
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: nsh_telnetmain
+ *
+ * Description:
+ * This is the main processing thread for telnetd. It never returns
+ * unless an error occurs
+ *
+ ****************************************************************************/
+
+int nsh_telnetmain(void)
+{
+ struct in_addr addr;
+#if defined(CONFIG_EXAMPLES_NSH_DHCPC) || defined(CONFIG_EXAMPLES_NSH_NOMAC)
+ uint8 mac[IFHWADDRLEN];
+#endif
+
+/* Many embedded network interfaces must have a software assigned MAC */
+
+#ifdef CONFIG_EXAMPLES_NSH_NOMAC
+ mac[0] = 0x00;
+ mac[1] = 0xe0;
+ mac[2] = 0xb0;
+ mac[3] = 0x0b;
+ mac[4] = 0xba;
+ mac[5] = 0xbe;
+ uip_setmacaddr("eth0", mac);
+#endif
+
+ /* Set up our host address */
+
+#if !defined(CONFIG_EXAMPLES_NSH_DHCPC)
+ addr.s_addr = HTONL(CONFIG_EXAMPLES_NSH_IPADDR);
+#else
+ addr.s_addr = 0;
+#endif
+ uip_sethostaddr("eth0", &addr);
+
+ /* Set up the default router address */
+
+ addr.s_addr = HTONL(CONFIG_EXAMPLES_NSH_DRIPADDR);
+ uip_setdraddr("eth0", &addr);
+
+ /* Setup the subnet mask */
+
+ addr.s_addr = HTONL(CONFIG_EXAMPLES_NSH_NETMASK);
+ uip_setnetmask("eth0", &addr);
+
+#if defined(CONFIG_EXAMPLES_NSH_DHCPC)
+ /* Set up the resolver */
+
+ resolv_init();
+#endif
+
+#if defined(CONFIG_EXAMPLES_NSH_DHCPC)
+ /* Get the MAC address of the NIC */
+
+ uip_getmacaddr("eth0", mac);
+
+ /* Set up the DHCPC modules */
+
+ handle = dhcpc_open(&mac, IFHWADDRLEN);
+
+ /* Get an IP address */
+
+ if (handle)
+ {
+ struct dhcpc_state ds;
+ (void)dhcpc_request(handle, &ds);
+ uip_sethostaddr("eth1", &ds.ipaddr);
+ if (ds.netmask.s_addr != 0)
+ {
+ uip_setnetmask("eth0", &ds.netmask);
+ }
+ if (ds.default_router.s_addr != 0)
+ {
+ uip_setdraddr("eth0", &ds.default_router);
+ }
+ if (ds.dnsaddr.s_addr != 0)
+ {
+ resolv_conf(&ds.dnsaddr);
+ }
+ dhcpc_close(handle);
+ }
+#endif
+
+ /* Execute nsh_connection on each connection to port 23 */
+
+ uip_server(HTONS(23), nsh_connection, CONFIG_EXAMPLES_NSH_STACKSIZE);
+ return OK;
+}
+
+/****************************************************************************
+ * Name: nsh_telnetout
+ *
+ * Description:
+ * Print a string to the remote shell window.
+ *
+ * This function is implemented by the shell GUI / telnet server and
+ * can be called by the shell back-end to output a string in the
+ * shell window. The string is automatically appended with a linebreak.
+ *
+ ****************************************************************************/
+
+int nsh_telnetout(FAR void *handle, const char *fmt, ...)
+{
+ struct telnetd_s *pstate = (struct telnetd_s *)handle;
+ unsigned len;
+ va_list ap;
+
+ va_start(ap, fmt);
+ vsnprintf(pstate->tn_iobuffer, CONFIG_EXAMPLES_NSH_IOBUFFER_SIZE, fmt, ap);
+ va_end(ap);
+
+ len = strlen(pstate->tn_iobuffer);
+ if (len < (CONFIG_EXAMPLES_NSH_IOBUFFER_SIZE - 1) && pstate->tn_iobuffer[len-1] == '\n')
+ {
+ pstate->tn_iobuffer[len-1] = ISO_cr;
+ pstate->tn_iobuffer[len] = ISO_nl;
+ pstate->tn_iobuffer[len+1] = '\0';
+ len++;
+ }
+
+ nsh_dumpbuffer("Shell output", pstate->tn_iobuffer, len);
+ if (send(pstate->tn_sockfd, pstate->tn_iobuffer, len, 0) < 0)
+ {
+ dbg("[%d] Failed to send response\n", pstate->tn_sockfd);
+ return ERROR;
+ }
+
+ return len;
+}
+
+/****************************************************************************
+ * Name: cmd_exit
+ *
+ * Description:
+ * Quit the shell instance
+ *
+ ****************************************************************************/
+
+void cmd_exit(void *handle, int argc, char **argv)
+{
+ struct telnetd_s *pstate = (struct telnetd_s *)handle;
+ pstate->tn_state = STATE_CLOSE;
+}
+