+# include "defs.h"
+struct packet *xptr, *gptr;
+# define PACKETLENGTH (datasize + sizeof *xptr - 1)
+# define ACKLENGTH (sizeof *xptr - 1)
+static int bufleft;
+int atime = ATIME;
+int maxbread = MAXBREAD;
+int datasize;
+static char savebuf[BUFSIZ], retransmit;
+static jmp_buf env;
+short masterseqno, lastseqno;
+FILE *readtty,*writetty;
+struct dumpstruc dump;
+
+/*
+ one problem has been character loss on
+ overloaded systems due to the daemon
+ taking too long to swap in
+ and losing characters.
+ A high priority process of small size
+ with a pipe would do the job.
+*/
+alarmint(){
+ errno = 100;
+ signal(SIGCLK,SIG_IGN); /* alarm off */
+ longjmp(env,0); /* ugh */
+ }
+/* returns number of bytes written, error returns WRITEFAIL (-3) */
+xwrite(inbuf,size,amt)
+ char *inbuf;
+{
+ struct packet *rpp;
+ int cnt, num, savetime;
+ register char *p, *b;
+ register int i;
+ if(xptr == NULL)xptr = (struct packet *)calloc((PACKETLENGTH+20)/4,4);
+ if(xptr == NULL){error("xptr NULL"); return(WRITEFAIL); }
+ amt = amt * size;
+ cnt = 0;
+ retransmit = 0;
+ savetime = atime;
+ while(amt > 0){
+ if(retransmit > maxbread){
+ debug("xwrite fail");
+ return(WRITEFAIL);
+ }
+ b = inbuf+cnt;
+ num = min(datasize,amt);
+ xptr->pcode = REQUEST;
+ xptr->seqno = masterseqno;
+ xptr->len = num;
+ p = xptr->data;
+ i = num;
+ while(i--)*p++ = *b++;
+ sendpacket(xptr);
+ rpp = getpacket();
+ if(rpp == NULL){
+ atime += 3; /* wait three more secs */
+ retransmit++;
+ dump.nretrans++;
+ continue;
+ }
+ if(rpp->chksum != 0 || rpp->pcode != ACK
+ || rpp->seqno != xptr->seqno ){
+ if(rpp->pcode == RESET){
+ error("reset");
+ return(WRITEFAIL);
+ }
+ if(rpp->seqno == 1 && rpp->pcode == REQUEST){
+ error("collision");
+ return(WRITEFAIL);
+ }
+ if(rpp->chksum != 0)
+ error("chksum %d",rpp->seqno);
+ else if(rpp->pcode != ACK)
+ error("not ack %d %d",rpp->pcode,rpp->seqno);
+ else if(rpp->seqno != xptr ->seqno)
+ error("WRSQNO got %d request %d",rpp->seqno,
+ xptr->seqno);
+ atime += 3;
+ retransmit++;
+ dump.nretrans++;
+ continue;
+ }
+ masterseqno++;
+ amt -= num;
+ retransmit = 0;
+ cnt += num;
+ }
+ atime = savetime;
+ return(cnt/size);
+ }
+/* return the number of bytes read, or error = BROKENREAD (-2) */
+nread(b,size,num)
+ register char *b;
+{
+ register char *p;
+ int bcnt = 0;
+ char *q;
+ register struct packet *pp;
+ int n,j,cnt;
+ num = num * size;
+ cnt = 0;
+ if(bufleft > 0){
+ p = savebuf;
+ cnt = n = min(bufleft,num);
+ while(n--)*b++ = *p++;
+ num -= cnt;
+ bufleft -= cnt;
+ if(bufleft > 0){
+ q = savebuf;
+ n = bufleft;
+ while(n--)*q++ = *p++;
+ }
+ }
+ if(num <= 0)
+ return(cnt/size);
+ retransmit = 0;
+ for(;;){
+ pp = getpacket();
+ if(pp == NULL){
+ if(++bcnt >= maxbread){
+ debug("read timeout");
+ return(BROKENREAD);
+ }
+ continue;
+ }
+ if(pp->chksum != 0){
+ error("chksum %d",pp->seqno);
+ retransmit++;
+ continue;
+ }
+ if(pp->pcode & ~(REQUEST|RESET)){
+ error("pcode %d %d",pp->pcode,pp->seqno);
+ retransmit++;
+ continue;
+ }
+ else if(pp->pcode == RESET)break;
+ else { /* else was a REQUEST packet, no chksum errs */
+ pp->pcode = ACK;
+ n = pp->len;
+ pp->len = 0;
+ sendpacket(pp); /* send ACK */
+ pp->len = n;
+ break;
+ }
+ }
+ retransmit = 0;
+ j = n = min(num,pp->len);
+ cnt += j;
+ p = pp->data;
+ while(n--)*b++ = *p++;
+ if(pp->len > num){
+ n = bufleft = pp->len - num;
+ q = savebuf;
+ while(n--)*q++ = *p++;
+ }
+ return(cnt/size);
+ }
+printpacket(pp,dest)
+ char *dest;
+ struct packet *pp; {
+ char *s;
+ int i;
+ char c;
+ dest[0] = 0;
+ if(pp == NULL)return;
+ if(pp->pcode == REQUEST)c='r';
+ else if(pp->pcode == ACK)c = 'a';
+ else if(pp->pcode == RESET)c = 'x';
+ else if(pp->pcode == PURGE)c = 'p';
+ else c = 'u';
+ sprintf(dest,"p:%d len:%d c:%c d:", pp->seqno, pp->len, c);
+ s = dest + strlen(dest);
+ for(i=0; i<pp->len && pp->data[i]; i++)*s++ = pp->data[i];
+ *s = 0;
+ }
+/*
+ * A purge can always be sent -
+ * the receiver totally ignores it.
+ * It is used to push the packet terminator
+ * down the wire in case of a crash
+ * leaving the receiver half reading.
+ */
+sendpurge()
+ {
+ if(xptr == NULL)xptr = (struct packet *)calloc((PACKETLENGTH+20)/4,4);
+ if(xptr == NULL){
+ error("bad xptr");
+ return;
+ }
+ xptr->pcode = PURGE;
+ xptr->seqno = 0;
+ xptr->len = 0;
+ debug("send purge");
+ sendpacket(xptr);
+ }
+/*
+ * A reset is sent by the sender whenever he begins to send.
+ * It is not acknowledged.
+ * The receiver must be prepared, when he receives a reset,
+ * to receive a new transmission.
+ */
+sendreset()
+ {
+ if(xptr == NULL)xptr = (struct packet *)calloc((PACKETLENGTH+20)/4,4);
+ if(xptr == NULL){
+ error("bad xptr");
+ return;
+ }
+ xptr->pcode = RESET;
+ xptr->seqno = 0;
+ xptr->len = 0;
+ debug("send reset");
+ sendpacket(xptr);
+ }
+/*
+ * Getreset returns in either of two cases:
+ * 1) the read times out (return BROKENREAD)
+ * 2) a reset packet is received (return 0)
+ * all other packets received are ignored.
+ */
+getreset() {
+ register struct packet *pp;
+ register int bcnt = 0;
+ bufleft = 0; /* if any chars are left in buffer, flush them*/
+ atime = ATIME + ((rand()>>8)%15);
+ lastseqno = -1; /* forces non-RESET pks to not be ACK-ed */
+ for(;;){
+ pp = getpacket();
+ if(pp == NULL){
+ if(++bcnt >= maxbread){
+ debug("reset timeout");
+ return(BROKENREAD);
+ }
+ continue;
+ }
+ if(pp->pcode == RESET){
+ debug("got reset");
+ addtolog(remote,"^R%c ",remote);
+ lastseqno = 0;
+ return(0);
+ }
+ }
+ }
+/*
+ * Just sends packet pp
+ * Calculates the chksum
+ */
+sendpacket(pp)
+ struct packet *pp; {
+ char buf[BUFSIZ];
+ int len, n, i;
+ long nt,ot;
+ register char *q, *p;
+ register int j;
+ dump.nbytesent += pp->len;
+ dump.npacksent++;
+ pp->chksum = 0;
+ n = 0;
+ p = (char *)pp;
+ len = ACKLENGTH + pp->len;
+ for(j = 0; j < len; j++)n ^= *p++;
+ pp->chksum = n;
+ p = buf;
+ q = (char *)pp;
+ len = n = (len+2)/3;
+ while(n--){
+ *p++ = (*q & 077) + INCR;
+ j = (*q++ >> 6) &03;
+ *p++ = (((*q << 2) | j) & 077) + INCR;
+ j = (*q++ >> 4) & 017;
+ *p++ = (((*q << 4) | j) & 077) + INCR;
+ *p++ = ((*q++ >> 2) & 077) + INCR;
+ }
+ *p++ = '\n';
+ *p = 0;
+/* because of bugs in processing around erase and kill in v6 */
+ for(p=buf; *p; p++)
+ if(*p == '\\')*p = '}';
+ /*
+ debug("send %d %s",len*4+1,buf);
+ */
+ ot = gettime();
+ i = fwrite(buf,1,len*4+1, writetty);
+ nt = gettime();
+ dump.waittime += (nt - ot); /* add time writing */
+ /*
+ debug("count %d",i);
+ */
+ fflush(writetty);
+ }
+/*
+ * returns NULL if couldn't get a packet with correct seqno
+ * chksum not checked here
+ * because other programs may want to interrogate checksum
+ */
+struct packet *getpacket() {
+ char buf[BUFSIZ];
+ int i,n,j, len, plen;
+ int bcnt;
+ register char *q, *p;
+ long ot, nt;
+ if(gptr == NULL)gptr = (struct packet *)calloc((PACKETLENGTH+20)/4,4);
+ if(gptr == NULL){error("gptr NULL"); return(NULL); }
+ bcnt = 0;
+ errno = 0;
+ setjmp(env);
+ alarm(0);
+ signal(SIGCLK,alarmint);
+ for(;;){
+ if(bcnt++ > maxbread)errno = 100; /* give up */
+ if(errno == 100){
+ if(debugflg)putchar('^');
+ return(NULL);
+ }
+ alarm(atime);
+ ot = gettime();
+ p = fgets(buf,BUFSIZ,readtty);
+ alarm(0);
+ nt = gettime();
+ dump.waittime += (nt - ot);
+ if(p == NULL){error("getpacket fails"); return(NULL); }
+ plen = strlen(buf);
+ /*
+ debug("receive %d %s",plen,buf);
+ */
+ /* remove this loop later */
+ for(p=buf; *p; p++)
+ if(*p == '}')*p = '\\';
+ p = buf;
+ q = (char *)gptr;
+ n = (strlen(buf)+3) /4;
+ while(n--){
+ if(*p == '\n')break;
+ if(*p < INCR || *p & 0200)error("bad char %o\n",*p);
+ i = *p++ - INCR;
+ j = *p++ - INCR;
+ *q++ = ((j & 03) << 6) | (i & 077);
+ i = *p++ -INCR;
+ *q++ = ((i & 017) << 4) | ((j >> 2) & 017);
+ j = *p++ - INCR;
+ *q++ = ((j & 077) << 2) | ((i >> 4) & 03);
+ }
+ *q = 0;
+ if(plen != ((ACKLENGTH + gptr->len + 2)/3)*4 + 1){
+ error("too short %d",gptr->seqno);
+ continue;
+ }
+ if(gptr->pcode == PURGE){
+ debug("got purge");
+ continue; /* never seen */
+ }
+ if(gptr->pcode == RESET)
+ break;
+ if(gptr->seqno == lastseqno){
+ if(retransmit)break;
+ /* send ACK - it was lost first time thru */
+ len = gptr->len;
+ n = gptr->pcode;
+ gptr->len = 0;
+ gptr->pcode = ACK;
+ sendpacket(gptr);
+ gptr->len = len;
+ gptr->pcode = n;
+ error("sendlostack %d",lastseqno);
+ break;
+ }
+ if(gptr->seqno == lastseqno + 1)break;
+ error("Wrong seq no g: %d last: %d",gptr->seqno,
+ lastseqno);
+ }
+ lastseqno = gptr->seqno;
+ n = 0;
+ len = gptr->len + ACKLENGTH;
+ p = (char *)gptr;
+ for(i=0; i < len; i++)n ^= *p++;
+ gptr->chksum = n;
+ if(n != 0)dump.ncksum++;
+ dump.nbytercv += gptr->len;
+ dump.npackrcv++;
+ return(gptr);
+}