/* cliutl.c */ #define CLIUTL_DEF 1 #define CLIUTLMASTER 1 /* * Includes */ #include #include #include "dfault.h" #include "evtdef.h" #include "hstdef.h" #include "prodef.h" #include "cliutl.h" #include "cldrvr.h" #include "mapskt.h" #include "kbdutl.h" #include "bytprc.h" #include "cticks.h" #include "suspnd.h" /* * debug - * * bit 0 (0x01) enable printing of internal events */ int debug = 0; /* * size of timer queue */ #define NTIMES 30 /* * timer queue of events which will be placed * into the event queue when the time is up. */ static struct { int eclass, /* event queue data */ event, idata, next; /* next item in list */ long when; /* when timer is to go off */ } Stq[NTIMES]; static int Stfirst, Stfree; /* pointers for timer queue */ /* * Compute elapsed time */ long elapsed(ltm) long ltm; { long el; el = cticks(NULL)-ltm; if(el < 0) el += WRAPTIME; return(el); } /* * msginit * * Initialize the message handler */ int tljobn; VOID msginit() { tljobn = cl_init(); } /* * mdemux * * Find the messages in the buffer, queue up all classes. * * The 'all' parameter tells demux whether it should attempt to empty * the input message buffer or return after the first useful message * is queued. * * returns the number of messages queued */ int mdemux(all) int all; { register int nmuxed; register MSGPKT *pkt; nmuxed = 0; /* * while all flag is on */ do { pkt = (MSGPKT *) cl_recv(); if(pkt==NULL) { all = 0; } else { skptuev(pkt->class,pkt->event,pkt->data); nmuxed++; } } while(all); return(nmuxed); } /* * socket * * Send a request for a socket to TCPIP. * If TCPIP responds with a TCPIP socket number, * then the socket has been allocated space in the * specified global region and has been initialized * for a TCP connection. */ int socket(lpnum) int lpnum; { cl_xmit(SCKTCLASS,REQSCKT,tljobn); return(response(SCKTCLASS,USRSCKT,lpnum)); } /* * attach * * This function is used only by programs started * by TCPIP using the configuration file services * option. * * Send a request to TCPIP to attach to a service. * If TCPIP responds with a TCPIP socket number, * then the service has been attached * to this process. */ int attach(lpnum,iport) int lpnum,iport; { int sknum; /* * To allow verification of the attaching job * the job number and port number are sent to TCPIP. */ cl_xmit(SRVCATTACH,iport,-tljobn); sknum = response(SRVCCLASS,ATTACHED,lpnum); if(sknum < 0) { kb_puts(skerrstring(501)); kb_nline(); } return(sknum); } /* * response * * Get a response to a TCPIP request */ int response(class,event,lpnum) int class,event,lpnum; { register struct socket *skt; int cl,ev,dat; long start; /* * wait for response */ start = cticks(NULL); do { ev = Sgetevent(class|ERRCLASS|MSGCLASS,&cl,&dat); if(ev) { if(cl==class) { if(ev!=event) { skptevent(class,ev,dat); ev = 0; } } else { kb_puts(skerrstring(ev)); kb_nline(); ev = 0; } } if(ev==0) suspnd(0); if(elapsed(start) > 300) { kb_puts("No response from TCPIP Server\r\n"); return(-1); } } while(ev==0); /* * Verify the socket parameter */ if(dat<0 || dat>=GSCKTS) return(-1); /* * Try to map the socket region */ skt = (struct socket *) mapskt(dat); if(skt==-1) { kb_puts(skerrstring(500)); kb_nline(); sktlist[dat].lpnum = -1; return(-1); } else { sktlist[dat].lpnum = lpnum; return(dat); } } /* * connect * * Request a connection */ VOID connect(sknum,st) int sknum; char *st; { register struct socket *skt; /* * Verify the socket parameter */ skt = (struct socket *) skvalid(sknum); if(skt==-1) return(-1); /* * Copy machine description into the socket request buffer */ strncpy(skt->request,st,sizeof(skt->request)); /* * Request a connection */ cl_xmit(TCPCLASS,REQCONNECT,sknum); } /* * listen * * Request to listen */ int listen(sknum,port) register int sknum; register int port; { register struct socket *skt; /* * Verify the socket parameter */ skt = (struct socket *) skvalid(sknum); if(skt==-1) return(-1); skt->sktport = port; /* * Request to listen */ cl_xmit(TCPCLASS,REQLISTEN,sknum); /* * wait for the assigned port */ while(!port) { suspnd(3); port = skt->sktport; } return(port); } /* * addsess * Add a session to a named machine, or prompt for a machine to be named. */ int addsess(lpnum,st,port) int lpnum; char *st; int port; { register struct socket *skt; char s[80]; int i,sknum; /* * bound the local socket numbers */ if(lpnum<0 || lpnum>=LSCKTS) return(-1); if(st==NULL) { /* no machine yet */ kb_puts( "\r\nEnter machine name/address, ESC to return: \r\n"); s[0] = '\0'; while(0>=(i = kb_gets(s,sizeof(s)-1,1))) Stask(); if(i==27 || !s[0]) return(-1); kb_puts("\r\n\n"); /* skip down a little */ st = s; /* make a copy of the pointer to s */ } /* * Request a TCPIP socket */ sknum = socket(lpnum); if(sknum<0) return(-1); /* * Specify the connection type */ skt = (struct socket *) mapskt(sknum); skt->sktport = port; /* * Request a connection */ connect(sknum,st); return(sknum); } /* * Log the Session */ VOID logsession(sknum,st) int sknum; char *st; { register struct socket *skt; /* * check validity */ skt = (struct socket *) skvalid(sknum); if(skt==-1) return(-1); strncpy(&skt->request,st,70); skt->request[69] = '\0'; cl_xmit(TCPCLASS,LOGSESSION,sknum); } /* * errhandle * * write error messages to the console window */ VOID errhandle() { int cl,ev,dat; while((ev = Sgetevent(ERRCLASS | MSGCLASS,&cl,&dat)) != 0) { kb_puts(skerrstring(ev)); kb_nline(); } } /* * enqueue * * Used by write. * QUEUESIZE is the size limitation of the advertised window. */ int enqueue(skt,buffer,nbytes) register struct socket *skt; register char *buffer; int nbytes; { int i; i = outroom(skt); if(i<=0 || nbytes==0) /* * no room at the inn */ return(0); if(nbytes>i) nbytes = i; /* * room at end */ i = skt->out.queue - skt->out.where; i = QUEUESIZE - i; if(iout.queue,buffer,i); movebytes(skt->out.where,(char *)(buffer+i),nbytes-i); skt->out.queue = skt->out.where + nbytes - i; } else { movebytes(skt->out.queue,buffer,nbytes); if(i==nbytes) { skt->out.queue = skt->out.where; } else { skt->out.queue += nbytes; } } /* * more stuff here */ return(nbytes); } /* * dequeue * * used by read, this copies data out of the * queue and then deallocates it from the queue. * * returns number of bytes copied from the queue */ int dequeue(skt,buffer,nbytes) register struct socket *skt; register char *buffer; int nbytes; { int i,qbytes; qbytes = inqlen(skt); if(qbytes==0) return(0); if(qbytesin.queue - skt->in.where; i = QUEUESIZE - i; if(iin.queue,i); movebytes((char *)(buffer+i),skt->in.where,nbytes-i); skt->in.queue = skt->in.where + nbytes - i; } else { movebytes(buffer,skt->in.queue,nbytes); if(i==nbytes) { skt->in.queue = skt->in.where; } else { skt->in.queue += nbytes; } } return(nbytes); } /* * skposterr * place an error into the event q * Takes the error number and puts it into the error structure */ void skposterr(num) int num; { if(skptevent(ERRCLASS,num,-1)) /* * only if we lost an event */ skptuev(ERRCLASS,502,-1); } /* * skgtevent * * Retrieves the next event (and clears it) which matches bits in * the given mask. Returns the event number or -1 on no event present. * Also returns the exact class and the associated integer in reference * parameters. * * The way the queue works: * There is always a dummy record pointed to by nnelast. * When data is put into the queue, it goes into nnelast, * then nnelast looks around for another empty one to obtain. * It looks at nnefree first, then bumps one from nnefirst if necessary. * When data is retrieved, it is searched from nnefirst to nnelast. * Any freed record is appended to nnefree. */ int skgtevent(mask,retclass,retint) int mask; int *retclass,*retint; { int i,j; i = nnefirst; j = 0; while(i!=nnelast) { if(mask&nnq[i].eclass) { if(i==nnefirst) { /* * step nnefirst */ nnefirst = nnq[nnefirst].next; } else { /* * bypass record i */ nnq[j].next = nnq[i].next; } nnq[i].next = nnefree; /* * install in free list */ nnefree = i; if(debug&0x01) { kbprintf("\r\nevent: cl=%d, ev=%d, dat=%d\r\n", nnq[i].eclass, nnq[i].event, nnq[i].idata); } *retint = nnq[i].idata; *retclass = nnq[i].eclass; return(nnq[i].event); } j = i; i = nnq[i].next; } return(0); } /* * skptevent * * add an event to the queue. * Will probably get the memory for the entry from the free list. * Returns 0 if there was room, 1 if an event was lost. */ int skptevent(class,event,dat) int class,event,dat; { int i; i = nnelast; /* * put data in */ nnq[i].eclass = class; nnq[i].event = event; nnq[i].idata = dat; /* * there is a spot in free list */ if(nnefree>=0) { nnq[i].next = nnelast = nnefree; /* * remove from free list */ nnefree = nnq[nnefree].next; return(0); } else { nnq[i].next = nnelast = nnefirst; /* * lose oldest event */ nnefirst = nnq[nnefirst].next; return(1); } } /* * skptuev * * put a unique event into the queue * First searches the queue for like events */ int skptuev(class,event,dat) int class,event,dat; { int i; i = nnefirst; while(i!=nnelast) { if(nnq[i].event==event && nnq[i].eclass==class && nnq[i].idata==dat) return(0); i = nnq[i].next; } return(skptevent(class,event,dat)); } /* * skeventinit () * * Setup all the pointers for the event queue -- makes a circular list * which is required for error messages. ( called from Snetinit () ) */ VOID skeventinit() { int i; for(i=0; i100); /* * error unknown */ return(errs[0]+5); } /* * skvalid * * Verify that the socket number is valid * and map the socket for access */ struct socket *skvalid(sknum) int sknum; { int lpnum; register int diff; register struct socket *skt; /* * check validity */ if(sknum<0 || sknum>=GSCKTS) return(-1); lpnum = sktlist[sknum].lpnum; if(lpnum<0 || lpnum>=LSCKTS) return(-1); return((struct socket *) mapskt(sknum)); } /* * skread * * Read data from the queue buffer (specified by sknum) * into the user buffer for up to n bytes. * * Returns number of bytes read or <0 if error. */ int skread(sknum,buffer,n) int sknum,n; char *buffer; { int i; register struct socket *skt; /* * check validity */ skt = (struct socket *) skvalid(sknum); if(skt==-1) return(-2); /* * foreign or skclose */ if(skt->state != SEST) { /* * ready for me to close my side? */ if(skt->state==SCWAIT) { if(!inqlen(skt)) { cl_xmit(TCPCLASS,CLOSING,sknum); while(skt->state==SCWAIT) { mdemux(1); suspnd(0); } return(-1); } } else { return(-1); } } /* * read from queue buffer */ i = dequeue(skt,buffer,n); /* * enter event if more data */ if(inqlen(skt)) skptuev(CONCLASS,CONDATA,sknum); return(i); } /* * skempty * * Discard data in the queue buffer (specified by sknum) */ int skempty(sknum) int sknum; { register struct socket *skt; /* * check validity */ skt = (struct socket *) skvalid(sknum); if(skt==-1) return(-1); skt->in.queue = skt->in.wtptr; if(skt->in.queue!=skt->in.rdptr) cl_xmit(TCPCLASS,DEQUEUE,sknum); return(0); } /* * skdeque * * inform TCPIP of any dequeued data */ int skdeque(sknum) int sknum; { register struct socket *skt; /* * check validity */ skt = (struct socket *) skvalid(sknum); if(skt==-1) return(-1); /* * Dequeue any messages */ mdemux(1); /* * Dequeue any data used */ if(skt->in.queue!=skt->in.rdptr) cl_xmit(TCPCLASS,DEQUEUE,sknum); return(0); } /* * Global push counter. * Incremented each time skwrite or skwchar is called. * Clear each time skenque is called. */ int pushsk; /* * skwrite * * Write data into the output queue (specified by sknum). * TCPIP will distribute the data onto the wire later. * * Returns number of bytes sent or <0 if an error. */ int skwrite(sknum,buffer,n) int sknum,n; char *buffer; { register struct socket *skt; /* * check validity */ skt = (struct socket *) skvalid(sknum); if(skt==-1) return(-2); if(skt->state!=SEST) return(-1); pushsk++; return(enqueue(skt,buffer,n)); } /* * skwchar * * Write data into the output queue (specified by sknum). * TCPIP will distribute the data onto the wire later. * * Returns number of bytes sent or <0 if an error. */ int skwchar(sknum,c) int sknum,c; { register struct socket *skt; /* * check validity */ skt = (struct socket *) skvalid(sknum); if(skt==-1) return(-2); if(skt->state!=SEST) return(-1); pushsk++; return(enqueue(skt,(char *) &c,1)); } /* * skenque * * Conditionally sets the push bit on the specified socket data, * informs TCPIP of the enqueued data, and returns whether the * queue is empty or how many bytes in the queue. <0 if an error. */ int skenque(sknum,pflag) int sknum; { register struct socket *skt; pushsk = 0; /* * check validity */ skt = (struct socket *) skvalid(sknum); if(skt==-1) return(-2); skt->out.push = pflag; /* * Dequeue any messages */ mdemux(1); /* * Trigger TCPIP into action */ if(skt->out.queue!=skt->out.wtptr) cl_xmit(TCPCLASS,ENQUEUE,sknum); return(QUEUESIZE - 1 - outroom(skt)); } /* * skstate * * Returns the state of the connection. <0 if an error. */ int skstate(sknum) int sknum; { register struct socket *skt; /* * check validity */ skt = (struct socket *) skvalid(sknum); if(skt==-1) return(-2); /* * socket state */ return(skt->state); } /* * skqlen * * Returns the number of bytes waiting to be read * from the incoming queue for socket sknum. * <0 if error. */ int skqlen(sknum) int sknum; { register struct socket *skt; /* * check validity */ skt = (struct socket *) skvalid(sknum); if(skt==-1) return(-2); return(inqlen(skt)); } int inqlen(skt) register struct socket *skt; { register int diff; diff = skt->in.wtptr - skt->in.queue; if(diff>=0) { return(diff); } else { return(diff+QUEUESIZE); } } /* * skroom * * Returns the number of bytes left in * the outgoing queue for socket sknum. * <0 if error. */ int skroom(sknum) int sknum; { register struct socket *skt; /* * check validity */ skt = (struct socket *) skvalid(sknum); if(skt==-1) return(-2); if(skt->state!=SEST) return(-1); return(outroom(skt)); } int outroom(skt) register struct socket *skt; { register int diff; diff = skt->out.queue - skt->out.rdptr; if(diff>=0) { return(QUEUESIZE-1-diff); } else { return(-1-diff); } } /* * skibytes * * Returns the number of bytes waiting to be read * from the incoming queue for socket sknum. */ int skibytes(sknum) int sknum; { register struct socket *skt; /* * check validity */ skt = (struct socket *) skvalid(sknum); if(skt==-1) return(0); return(inqlen(skt)); } /* * skobytes * * Returns the number of bytes in the outgoing queue * waiting to be transmitted for socket sknum. */ int skobytes(sknum) int sknum; { register struct socket *skt; /* * check validity */ skt = (struct socket *) skvalid(sknum); if(skt==-1) return(0); return(QUEUESIZE-1-outroom(skt)); } /* * sktest * * Checks to see if a particular session has been established yet. * Returns 0 if the connection is in "established" state. */ int sktest(sknum) int sknum; { register struct socket *skt; /* * check validity */ skt = (struct socket *) skvalid(sknum); if(skt==-1) return(-2); if(skt->state==SEST) { return(0); } else { if(skt->state==SCWAIT) { if(!inqlen(skt)) { cl_xmit(TCPCLASS,CLOSING,sknum); while(skt->state==SCWAIT) { mdemux(1); suspnd(0); } return(-1); } else { return(0); } } } return(-1); } /* * sksendwait * * Invalid socket - return(-2) * Connection dropped - return(-1) * Data sent - return( 0) * Timeout - return( 1) */ int sksendwait(sknum,p,wt) int sknum,p; long wt; { register struct socket *skt; long start; /* * check validity */ skt = (struct socket *) skvalid(sknum); if(skt==-1) return(-2); /* * Wait until connection dropped, data sent, or timeout */ start = cticks(NULL); while(1) { if(!skenque(sknum,p)) return(0); if(sktest(sknum)) return(-1); if(elapsed(start) >= wt) return(1); mdemux(1); suspnd(0); } } /* * skrurgent * * Returns pointer to urgent input data */ char *skrurgent(sknum) int sknum; { register struct socket *skt; /* * check validity */ skt = (struct socket *) skvalid(sknum); if(skt==-1) return(NULL); return(skt->in.urgent); } /* * skwurgent * * Loads the output urgent data flag */ int skwurgent(sknum,i) int sknum,i; { register struct socket *skt; /* * check validity */ skt = (struct socket *) skvalid(sknum); if(skt==-1) return(0); skt->out.urgent = i; return(1); } /* * skclose * * Start the closing process on socket sknum. */ VOID skclose(sknum) int sknum; { int lpnum; /* * check validity */ if(sknum<0 || sknum>=GSCKTS) return; lpnum = sktlist[sknum].lpnum; if(lpnum<0 || lpnum>=LSCKTS) return; cl_xmit(TCPCLASS,CLOSING,sknum); } /* * skabort * * Abort the socket connection for sknum. */ VOID skabort(sknum) int sknum; { int lpnum; /* * check validity */ if(sknum<0 || sknum>=GSCKTS) return; lpnum = sktlist[sknum].lpnum; if(lpnum<0 || lpnum>=LSCKTS) return; cl_xmit(SCKTABORT,tljobn,sknum); } /* * skrelease * * Finish the closing process on socket sknum. * 1 - issue a close if in state STWAIT/STLAST * 2 - return(-1) if in state SCLOSED * 3 - loop until a timeout if flag is set, * then issue a close and release the socket * * return -1 if the socket is closed or released * else return sknum */ int skrelease(sknum,tflag) int sknum,tflag; { register struct socket *skt; long time; /* * check validity */ skt = (struct socket *) skvalid(sknum); if(skt==-1) return(-1); time = cticks(NULL); do { switch(skt->state) { default: /* check for timeout */ if(elapsed(time) < (long) RELEASETIME) { break; } case SLAST: /* close the socket */ case STWAIT: skclose(sknum); case SCLOSED: /* socket is released */ return(sktlist[sknum].lpnum = -1); break; } if(sknum>=0 && tflag) suspnd(15); } while(sknum>=0 && tflag); return(sknum); } /* * skgtftp * * This routine provides the information needed to open an ftp connection * back to the originator of the command connection. The other side's IP * number and the port numbers are returned in an INTEGER array ( convenient * for use in PORT commands ). */ int skgtftp(sknum,a) int sknum,a[]; { register struct socket *skt; register unsigned int i; /* * check validity */ skt = (struct socket *) skvalid(sknum); if(skt==-1) return(-2); a[0] = skt->tcpout.i.ipdest[0] & 0xFF; a[1] = skt->tcpout.i.ipdest[1] & 0xFF; a[2] = skt->tcpout.i.ipdest[2] & 0xFF; a[3] = skt->tcpout.i.ipdest[3] & 0xFF; i = intswap(skt->tcpout.t.source); a[4] = i>>8 & 0xFF; a[5] = i & 0xFF; i = intswap(skt->tcpout.t.dest); a[6] = i>>8 & 0xFF; a[7] = i & 0xFF; return(0); } /* * Force the from port on the connection */ int skfromport(sknum,fromport) int sknum,fromport; { register struct socket *skt; /* * check validity */ skt = (struct socket *) skvalid(sknum); if(skt==-1) return(-2); /* * allow the from port to be forced */ skt->skfport = fromport; return(0); } /* * Snetinit * * Handle all the initialization: * the message handler, and internal * queues and parameters. */ int Snetinit() { int i; /* * Initialize the message handler */ msginit(); /* * no ports allocated to this job */ for(i=0; i=0) { Stq[i].when -= WRAPTIME; i = Stq[i].next; } } recent = t; /* * Q is not empty and timer is going off */ while (Stfirst >= 0 && t > Stq[Stfirst].when) { i = Stfirst; skptevent(Stq[i].eclass,Stq[i].event,Stq[i].idata); /* * remove from q */ Stfirst = Stq[Stfirst].next; Stq[i].next = Stfree; /* * add to free list */ Stfree = i; } } /* * Stmrset * * Set an async timer which is checked in Stask -- when time elapses * sticks an event in the network event queue * * class, event, dat is what gets posted when howlong times out. */ int Stmrset(class,event,dat,howlong) int class,event,dat,howlong; { int i,j,jlast,retval; long gooff; retval = 0; gooff = cticks(NULL)+howlong*TICKSPERSEC; /* * queue is full, post first event */ if(Stfree < 0) { Stfree = Stfirst; Stfirst = Stq[Stfirst].next; Stq[Stfree].next = -1; skptevent( Stq[Stfree].eclass, Stq[Stfree].event, Stq[Stfree].idata); retval = -1; } /* * event to occur at that time */ Stq[Stfree].idata = dat; Stq[Stfree].event = event; Stq[Stfree].eclass = class; Stq[Stfree].when = gooff; /* * remove from free list */ i = Stfree; Stfree = Stq[i].next; if(Stfirst < 0) { /* * if no queue yet anchor active q */ Stfirst = i; Stq[i].next = -1; } else /* * goes first on list at beginning of list */ if(gooff < Stq[Stfirst].when) { Stq[i].next = Stfirst; Stfirst = i; } else { /* * goes in middle * search q from beginning */ j = jlast = Stfirst; while (gooff >= Stq[j].when&&j >= 0) { jlast = j; j = Stq[j].next; } /* * insert in q */ Stq[i].next = j; Stq[jlast].next = i; } return(retval); } /* * Stmrunset * * Remove all timer events from the queue * that match the class/event/dat. */ int Stmrunset(class,event,dat) int class,event,dat; { int i,ilast,retval; retval = ilast = -1; i = Stfirst; /* * search list */ while (i >= 0 ) { if(Stq[i].idata == dat && Stq[i].eclass == class && Stq[i].event == event) { /* * found at least one */ retval = 0; if(i == Stfirst) { /* * first one matches * attach to free list */ Stfirst = Stq[i].next; Stq[i].next = Stfree; Stfree = i; i = Stfirst; /* * start list over */ continue; } else { /* * remove this entry * attach to free list */ Stq[ilast].next = Stq[i].next; Stq[i].next = Stfree; Stfree = i; i = ilast; } } ilast = i; i = Stq[i].next; } return(retval); } /* * Sgetevent * * Run Stask() * Gets events from session or TCPIP */ int Sgetevent(mask,retclass,retdat) int mask; register int *retclass,*retdat; { /* * allow messages */ mdemux(); /* * allow timers to take place */ Stask(); /* * session event */ return(skgtevent(mask,retclass,retdat)); }