Telnet.c

Telnet.c example details

This program installs a Telnet-server on TCP port 23 to perform ASCII up-
and downloads to and from configuration file and application file in Intel
hex format.
See also
tcp_get_state(), direntry_type, net_send_ready
/***************************************************************************//**
@file Telnet.c
@brief Test Program for the telnet protocol
@verbatim
_ _ _
__| | ___(_) ____ _ __ | |_
/ _` | / __| |/ _` | '_ \| __|
| (_| | _ \__ \ | (_| | | | | |_
\__,_|(_) ___/_|\__, |_| |_|\__|
Signalprocessing |___/ Technology
@endverbatim
@author D.SignT GmbH & Co. KG, Claus Hermbusche
@date 2019-06-03
@anchor TELNETEX
@details
This program installs a Telnet-server on TCP port 23 to perform ASCII up-
and downloads to and from configuration file and application file in Intel
hex format.
@cond Software License Agreement
Copyright (C) 2001-2019 D.SignT GmbH & Co. KG - http://www.dsignt.de
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
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.
Neither the name of D.SignT GmbH & Co. KG nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
Disclaimer
THIS SOFTWARE IS PROVIDED BY D.SIGNT GMBH & CO. KG "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 D.SIGNT GMBH & CO. KG 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.
@endcond
*******************************************************************************/
/*******************************************************************************
include stdtypes.h to avoid data type mismatch
*******************************************************************************/
/*******************************************************************************
include Runtime Source
*******************************************************************************/
#include <stdlib.h>
#include <stdio.h>
#include <stddef.h>
#include <inttypes.h>
#include <time.h>
#include <string.h>
#include <ctype.h>
/*******************************************************************************
network support functions
*******************************************************************************/
#include <Libs/NETlib/net.h> /* D.Module network support */
#include <Libs/NETlib/ftplib.h> /* FTP support */
/*******************************************************************************
board specific functions
*******************************************************************************/
#include <BoardSupport/inc/BoardSpecific.h> /* board support functions */
/*******************************************************************************
common support functions
*******************************************************************************/
#include <Common/Common.h> /* on exit function */
#include <Common/uartio.h> /* UART support */
#include <Common/timer.h> /* timer setting */
#include <Common/CPrintf.h> /* CPrintf defines */
/*******************************************************************************
network configuration
*******************************************************************************/
#include <BoardSupport/config/netconfig.c> /* network configuration */
#include "telnetio.h" /* telnet support */
/*******************************************************************************
File System content
*******************************************************************************/
#include "efs.def"
#ifdef __cplusplus
extern "C" {
#endif /* !__cplusplus */
/*******************************************************************************
local prototypes
*******************************************************************************/
// char *create_hello_msg(void);
#pragma CODE_SECTION(PrintResult , ".commontext")
void PrintResult ( telnet_return_type *telnet_ret);
#pragma CODE_SECTION (transmit_file , ".nettextslow");
int transmit_file ( SOCKET *server, direntry_type *dir, unsigned int rw,unsigned int flags );
#pragma CODE_SECTION (user_menu , ".nettextslow");
int16_t user_menu ( T_ComDevice *CmdDevice, char c );
#ifdef __cplusplus
} // extern "C"
#endif
/*******************************************************************************
* *
* DEFINES *
* *
*******************************************************************************/
/*******************************************************************************
defines for user menu
*******************************************************************************/
#define TMENU1 0
#define TMENU2 1
/*******************************************************************************
* *
* GLOBALS *
* *
*******************************************************************************/
/*******************************************************************************
program name
*******************************************************************************/
char *program_name = "telnet";
timeval telnet_stamp1; /* used to determine session time */
#define EMPTY_LINE ""
/*******************************************************************************
define some strings for telnet operation
the position within this table must match the reason in telnet return value
*******************************************************************************/
const char * aLogMessages[] = {
" File upload... ",
" Boot command... ",
" New telnet connection established from ",
" Telnet file upload... ",
" Telnet connection closed\r\n",
" File download... ",
" success\r\n",
" error\r\n",
};
/*******************************************************************************
* *
* FUNCTIONS *
* *
*******************************************************************************/
/*******************************************************************************
@brief print FTP or Telnet server result
@param -
@return nothing
*******************************************************************************/
void PrintResult ( telnet_return_type *telnet_ret)
{
/***************************************************************************
locals
***************************************************************************/
char buffer[25]; /* small buffer for conversion */
direntry_type *fp; /* EFS file pointer */
struct tm *local_time; /* structure to hold time and date */
uint32_t further_details = 0; /* if set true, further details are printed*/
timeval stamp2, delta; /* used to determine session time */
/***************************************************************************
print time stamp
***************************************************************************/
local_time = localtime ((const time_t *)&RecentEpoch);
sprintf ( buffer, "%s", asctime (local_time));
buffer[24] = 0; /* eliminate new line */
CPrintf ( "\r\n ---------- UTC time: "VT100_BLUE"%s"VT100_DEFAULT" ----------\r\n", buffer);
/***************************************************************************
print result string
***************************************************************************/
CPrintf ( aLogMessages[telnet_ret-> reason]);
/***************************************************************************
parse result to switch input/output control
***************************************************************************/
switch ( telnet_ret-> reason )
{
case TELNET_NEW_CON : /* a new telnet connection was established */
CPrintf ( " %s\r\n", inet_ntoa(telnet_ret-> offset, buffer));
CPuts ( " input control switched to telnet session\r\n");
telnet_stamp1 = GetTimeStamp(); /* remember time stamp */
CPrintf_select_output (CPRINTF_TELNET_OUTPUT); /* telnet outputs */
/*******************************************************************
print available commands to telnet terminal
*******************************************************************/
CPrintf (commands);
break;
case TELNET_CLOSE_CON : /* telnet connection was closed */
sprintf ( buffer, "%s", asctime (local_time));
buffer[24] = 0; /* eliminate new line */
CPrintf ( "\r\n ---------- UTC time: "VT100_BLUE"%s"VT100_DEFAULT" ----------\r\n", buffer);
/*******************************************************************
calculate session time
*******************************************************************/
stamp2 = GetTimeStamp();
tv_interval (&delta, &telnet_stamp1, &stamp2);
local_time = localtime ((const time_t *)&delta);
CPuts (" session duration [hh:mm:ss]: ");
" %02d:%02d:%02d\r\n"
VT100_DEFAULT,
local_time->tm_hour, local_time->tm_min, local_time->tm_sec );
CPuts ( " input control switched back to serial terminal\r\n");
break;
default:
further_details = 1; /* print further details on last operation */
break;
}
if (!further_details) return; /* no further details needed */
/***************************************************************************
print further details on event
***************************************************************************/
switch ( telnet_ret-> error_code )
{
/*******************************************************************
success
*******************************************************************/
fp = telnet_ret-> fp;
if (telnet_ret-> offset==0)
{
if (!strncmp(fp->dir,"0:",2))
{
CPuts (" RAM-disc:\r\n");
}
if (!strncmp(fp->dir,"1:",2))
{
CPuts (" NAND-disc:\r\n");
}
#ifdef USE_SPIDRIVE
if (!strncmp(fp->dir,"2:",2))
{
CPuts (" SD-card:\r\n");
}
#endif
}
else
{
CPrintf (" EFS offset: 0x%08"PRIx32"\r\n", telnet_ret-> offset);
}
CPrintf (" file name: %s\r\n", fp-> name);
CPrintf (" file size: %"PRId32"\r\n", telnet_ret-> fp-> size);
break;
default:
/*******************************************************************
print error
*******************************************************************/
net_print_error (telnet_ret-> error_code, CPrintf);
break;
}
/***************************************************************************
print available commands
***************************************************************************/
CPrintf (commands);
}
/*******************************************************************************
@brief Transmit a user specified file from or to FLASH or RAM
@param server - connected TCP server socket
@param dir - pointer to file specified above
@param rw - read/write flag (FTP_READ, FTP_WRITE)
@param flags - flags for controlling action:
@sa FSYS_SEND_ALL - generate blocking call
@sa FSYS_ERASE_SECTOR - pre-erase FLASH-sectors
@sa FSYS_WRITE_BOOT_SECTOR - write boot sector
@return TRUE - Success
@return FASLE - Error occurred
@sa fsys_get_file_size() - netlib
@sa fsys_init_flash() - netlib
@sa fsys_send_all() - netlib
@sa net_send_string() - netlib
*******************************************************************************/
int transmit_file ( SOCKET *server,
unsigned int rw,
unsigned int flags )
{
/***************************************************************************
locals
***************************************************************************/
char *transmit_buffer;
/***************************************************************************
fsys_send_all() must be called with a valid read or write flag
***************************************************************************/
if (rw & (FTP_WRITE | FTP_READ) == 0)
{
net_send_string (server, "Error: read/write flag not specified\r\n");
return (0);
}
/***************************************************************************
malloc some space for transmission
note: the used data buffer for file transmission must be dynamic; it is
freed automatically after file transmission
***************************************************************************/
transmit_buffer = (char *) malloc (TCP_MAX_PACKET_SIZE * sizeof (char));
if ( transmit_buffer == NULL ) /* out of memory */
{
net_send_string (server, out_of_memory_msg);
return (0);
}
/***************************************************************************
initialize transmission
***************************************************************************/
dir-> data_con = server; /* actual server connection */
dir-> buffer = transmit_buffer; /* data buffer */
dir-> pos = 0L; /* offset */
dir-> rw = rw; /* transmit direction */
dir-> size = fsys_get_file_size (dir); /* get actual file-size */
/***************************************************************************
in case of write prepare FLASH sectors table
***************************************************************************/
if ( rw == FTP_WRITE )
{
/***********************************************************************
initialize FLASH sectors; used for automatic sector erase
***********************************************************************/
}
/***************************************************************************
transmit data
flags:
FSYS_SEND_ALL - process blocking call
FSYS_ERASE_SECTOR - erase FLASH-sector before data transmission
FSYS_WRITE_BOOT_SECTOR - write boot sector
***************************************************************************/
fsys_send_all (dir, 0L, flags | FSYS_SEND_ALL);
/***************************************************************************
previously allocated transmit_buffer is freed automatically in
fsys_send_all(), if all data was successfully transmitted. Since we passed
FSYS_SEND_ALL, fsys_send_all() is a blocking call until all data is trans-
mitted. Thus we don't need to check the return value.
***************************************************************************/
/***************************************************************************
check success
***************************************************************************/
if ( dir-> error < SO_NO_ERROR )
{
switch (dir-> error)
{
net_send_string (server, "BIOS protect error\r\n");
break;
net_send_string (server, "intel hex checksum error\r\n");
break;
net_send_string (server, "intel hex error\r\n");
break;
net_send_string (server, "sector erase error\r\n");
break;
net_send_string (server, "FLASH program error\r\n");
break;
net_send_string (server, "buffer underrun error\r\n");
break;
net_send_string (server, "out of memory error\r\n");
break;
net_send_string (server, "out of range error\r\n");
break;
default:
net_send_string (server, "Unknown error!\r\n");
}
}
else
{
net_send_string (server, success_msg);
}
return (0);
}
/*******************************************************************************
@brief process a user menu
@param tcp_server - established connection
@param c - command to process
@return always TRUE
@sa net_send_string() - netlib
@sa tolower() - rts
@sa net_isq() - netlib
@sa transmit_file() - this file
*******************************************************************************/
int16_t user_menu ( T_ComDevice *CmdDevice, char c )
{
/***************************************************************************
locals
***************************************************************************/
static unsigned int rw = 0; /* read/write flag */
static unsigned int menu_level = TMENU1; /* menu level */
direntry_type *transfer = NULL;
/* some locals for a quicker access to command device members */
SOCKET *tcp_server = ((telnet_server_type*)(CmdDevice->gp))->server;
telnet_return_type *telnet_ret = &((telnet_server_type*)(CmdDevice->gp))->telnet_ret;
/***************************************************************************
process a simple two-level single-character menu
***************************************************************************/
switch ( menu_level )
{
case TMENU1: /* first menu level */
/*******************************************************************
parse command
*******************************************************************/
switch ( tolower(c) )
{
/***************************************************************
upload command
***************************************************************/
case 'u':
/***********************************************************
set direction
***********************************************************/
rw = FTP_WRITE;
telnet_ret->reason = TELNET_UPLOAD;
/***********************************************************
switch to next menu
***********************************************************/
menu_level = TMENU2;
/***********************************************************
print message
***********************************************************/
net_send_string (tcp_server, upload_msg);
break;
/***************************************************************
download command
***************************************************************/
case 'd':
/***********************************************************
set direction
***********************************************************/
rw = FTP_READ;
telnet_ret->reason = TELNET_DOWNLOAD;
/***********************************************************
print message
***********************************************************/
net_send_string (tcp_server, download_msg);
/***********************************************************
switch to next menu
***********************************************************/
menu_level = TMENU2;
break;
/***************************************************************
unknown command; print command list
***************************************************************/
default :
rw = 0;
net_send_string (tcp_server, commands);
break;
} // switch
break;
case TMENU2: /* second menu level */
/*******************************************************************
parse command
*******************************************************************/
switch ( tolower(c) )
{
/***************************************************************
intel-hex file
***************************************************************/
case 'i' :
if ( rw & FTP_WRITE )
{
net_send_string (tcp_server, receive_hex_msg);
}
else
{
net_send_string (tcp_server, send_hex_msg);
}
/***********************************************************
start transmission
***********************************************************/
transfer = &application;
/***********************************************************
back to root menu
***********************************************************/
menu_level = TMENU1;
break;
#ifdef HAS_SETUP_FILE
/***************************************************************
setup file
***************************************************************/
case 's' :
if ( rw & FTP_WRITE )
{
net_send_string (tcp_server, receive_hex_msg);
}
else
{
net_send_string (tcp_server, send_hex_msg);
}
/***********************************************************
start transmission
***********************************************************/
transfer = &setup;
/***********************************************************
back to root menu
***********************************************************/
menu_level = TMENU1;
break;
#endif
/***************************************************************
config file
***************************************************************/
case 'c' :
if ( rw & FTP_WRITE )
{
net_send_string (tcp_server, receive_config_msg);
}
else
{
net_send_string (tcp_server, send_config_msg);
}
/***********************************************************
start transmission
***********************************************************/
transfer = &config;
/***********************************************************
back to root menu
***********************************************************/
menu_level = TMENU1;
break;
/***************************************************************
Escape
***************************************************************/
case 0x1b :
/***********************************************************
print available commands
***********************************************************/
net_send_string (tcp_server, commands);
/***********************************************************
back to root menu
***********************************************************/
menu_level = TMENU1;
rw = 0;
break;
/***************************************************************
wrong command print message
***************************************************************/
default :
net_send_string (tcp_server, file_msg);
break;
}
break;
default :
break;
} // switch
if ( transfer )
{
/***********************************************************************
transmit file
***********************************************************************/
transmit_file ( tcp_server, transfer, rw, 0 );
/***********************************************************************
pick up transmission result for later processing
***********************************************************************/
telnet_ret->error_code = transfer->error;
telnet_ret->fp = transfer;
telnet_ret->offset = transfer->offset;
/***********************************************************************
return true to calling command processor to signal a valid result
***********************************************************************/
return (1);
} // if
/***************************************************************************
This function is called as callback from the cmd module.
***************************************************************************/
return (0);
}
/*******************************************************************************
@brief Main application
@param -
@return never
*******************************************************************************/
#pragma CODE_SECTION(main , ".commontext");
int main ( void )
{
/***************************************************************************
locals
***************************************************************************/
int main_loop = 1; /* main loop switch, set to 0 to exit */
timeval stamp1, stamp2, delta; /* used to determine startup time */
telnet_return_type *telnet_ret; /* telnet server result */
/***************************************************************************
initialize application (e.g. timer clocks, PLL settings, EMIF etc.)
(ref. \Common\Common.c)
***************************************************************************/
/***************************************************************************
select output device for CPrintf (ref. \Common\cprintf.c)
possible settings:
CPRINTF_UART_OUTPUT -> output to UART
CPRINTF_CCS_OUTPUT -> output to CCS
CPRINTF_UART_OUTPUT | CPRINTF_CCS_OUTPUT -> output to UART and CCS
***************************************************************************/
/***************************************************************************
print a start up message
***************************************************************************/
/**************************************************************************/
// CPrintfProgress (" Heap check ");
// at least 0x2000 bytes required for this app
/**************************************************************************/
// CPrintfProgressSuccess();
/**************************************************************************/
CPrintfProgress (" Setup system time ");
/**************************************************************************/
CPrintf (" *** timer %d mapped to CPU int %d ***\r\n",
/**************************************************************************/
CPrintfProgress (" Enable interrupts ");
/**************************************************************************/
/**************************************************************************/
CPrintfProgress (" Start system timer ");
/**************************************************************************/
CPrintf (" *** timer %d running at %"PRId32" Hz ***\r\n", SystemTimerDev, RES_SECONDS/GetSystemTimerRes());
/***************************************************************************
measure network initialization time
***************************************************************************/
stamp1 = GetTimeStamp();
/**************************************************************************/
CPrintfProgress (" Initialize network ");
/**************************************************************************/
InitializeNetwork ( 64); // 64 bytes for ping
stamp2 = GetTimeStamp();
tv_interval (&delta, &stamp1, &stamp2);
CPuts (" network startup time [sec]: ");
"%"PRId32".%03"PRId32"\r\n"
delta.tv_sec,
delta.tv_usec/1000);
/***************************************************************************
initialize telnet server
the automatic command processing of the cmd module is deactivated here
(parameter NULL for command table) since we use a simple two-level one
character command menu here
***************************************************************************/
if ( Telnet_initialize_server ( NULL, /* no user list */
create_hello_msg(), /* welcome message */
user_menu, /* input parser */
NULL /* no command table */
) == FALSE )
{
prg_exit ("Telnet_initialize_server() failed");
}
CPrintf (" *** Telnet port 23 open ***\r\n");
/**************************************************************************/
CPrintfProgress (" Initialize files ");
/**************************************************************************/
#ifdef HAS_SETUP_FILE
setup.buffer =
#endif
config.buffer = application.buffer = NULL;
#ifdef HAS_SETUP_FILE
fsys_init_file ( &setup, 0, 0);
#endif
fsys_init_file ( &config, 0, 0);
fsys_init_file ( &application, 0, 0);
/***************************************************************************
main program loop: set main_loop to 0 to exit loop
***************************************************************************/
CPuts ("\r\n Entering main loop, waiting for telnet connection ...\r\n");
while ( main_loop )
{
/***********************************************************************
process net_isq()
***********************************************************************/
net_isq (); // process ISQ
/***********************************************************************
monitor link status
***********************************************************************/
/***********************************************************************
try to detect IP assignment
if DHCP is used, the assigned IP address may change
***********************************************************************/
/***********************************************************************
process Telnet_server()
***********************************************************************/
telnet_ret = Telnet_server ();
if ((uint32_t)telnet_ret > FTP_NO_ERROR)
{
/*******************************************************************
something happened on the Telnet server. Print details
*******************************************************************/
PrintResult (telnet_ret);
}
/***********************************************************************
show that the program is running, perform symbol animation
***********************************************************************/
}
/***************************************************************************
exit program, shut down peripherals
***************************************************************************/
return (0);
}