home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Columbia Kermit
/
kermit.zip
/
archives
/
msvp98b1.lzh
/
MSNTND.C
< prev
next >
Wrap
Text File
|
1993-05-14
|
21KB
|
705 lines
/* File MSNTND.C
* Telnet driver
*
* Copyright (C) 1985, 1992, Trustees of Columbia University in the
* City of New York. Permission is granted to any individual or institution
* to use this software as long as it is not sold for profit. This copyright
* notice must be retained. This software may not be included in commercial
* products without written permission of Columbia University.
*
* Written for MS-DOS Kermit by Joe R. Doupnik,
* Utah State University, jrd@cc.usu.edu, jrd@usu.Bitnet,
* and by Frank da Cruz, Columbia Univ., fdc@watsun.cc.columbia.edu.
* With earlier contributions by Erick Engelke of the University of
* Waterloo, Waterloo, Ontario, Canada.
*
* Last edit
* 25 May 1992
*/
#include "msntcp.h"
#include "msnlib.h"
/* TCP/IP Telnet negotiation support code */
#define IAC 255
#define DONT 254
#define DO 253
#define WONT 252
#define WILL 251
#define SB 250
#define BREAK 243
#define SE 240
#define TELOPT_ECHO 1
#define TELOPT_SGA 3
#define TELOPT_STATUS 5
#define TELOPT_TTYPE 24
#define TELOPT_NAWS 31
#define NTELOPTS 24
#define BAPICON 0xa0 /* 3Com BAPI, connect to port */
#define BAPIDISC 0xa1 /* 3Com BAPI, disconnect */
#define BAPIWRITE 0xa4 /* 3Com BAPI, write block */
#define BAPIREAD 0xa5 /* 3Com BAPI, read block */
#define BAPIBRK 0xa6 /* 3Com BAPI, send short break */
#define BAPISTAT 0xa7 /* 3Com BAPI, read status (# chars avail) */
#define BAPIHERE 0xaf /* 3Com BAPI, presence check */
#define BAPIEECM 0xb0 /* 3Com BAPI, enable/disable ECM char */
#define BAPIECM 0xb1 /* 3Com BAPI, trap Enter Command Mode char */
#define BAPISTAT_SUC 0 /* function successful */
#define BAPISTAT_NCW 1 /* no character written */
#define BAPISTAT_NCR 2 /* no character read */
#define BAPISTAT_NSS 3 /* no such session */
#define BAPISTAT_NOS 7 /* session aborted */
#define BAPISTAT_NSF 9 /* no such function */
byte *termtype; /* Telnet, used in negotiations */
static int sgaflg = 0; /* Telnet SGA flag */
static tn_inited = 0; /* Telnet Options initied */
static ttinccnt = 0; /* Telnet count of chars in read socket */
int echo = 1; /* Telnet echo, default on, but hate it */
struct {
word ident;
char *name;
} termarray[]= /* terminal type names, from mssdef.h */
{
{0,"UNKNOWN"}, {1,"H-19"}, {2,"VT52"}, {4,"VT100"},
{8,"VT102"}, {0x10,"VT220"}, {0x20,"VT320"}, {0x40,"TEK4014"},
{0x80,"VIP7809"}, {0x100, "PT200"}, {0xff,"UNKNOWN"}
};
extern byte bapibuf[]; /* buffer of in/out chars, 512 long */
extern int bapireq, bapiret; /* count of chars in/out */
extern int msgcnt; /* count of chars in message buffer */
extern longword my_ip_addr, sin_mask; /* binary of our IP, netmask */
extern longword ipbcast; /* binary IP of broadcast */
extern byte * def_domain; /* default domain string */
longword host; /* binary IP of host */
word sock_delay = 30;
in_IAC = 0; /* non-zero when doing Telnet Options*/
static tcp_Socket *s, socketdata;
int doslevel = 1; /* operating at DOS level if != 0 */
int ktcpstate = 0; /* remember if started or stopped */
extern byte kmyip[]; /* our IP number */
extern byte knetmask[]; /* our netmask */
extern byte kdomain[]; /* our domain */
extern byte kgateway[]; /* our gateway */
extern byte kns1[]; /* our nameserver #1 */
extern byte kns2[]; /* our nameserver #2 */
extern byte kbcast[]; /* broadcast address pattern */
extern byte khost[]; /* remote host name/IP # */
extern word kport; /* remote host TCP port */
extern word kserver; /* if Kermit is in server mode */
extern word kdebug;
extern byte ktttype[]; /* user term type override string */
extern word kterm; /* terminal type index, from mssdef.h*/
extern byte kterm_lines; /* terminal screen height */
extern byte kterm_cols; /* terminal screen width */
/* this runs at Kermit task level */
int
main() /* start TCP from Kermit */
{
static int status = 0;
byte ch, * p;
int i;
extern longword bootphost;
int arp_add_gateway(byte *data, longword ip);
longword arp_rpt_gateway(int i);
extern int pkt_rarp_init(void);
extern int dobootp();
extern void ntoa(byte * p, longword ip);
if (ktcpstate == 2) /* if have started already */
return (ktcpstate); /* say have started */
doslevel = 1; /* say at DOS level here */
tn_inited = 0; /* Telnet Options initied */
ttinccnt = 0; /* count of chars in read socket */
echo = 1; /* Telnet echo, default on */
kecho(echo); /* update Kermit main body */
msgcnt = 0; /* output queue buffer count */
host = 0L;
def_domain = kdomain; /* define our domain */
msgcnt = 0; /* init msg queue */
in_IAC = 0; /* setup for Options negotiations */
my_ip_addr = 0L;
if (ktttype[0] != '\0') /* if user override given */
termtype = ktttype;
else /* get termtype from real use */
{
for (i = 0; termarray[i].ident != 0xff; i++)
if (termarray[i].ident == kterm) /* match */
break;
termtype = termarray[i].name;
}
s = NULL; /* no TCP socket yet */
if (tcp_init() == 0) /* init TCP code */
{
outs("\r\n Unable to initialize TCP/IP system, quitting");
goto anyerr;
}
sin_mask = resolve(knetmask);
if (sin_mask == 0L)
{ /* something drastically wrong */
outs("\r\n Bad network submask, terminating");
goto anyerr;
}
/* set Ethernet broadcast to all 1's or all 0's */
ipbcast = resolve(kbcast); /* IP broadcast address */
bootphost = ipbcast; /* set Bootp to this IP too */
if ((p = strchr(kmyip, ' ')) != NULL) /* have a space */
*p = '\0'; /* terminate on the space */
if (stricmp(kmyip, "bootp") == 0)
{
if (dobootp() != 0) /* use BOOTP for Internet address */
{
outs("\r\n BOOTP query failed. Quitting.");
goto anyerr;
}
else
{
kmyip[0] = '\0';
ntoa(&kmyip[strlen(kmyip)], my_ip_addr);
ntoa(knetmask, sin_mask);
ntoa(kgateway, arp_rpt_gateway(0));
ntoa(kns1, def_nameservers[0]);
ntoa(kns2, def_nameservers[1]);
readback(); /* readback these to Kermit main */
}
}
else
{
if (stricmp(kmyip, "rarp") == 0)
{
my_ip_addr = 0L; /* clear our IP for RARP */
if (pkt_rarp_init() == 0)
goto anyerr; /* no RARP handle */
if (do_rarp() == 0) /* use RARP */
{
outs("\r\n RARP query failed.");
goto anyerr;
}
else
{
kmyip[0] = '\0';
ntoa(&kmyip[strlen(kmyip)], my_ip_addr);
readback();
}
}
else /* convert to 32 bit binary */
my_ip_addr = resolve(kmyip);
}
if (my_ip_addr == 0L)
{ /* something drastically wrong */
outs("\r\n Cannot understand my IP address, terminating");
goto anyerr;
}
if (stricmp(kns1, "unknown"))
add_server(&last_nameserver, MAX_NAMESERVERS, def_nameservers,
resolve(kns1));
if (stricmp(kns2, "unknown"))
add_server(&last_nameserver, MAX_NAMESERVERS, def_nameservers,
resolve(kns2));
if (stricmp(kgateway, "unknown"))
arp_add_gateway(kgateway, 0L);
s = &socketdata;
/* kserver is set by main body SERVER command. If khost[0] is '*'
then also behave as a server/listener for any main body mode. */
if ((khost[0] == '*') || (kserver != 0))
{
host = 0L; /* force no host IP at this stage */
tcp_listen(s, kport, host, 0, NULL, 0); /* post a listen */
}
else /* normal client mode */
{
outs("\r\n Resolving address of host ");
outs(khost); outs(" ...");
if ((host = resolve(khost)) == 0)
{
outs( "\r\n Cannot resolve address of host ");
outs(khost);
goto anyerr;
}
outs("\r\n"); /* get clean screen line */
}
if (host == my_ip_addr)
{
outs("\r\n Cannot talk to myself, sorry.");
sock_wait_established( s, 0, NULL, &status );
goto anyerr;
}
if (hookvect() == 0)
{
doslevel = 1; /* at DOS level i/o */
outs("\r\n Hooking vectors failed");
if (s != NULL)
{
sock_close(s);
sock_wait_closed(s, sock_delay, NULL, &status);
}
goto anyerr;
}
if ((khost[0] == '*') || (kserver != 0)) /* if server */
{
doslevel = 0; /* end of DOS level i/o */
if (kserver == 0)
outs("\r\n Operating as a Telnet server. Waiting...");
}
else /* we are a client */
{
if (tcp_open(s, 0, host, kport, NULL) == 0)
{
outs("\r\n Unable to contact the host.");
outs("\r\n The host may be down or");
outs(" a gateway may be needed.");
goto anyerr;
}
sock_wait_established(s, sock_delay, NULL, &status);
}
if (sock_mode(s, TCP_MODE_NAGLE) == 0)
{
outs("\r\n Unable to set socket mode");
goto anyerr;
}
doslevel = 0; /* end of DOS level i/o */
return (ktcpstate = 2); /* say we are active */
sock_err:
switch (status)
{
case 1 : outs("\r\n Session is closed");
break;
case -1: outs("\r\n Cannot start a connection");
break;
}
anyerr: sock_close(s);
kecho(echo = 0);
tcp_shutdown();
eth_release(); /* do absolutely */
unhookvect();
doslevel = 1; /* at DOS level i/o */
return (ktcpstate = 1); /* say have stopped */
}
/* this runs at Kermit task level */
int
exit(value) /* stop TCP from Kermit */
int value;
{
doslevel = 1; /* tell i/o routines this is DOS */
sock_close(s); /* graceful way */
kecho(echo = 0);
tcp_shutdown(); /* force the issue if necessary */
eth_release(); /* do absolutely */
unhookvect();
return (ktcpstate = 1); /* state is down */
}
/* This is called by the main body of Kermit to transfer data. It returns
the transfer status as a BAPI valued int. */
serial_handler(cmd)
unsigned int cmd;
{
int i, status, cmdstatus;
register int ch;
register byte * bp;
word ttinc();
extern int sock_findch(tcp_Socket *, byte);
if (tn_inited == 0) /* if not initialized yet */
{
tn_inited = 1; /* say we are doing this */
if (tn_ini() == -1) /* init Telnet negotiations */
return (BAPISTAT_NOS); /* fatal error, quit */
}
tcp_tick(s); /* catch up on packet reading */
cmdstatus = BAPISTAT_SUC; /* success so far */
switch (cmd) /* cmd is function code */
{
case BAPIWRITE: /* write a block, bapireq chars */
if (khost[0] == '*' && s->state != tcp_StateESTAB)
{
bapiret = bapireq; /* discard output and */
break; /* send nothing until client appears*/
}
if (echo)
if (sock_flushnext(s) == 0) /* send next now */
cmdstatus = BAPISTAT_NOS; /* no sess*/
bapiret = sock_write(s, bapibuf, bapireq);
/* if terminal serving with no local echoing do echo here */
if (echo == 0 && khost[0] == '*' && kserver == 0)
outsn(bapibuf, bapiret); /* echo to us */
break;
case BAPIREAD: /* read block, count of bapireq */
i = sock_findch(s, IAC); /* is IAC present? */
if (i < 0)
{
cmdstatus = BAPISTAT_NCR;/* nothing present */
break;
}
if (i > 0 && in_IAC == 0) /* read up to IAC */
{
if (i > bapireq) i = bapireq;
bapiret = sock_fastread(s, bapibuf, i);
/* if terminal serving with local echoing then echo to host */
if (echo != 0 && khost[0] == '*' &&
kserver == 0)
sock_write(s, bapibuf, bapiret);
break;
}
bp = bapibuf; /* use slow reading for Options */
ttinccnt = 0; /* setup to read from buffer */
/* state is here, IAC style, vs fast mode */
in_IAC = 1;
for (i = 0; i < bapireq; i++)
{
if ((ch = ttinc()) == -1)
break; /* no char */
ch &= 0xff;
if ( ch == IAC ) /* Telnet options intro */
{ /* do Options */
if ((ch = ttinc()) == -1)
break; /* do more later */
if ((ch &= 0xff) != IAC)
{ /* IAC IAC means IAC */
tn_doop(ch); /* negotiate */
continue;
}
}
in_IAC = 0; /* end of mode */
*bp++ = ch & 0xff;
bapiret++;
}
break;
case BAPIBRK: /* send BREAK */
sock_putc(s, IAC); /* char to socket buffer */
sock_putc(s, BREAK);
if (sock_flush(s) == 0) /* send it now, with push */
cmdstatus = BAPISTAT_NOS; /* no session */
break;
case BAPISTAT: /* check read status (chars avail) */
bapiret = sock_dataready(s); /* # chars available */
break;
default: cmdstatus = BAPISTAT_NSF;/* unsupported function */
break;
}
if ((sock_dataready(s) == 0) && (msgcnt == 0) &&
(s->state == tcp_StateCLOSED)) /* no data and no session */
return (BAPISTAT_NOS); /* means exit TCP */
else
return (cmdstatus); /* stuff is yet unread */
}
/* ttinc - destructively read char from socket buffer, return -1 if fail */
word
ttinc(void)
{
byte ch;
if (ttinccnt <= 0) /* if no known chars */
{
tcp_tick(s); /* read another packet */
ttinccnt = sock_dataready(s); /* count available, if any */
}
if (sock_fastread(s, &ch, 1) != 0) /* qty chars returned */
{
ttinccnt--; /* one less char in socket */
return (0xff & ch);
}
return (-1);
}
/* Initialize a telnet connection */
/* Returns -1 on error, 0 is ok */
int
tn_ini(void)
{
sgaflg = 0; /* SGA flag starts out this way */
if (khost[0] == '*' || kserver != 0 ||
kport != 23) /* if not server or not Telnet port */
return (0); /* don't go first */
if (send_iac( WILL, TELOPT_TTYPE )) return( -1 );
if (send_iac( WILL, TELOPT_NAWS)) return(-1);
if (send_iac( DO, TELOPT_SGA )) return( -1 );
return(0);
}
/*
* send_iac - send interupt character and pertanent stuff
* - return 0 on success
*/
send_iac(cmd, opt)
byte cmd, opt;
{
byte io_data[3];
io_data[0] = IAC;
io_data[1] = cmd;
io_data[2] = opt;
sock_fastwrite(s, io_data, 3);
return(!tcp_tick(s));
}
/*
* Process in-band Telnet negotiation characters from the remote host.
* Call with the telnet IAC character and the current duplex setting
* (0 = remote echo, 1 = local echo).
* Returns:
* -1 on success or char 0x255 (IAC) when IAC is the first char read here.
*/
#define TSBUFSIZ 41
byte sb[TSBUFSIZ]; /* Buffer for subnegotiations */
int
tn_doop(ch)
word ch;
{ /* enter after reading IAC char */
register int c, x;
word y, n, flag, ttinc();
if (ch < SB) return(0); /* ch is not in range of Options */
if ((x = ttinc()) == -1) /* read Option character */
return (-1); /* nothing there */
x &= 0xff;
c = ch; /* use register'd character */
switch (x) {
case TELOPT_ECHO: /* ECHO negotiation */
if (c == WILL) /* Host says it will echo */
{
if (echo != 0) /* reply only if change required */
{
send_iac(DO,x); /* Please do */
kecho(echo = 0); /* echo is from the other side */
}
break;
}
if (c == WONT) /* Host says it won't echo */
{
if (echo == 0) /* If we not echoing now */
send_iac(DONT,x); /* agree to no host echo */
break;
}
if (c == DO)
{ /* Host wants me to echo to it */
send_iac(WONT,x); /* I say I won't */
break;
}
break; /* do not respond to DONT */
case TELOPT_SGA: /* Suppress Go-Ahead */
if (c == WONT) /* Host says it won't sup go-aheads */
{
if (sgaflg == 0)
send_iac(DONT, x); /* acknowledge */
sgaflg = 1; /* suppress, remember */
if (echo == 0) /* if we're not echoing, */
kecho(echo = 1); /* switch to local echo */
break;
}
if (c == WILL) /* Host says it will use go aheads */
{
if (sgaflg != 0) /* if suppressed */
{
sgaflg = 0; /* do go-aheads, remember */
send_iac(DO,x); /* this is a change, so ACK */
}
break;
}
break; /* no response to other cases */
case TELOPT_TTYPE: /* Terminal Type */
switch (c) {
case DO: /* DO terminal type */
send_iac(WILL, x); /* say I'll send it if asked */
break;
case SB: /* enter subnegotiations */
n = flag = 0; /* flag for when done reading SB */
while (n < TSBUFSIZ)
{ /* loop looking for IAC SE */
if ((y = ttinc()) == -1)
break; /* nothing there */
y &= 0xff; /* make sure it's just 8 bits */
sb[n++] = y; /* save what we got in buffer */
if (y == IAC) /* If this is an IAC */
{
if (flag) /* If previous char was IAC */
{
n--; /* it's quoted, keep one IAC */
flag = 0; /* and turn off the flag. */
}
else flag = 1; /* Otherwise set the flag. */
}
else if (flag) /* Something else following IAC */
{
if (y != SE) /* If not SE, it's a protocol error */
flag = 0;
break;
} /* end of if (y == IAC) */
} /* end while */
if (flag == 0 || y == -1) /* no terminal type IAC SE */
break; /* flag == 0 is invalid SB */
if ( *sb == 1 ) /* wants us to report terminal type */
tn_sttyp(); /* do so */
break;
default: /* ignore other TTYPE Options */
break;
} /* end of inner switch (c) */
break;
case TELOPT_NAWS: /* terminal width and height */
switch (c)
{
case DO: tn_snaws(); /* host wants report, send it */
default: break; /* other actions, discard */
}
break;
default: /* all other Options: refuse nicely */
switch(c) {
case WILL: /* You will? */
send_iac(DONT,x); /* Please don't */
break;
case DO: /* You want me to? */
send_iac(WONT,x); /* I won't */
send_iac(DONT,x); /* Don't you either */
break;
case DONT:
send_iac(WONT,x); /* I won't */
break;
case WONT: /* You won't? */
break; /* Good */
default:
break; /* unknown character, discard */
} /* end of default switch (c) */
break;
} /* end switch (x) */
return (-1); /* say done with Telnet Options */
}
/* Telnet send terminal type */
/* Returns -1 on error, 0 on success */
int
tn_sttyp()
{ /* Send telnet terminal type. */
register byte *ttn;
register int ttnl; /* Name & length of terminal type. */
ttn = termtype; /* we already got this from environment */
if ((*ttn == 0) || ((ttnl = strlen(ttn)) >= TSBUFSIZ)) {
ttn = "UNKNOWN";
ttnl = 7;
}
ttn = strcpy(&sb[1], ttn); /* Copy to subnegotiation buffer */
while (*ttn != NULL)
{
if (*ttn >= 'a' && *ttn <= 'z') *ttn += (- 'a' + 'A');
ttn++;
}
*sb = 0; /* 'is'... */
*ttn++ = IAC;
*ttn = SE;
send_iac((byte)SB, TELOPT_TTYPE); /* Send: Terminal Type */
sock_flushnext(s); /* send on next write */
sock_fastwrite(s, sb, ttnl + 3);
return (0);
}
/* Send terminal width and height (characters). RFC 1073 */
int
tn_snaws()
{
static char sbuf[6] = {0,0, 0,0, IAC, SE};
sbuf[1] = kterm_cols;
sbuf[3] = kterm_lines;
send_iac((byte)SB, TELOPT_NAWS); /* Send: Terminal size */
sock_flushnext(s); /* send on next write */
sock_fastwrite(s, sbuf, 6);
return (0);
}
/* Compose a nice greeting message for incoming Telnet connections. Called
by tcp_handler() in the tcp_StateLISTEN section. It also notifies the
local terminal emulator of the client's presence and address. */
void
server_hello(tcp_Socket *s)
{
char hellomsg[256]; /* work buffer, keep short */
register int len;
strcpy(hellomsg,
"\r\n Welcome to the MS-DOS Kermit Telnet server at [");
ntoa(&hellomsg[strlen(hellomsg)], my_ip_addr); /* our IP */
strcat(hellomsg, "].\r\n"); /* as [dotted decimal] */
if (kserver != 0) /* if file serving */
{
strcat(hellomsg," Escape back to your Kermit prompt and");
strcat(hellomsg," issue Kermit file server commands.\r\n\n");
}
else /* if terminal emulating */
{
strcat(hellomsg,
" You are talking to the terminal emulator,\r\n");
strcat(hellomsg, " adjust local echoing accordingly.\r\n");
}
/* stuff string in socket buffer, adjust socket data length */
bcopy(hellomsg, &s->data[s->datalen], len = strlen(hellomsg));
s->datalen += len;
/* tell main body the news */
strcpy(hellomsg, "\r\n Connection starting from [");
ntoa(&hellomsg[strlen(hellomsg)], s->hisaddr); /* their IP */
strcat(hellomsg, "].\r\n");
outs(hellomsg); /* send connection info to main body */
}