#ifdef DEBUG
#define NEGOTIATEDEBUG
#endif
#if 0
#define PRINT_EVERYTHING
#endif
#include <time.h>
#include "telnet.h"
#include "telnet_arpa.h"
#include "auth.h"
#include "encrypt.h"
#define STNORM 0
#define NEGOTIATE 1
#define ESCFOUND 5
#define IACFOUND 6
unsigned char parsedat[256];
static void parse_subnegotiat(kstream ks,int end_sub);
static char *telstates[]={
"EOF",
"Suspend Process",
"Abort Process",
"Unknown (239)",
"Subnegotiation End",
"NOP",
"Data Mark",
"Break",
"Interrupt Process",
"Abort Output",
"Are You There",
"Erase Character",
"Erase Line",
"Go Ahead",
"Subnegotiate",
"Will",
"Won't",
"Do",
"Don't"
};
static char *teloptions[256]={
"Binary",
"Echo",
"Reconnection",
"Supress Go Ahead",
"Message Size Negotiation",
"Status",
"Timing Mark",
"Remote Controlled Trans and Echo",
"Output Line Width",
"Output Page Size",
"Output Carriage-Return Disposition",
"Output Horizontal Tab Stops",
"Output Horizontal Tab Disposition",
"Output Formfeed Disposition",
"Output Vertical Tabstops",
"Output Vertical Tab Disposition",
"Output Linefeed Disposition",
"Extended ASCII",
"Logout",
"Byte Macro",
"Data Entry Terminal",
"SUPDUP",
"SUPDUP Output",
"Send Location",
"Terminal Type",
"End of Record",
"TACACS User Identification",
"Output Marking",
"Terminal Location Number",
"3270 Regime",
"X.3 PAD",
"Negotiate About Window Size",
"Terminal Speed",
"Toggle Flow Control",
"Linemode",
"X Display Location",
"Environment",
"Authentication",
"Data Encryption",
"39",
"40","41","42","43","44","45","46","47","48","49",
"50","51","52","53","54","55","56","57","58","59",
"60","61","62","63","64","65","66","67","68","69",
"70","71","72","73","74","75","76","77","78","79",
"80","81","82","83","84","85","86","87","88","89",
"90","91","92","93","94","95","96","97","98","99",
"100","101","102","103","104","105","106","107","108","109",
"110","111","112","113","114","115","116","117","118","119",
"120","121","122","123","124","125","126","127","128","129",
"130","131","132","133","134","135","136","137","138","139",
"140","141","142","143","144","145","146","147","148","149",
"150","151","152","153","154","155","156","157","158","159",
"160","161","162","163","164","165","166","167","168","169",
"170","171","172","173","174","175","176","177","178","179",
"180","181","182","183","184","185","186","187","188","189",
"190","191","192","193","194","195","196","197","198","199",
"200","201","202","203","204","205","206","207","208","209",
"210","211","212","213","214","215","216","217","218","219",
"220","221","222","223","224","225","226","227","228","229",
"230","231","232","233","234","235","236","237","238","239",
"240","241","242","243","244","245","246","247","248","249",
"250","251","252","253","254",
"Extended Options List"
};
static char *LMoptions[]={
"None", "MODE", "FORWARDMASK", "SLC"
};
static char *ModeOptions[]={
"None", "EDIT", "TRAPSIG", "ACK", "SOFT TAB", "LIT ECHO"
};
static char *SLCoptions[]={
"None", "SYNCH", "BREAK", "IP", "ABORT OUTPUT",
"AYT", "EOR", "ABORT", "EOF", "SUSP",
"EC", "EL", "EW", "RP", "LNEXT",
"XON", "XOFF", "FORW1", "FORW2", "MCL",
"MCR", "MCWL", "MCWR", "MCBOL", "MCEOL",
"INSRT", "OVER", "ECR", "EWR", "EBOL",
"EEOL"
};
static char *SLCflags[]={
"SLC_NOSUPPORT", "SLC_CANTCHANGE", "SLC_VALUE", "SLC_DEFAULT"
};
static unsigned char LMdefaults[NTELOPTS + 1]={
(unsigned char)-1,
(unsigned char)-1,
3,
3,
15,
25,
(unsigned char)-1,
3,
4,
26,
8,
21,
23,
18,
22,
17,
19,
22,
5,
(unsigned char)-1,
(unsigned char)-1,
(unsigned char)-1,
(unsigned char)-1,
(unsigned char)-1,
(unsigned char)-1,
(unsigned char)-1,
(unsigned char)-1,
(unsigned char)-1,
(unsigned char)-1,
(unsigned char)-1,
(unsigned char)-1
};
void
start_negotiation(kstream ks)
{
char buf[128];
#ifdef ENCRYPTION
if (encrypt_flag)
wsprintf(buf,"%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c",
IAC, WILL, TELOPT_AUTHENTICATION,
IAC, WILL, TELOPT_ENCRYPT,
IAC, DO, TELOPT_SGA,
IAC, DO, TELOPT_ECHO,
IAC, WILL, TELOPT_NAWS
);
else
#endif
wsprintf(buf,"%c%c%c%c%c%c%c%c%c%c%c%c",
IAC, WILL, TELOPT_AUTHENTICATION,
IAC, DO, TELOPT_SGA,
IAC, DO, TELOPT_ECHO,
IAC, WILL, TELOPT_NAWS
);
TelnetSend(ks,buf,lstrlen(buf),0);
#ifdef NOT
if(tw->mapoutput) {
netprintf(tw->pnum,"%c%c%c",IAC,DO,TELOPT_BINARY);
tw->uwantbinary=1;
netprintf(tw->pnum,"%c%c%c",IAC,WILL,TELOPT_BINARY);
tw->iwantbinary=1;
}
#endif
#ifdef NEGOTIATEDEBUG
wsprintf(strTmp,"SEND: %s %s\r\n",telstates[DO - TELCMD_FIRST],
teloptions[TELOPT_ECHO]);
OutputDebugString(strTmp);
wsprintf(strTmp,"SEND: %s %s\r\n",telstates[DO - TELCMD_FIRST],
teloptions[TELOPT_SGA]);
OutputDebugString(strTmp);
wsprintf(strTmp,"SEND: %s %s\r\n",telstates[WILL - TELCMD_FIRST],
teloptions[TELOPT_NAWS]);
OutputDebugString(strTmp);
#ifdef NOT
tprintf(cvs,"SEND: %s %s\r\n",telstates[DO - TELCMD_FIRST],
teloptions[BINARY]);
tprintf(cvs,"SEND: %s %s\r\n",telstates[WILL - TELCMD_FIRST],
teloptions[BINARY]);
#endif
#endif
}
void
parse(CONNECTION *con,unsigned char *st,int cnt)
{
static int sub_pos;
static int end_sub;
unsigned char *mark, *orig;
char buf[256];
kstream ks;
ks = con->ks;
#ifdef PRINT_EVERYTHING
hexdump("Options to process:", st, cnt);
#endif
orig = st;
mark = st + cnt;
#ifdef HUH
netpush(tw->pnum);
#endif
while(st < mark) {
while(con->telstate != STNORM && st < mark) {
switch(con->telstate) {
case IACFOUND:
if(*st == IAC) {
st++;
con->telstate = STNORM;
break;
}
if(*st > 239) {
con->telstate = *st++;
break;
}
#ifdef NEGOTIATEDEBUG
wsprintf(buf, "\r\n strange telnet option");
OutputDebugString(buf);
#endif
orig=++st;
con->telstate=STNORM;
break;
case EL:
case EC:
case AYT:
case AO:
case IP:
case BREAK:
case DM:
case NOP:
case SE:
case ABORT:
case SUSP:
case xEOF:
#ifdef NEGOTIATEDEBUG
wsprintf(buf,"RECV: %s\r\n",
telstates[con->telstate-TELCMD_FIRST]);
OutputDebugString(buf);
#endif
con->telstate=STNORM;
orig=++st;
break;
case GA:
#ifdef NEGOTIATEDEBUG
wsprintf(buf,"RECV: %s\r\n",
telstates[con->telstate-TELCMD_FIRST]);
OutputDebugString(buf);
#endif
con->telstate=STNORM;
orig=++st;
break;
case DO:
#ifdef NEGOTIATEDEBUG
wsprintf(buf,"RECV: %s %s\r\n",
telstates[con->telstate-TELCMD_FIRST],teloptions[*st]);
OutputDebugString(buf);
#endif
switch(*st) {
#ifdef NOT
case TELOPT_BINARY:
if(!tw->ibinary) {
if(!tw->iwantbinary) {
netprintf(tw->pnum,"%c%c%c",
IAC,WILL,BINARY);
if(tw->condebug>0)
tprintf(cv,"SEND: %s %s\r\n",
telstates[WILL - TELCMD_FIRST],
teloptions[BINARY]);
}
else
tw->iwantbinary=0;
tw->ibinary=1;
}
else {
if(tw->condebug>0)
tprintf(cv,"NO REPLY NEEDED: %s %s\r\n",
telstates[WILL - TELCMD_FIRST],
teloptions[BINARY]);
}
break;
#endif
case TELOPT_SGA:
if(!con->igoahead) {
wsprintf(buf,"%c%c%c",IAC,WILL,TELOPT_SGA);
TelnetSend(ks,buf,lstrlen(buf),0);
#ifdef NEGOTIATEDEBUG
wsprintf(strTmp,"SEND: %s %s\r\n",
telstates[WILL - TELCMD_FIRST],
teloptions[TELOPT_SGA]);
OutputDebugString(strTmp);
OutputDebugString("igoahead");
#endif
con->igoahead=1;
}
else {
#ifdef NEGOTIATEDEBUG
wsprintf(strTmp,
"NO REPLY NEEDED: %s %s\r\n",
telstates[WILL - TELCMD_FIRST],
teloptions[TELOPT_SGA]);
OutputDebugString(strTmp);
#endif
}
break;
case TELOPT_TTYPE:
if(!con->termsent) {
con->termsent=TRUE;
wsprintf(buf,"%c%c%c",IAC,WILL,TELOPT_TTYPE);
TelnetSend(ks,buf,lstrlen(buf),0);
#ifdef NEGOTIATEDEBUG
wsprintf(strTmp,"SEND: %s %s\r\n",
telstates[WILL - TELCMD_FIRST],
teloptions[TELOPT_TTYPE]);
OutputDebugString(strTmp);
#endif
}
else {
#ifdef NEGOTIATEDEBUG
wsprintf(strTmp,"NO REPLY NEEDED: %s %s\r\n",
telstates[WILL - TELCMD_FIRST],
teloptions[TELOPT_TTYPE]);
OutputDebugString(strTmp);
#endif
}
break;
#ifdef LATER
case TELOPT_LINEMODE:
tw->lmflag=1;
netprintf(tw->pnum,"%c%c%c",IAC,WILL,TELOPT_LINEMODE);
netprintf(tw->pnum,"%c%c%c%c",
IAC,SB,TELOPT_LINEMODE,SLC,0,SLC_DEFAULT,0,IAC,SE);
if(tw->condebug>0) {
tprintf(cv,"SEND: %s %s\r\n",
telstates[WILL - TELCMD_FIRST],
teloptions[TELOPT_LINEMODE]);
tprintf(cv,
"SEND: SB LINEMODE SLC 0 SLC_DEFAULT 0 IAC SE\r\n");
}
break;
#endif
case TELOPT_NAWS:
con->bResizeable=TRUE;
send_naws(con);
break;
case TELOPT_AUTHENTICATION:
wsprintf(buf, "%c%c%c", IAC, WILL, TELOPT_AUTHENTICATION);
TelnetSend(ks, buf, lstrlen(buf), 0);
#ifdef NEGOTIATEDEBUG
wsprintf(strTmp,"SEND: %s %s\r\n",
telstates[WILL - TELCMD_FIRST],
teloptions[TELOPT_AUTHENTICATION]);
OutputDebugString(strTmp);
#endif
break;
#ifdef ENCRYPTION
case TELOPT_ENCRYPT:
wsprintf(buf, "%c%c%c", IAC,
(encrypt_flag ? WILL : WONT), TELOPT_ENCRYPT);
TelnetSend(ks, buf, lstrlen(buf), 0);
#ifdef NEGOTIATEDEBUG
wsprintf(strTmp,"SEND: %s %s\r\n",
telstates[(encrypt_flag ? WILL : WONT)
- TELCMD_FIRST],
teloptions[TELOPT_ENCRYPT]);
OutputDebugString(strTmp);
#endif
break;
#endif
default:
wsprintf(buf, "%c%c%c", IAC, WONT, *st);
TelnetSend(ks, buf, lstrlen(buf), 0);
#ifdef NEGOTIATEDEBUG
wsprintf(strTmp,"SEND: %s %s\r\n",
telstates[WONT - TELCMD_FIRST], teloptions[*st]);
OutputDebugString(strTmp);
#endif
break;
}
con->telstate = STNORM;
orig = ++st;
break;
case DONT:
switch (*st) {
case TELOPT_NAWS:
con->bResizeable=FALSE;
#ifdef NEGOTIATEDEBUG
wsprintf(strTmp,"RECV: %s %s\r\n",
telstates[con->telstate-TELCMD_FIRST],teloptions[*st]);
OutputDebugString(strTmp);
#endif
break;
#ifdef NOT
case BINARY:
if(tw->ibinary) {
if(!tw->iwantbinary) {
netprintf(tw->pnum,"%c%c%c",IAC,WONT,BINARY);
if(tw->condebug>0)
tprintf(cv,"SEND: %s %s\r\n",
telstates[WONT-TELCMD_FIRST],
teloptions[BINARY]);
}
else
tw->iwantbinary=0;
tw->ibinary=0;
tw->mapoutput=0;
}
#ifdef NEGOTIATEDEBUG
wsprintf(strTmp,"NO REPLY NEEDED: %s %s\r\n",
telstates[WONT-TELCMD_FIRST],
teloptions[BINARY]);
OutputDebugString(strTmp);
#endif
break;
#endif
#ifdef ENCRYPTION
case ENCRYPTION:
break;
#endif
}
con->telstate=STNORM;
orig=++st;
break;
case WILL:
#ifdef NEGOTIATEDEBUG
wsprintf(strTmp,"RECV: %s %s\r\n",
telstates[con->telstate-TELCMD_FIRST],
teloptions[*st]);
OutputDebugString(strTmp);
#endif
switch(*st) {
#ifdef NOT
case TELOPT_BINARY:
if(!tw->ubinary) {
if(!tw->uwantbinary) {
netprintf(tw->pnum,"%c%c%c",
IAC,DO,TELOPT_BINARY);
if(tw->condebug>0)
tprintf(cv,"SEND: %s %s\r\n",
telstates[DO - TELCMD_FIRST],
teloptions[TELOPT_BINARY]);
}
else
tw->uwantbinary=0;
tw->ubinary=1;
}
else {
if(tw->condebug>0)
tprintf(cv,"NO REPLY NEEDED: %s %s\r\n",
telstates[DO - TELCMD_FIRST],
teloptions[TELOPT_BINARY]);
}
break;
#endif
case TELOPT_SGA:
if(!con->ugoahead) {
con->ugoahead=1;
wsprintf(buf,"%c%c%c",IAC,DO,TELOPT_SGA);
TelnetSend(ks,buf,lstrlen(buf),0);
#ifdef NEGOTIATEDEBUG
wsprintf(strTmp,"SEND: %s %s\r\n",
telstates[DO - TELCMD_FIRST],
teloptions[TELOPT_SGA]);
OutputDebugString(strTmp);
#endif
}
break;
case TELOPT_ECHO:
if(!con->echo) {
con->echo = 1;
wsprintf(buf, "%c%c%c", IAC, DO, TELOPT_ECHO);
TelnetSend(ks, buf, lstrlen(buf), 0);
#ifdef NEGOTIATEDEBUG
wsprintf(strTmp,"SEND: %s %s\r\n",
telstates[DO - TELCMD_FIRST],
teloptions[TELOPT_ECHO]);
OutputDebugString(strTmp);
#endif
}
break;
case TELOPT_TM:
con->timing=0;
break;
#ifdef ENCRYPTION
case TELOPT_ENCRYPT:
wsprintf(buf, "%c%c%c", IAC,
(encrypt_flag ? DO : DONT), TELOPT_ENCRYPT);
TelnetSend(ks, buf, lstrlen(buf), 0);
if (encrypt_flag)
encrypt_send_support();
#ifdef NEGOTIATEDEBUG
wsprintf(strTmp,"SEND: %s %s\r\n",
telstates[(encrypt_flag ? DO : DONT) - TELCMD_FIRST],
teloptions[TELOPT_ENCRYPT]);
OutputDebugString(strTmp);
#endif
break;
#endif
default:
wsprintf(buf,"%c%c%c",IAC,DONT,*st);
TelnetSend(ks,buf,lstrlen(buf),0);
#ifdef NEGOTIATEDEBUG
wsprintf(strTmp,"SEND: %s %s\r\n",
telstates[DONT-TELCMD_FIRST],teloptions[*st]);
OutputDebugString(strTmp);
#endif
break;
}
con->telstate=STNORM;
orig=++st;
break;
case WONT:
#ifdef NEGOTIATEDEBUG
wsprintf(strTmp,"RECV: %s %s\r\n",
telstates[con->telstate-TELCMD_FIRST],teloptions[*st]);
OutputDebugString((LPSTR)strTmp);
#endif
con->telstate=STNORM;
switch(*st++) {
#ifdef NOT
case BINARY:
if(tw->ubinary) {
if(!tw->uwantbinary) {
netprintf(tw->pnum,"%c%c%c",
IAC,DONT,BINARY);
if(tw->condebug>0)
tprintf(cv,"SEND: %s %s\r\n",
telstates[DONT-TELCMD_FIRST],
teloptions[BINARY]);
}
else
tw->uwantbinary=0;
tw->ubinary=0;
tw->mapoutput=0;
}
else {
if(tw->condebug>0)
tprintf(cv,"NO REPLY NEEDED: %s %s\r\n",
telstates[DONT-TELCMD_FIRST],
teloptions[BINARY]);
}
break;
#endif
case TELOPT_ECHO:
if(con->echo) {
con->echo=0;
wsprintf(buf,"%c%c%c",IAC,DONT,TELOPT_ECHO);
TelnetSend(ks,buf,lstrlen(buf),0);
#ifdef NEGOTIATEDEBUG
wsprintf(strTmp,"SEND: %s %s\r\n",
telstates[DONT-TELCMD_FIRST],
teloptions[TELOPT_ECHO]);
OutputDebugString(strTmp);
OutputDebugString("Other side won't echo!");
#endif
}
break;
case TELOPT_TM:
con->timing=0;
break;
#ifdef ENCRYPTION
case TELOPT_ENCRYPT:
break;
#endif
default:
break;
}
orig=st;
break;
case SB:
con->telstate=NEGOTIATE;
orig=st;
end_sub=0;
sub_pos=con->substate=0;
#ifdef OLD_WAY
break;
#endif
case NEGOTIATE:
if(con->substate==0) {
if(*st==IAC) {
if(*(st+1)==IAC) {
st++;
parsedat[sub_pos++]=*st++;
}
else {
end_sub=sub_pos;
con->substate=*st++;
}
}
else
parsedat[sub_pos++]=*st++;
}
else {
con->substate=*st++;
if(con->substate==SE)
parse_subnegotiat(ks,end_sub);
orig=st;
if (encrypt_flag == 2) {
decrypt_ks_hack(orig, mark - orig);
encrypt_flag = 1;
}
con->telstate=STNORM;
}
break;
default:
con->telstate=STNORM;
break;
}
}
if(con->telstate==STNORM && st<mark) {
while(st<mark && *st!=27 && *st!=IAC) {
#if 0
if(!tw->ubinary)
*st&=127;
#endif
st++;
}
#if 0
if(!tw->timing)
parsewrite(tw,orig,st-orig);
#endif
orig=st;
if(st<mark)
switch(*st) {
case IAC:
con->telstate=IACFOUND;
st++;
break;
default:
#ifdef NEGOTIATEDEBUG
wsprintf(buf," strange char>128 0x%x\r\n", *st);
OutputDebugString(buf);
#endif
st++;
break;
}
}
}
}
static void
parse_subnegotiat(kstream ks, int end_sub)
{
char buf[128];
switch(parsedat[0]) {
case TELOPT_TTYPE:
if(parsedat[1]==1) {
wsprintf(buf,"%c%c%c%cvt100%c%c",IAC,SB,TELOPT_TTYPE,
0,IAC,SE);
TelnetSend(ks,(LPSTR)buf,11,0);
#ifdef NEGOTIATEDEBUG
wsprintf(strTmp,"SB TERMINAL-TYPE SEND\r\n"
"SEND: SB TERMINAL-TYPE IS vt100 \r\n len=%d \r\n",
lstrlen((LPSTR)buf));
OutputDebugString(strTmp);
#endif
}
break;
case TELOPT_AUTHENTICATION:
auth_parse(ks, parsedat, end_sub);
break;
#ifdef ENCRYPTION
case TELOPT_ENCRYPT:
if (encrypt_flag)
encrypt_parse(ks, parsedat, end_sub);
break;
#endif
default:
break;
}
}
void
send_naws(CONNECTION *con)
{
unsigned char buf[40];
int len;
wsprintf(buf, "%c%c%c", IAC, SB, TELOPT_NAWS);
len = 3;
buf[len++] = HIBYTE(con->width);
if (buf[len-1] == IAC) buf[len++] = IAC;
buf[len++] = LOBYTE(con->width);
if (buf[len-1] == IAC) buf[len++] = IAC;
buf[len++] = HIBYTE(con->height);
if (buf[len-1] == IAC) buf[len++] = IAC;
buf[len++] = LOBYTE(con->height);
if (buf[len-1] == IAC) buf[len++] = IAC;
buf[len++] = IAC;
buf[len++] = SE;
TelnetSend(con->ks, buf, len, 0);
#ifdef NEGOTIATEDEBUG
wsprintf(buf, "SEND: SB NAWS %d %d %d %d IAC SE\r\n",
HIBYTE(con->width), LOBYTE(con->width),
HIBYTE(con->height), LOBYTE(con->height));
OutputDebugString(buf);
#endif
}