/*         dcpgpkt.c

         Revised edition of dcp

         Stuart Lynne May/87

         Copyright (c) Richard H. Lamb 1985, 1986, 1987
         Changes Copyright (c) Stuart Lynne 1987

*/
/* "DCP" a uucp clone. Copyright Richard H. Lamb 1985,1986,1987 */
/* 3-window "g" ptotocol */
/* Thanks got 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
#define HDRSIZE         6
#define MAXTRY          4

#define MAXERR  200      /* Dont want to quit in a middle of a long file*/
#define TIMEOUT 4      /* could be longer */
#define KPKT    1024/PKTSIZE
#define POK      -1

#define SWINDOW 3   /* fixed now, U make it variable */
#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 defin */
static int   rwl, swl, swu, rwu, nerr, nbuffers, npkt, irec, timeout, GOT_SYNC, GOT_HDR;
static int   fseq[NBUF], outlen[NBUF], inlen[NBUF], arr[NBUF];
static char   outbuf[NBUF][PKTSIZE+1], inbuf[NBUF][PKTSIZE+1];
static unsigned char   grpkt[HDRSIZE+1];
static long   ftimer[NBUF], acktmr, naktmr;
/**/
/******************SUB SUB SUB PACKET HANDLER************/
gopenpk()
{
   int   i, j, n1, n2, 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 = RWINDOW - 1;
   for (i = 0; i < NBUF; i++) {
      ftimer[i] = 0;
      arr[i] = FALSE;
   }
   GOT_SYNC = GOT_HDR = FALSE;
   /* 3-way handshake */
   timeout = 1; /* want some timeout capability here */
   gspack(7, 0, 0, 0, tmp);
rsrt:
   if (nerr >= MAXERR)
      return(-1);
   /* INIT sequence. Easy fix for variable pktsize and windows. */
   /* I didnt since all the machines I talk to use W=3 PKTSZ=64 */
   /* If you do this make sure to reflect the changes in "grpack" */
   /* and "gspack" */
   switch (grpack(&n1, &n2, &len, tmp)) {
   case 7:
      gspack(6, 0, 0, 0, tmp);
      goto rsrt;
   case 6:
      gspack(5, 0, 0, 0, tmp);
      goto rsrt;
   case 5:
      break;
   default:
      nerr++;
      gspack(7, 0, 0, 0, tmp);
      goto rsrt;
   }
   nerr = 0;
   return(0);      /* channel open */
}


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, "number of errors %d and pkts xfered %d", nerr, npkt );
   return(0);
}


/*
 *
 * ggetpkt
 ***** description: Gets no more than a packet's worth of data from
****               the "packet i/o state machine". May have to
****               periodically run the pkt machine to get some
****               packets.
* on input: dont care   getpkt(data,&len)  char *data int len
* on return: data+\0 and length in len. ret(0) if alls well
* ret(-1) if problems.(fail)
 */
ggetpkt(cdata, len)
int   *len;
char   cdata[];
{
   int   i2;
   irec = 1;
   timeout = 0;
   /* WAIT FOR THE DESIRED PACKET */
   while ((arr[rwl]) == FALSE)
      if (gmachine() != POK)
         return(-1);
   /* GOT A PKT ! */
   i2 = rwl; /*<-- mod(,rwindow) for larger than 8 seq no.s */
   *len = inlen[i2];
   strncpy(cdata, inbuf[i2], *len);
   arr[i2] = FALSE;
   rwu = (1 + rwu) % MAXSEQ; /* bump rec window */
   npkt++;
   return(0);
}


/*
 *
 *  sendpkt
 *
*****   description:    Put at most a packet's worth of data  in the pkt state
 ****                   machine for xmission.
*****                   May have to run the pkt machine a few times to get
*****                   an available output slot.
 *
 * on input: char *data int len,flg; 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:   ret(0) if alls well ret(-1) if problems (fail)
 *
*/
gsendpkt(cdata, len, flg)
int   len, flg;
char   *cdata;
{
   int   i, i1;
   long   ttmp;
   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 >= SWINDOW)
      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) {
      strcpy(outbuf[i1], cdata);
      len = PKTSIZE;
      for (i = strlen(cdata); i < len; i++)
         outbuf[i1][i] = '\0';
   } else {
      strncpy(outbuf[i1], cdata, len);
      outbuf[i1][len] = '\0';
   }
   /* mark packet  */
   outlen[i1] = len;
   ftimer[i1] = time(&ttmp);
   fseq[i1] = swu;
   swu = (1 + swu) % MAXSEQ;   /* bump send window */
   nbuffers++;
   npkt++;
   /* send it */
   gspack(DATA, rwl, fseq[i1], outlen[i1], outbuf[i1]);
   /* send it once then let the pkt machine take it. wouldnt need this for */
   /* mtasking systems */
   /* sl gmachine(); */
   return(0);
}


