//#define DEBUG
//****************************************************************************}
//program TelnetClient }
#define VeRsIoN "1.04"
//----------------------------------------------------------------------------}
// date of origin : 15.12. 1996 }
//define LaSt_DaTe "6. Jan 1996"
// last change time : 17.30 }
// Author : Petr Jaklin , V Závetrí 1678, Teplice 415 01 }
//----------------------------------------------------------------------------}
// Open telnet session to specified host }
// not supported: scr attributes
// support for TERMINAL TYPE disabled: NW xconsole use ANSI term as Xterm ...
// v1.02: displayInputCursor
// v1.03: new ESC sequences for XCONSOLE compatibility (e[?2J is supposed to
// be CLRSCR; at e[12h I switch from 0-based cursor to 1-based one)
// v1.04: TermType VT100
//****************************************************************************}
#include <arpa/inet.h>
#include <conio.h>
#include <errno.h>
#include <io.h>
#include <netinet/in.h>
#include <nwsemaph.h>
#include <process.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
//---------------------------------------------------------------------------}
/* DESCRIPTION FILE
copyright "Copyright (C) 1996, Petr Jaklin, 3NET s.r.o."
version VeRsIoN
description "Telnet client"
screenname "Telnet"
threadname "telnet"
output f:\tc\tel
input tel g:\progbase\c386\novi\prelude
stack 8192
type 0
debug
multiple
import __WATCOM_Prelude
import @g:\progbase\c386\novi\clib.imp
import @g:\progbase\c386\novi\socklib.imp
#import @f:\tc\h\server4.imp
module clib, tcpip
*/
//---------------------------------------------------------------------------
// TYPEs
typedef enum {
TSdata,
TSiac,
TSdo,
TSdn,
TSwl,
TSwn,
TSsub,
TSsubOpt,
TSsubIac
} TelnetStatus;
typedef enum {
ASdata,
ASesc,
ASprefix,
AScharSet,
ASnum,
ASchar,
AScharEnd,
ASsepar,
} ANSIStatus;
char protErr[]="PROTO_ERROR: ";
char eraseLine[80] = {
" "
" "};
//---------------------------------------------------------------------------
// variables
TelnetStatus telStat=TSdata; // telnet automat status
ANSIStatus ansiStat=ASdata; // ansi automat status
char *myname; // prg name string
char host[80]; // name of host to telnet
char iobuf[4097]; // reading buffer
struct sockaddr_in server_sockaddr; // IP target address
int sock; // IP socket
int readData=FALSE; // do read flag
int endTel=FALSE; // main quit flag
char oline[120]; // keyboard buffer
LONG wrtSem; // IP write semaphore (I dont know
// if 'write' is reentrant
LONG readID; // read thread ID
char *CMD[] = // telnet cmd names for 'logging'
{ "End of subnegotiation",
"No operation",
"Data Mark",
"Break",
"Interrupt process",
"Abort output",
"Are You there ?",
"Erase charecter",
"Erase line",
"Go ahead",
"SubNegotiation",
"WILL",
"WON'T",
"DO",
"DON'T",
"IAC"
};
char strOpt[20]; // buffer for telnet cmd logging
#define SbMax 50
char sbC[SbMax]; // sub negotiation buffer
int sbNum; // counter of sbC buffer
int remEcho=FALSE; // echo option status
int isANSI=TRUE; // is ANSI code supported
#define AsMaxNum 20
BYTE asNum[AsMaxNum]; // array of ansi arguments
int asNums=0; // # of valid asNum[]
int asPrefix=0; // ESC prefix [ ... 1 , ( ... 2
// ) ... 3 , [= ... 4
// [? ... 5 , ... 0
int charSet=0; // 0=ASCII, 1=graphics
int asCx=0; // cursor saving vars (for ANSI e[s)
int asCy=0;
int xy0=0; // cursor home address (0,0) is default
int displayInputCursor=FALSE; // cursor state
int setCursor=FALSE; // inter-thread flag
#ifdef DEBUG
int logIt=TRUE; // log traffic into sys:system\tel.log
int ctrl=TRUE; // do log control too
#else
int logIt=FALSE; // log traffic into sys:system\tel.log
int ctrl=FALSE; // do log control too
#endif
FILE *logFile=NULL; // logfile handle
//---------------------------------------------------------------------------
void ecprintf ( char *format,
...)
// printf + if control echo to log
//---------------------------------------------------------------------------
{
va_list params;
va_start(params,format);
vprintf(format,params);
va_end(params);
if (ctrl) {
va_start(params,format);
vfprintf(logFile,format,params);
va_end(params);
}
} // ecprintf
//---------------------------------------------------------------------------
char * Option ( int opt )
//---------------------------------------------------------------------------
{
switch (opt) {
case 0 : return("BinaryTransmission");
case 1 : return("ECHO");
case 3 : return("SuppresGA");
case 5 : return("Status");
case 17 : return("Extended ASCII");
case 24 : return("Terminal Type");
default : sprintf(strOpt,"Unknown-%02X",opt);
return(strOpt);
}
} // option
//---------------------------------------------------------------------------
int WriteData ( BYTE *buff,
int size)
//---------------------------------------------------------------------------
{
int wr;
char str[100];
int i;
WaitOnLocalSemaphore(wrtSem);
if (logIt) {
for (i=0;i<size;i++)
if (buff[i])
str[i]=buff[i];
else
str[i]=255;
str[size]=0;
fprintf(logFile,str);
}
wr=write(sock,buff,size);
if (wr==-1) {
perror("write");
exit(1);
}
SignalLocalSemaphore(wrtSem);
return(wr);
} // writedata
//---------------------------------------------------------------------------
void WriteCtrl ( int cmd,
int option)
//---------------------------------------------------------------------------
{
char buff[3];
buff[0]=255;
buff[1]=cmd;
buff[2]=option;
if (ctrl)
ecprintf("WriteCTRL(%s,%s)\r\n",CMD[cmd-240],Option(option));
WriteData(buff,3);
} // writectrl
//---------------------------------------------------------------------------
void EraseLine ( int from )
// erases line from 'from' to end of line
//---------------------------------------------------------------------------
{
int x,y;
eraseLine[80-from]=0;
x=wherex();
y=wherey();
gotoxy(from,y);
printf(eraseLine);
gotoxy(x,y);
eraseLine[80-from]=' ';
} // eraseline
//---------------------------------------------------------------------------
void Mode ( int mode )
//---------------------------------------------------------------------------
{
mode=mode;
ecprintf("ANSI_MODE: Ignored\r\n");
} // mode
//---------------------------------------------------------------------------
void ScrAttr ( int attr )
// not supported
//---------------------------------------------------------------------------
{
switch (attr) {
case 0 : // normal (white on black)
break;
case 1 : // bold
break;
case 4 : // underline
break;
case 5 : // blink
break;
case 7 : // reverse
break;
case 8 : // no display (foreg==backg)
break;
// foregrounds
case 30 : // black
break;
case 31 : // red
break;
case 32 : // green
break;
case 33 : // yellow
break;
case 34 : // blue
break;
case 35 : // magenta
break;
case 36 : // cyan
break;
case 37 : // white
break;
// backgrounds
case 40 : // black
break;
case 41 : // red
break;
case 42 : // green
break;
case 43 : // yellow
break;
case 44 : // blue
break;
case 45 : // magenta
break;
case 46 : // cyan
break;
case 47 : // white
break;
default : ecprintf("ANSI_SCR_MODE: Unknown attribute (%d)\r\n",attr);
}
} // scrattr
//---------------------------------------------------------------------------
void InvalidAnsi ( char c )
//---------------------------------------------------------------------------
{
int i;
ecprintf("INVALID_ANSI:0x1b[");
for (i=0;i<asNums;i++) {
ecprintf("%d",asNum[i]);
if (i<(asNum-1))
ecprintf(";");
}
ecprintf("%c\r\n",c);
} // InvalidAnsi
//---------------------------------------------------------------------------
void CompleteAnsi ( char c )
//---------------------------------------------------------------------------
{
char str[10];
int i;
switch (asPrefix) {
case 0 : // none
switch (c) {
case 'A' : gotoxy(wherex(),wherey()-1);
break;
case 'B' : gotoxy(wherex(),wherey()+1);
break;
case 'C' : gotoxy(wherex()+1,wherey());
break;
case 'D' : gotoxy(wherex()-1,wherey());
break;
case 'F' : charSet=1;
break;
case 'G' : charSet=0;
break;
case 'H' : gotoxy(0,0);
break;
case '(' : charSet=0;
break;
case ')' : charSet=1;
break;
case '=' : ;
case '>' : ;
case 'c' : ; // reset terminal
break;
default : InvalidAnsi(c);
}
break;
case 1 : // '['
switch (c) {
case 'f' : ;
case 'H' : if (asNums==1)
gotoxy(0,asNum[0]-xy0);
else if (asNums==2)
gotoxy(asNum[1]-xy0,asNum[0]-xy0);
else if (asNums==0)
gotoxy(0,0);
else
InvalidAnsi(c);
break;
case 'A' : if (asNums==1)
gotoxy(wherex(),wherey()-asNum[0]);
else
InvalidAnsi(c);
break;
case 'B' : if (asNums==1)
gotoxy(wherex(),wherey()+asNum[0]);
else
InvalidAnsi(c);
break;
case 'C' : if (asNums==1)
gotoxy(wherex()+asNum[0],wherey());
else
InvalidAnsi(c);
break;
case 'D' : if (asNums==1)
gotoxy(wherex()-asNum[0],wherey());
else
InvalidAnsi(c);
break;
case 'J' : if ((asNums==1) && (asNum[0]==2)) {
clrscr();
// cannot be here: waits for kbd free !?! DisplayInputCursor();
displayInputCursor=TRUE;
setCursor=TRUE;
}
else
InvalidAnsi(c);
break;
case 'K' : if (asNums==1) {
if (asNum[0]==0)
EraseLine(wherex());
else if (asNum[0]==2)
EraseLine(0);
else
InvalidAnsi(c);
}
else
InvalidAnsi(c);
break;
case 'h' : if ((asNums==1) && (asNum[0]==12))
xy0=1;
else
InvalidAnsi(c);
break;
case 'm' : for (i=0;i<asNums;i++)
ScrAttr(asNum[i]);
break;
case 'n' : if ((asNums==1) && (asNum[0]==6)) {
sprintf(str,"\x1b[%d;%dR",wherey()+xy0,
wherex()+xy0);
WriteData(str,strlen(str));
}
else
InvalidAnsi(c);
break;
case 'p' : ecprintf("ANSI_KEY_REMAP: Not Supported\r\n");
break;
case 's' : if ((asNums==1) && (asNum[0]==0)) {
asCx=wherex();
asCy=wherey();
}
else
InvalidAnsi(c);
break;
case 'u' : if ((asNums==1) && (asNum[0]==0))
gotoxy(asCx,asCy);
else
InvalidAnsi(c);
break;
default : InvalidAnsi(c);
} //switch letter [
break;
case 2 : // '('
;
case 3 : // ')'
;
break; // fixed now: standerd is ASCII, alternate is line draw
case 4 : // '[='
if (asNums!=1)
InvalidAnsi(c);
else if ((c=='h') || (c=='l'))
Mode(asNum[0]);
else
InvalidAnsi(c);
break;
case 5 : // '[?'
switch (c) {
case 'J' : if ((asNums==1) && (asNum[0]==2)) {
clrscr();
// cannot be here: waits for kbd free !?! DisplayInputCursor();
displayInputCursor=TRUE;
setCursor=TRUE;
}
else
InvalidAnsi(c);
break;
case 'h' : if (asNums==1)
switch(asNum[0]) {
case 25 : ;
case 50 : setCursor=TRUE;
displayInputCursor=TRUE;
break;
}
else
InvalidAnsi(c);
break;
case 'l' : if (asNums==1)
switch(asNum[0]) {
case 25 : ;
case 50 : setCursor=TRUE;
displayInputCursor=FALSE;
break;
}
else
InvalidAnsi(c);
break;
}
break;
}
ansiStat=ASdata;
} // completeansi
//---------------------------------------------------------------------------
void DataDisplay ( char c )
// pure data automat engine (ANSI)
//---------------------------------------------------------------------------
{
if (isANSI)
switch (ansiStat) {
case ASdata : if (c==27) {
asPrefix=0;
ansiStat=ASesc;
}
else if (c==15)
charSet=0; // shift in
else if (c==14)
charSet=1; // shift out
else {
if (charSet==1)
switch (c) {
case 0x61 : c='░';break;
case 0x6a : c='┘';break;
case 0x6b : c='┐';break;
case 0x6c : c='┌';break;
case 0x6d : c='└';break;
case 0x71 : c='─';break;
case 0x74 : c='├';break;
case 0x75 : c='┤';break;
case 0x76 : c='┴';break;
case 0x77 : c='┬';break;
case 0x78 : c='│';break;
}
printf("%c",c);
}
break;
case ASesc : if (c=='[') {
asPrefix=1;
ansiStat=ASprefix;
}
else if (c=='(') {
asPrefix=2;
ansiStat=AScharSet;
}
else if (c==')') {
asPrefix=3;
ansiStat=AScharSet;
}
else
CompleteAnsi(c);
break;
case AScharSet : CompleteAnsi(c);
break;
case ASprefix : if (c=='=') {
if (asPrefix==1)
asPrefix=4;
else
CompleteAnsi(c);
}
else if (c=='?') {
if (asPrefix==1)
asPrefix=5;
else
CompleteAnsi(c);
}
else if ((c>='0') && (c<='9')) {
asNum[0]=c-0x30;
asNums=1;
ansiStat=ASnum;
}
else if (c==';') {
asNum[0]=0;
asNums=1;
ansiStat=ASsepar;
}
else if (c==34) { // "
asNums=0;
ansiStat=ASchar;
}
else
CompleteAnsi(c);
break;
case ASnum : if ((c>='0') && (c<='9')) {
asNum[asNums-1]*=10;
asNum[asNums-1]+=c-0x30;
}
else if (c==';')
ansiStat=ASsepar;
else
CompleteAnsi(c);
break;
case ASchar : if (c=='"')
ansiStat=AScharEnd;
else if (asNums==AsMaxNum) {
ecprintf("ANSI_ERROR: TooLongData\r\n");
ansiStat=ASdata;
}
else
asNum[asNums++]=c;
break;
case AScharEnd : if (c==';')
ansiStat=ASsepar;
else
CompleteAnsi(c);
break;
case ASsepar : if ((c>='0') && (c<='9')) {
if (asNums==AsMaxNum) {
ecprintf("ANSI_ERROR: TooLongData\r\n");
ansiStat=ASdata;
}
asNum[asNums]=c-0x30;
asNums+=1;
ansiStat=ASnum;
}
else if (c=='"')
ansiStat=ASchar;
else {
asNum[asNums]=0;
asNums+=1;
CompleteAnsi(c);
} //else
break;
}
else
printf("%c",c);
} // datadisplay
//---------------------------------------------------------------------------
void Display ( char c)
// telnet automat engine
//---------------------------------------------------------------------------
{
switch (telStat) {
case TSdata : switch (c) {
case 255 : telStat=TSiac;
break;
case 0 : break;
default : DataDisplay(c);
}
break;
case TSiac : if (c<240) {
ecprintf("%s IAC/%02x\r\n",protErr,c);
telStat=TSdata;
}
else {
switch (c) {
case 255 : DataDisplay(255); // iac
telStat=TSdata;
break;
case 240 : ecprintf(protErr); // subnego. end
telStat=TSdata;
break;
case 246 : WriteData("OK",2); // are you there
telStat=TSdata;
break;
case 247 : printf("\8"); // erase character
telStat=TSdata;
break;
case 248 : EraseLine(0);
telStat=TSdata;
break;
case 250 : telStat=TSsub;
break;
case 251 : telStat=TSwl;
break;
case 252 : telStat=TSwn;
break;
case 253 : telStat=TSdo;
break;
case 254 : telStat=TSdn;
break;
default : telStat=TSdata;
break;
}
if (c<250)
if (ctrl)
ecprintf("ReadCTRL(%s)\r\n",CMD[c-240]);
}
break;
case TSdo : if (ctrl)
ecprintf("ReadCTRL(DO,%s)\r\n",Option(c));
switch (c) {
case 3 : WriteCtrl(251,3); // suppres GA (will)
break;
case 24 : WriteCtrl(251,24); // terminal type
break;
default : WriteCtrl(252,c); // won't
}
telStat=TSdata;
break;
case TSdn : if (ctrl)
ecprintf("ReadCTRL(DON'T,%s)\r\n",Option(c));
WriteCtrl(252,c); // won't
telStat=TSdata;
break;
case TSwl : if (ctrl)
ecprintf("ReadCTRL(WILL,%s)\r\n",Option(c));
switch (c) {
case 1 : WriteCtrl(253,1); // echo (do)
remEcho=TRUE;
break;
case 3 : WriteCtrl(253,3); // suppres GA (do)
break;
default : WriteCtrl(254,c); // don't
}
telStat=TSdata;
break;
case TSwn : if (ctrl)
ecprintf("ReadCTRL(WON'T,%s)\r\n",Option(c));
WriteCtrl(254,c); // don't
if (c==1)
remEcho=FALSE;
telStat=TSdata;
break;
case TSsub : if (ctrl)
ecprintf("ReadCTRL(SubNeg,%s)\r\n",Option(c));
telStat=TSsubOpt;
sbNum=1;
sbC[0]=c;
break;
case TSsubOpt : if (sbNum<SbMax)
sbC[sbNum++]=c;
else
sbNum=SbMax+1;
if (c==255) // iac
telStat=TSsubIac;
break;
case TSsubIac : if (sbNum<SbMax)
sbC[sbNum++]=c;
else
sbNum=SbMax+1;
if (c==240) { // SubNeg End
if ((sbNum>0) && (sbNum<SbMax)) {
if (sbC[0]==24) { // terminal type
if ((sbNum==4) && (sbC[1]==1)) { // send terminal type
strcpy(sbC,"\xff\xfa\x18\0VT100\xff\xf0");
WriteData(sbC,12); // my term is VT100
if (ctrl)
ecprintf("WriteCTRL(SubNegSend,TermType,VT100)\r\n");
}
}
}
telStat=TSdata;
}
}
} //display
//---------------------------------------------------------------------------
void ReadData ( /*void*/ )
// data reading thread procedure
//---------------------------------------------------------------------------
{
int rc;
int i;
while (!readData)
delay(200);
while (sock!=-1) {
rc=read(sock,iobuf,sizeof(iobuf));
if (rc==-1) {
printf("\r\n");
perror("read");
exit(1);
}
else {
for (i=0;i<rc;i++) {
if (logIt)
fprintf(logFile,"%c",iobuf[i]);
Display(iobuf[i]);
}
}
ThreadSwitchWithDelay();
}
endTel=TRUE;
delay(1000);
ExitThread(EXIT_NLM,0);
} // readdata
//---------------------------------------------------------------------------
void exitproc ( void )
//---------------------------------------------------------------------------
{
if (sock!=-1) {
if (-1== close(sock))
perror("close");
sock=-1;
}
if (logFile!=NULL) {
fclose(logFile);
logFile=NULL;
}
if (wrtSem!=-1) {
CloseLocalSemaphore(wrtSem);
wrtSem=-1;
}
} // exitproc
//---------------------------------------------------------------------------
void WriteHelp ( int err )
//---------------------------------------------------------------------------
{
if (err)
printf(" TELnet argument ERROR.\r\n\r\n");
printf("Syntax: %s <server_address> [/L] [/C] [/NA]\r\n\n", myname);
printf("where /L ... log traffic into SYS:SYSTEM\\TEL.LOG\r\n");
printf(" /C ... log control messages too\r\n");
printf(" /NA ... don't use ANSI control codes\r\n");
exit(err);
} // writehelp
//---------------------------------------------------------------------------
void ReadParam ( int argc,
char **argv)
//---------------------------------------------------------------------------
{
int i;
char str[80];
char host[80];
myname=*argv;
if (argc<2)
WriteHelp(0);
host[0]=0;
for (i=1;i<argc;i++) {
strcpy(str,strupr(argv[i]));
if (strcmp(str,"/L")==0)
logIt=TRUE;
else if (strcmp(str,"/C")==0)
ctrl=TRUE;
else if (strcmp(str,"/?")==0)
WriteHelp(FALSE);
else if (strcmp(str,"/H")==0)
WriteHelp(FALSE);
else if (strcmp(str,"/NA")==0)
isANSI=FALSE;
else if (str[0]=='/')
WriteHelp(TRUE);
else
strcpy(host,str);
}
if (host[0]==0)
WriteHelp(TRUE);
if (logIt | ctrl) {
if ((logFile=fopen("SYS:SYSTEM\\TEL.LOG","ab"))==NULL) {
printf("LOG_ERROR: Unable to Open Log File.\r\n");
logIt=ctrl=FALSE;
}
}
server_sockaddr.sin_addr.s_addr = inet_addr(host);
} // readparam
//---------------------------------------------------------------------------
void MoveCursor ( char c )
// send ESC [ ABCD
//---------------------------------------------------------------------------
{
char data[3];
data[0]=27;
data[1]='[';
data[2]=c;
WriteData(data,3);
} // movecursor
//---------------------------------------------------------------------------
void KeybInput ( void )
//---------------------------------------------------------------------------
{
int i;
int c1,c2;
endTel=FALSE;
i=0;
do {
while (!kbhit()) {
delay(60);
if (setCursor) {
if (displayInputCursor)
DisplayInputCursor();
else
HideInputCursor();
setCursor=FALSE;
}
}
if (0==(c1=remEcho?getch():getche()))
c2=remEcho?getch():getche();
switch (c1) {
case 0 : switch (c2) {
case 0x14 : endTel=TRUE; // ALT-T
break;
case 0x48 : MoveCursor('A');
break;
case 0x50 : MoveCursor('B');
break;
case 0x4d : MoveCursor('C');
break;
case 0x4b : MoveCursor('D');
break;
case 0x53 : oline[i++]=255;
oline[i++]=247; // erase char (del)
break;
default : oline[i++]=0;
oline[i++]=c2;
}
break;
case 3 : oline[i++]=255;
oline[i++]=244; // Interrupt process
break;
case 13 : oline[i++]=13; // CRLF
oline[i++]=0;
WriteData(oline,i);
i=0;
break;
default : oline[i++]=c1;
}
if (i>0) {
WriteData(oline,i);
i=0;
}
} while (!endTel);
} // keybinput
//---------------------------------------------------------------------------
void main ( int argc,
char **argv)
//---------------------------------------------------------------------------
{
int rc;
sock=-1;
wrtSem=-1;
SetCtrlCharCheckMode(FALSE);
SetAutoScreenDestructionMode(FALSE);
SetCursorCouplingMode(TRUE);
atexit(exitproc);
ReadParam(argc,argv);
if (server_sockaddr.sin_addr.s_addr == -1) {
printf("ERROR: %s is a malformed address\r\n",host);
exit(1);
}
server_sockaddr.sin_family = AF_INET;
server_sockaddr.sin_port = htons(23);
printf("Opening socket\r\n");
sock=socket(PF_INET, SOCK_STREAM, 0);
if (sock==-1) {
perror("socket");
exit(1);
}
wrtSem=OpenLocalSemaphore(1);
readData=FALSE;
if ((readID=BeginThread(ReadData,NULL,NULL,NULL))==-1) {
printf(" Error creating reading thread.\r\n");
exit(1);
}
printf("Connecting\r\n");
rc=connect(sock,(struct sockaddr *)&server_sockaddr,sizeof(server_sockaddr));
if (rc==-1) {
perror("connect");
exit(1);
}
printf("Connected. Press Alt-T to quit session.\r\n");
WriteData("OK",0); // ? begin of communication ?
readData=TRUE;
delay(800); // wait for negotiation of session
KeybInput();
if (logFile!=NULL) {
fclose(logFile);
logFile=NULL;
}
ExitThread(EXIT_NLM,0);
} // main