/* 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= 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