home *** CD-ROM | disk | FTP | other *** search
- /*
- * TOOLS.C
- *
- ****************************************************************************
- * *
- * part of: *
- * TCP/IP kernel for NCSA Telnet *
- * by Tim Krauskopf *
- * *
- * National Center for Supercomputing Applications *
- * 152 Computing Applications Building *
- * 605 E. Springfield Ave. *
- * Champaign, IL 61820 *
- * *
- * Copyright (c) 1987, Board of Trustees of the University of Illinois *
- * *
- ****************************************************************************
- *
- * Portions of the driver code that are not specific to a particular protocol
- *
- */
- #include <stdio.h>
- #include <string.h>
- #ifdef MEMORY_DEBUG
- #include "memdebug.h"
- #endif
- #include "protocol.h"
- #include "data.h"
- #include "externs.h"
-
- extern int ftpdata; /* current ftp data port */
-
- static char *get_name(int port); /* returns name of a port */
- static char *find_port(int port);
-
- /************************************************************************/
- /* netsleep
- * sleep, while demuxing packets, so we don't miss anything
- *
- */
- int netsleep(n)
- int n;
- {
- int i,nmux,redir;
- int32 t,gt,start;
- struct port *p;
- uint8 *pc;
-
- redir=0;
- start=n_clicks();
-
- if(n)
- t=start+n*TICKSPERSEC;
- else
- t=start;
-
- do {
- nmux=demux(1); /* demux all packets */
-
- /*
- * if there were packets in the incoming packet buffer, then more might
- * have arrived while we were processing them. This gives absolute priority
- * to packets coming in from the network.
- */
- if(nmux)
- continue;
- /*
- * Check for any ICMP redirect events.
- */
- if(IREDIR==netgetevent(ICMPCLASS,&i,&i))
- redir=1;
- /*
- * Check each port to see if action is necessary.
- * This now sends all Ack packets, due to p->lasttime being set to 0L.
- * Waiting for nmux==0 for sending ACKs makes sure that the network
- * has a much higher priority and reduces the number of unnecessary ACKs.
- */
- gt=n_clicks();
- for(i=0; i<NPORTS; i++) {
- p=portlist[i];
- if((p!=NULL)&&(p->state>SLISTEN)) {
-
- if(!p->out.lasttime)
- transq(p); /* takes care of all ACKs */
- else
- if((p->out.contain>0)||(p->state>SEST)) {
- /*
- * if a retransmission timeout occurs, exponential back-off.
- * This number returns toward the correct value by the RTT measurement
- * code in ackcheck.
- *
- * fix: 5/12/88, if timer was at MAXRTO, transq didn't get hit - TK
- */
- if((p->out.lasttime+p->rto<gt)) {
- if(p->rto<MAXRTO)
- p->rto<<=1; /* double it */
- transq(p);
- }
- }
- if((p->out.lasttime+POKEINTERVAL<gt)&&(p->state==SEST))
- transq(p);
- /*
- * check to see if ICMP redirection occurred and needs servicing.
- * If it needs servicing, try to get the new hardware address for the new
- * gateway. If getdlayer fails, we assume an ARP was sent, another ICMP
- * redirect will occur, this routine will reactivate, and then the hardware
- * address will be available in the cache.
- * Check all ports to see if they match the redirected address.
- */
-
- if(redir&&comparen(p->tcpout.i.ipdest,nnicmpsave,4)) {
- pc=getdlayer(nnicmpnew);
- if(pc!= NULL)
- movebytes(p->tcpout.d.dest,pc,DADDLEN);
-
- }
- }
- }
- redir=0; /* reset flag for next demux */
- } while((t>n_clicks()) && (n_clicks()>=start)); /* allow for wraparound of timer */
-
- return(nmux); /* will demux once, even for sleep(0) */
- }
-
- /***************************************************************************/
- /* enqueue
- * add something to a TCP queue. Used by both 'write()' and tcpinterpret
- * WINDOWSIZE is the size limitation of the advertised window.
- */
- int enqueue(wind,buffer,nbytes)
- struct window *wind;
- char *buffer;
- int nbytes;
- {
- int i;
-
- i=WINDOWSIZE-wind->contain;
- if(i<=0 || nbytes==0)
- return(0); /* no room at the inn */
- if(nbytes>i)
- nbytes=i;
- i=wind->where-wind->endlim; /* room at end */
- i+=WINDOWSIZE;
- if(i<nbytes) {
- movebytes(wind->endlim,buffer,i);
- movebytes(wind->where,(char *)(buffer+i),nbytes-i);
- wind->endlim=wind->where+nbytes-i;
- }
- else {
- movebytes(wind->endlim,buffer,nbytes); /* fits in one chunk */
- wind->endlim+=nbytes;
- }
- wind->contain+=nbytes; /* more stuff here */
- return(nbytes);
- }
-
- /*************************************************************************/
- /* dequeue
- * used by read, this copies data out of the queue and then
- * deallocates it from the queue.
- * cpqueue and rmqueue are very similar and are to be used by tcpsend
- * to store unacknowledged data.
- *
- * returns number of bytes copied from the queue
- */
- int dequeue(wind,buffer,nbytes)
- struct window *wind;
- char *buffer;
- int nbytes; /* maximum number to copy out */
- {
- int i;
-
- if(wind->contain==0)
- return(0);
- if((wind->contain)<(uint)nbytes)
- nbytes=wind->contain;
- i=wind->endbuf-wind->base;
- if(i<=nbytes) {
- movebytes(buffer,wind->base,i);
- movebytes((char *)(buffer+i),wind->where,nbytes-i);
- wind->base=wind->where+nbytes-i;
- }
- else {
- movebytes(buffer,wind->base,nbytes);
- if((wind->contain)==(uint)nbytes)
- wind->base=wind->endlim=wind->where;
- else
- wind->base+=nbytes;
- }
- wind->contain-=nbytes;
- return(nbytes);
- }
-
- /**************************************************************************/
- /* rmqueue
- * does the queue deallocation that is left out of cpqueue
- *
- * rmqueue of WINDOWSIZE or greater bytes will empty the queue
- */
- int rmqueue(wind,nbytes)
- struct window *wind;
- int nbytes; /* number to remove */
- {
- int i;
-
- if((wind->contain)< (uint)nbytes)
- nbytes=wind->contain;
- i=wind->endbuf-wind->base;
- if(i<=nbytes)
- wind->base=wind->where+nbytes-i;
- else {
- if((wind->contain)==(uint)nbytes)
- wind->base=wind->endlim=wind->where;
- else
- wind->base+=nbytes;
- }
- wind->contain-=nbytes;
- return(nbytes);
- }
-
- /************************************************************************/
- /* transq
- *
- * Needed for TCP, not as general as cpqueue,
- * but is required for efficient transmit of the whole window.
- *
- * Transmit the entire queue (window) to the other host without expecting
- * any sort of acknowledgement.
- *
- */
- int transq(prt)
- struct port *prt;
- {
- uint bites;
- unsigned int i,j,n;
- struct window *wind;
- uint32 saveseq;
- uint8 *endb,*whereb,*baseb;
-
- if(prt==NULL) {
- nnerror(406); /* NULL port for trans */
- return(-1);
- }
- wind=&prt->out;
- /*
- * find out how many bytes the other side will allow us to send (window)
- */
- bites=wind->size;
- if(wind->contain<bites)
- bites=wind->contain;
- /*
- * set up the tcp packet for this, ACK field is same for all packets
- */
- prt->tcpout.t.ack=longswap(prt->in.nxt);
- /*
- * any more flags should be set?
- */
- if(wind->push && (bites>0)) /* is push indicator on? */
- prt->tcpout.t.flags|=TPUSH;
- else
- prt->tcpout.t.flags&=~TPUSH; /* else clear push */
- /* we never set push flag unless we are actually sedning data */
- if((bites<=0) || prt->state!=SEST) { /* if no data to send . . . */
- tcpsend(prt,0); /* just a retransmission or ACK */
- return(0);
- }
- /*
- * we have data to send, get the correct sequence #'s
- * To be really real, we should check wraparound sequence # in the loop.
- */
- saveseq=wind->nxt;
- whereb=wind->where;
- endb=wind->endbuf;
- baseb=wind->base;
- /*
- * in a loop, transmit the entire queue of data
- */
- for(i=0; i<bites; i+=prt->sendsize) {
- n=prt->sendsize;
- if(i+n>bites)
- n=bites-i;
- j=endb-baseb;
- if(j<n) {
- movebytes(prt->tcpout.x.data,baseb,j);
- movebytes((char *)(prt->tcpout.x.data+j),whereb,n-j);
- baseb=whereb+n-j;
- }
- else {
- movebytes(prt->tcpout.x.data, baseb, n);
- baseb+=n;
- }
-
- tcpsend(prt,n); /* send it */
-
- wind->nxt+=n;
- }
- wind->nxt=saveseq; /* get back first seq # */
- return(0);
- }
-
- /************************************************************************/
- /* comparen
- * Take n bytes and return identical (true=1) or not identical (false=0)
- *
- * Could be written in assembler for improved performance
- */
- int comparen(s1,s2,n)
- uint8 *s1,*s2;
- register int n;
- {
- while (n--)
- if(*s1++!=*s2++)
- return(0);
- return(1);
- }
-
- /************************************************************************/
- /* netposterr
- * place an error into the event q
- * Takes the error number and puts it into the error structure
- */
- void netposterr(num)
- int num;
- {
- if(netputevent(ERRCLASS,ERR1,num))
- netputuev(ERRCLASS,ERR1,501); /* only if we lost an event */
- }
-
- /***********************************************************************/
- /* netgetevent
- * 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 netgetevent(mask,retclass,retint)
- uint8 mask;
- int *retclass,*retint;
- {
- int i,j=0;
-
- i=nnefirst;
- while(i!=nnelast) {
- if(mask&nnq[i].eclass) {
- if(i==nnefirst)
- nnefirst=nnq[nnefirst].next; /* step nnefirst */
- else
- nnq[j].next=nnq[i].next; /* bypass record i */
- nnq[i].next=nnefree;
- nnefree=i; /* install in free list */
- *retint=nnq[i].idata;
- *retclass=nnq[i].eclass;
- return((int)nnq[i].event);
- }
- j=i;
- i=nnq[i].next;
- }
- return(0);
- }
-
- /***********************************************************************/
- /* netputevent
- * 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 netputevent(class,what,dat)
- int class,what,dat;
- {
- int i;
-
- i=nnelast;
- nnq[i].eclass=(uint8)class; /* put data in */
- nnq[i].event=(uint8)what;
- nnq[i].idata=dat;
- if(nnefree>=0) { /* there is a spot in free list */
- nnq[i].next=nnelast=nnefree;
- nnefree=nnq[nnefree].next; /* remove from free list */
- return(0);
- }
- else {
- nnq[i].next=nnelast=nnefirst;
- nnefirst=nnq[nnefirst].next; /* lose oldest event */
- return(1);
- }
- }
-
- /***************************************************************************/
- /* netputuev
- * put a unique event into the queue
- * First searches the queue for like events
- */
- int netputuev(class,what,dat)
- int class,what,dat;
- {
- int i;
-
- i=nnefirst;
- while(i!=nnelast) {
- if(nnq[i].idata==dat && nnq[i].event==(uint8)what && nnq[i].eclass==(uint8)class)
- return(0);
- i=nnq[i].next;
- }
- return(netputevent(class,what,dat));
- }
-
- /************************************************************************/
- /* neterrstring
- * returns the string associated with a particular error number
- *
- * error number is formatted %4d at the beginning of the string
- */
- static char *errs[]={
- " 0 Error unknown",
- " 100 Network jammed, probable break in wire",
- " 101 Could not initialize hardware level network driver",
- " 102 ERROR: The conflicting machine is using the same IP number",
- " 103 RARP request failed, an IP number is required",
- " 300 Bad IP checksum",
- " 301 IP packet not for me",
- " 302 IP packet with options received",
- " 303 IP: unknown higher layer protocol",
- " 304 IP: fragmented packet received, frags not supported",
- " 400 TCP: bad checksum",
- " 401 ACK invalid for TCP syn sent",
- " 403 TCP in unknown state",
- " 404 Invalid port for TCPsend",
- " 405 TCP connection reset by other host",
- " 406 Null port specified for ackandtrans",
- " 407 Packet received for invalid port -- reset sent",
- " 500 No internal TCP ports available",
- " 501 Warning: Event queue filled, probably non-fatal",
- " 504 Local host or gateway not responding",
- " 505 Memory allocation error, cannot open port",
- " 506 Not allowed to connect to broadcast address",
- " 507 Reset received: syn sent, host is refusing connection",
- " 600 ICMP: Echo reply",
- " 603 ICMP: Destination unreachable",
- " 604 ICMP: Source Quench",
- " 605 ICMP: Redirect, another gateway is more efficient",
- " 608 ICMP: Echo requested (ping requested)",
- " 611 ICMP: Time Exceeded on Packet",
- " 612 ICMP: Parameter problem in IP",
- " 613 ICMP: Timestamp request",
- " 614 ICMP: Timestamp reply",
- " 615 ICMP: Information request",
- " 616 ICMP: Information reply",
- " 699 ICMP: Checksum error",
- " 700 Bad UDP checksum",
- " 800 Domain: Name request to server failed",
- " 801 Domain: Using default domain",
- " 802 Domain: name does not exist",
- " 803 Domain: UDP name server did not resolve the name",
- " 804 Domain: name server failed, unknown reason",
- " 805 Host machine not in configuration file",
- " 806 Missing IP number, requires domain lookup",
- " 900 Session: Cannot find or open configuration file",
- " 901 Session: Cannot allocate memory for processing",
- " 902 Session: Invalid keyword in configuration file",
- " 903 Session: Element too long (>200), maybe missing quote",
- " 904 Session: Probable missing quote marks, a field must be on one line",
- " 905 Session: 'name' field required before other machine entries",
- " 906 Session: Syntax error, invalid IP number",
- " 907 Session: Syntax error, Subnet mask invalid",
- " 908 Session: Syntax error, IP address for this PC is invalid",
- ""};
-
- static char errspace[80]; /* room for user-defined errors */
-
- char *neterrstring(errno)
- int errno;
- {
- int i;
- char s[10];
-
- if (errno<0)
- return(errspace);
- sprintf(s,"%4d",errno);
- i=0;
- do {
- if(!strncmp(errs[i],s,4))
- return(errs[i]+5); /* pointer to error message */
- i++;
- }while(*errs[i]||i>100); /* until NULL found */
- return(errs[0]+5); /* error unknown */
- }
-