home *** CD-ROM | disk | FTP | other *** search
- /*
- For best results in visual layout while viewing this file, set
- tab stops to every 8 columns.
- */
-
- /*
- dcpgpkt.c
-
- Revised edition of dcp
-
- Stuart Lynne May/87
-
- Copyright (c) Richard H. Lamb 1985, 1986, 1987
- Changes Copyright (c) Stuart Lynne 1987
-
- Maintenance notes:
-
- 25Aug87 - Allow for up to 7 windows - Jal
- 01Nov87 - those strncpy's should really be memcpy's! - Jal
- */
-
- /* "DCP" a uucp clone. Copyright Richard H. Lamb 1985,1986,1987 */
-
- /*
- * Cleaned up by Stephen Trier, April 21, 1990
- */
-
- /* 7-window "g" ptotocol */
-
- /*
- Thanks goes to John Gilmore for sending me a copy of Greg Chesson's
- UUCP protocol description -- Obviously invaluable.
- Thanks also go to Andrew Tannenbaum for the section on Siding window
- protocols with a program example in his "Computer Networks" book.
- */
-
- #include "dcp.h"
-
- #define PKTSIZE 64
- #define PKTSIZ2 2 /* 8x(2**2) = 64 */
-
- #define HDRSIZE 6
- #define MAXTRY 4
-
- /* g-packet type definitions */
-
- #define DATA 0
- #define CLOSE 1
- #define NAK 2
- #define SRJ 3
- #define ACK 4
- #define INITC 5
- #define INITB 6
- #define INITA 7
-
- #define MAXERR 200 /* Don't want to quit in a middle of a long file */
- #define TIMEOUT 4 /* could be longer */
- #define KPKT 1024/PKTSIZE
- #define POK -1
-
- #define MAXWINDOW 7
- #define SWINDOW 3 /* fixed now, you make it variable! (done.) */
- #define RWINDOW 3
- #define NBUF 8 /* always SAME as MAXSEQ ? */
- #define MAXSEQ 8
-
- #define between(a,b,c) ((a<=b && b<c) || (c<a && a<=b) || (b<c && c<a))
-
- /* packet definitions */
-
- static int rwl, swl, swu, rwu, nerr, nbuffers, npkt, irec, timeout;
- static int GOT_SYNC, GOT_HDR;
- static int fseq[NBUF], outlen[NBUF], inlen[NBUF], arrived[NBUF];
- static int nwindows;
- static char outbuf[NBUF][PKTSIZE+1], inbuf[NBUF][PKTSIZE+1];
- static unsigned char grpkt[HDRSIZE+1];
- static long ftimer[NBUF], acktmr, naktmr;
-
- static void gspack();
- static int grpack(), gmachine();
-
-
- /****************** SUB SUB SUB PACKET HANDLER ************/
-
- /*
- g o p e n p k
- */
-
- int gopenpk()
- {
- int i, xxx, yyy, len;
- char tmp[PKTSIZE+1];
-
- pktsize = PKTSIZE; /* change it later after the init */
- msgtime = MSGTIME; /* not sure I need this for "g" proto */
-
- /* initialize proto parameters */
- swl = nerr = nbuffers = npkt = 0;
- swl = swu = 1;
- rwl = 0;
- rwu = MAXWINDOW - 1;
- nwindows = MAXWINDOW;
- for (i = 0; i < NBUF; i++) {
- ftimer[i] = 0;
- arrived[i] = FALSE;
- }
- GOT_SYNC = GOT_HDR = FALSE;
-
- /* 3-way handshake */
- timeout = 2; /* want some timeout capability here */
- gspack(INITA, 0, 0, 0, tmp);
- rsrt:
- if (nerr >= MAXERR)
- return(FAILED);
-
- /* INIT sequence. Easy fix for variable packet size.
- I didn't since all the machines I talk to use PKTSZ=64.
- If you do this make sure to reflect the changes in "grpack"
- and "gspack" */
-
- switch (grpack(&yyy, &xxx, &len, tmp)) {
- case INITA:
- gspack(INITB, 0, 0, 0, tmp); /* data segment (packet) size */
- nwindows = yyy;
- if (nwindows > MAXWINDOW)
- nwindows = MAXWINDOW;
- rwu = nwindows - 1;
- goto rsrt;
- case INITB:
- gspack(INITC, 0, 0, 0, tmp);
- goto rsrt;
- case INITC:
- break;
- default:
- nerr++;
- gspack(INITA, 0, 0, 0, tmp);
- goto rsrt;
- }
-
- nerr = 0;
- return(OK); /* channel open */
-
- } /*gopenpk*/
-
-
- /*
- g c l o s e p k
- */
-
- int gclosepk()
- {
- int i;
- char tmp[PKTSIZE+1];
-
- timeout = 1;
- for (i = 0; i < MAXTRY; i++) {
- gspack(CLOSE, 0, 0, 0, tmp);
- if (gmachine() == CLOSE)
- break;
- }
-
- printmsg(0, "%d packets transferred, %d errors.", npkt, nerr);
-
- return(0);
-
- } /*gclosepk*/
-
-
- /*
- g g e t p k t
-
- Gets no more than a packet's worth of data from
- the "packet I/O state machine". May have to
- periodically run the packet machine to get some packets.
-
- on input: don't care
- on return: data+\0 and length in len.
-
- ret(0) if all's well
- ret(-1) if problems (failed)
- */
-
- int ggetpkt(data, len)
- char *data;
- int *len;
- {
- int i;
-
- irec = 1;
- timeout = 0;
-
- /* LOOP TO WAIT FOR THE DESIRED PACKET */
- while ((arrived[rwl]) == FALSE)
- if (gmachine() != POK)
- return(-1);
-
- /* GOT A PACKET! */
- i = rwl; /*<-- mod(,rwindow) for larger than 8 seq no.s */
- *len = inlen[i];
- memcpy(data, inbuf[i], *len);
-
- arrived[i] = FALSE;
- rwu = (rwu + 1) % MAXSEQ; /* bump receive window */
- npkt++;
-
- return(0);
-
- } /*ggetpkt*/
-
-
- /*
- g s e n d p k t
-
- Put at most a packet's worth of data in the packet state
- machine for transmission.
- May have to run the packet machine a few times to get
- an available output slot.
-
- on input: data=*data; len=length of data in data.
- flg = 2 just send the packet with no wait for ack.
- flg > 0 zero out the unused part of the buffer. (for UUCP "msg" pkts)
- flg = 0 normal data
-
- return:
- 0 if all's well
- -1 if problems (failed)
- */
-
- int gsendpkt(data, len, flg)
- char *data;
- int len, flg;
- {
- int i1;
-
- irec = 0;
- timeout = 0; /* non-blocking reads */
- /* WAIT FOR INPUT i.e. if weve sent SWINDOW pkts and none have been
- acked, wait for acks */
- while (nbuffers >= nwindows)
- if (gmachine() != POK)
- return(-1);
-
- i1 = swu; /* <--If we ever have more than 8 seq no.s, must mod() here */
-
- /* PLACE PACKET IN TABLE AND MARK UNACKED */
-
- /* fill with zeros or not */
- if (flg) {
- int i;
- strcpy(outbuf[i1], data);
- for (i = strlen(data); i < PKTSIZE; i++)
- outbuf[i1][i] = '\0';
- len = PKTSIZE;
- } else {
- memcpy(outbuf[i1], data, len);
- outbuf[i1][len] = '\0';
- }
-
- /* mark packet */
- outlen[i1] = len;
- ftimer[i1] = time(nil(long));
- fseq[i1] = swu;
- swu = (swu + 1) % MAXSEQ; /* bump send window */
- nbuffers++;
- npkt++;
-
- /* send it */
- gspack(DATA, rwl, fseq[i1], outlen[i1], outbuf[i1]);
- /* send it once then let the packet machine take it.
- Wouldn't need this for multi-tasking systems. */
- /* sl gmachine(); */
-
- return(0);
-
- } /*gsendpkt*/
-
-
- /********** Packet Machine ********** RH Lamb 3/87 */
-
- /*
- g m a c h i n e
-
- Ideally we would like to fork this process off in an infinite loop and
- send and receive packets through "inbuf" and "outbuf". Can't do this in
- MS-DOS so we setup "getpkt" and "sendpkt" to call this routine often and
- return only when the input buffer is empty thus "blocking" the packet-
- machine task.
- */
-
- static int gmachine()
- {
- int rack, rseq, rlen, i1, i2, dflg;
- char rdata[PKTSIZE+1];
- long itmp;
-
- reply:
- printmsg(10, "* send %d < W < %d, receive %d < W < %d, error %d",
- swl, swu, rwl, rwu, nerr);
- /* waiting for ACKs for swl to swu-1. Next pkt to send=swu */
- /* rwl=expected pkt */
- printmsg(7, "Kbytes transfered %d errors %d", npkt / KPKT, nerr);
- if (nerr >= MAXERR)
- goto close;
- dflg = 0;
-
- switch (grpack(&rack, &rseq, &rlen, rdata)) {
-
- case CLOSE:
- printmsg(5, "**got CLOSE");
- goto close;
-
- case NAK:
- nerr++;
- acktmr = naktmr = 0; /* stop ack/nak timer */
- printmsg(5, "**got NAK %d", rack);
- nloop:
- if (between(swl, rack, swu)) { /* resend rack->(swu-1) */
- i1 = rack;
- gspack(DATA, rwl, rack, outlen[i1], outbuf[i1]);
- printmsg(5, "*** resent %d", rack);
- ftimer[i1] = time(nil(long));
- rack = (rack + 1) % MAXSEQ;
- goto nloop;
- }
- if (dflg)
- return(POK);
- goto reply; /* any other stuff ? */
-
- case EMPTY:
- printmsg(5, "**got EMPTY");
- itmp = time(nil(long));
- if (acktmr)
- if ((itmp - acktmr) >= TIMEOUT) { /* ack timed out*/
- gspack(ACK, rwl, 0, 0, rdata);
- acktmr = itmp;
- }
- if (naktmr)
- if ((itmp - naktmr) >= TIMEOUT) { /*nak timed out*/
- gspack(NAK, rwl, 0, 0, rdata);
- naktmr = itmp;
- }
- /* resend any timed out un-acked pkts */
- for (i2 = swl; between(swl, i2, swu); i2 = (1 + i2) % MAXSEQ) {
- acktmr = naktmr = 0; /* reset ack/nak */
- i1 = i2;
- printmsg(5, "---> seq, elapst %d %ld", i2, (itmp - ftimer[i1]));
- if ((itmp - ftimer[i1]) >= TIMEOUT) {
- printmsg(5, "*** timeout %d", i2);
- /* Since "g" is "go-back-N", when we time out we
- must send the last N pkts in order. The generalized
- sliding window scheme relaxes this reqirment. */
- nerr++;
- dflg = 1; /* same hack */
- rack = i2;
- goto nloop;
- }
- }
- return(POK);
-
- case ACK:
- printmsg(5, "**got ACK %d", rack);
- acktmr = naktmr = 0; /* disable ack/nak's */
- aloop:
- if (between(swl, rack, swu)) { /* S<-- -->(S+W-1)%8 */
- printmsg(5, "*** ACK %d", swl);
- ftimer[swl] = 0;
- nbuffers--;
- swl = (1 + swl) % MAXSEQ;
- dflg = 1; /* same hack */ /* sl */
- goto aloop;
- }
- if (dflg)
- return(POK); /* hack for non-mtask sys's */
- /* to empty "inbuf[]" */
- goto reply;
-
- case DATA:
- printmsg(5, "**got DATA %d %d", rack, rseq);
- i1 = (rwl + 1) % MAXSEQ; /* (R+1)%8 <-- -->(R+W)%8 */
- i2 = (rwu + 1) % MAXSEQ;
- if (between(i1, rseq, i2)) {
- if (i1 == rseq) {
- i1 = rseq;
- arrived[i1] = TRUE;
- inlen[i1] = rlen;
- memcpy(inbuf[i1], rdata, rlen);
- rwl = (rwl + 1) % MAXSEQ;
- printmsg(5, "*** ACK d %d", rwl);
- gspack(ACK, rwl, 0, 0, rdata);
- acktmr = time(nil(long)); /* enable ack/nak tmout*/
- dflg = 1; /* return to call when finished */
- /* in a mtask system, unneccesary */
- } else {
- nerr++;
- printmsg(5, "*** unexpect %d ne %d", rseq, rwl);
- }
- } else {
- nerr++;
- printmsg(5, "*** wrong seq %d", rseq);
- }
- goto aloop;
-
- case ERROR:
- nerr++;
- printmsg(5, "*** got BAD CHK");
- gspack(NAK, rwl, 0, 0, rdata);
- naktmr = time(nil(long)); /* set nak timer */
- printmsg(5, "*** NAK d %d", rwl);
- goto reply;
-
- default:
- printmsg(5, "*** got SCREW UP");
- goto reply; /* ignore it */
-
- }
-
- close:
- gspack(CLOSE, 0, 0, 0, rdata);
- return(CLOSE);
-
- } /*gmachine*/
-
-
- /*************** FRAMMING *****************************/
-
- /*
- g s p a c k
-
- Send a packet
-
- type=type yyy=pkrec xxx=timesent len=length<=PKTSIZE data=*data
- ret(0) always
- */
-
- static void gspack(type, yyy, xxx, len, data)
- int type, yyy, xxx, len;
- char data[];
- {
- unsigned int check, i;
- unsigned char header[HDRSIZE+1];
-
- /***** Link Testing Mods *****/
- /* unsigned char dpkerr[10]; /**/
- /***** End Link Testing Mods *****/
-
- if (len > PKTSIZE) /* just in case */
- len = PKTSIZE;
- if (len == 0)
- data[0] = '\0';
-
- /***** Link Testing Mods - create artificial errors *****/
- /* printf("**n:normal,e:error,l:lost,p:partial,h:bad header,s:new seq--> ");
- gets(dpkerr);
- if (dpkerr[0] == 's')
- sscanf(&dpkerr[1], "%d", &xxx); /**/
- /***** End Link Testing Mods *****/
-
- printmsg(5, "send packet type %d, yyy=%d, xxx=%d, len=%d",
- type, yyy, xxx, len);
- printmsg(5, "data=|%s|", data);
- header[0] = '\020';
- type %= 8;
- header[4] = type << 3;
- switch (type) {
- case CLOSE:
- break; /* stop protocol */
- case NAK:
- header[4] += yyy;
- break; /* reject */
- case SRJ:
- break;
- case ACK:
- header[4] += yyy;
- break; /* ack */
- case INITC:
- header[4] += nwindows;
- break;
- case INITB:
- header[4] += 1;
- break; /* pktsiz = 64 (1) */
- case INITA:
- header[4] += MAXWINDOW;
- break;
- case DATA:
- header[4] = 0x80 + (xxx << 3) + yyy;
- /* havn't set it up for VERY LONG packets with a few
- bytes yet (-128) */
- if (len < PKTSIZE) { /* short packet? */
- header[4] |= 0x40;
- memmove(data + 1, data, PKTSIZE - 1);
- data[0] = PKTSIZE - len;
- }
- break;
- }
- if (type != DATA) {
- header[1] = 9; /* control packet size = 0 (9) */
- check = (0xaaaa - header[4]) & 0xffff;
- } else {
- header[1] = PKTSIZ2; /* data packet size = 64 (2) */
- check = checksum(data, PKTSIZE);
- i = header[4]; /* got to do this on PC for ex-or high bits */
- i &= 0xff;
- check = (check ^ i) & 0xffff;
- check = (0xaaaa - check) & 0xffff;
- }
- header[2] = check & 0xff;
- header[3] = (check >> 8) & 0xff;
- header[5] = (header[1] ^ header[2] ^ header[3] ^ header[4]) & 0xff;
-
- /***** More Link Testing Mods *****/
- /* switch(dpkerr[0]) {
- case 'e':
- data[10] = - data[10];
- break;
- case 'h':
- header[5] = - header[5];
- break;
- case 'l':
- return;
- case 'p':
- swrite(header, HDRSIZE);
- if (header[1] != 9)
- swrite(data, PKTSIZE - 3);
- return;
- default:
- break;
- } /**/
- /***** End Link Testing Mods *****/
-
- swrite(header, HDRSIZE); /* header is 6-bytes long */
- if (header[1] != 9)
- swrite(data, PKTSIZE); /* data is always 64 bytes long */
-
- } /*gspack*/
-
-
- /*
- g r p a c k
-
- Read packet
-
- on return: yyy=pkrec xxx=pksent len=length<=PKTSIZE data=*data
-
- ret(type) ok
- ret(EMPTY) input buf empty
- ret(ERROR) bad header
-
- ret(EMPTY) lost packet timeout
- ret(ERROR) checksum error
- ret(-5) packet size != 64
-
- NOTE (specifications for sread()):
-
- sread(buf, n, timeout)
- while(TRUE) {
- if (# of chars available >= n) (without dec internal counter)
- read n chars into buf (decrement internal char counter)
- break
- else
- if (time > timeout)
- break
- }
- return(# of chars available)
-
- */
-
- static int grpack(yyy, xxx, len, data)
- int *yyy, *xxx, *len;
- char data[];
- {
- unsigned int type, check, checkchk, i;
- unsigned char c, c2;
-
- if (GOT_SYNC)
- goto get_hdr;
-
- if (GOT_HDR)
- goto get_data;
-
- do {
- if (sread(&c, 1, timeout) == 0)
- return(EMPTY);
- } while ((c & 0x7f) != '\020');
-
- GOT_SYNC = TRUE;
- get_hdr:
- if (sread(&grpkt[1], HDRSIZE - 1, timeout) < (HDRSIZE - 1))
- return(EMPTY);
- GOT_SYNC = FALSE;
- /* i = grpkt[1] ^ grpkt[2] ^ grpkt[3] ^ grpkt[4] ^ grpkt[5]; */
- i = (unsigned)grpkt[1] ^ (unsigned)grpkt[2] ^
- (unsigned)grpkt[3] ^ (unsigned)grpkt[4] ^
- (unsigned)grpkt[5];
-
- i &= 0xff;
- printmsg(10, "prpkt %02x %02x %02x %02x %02x .. %02x ",
- grpkt[1], grpkt[2], grpkt[3], grpkt[4], grpkt[5], i);
-
- if (i != 0) { /* bad header */
- printmsg(0, "*** bad header ***");
- return(ERROR); /* I'm not sure whether "g" considers it an empty or error */
- }
-
- GOT_HDR = TRUE;
- if (grpkt[1] == 9) { /* control packet */
- *data = '\0';
- *len = 0;
- c = grpkt[4];
- type = c >> 3;
- *yyy = c & 0x07;
- *xxx = 0;
- check = 0;
- checkchk = 0;
- GOT_HDR = FALSE;
- } else { /* data packet */
- if (grpkt[1] != PKTSIZ2)
- return(-5); /* can't handle packet size other than 64 */
- get_data:
- if (sread(data, PKTSIZE, timeout) < PKTSIZE)
- return(EMPTY);
- GOT_HDR = FALSE;
- type = 0;
- c2 = grpkt[4];
- c = c2 & 0x3f;
- *xxx = c >> 3;
- *yyy = c & 0x07;
- i = grpkt[3];
- i = (i << 8) & 0xff00;
- check = grpkt[2];
- check = i | (check & 0xff);
- checkchk = checksum(data, PKTSIZE);
- i = grpkt[4] | 0x80;
- i &= 0xff;
- checkchk = 0xaaaa - (checkchk ^ i);
- checkchk &= 0xffff;
- if (checkchk != check) {
- printmsg(4, "*** checksum error ***");
- return(ERROR);
- }
- *len = PKTSIZE;
- /* Haven't set it up for very long pkts yet (>128). RH Lamb */
- if (c2 & 0x40) {
- int ii;
- ii = (data[0] & 0xff);
- *len = (*len - ii) & 0xff;
- memmove(data, data + 1, *len);
- }
- data[*len] = '\0';
- }
-
- printmsg(12, "receive packet type %d, yyy=%d, xxx=%d, len=%d",
- type, *yyy, *xxx, *len);
- printmsg(13, " checksum rec=%04x comp=%04x\ndata=|%s|",
- check, checkchk, data);
-
- return(type);
-
- } /*grpack*/
-
-
- /*
- c h e c k s u m
- */
-
- unsigned checksum(data, len)
- int len;
- char data[];
- {
- unsigned int i, j, tmp, chk1, chk2;
- chk1 = 0xffff;
- chk2 = 0;
- j = len;
- for (i = 0; i < len; i++) {
- if (chk1 & 0x8000) {
- chk1 <<= 1;
- chk1++;
- } else {
- chk1 <<= 1;
- }
- tmp = chk1;
- chk1 += (data[i] & 0xff);
- chk2 += chk1 ^ j;
- if ((chk1 & 0xffff) <= (tmp & 0xffff))
- chk1 ^= chk2;
- j--;
- }
- return(chk1 & 0xffff);
-
- } /*checksum*/
-
-
- #if FALSE
-
- /*
- gwrmsg - send a null terminated string out
- */
-
- gwrmsg(typ, buf)
- char typ;
- char *buf; /* null terminated */
- {
-
- } /*gwrmsg*/
-
-
- /*
- grdmsg - read a null terminated string
- */
-
- grdmsg(buf)
- char *buf;
- {
-
- } /*grdmsg*/
-
-
- /*
- gwrdata - read a file and send it out
- */
-
- gwrdata(f)
- {
-
- } /*gwrdata*/
-
-
- /*
- grrdata - read in data and send to file
- */
-
- grrdata(f)
- {
-
- } /*grrdata*/
-
-
- /*
- grdblk - read a block of data in
- */
-
- grdblk(blk, len)
- {
-
- } /*grdblk*/
-
-
- /*
- gwrblk - write out a block of data
- */
-
- gwrblk(blk, len)
- {
-
- } /*gwrblk*/
-
- #endif