#include #include "global.h" #include "config.h" #include "mbuf.h" #include "timer.h" #include "icmp.h" #include "netuser.h" #include "tcp.h" #include "telnet.h" #include "session.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 */ extern int errno; #ifdef SYS5 extern unsigned long selmask; #endif /* Start telnet-unix server */ tnix1(argc,argv) char *argv[]; { struct socket lsocket; extern int32 ip_addr; void tnix_state(); /* Incoming Telnet */ lsocket.address = ip_addr; if(argc < 2) lsocket.port = TELNET_PORT; else lsocket.port = atoi(argv[1]); tnix_tcb = open_tcp(&lsocket,NULLSOCK,TCP_SERVER,0, NULLVFP,NULLVFP,tnix_state,0,(char *)NULL); if(tnix_tcb == NULLTCB) fprintf(stderr,"start telunix fails rsn %d.\n",net_error); else log(tnix_tcb,"STARTED Telunix - (%d %x)", lsocket.port,tnix_tcb); } /* Shut down Telnet server */ tnix0() { if(tnix_tcb != NULLTCB) { log(tnix_tcb,"STOPPED Telunix - (%x)",tnix_tcb); close_tcp(tnix_tcb); } else fprintf(stderr,"stop telunix fails -- no server active.\n"); } /* 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(); extern int send_tcp(); extern struct mbuf *free_p(); extern int del_tcp(); extern int close_tcp(); #ifdef SYS5 extern void free(); #endif 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\n"); 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\n"); close_tcp(tcb); return; } #ifdef SYS5 selmask |= (1 << tn->fd); #endif 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); #ifdef SYS5 selmask &= ~(1 << tn->fd); #endif 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); #ifdef SYS5 selmask &= ~(1 << tn->fd); #endif 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: */ do { if(tn->outbuf == NULLBUF && (tn->outbuf = alloc_mbuf(TURQSIZ)) == NULLBUF) return; /* can't do much without a buffer */ if(tn->outbuf->cnt < TURQSIZ) { if((i = read(tn->fd, tn->outbuf->data + tn->outbuf->cnt, (int)(TURQSIZ - tn->outbuf->cnt))) == -1) { log(tcb,"error Telunix - read (%d %d %d)", errno, tn->fd, TURQSIZ - tn->outbuf->cnt); close_tcp(tcb); return; } if((tn->outbuf->cnt += i) < TURQSIZ) i = 0; /* didn't fill buffer so don't retry */ } else { i = -1; /* any nonzero value will do */ } if(tn->outbuf->cnt == 0 || /* nothing to send */ tcb->sndcnt > tcb->window) /* too congested to send */ return; 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; return; } 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; }