1: #ifndef lint 2: static char sccsid[] = "@(#)fio.c 5.3 (Berkeley) 10/9/85"; 3: #endif 4: 5: /* 6: * flow control protocol. 7: * 8: * This protocol relies on flow control of the data stream. 9: * It is meant for working over links that can (almost) be 10: * guaranteed to be errorfree, specifically X.25/PAD links. 11: * A sumcheck is carried out over a whole file only. If a 12: * transport fails the receiver can request retransmission(s). 13: * This protocol uses a 7-bit datapath only, so it can be 14: * used on links that are not 8-bit transparent. 15: * 16: * When using this protocol with an X.25 PAD: 17: * Although this protocol uses no control chars except CR, 18: * control chars NULL and ^P are used before this protocol 19: * is started; since ^P is the default char for accessing 20: * PAD X.28 command mode, be sure to disable that access 21: * (PAD par 1). Also make sure both flow control pars 22: * (5 and 12) are set. The CR used in this proto is meant 23: * to trigger packet transmission, hence par 3 should be 24: * set to 2; a good value for the Idle Timer (par 4) is 10. 25: * All other pars should be set to 0. 26: * 27: * Normally a calling site will take care of setting the 28: * local PAD pars via an X.28 command and those of the remote 29: * PAD via an X.29 command, unless the remote site has a 30: * special channel assigned for this protocol with the proper 31: * par settings. 32: * 33: * Additional comments for hosts with direct X.25 access: 34: * - the global variable IsTcpIp, when set, excludes the ioctl's, 35: * so the same binary can run on X.25 and non-X.25 hosts; 36: * - reads are done in small chunks, which can be smaller than 37: * the packet size; your X.25 driver must support that. 38: * 39: * 40: * Author: 41: * Piet Beertema, CWI, Amsterdam, Sep 1984 42: * Modified for X.25 hosts: 43: * Robert Elz, Melbourne Univ, Mar 1985 44: */ 45: 46: #include "uucp.h" 47: #include <signal.h> 48: #ifdef USG 49: #include <termio.h> 50: #else !USG 51: #include <sgtty.h> 52: #endif !USG 53: #include <setjmp.h> 54: 55: #define FIBUFSIZ 256 /* for X.25 interfaces: set equal to packet size, 56: * but see comment above 57: */ 58: 59: #define FOBUFSIZ 256 /* for X.25 interfaces: set equal to packet size; 60: * otherwise make as large as feasible to reduce 61: * number of write system calls 62: */ 63: 64: #ifndef MAXMSGLEN 65: #define MAXMSGLEN BUFSIZ 66: #endif MAXMSGLEN 67: 68: static int fchksum; 69: static jmp_buf Ffailbuf; 70: 71: static 72: falarm() 73: { 74: signal(SIGALRM, falarm); 75: longjmp(Ffailbuf, 1); 76: } 77: 78: static int (*fsig)(); 79: 80: #ifndef USG 81: #define TCGETA TIOCGETP 82: #define TCSETA TIOCSETP 83: #define termio sgttyb 84: #endif USG 85: 86: fturnon() 87: { 88: int ret; 89: struct termio ttbuf; 90: 91: if (!IsTcpIp) { 92: ioctl(Ifn, TCGETA, &ttbuf); 93: #ifdef USG 94: ttbuf.c_iflag = IXOFF|IXON|ISTRIP; 95: ttbuf.c_cc[VMIN] = FIBUFSIZ > 64 ? 64 : FIBUFSIZ; 96: ttbuf.c_cc[VTIME] = 5; 97: #else !USG 98: ttbuf.sg_flags = ANYP|CBREAK|TANDEM; 99: #endif USG 100: ret = ioctl(Ifn, TCSETA, &ttbuf); 101: ASSERT(ret >= 0, "STTY FAILED", "", ret); 102: } 103: fsig = signal(SIGALRM, falarm); 104: /* give the other side time to perform its ioctl; 105: * otherwise it may flush out the first data this 106: * side is about to send. 107: */ 108: sleep(2); 109: return SUCCESS; 110: } 111: 112: fturnoff() 113: { 114: (void) signal(SIGALRM, fsig); 115: return SUCCESS; 116: } 117: 118: fwrmsg(type, str, fn) 119: register char *str; 120: int fn; 121: char type; 122: { 123: register char *s; 124: char bufr[MAXMSGLEN]; 125: 126: s = bufr; 127: *s++ = type; 128: while (*str) 129: *s++ = *str++; 130: if (*(s-1) == '\n') 131: s--; 132: *s++ = '\r'; 133: *s = 0; 134: (void) write(fn, bufr, s - bufr); 135: return SUCCESS; 136: } 137: 138: frdmsg(str, fn) 139: register char *str; 140: register int fn; 141: { 142: register char *smax; 143: 144: if (setjmp(Ffailbuf)) 145: return FAIL; 146: smax = str + MAXMSGLEN - 1; 147: (void) alarm(2*MAXMSGTIME); 148: for (;;) { 149: if (read(fn, str, 1) <= 0) 150: goto msgerr; 151: *str &= 0177; 152: if (*str == '\r') 153: break; 154: if (*str < ' ') { 155: continue; 156: } 157: if (str++ >= smax) 158: goto msgerr; 159: } 160: *str = '\0'; 161: (void) alarm(0); 162: return SUCCESS; 163: msgerr: 164: (void) alarm(0); 165: return FAIL; 166: } 167: 168: fwrdata(fp1, fn) 169: FILE *fp1; 170: int fn; 171: { 172: register int alen, ret; 173: register char *obp; 174: char ack, ibuf[MAXMSGLEN]; 175: int flen, mil, retries = 0; 176: long abytes, fbytes; 177: struct timeb t1, t2; 178: 179: ret = FAIL; 180: retry: 181: fchksum = 0xffff; 182: abytes = fbytes = 0L; 183: ack = '\0'; 184: #ifdef USG 185: time(&t1.time); 186: t1.millitm = 0; 187: #else !USG 188: ftime(&t1); 189: #endif !USG 190: do { 191: alen = fwrblk(fn, fp1, &flen); 192: fbytes += flen; 193: if (alen <= 0) { 194: abytes -= alen; 195: goto acct; 196: } 197: abytes += alen; 198: } while (!feof(fp1) && !ferror(fp1)); 199: DEBUG(8, "\nchecksum: %04x\n", fchksum); 200: if (frdmsg(ibuf, fn) != FAIL) { 201: if ((ack = ibuf[0]) == 'G') 202: ret = SUCCESS; 203: DEBUG(4, "ack - '%c'\n", ack); 204: } 205: acct: 206: #ifdef USG 207: time(&t2.time); 208: t2.millitm = 0; 209: #else !USG 210: ftime(&t2); 211: #endif !USG 212: Now = t2; 213: t2.time -= t1.time; 214: mil = t2.millitm - t1.millitm; 215: if (mil < 0) { 216: --t2.time; 217: mil += 1000; 218: } 219: sprintf(ibuf, "sent data %ld bytes %ld.%02d secs", 220: fbytes, (long)t2.time, mil / 10); 221: sysacct(abytes, t2.time); 222: if (retries > 0) 223: sprintf(&ibuf[strlen(ibuf)], ", %d retries", retries); 224: DEBUG(1, "%s\n", ibuf); 225: syslog(ibuf); 226: if (ack == 'R') { 227: DEBUG(4, "RETRY:\n", 0); 228: fseek(fp1, 0L, 0); 229: retries++; 230: goto retry; 231: } 232: #ifdef SYSACCT 233: if (ret == FAIL) 234: sysaccf(NULL); /* force accounting */ 235: #endif SYSACCT 236: return ret; 237: } 238: 239: /* max. attempts to retransmit a file: */ 240: #define MAXRETRIES (fbytes < 10000L ? 2 : 1) 241: 242: frddata(fn, fp2) 243: register int fn; 244: register FILE *fp2; 245: { 246: register int flen; 247: register char eof; 248: char ibuf[FIBUFSIZ]; 249: int ret, mil, retries = 0; 250: long alen, abytes, fbytes; 251: struct timeb t1, t2; 252: 253: ret = FAIL; 254: retry: 255: fchksum = 0xffff; 256: abytes = fbytes = 0L; 257: #ifdef USG 258: time(&t1.time); 259: t1.millitm = 0; 260: #else !USG 261: ftime(&t1); 262: #endif !USG 263: do { 264: flen = frdblk(ibuf, fn, &alen); 265: abytes += alen; 266: if (flen < 0) 267: goto acct; 268: if (eof = flen > FIBUFSIZ) 269: flen -= FIBUFSIZ + 1; 270: fbytes += flen; 271: if (fwrite(ibuf, sizeof (char), flen, fp2) != flen) 272: goto acct; 273: } while (!eof); 274: ret = SUCCESS; 275: acct: 276: #ifdef USG 277: time(&t2.time); 278: t2.millitm = 0; 279: #else !USG 280: ftime(&t2); 281: #endif !USG 282: Now = t2; 283: t2.time -= t1.time; 284: mil = t2.millitm - t1.millitm; 285: if (mil < 0) { 286: --t2.time; 287: mil += 1000; 288: } 289: sprintf(ibuf, "received data %ld bytes %ld.%02d secs", 290: fbytes, (long)t2.time, mil/10); 291: if (retries > 0) 292: sprintf(&ibuf[strlen(ibuf)]," %d retries", retries); 293: sysacct(abytes, t2.time); 294: DEBUG(1, "%s\n", ibuf); 295: syslog(ibuf); 296: if (ret == FAIL) { 297: if (retries++ < MAXRETRIES) { 298: DEBUG(8, "send ack: 'R'\n", 0); 299: fwrmsg('R', "", fn); 300: fseek(fp2, 0L, 0); 301: DEBUG(4, "RETRY:\n", 0); 302: goto retry; 303: } 304: DEBUG(8, "send ack: 'Q'\n", 0); 305: fwrmsg('Q', "", fn); 306: #ifdef SYSACCT 307: sysaccf(NULL); /* force accounting */ 308: #endif SYSACCT 309: } 310: else { 311: DEBUG(8, "send ack: 'G'\n", 0); 312: fwrmsg('G', "", fn); 313: } 314: return ret; 315: } 316: 317: static 318: frdbuf(blk, len, fn) 319: register char *blk; 320: register int len; 321: register int fn; 322: { 323: static int ret = FIBUFSIZ / 2; 324: 325: if (setjmp(Ffailbuf)) 326: return FAIL; 327: (void) alarm(MAXMSGTIME); 328: ret = read(fn, blk, len); 329: alarm(0); 330: return ret <= 0 ? FAIL : ret; 331: } 332: 333: #if !defined(BSD4_2) && !defined(USG) 334: /* call ultouch every TC calls to either frdblk or fwrblk */ 335: #define TC 20 336: static int tc = TC; 337: #endif !defined(BSD4_2) && !defined(USG) 338: 339: /* Byte conversion: 340: * 341: * from pre to 342: * 000-037 172 100-137 343: * 040-171 040-171 344: * 172-177 173 072-077 345: * 200-237 174 100-137 346: * 240-371 175 040-171 347: * 372-377 176 072-077 348: */ 349: 350: static 351: fwrblk(fn, fp, lenp) 352: int fn; 353: register FILE *fp; 354: int *lenp; 355: { 356: register char *op; 357: register int c, sum, nl, len; 358: char obuf[FOBUFSIZ + 8]; 359: int ret; 360: 361: #if !defined(BSD4_2) && !defined(USG) 362: /* call ultouch occasionally */ 363: if (--tc < 0) { 364: tc = TC; 365: ultouch(); 366: } 367: #endif !defined(BSD4_2) && !defined(USG) 368: op = obuf; 369: nl = 0; 370: len = 0; 371: sum = fchksum; 372: while ((c = getc(fp)) != EOF) { 373: len++; 374: if (sum & 0x8000) { 375: sum <<= 1; 376: sum++; 377: } else 378: sum <<= 1; 379: sum += c; 380: sum &= 0xffff; 381: if (c & 0200) { 382: c &= 0177; 383: if (c < 040) { 384: *op++ = '\174'; 385: *op++ = c + 0100; 386: } else 387: if (c <= 0171) { 388: *op++ = '\175'; 389: *op++ = c; 390: } 391: else { 392: *op++ = '\176'; 393: *op++ = c - 0100; 394: } 395: nl += 2; 396: } else { 397: if (c < 040) { 398: *op++ = '\172'; 399: *op++ = c + 0100; 400: nl += 2; 401: } else 402: if (c <= 0171) { 403: *op++ = c; 404: nl++; 405: } else { 406: *op++ = '\173'; 407: *op++ = c - 0100; 408: nl += 2; 409: } 410: } 411: if (nl >= FOBUFSIZ - 1) { 412: /* 413: * peek at next char, see if it will fit 414: */ 415: c = getc(fp); 416: if (c == EOF) 417: break; 418: (void) ungetc(c, fp); 419: if (nl >= FOBUFSIZ || c < 040 || c > 0171) 420: goto writeit; 421: } 422: } 423: /* 424: * At EOF - append checksum, there is space for it... 425: */ 426: sprintf(op, "\176\176%04x\r", sum); 427: nl += strlen(op); 428: writeit: 429: *lenp = len; 430: fchksum = sum; 431: DEBUG(8, "%d/", len); 432: DEBUG(8, "%d,", nl); 433: ret = write(fn, obuf, nl); 434: return ret == nl ? nl : ret < 0 ? 0 : -ret; 435: } 436: 437: static 438: frdblk(ip, fn, rlen) 439: register char *ip; 440: int fn; 441: long *rlen; 442: { 443: register char *op, c; 444: register int sum, len, nl; 445: char buf[5], *erbp = ip; 446: int i; 447: static char special = 0; 448: 449: #if !defined(BSD4_2) && !defined(USG) 450: /* call ultouch occasionally */ 451: if (--tc < 0) { 452: tc = TC; 453: ultouch(); 454: } 455: #endif !defined(BSD4_2) && !defined(USG) 456: if ((len = frdbuf(ip, FIBUFSIZ, fn)) == FAIL) { 457: *rlen = 0; 458: goto dcorr; 459: } 460: *rlen = len; 461: DEBUG(8, "%d/", len); 462: op = ip; 463: nl = 0; 464: sum = fchksum; 465: do { 466: if ((*ip &= 0177) >= '\172') { 467: if (special) { 468: DEBUG(8, "%d", nl); 469: special = 0; 470: op = buf; 471: if (*ip++ != '\176' || (i = --len) > 5) 472: goto dcorr; 473: while (i--) 474: *op++ = *ip++ & 0177; 475: while (len < 5) { 476: i = frdbuf(&buf[len], 5 - len, fn); 477: if (i == FAIL) { 478: len = FAIL; 479: goto dcorr; 480: } 481: DEBUG(8, ",%d", i); 482: len += i; 483: *rlen += i; 484: while (i--) 485: *op++ &= 0177; 486: } 487: if (buf[4] != '\r') 488: goto dcorr; 489: sscanf(buf, "%4x", &fchksum); 490: DEBUG(8, "\nchecksum: %04x\n", sum); 491: if (fchksum == sum) 492: return FIBUFSIZ + 1 + nl; 493: else { 494: DEBUG(8, "\n", 0); 495: DEBUG(4, "Bad checksum\n", 0); 496: return FAIL; 497: } 498: } 499: special = *ip++; 500: } else { 501: if (*ip < '\040') { 502: /* error: shouldn't get control chars */ 503: goto dcorr; 504: } 505: switch (special) { 506: case 0: 507: c = *ip++; 508: break; 509: case '\172': 510: c = *ip++ - 0100; 511: break; 512: case '\173': 513: c = *ip++ + 0100; 514: break; 515: case '\174': 516: c = *ip++ + 0100; 517: break; 518: case '\175': 519: c = *ip++ + 0200; 520: break; 521: case '\176': 522: c = *ip++ + 0300; 523: break; 524: } 525: *op++ = c; 526: if (sum & 0x8000) { 527: sum <<= 1; 528: sum++; 529: } else 530: sum <<= 1; 531: sum += c & 0377; 532: sum &= 0xffff; 533: special = 0; 534: nl++; 535: } 536: } while (--len); 537: fchksum = sum; 538: DEBUG(8, "%d,", nl); 539: return nl; 540: dcorr: 541: DEBUG(8, "\n", 0); 542: DEBUG(4, "Data corrupted\n", 0); 543: while (len != FAIL) { 544: if ((len = frdbuf(erbp, FIBUFSIZ, fn)) != FAIL) 545: *rlen += len; 546: } 547: return FAIL; 548: }