1 #ifndef lint 2 static char sccsid[] = "@(#)fio.c 5.6 (Berkeley) 06/29/90"; 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 4096 /* for X.25 interfaces: set equal to packet size, 56 * but see comment above 57 */ 58 59 #define FOBUFSIZ 4096 /* 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 extern long Bytes_Sent, Bytes_Received; 72 73 static 74 falarm() 75 { 76 signal(SIGALRM, falarm); 77 longjmp(Ffailbuf, 1); 78 } 79 80 static void (*fsig)(); 81 82 #ifndef USG 83 #define TCGETA TIOCGETP 84 #define TCSETAF TIOCSETP 85 #define termio sgttyb 86 #endif USG 87 static struct termio ttbuf; 88 89 fturnon() 90 { 91 int ttbuf_flags; 92 93 if (!IsTcpIp) { 94 ioctl(Ifn, TCGETA, &ttbuf); 95 #ifdef USG 96 ttbuf_flags = ttbuf.c_iflag; 97 ttbuf.c_iflag = IXOFF|IXON|ISTRIP; 98 ttbuf.c_cc[VMIN] = FIBUFSIZ > 64 ? 64 : FIBUFSIZ; 99 ttbuf.c_cc[VTIME] = 5; 100 if (ioctl(Ifn, TCSETAF, &ttbuf) < 0) { 101 syslog(LOG_ERR, "ioctl(TCSETAF) failed: %m"); 102 cleanup(FAIL); 103 } 104 ttbuf.c_iflag = ttbuf_flags; 105 #else !USG 106 ttbuf_flags = ttbuf.sg_flags; 107 ttbuf.sg_flags = ANYP|CBREAK; 108 if (ioctl(Ifn, TCSETAF, &ttbuf) < 0) { 109 syslog(LOG_ERR, "ioctl(TCSETAF) failed: %m"); 110 cleanup(FAIL); 111 } 112 /* this is two seperate ioctls to set the x.29 params */ 113 ttbuf.sg_flags |= TANDEM; 114 if (ioctl(Ifn, TCSETAF, &ttbuf) < 0) { 115 syslog(LOG_ERR, "ioctl(TCSETAF) failed: %m"); 116 cleanup(FAIL); 117 } 118 ttbuf.sg_flags = ttbuf_flags; 119 #endif USG 120 } 121 fsig = signal(SIGALRM, falarm); 122 /* give the other side time to perform its ioctl; 123 * otherwise it may flush out the first data this 124 * side is about to send. 125 */ 126 sleep(2); 127 return SUCCESS; 128 } 129 130 fturnoff() 131 { 132 if (!IsTcpIp) 133 ioctl(Ifn, TCSETAF, &ttbuf); 134 (void) signal(SIGALRM, fsig); 135 sleep(2); 136 return SUCCESS; 137 } 138 139 fwrmsg(type, str, fn) 140 register char *str; 141 int fn; 142 char type; 143 { 144 register char *s; 145 char bufr[MAXMSGLEN]; 146 147 s = bufr; 148 *s++ = type; 149 while (*str) 150 *s++ = *str++; 151 if (*(s-1) == '\n') 152 s--; 153 *s++ = '\r'; 154 *s = 0; 155 (void) write(fn, bufr, s - bufr); 156 return SUCCESS; 157 } 158 159 frdmsg(str, fn) 160 register char *str; 161 register int fn; 162 { 163 register char *smax; 164 165 if (setjmp(Ffailbuf)) 166 return FAIL; 167 smax = str + MAXMSGLEN - 1; 168 (void) alarm(2*MAXMSGTIME); 169 for (;;) { 170 if (read(fn, str, 1) <= 0) 171 goto msgerr; 172 *str &= 0177; 173 if (*str == '\r') 174 break; 175 if (*str < ' ') { 176 continue; 177 } 178 if (str++ >= smax) 179 goto msgerr; 180 } 181 *str = '\0'; 182 (void) alarm(0); 183 return SUCCESS; 184 msgerr: 185 (void) alarm(0); 186 return FAIL; 187 } 188 189 fwrdata(fp1, fn) 190 FILE *fp1; 191 int fn; 192 { 193 register int alen, ret; 194 char ack, ibuf[MAXMSGLEN]; 195 int flen, mil, retries = 0; 196 long abytes, fbytes; 197 struct timeb t1, t2; 198 float ft; 199 200 ret = FAIL; 201 retry: 202 fchksum = 0xffff; 203 abytes = fbytes = 0L; 204 ack = '\0'; 205 #ifdef USG 206 time(&t1.time); 207 t1.millitm = 0; 208 #else !USG 209 ftime(&t1); 210 #endif !USG 211 do { 212 alen = fwrblk(fn, fp1, &flen); 213 fbytes += flen; 214 if (alen <= 0) { 215 abytes -= alen; 216 goto acct; 217 } 218 abytes += alen; 219 } while (!feof(fp1) && !ferror(fp1)); 220 DEBUG(8, "\nchecksum: %04x\n", fchksum); 221 if (frdmsg(ibuf, fn) != FAIL) { 222 if ((ack = ibuf[0]) == 'G') 223 ret = SUCCESS; 224 DEBUG(4, "ack - '%c'\n", ack); 225 } 226 acct: 227 #ifdef USG 228 time(&t2.time); 229 t2.millitm = 0; 230 #else !USG 231 ftime(&t2); 232 #endif !USG 233 Now = t2; 234 t2.time -= t1.time; 235 mil = t2.millitm - t1.millitm; 236 if (mil < 0) { 237 --t2.time; 238 mil += 1000; 239 } 240 ft = (float)t2.time + (float)mil/1000.; 241 sprintf(ibuf, "sent data %ld bytes %.2f secs %ld bps", 242 fbytes, ft, (long)((float)fbytes*8./ft)); 243 sysacct(abytes, t2.time); 244 Bytes_Sent += fbytes; 245 if (retries > 0) 246 sprintf(&ibuf[strlen(ibuf)], ", %d retries", retries); 247 DEBUG(1, "%s\n", ibuf); 248 log_xferstats(ibuf); 249 if (ack == 'R') { 250 DEBUG(4, "RETRY:\n", 0); 251 fseek(fp1, 0L, 0); 252 retries++; 253 goto retry; 254 } 255 #ifdef SYSACCT 256 if (ret == FAIL) 257 sysaccf(NULL); /* force accounting */ 258 #endif SYSACCT 259 return ret; 260 } 261 262 /* max. attempts to retransmit a file: */ 263 #define MAXRETRIES (fbytes < 10000L ? 2 : 1) 264 265 frddata(fn, fp2) 266 register int fn; 267 register FILE *fp2; 268 { 269 register int flen; 270 register char eof; 271 char ibuf[FIBUFSIZ]; 272 int ret, mil, retries = 0; 273 long alen, abytes, fbytes; 274 struct timeb t1, t2; 275 float ft; 276 277 ret = FAIL; 278 retry: 279 fchksum = 0xffff; 280 abytes = fbytes = 0L; 281 #ifdef USG 282 time(&t1.time); 283 t1.millitm = 0; 284 #else !USG 285 ftime(&t1); 286 #endif !USG 287 do { 288 flen = frdblk(ibuf, fn, &alen); 289 abytes += alen; 290 if (flen < 0) 291 goto acct; 292 if (eof = flen > FIBUFSIZ) 293 flen -= FIBUFSIZ + 1; 294 fbytes += flen; 295 if (fwrite(ibuf, sizeof (char), flen, fp2) != flen) 296 goto acct; 297 } while (!eof); 298 ret = SUCCESS; 299 acct: 300 #ifdef USG 301 time(&t2.time); 302 t2.millitm = 0; 303 #else !USG 304 ftime(&t2); 305 #endif !USG 306 Now = t2; 307 t2.time -= t1.time; 308 mil = t2.millitm - t1.millitm; 309 if (mil < 0) { 310 --t2.time; 311 mil += 1000; 312 } 313 ft = (float)t2.time + (float)mil/1000.; 314 sprintf(ibuf, "received data %ld bytes %.2f secs %ld bps", 315 fbytes, ft, (long)((float)fbytes*8./ft)); 316 if (retries > 0) 317 sprintf(&ibuf[strlen(ibuf)]," %d retries", retries); 318 sysacct(abytes, t2.time); 319 Bytes_Received += fbytes; 320 DEBUG(1, "%s\n", ibuf); 321 log_xferstats(ibuf); 322 if (ret == FAIL) { 323 if (retries++ < MAXRETRIES) { 324 DEBUG(8, "send ack: 'R'\n", 0); 325 fwrmsg('R', "", fn); 326 fseek(fp2, 0L, 0); 327 DEBUG(4, "RETRY:\n", 0); 328 goto retry; 329 } 330 DEBUG(8, "send ack: 'Q'\n", 0); 331 fwrmsg('Q', "", fn); 332 #ifdef SYSACCT 333 sysaccf(NULL); /* force accounting */ 334 #endif SYSACCT 335 } 336 else { 337 DEBUG(8, "send ack: 'G'\n", 0); 338 fwrmsg('G', "", fn); 339 } 340 return ret; 341 } 342 343 static 344 frdbuf(blk, len, fn) 345 register char *blk; 346 register int len; 347 register int fn; 348 { 349 static int ret = FIBUFSIZ / 2; 350 351 if (setjmp(Ffailbuf)) 352 return FAIL; 353 (void) alarm(MAXMSGTIME); 354 ret = read(fn, blk, len); 355 alarm(0); 356 return ret <= 0 ? FAIL : ret; 357 } 358 359 #if !defined(BSD4_2) && !defined(USG) 360 /* call ultouch every TC calls to either frdblk or fwrblk */ 361 #define TC 20 362 static int tc = TC; 363 #endif !defined(BSD4_2) && !defined(USG) 364 365 /* Byte conversion: 366 * 367 * from pre to 368 * 000-037 172 100-137 369 * 040-171 040-171 370 * 172-177 173 072-077 371 * 200-237 174 100-137 372 * 240-371 175 040-171 373 * 372-377 176 072-077 374 */ 375 376 static 377 fwrblk(fn, fp, lenp) 378 int fn; 379 register FILE *fp; 380 int *lenp; 381 { 382 register char *op; 383 register int c, sum, nl, len; 384 char obuf[FOBUFSIZ + 8]; 385 int ret; 386 387 #if !defined(BSD4_2) && !defined(USG) 388 /* call ultouch occasionally */ 389 if (--tc < 0) { 390 tc = TC; 391 ultouch(); 392 } 393 #endif !defined(BSD4_2) && !defined(USG) 394 op = obuf; 395 nl = 0; 396 len = 0; 397 sum = fchksum; 398 while ((c = getc(fp)) != EOF) { 399 len++; 400 if (sum & 0x8000) { 401 sum <<= 1; 402 sum++; 403 } else 404 sum <<= 1; 405 sum += c; 406 sum &= 0xffff; 407 if (c & 0200) { 408 c &= 0177; 409 if (c < 040) { 410 *op++ = '\174'; 411 *op++ = c + 0100; 412 } else 413 if (c <= 0171) { 414 *op++ = '\175'; 415 *op++ = c; 416 } 417 else { 418 *op++ = '\176'; 419 *op++ = c - 0100; 420 } 421 nl += 2; 422 } else { 423 if (c < 040) { 424 *op++ = '\172'; 425 *op++ = c + 0100; 426 nl += 2; 427 } else 428 if (c <= 0171) { 429 *op++ = c; 430 nl++; 431 } else { 432 *op++ = '\173'; 433 *op++ = c - 0100; 434 nl += 2; 435 } 436 } 437 if (nl >= FOBUFSIZ - 1) { 438 /* 439 * peek at next char, see if it will fit 440 */ 441 c = getc(fp); 442 if (c == EOF) 443 break; 444 (void) ungetc(c, fp); 445 if (nl >= FOBUFSIZ || c < 040 || c > 0171) 446 goto writeit; 447 } 448 } 449 /* 450 * At EOF - append checksum, there is space for it... 451 */ 452 sprintf(op, "\176\176%04x\r", sum); 453 nl += strlen(op); 454 writeit: 455 *lenp = len; 456 fchksum = sum; 457 DEBUG(8, "%d/", len); 458 DEBUG(8, "%d,", nl); 459 ret = write(fn, obuf, nl); 460 return ret == nl ? nl : ret < 0 ? 0 : -ret; 461 } 462 463 static 464 frdblk(ip, fn, rlen) 465 register char *ip; 466 int fn; 467 long *rlen; 468 { 469 register char *op, c; 470 register int sum, len, nl; 471 char buf[5], *erbp = ip; 472 int i; 473 static char special = 0; 474 475 #if !defined(BSD4_2) && !defined(USG) 476 /* call ultouch occasionally */ 477 if (--tc < 0) { 478 tc = TC; 479 ultouch(); 480 } 481 #endif !defined(BSD4_2) && !defined(USG) 482 if ((len = frdbuf(ip, FIBUFSIZ, fn)) == FAIL) { 483 *rlen = 0; 484 goto dcorr; 485 } 486 *rlen = len; 487 DEBUG(8, "%d/", len); 488 op = ip; 489 nl = 0; 490 sum = fchksum; 491 do { 492 if ((*ip &= 0177) >= '\172') { 493 if (special) { 494 DEBUG(8, "%d", nl); 495 special = 0; 496 op = buf; 497 if (*ip++ != '\176' || (i = --len) > 5) 498 goto dcorr; 499 while (i--) 500 *op++ = *ip++ & 0177; 501 while (len < 5) { 502 i = frdbuf(&buf[len], 5 - len, fn); 503 if (i == FAIL) { 504 len = FAIL; 505 goto dcorr; 506 } 507 DEBUG(8, ",%d", i); 508 len += i; 509 *rlen += i; 510 while (i--) 511 *op++ &= 0177; 512 } 513 if (buf[4] != '\r') 514 goto dcorr; 515 sscanf(buf, "%4x", &fchksum); 516 DEBUG(8, "\nchecksum: %04x\n", sum); 517 if (fchksum == sum) 518 return FIBUFSIZ + 1 + nl; 519 else { 520 DEBUG(8, "\n", 0); 521 DEBUG(4, "Bad checksum\n", 0); 522 return FAIL; 523 } 524 } 525 special = *ip++; 526 } else { 527 if (*ip < '\040') { 528 /* error: shouldn't get control chars */ 529 goto dcorr; 530 } 531 switch (special) { 532 case 0: 533 c = *ip++; 534 break; 535 case '\172': 536 c = *ip++ - 0100; 537 break; 538 case '\173': 539 c = *ip++ + 0100; 540 break; 541 case '\174': 542 c = *ip++ + 0100; 543 break; 544 case '\175': 545 c = *ip++ + 0200; 546 break; 547 case '\176': 548 c = *ip++ + 0300; 549 break; 550 } 551 *op++ = c; 552 if (sum & 0x8000) { 553 sum <<= 1; 554 sum++; 555 } else 556 sum <<= 1; 557 sum += c & 0377; 558 sum &= 0xffff; 559 special = 0; 560 nl++; 561 } 562 } while (--len); 563 fchksum = sum; 564 DEBUG(8, "%d,", nl); 565 return nl; 566 dcorr: 567 DEBUG(8, "\n", 0); 568 DEBUG(4, "Data corrupted\n", 0); 569 while (len != FAIL) { 570 if ((len = frdbuf(erbp, FIBUFSIZ, fn)) != FAIL) 571 *rlen += len; 572 } 573 return FAIL; 574 } 575 576