[En-Nut-Discussion] Telnet server with echo and cursor key support
Tomohiro HARAIKAWA
hal at cs.inf.shizuoka.ac.jp
Sun Jul 6 02:04:32 CEST 2003
Hi, all,
As you know, a telnet client (more accurately, a VT100 emulator like
\windows\system32\telnet.exe or Tera Term Pro) and a server negotiates
via an NVT protocol. A server should support NVT ECHO for the correct
handling of echo-back. Otherwise, a character is echoed back twice if
a client chooses a local echo.
The following code fragment is to support NVT ECHO and automatically
determines whether the server should echo or not.
The code can be reused freely. I'm very sorry about my puzzly coded
state machine. I never supposed the code to be shown to others. :-)
---
Tomohiro Haraikawa
Faculty of Information, Shizuoka University
E-mail: hal at cs.inf.shizuoka.ac.jp
#define NVT_SE 240
#define NVT_SB 250
#define NVT_WILL 251
#define NVT_WONT 252
#define NVT_DO 253
#define NVT_DONT 254
#define NVT_IAC 255
#define NVT_OPT_ECHO 1
#define NVT_OPT_SGA 3
static int nvt_session(TCPSOCKET *sock, u_char req, u_char opt, u_char accept)
{
u_char nvt[3] = { NVT_IAC, 0 };
switch (req) {
case NVT_WILL:
nvt[1] = accept ? NVT_DO : NVT_DONT;
break;
case NVT_WONT:
nvt[1] = NVT_DONT;
break;
case NVT_DO:
nvt[1] = accept ? NVT_WILL : NVT_WONT;
break;
case NVT_DONT:
nvt[1] = NVT_WONT;
}
if (!nvt[1]) return -1;
nvt[2] = opt;
return NutTcpSend(sock, nvt, sizeof(nvt));
}
static void telnet_session(TCPSOCKET *sock)
{
u_char buff[128], ch = '\0', echo = 0, nvt = 0, nvt_sub = NVT_SE;
const u_char lf = '\n';
int eol, len;
do {
len = 0;
do {
if ((eol = NutTcpReceive(sock, buff + len, 1)) >= 0) {
if (nvt == NVT_IAC) {
nvt = 0;
switch (buff[len]) {
case NVT_IAC:
break;
case NVT_SB:
case NVT_SE:
nvt_sub = buff[len];
continue;
case NVT_WILL:
case NVT_WONT:
case NVT_DO:
case NVT_DONT:
nvt = buff[len];
default:
continue;
}
} else if (nvt_sub == NVT_SB) {
nvt = (buff[len] == NVT_IAC) ? NVT_IAC : 0;
continue;
} else if (NVT_WILL <= nvt && nvt <= NVT_DONT) {
switch (buff[len]) {
case NVT_OPT_ECHO:
echo = (nvt == NVT_DO || nvt == NVT_WONT);
nvt_session(sock, nvt, buff[len], 1);
break;
case NVT_OPT_SGA:
if (nvt == NVT_WILL || nvt == NVT_DO) {
nvt_session(sock, nvt, buff[len], 1);
} else {
nvt_session(sock, nvt, buff[len], 0);
}
}
nvt = 0;
continue;
} else if (buff[len] == NVT_IAC) {
nvt = NVT_IAC;
continue;
}
if (echo) {
if (buff[len] != '\n') NutTcpSend(sock, buff + len, 1);
if (buff[len] == '\r') NutTcpSend(sock, &lf, 1);
}
switch (buff[len]) {
case 0x08:
if (len >= 1) len--;
case '\0':
continue;
case '\n':
if (ch == '\r') {
ch = buff[len];
continue;
}
/* fall through */
case '\r':
eol = 0;
/* fall through */
default:
ch = buff[len];
}
}
if (++len >= (int)sizeof(buff) - 1) break;
} while (eol > 0);
buff[len] = '\0';
/*
* now buff contains a command line */
*/
} while (eol >= 0);
}
More information about the En-Nut-Discussion
mailing list