home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
kermit.columbia.edu
/
kermit.columbia.edu.tar
/
kermit.columbia.edu
/
archives
/
msr313src.zip
/
msntnd.c
< prev
next >
Wrap
C/C++ Source or Header
|
1993-07-12
|
28KB
|
969 lines
/* File MSNTND.C
* Telnet driver
*
* Copyright (C) 1985, 1993, 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
* 16 June 1993 v3.13
*/
#include "msntcp.h"
#include "msnlib.h"
#define MAXSESSIONS 6
#define MSGBUFLEN 512
/* 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 BAPIPING 0xb2 /* Send Ping, Kermit extension of BAPI */
#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 */
#define TSBUFSIZ 41
static byte sb[TSBUFSIZ]; /* Buffer for subnegotiations */
static byte *termtype; /* Telnet, used in negotiations */
static int sgaflg = 0; /* Telnet SGA flag */
static int dosga = 0; /* Telnet 1 if I sent DO SGA from tn_ini() */
static int wttflg = 0; /* Telnet Will Termtype flag */
static int wnawsflg = 0; /* Telnet Will NAWS option flag */
static ttinccnt = 0; /* Telnet count of chars in read socket */
static int in_IAC = 0; /* non-zero when doing Telnet Options*/
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"}, {0x200, "D463"},
{0x400, "D470"}, {0xff,"UNKNOWN"}
};
extern byte FAR * bapiadr; /* Far address of local Telnet client's buf */
extern int bapireq, bapiret; /* count of chars in/out */
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 */
extern byte * hostname; /* our name from BootP server */
static longword host; /* binary IP of host */
int doslevel = 1; /* operating at DOS level if != 0 */
word sock_delay = 30;
static tcp_Socket *s; /* ptr to active socket */
int msgcnt; /* count of chars in message buffer */
byte msgbuf[MSGBUFLEN+1]; /* message buffer for client */
extern int hookvect(void);
extern int unhookvect(void);
extern int kecho(int);
extern void readback(void);
int session_close(int);
int session_rotate(int);
int tn_ini(void);
int ttinc(void);
int tn_doop(word);
int send_iac(byte, int);
int tn_sttyp(void);
int tn_snaws(void);
int subnegotiate(void);
void optdebug(byte, int);
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 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 */
extern byte kbtpserver[]; /* IP of Bootp host answering query */
extern byte kdebug; /* non-zero if debug mode is active */
static struct sessioninfo
{
tcp_Socket socketdata; /* current socket storage area */
int sessionid; /* identifying session */
int tn_inited; /* Telnet Options initied */
int echo; /* local echo state (0=remote) */
} session[MAXSESSIONS];
static unsigned num_sessions = 0; /* qty of active sessions */
static int active = -1; /* ident of active session */
/* this runs at Kermit task level */
int
main(void) /* start TCP from Kermit */
{
int status = 0;
register byte *p;
register int i;
doslevel = 1; /* say at DOS level here */
if (num_sessions == 0) /* then initialize the system */
{
my_ip_addr = 0L;
def_domain = kdomain; /* define our domain */
host = 0L; /* BELONGS IN SESSION, but is global */
for (i = 0; i < MAXSESSIONS; i++) /* init sessioninfo array */
session[i].sessionid = -1;
if (hookvect() == 0)
{
outs("\r\n Hooking vectors failed");
goto anyerr;
}
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;
}
/* 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;
}
ntoa(kmyip, my_ip_addr);
if (sin_mask != 0L)
ntoa(knetmask, sin_mask);
if (arp_rpt_gateway(0) != 0L)
ntoa(kgateway, arp_rpt_gateway(0));
if (last_nameserver > 0)
ntoa(kns1, def_nameservers[0]);
if (last_nameserver > 1)
ntoa(kns2, def_nameservers[1]);
if (kdomain[0] == '\0' && hostname[0] != '\0')
/* construct domain */
if (p = strchr(hostname, '.')) /* find dot */
strcpy(kdomain, &p[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;
}
ntoa(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 ((sin_mask = resolve(knetmask)) == 0L)
{ /* something drastically wrong */
outs("\r\n Bad network submask, 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);
} /* end of initial system setup */
/* starting a new session */
if (num_sessions >= MAXSESSIONS)
{
outs("\nAll sessions are in use. Sorry\n");
return (-1); /* say can't do another, fail */
}
status = -1;
for (i = 0; i < MAXSESSIONS; i++) /* find free ident */
if (session[i].sessionid < 0)
{
s = &session[i].socketdata; /* active socket */
session[i].sessionid = i; /* ident */
status = i;
active = i; /* identify active session */
session[i].echo = echo; /* Telnet echo from mainpgm */
session[i].tn_inited = 0; /* do Telnet Options init */
num_sessions++; /* say have another active session */
break;
}
if (status == -1)
goto sock_err; /* report bad news and exit */
status = 0;
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;
}
/* 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, 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.");
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) == 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 (active); /* return handle of this session */
sock_err:
switch (status)
{
case 1 : outs("\r\n Session is closed");
break;
case -1:/* outs("\r\n Cannot start a connection");*/
break;
}
anyerr: session_close(active);
if (num_sessions == 0)
{
tcp_shutdown();
eth_release(); /* do absolutely */
unhookvect();
doslevel = 1; /* at DOS level i/o */
}
return (-1); /* say have stopped */
}
/* this runs at Kermit task level */
int
exit(int value) /* stop TCP from Kermit */
{
int i;
doslevel = 1; /* tell i/o routines this is DOS */
for (i = 0; i < MAXSESSIONS; i++)
session_close(i); /* close session, free buffers */
num_sessions = 0;
tcp_shutdown(); /* force the issue if necessary */
eth_release(); /* do absolutely */
unhookvect();
return (-1);
}
/* return the int of the next available session after session "ident", or
-1 if none. Do not actually change sessions.
*/
int
session_rotate(int ident)
{
register int i, j;
for (i = 1; i <= MAXSESSIONS; i++)
{
j = (i + ident) % MAXSESSIONS; /* modulo maxsessions */
if (session[j].sessionid != -1)
return (j);
}
return (-1);
}
/* Change to session ident "ident" and active to "ident", return "active"
if that session is valid, else return -1.
*/
int
session_change(int ident)
{ /* change active session to ident */
if (ident < 0 || ident >= MAXSESSIONS)
return (-1);
if (session[ident].sessionid < 0)
return (-1); /* inactive session */
s = &session[ident].socketdata; /* watch mid-stream stuff */
kecho(echo = session[ident].echo); /* update Kermit main body */
return (active = ident); /* ident of active session */
}
/* Close session "ident". Return ident of next available session, if any,
without actually changing sessions. Returns -1 if no more sessions or
if ident is out of legal range or if the session is already closed.
*/
int
session_close(int ident) /* close a particular, ident, session */
{
if (ident >= 0 && ident < MAXSESSIONS &&
session[ident].sessionid >= 0) /* graceful close */
{
sock_close(&session[ident].socketdata);
/* free receive and send socket data buffers */
/* this is pushing the tcp timeout a bit much */
free(session[ident].socketdata.rdata);
free(session[ident].socketdata.sdata);
/* clear pointers to same */
session[ident].socketdata.rdata = NULL;
session[ident].socketdata.sdata = NULL;
session[ident].sessionid = -1; /* marked closed */
session[ident].tn_inited = 0; /* not inited */
if (num_sessions > 0) num_sessions--;/* qty active sessions */
}
/* activate, rtn next available session */
return (session_change(session_rotate(ident)));
}
/* This is called by the main body of Kermit to transfer data. It returns
the transfer status as a BAPI valued int. */
int
serial_handler(word cmd)
{
int i, cmdstatus;
register int ch;
byte FAR * bp;
extern int session_change(int);
if (session[active].tn_inited == 0) /* if not initialized yet */
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 (session[active].echo)
if (sock_flushnext(s) == 0) /* send next now */
cmdstatus = BAPISTAT_NOS; /* no sess*/
if (s->state == tcp_StateESTAB)
bapiret = sock_write(s, bapiadr, bapireq);
else
{
cmdstatus = BAPISTAT_NOS; /* no session */
bapiret = bapireq; /* discard data */
break;
}
/* if terminal serving with no local echoing do echo here */
if (session[active].echo == 0 &&
khost[0] == '*' && kserver == 0)
{ /* echo to us */
i = bapiret > MSGBUFLEN-msgcnt?
MSGBUFLEN-msgcnt: bapiret;
bcopyff(bapiadr, msgbuf, i);
outsn(msgbuf, i);
}
break;
case BAPIREAD: /* read block, count of bapireq */
/* is IAC present? */
i = fstchr(s->rdata, s->rdatalen, IAC);
bapiret = 0;
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, bapiadr, i);
/* if terminal serving with local echoing then echo to host */
if (session[active].echo != 0 &&
khost[0] == '*' &&
kserver == 0)
sock_write(s, bapiadr, bapiret);
break;
}
bp = bapiadr; /* 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++ = (byte) (ch & 0xff);
bapiret++;
}
session[active].echo = echo;
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 BAPIPING: /* Ping current host */
do_ping(khost, host);
break;
case BAPISTAT: /* check read status (chars avail) */
bapiret = sock_dataready(s); /* # chars available */
break;
case BAPIDISC: /* close this connection */
session_close(active);
/* fall through to do session rotate */
case BAPIECM:
i = session_rotate(active); /* get new ident */
if (i != -1) /* if exists */
i = session_change(i); /* make active */
bapiret = 0; /* no chars processed */
return (i); /* New session or -1, special */
default: cmdstatus = BAPISTAT_NSF;/* unsupported function */
break;
}
if ((sock_dataready(s) == 0) && (msgcnt == 0) &&
(s->sisopen == SOCKET_CLOSED)) /* no data and no session */
return (BAPISTAT_NOS); /* means exit session */
else
return (cmdstatus); /* stuff is yet unread */
}
/* ttinc - destructively read char from socket buffer, return -1 if fail */
int
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 */
wttflg = 0; /* Did not send WILL TERM TYPE yet. */
wnawsflg = 0; /* Did not send NAWS info yet */
dosga = 0; /* Did not send DO SGA yet. */
in_IAC = 0; /* haven't done IAC reception yet */
ttinccnt = 0; /* count of chars in read socket */
session[active].tn_inited = 1; /* say we are doing this proc */
kecho(echo = 1); /* start with echo to ourselves */
/* if not server or not Telnet port */
if (khost[0] == '*' || kserver != 0 || kport != 23)
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 );
wttflg = 1; /* Remember I offered TTYPE */
wnawsflg = 1; /* Remember I offered NAWS */
dosga = 1; /* Remember I sent DO SGA */
return(0);
}
/*
* send_iac - send interupt character and pertanent stuff
* - return 0 on success
*/
int
send_iac(byte cmd, int opt)
{
byte io_data[3];
io_data[0] = IAC;
io_data[1] = cmd;
io_data[2] = (byte)(opt & 0xff);
if (sock_fastwrite(s, io_data, 3) == 0)
return (1); /* failed to write */
if (kdebug != 0)
{
outs("Opt send ");
optdebug(cmd, opt);
if (cmd != SB) outs("\r\n");
}
tcp_tick(s);
return (0);
}
/*
* 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.
*/
int
tn_doop(word ch)
{ /* enter after reading IAC char */
register int c, x;
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 */
if (kdebug != 0)
{
outs("Opt recv ");
optdebug((byte)c, x);
if (c != SB) outs("\r\n");
}
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 */
kecho(echo = 1); /* do local echoing */
}
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 || !dosga) /* ACK only if necessary */
{
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 */
if (wttflg == 0) { /* If I haven't said so before, */
send_iac(WILL, x); /* say I'll send it if asked */
wttflg++;
}
break;
case SB: /* enter subnegotiations */
if (wttflg == 0)
break; /* we have not been introduced yet */
if (subnegotiate() != 0) /* successful negotiation */
tn_sttyp(); /* report terminal type */
break;
default: /* ignore other TTYPE Options */
break;
} /* end of inner switch (c) */
break;
case TELOPT_NAWS: /* terminal width and height */
switch (c) {
case DO: /* DO terminal type */
if (wnawsflg == 0) { /* If I haven't said so before, */
send_iac(WILL, x); /* say I'll send it if asked */
wnawsflg++;
}
break;
case SB: /* enter subnegotiations */
if (wnawsflg == 0)
break; /* we have not been introduced yet */
if (subnegotiate() != 0) /* successful negotiation */
tn_snaws(); /* report screen size */
break;
default: /* ignore other NAWS Options */
break;
} /* end of inner switch (c) */
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 */
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 */
}
/* Perform Telnet Option subnegotiation. SB byte has been read. Consume
through IAC SE. Return 1 if successful, else 0.
*/
int
subnegotiate(void)
{
register word flag, y;
word n;
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++] = (byte) y; /* save what we got in buffer */
if (kdebug != 0)
{
if (y == SE) outs(" se\r\n");
else
if (y != IAC)
{
if (n == 1 && y == 1)
outs(" send");
else
{
outs(" \\x");
outhex((byte)y);
}
}
}
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 option IAC SE */
return (0); /* flag == 0 is invalid SB */
if ( *sb == 1 ) /* wants us to report option */
return (1); /* say can do report */
else
return (0);
}
/* Telnet send terminal type */
/* Returns -1 on error, 0 on success */
int
tn_sttyp(void)
{ /* 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 += (byte)('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);
if (kdebug != 0)
{
int i;
outs(" "); for (i = 0; i < ttnl; i++) outch(sb[i+1]);
outs(" se\r\n");
}
return (0);
}
/* Send terminal width and height (characters). RFC 1073 */
int
tn_snaws(void)
{
char sbuf[6] = {0,0, 0,0, (char)IAC, (char)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);
if (kdebug != 0)
{
outs(" \\x");
outhex(sbuf[1]);
outs(" \\x");
outhex(sbuf[3]);
outs(" se\r\n");
}
return (0);
}
/* assist displaying of Telnet Options negotiation material */
void
optdebug(byte cmd, int option)
{
switch (cmd)
{
case WILL: outs("will ");
break;
case WONT: outs("wont ");
break;
case DO: outs("do ");
break;
case DONT: outs("dont ");
break;
case SB: outs("sb ");
break;
default: outs("\\x "); outhex(cmd);
break;
} /* end of switch c */
switch (option)
{
case TELOPT_ECHO: outs("echo");
break;
case TELOPT_SGA: outs("sga");
break;
case TELOPT_TTYPE: outs("ttype");
break;
case TELOPT_NAWS: outs("naws");
break;
default: outs("\\x"); outhex((byte)option);
break;
}
}
/* 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[MSGBUFLEN]; /* 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 */
bcopyff(hellomsg, &s->sdata[s->sdatalen],
len = strlen(hellomsg));
s->sdatalen += 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 */
}