/* telserv.c Actually a telnet client that communicates with a server. * This is a stub that permits adding a server as a telnet socket, based * on telunix that I use on my Unix machine. Joe, K5JB * Initially use ts->fp for the output message queue ID and leave input * queue ID a global * Before starting telserv, the server must be running with input key of * 1234 and output key of 5678. This client will use the reverse of those * keys to establish communication. * (See notes in src/msgs for message queue management utilities.) */ #include "unixopt.h" #ifdef TELSERV /* rest of module */ #include #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 #include /* one uploaded to ucsd.edu 2/8/92 used sys/fcntl.h */ #include #include #include #include #include #include #define MSGLEN 256 /* can tune this later */ #define INKEY 5678 /* these compliment keys in the server */ #define OUTKEY 1234 extern int exitval; #define TSMAXSCAN 1 /* initially we will do only process */ #define TSQSIZ 512 /* max data we will request from pty at once */ struct tcb *tserv_tcb = NULLTCB; struct tcb *tservtcb[TSMAXSCAN]; /* savebuf for tcb ptrs for scan routines */ int inqid = -1; /* global until I add another structure member fd */ struct inmsgbuf { long mtype; char linebuf[MSGLEN]; } inmsgbuf; struct outmsgbuf { long mtype; char linebuf[MSGLEN]; } outmsgbuf; static void printerr() { printf("Errno was: "); switch(errno){ case EINVAL: printf("invalid\n"); break; case EACCES: printf("access\n"); break; case EPERM: printf("permission\n"); break; default: printf("unknown errno\n"); } } static int openmsgs() { int outqid; int i,rtn; struct msgbuf *msgp; if(inqid >= 0) /* already opened */ return -1; inqid = msgget(INKEY,0600); /* rw by owner only */ if(inqid == -1) printf("Oops, msgget 1 failed\n"); #ifdef DEBUG printf("Client Input Queue ID is %d\n",inqid); #endif outqid = msgget(OUTKEY,0600); /* rw by owner only */ if(outqid == -1) printf("Oops, msgget 2 failed\n"); #ifdef DEBUG printf("Server Output Queue ID is %d\n",outqid); #endif return (inqid >= 0) ? outqid : -1; } static void closemsgs(ts) /* won't actually close, just ignore */ struct telnet *ts; { ts->fd = 0; inqid = -1; } static int writemsg(outqid,buf,qty) int outqid,qty; struct outmsgbuf *buf; { int rtn = qty; outmsgbuf.mtype = 1; /* if 0, send fails with EINVAL */ if(qty > MSGLEN) qty = MSGLEN; /* come back later and make a loop here */ memcpy(outmsgbuf.linebuf,buf,qty); if(msgsnd(outqid,&outmsgbuf,qty,IPC_NOWAIT) != 0){ printf("Oops, msgsnd failed\n"); return -1; }else return rtn == qty ? qty : rtn - MSGLEN; } /* Start telnet server */ int tserv1(argc,argv) char *argv[]; { struct socket lsocket; extern int32 ip_addr; void tserv_state(); int fd; lsocket.address = ip_addr; if(argc < 2) lsocket.port = TTYLINK_PORT; /* needs defining */ else lsocket.port = atoi(argv[1]); tserv_tcb = open_tcp(&lsocket,NULLSOCK,TCP_SERVER,0, NULLVFP,NULLVFP,tserv_state,0,NULLCHAR); if(tserv_tcb == NULLTCB){ fprintf(stderr,"start telserver fails rsn %d.\n",net_error); return(-1); }else log(tserv_tcb,"STARTED Telserver - (%d %x)", lsocket.port,tserv_tcb); return 0; } /* Shut down Telnet server */ int tserv0() { if(tserv_tcb != NULLTCB) { log(tserv_tcb,"STOPPED Telserver - (%x)",tserv_tcb); close_tcp(tserv_tcb); return(0); } else return -1; } /* Handle incoming Telnet-Unix connect requests */ static void tserv_state(tcb,old,new) struct tcb *tcb; char old,new; { register struct telnet *ts; int tserv_addscan(); extern void tserv_rmvscan(),sndmsg(); extern int send_tcp(); extern struct mbuf *free_p(); extern int del_tcp(); extern int close_tcp(); extern void free(); char junk; char *wakeup = "hello\n"; char *goodnight = "goodbye\n"; struct msqid_ds statbuf; ts = (struct telnet *)tcb->user; switch(new){ case ESTABLISHED: /* Create and initialize a Telnet protocol descriptor */ if((ts = (struct telnet *)calloc(1,sizeof(struct telnet))) == NULLTN){ log(tcb,"reject Telserver - no space"); sndmsg(tcb,"Rejected; no space on remote\015\012"); close_tcp(tcb); return; } ts->session = NULLSESSION; ts->state = TS_DATA; tcb->user = (char *)ts; /* Upward pointer */ ts->tcb = tcb; /* Downward pointer */ ts->inbuf = NULLBUF; ts->outbuf = NULLBUF; if(tserv_addscan(tcb) < 0 || (ts->fd = openmsgs()) == -1) { /* only one process at a time */ tserv_rmvscan(tcb); log(tcb,"reject Telserver - no Unix ports"); sndmsg(tcb, "Rejected; no ports available on remote\015\012"); close_tcp(tcb); return; } /* flush old server data */ if(msgctl(inqid,IPC_STAT,&statbuf) == -1){ log(tcb,"error Telserver - state: msgctl (%d %d %d)", errno, ts->fd, TSQSIZ - ts->outbuf->cnt); close_tcp(tcb); closemsgs(ts); break; } while(statbuf.msg_qnum--) msgrcv(inqid,&inmsgbuf,MSGLEN,0,IPC_NOWAIT); writemsg(ts->fd,wakeup,7); /* replace with suitable wake up call */ log(tcb,"open Telserver - (%d %x %d %d)",ts->fd,tcb,old,new); break; case FINWAIT1: case FINWAIT2: case CLOSING: case LAST_ACK: case TIME_WAIT: if(ts != NULLTN && ts->fd > 0) { log(tcb,"close Telserver - (%d %x %d %d)", ts->fd,tcb,old,new); writemsg(ts->fd,goodnight,9); /* suitable message, note length */ closemsgs(ts); } tserv_rmvscan(tcb); break; case CLOSE_WAIT: /* flush that last buffer */ if(ts != NULLTN && ts->outbuf != NULLBUF && ts->outbuf->cnt != 0) { send_tcp(tcb,ts->outbuf); ts->outbuf = NULLBUF; } close_tcp(tcb); break; case CLOSED: if(ts != NULLTN){ if(ts->fd > 0){ log(tcb,"close Telserver - (%d %x %d %d)",ts->fd,tcb,old,new); closemsgs(ts); } if(ts->inbuf != NULLBUF) free_p(ts->inbuf); if(ts->outbuf != NULLBUF) free_p(ts->outbuf); free((char *)ts); } tserv_rmvscan(tcb); del_tcp(tcb); if(tcb == tserv_tcb) tserv_tcb = NULLTCB; break; } } /* Telserver io interface. Called periodically to process any waiting io. */ static void tserv_try(tcb) register struct tcb *tcb; { extern void tserv_rmvscan(); extern int recv_tcp(); extern int send_tcp(); extern void tserv_input(); register struct telnet *ts; register int i; struct msqid_ds statbuf; if((ts = (struct telnet *)tcb->user) == NULLTN || ts->fd < 3) { /* Unknown connection - remove it from queue */ log(tcb,"error Telnet - tserv_try (%d)", ts); tserv_rmvscan(tcb); return; } /* * First, check if there is any pending input messages */ if(ts->inbuf != NULLBUF) tserv_input(ts); if(ts->inbuf == NULLBUF) if(tcb->rcvcnt > 0 && recv_tcp(tcb,&(ts->inbuf),0) > 0) tserv_input(ts); /* * Next, check if there is any io for tcp: */ i = 0; do{ if(ts->outbuf == NULLBUF && (ts->outbuf = alloc_mbuf(TSQSIZ)) == NULLBUF) break; /* can't do much without a buffer */ if(ts->outbuf->cnt < TSQSIZ) { if(msgctl(inqid,IPC_STAT,&statbuf) == -1){ log(tcb,"error Telserver - msgctl (%d %d %d)", errno, ts->fd, TSQSIZ - ts->outbuf->cnt); close_tcp(tcb); break; } if(statbuf.msg_qnum){ i = msgrcv(inqid,&inmsgbuf,MSGLEN,0,IPC_NOWAIT); memcpy(ts->outbuf->data + ts->outbuf->cnt,inmsgbuf.linebuf, i <= (TSQSIZ - (int)ts->outbuf->cnt) ? i : (TSQSIZ - (int)ts->outbuf->cnt)); } if((ts->outbuf->cnt += i) < TSQSIZ) i = 0; /* didn't fill buffer so don't retry */ } if(ts->outbuf->cnt == 0 || /* nothing to send */ tcb->sndcnt > tcb->window) /* too congested to send */ break; if(send_tcp(tcb,ts->outbuf) < 0) { log(tcb,"error Telserver - send_tcp (%d %d %d)", net_error, ts->fd, ts->outbuf->cnt); close_tcp(tcb); ts->outbuf = NULLBUF; break; } ts->outbuf = NULLBUF; } while(i); } /* Process incoming TELNET characters */ void tserv_input(ts) register struct telnet *ts; { void doopt(),dontopt(),willopt(),wontopt(),answer(); char *memchr(); register int i; register struct mbuf *bp; char c; bp = ts->inbuf; /* Optimization for very common special case -- no special chars */ if(ts->state == TS_DATA){ while(bp != NULLBUF && memchr(bp->data,IAC,(int)bp->cnt) == NULLCHAR) { if((i = writemsg(ts->fd, bp->data, (unsigned)bp->cnt)) == bp->cnt) { ts->inbuf = bp = free_mbuf(bp); } else if(i == -1) { log(ts->tcb,"error Telserver - write (%d %d %d)", errno, ts->fd, bp->cnt); close_tcp(ts->tcb); closemsgs(ts); return; } else { bp->cnt -= i; bp->data += i; return; } } if(bp == NULLBUF) return; } while(pullup(&(ts->inbuf),&c,1) == 1){ bp = ts->inbuf; switch(ts->state){ case TS_DATA: if(uchar(c) == IAC){ ts->state = TS_IAC; } else { if(!ts->remote[TN_TRANSMIT_BINARY]) c &= 0x7f; if(writemsg(ts->fd, &c, 1) != 1) { /* we drop a character here */ return; } } break; case TS_IAC: switch(uchar(c)){ case WILL: ts->state = TS_WILL; break; case WONT: ts->state = TS_WONT; break; case DO: ts->state = TS_DO; break; case DONT: ts->state = TS_DONT; break; case IAC: if(writemsg(ts->fd, &c, 1) != 1) { /* we drop a character here */ return; } ts->state = TS_DATA; break; default: ts->state = TS_DATA; break; } break; case TS_WILL: willopt(ts,c); ts->state = TS_DATA; break; case TS_WONT: wontopt(ts,c); ts->state = TS_DATA; break; case TS_DO: doopt(ts,c); ts->state = TS_DATA; break; case TS_DONT: dontopt(ts,c); ts->state = TS_DATA; break; } } } /* called periodically from main loop */ void tserv_scan() { void tserv_try(); register int i; for(i = 0; i < TSMAXSCAN; i += 1) if(tservtcb[i] != NULLTCB) tserv_try(tservtcb[i]); } int tserv_addscan(tcb) struct tcb *tcb; { register int i; for(i = 0; i < TSMAXSCAN; i += 1) if(tservtcb[i] == NULLTCB) { tservtcb[i] = tcb; return i; } return -1; } void tserv_rmvscan(tcb) struct tcb *tcb; { register int i; for(i = 0; i < TSMAXSCAN; i += 1) if(tservtcb[i] == tcb) tservtcb[i] = NULLTCB; } #endif /* TELSERV */