/************     packet machine   ****** RH Lamb 3/87 */
/* Idealy we would like to fork this process off in an infinite loop */
/* and send and rec pkts thru "inbuf" and "outbuf". Cant 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 pkt-machine task. */
gmachine()
{
   int   rack, rseq, rlen, i1, i2, dflg;
   char   rdata[PKTSIZE+1];
   long   ttmp, itmp;
reply:
   printmsg( 6, "*send %d<W<%d, rec %d<W<%d, err %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\r", 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(&ttmp);
         rack = (1 + rack) % MAXSEQ;
         goto nloop;
      }
      if (dflg)
         return(POK);
      goto reply; /* any other stuff ? */

   case EMPTY:
      printmsg( 5, "**got EMPTY" );
      itmp = time(&ttmp);
      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 = (1 + rwl) % MAXSEQ; /* (R+1)%8 <-- -->(R+W)%8 */
      i2 = (1 + rwu) % MAXSEQ;
      if (between(i1, rseq, i2)) {
         if (i1 == rseq) {
            i1 = rseq;
            arr[i1] = TRUE;
            inlen[i1] = rlen;
            strncpy(inbuf[i1], rdata, rlen);
            rwl = (rwl + 1) % MAXSEQ;
            printmsg( 5, "***ACK d %d", rwl );
            gspack(ACK, rwl, 0, 0, rdata);
            acktmr = time(&ttmp); /* 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(&ttmp); /* 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);
}


/**/
/*************** FRAMMING *****************************/
/*
 *
 *
 *      send a packet
 * nt2=type nt3=pkrec nt4=pksent len=length<=PKTSIZE cnt1= data * ret(0) always
 */
gspack(nt2, nt3, nt4, len, cnt1)
int   nt2, nt3, nt4, len;
char   cnt1[];
{
   unsigned int   check, i;
   unsigned char   c2, pkt[HDRSIZE+1], dpkerr[10];
   if (len > 64)
      len = 64;
   if (len == 0)
      cnt1[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",&nt4); } /**/
   /** End Link testing mods ***/
   printmsg( 5, "send packet type %d, num=%d, n=%d, len=%d", nt2, nt3, nt4, len );
   printmsg( 5, "data =\n|%s|", cnt1 );
   c2 = '\0';
   pkt[0] = '\020';
   pkt[4] = nt2 << 3;
   nt2 &= 7;
   switch (nt2) {
   case 1:
      break;                  /* stop protocol */
   case 2:
      pkt[4] += nt3;
      break;   /* reject        */
   case 3:
      break;
   case 4:
      pkt[4] += nt3;
      break;   /* ack          */
   case 5:
      pkt[4] += SWINDOW;
      break;   /* 3 windows */
   case 6:
      pkt[4] += 1;
      break;   /* pktsiz = 64 (1) */
   case 7:
      pkt[4] += SWINDOW;
      break;   /* 3 windows */
   case 0:
      pkt[4] += 0x80 + nt3 + (nt4 << 3);
      c2 = (PKTSIZE - len) & 0xff;
      /* havnt set it up for VERY LONG pkts with a few */
      /* bytes yet (-128) */
      if (c2) { /* short packet handling */
         pkt[4] += 0x40;   /* if len < PKTSIZE */
         for (i = PKTSIZE - 1; i > 0; i--)
            cnt1[i] = cnt1[i-1];
         cnt1[0] = c2;
      }
      break;
   }
   pkt[4] &= 0xff;
   if (nt2) {
      pkt[1] = 9;             /* control packet size = 0 (9) */
      check = (0xaaaa - pkt[4]) & 0xffff;
   } else {
      pkt[1] = PKTSIZ2;             /* data packet size = 64 (2) */
      check = checksum(cnt1, PKTSIZE);
      i = pkt[4];/* got to do this on PC for ex-or high bits */
      i &= 0xff;
      check = (check ^ i) & 0xffff;
      check = (0xaaaa - check) & 0xffff;
   }
   pkt[2] = check & 0xff;
   pkt[3] = (check >> 8) & 0xff;
   pkt[5] = (pkt[1] ^ pkt[2] ^ pkt[3] ^ pkt[4]) & 0xff;
/***More Link testing MODS ******/ /*
switch(dpkerr[0]) {
case 'e':   cnt1[10] = -cnt1[10];
         break;
case 'h':   pkt[5] = -pkt[5];
         break;
case 'l':   return;
case 'p':   swrite(pkt,HDRSIZE);
         if(pkt[1] != 9) swrite(cnt1,PKTSIZE-3);
         return;
default:   break;
   }  /**/
   /******End Link Testing Mods **********/
   swrite(pkt, HDRSIZE);       /* header is 6-bytes long */
   /*      write(flog,pkt,HDRSIZE);   */
   if (pkt[1] != 9) {
      swrite(cnt1, PKTSIZE);     /* data is always 64
      bytes long */
      /*    write(flog,cnt1,PKTSIZE);   */
   }
}


/**/
/*
 *
 *      read packet
 * on return: nt3=pkrec nt4=pksent len=length <=PKTSIZE  cnt1=data *
 * ret(type) ok; ret(EMPTY) input buf empty; ret(ERROR) bad header;
 *          ret(EMPTY) lost pkt timeout; ret(ERROR) checksum error;ret(-5) ?
****NOTE :
***sread(buf,n,timeout)
   while(TRUE) {
      if(# of chars available >= n) (without dec internal counter)
         read n chars into buf (decrenent internal char counter)
         break
      else
         if(time>timeout) break
   }
   return(# of chars available)
****END NOTE
 */
grpack(nt3, nt4, len, cnt1)
int   *nt3, *nt4, *len;
char   cnt1[];
{
   unsigned int   nt1, check, checkchk, i;
   unsigned char   c, c2;
   int   ii;
   if (GOT_SYNC)
      goto get_hdr;
   if (GOT_HDR)
      goto get_data;
   c = '\0';
   while ((c & 0x7f) != '\020')
      if (sread(&c, 1, timeout) == 0)
         return(EMPTY);
   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) {               /*  bad header */
      printmsg( 0, "****bad header****" );
      return(ERROR); /* Im not sure whether "g" considers it an empty or error */
   }
   GOT_HDR = TRUE;
   if ((grpkt[1] &= 0x7f) == 9) {       /* control packet */
      *len = 0;
      c = grpkt[4] & 0xff;
      nt1  = c >> 3;
      *nt3 = c & 7;
      *nt4 = 0;
      check = 0;
      checkchk = 0;
      cnt1[*len] = '\0';
      GOT_HDR = FALSE;
   } else {       /* data packet */
      if (grpkt[1] != PKTSIZ2)
         return(-5);   /* cant handle other than 64*/
get_data:
      if (sread(cnt1, PKTSIZE, timeout) < PKTSIZE)
         return(EMPTY);
      GOT_HDR = FALSE;
      nt1  = 0;
      c2 = grpkt[4] & 0xff;
      c = c2 & 0x3f;
      *nt4 = c >> 3;
      *nt3 = c & 7;
      i = grpkt[3];
      i = (i << 8) & 0xff00;
      check = grpkt[2];
      check = i | (check & 0xff);
      checkchk = checksum(cnt1, PKTSIZE);
      i = grpkt[4] | 0x80;
      i &= 0xff;
      checkchk = 0xaaaa - (checkchk ^ i);
      checkchk &= 0xffff;
      if (checkchk != check) {
         printmsg( 4, "***checksum error***" );
         return(ERROR);
      }
      *len = PKTSIZE;
      /* havnt set it up for very long pkts yet (>128) RH Lamb */
      if (c2 & 0x40) {
         ii = (cnt1[0] & 0xff);
         *len = (*len - ii) & 0xff;
         for (ii = 0; ii < *len; ii++)
            cnt1[ii] = cnt1[ii+1];
      }
      cnt1[*len] = '\0';
   }
   printmsg( 5, "rec  packet type %d, num=%d, n=%d, len=%d", nt1, *nt3, *nt4, *len );
   printmsg( 6, "  checksum rec = %x comp = %x, data=\n|%s|", check, checkchk, cnt1 );
   ii = nt1;
   return(ii);
}


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);
}


/* */
/*
gwrmsg
   send a null terminated string out
*/
gwrmsg( typ, buf )
char typ;
char *buf; /* null terminated */
{
}

/*
grdmsg
   read a null terminated string
*/
grdmsg( buf )
char * buf;
{

}

/*
gwrdata
   read a file and send it out
*/
gwrdata( f )
{
}

/*
grrdata
   read in data and send to file
*/
grrdata( f )
{
}


/*
grdblk
   read a block of data in
*/
grdblk( blk, len )
{
}

/*
gwrblk
   write out a block of data
*/
gwrblk( blk, len )
{
}
