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