P8000/WEGA/contrib/K5JB/telunix.c

414 lines
9.2 KiB
C

/* This version of telunix.c is the one that should be used with Unix
* systems that have normal ptys on them. My AT&T 3B2 has brain-damaged
* pty driver so I had to booger telunix.c to get it to work. Often I
* distribute the NET package with the wrong telunix.c in it. If this
* is the case, replace the telunix.c in the package with this one.
*
* This version of telunix.c will work in Coherent. Set up ttyp0, ttyp1
* and ttyp2 in /etc/ttys (I used 1lPttyp0, for example), do a kill quit 1,
* and use the start telunix command.
* Users can be provided with a .profile that does stty -echo. They will
* have to use "eol unix" or they will get double prompts. If you start
* telunix with the default port (513), a user would do telnet yourhostname 513
* to start the session. If you don't use telnet, you can instead start
* telunix 23 (telnet port) so any incoming telnet session gets the logon.
* Thanks to VE5BC who heckled me into fixing this. - K5JB
*/
#include "unixopt.h"
#ifdef TELUNIX /* rest of module */
#include <stdio.h>
#include "global.h"
#include "mbuf.h"
#include "timer.h"
#include "icmp.h"
#include "netuser.h"
#include "tcp.h"
#include "telnet.h"
#include "session.h"
#include <errno.h>
#include <fcntl.h> /* one uploaded to ucsd.edu 2/8/92 used sys/fcntl.h */
#define TUMAXSCAN 3 /* max number of telunix clients active */
#define TURQSIZ 512 /* max data we will request from pty at once */
struct tcb *tnix_tcb = NULLTCB;
struct tcb *tnixtcb[TUMAXSCAN]; /* savebuf for tcb ptrs for scan routines */
int /* moved here from sys5_io.c */
OpenPty()
{
int pty;
int letcnt=0, numcnt=0;
/* we will sort these out after I find out what is common - K5JB */
#ifdef COH386
static char *letters = "p", *numbers = "0123456789";
static char master[] = "/dev/ptyXX";
#else
static char *letters = "pqrs", *numbers = "0123456789abcdef";
#ifdef hpux
static char master[] = "/dev/ptym/ptyXX";
#else
static char master[] = "/dev/ptyXX";
#endif
#endif
static int letmax, nummax;
letmax=strlen(letters)-1, nummax=strlen(numbers)-1;
do {
#ifdef hpux
master[strlen("/dev/ptym/pty")] = letters[letcnt];
master[strlen("/dev/ptym/ptyX")] = numbers[numcnt];
#else
master[strlen("/dev/pty")] = letters[letcnt];
master[strlen("/dev/ptyX")] = numbers[numcnt];
#endif
if (letcnt > letmax) {
return -1;
} else if (++numcnt > nummax) {
letcnt++;
numcnt = 0;
}
} while ((pty=open(master, O_RDWR | O_NDELAY)) < 0);
return(pty);
}
/* Start telnet-unix server */
int
tnix1(argc,argv)
char *argv[];
{
struct socket lsocket;
extern int32 ip_addr;
void tnix_state();
int fd;
/* test for presence of ptys */
if((fd = OpenPty()) == -1)
return -1;
close(fd);
lsocket.address = ip_addr;
if(argc < 2)
lsocket.port = LOGIN_PORT;
else
lsocket.port = atoi(argv[1]);
tnix_tcb = open_tcp(&lsocket,NULLSOCK,TCP_SERVER,0,
NULLVFP,NULLVFP,tnix_state,0,NULLCHAR);
if(tnix_tcb == NULLTCB){
fprintf(stderr,"start telunix fails rsn %d.\n",net_error);
return(-1);
}else
log(tnix_tcb,"STARTED Telunix - (%d %x)", lsocket.port,tnix_tcb);
return 0;
}
/* Shut down Telnet server */
int
tnix0()
{
if(tnix_tcb != NULLTCB) {
log(tnix_tcb,"STOPPED Telunix - (%x)",tnix_tcb);
close_tcp(tnix_tcb);
return(0);
} else
return -1;
}
/* Handle incoming Telnet-Unix connect requests */
static void
tnix_state(tcb,old,new)
struct tcb *tcb;
char old,new;
{
register struct telnet *tn;
int tnix_addscan();
extern void tnix_rmvscan(),sndmsg();
extern int send_tcp();
extern struct mbuf *free_p();
extern int del_tcp();
extern int close_tcp();
extern void free();
tn = (struct telnet *)tcb->user;
switch(new){
case ESTABLISHED:
/* Create and initialize a Telnet protocol descriptor */
if((tn = (struct telnet *)calloc(1,sizeof(struct telnet))) == NULLTN){
log(tcb,"reject Telunix - no space");
sndmsg(tcb,"Rejected; no space on remote\015\012");
close_tcp(tcb);
return;
}
tn->session = NULLSESSION;
tn->state = TS_DATA;
tcb->user = (char *)tn; /* Upward pointer */
tn->tcb = tcb; /* Downward pointer */
tn->inbuf = NULLBUF;
tn->outbuf = NULLBUF;
if(tnix_addscan(tcb) < 0 ||
(tn->fd = OpenPty()) < 3) { /* barf if <= stderr */
tnix_rmvscan(tcb);
log(tcb,"reject Telunix - no Unix ports");
sndmsg(tcb,
"Rejected; no ports available on remote\015\012");
close_tcp(tcb);
return;
}
log(tcb,"open Telunix - (%d %x %d %d)",tn->fd,tcb,old,new);
break;
case FINWAIT1:
case FINWAIT2:
case CLOSING:
case LAST_ACK:
case TIME_WAIT:
if(tn != NULLTN &&
tn->fd > 2) {
log(tcb,"close Telunix - (%d %x %d %d)",
tn->fd,tcb,old,new);
close(tn->fd);
tn->fd = 0;
}
tnix_rmvscan(tcb);
break;
case CLOSE_WAIT:
/* flush that last buffer */
if(tn != NULLTN &&
tn->outbuf != NULLBUF &&
tn->outbuf->cnt != 0) {
send_tcp(tcb,tn->outbuf);
tn->outbuf = NULLBUF;
}
close_tcp(tcb);
break;
case CLOSED:
if(tn != NULLTN) {
if(tn->fd > 2) {
log(tcb,"close Telunix - (%d %x %d %d)",
tn->fd,tcb,old,new);
close(tn->fd);
tn->fd = 0;
}
if(tn->inbuf != NULLBUF)
free_p(tn->inbuf);
if(tn->outbuf != NULLBUF)
free_p(tn->outbuf);
free((char *)tn);
}
tnix_rmvscan(tcb);
del_tcp(tcb);
if(tcb == tnix_tcb)
tnix_tcb = NULLTCB;
break;
}
}
/* Telunix io interface. Called periodically to process any waiting io. */
void
tnix_try(tcb)
register struct tcb *tcb;
{
extern void tnix_rmvscan();
extern int recv_tcp();
extern int send_tcp();
extern void tnix_input();
register struct telnet *tn;
register int i;
if((tn = (struct telnet *)tcb->user) == NULLTN || tn->fd < 3) {
/* Unknown connection - remove it from queue */
log(tcb,"error Telnet - tnix_try (%d)", tn);
tnix_rmvscan(tcb);
return;
}
/*
* First, check if there is any pending io for the pty:
*/
if(tn->inbuf != NULLBUF)
tnix_input(tn);
if(tn->inbuf == NULLBUF)
if(tcb->rcvcnt > 0 && recv_tcp(tcb,&(tn->inbuf),0) > 0)
tnix_input(tn);
/*
* Next, check if there is any io for tcp:
*/
i = 0;
do{
if(tn->outbuf == NULLBUF &&
(tn->outbuf = alloc_mbuf(TURQSIZ)) == NULLBUF)
break; /* can't do much without a buffer */
if(tn->outbuf->cnt < TURQSIZ) {
if((i = read(tn->fd, tn->outbuf->data + tn->outbuf->cnt,
TURQSIZ - (int)tn->outbuf->cnt)) == -1) {
#ifdef COH386
/* Coherent read() returns -1 on no data available */
i = 0;
#endif
if(errno != EAGAIN){
log(tcb,"error Telunix - read (%d %d %d)",
errno, tn->fd, TURQSIZ - tn->outbuf->cnt);
close_tcp(tcb);
break;
}
}
if((tn->outbuf->cnt += i) < TURQSIZ)
i = 0; /* didn't fill buffer so don't retry */
}
if(tn->outbuf->cnt == 0 || /* nothing to send */
tcb->sndcnt > tcb->window) /* too congested to send */
break;
if(send_tcp(tcb,tn->outbuf) < 0) {
log(tcb,"error Telunix - send_tcp (%d %d %d)",
net_error, tn->fd, tn->outbuf->cnt);
close_tcp(tcb);
tn->outbuf = NULLBUF;
break;
}
tn->outbuf = NULLBUF;
} while(i);
}
/* Process incoming TELNET characters */
void
tnix_input(tn)
register struct telnet *tn;
{
void doopt(),dontopt(),willopt(),wontopt(),answer();
char *memchr();
register int i;
register struct mbuf *bp;
char c;
bp = tn->inbuf;
/* Optimization for very common special case -- no special chars */
if(tn->state == TS_DATA){
while(bp != NULLBUF &&
memchr(bp->data,IAC,(int)bp->cnt) == NULLCHAR) {
if((i = write(tn->fd, bp->data, (int)bp->cnt)) == bp->cnt) {
tn->inbuf = bp = free_mbuf(bp);
} else if(i == -1) {
log(tn->tcb,"error Telunix - write (%d %d %d)",
errno, tn->fd, bp->cnt);
close_tcp(tn->tcb);
return;
} else {
bp->cnt -= i;
bp->data += i;
return;
}
}
if(bp == NULLBUF)
return;
}
while(pullup(&(tn->inbuf),&c,1) == 1){
bp = tn->inbuf;
switch(tn->state){
case TS_DATA:
if(uchar(c) == IAC){
tn->state = TS_IAC;
} else {
if(!tn->remote[TN_TRANSMIT_BINARY])
c &= 0x7f;
if(write(tn->fd, &c, 1) != 1) {
/* we drop a character here */
return;
}
}
break;
case TS_IAC:
switch(uchar(c)){
case WILL:
tn->state = TS_WILL;
break;
case WONT:
tn->state = TS_WONT;
break;
case DO:
tn->state = TS_DO;
break;
case DONT:
tn->state = TS_DONT;
break;
case IAC:
if(write(tn->fd, &c, 1) != 1) {
/* we drop a character here */
return;
}
tn->state = TS_DATA;
break;
default:
tn->state = TS_DATA;
break;
}
break;
case TS_WILL:
willopt(tn,c);
tn->state = TS_DATA;
break;
case TS_WONT:
wontopt(tn,c);
tn->state = TS_DATA;
break;
case TS_DO:
doopt(tn,c);
tn->state = TS_DATA;
break;
case TS_DONT:
dontopt(tn,c);
tn->state = TS_DATA;
break;
}
}
}
/* called periodically from main loop */
void
tnix_scan()
{
void tnix_try();
register int i;
for(i = 0; i < TUMAXSCAN; i += 1)
if(tnixtcb[i] != NULLTCB)
tnix_try(tnixtcb[i]);
}
int
tnix_addscan(tcb)
struct tcb *tcb;
{
register int i;
for(i = 0; i < TUMAXSCAN; i += 1)
if(tnixtcb[i] == NULLTCB) {
tnixtcb[i] = tcb;
return i;
}
return -1;
}
void
tnix_rmvscan(tcb)
struct tcb *tcb;
{
register int i;
for(i = 0; i < TUMAXSCAN; i += 1)
if(tnixtcb[i] == tcb)
tnixtcb[i] = NULLTCB;
}
#endif /* TELUNIX */