123599Sbloom #ifndef lint 2*25130Sbloom static char sccsid[] = "@(#)fio.c 5.2 (Berkeley) 10/09/85"; 323599Sbloom #endif 423599Sbloom 523599Sbloom /* 623599Sbloom * flow control protocol. 723599Sbloom * 823599Sbloom * This protocol relies on flow control of the data stream. 923599Sbloom * It is meant for working over links that can (almost) be 1023599Sbloom * guaranteed to be errorfree, specifically X.25/PAD links. 1123599Sbloom * A sumcheck is carried out over a whole file only. If a 1223599Sbloom * transport fails the receiver can request retransmission(s). 1323599Sbloom * This protocol uses a 7-bit datapath only, so it can be 1423599Sbloom * used on links that are not 8-bit transparent. 1523599Sbloom * 1623599Sbloom * When using this protocol with an X.25 PAD: 1723599Sbloom * Although this protocol uses no control chars except CR, 1823599Sbloom * control chars NULL and ^P are used before this protocol 1923599Sbloom * is started; since ^P is the default char for accessing 2023599Sbloom * PAD X.28 command mode, be sure to disable that access 2123599Sbloom * (PAD par 1). Also make sure both flow control pars 2223599Sbloom * (5 and 12) are set. The CR used in this proto is meant 2323599Sbloom * to trigger packet transmission, hence par 3 should be 2423599Sbloom * set to 2; a good value for the Idle Timer (par 4) is 10. 2523599Sbloom * All other pars should be set to 0. 26*25130Sbloom * 2723599Sbloom * Normally a calling site will take care of setting the 2823599Sbloom * local PAD pars via an X.28 command and those of the remote 2923599Sbloom * PAD via an X.29 command, unless the remote site has a 3023599Sbloom * special channel assigned for this protocol with the proper 3123599Sbloom * par settings. 3223599Sbloom * 33*25130Sbloom * Additional comments for hosts with direct X.25 access: 34*25130Sbloom * - the global variable IsTcpIp, when set, excludes the ioctl's, 35*25130Sbloom * so the same binary can run on X.25 and non-X.25 hosts; 36*25130Sbloom * - reads are done in small chunks, which can be smaller than 37*25130Sbloom * the packet size; your X.25 driver must support that. 38*25130Sbloom * 39*25130Sbloom * 40*25130Sbloom * Author: 41*25130Sbloom * Piet Beertema, CWI, Amsterdam, Sep 1984 42*25130Sbloom * Modified for X.25 hosts: 43*25130Sbloom * Robert Elz, Melbourne Univ, Mar 1985 4423599Sbloom */ 4523599Sbloom 46*25130Sbloom #include "uucp.h" 4723599Sbloom #include <signal.h> 4823599Sbloom #ifdef USG 4923599Sbloom #include <termio.h> 5023599Sbloom #else !USG 5123599Sbloom #include <sgtty.h> 5223599Sbloom #endif !USG 5323599Sbloom #include <setjmp.h> 5423599Sbloom 55*25130Sbloom #define FIBUFSIZ 256 /* for X.25 interfaces: set equal to packet size, 56*25130Sbloom * but see comment above 57*25130Sbloom */ 5823599Sbloom 59*25130Sbloom #define FOBUFSIZ 256 /* for X.25 interfaces: set equal to packet size; 60*25130Sbloom * otherwise make as large as feasible to reduce 61*25130Sbloom * number of write system calls 62*25130Sbloom */ 63*25130Sbloom 6423599Sbloom #ifndef MAXMSGLEN 6523599Sbloom #define MAXMSGLEN BUFSIZ 6623599Sbloom #endif MAXMSGLEN 6723599Sbloom 6823599Sbloom static int fchksum; 6923599Sbloom static jmp_buf Ffailbuf; 7023599Sbloom 7123599Sbloom static 7223599Sbloom falarm() 7323599Sbloom { 7423599Sbloom signal(SIGALRM, falarm); 7523599Sbloom longjmp(Ffailbuf, 1); 7623599Sbloom } 7723599Sbloom 7823599Sbloom static int (*fsig)(); 7923599Sbloom 8023599Sbloom #ifndef USG 8123599Sbloom #define TCGETA TIOCGETP 8223599Sbloom #define TCSETA TIOCSETP 8323599Sbloom #define termio sgttyb 8423599Sbloom #endif USG 8523599Sbloom 8623599Sbloom fturnon() 8723599Sbloom { 8823599Sbloom int ret; 8923599Sbloom struct termio ttbuf; 9023599Sbloom 91*25130Sbloom if (!IsTcpIp) { 92*25130Sbloom ioctl(Ifn, TCGETA, &ttbuf); 9323599Sbloom #ifdef USG 94*25130Sbloom ttbuf.c_iflag = IXOFF|IXON|ISTRIP; 95*25130Sbloom ttbuf.c_cc[VMIN] = FIBUFSIZ > 64 ? 64 : FIBUFSIZ; 96*25130Sbloom ttbuf.c_cc[VTIME] = 5; 97*25130Sbloom #else !USG 98*25130Sbloom ttbuf.sg_flags = ANYP|CBREAK|TANDEM; 9923599Sbloom #endif USG 100*25130Sbloom ret = ioctl(Ifn, TCSETA, &ttbuf); 101*25130Sbloom ASSERT(ret >= 0, "STTY FAILED", "", ret); 102*25130Sbloom } 10323599Sbloom fsig = signal(SIGALRM, falarm); 10423599Sbloom /* give the other side time to perform its ioctl; 10523599Sbloom * otherwise it may flush out the first data this 10623599Sbloom * side is about to send. 10723599Sbloom */ 10823599Sbloom sleep(2); 10923599Sbloom return SUCCESS; 11023599Sbloom } 11123599Sbloom 11223599Sbloom fturnoff() 11323599Sbloom { 11423599Sbloom (void) signal(SIGALRM, fsig); 11523599Sbloom return SUCCESS; 11623599Sbloom } 11723599Sbloom 11823599Sbloom fwrmsg(type, str, fn) 11923599Sbloom register char *str; 12023599Sbloom int fn; 12123599Sbloom char type; 12223599Sbloom { 12323599Sbloom register char *s; 12423599Sbloom char bufr[MAXMSGLEN]; 12523599Sbloom 12623599Sbloom s = bufr; 12723599Sbloom *s++ = type; 12823599Sbloom while (*str) 12923599Sbloom *s++ = *str++; 13023599Sbloom if (*(s-1) == '\n') 13123599Sbloom s--; 13223599Sbloom *s++ = '\r'; 133*25130Sbloom *s = 0; 13423599Sbloom (void) write(fn, bufr, s - bufr); 13523599Sbloom return SUCCESS; 13623599Sbloom } 13723599Sbloom 13823599Sbloom frdmsg(str, fn) 13923599Sbloom register char *str; 14023599Sbloom register int fn; 14123599Sbloom { 14223599Sbloom register char *smax; 14323599Sbloom 14423599Sbloom if (setjmp(Ffailbuf)) 14523599Sbloom return FAIL; 14623599Sbloom smax = str + MAXMSGLEN - 1; 14723599Sbloom (void) alarm(2*MAXMSGTIME); 14823599Sbloom for (;;) { 14923599Sbloom if (read(fn, str, 1) <= 0) 15023599Sbloom goto msgerr; 151*25130Sbloom *str &= 0177; 15223599Sbloom if (*str == '\r') 15323599Sbloom break; 154*25130Sbloom if (*str < ' ') { 15523599Sbloom continue; 156*25130Sbloom } 15723599Sbloom if (str++ >= smax) 15823599Sbloom goto msgerr; 15923599Sbloom } 16023599Sbloom *str = '\0'; 16123599Sbloom (void) alarm(0); 16223599Sbloom return SUCCESS; 16323599Sbloom msgerr: 16423599Sbloom (void) alarm(0); 16523599Sbloom return FAIL; 16623599Sbloom } 16723599Sbloom 16823599Sbloom fwrdata(fp1, fn) 16923599Sbloom FILE *fp1; 17023599Sbloom int fn; 17123599Sbloom { 172*25130Sbloom register int alen, ret; 173*25130Sbloom register char *obp; 174*25130Sbloom char ack, ibuf[MAXMSGLEN]; 175*25130Sbloom int flen, mil, retries = 0; 17623599Sbloom long abytes, fbytes; 17723599Sbloom struct timeb t1, t2; 17823599Sbloom 17923599Sbloom ret = FAIL; 18023599Sbloom retry: 18123599Sbloom fchksum = 0xffff; 18223599Sbloom abytes = fbytes = 0L; 18323599Sbloom ack = '\0'; 18423599Sbloom #ifdef USG 18523599Sbloom time(&t1.time); 18623599Sbloom t1.millitm = 0; 18723599Sbloom #else !USG 18823599Sbloom ftime(&t1); 18923599Sbloom #endif !USG 190*25130Sbloom do { 191*25130Sbloom alen = fwrblk(fn, fp1, &flen); 192*25130Sbloom fbytes += flen; 193*25130Sbloom if (alen <= 0) { 194*25130Sbloom abytes -= alen; 19523599Sbloom goto acct; 19623599Sbloom } 197*25130Sbloom abytes += alen; 198*25130Sbloom } while (!feof(fp1) && !ferror(fp1)); 199*25130Sbloom DEBUG(8, "\nchecksum: %04x\n", fchksum); 200*25130Sbloom if (frdmsg(ibuf, fn) != FAIL) { 201*25130Sbloom if ((ack = ibuf[0]) == 'G') 202*25130Sbloom ret = SUCCESS; 203*25130Sbloom DEBUG(4, "ack - '%c'\n", ack); 20423599Sbloom } 20523599Sbloom acct: 20623599Sbloom #ifdef USG 20723599Sbloom time(&t2.time); 20823599Sbloom t2.millitm = 0; 20923599Sbloom #else !USG 21023599Sbloom ftime(&t2); 21123599Sbloom #endif !USG 21223599Sbloom Now = t2; 21323599Sbloom t2.time -= t1.time; 21423599Sbloom mil = t2.millitm - t1.millitm; 21523599Sbloom if (mil < 0) { 21623599Sbloom --t2.time; 21723599Sbloom mil += 1000; 21823599Sbloom } 21923599Sbloom sprintf(ibuf, "sent data %ld bytes %ld.%02d secs", 220*25130Sbloom fbytes, (long)t2.time, mil / 10); 22123599Sbloom sysacct(abytes, t2.time - t1.time); 222*25130Sbloom if (retries > 0) 223*25130Sbloom sprintf(&ibuf[strlen(ibuf)], ", %d retries", retries); 22423599Sbloom DEBUG(1, "%s\n", ibuf); 22523599Sbloom syslog(ibuf); 226*25130Sbloom if (ack == 'R') { 227*25130Sbloom DEBUG(4, "RETRY:\n", 0); 228*25130Sbloom fseek(fp1, 0L, 0); 229*25130Sbloom retries++; 230*25130Sbloom goto retry; 231*25130Sbloom } 23223599Sbloom #ifdef SYSACCT 233*25130Sbloom if (ret == FAIL) 23423599Sbloom sysaccf(NULL); /* force accounting */ 23523599Sbloom #endif SYSACCT 23623599Sbloom return ret; 23723599Sbloom } 23823599Sbloom 23923599Sbloom /* max. attempts to retransmit a file: */ 24023599Sbloom #define MAXRETRIES (fbytes < 10000L ? 2 : 1) 24123599Sbloom 24223599Sbloom frddata(fn, fp2) 24323599Sbloom register int fn; 24423599Sbloom register FILE *fp2; 24523599Sbloom { 24623599Sbloom register int flen; 24723599Sbloom register char eof; 248*25130Sbloom char ibuf[FIBUFSIZ]; 249*25130Sbloom int ret, mil, retries = 0; 25023599Sbloom long alen, abytes, fbytes; 25123599Sbloom struct timeb t1, t2; 25223599Sbloom 25323599Sbloom ret = FAIL; 25423599Sbloom retry: 25523599Sbloom fchksum = 0xffff; 25623599Sbloom abytes = fbytes = 0L; 25723599Sbloom #ifdef USG 25823599Sbloom time(&t1.time); 25923599Sbloom t1.millitm = 0; 26023599Sbloom #else !USG 26123599Sbloom ftime(&t1); 26223599Sbloom #endif !USG 26323599Sbloom do { 26423599Sbloom flen = frdblk(ibuf, fn, &alen); 26523599Sbloom abytes += alen; 26623599Sbloom if (flen < 0) 26723599Sbloom goto acct; 268*25130Sbloom if (eof = flen > FIBUFSIZ) 269*25130Sbloom flen -= FIBUFSIZ + 1; 27023599Sbloom fbytes += flen; 27123599Sbloom if (fwrite(ibuf, sizeof (char), flen, fp2) != flen) 27223599Sbloom goto acct; 27323599Sbloom } while (!eof); 274*25130Sbloom ret = SUCCESS; 27523599Sbloom acct: 27623599Sbloom #ifdef USG 27723599Sbloom time(&t2.time); 27823599Sbloom t2.millitm = 0; 27923599Sbloom #else !USG 28023599Sbloom ftime(&t2); 28123599Sbloom #endif !USG 28223599Sbloom Now = t2; 28323599Sbloom t2.time -= t1.time; 28423599Sbloom mil = t2.millitm - t1.millitm; 28523599Sbloom if (mil < 0) { 28623599Sbloom --t2.time; 28723599Sbloom mil += 1000; 28823599Sbloom } 28923599Sbloom sprintf(ibuf, "received data %ld bytes %ld.%02d secs", 290*25130Sbloom fbytes, (long)t2.time, mil/10); 291*25130Sbloom if (retries > 0) 292*25130Sbloom sprintf(&ibuf[strlen(ibuf)]," %d retries", retries); 29323599Sbloom sysacct(abytes, t2.time - t1.time); 29423599Sbloom DEBUG(1, "%s\n", ibuf); 29523599Sbloom syslog(ibuf); 296*25130Sbloom if (ret == FAIL) { 297*25130Sbloom if (retries++ < MAXRETRIES) { 298*25130Sbloom DEBUG(8, "send ack: 'R'\n", 0); 299*25130Sbloom fwrmsg('R', "", fn); 300*25130Sbloom fseek(fp2, 0L, 0); 301*25130Sbloom DEBUG(4, "RETRY:\n", 0); 302*25130Sbloom goto retry; 303*25130Sbloom } 304*25130Sbloom DEBUG(8, "send ack: 'Q'\n", 0); 305*25130Sbloom fwrmsg('Q', "", fn); 306*25130Sbloom #ifdef SYSACCT 307*25130Sbloom sysaccf(NULL); /* force accounting */ 308*25130Sbloom #endif SYSACCT 309*25130Sbloom } 310*25130Sbloom else { 311*25130Sbloom DEBUG(8, "send ack: 'G'\n", 0); 312*25130Sbloom fwrmsg('G', "", fn); 313*25130Sbloom } 31423599Sbloom return ret; 31523599Sbloom } 31623599Sbloom 31723599Sbloom static 31823599Sbloom frdbuf(blk, len, fn) 31923599Sbloom register char *blk; 32023599Sbloom register int len; 32123599Sbloom register int fn; 32223599Sbloom { 323*25130Sbloom static int ret = FIBUFSIZ / 2; 32423599Sbloom 32523599Sbloom if (setjmp(Ffailbuf)) 32623599Sbloom return FAIL; 32723599Sbloom (void) alarm(MAXMSGTIME); 32823599Sbloom ret = read(fn, blk, len); 32923599Sbloom alarm(0); 33023599Sbloom return ret <= 0 ? FAIL : ret; 33123599Sbloom } 33223599Sbloom 333*25130Sbloom #if !defined(BSD4_2) && !defined(USG) 33423599Sbloom /* call ultouch every TC calls to either frdblk or fwrblk */ 335*25130Sbloom #define TC 20 336*25130Sbloom static int tc = TC; 337*25130Sbloom #endif !defined(BSD4_2) && !defined(USG) 33823599Sbloom 33923599Sbloom /* Byte conversion: 34023599Sbloom * 341*25130Sbloom * from pre to 342*25130Sbloom * 000-037 172 100-137 343*25130Sbloom * 040-171 040-171 344*25130Sbloom * 172-177 173 072-077 345*25130Sbloom * 200-237 174 100-137 346*25130Sbloom * 240-371 175 040-171 347*25130Sbloom * 372-377 176 072-077 34823599Sbloom */ 34923599Sbloom 35023599Sbloom static 351*25130Sbloom fwrblk(fn, fp, lenp) 35223599Sbloom int fn; 353*25130Sbloom register FILE *fp; 354*25130Sbloom int *lenp; 35523599Sbloom { 35623599Sbloom register char *op; 357*25130Sbloom register int c, sum, nl, len; 358*25130Sbloom char obuf[FOBUFSIZ + 8]; 35923599Sbloom int ret; 36023599Sbloom 361*25130Sbloom #if !defined(BSD4_2) && !defined(USG) 36223599Sbloom /* call ultouch occasionally */ 36323599Sbloom if (--tc < 0) { 36423599Sbloom tc = TC; 36523599Sbloom ultouch(); 36623599Sbloom } 367*25130Sbloom #endif !defined(BSD4_2) && !defined(USG) 36823599Sbloom op = obuf; 36923599Sbloom nl = 0; 370*25130Sbloom len = 0; 37123599Sbloom sum = fchksum; 372*25130Sbloom while ((c = getc(fp)) != EOF) { 373*25130Sbloom len++; 37423599Sbloom if (sum & 0x8000) { 37523599Sbloom sum <<= 1; 37623599Sbloom sum++; 37723599Sbloom } else 37823599Sbloom sum <<= 1; 379*25130Sbloom sum += c; 38023599Sbloom sum &= 0xffff; 381*25130Sbloom if (c & 0200) { 382*25130Sbloom c &= 0177; 383*25130Sbloom if (c < 040) { 38423599Sbloom *op++ = '\174'; 385*25130Sbloom *op++ = c + 0100; 38623599Sbloom } else 387*25130Sbloom if (c <= 0171) { 38823599Sbloom *op++ = '\175'; 389*25130Sbloom *op++ = c; 39023599Sbloom } 39123599Sbloom else { 39223599Sbloom *op++ = '\176'; 393*25130Sbloom *op++ = c - 0100; 39423599Sbloom } 39523599Sbloom nl += 2; 39623599Sbloom } else { 397*25130Sbloom if (c < 040) { 39823599Sbloom *op++ = '\172'; 399*25130Sbloom *op++ = c + 0100; 40023599Sbloom nl += 2; 40123599Sbloom } else 402*25130Sbloom if (c <= 0171) { 403*25130Sbloom *op++ = c; 40423599Sbloom nl++; 40523599Sbloom } else { 40623599Sbloom *op++ = '\173'; 407*25130Sbloom *op++ = c - 0100; 40823599Sbloom nl += 2; 40923599Sbloom } 41023599Sbloom } 411*25130Sbloom if (nl >= FOBUFSIZ - 1) { 412*25130Sbloom /* 413*25130Sbloom * peek at next char, see if it will fit 414*25130Sbloom */ 415*25130Sbloom c = getc(fp); 416*25130Sbloom if (c == EOF) 417*25130Sbloom break; 418*25130Sbloom (void) ungetc(c, fp); 419*25130Sbloom if (nl >= FOBUFSIZ || c < 040 || c > 0171) 420*25130Sbloom goto writeit; 421*25130Sbloom } 422*25130Sbloom } 423*25130Sbloom /* 424*25130Sbloom * At EOF - append checksum, there is space for it... 425*25130Sbloom */ 426*25130Sbloom sprintf(op, "\176\176%04x\r", sum); 427*25130Sbloom nl += strlen(op); 428*25130Sbloom writeit: 429*25130Sbloom *lenp = len; 43023599Sbloom fchksum = sum; 431*25130Sbloom DEBUG(8, "%d/", len); 43223599Sbloom DEBUG(8, "%d,", nl); 43323599Sbloom ret = write(fn, obuf, nl); 43423599Sbloom return ret == nl ? nl : ret < 0 ? 0 : -ret; 43523599Sbloom } 43623599Sbloom 43723599Sbloom static 43823599Sbloom frdblk(ip, fn, rlen) 43923599Sbloom register char *ip; 44023599Sbloom int fn; 44123599Sbloom long *rlen; 44223599Sbloom { 44323599Sbloom register char *op, c; 44423599Sbloom register int sum, len, nl; 44523599Sbloom char buf[5], *erbp = ip; 44623599Sbloom int i; 44723599Sbloom static char special = 0; 44823599Sbloom 449*25130Sbloom #if !defined(BSD4_2) && !defined(USG) 45023599Sbloom /* call ultouch occasionally */ 45123599Sbloom if (--tc < 0) { 45223599Sbloom tc = TC; 45323599Sbloom ultouch(); 45423599Sbloom } 455*25130Sbloom #endif !defined(BSD4_2) && !defined(USG) 456*25130Sbloom if ((len = frdbuf(ip, FIBUFSIZ, fn)) == FAIL) { 45723599Sbloom *rlen = 0; 45823599Sbloom goto dcorr; 45923599Sbloom } 46023599Sbloom *rlen = len; 46123599Sbloom DEBUG(8, "%d/", len); 46223599Sbloom op = ip; 46323599Sbloom nl = 0; 46423599Sbloom sum = fchksum; 46523599Sbloom do { 46623599Sbloom if ((*ip &= 0177) >= '\172') { 46723599Sbloom if (special) { 46823599Sbloom DEBUG(8, "%d", nl); 46923599Sbloom special = 0; 47023599Sbloom op = buf; 47123599Sbloom if (*ip++ != '\176' || (i = --len) > 5) 47223599Sbloom goto dcorr; 47323599Sbloom while (i--) 474*25130Sbloom *op++ = *ip++ & 0177; 47523599Sbloom while (len < 5) { 47623599Sbloom i = frdbuf(&buf[len], 5 - len, fn); 47723599Sbloom if (i == FAIL) { 47823599Sbloom len = FAIL; 47923599Sbloom goto dcorr; 48023599Sbloom } 48123599Sbloom DEBUG(8, ",%d", i); 48223599Sbloom len += i; 48323599Sbloom *rlen += i; 484*25130Sbloom while (i--) 485*25130Sbloom *op++ &= 0177; 48623599Sbloom } 48723599Sbloom if (buf[4] != '\r') 48823599Sbloom goto dcorr; 48923599Sbloom sscanf(buf, "%4x", &fchksum); 49023599Sbloom DEBUG(8, "\nchecksum: %04x\n", sum); 49123599Sbloom if (fchksum == sum) 492*25130Sbloom return FIBUFSIZ + 1 + nl; 49323599Sbloom else { 49423599Sbloom DEBUG(8, "\n", 0); 49523599Sbloom DEBUG(4, "Bad checksum\n", 0); 49623599Sbloom return FAIL; 49723599Sbloom } 49823599Sbloom } 49923599Sbloom special = *ip++; 50023599Sbloom } else { 50123599Sbloom if (*ip < '\040') { 50223599Sbloom /* error: shouldn't get control chars */ 50323599Sbloom goto dcorr; 50423599Sbloom } 50523599Sbloom switch (special) { 50623599Sbloom case 0: 50723599Sbloom c = *ip++; 50823599Sbloom break; 50923599Sbloom case '\172': 51023599Sbloom c = *ip++ - 0100; 51123599Sbloom break; 51223599Sbloom case '\173': 51323599Sbloom c = *ip++ + 0100; 51423599Sbloom break; 51523599Sbloom case '\174': 51623599Sbloom c = *ip++ + 0100; 51723599Sbloom break; 51823599Sbloom case '\175': 51923599Sbloom c = *ip++ + 0200; 52023599Sbloom break; 52123599Sbloom case '\176': 52223599Sbloom c = *ip++ + 0300; 52323599Sbloom break; 52423599Sbloom } 52523599Sbloom *op++ = c; 52623599Sbloom if (sum & 0x8000) { 52723599Sbloom sum <<= 1; 52823599Sbloom sum++; 52923599Sbloom } else 53023599Sbloom sum <<= 1; 53123599Sbloom sum += c & 0377; 53223599Sbloom sum &= 0xffff; 53323599Sbloom special = 0; 53423599Sbloom nl++; 53523599Sbloom } 53623599Sbloom } while (--len); 53723599Sbloom fchksum = sum; 53823599Sbloom DEBUG(8, "%d,", nl); 53923599Sbloom return nl; 54023599Sbloom dcorr: 54123599Sbloom DEBUG(8, "\n", 0); 54223599Sbloom DEBUG(4, "Data corrupted\n", 0); 54323599Sbloom while (len != FAIL) { 544*25130Sbloom if ((len = frdbuf(erbp, FIBUFSIZ, fn)) != FAIL) 54523599Sbloom *rlen += len; 54623599Sbloom } 54723599Sbloom return FAIL; 54823599Sbloom } 549*25130Sbloom 550