From ff1de6a212305bb1bc98e52f24f75970269de4cb Mon Sep 17 00:00:00 2001 From: Emanuele Aliberti Date: Sat, 27 Jan 2001 22:38:43 +0000 Subject: [PATCH] Simple W32 telnet client. Version resource added to ping and roshttpd. svn path=/trunk/; revision=1575 --- reactos/apps/utils/net/ping/makefile | 2 +- reactos/apps/utils/net/ping/ping.c | 3 +- reactos/apps/utils/net/ping/ping.rc | 39 ++ .../utils/net/roshttpd/common/roshttpd.rc | 39 ++ reactos/apps/utils/net/roshttpd/makefile | 2 +- reactos/apps/utils/net/telnet/Makefile | 79 ++++ reactos/apps/utils/net/telnet/ansi.cpp | 307 +++++++++++++ reactos/apps/utils/net/telnet/console.cpp | 43 ++ reactos/apps/utils/net/telnet/console.h | 12 + reactos/apps/utils/net/telnet/helpsock.cpp | 40 ++ reactos/apps/utils/net/telnet/main.cpp | 180 ++++++++ reactos/apps/utils/net/telnet/nvt.cpp | 296 ++++++++++++ reactos/apps/utils/net/telnet/telnet.cpp | 130 ++++++ reactos/apps/utils/net/telnet/telnet.h | 24 + reactos/apps/utils/net/telnet/telnet.rc | 39 ++ reactos/apps/utils/net/telnet/vm.cpp | 424 ++++++++++++++++++ 16 files changed, 1656 insertions(+), 3 deletions(-) create mode 100644 reactos/apps/utils/net/ping/ping.rc create mode 100644 reactos/apps/utils/net/roshttpd/common/roshttpd.rc create mode 100644 reactos/apps/utils/net/telnet/Makefile create mode 100644 reactos/apps/utils/net/telnet/ansi.cpp create mode 100644 reactos/apps/utils/net/telnet/console.cpp create mode 100644 reactos/apps/utils/net/telnet/console.h create mode 100644 reactos/apps/utils/net/telnet/helpsock.cpp create mode 100644 reactos/apps/utils/net/telnet/main.cpp create mode 100644 reactos/apps/utils/net/telnet/nvt.cpp create mode 100644 reactos/apps/utils/net/telnet/telnet.cpp create mode 100644 reactos/apps/utils/net/telnet/telnet.h create mode 100644 reactos/apps/utils/net/telnet/telnet.rc create mode 100644 reactos/apps/utils/net/telnet/vm.cpp diff --git a/reactos/apps/utils/net/ping/makefile b/reactos/apps/utils/net/ping/makefile index c1bdf2c61f1..26a11a9421d 100644 --- a/reactos/apps/utils/net/ping/makefile +++ b/reactos/apps/utils/net/ping/makefile @@ -6,7 +6,7 @@ PATH_TO_TOP = ../../.. TARGETNAME=ping CFLAGS = -I../../../include -OBJECTS = $(TARGETNAME).o +OBJECTS = $(TARGETNAME).o $(TARGETNAME).coff PROGS = $(TARGETNAME).exe LIBS = ../../../lib/ntdll/ntdll.a \ ../../../lib/ws2_32/ws2_32.a diff --git a/reactos/apps/utils/net/ping/ping.c b/reactos/apps/utils/net/ping/ping.c index b9d28ca6bb1..285c03726ad 100644 --- a/reactos/apps/utils/net/ping/ping.c +++ b/reactos/apps/utils/net/ping/ping.c @@ -1,4 +1,5 @@ -/* +/* $Id: ping.c,v 1.3 2001/01/27 22:38:42 ea Exp $ + * * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS ping utility * FILE: apps/net/ping/ping.c diff --git a/reactos/apps/utils/net/ping/ping.rc b/reactos/apps/utils/net/ping/ping.rc new file mode 100644 index 00000000000..0a4e5885ac8 --- /dev/null +++ b/reactos/apps/utils/net/ping/ping.rc @@ -0,0 +1,39 @@ +#include +#include + +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +VS_VERSION_INFO VERSIONINFO + FILEVERSION RES_UINT_FV_MAJOR,RES_UINT_FV_MINOR,RES_UINT_FV_REVISION,RES_UINT_FV_BUILD + PRODUCTVERSION RES_UINT_PV_MAJOR,RES_UINT_PV_MINOR,RES_UINT_PV_REVISION,RES_UINT_PV_BUILD + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", RES_STR_COMPANY_NAME + VALUE "FileDescription", "ReactOS TCP/IPv4 Win32 Ping\0" + VALUE "FileVersion", RES_STR_FILE_VERSION + VALUE "InternalName", "ping\0" + VALUE "LegalCopyright", RES_STR_LEGAL_COPYRIGHT + VALUE "OriginalCopyright", "Casper S. Hornstrup (chorns@users.sourceforge.net)\0" + VALUE "OriginalFilename", "ping.exe\0" + VALUE "ProductName", RES_STR_PRODUCT_NAME + VALUE "ProductVersion", RES_STR_PRODUCT_VERSION + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + diff --git a/reactos/apps/utils/net/roshttpd/common/roshttpd.rc b/reactos/apps/utils/net/roshttpd/common/roshttpd.rc new file mode 100644 index 00000000000..6c7270242a1 --- /dev/null +++ b/reactos/apps/utils/net/roshttpd/common/roshttpd.rc @@ -0,0 +1,39 @@ +#include +#include + +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +VS_VERSION_INFO VERSIONINFO + FILEVERSION RES_UINT_FV_MAJOR,RES_UINT_FV_MINOR,RES_UINT_FV_REVISION,RES_UINT_FV_BUILD + PRODUCTVERSION RES_UINT_PV_MAJOR,RES_UINT_PV_MINOR,RES_UINT_PV_REVISION,RES_UINT_PV_BUILD + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", RES_STR_COMPANY_NAME + VALUE "FileDescription", "ReactOS HTTP Win32 Server\0" + VALUE "FileVersion", RES_STR_FILE_VERSION + VALUE "InternalName", "roshttpd\0" + VALUE "LegalCopyright", RES_STR_LEGAL_COPYRIGHT + VALUE "OriginalCopyright", "Casper S. Hornstrup (chorns@users.sourceforge.net)\0" + VALUE "OriginalFilename", "roshttpd.exe\0" + VALUE "ProductName", RES_STR_PRODUCT_NAME + VALUE "ProductVersion", RES_STR_PRODUCT_VERSION + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + diff --git a/reactos/apps/utils/net/roshttpd/makefile b/reactos/apps/utils/net/roshttpd/makefile index 639bcaf7dd9..f30670971b8 100644 --- a/reactos/apps/utils/net/roshttpd/makefile +++ b/reactos/apps/utils/net/roshttpd/makefile @@ -8,7 +8,7 @@ TARGETNAME=roshttpd CFLAGS = -Iinclude -DUNICODE -D_UNICODE -DDBG MAIN_OBJECTS = $(TARGETNAME).o config.o error.o http.o httpd.o -COMMON_OBJECTS = common/list.o common/socket.o common/thread.o +COMMON_OBJECTS = common/list.o common/socket.o common/thread.o common/$(TARGETNAME).coff OBJECTS = $(MAIN_OBJECTS) $(COMMON_OBJECTS) PROGS = $(TARGETNAME).exe diff --git a/reactos/apps/utils/net/telnet/Makefile b/reactos/apps/utils/net/telnet/Makefile new file mode 100644 index 00000000000..95df02cf62a --- /dev/null +++ b/reactos/apps/utils/net/telnet/Makefile @@ -0,0 +1,79 @@ +# $Id: Makefile,v 1.1 2001/01/27 22:38:43 ea Exp $ +# +# ReactOS Network Virtual Terminal (telnet) console client +# +PATH_TO_TOP=../../.. +PATH_TO_LIB=$(PATH_TO_TOP)/lib + +TARGET_NAME=telnet + +OBJECTS=\ + ansi.o \ + console.o \ + helpsock.o \ + main.o \ + nvt.o \ + telnet.o \ + vm.o \ + $(TARGET_NAME).coff + +LIBRARIES=\ + $(PATH_TO_LIB)/crtdll/crtdll.a \ + $(PATH_TO_LIB)/kernel32/kernel32.a \ + $(PATH_TO_LIB)/ws2_32/ws2_32.a + +PROGS=$(TARGET_NAME).exe + +ifeq ($(DOSCLI), yes) +CLEAN_FILES = *.o $(TARGET_NAME).exe $(TARGET_NAME).sym +else +CLEAN_FILES = *.o $(TARGET_NAME).exe $(TARGET_NAME).sym +endif + +all: $(TARGET_NAME).exe + +clean: $(CLEAN_FILES:%=%_clean) + +$(CLEAN_FILES:%=%_clean): %_clean: + - $(RM) $* + +.phony: clean $(CLEAN_FILES:%=%_clean) + +install: $(PROGS:%=$(FLOPPY_DIR)/apps/%) + +$(PROGS:%=$(FLOPPY_DIR)/apps/%): $(FLOPPY_DIR)/apps/%: % +ifeq ($(DOSCLI),yes) + $(CP) $* $(FLOPPY_DIR)\apps\$* +else + $(CP) $* $(FLOPPY_DIR)/apps/$* +endif + +dist: $(PROGS:%=../../$(DIST_DIR)/apps/%) + +$(PROGS:%=../../$(DIST_DIR)/apps/%): ../../$(DIST_DIR)/apps/%: % +ifeq ($(DOSCLI),yes) + $(CP) $* ..\..\$(DIST_DIR)\apps\$* +else + $(CP) $* ../../$(DIST_DIR)/apps/$* +endif + +ansi.o: telnet.h + +helpsock.o: telnet.h + +main.o: telnet.h + +nvt.o: telnet.h + +telnet.o: telnet.h + +telnet.coff: $(PATH_TO_TOP)/include/reactos/resource.h + +vm.o: telnet.h + +$(TARGET_NAME).exe: $(OBJECTS) $(LIBRARIES) + $(CC) $(OBJECTS) $(LIBRARIES) -o $(TARGET_NAME).exe + +include $(PATH_TO_TOP)/rules.mak + +# EOF diff --git a/reactos/apps/utils/net/telnet/ansi.cpp b/reactos/apps/utils/net/telnet/ansi.cpp new file mode 100644 index 00000000000..4130efcac15 --- /dev/null +++ b/reactos/apps/utils/net/telnet/ansi.cpp @@ -0,0 +1,307 @@ +/* $Id: ansi.cpp,v 1.1 2001/01/27 22:38:43 ea Exp $ + * + * FILE : ansi.cpp + * AUTHOR : unknown (sources found on www.telnet.org) + * PROJECT : ReactOS Operating System + * DESCRIPTION: telnet client for the W32 subsystem + * DATE : 2001-01-21 + * REVISIONS + * 2001-02-21 ea Modified to compile under 0.0.16 src tree + */ +#include +#include + +#include "telnet.h" + +// Need to implement a Keymapper. +// here are some example key maps + +// vt100 f1 - \eOP +// vt100 f2 - \eOQ + +// ansi f5 - \e[17~ +// f6 - \e[18~ +// f7 - \e[20~ +// f10- \e[[V + +enum _ansi_state +{ + as_normal, + as_esc, + as_esc1 +}; + +//SetConsoleMode + +/////////////////////////////////////////////////////////////////////////////// +// SET SCREEN ATTRIBUTE +/* +ESC [ Ps..Ps m Ps refers to selective parameter. Multiple parameters are + separated by the semicolon character (073 octal). The param- + eters are executed in order and have the following meaning: + + 0 or none All attributes off + 1 Bold on + 4 Underscore on + 5 Blink on + 7 Reverse video on + + 3x set foreground color to x + nx set background color to x + + Any other parameters are ignored. +*/ +static int sa = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; + +void ansi_set_screen_attribute(char* buffer) +{ + while(*buffer) + { + switch(*buffer++) + { + case '0': //Normal + sa = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; + break; + case '1': //Hign Intensity + sa |= FOREGROUND_INTENSITY; + break; + case '4': //Underscore + break; + case '5': //Blink. + sa |= BACKGROUND_INTENSITY; + break; + case '7': + sa = BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE; + break; + case '8': + sa = 0; + break; + case '3': + sa = sa & (BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_INTENSITY) | + (*buffer & 1)?FOREGROUND_RED:0 | + (*buffer & 2)?FOREGROUND_GREEN:0 | + (*buffer & 4)?FOREGROUND_BLUE:0; + if(*buffer) + buffer++; + break; + case '6': + sa = sa & (FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY) | + (*buffer & 1)?BACKGROUND_RED:0 | + (*buffer & 2)?BACKGROUND_GREEN:0 | + (*buffer & 4)?BACKGROUND_BLUE:0; + if(*buffer) + buffer++; + break; + } + if(*buffer && *buffer == ';') + buffer++; + } + SetConsoleTextAttribute(StandardOutput,sa); +} + +/////////////////////////////////////////////////////////////////////////////// +// ERASE LINE +/* +ESC [ 0K Same *default* +ESC [ 1K Erase from beginning of line to cursor +ESC [ 2K Erase line containing cursor +*/ + +void ansi_erase_line(char* buffer) +{ + int act = 0; + while(*buffer) + { + act = (*buffer++) - '0'; + } + + CONSOLE_SCREEN_BUFFER_INFO csbi; + GetConsoleScreenBufferInfo(StandardOutput,&csbi); + + COORD pos; + DWORD n; + + switch(act) + { + case 0: //erase to end of line + pos.X = csbi.dwCursorPosition.X; + pos.Y = csbi.dwCursorPosition.Y; + n = csbi.dwSize.X - csbi.dwCursorPosition.X; + break; + case 1: //erase from beginning + pos.X = 0; + pos.Y = csbi.dwCursorPosition.Y; + n = csbi.dwCursorPosition.X; + break; + case 2: // erase whole line + pos.X = 0; + pos.Y = csbi.dwCursorPosition.Y; + n = csbi.dwSize.X; + break; + } + + DWORD w; + FillConsoleOutputCharacter(StandardOutput,' ',n,pos,&w); +} + + +/////////////////////////////////////////////////////////////////////////////// +// SET POSITION +// ESC [ Pl;PcH Direct cursor addressing, where Pl is line#, Pc is column# +// default = (1,1) + +void ansi_set_position(char* buffer) +{ + COORD pos = {0,0}; + + // Grab line + while(*buffer && *buffer != ';') + pos.Y = pos.Y*10 + *buffer++ - '0'; + + if(*buffer) + buffer++; + + // Grab y + while(*buffer && *buffer != ';') + pos.X = pos.X*10 + *buffer++ - '0'; + + (pos.X)?pos.X--:0; + (pos.Y)?pos.Y--:0; + + SetConsoleCursorPosition(StandardOutput,pos); + +} + +/////////////////////////////////////////////////////////////////////////////// +// ERASE SCREEN +/* +ESC [ 0J Same *default* +ESC [ 2J Erase entire screen +*/ + +void ansi_erase_screen(char* buffer) +{ + int act = 0; + while(*buffer) + { + act = (*buffer++) - '0'; + } + + CONSOLE_SCREEN_BUFFER_INFO csbi; + GetConsoleScreenBufferInfo(StandardOutput,&csbi); + + COORD pos; + DWORD n; + + switch(act) + { + case 0: + pos.X = csbi.dwCursorPosition.X; + pos.Y = csbi.dwCursorPosition.Y; + n = csbi.dwSize.X*csbi.dwSize.Y; + break; + case 2: + pos.X = 0; + pos.Y = 0; + n = csbi.dwSize.X*csbi.dwSize.Y; + break; + } + + DWORD w; + FillConsoleOutputCharacter(StandardOutput,' ',n,pos,&w); + SetConsoleCursorPosition(StandardOutput,pos); +} + +/////////////////////////////////////////////////////////////////////////////// +// MOVE UP +// ESC [ Pn A Cursor up Pn lines (Pn default=1) + +void ansi_move_up(char* buffer) +{ + int cnt = *buffer?0:1; + while(*buffer) + { + cnt = cnt*10 + (*buffer++) - '0'; + } + + COORD pos; + + CONSOLE_SCREEN_BUFFER_INFO csbi; + GetConsoleScreenBufferInfo(StandardOutput,&csbi); + + pos.X = csbi.dwCursorPosition.X; + pos.Y = ((csbi.dwCursorPosition.Y-cnt)>=0)?(csbi.dwCursorPosition.Y-cnt):0; + + SetConsoleCursorPosition(StandardOutput,pos); +} + +/////////////////////////////////////////////////////////////////////////////// +char codebuf[256]; +unsigned char codeptr; + +#define NUM_CODEC 6 + +typedef void (*LPCODEPROC)(char*); + +struct +{ + unsigned char cmd; + LPCODEPROC proc; +} codec[NUM_CODEC] = { + {'m',ansi_set_screen_attribute}, + {'H',ansi_set_position}, + {'K',ansi_erase_line}, + {'J',ansi_erase_screen}, + {'A',ansi_move_up}, + {0,0} +}; + +void ansi(SOCKET server,unsigned char data) +{ + static _ansi_state state = as_normal; + DWORD z; + switch( state) + { + case as_normal: + switch(data) + { + case 0: //eat null codes. + break; + case 27: //ANSI esc. + state = as_esc; + break; + default: //Send all else to the console. + WriteConsole(StandardOutput,&data,1,&z,NULL); + break; + } + break; + case as_esc: + state = as_esc1; + codeptr=0; + codebuf[codeptr] = 0; + break; + case as_esc1: + if(data > 64) + { + int i = 0; + codebuf[codeptr] = 0; + for(i=0; codec[i].cmd && codec[i].cmd != data; i++); + if(codec[i].proc) + codec[i].proc(codebuf); +#ifdef _DEBUG + else + { + char buf[256]; + wsprintf(buf,"Unknown Ansi code:'%c' (%s)\n",data,codebuf); + OutputDebugString(buf); + } +#endif + state = as_normal; + } + else + codebuf[codeptr++] = data; + break; + } +} + +/* EOF */ diff --git a/reactos/apps/utils/net/telnet/console.cpp b/reactos/apps/utils/net/telnet/console.cpp new file mode 100644 index 00000000000..09dcaec1dd6 --- /dev/null +++ b/reactos/apps/utils/net/telnet/console.cpp @@ -0,0 +1,43 @@ +/* $Id: console.cpp,v 1.1 2001/01/27 22:38:43 ea Exp $ + * + * FILE : console.cpp + * AUTHOR : E.Aliberti + * PROJECT : ReactOS Operating System + * DESCRIPTION: telnet client for the W32 subsystem + * DATE : 2001-01-21 + * REVISIONS + * 2001-02-21 ea added to group console-related methods + */ +#include + +const char * title = "telnet - "; + +// Set console's title +void console_title_connecting ( + char const* pszHostName, + const int nPort + ) +{ + char t[256]; + wsprintf(t,"%sconnecting to %s:%i", title, pszHostName, nPort); + SetConsoleTitle(t); +} + +void console_title_connected ( + char const* pszHostName, + const int nPort + ) +{ + char t[256]; + wsprintf(t,"%sconnected to %s:%i", title, pszHostName, nPort); + SetConsoleTitle(t); +} + +void console_title_not_connected (void) +{ + char t[256]; + wsprintf(t,"%snot connected", title); + SetConsoleTitle(t); +} + +/* EOF */ diff --git a/reactos/apps/utils/net/telnet/console.h b/reactos/apps/utils/net/telnet/console.h new file mode 100644 index 00000000000..0846f381f4b --- /dev/null +++ b/reactos/apps/utils/net/telnet/console.h @@ -0,0 +1,12 @@ +#ifndef _APPS_NET_TELNET_CONSOLE_H +#define _APPS_NET_TELNET_CONSOLE_H +void console_title_connecting ( + char const* pszHostName, + const int nPort + ); +void console_title_connected ( + char const* pszHostName, + const int nPort + ); +void console_title_not_connected (void); +#endif /* _APPS_NET_TELNET_CONSOLE_H */ diff --git a/reactos/apps/utils/net/telnet/helpsock.cpp b/reactos/apps/utils/net/telnet/helpsock.cpp new file mode 100644 index 00000000000..a7a6b657729 --- /dev/null +++ b/reactos/apps/utils/net/telnet/helpsock.cpp @@ -0,0 +1,40 @@ +/* $Id: helpsock.cpp,v 1.1 2001/01/27 22:38:43 ea Exp $ + * + * FILE : helpsock.cpp + * AUTHOR : unknown (sources found on www.telnet.org) + * PROJECT : ReactOS Operating System + * DESCRIPTION: telnet client for the W32 subsystem + * DATE : 2001-01-21 + * REVISIONS + * 2001-02-21 ea Modified to compile under 0.0.16 src tree + */ +#include +#include +#include + +#include "telnet.h" + +char const* sockmsg(int ecode) +{ + switch(ecode) + { +// programming errors +// (should never occour in release code?) + case WSASYSNOTREADY: return "tcp/ip network not ready"; + case WSAEINVAL: return "invalid winsock version"; + case WSAVERNOTSUPPORTED: return "wrong winsock version"; + case WSANOTINITIALISED: return "winsock not initialized"; + case WSAEINTR: "The call was canceled"; + case WSAEINPROGRESS: "A blocking winsock operation is in progress"; + default: return "unknown winsock error"; +// general TCP problems + case WSAENETDOWN: return "winsock has detected that the network subsystem has failed"; +// GetXbyY related errors: + case WSAHOST_NOT_FOUND: return "Authoritative Answer Host not found"; + case WSATRY_AGAIN: return "Non-Authoritative Host not found, or SERVERFAIL"; + case WSANO_RECOVERY: "Nonrecoverable errors: FORMERR, REFUSED, NOTIMP"; + case WSANO_DATA: "Valid name, no data record of requested type"; + } +} + +/* EOF */ diff --git a/reactos/apps/utils/net/telnet/main.cpp b/reactos/apps/utils/net/telnet/main.cpp new file mode 100644 index 00000000000..3b382b3ad9c --- /dev/null +++ b/reactos/apps/utils/net/telnet/main.cpp @@ -0,0 +1,180 @@ +/* $Id: main.cpp,v 1.1 2001/01/27 22:38:43 ea Exp $ + * + * FILE : main.cpp + * AUTHOR : unknown (sources found on www.telnet.org) + * PROJECT : ReactOS Operating System + * DESCRIPTION: telnet client for the W32 subsystem + * DATE : 2001-01-21 + * REVISIONS + * 2001-02-21 ea Modified to compile under 0.0.16 src tree + * 2001-02-27 ea If run with no argument, it asks for a hostname. + */ +/////////////////////////////////////////////////////////////////////////////// +// +// File: +// main.cpp +// +// Purpose: +// This file provdes the main entry point for the project, and all the +// global scope support routines. +// +// Notes: +// This file expects to be linked without the C-Runtime. If compiling, +// please force the entry point symbol to be "main", and do not link in +// the default librarys. +// This means that no c-runtime functions can be used anywhere in the +// project. I expect this will also exclude any MFC based additions. +// +/////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include + +#include "telnet.h" + +/////////////////////////////////////////////////////////////////////////////// + +// +// Our simple replacement for the c-runtime includes getting the StandardInput, +// StandardOutput & StandardError handles, and providing new and delete operators, that work +// with the win32 heap functions. +// + +// +// standard handles needed for CRT emulation +// +HANDLE hHeap; +HANDLE StandardInput; +HANDLE StandardOutput; +HANDLE StandardError; + +// +// new will use the win32 heap functions. +// +void* operator new(unsigned int nSize) +{ + return HeapAlloc(hHeap,0,nSize); +} + +// +// delete operator provides all memory de-allocation. +// HeapFree doesn't accept NULL. +// +void operator delete(void* pMem) +{ + if(pMem) HeapFree(hHeap,0,pMem); +} + + + +void err(char const* s, ...) +{ + char buf [1024]; + DWORD nout; + + wvsprintf (buf, s, (char*)(s + sizeof(int))); + WriteFile (StandardError,"Error: ", 7, & nout, NULL); + WriteFile (StandardError, buf, lstrlen(buf), & nout, NULL); + WriteFile (StandardError, "\r\n\r\n", 4, & nout, NULL); +#ifdef _DEBUG + OutputDebugString(buf); + OutputDebugString("\n"); +#endif + ExitProcess (ERROR_SUCCESS); +} + + +int main(int argc, char * argv[]) +{ + WSADATA wd; + int errn; + char name [256] = {'\0'}; + short port = IPPORT_TELNET; /* default tcp port */ + + /////////////////////////////////////// + // CRT emulation init + // Get the IO handles + StandardInput = GetStdHandle(STD_INPUT_HANDLE); + StandardOutput = GetStdHandle(STD_OUTPUT_HANDLE); + StandardError = GetStdHandle(STD_ERROR_HANDLE); + + // Get the heap + hHeap = GetProcessHeap(); + + // Explicitly force the console into a good mode (actually all we are doing is turning + // mouse input off. + SetConsoleMode ( + StandardInput, + (ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT) + ); + + /////////////////////////////////////// + // Init winsock + + if (errn = WSAStartup (0x0101, & wd)) + { + err(sockmsg(errn)); + } + + /* hostname */ + if (1 < argc) + { + lstrcpy (name, argv [1]); + } + /* + * Default port is IPPORT_TELNET. + * User may hand one. + */ + if (3 == argc) + { + port = atoi (argv[2]); + if (port <= 0) + { + struct servent * service = NULL; + + service = getservbyname (argv[2], "tcp"); + if (NULL == service) + { + err("Invalid service name specified"); + } + port = service->s_port; + } + else + { + err("Invalid port specified"); + } + } + /* Too many arguments */ + if (3 < argc) + { + err("Usage: telnet []"); + } + /* No argument */ + if (1 == argc) + { + DWORD Count; + char *c; + + WriteFile (StandardError,"host: ", 6, & Count, NULL); + ReadFile (StandardInput, name, sizeof name, & Count, NULL); + c = name; + while (*c > ' ') ++ c; + *c = '\0'; + } + + // guess what this does. + telnet (name, port); + + //Bye bye... + WSACleanup (); + + // Exit process terminates any waiting threads. + // (Its the CRT that makes a process close when the main thread exits. + // The WinAPI will leave the process as is for as long as it has a + // thread any thread. + ExitProcess (EXIT_SUCCESS); +} + +/* EOF */ diff --git a/reactos/apps/utils/net/telnet/nvt.cpp b/reactos/apps/utils/net/telnet/nvt.cpp new file mode 100644 index 00000000000..a455a29ae5b --- /dev/null +++ b/reactos/apps/utils/net/telnet/nvt.cpp @@ -0,0 +1,296 @@ +/* $Id: nvt.cpp,v 1.1 2001/01/27 22:38:43 ea Exp $ + * + * FILE : nvt.cpp + * AUTHOR : unknown (sources found on www.telnet.org) + * PROJECT : ReactOS Operating System + * DESCRIPTION: telnet client for the W32 subsystem + * DATE : 2001-01-21 + * REVISIONS + * 2001-02-21 ea Modified to compile under 0.0.16 src tree + */ +/////////////////////////////////////////////////////////////////////////////// +// +// file: nvt.cpp +// +// purpose: Provides the "bare bones" telnet "Network Virtual Terminal" +// that is our default. We only se a more capable terminal, if +// properly requested via the telnet option. +// +// refrence: The following excerpt from rfc 854 +// +/////////////////////////////////////////////////////////////////////////////// +/* +THE NETWORK VIRTUAL TERMINAL + + The Network Virtual Terminal (NVT) is a bi-directional character + device. The NVT has a printer and a keyboard. The printer responds + to incoming data and the keyboard produces outgoing data which is + sent over the TELNET connection and, if "echoes" are desired, to the + NVT's printer as well. "Echoes" will not be expected to traverse the + network (although options exist to enable a "remote" echoing mode of + operation, no host is required to implement this option). The code + set is seven-bit USASCII in an eight-bit field, except as modified + herein. Any code conversion and timing considerations are local + problems and do not affect the NVT. + + TRANSMISSION OF DATA + + Although a TELNET connection through the network is intrinsically + full duplex, the NVT is to be viewed as a half-duplex device + operating in a line-buffered mode. That is, unless and until + options are negotiated to the contrary, the following default + conditions pertain to the transmission of data over the TELNET + connection: + + 1) Insofar as the availability of local buffer space permits, + data should be accumulated in the host where it is generated + until a complete line of data is ready for transmission, or + until some locally-defined explicit signal to transmit occurs. + This signal could be generated either by a process or by a + human user. + + The motivation for this rule is the high cost, to some hosts, + of processing network input interrupts, coupled with the + default NVT specification that "echoes" do not traverse the + network. Thus, it is reasonable to buffer some amount of data + at its source. Many systems take some processing action at the + end of each input line (even line printers or card punches + frequently tend to work this way), so the transmission should + be triggered at the end of a line. On the other hand, a user + or process may sometimes find it necessary or desirable to + provide data which does not terminate at the end of a line; + therefore implementers are cautioned to provide methods of + locally signaling that all buffered data should be transmitted + immediately. + + 2) When a process has completed sending data to an NVT printer + and has no queued input from the NVT keyboard for further + processing (i.e., when a process at one end of a TELNET + connection cannot proceed without input from the other end), + the process must transmit the TELNET Go Ahead (GA) command. + + This rule is not intended to require that the TELNET GA command + be sent from a terminal at the end of each line, since server + hosts do not normally require a special signal (in addition to + end-of-line or other locally-defined characters) in order to + commence processing. Rather, the TELNET GA is designed to help + a user's local host operate a physically half duplex terminal + which has a "lockable" keyboard such as the IBM 2741. A + description of this type of terminal may help to explain the + proper use of the GA command. + + The terminal-computer connection is always under control of + either the user or the computer. Neither can unilaterally + seize control from the other; rather the controlling end must + relinguish its control explicitly. At the terminal end, the + hardware is constructed so as to relinquish control each time + that a "line" is terminated (i.e., when the "New Line" key is + typed by the user). When this occurs, the attached (local) + computer processes the input data, decides if output should be + generated, and if not returns control to the terminal. If + output should be generated, control is retained by the computer + until all output has been transmitted. + + The difficulties of using this type of terminal through the + network should be obvious. The "local" computer is no longer + able to decide whether to retain control after seeing an + end-of-line signal or not; this decision can only be made by + the "remote" computer which is processing the data. Therefore, + the TELNET GA command provides a mechanism whereby the "remote" + (server) computer can signal the "local" (user) computer that + it is time to pass control to the user of the terminal. It + should be transmitted at those times, and only at those times, + when the user should be given control of the terminal. Note + that premature transmission of the GA command may result in the + blocking of output, since the user is likely to assume that the + transmitting system has paused, and therefore he will fail to + turn the line around manually. + + The foregoing, of course, does not apply to the user-to-server + direction of communication. In this direction, GAs may be sent at + any time, but need not ever be sent. Also, if the TELNET + connection is being used for process-to-process communication, GAs + need not be sent in either direction. Finally, for + terminal-to-terminal communication, GAs may be required in + neither, one, or both directions. If a host plans to support + terminal-to-terminal communication it is suggested that the host + provide the user with a means of manually signaling that it is + time for a GA to be sent over the TELNET connection; this, + however, is not a requirement on the implementer of a TELNET + process. + + Note that the symmetry of the TELNET model requires that there is + an NVT at each end of the TELNET connection, at least + conceptually. +*//* + + THE NVT PRINTER AND KEYBOARD + + The NVT printer has an unspecified carriage width and page length + and can produce representations of all 95 USASCII graphics (codes + 32 through 126). Of the 33 USASCII control codes (0 through 31 + and 127), and the 128 uncovered codes (128 through 255), the + following have specified meaning to the NVT printer: + + NAME CODE MEANING + + NULL (NUL) 0 No Operation + Line Feed (LF) 10 Moves the printer to the + next print line, keeping the + same horizontal position. + Carriage Return (CR) 13 Moves the printer to the left + margin of the current line. + + In addition, the following codes shall have defined, but not + required, effects on the NVT printer. Neither end of a TELNET + connection may assume that the other party will take, or will + have taken, any particular action upon receipt or transmission + of these: + + BELL (BEL) 7 Produces an audible or + visible signal (which does + NOT move the print head). + Back Space (BS) 8 Moves the print head one + character position towards + the left margin. + Horizontal Tab (HT) 9 Moves the printer to the + next horizontal tab stop. + It remains unspecified how + either party determines or + establishes where such tab + stops are located. + Vertical Tab (VT) 11 Moves the printer to the + next vertical tab stop. It + remains unspecified how + either party determines or + establishes where such tab + stops are located. + Form Feed (FF) 12 Moves the printer to the top + of the next page, keeping + the same horizontal position. + + All remaining codes do not cause the NVT printer to take any + action. + + The sequence "CR LF", as defined, will cause the NVT to be + positioned at the left margin of the next print line (as would, + for example, the sequence "LF CR"). However, many systems and + terminals do not treat CR and LF independently, and will have to + go to some effort to simulate their effect. (For example, some + terminals do not have a CR independent of the LF, but on such + terminals it may be possible to simulate a CR by backspacing.) + Therefore, the sequence "CR LF" must be treated as a single "new + line" character and used whenever their combined action is + intended; the sequence "CR NUL" must be used where a carriage + return alone is actually desired; and the CR character must be + avoided in other contexts. This rule gives assurance to systems + which must decide whether to perform a "new line" function or a + multiple-backspace that the TELNET stream contains a character + following a CR that will allow a rational decision. + + Note that "CR LF" or "CR NUL" is required in both directions + (in the default ASCII mode), to preserve the symmetry of the + NVT model. Even though it may be known in some situations + (e.g., with remote echo and suppress go ahead options in + effect) that characters are not being sent to an actual + printer, nonetheless, for the sake of consistency, the protocol + requires that a NUL be inserted following a CR not followed by + a LF in the data stream. The converse of this is that a NUL + received in the data stream after a CR (in the absence of + options negotiations which explicitly specify otherwise) should + be stripped out prior to applying the NVT to local character + set mapping. + + The NVT keyboard has keys, or key combinations, or key sequences, + for generating all 128 USASCII codes. Note that although many + have no effect on the NVT printer, the NVT keyboard is capable of + generating them. + + In addition to these codes, the NVT keyboard shall be capable of + generating the following additional codes which, except as noted, + have defined, but not reguired, meanings. The actual code + assignments for these "characters" are in the TELNET Command + section, because they are viewed as being, in some sense, generic + and should be available even when the data stream is interpreted + as being some other character set. + + Synch + + This key allows the user to clear his data path to the other + party. The activation of this key causes a DM (see command + section) to be sent in the data stream and a TCP Urgent + notification is associated with it. The pair DM-Urgent is to + have required meaning as defined previously. + + Break (BRK) + + This code is provided because it is a signal outside the + USASCII set which is currently given local meaning within many + systems. It is intended to indicate that the Break Key or the + Attention Key was hit. Note, however, that this is intended to + provide a 129th code for systems which require it, not as a + synonym for the IP standard representation. + + Interrupt Process (IP) + + Suspend, interrupt, abort or terminate the process to which the + NVT is connected. Also, part of the out-of-band signal for + other protocols which use TELNET. + + Abort Output (AO) + + Allow the current process to (appear to) run to completion, but + do not send its output to the user. Also, send a Synch to the + user. + + Are You There (AYT) + + Send back to the NVT some visible (i.e., printable) evidence + that the AYT was received. + + Erase Character (EC) + + The recipient should delete the last preceding undeleted + character or "print position" from the data stream. + + Erase Line (EL) + + The recipient should delete characters from the data stream + back to, but not including, the last "CR LF" sequence sent over + the TELNET connection. + + The spirit of these "extra" keys, and also the printer format + effectors, is that they should represent a natural extension of + the mapping that already must be done from "NVT" into "local". + Just as the NVT data byte 68 (104 octal) should be mapped into + whatever the local code for "uppercase D" is, so the EC character + should be mapped into whatever the local "Erase Character" + function is. Further, just as the mapping for 124 (174 octal) is + somewhat arbitrary in an environment that has no "vertical bar" + character, the EL character may have a somewhat arbitrary mapping + (or none at all) if there is no local "Erase Line" facility. + Similarly for format effectors: if the terminal actually does + have a "Vertical Tab", then the mapping for VT is obvious, and + only when the terminal does not have a vertical tab should the + +*/ + +#include +#include + +#include "telnet.h" + +void nvt(SOCKET server,unsigned char data) +{ + DWORD z; + switch(data) + { + case 0: //eat null codes. + break; + default: //Send all else to the console. + WriteConsole(StandardOutput, & data, 1, & z, NULL); + break; + } +} + +/* EOF */ diff --git a/reactos/apps/utils/net/telnet/telnet.cpp b/reactos/apps/utils/net/telnet/telnet.cpp new file mode 100644 index 00000000000..2a5368a7206 --- /dev/null +++ b/reactos/apps/utils/net/telnet/telnet.cpp @@ -0,0 +1,130 @@ +/* $Id: telnet.cpp,v 1.1 2001/01/27 22:38:43 ea Exp $ + * + * FILE : telnet.cpp + * AUTHOR : unknown (sources found on www.telnet.org) + * PROJECT : ReactOS Operating System + * DESCRIPTION: telnet client for the W32 subsystem + * DATE : 2001-01-21 + * REVISIONS + * 2001-02-21 ea Modified to compile under 0.0.16 src tree + */ +#include +#include + +#include "telnet.h" +#include "console.h" + + +// +// sock_loop is the thread dedicatd to reading socket input. +// It waits for data from the socket, and then gives it one byte at a time +// to the telnet vm to process. +// + +DWORD sock_loop(SOCKET server) +{ + char buf[256]; + unsigned long read; + char* scan; + + while( (read = recv(server,buf,sizeof(buf),0)) && read != SOCKET_ERROR ) + { + scan = buf; + while(read--) + vm(server,*scan++); + } + int x = WSAGetLastError(); + return 0; +} + +DWORD input_loop(SOCKET server) +{ + char buf[256]; + DWORD read; + + do + { + WaitForSingleObject(StandardInput, INFINITE); + ReadFile(StandardInput, buf, sizeof buf, & read, NULL); + } + while(SOCKET_ERROR != send(server, buf, read, 0)); + + return 0; + +} + +void telnet(SOCKET server) +{ + DWORD dwThreadIdsock; + DWORD dwThreadIdinput; + HANDLE threads[2]; + + + threads[0] = CreateThread( + NULL, /* no security attributes */ + 0, /* use default stack size */ + (LPTHREAD_START_ROUTINE) sock_loop, /* thread function */ + (LPVOID)server, /* argument to thread function */ + 0, /* use default creation flags */ + &dwThreadIdsock); /* returns the thread identifier */ + + //wait for the other thread to complete any setup negotiation... + //Sleep(500); //- this is not the problem - its just bloody stuffing up! + + threads[1] = CreateThread( + NULL, /* no security attributes */ + 0, /* use default stack size */ + (LPTHREAD_START_ROUTINE) input_loop, /* thread function */ + (LPVOID)server, /* argument to thread function */ + 0, /* use default creation flags */ + &dwThreadIdinput); /* returns the thread identifier */ + + + WaitForMultipleObjects(2,threads,FALSE,INFINITE); +} + + +// +// connect to the hostname,port +// +void telnet( + char const* pszHostName, + const short nPort) +{ + unsigned long ip; + if((*pszHostName <= '9') && (*pszHostName >= '0')) + { + if((ip = inet_addr(pszHostName)) == INADDR_NONE) + err("invalid host IP address given"); + } + else + { + hostent* ent = gethostbyname(pszHostName); + if(!ent) + err(sockmsg(WSAGetLastError())); + ip = *(unsigned long*)(ent->h_addr); + } + + sockaddr_in name; + name.sin_family = AF_INET; + name.sin_port = htons(nPort); + name.sin_addr = *(in_addr*)&ip; + + console_title_connecting (pszHostName, nPort); + + SOCKET server; + + if((server = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP))==INVALID_SOCKET) + err(sockmsg(WSAGetLastError())); + + if(SOCKET_ERROR == connect(server,(sockaddr*)&name,sizeof(sockaddr))) + err(sockmsg(WSAGetLastError())); + + console_title_connected (pszHostName, nPort); + + telnet(server); + + closesocket(server); +} + +/* EOF */ diff --git a/reactos/apps/utils/net/telnet/telnet.h b/reactos/apps/utils/net/telnet/telnet.h new file mode 100644 index 00000000000..c84d9c470c1 --- /dev/null +++ b/reactos/apps/utils/net/telnet/telnet.h @@ -0,0 +1,24 @@ +#ifndef _APPS_NET_TELNET_H +#define _APPS_NET_TELNET_H +//Global Handles +extern HANDLE StandardInput; +extern HANDLE StandardOutput; +extern HANDLE StandardError; + +extern char const* sockmsg(int ecode); +extern void err(char const* s,...); + +extern void telnet(char const* pszHostName,const short nPort); +extern void vm(SOCKET,unsigned char); + +// terminal handlers: +void ansi(SOCKET server,unsigned char data); +void nvt(SOCKET server,unsigned char data); + +// helpsock +char const* sockmsg(int ecode); + +// command shell +int shell(char const* pszHostName,const int nPort); + +#endif /* ndef _APPS_NET_TELNET_H */ diff --git a/reactos/apps/utils/net/telnet/telnet.rc b/reactos/apps/utils/net/telnet/telnet.rc new file mode 100644 index 00000000000..420454f797f --- /dev/null +++ b/reactos/apps/utils/net/telnet/telnet.rc @@ -0,0 +1,39 @@ +#include +#include + +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +VS_VERSION_INFO VERSIONINFO + FILEVERSION RES_UINT_FV_MAJOR,RES_UINT_FV_MINOR,RES_UINT_FV_REVISION,RES_UINT_FV_BUILD + PRODUCTVERSION RES_UINT_PV_MAJOR,RES_UINT_PV_MINOR,RES_UINT_PV_REVISION,RES_UINT_PV_BUILD + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", RES_STR_COMPANY_NAME + VALUE "FileDescription", "ReactOS Telnet Win32 Client\0" + VALUE "FileVersion", RES_STR_FILE_VERSION + VALUE "InternalName", "telnet\0" + VALUE "LegalCopyright", RES_STR_LEGAL_COPYRIGHT + VALUE "OriginalCopyright", "Anonymous sources found at http://www.telnet.org/\0" + VALUE "OriginalFilename", "telnet.exe\0" + VALUE "ProductName", RES_STR_PRODUCT_NAME + VALUE "ProductVersion", RES_STR_PRODUCT_VERSION + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + diff --git a/reactos/apps/utils/net/telnet/vm.cpp b/reactos/apps/utils/net/telnet/vm.cpp new file mode 100644 index 00000000000..0db3549e0b5 --- /dev/null +++ b/reactos/apps/utils/net/telnet/vm.cpp @@ -0,0 +1,424 @@ +/* $Id: vm.cpp,v 1.1 2001/01/27 22:38:43 ea Exp $ + * + * FILE : vm.cpp + * AUTHOR : unknown (sources found on www.telnet.org) + * PROJECT : ReactOS Operating System + * DESCRIPTION: telnet client for the W32 subsystem + * DATE : 2001-01-21 + * REVISIONS + * 2001-02-21 ea Modified to compile under 0.0.16 src tree + */ +#include +#include + +#include "telnet.h" + +// NAME CODE MEANING +// NVT codes +#define NUL 0 // No Operation +#define BEL 7 // BELL +#define BS 8 // Back Space +#define HT 9 // Horizontal Tab +#define LF 10 // Line Feed +#define VT 11 // Vertical Tab +#define FF 12 // Form Feed +#define CR 13 // Carriage Return + +// telnet command codes +#define SE 240 // End of subnegotiation parameters. +#define NOP 241 // No operation. +#define DM 242 // Data Mark +#define BRK 243 // Break +#define IP 244 // Interrupt Process +#define AO 245 // Abort output +#define AYT 246 // Are You There +#define EC 247 // Erase character +#define EL 248 // Erase Line +#define GA 249 // Go ahead +#define SB 250 // SuBnegotiate +#define WILL 251 // +#define WONT 252 // +#define DO 253 // +#define DONT 254 // +#define IAC 255 // Interpret As Command + +//Telnet options: +// 0x00 - binary mode +// 0x01 - Local Echo +// 0x03 - Suppress GA (char at a time) +// 0x05 - status +// 0x06 - Timing Mark +// +// do 0x25 - zub? +// +// 0xff - Extended Options List + +enum _option +{ + TOPT_BIN = 0, // Binary Transmission + TOPT_ECHO = 1, // Echo + TOPT_RECN = 2, // Reconnection + TOPT_SUPP = 3, // Suppress Go Ahead + TOPT_APRX = 4, // Approx Message Size Negotiation + TOPT_STAT = 5, // Status + TOPT_TIM = 6, // Timing Mark + TOPT_REM = 7, // Remote Controlled Trans and Echo + TOPT_OLW = 8, // Output Line Width + TOPT_OPS = 9, // Output Page Size + TOPT_OCRD = 10, // Output Carriage-Return Disposition + TOPT_OHT = 11, // Output Horizontal Tabstops + TOPT_OHTD = 12, // Output Horizontal Tab Disposition + TOPT_OFD = 13, // Output Formfeed Disposition + TOPT_OVT = 14, // Output Vertical Tabstops + TOPT_OVTD = 15, // Output Vertical Tab Disposition + TOPT_OLD = 16, // Output Linefeed Disposition + TOPT_EXT = 17, // Extended ASCII + TOPT_LOGO = 18, // Logout + TOPT_BYTE = 19, // Byte Macro + TOPT_DATA = 20, // Data Entry Terminal + TOPT_SUP = 21, // SUPDUP + TOPT_SUPO = 22, // SUPDUP Output + TOPT_SNDL = 23, // Send Location + TOPT_TERM = 24, // Terminal Type + TOPT_EOR = 25, // End of Record + TOPT_TACACS = 26, // TACACS User Identification + TOPT_OM = 27, // Output Marking + TOPT_TLN = 28, // Terminal Location Number + TOPT_3270 = 29, // Telnet 3270 Regime + TOPT_X3 = 30, // X.3 PAD + TOPT_NAWS = 31, // Negotiate About Window Size + TOPT_TS = 32, // Terminal Speed + TOPT_RFC = 33, // Remote Flow Control + TOPT_LINE = 34, // Linemode + TOPT_XDL = 35, // X Display Location + TOPT_ENVIR = 36,// Telnet Environment Option + TOPT_AUTH = 37, // Telnet Authentication Option + TOPT_NENVIR = 39,// Telnet Environment Option + TOPT_EXTOP = 255, // Extended-Options-List + TOPT_ERROR = 256 // Magic number +}; + +// Wanted by linux box: +// 37 - TOPT_AUTH +// 24 - TOPT_TERM + +enum _verb +{ + verb_sb = 250, + verb_will = 251, + verb_wont = 252, + verb_do = 253, + verb_dont = 254 +}; + +enum _state +{ + state_data, //we expect a data byte + state_code, //we expect a code + state_option //we expect an option +}; + +int option_error(_verb,_option,int,SOCKET); + +typedef void(*LPOPTIONPROC)(SOCKET,_verb,_option); +typedef void(*LPDATAPROC)(SOCKET,unsigned char data); + +/////////////////////////////////////////////////////////////////////////////// + +inline void yesreply(SOCKET server, _verb verb,_option option) +{ + unsigned char buf[3]; + buf[0] = IAC; + buf[1] = (verb==verb_do)?WILL:(verb==verb_dont)?WONT:(verb==verb_will)?DO:DONT; + buf[2] = (unsigned char)option; + send(server,(char*)buf,3,0); +} + +inline void noreply(SOCKET server, _verb verb,_option option) +{ + unsigned char buf[3]; + buf[0] = IAC; + buf[1] = (verb==verb_do)?WONT:(verb==verb_dont)?WILL:(verb==verb_will)?DONT:DO; + buf[2] = (unsigned char)option; + send(server,(char*)buf,3,0); +} + +inline void askfor(SOCKET server, _verb verb,_option option) +{ + unsigned char buf[3]; + buf[0] = IAC; + buf[1] = (unsigned char)verb; + buf[2] = (unsigned char)option; + send(server,(char*)buf,3,0); +} + + +void ddww_error(SOCKET server,_verb verb,_option option) +{ +#ifdef _DEBUG + char tmp[256]; + wsprintf(tmp,"Unknown Option Code: %s, %i\n",(verb==verb_do)?"DO":(verb==verb_dont)?"DON'T":(verb==verb_will)?"WILL":"WONT",(int)option); + OutputDebugString(tmp); +#endif + + switch(verb) + { + case verb_will: // server wants to support something + noreply(server,verb,option); // I don't want that. + break; + case verb_wont: // server waants to disable support + return; // don't confirm - already disabled. + case verb_do: // server wants me to support something + noreply(server,verb,option); //I won't do that + break; + case verb_dont: // server wants me to disable something + return; // don't worry, we don't do that anyway (I hope :) + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Option ECHO & SUPPRESS GA +// +// These options are curiously intertwined... +// The Win32 console doesn't support ECHO_INPUT (echo) if +// LINE_INPUT (==GA) isn't set. +// I can't see how to code this negotiation without using +// some form of Lock-Step algorythm +// ie: if("WILL ECHO") +// Send "DO SUPP" +// if("WILL SUPP") +// Send "DO ECHO" +// else +// Send "DONT ECHO" + + +void ddww_echo(SOCKET server,_verb verb, _option option) +{ + DWORD mode; + GetConsoleMode(StandardInput, & mode); // ENABLE_ECHO_INPUT + + int set = !(mode & ENABLE_ECHO_INPUT); + + switch(verb) + { + case verb_will: // server wants to echo stuff + if(set) return; //don't confirm - already set. + SetConsoleMode(StandardInput,mode & (~ENABLE_ECHO_INPUT)); + break; + case verb_wont: // server don't want to echo + if(!set) return; //don't confirm - already unset. + SetConsoleMode(StandardInput,mode | ENABLE_ECHO_INPUT); + break; + case verb_do: // server wants me to loopback + noreply(server,verb,option); + return; + case verb_dont: // server doesn't want me to echo + break; // don't bother to reply - I don't + } + yesreply(server,verb,option); +} + + +void ddww_supp(SOCKET server,_verb verb,_option option) //Suppress GA +{ + DWORD mode; + GetConsoleMode(StandardInput,&mode); // ENABLE_LINE_INPUT + + int set = !(mode & ENABLE_LINE_INPUT); + + switch(verb) + { + case verb_will: // server wants to suppress GA's + if(set) break; //don't confirm - already set. + SetConsoleMode(StandardInput,mode & (~(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT))); + askfor(server,verb_do,TOPT_SUPP); + askfor(server,verb_will,TOPT_SUPP); + askfor(server,verb_do,TOPT_ECHO); + break; + case verb_wont: // server wants to send GA's + if(!set) break; //don't confirm - already unset. + SetConsoleMode(StandardInput,mode | ENABLE_LINE_INPUT); + askfor(server,verb_dont,TOPT_SUPP); + askfor(server,verb_wont,TOPT_SUPP); + break; + case verb_do: // server wants me to suppress GA's + if(set) break; + askfor(server,verb_do,TOPT_SUPP); + break; + case verb_dont: // server wants me to send GA's + if(!set) break; + askfor(server,verb_dont,TOPT_SUPP); + break; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Option TERMINAL-TYPE + +void ddww_term(SOCKET server,_verb verb,_option option) //Subnegotiate terminal type +{ + switch(verb) + { + case verb_will: + noreply(server,verb,option); // I don't want terminal info + break; + case verb_wont: + //dat be cool - its not going to send. no need to confirm + break; + case verb_do: + yesreply(server,verb,option); //I'll send it when asked + break; + case verb_dont://Ok - I won't + break; + } +} + +// TERMINAL TYPE subnegotation +enum +{ + SB_TERM_IS = 0, + SB_TERM_SEND = 1 +}; + +#define NUM_TERMINALS 2 + +struct +{ + char* name; + LPDATAPROC termproc; + //pre requsites. +} terminal[NUM_TERMINALS] = { + { "NVT", nvt }, + { "ANSI", ansi } +}; + +int term_index = 0; + +void sbproc_term(SOCKET server,unsigned char data) +{ + + if(data == SB_TERM_SEND) + { + if(term_index == NUM_TERMINALS) + term_index = 0; + else + term_index++; + char buf[16]; //pls limit + buf[0] = IAC; + buf[1] = SB; + buf[2] = TOPT_TERM; + buf[3] = SB_TERM_IS; + lstrcpy(&buf[4],terminal[(term_index==NUM_TERMINALS)?(NUM_TERMINALS-1):term_index].name); + int nlen = lstrlen(&buf[4]); + buf[4+nlen] = IAC; + buf[5+nlen] = SE; + send(server,buf,4+nlen+2,0); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +struct +{ + _option option; + LPOPTIONPROC OptionProc; + LPDATAPROC DataProc; +} ol[] = { + {TOPT_ECHO, ddww_echo, NULL}, + {TOPT_SUPP, ddww_supp, NULL}, + {TOPT_TERM, ddww_term, sbproc_term}, + {TOPT_ERROR, ddww_error, NULL} +}; + + +void vm(SOCKET server,unsigned char code) +{ +//These vars are the finite state + static int state = state_data; + static _verb verb = verb_sb; + static LPDATAPROC DataProc = terminal[(term_index==NUM_TERMINALS)?(NUM_TERMINALS-1):term_index].termproc; +// for index + int i=0; + +//Decide what to do (state based) + switch(state) + { + case state_data: + switch(code) + { + case IAC: state = state_code; break; + default: DataProc(server,code); + } + break; + case state_code: + state = state_data; + switch(code) + { + // State transition back to data + case IAC: + DataProc(server,code); + break; + // Code state transitions back to data + case SE: + DataProc = terminal[(term_index==NUM_TERMINALS)?(NUM_TERMINALS-1):term_index].termproc; + break; + case NOP: + break; + case DM: + break; + case BRK: + break; + case IP: + break; + case AO: + break; + case AYT: + break; + case EC: + break; + case EL: + break; + case GA: + break; + // Transitions to option state + case SB: + verb = verb_sb; + state = state_option; + break; + case WILL: + verb = verb_will; + state = state_option; + break; + case WONT: + verb = verb_wont; + state = state_option; + break; + case DO: + verb = verb_do; + state = state_option; + break; + case DONT: + verb = verb_dont; + state = state_option; + break; + } + break; + case state_option: + state = state_data; + + //Find the option entry + for( + i = 0; + ol[i].option != TOPT_ERROR && ol[i].option != code; + i++); + + //Do some verb specific stuff + if(verb == verb_sb) + DataProc = ol[i].DataProc; + else + ol[i].OptionProc(server,verb,(_option)code); + break; + } +} + +/* EOF */