1 #ifndef lint 2 static char sccsid[] = "@(#)fio.c 5.1 (Berkeley) 06/19/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 * Normally a calling site will take care of setting the 27 * local PAD pars via an X.28 command and those of the remote 28 * PAD via an X.29 command, unless the remote site has a 29 * special channel assigned for this protocol with the proper 30 * par settings. 31 * 32 * Author: Piet Beertema, CWI, Amsterdam, Sep 1984 33 */ 34 35 #include <signal.h> 36 #include "uucp.h" 37 #ifdef USG 38 #include <termio.h> 39 #else !USG 40 #include <sgtty.h> 41 #endif !USG 42 #include <setjmp.h> 43 44 #define FBUFSIZ 256 45 46 #ifndef MAXMSGLEN 47 #define MAXMSGLEN BUFSIZ 48 #endif MAXMSGLEN 49 50 static int fchksum; 51 static jmp_buf Ffailbuf; 52 53 static 54 falarm() 55 { 56 signal(SIGALRM, falarm); 57 longjmp(Ffailbuf, 1); 58 } 59 60 static int (*fsig)(); 61 62 #ifndef USG 63 #define TCGETA TIOCGETP 64 #define TCSETA TIOCSETP 65 #define termio sgttyb 66 #endif USG 67 68 fturnon() 69 { 70 int ret; 71 struct termio ttbuf; 72 73 ioctl(Ifn, TCGETA, &ttbuf); 74 #ifdef USG 75 ttbuf.c_iflag = IXOFF|IXON|ISTRIP; 76 ttbuf.c_cc[VMIN] = FBUFSIZ > 64 ? 64 : FBUFSIZ; 77 ttbuf.c_cc[VTIME] = 5; 78 #else 79 ttbuf.sg_flags = ANYP|CBREAK|TANDEM; 80 #endif USG 81 ret = ioctl(Ifn, TCSETA, &ttbuf); 82 ASSERT(ret >= 0, "STTY FAILED", "", ret); 83 fsig = signal(SIGALRM, falarm); 84 /* give the other side time to perform its ioctl; 85 * otherwise it may flush out the first data this 86 * side is about to send. 87 */ 88 sleep(2); 89 return SUCCESS; 90 } 91 92 fturnoff() 93 { 94 (void) signal(SIGALRM, fsig); 95 return SUCCESS; 96 } 97 98 fwrmsg(type, str, fn) 99 register char *str; 100 int fn; 101 char type; 102 { 103 register char *s; 104 char bufr[MAXMSGLEN]; 105 106 s = bufr; 107 *s++ = type; 108 while (*str) 109 *s++ = *str++; 110 if (*(s-1) == '\n') 111 s--; 112 *s++ = '\r'; 113 (void) write(fn, bufr, s - bufr); 114 return SUCCESS; 115 } 116 117 frdmsg(str, fn) 118 register char *str; 119 register int fn; 120 { 121 register char *smax; 122 123 if (setjmp(Ffailbuf)) 124 return FAIL; 125 smax = str + MAXMSGLEN - 1; 126 (void) alarm(2*MAXMSGTIME); 127 for (;;) { 128 if (read(fn, str, 1) <= 0) 129 goto msgerr; 130 if (*str == '\r') 131 break; 132 if (*str < ' ') 133 continue; 134 if (str++ >= smax) 135 goto msgerr; 136 } 137 *str = '\0'; 138 (void) alarm(0); 139 return SUCCESS; 140 msgerr: 141 (void) alarm(0); 142 return FAIL; 143 } 144 145 fwrdata(fp1, fn) 146 FILE *fp1; 147 int fn; 148 { 149 register int flen, alen, ret; 150 char ibuf[FBUFSIZ]; 151 char ack; 152 long abytes, fbytes; 153 struct timeb t1, t2; 154 int mil, retries = 0; 155 156 ret = FAIL; 157 retry: 158 fchksum = 0xffff; 159 abytes = fbytes = 0L; 160 ack = '\0'; 161 #ifdef USG 162 time(&t1.time); 163 t1.millitm = 0; 164 #else !USG 165 ftime(&t1); 166 #endif !USG 167 while ((flen = fread(ibuf, sizeof (char), FBUFSIZ, fp1)) > 0) { 168 alen = fwrblk(fn, ibuf, flen); 169 abytes += alen >= 0 ? alen : -alen; 170 if (alen <= 0) 171 goto acct; 172 fbytes += flen; 173 } 174 sprintf(ibuf, "\176\176%04x\r", fchksum); 175 abytes += alen = strlen(ibuf); 176 if (write(fn, ibuf, alen) == alen) { 177 DEBUG(8, "%d\n", alen); 178 DEBUG(8, "checksum: %04x\n", fchksum); 179 if (frdmsg(ibuf, fn) != FAIL) { 180 if ((ack = ibuf[0]) == 'G') 181 ret = 0; 182 DEBUG(4, "ack - '%c'\n", ack); 183 } 184 } 185 acct: 186 if (ack == 'R') { 187 DEBUG(4, "RETRY:\n", 0); 188 fseek(fp1, 0L, 0); 189 retries++; 190 goto retry; 191 } 192 #ifdef USG 193 time(&t2.time); 194 t2.millitm = 0; 195 #else !USG 196 ftime(&t2); 197 #endif !USG 198 Now = t2; 199 t2.time -= t1.time; 200 mil = t2.millitm - t1.millitm; 201 if (mil < 0) { 202 --t2.time; 203 mil += 1000; 204 } 205 sprintf(ibuf, "sent data %ld bytes %ld.%02d secs", 206 fbytes, (long)t2.time, mil/10); 207 sysacct(abytes, t2.time - t1.time); 208 if (retries > 0) 209 sprintf((char *)ibuf+strlen(ibuf)," %d retries", retries); 210 DEBUG(1, "%s\n", ibuf); 211 syslog(ibuf); 212 #ifdef SYSACCT 213 if (ret) 214 sysaccf(NULL); /* force accounting */ 215 #endif SYSACCT 216 return ret; 217 } 218 219 /* max. attempts to retransmit a file: */ 220 #define MAXRETRIES (fbytes < 10000L ? 2 : 1) 221 222 frddata(fn, fp2) 223 register int fn; 224 register FILE *fp2; 225 { 226 register int flen; 227 register char eof; 228 char ibuf[FBUFSIZ]; 229 int ret, retries = 0; 230 long alen, abytes, fbytes; 231 struct timeb t1, t2; 232 int mil; 233 234 ret = FAIL; 235 retry: 236 fchksum = 0xffff; 237 abytes = fbytes = 0L; 238 #ifdef USG 239 time(&t1.time); 240 t1.millitm = 0; 241 #else !USG 242 ftime(&t1); 243 #endif !USG 244 do { 245 flen = frdblk(ibuf, fn, &alen); 246 abytes += alen; 247 if (flen < 0) 248 goto acct; 249 if (eof = flen > FBUFSIZ) 250 flen -= FBUFSIZ + 1; 251 fbytes += flen; 252 if (fwrite(ibuf, sizeof (char), flen, fp2) != flen) 253 goto acct; 254 } while (!eof); 255 ret = 0; 256 acct: 257 if (ret) { 258 if (retries++ < MAXRETRIES) { 259 DEBUG(8, "send ack: 'R'\n", 0); 260 fwrmsg('R', "", fn); 261 fseek(fp2, 0L, 0); 262 DEBUG(4, "RETRY:\n", 0); 263 goto retry; 264 } 265 DEBUG(8, "send ack: 'Q'\n", 0); 266 fwrmsg('Q', "", fn); 267 #ifdef SYSACCT 268 sysaccf(NULL); /* force accounting */ 269 #endif SYSACCT 270 } else { 271 DEBUG(8, "send ack: 'G'\n", 0); 272 fwrmsg('G', "", fn); 273 } 274 #ifdef USG 275 time(&t2.time); 276 t2.millitm = 0; 277 #else !USG 278 ftime(&t2); 279 #endif !USG 280 Now = t2; 281 t2.time -= t1.time; 282 mil = t2.millitm - t1.millitm; 283 if (mil < 0) { 284 --t2.time; 285 mil += 1000; 286 } 287 sprintf(ibuf, "received data %ld bytes %ld.%02d secs", 288 fbytes, (long)t2.time, mil/10); 289 sysacct(abytes, t2.time - t1.time); 290 if (retries > 0) 291 sprintf((char *)ibuf+strlen(ibuf)," %d retries", retries); 292 DEBUG(1, "%s\n", ibuf); 293 syslog(ibuf); 294 return ret; 295 } 296 297 static 298 frdbuf(blk, len, fn) 299 register char *blk; 300 register int len; 301 register int fn; 302 { 303 static int ret = FBUFSIZ / 2; 304 #ifndef Not080 305 extern int linebaudrate; 306 #endif Not080 307 308 if (setjmp(Ffailbuf)) 309 return FAIL; 310 #ifndef Not080 311 if (len == FBUFSIZ && ret < FBUFSIZ / 2 && 312 linebaudrate > 0 && linebaudrate < 4800) 313 sleep(1); 314 #endif Not080 315 (void) alarm(MAXMSGTIME); 316 ret = read(fn, blk, len); 317 alarm(0); 318 return ret <= 0 ? FAIL : ret; 319 } 320 321 /* call ultouch every TC calls to either frdblk or fwrblk */ 322 323 #define TC 20 324 static int tc = TC; 325 326 /* Byte conversion: 327 * 328 * from pre to 329 * 000-037 172 100-137 330 * 040-171 040-171 331 * 172-177 173 072-077 332 * 200-237 174 100-137 333 * 240-371 175 040-171 334 * 372-377 176 072-077 335 */ 336 337 static 338 fwrblk(fn, ip, len) 339 int fn; 340 register char *ip; 341 register int len; 342 { 343 register char *op; 344 register int sum, nl; 345 int ret; 346 char obuf[FBUFSIZ * 2]; 347 348 /* call ultouch occasionally */ 349 if (--tc < 0) { 350 tc = TC; 351 ultouch(); 352 } 353 DEBUG(8, "%d/", len); 354 op = obuf; 355 nl = 0; 356 sum = fchksum; 357 do { 358 if (sum & 0x8000) { 359 sum <<= 1; 360 sum++; 361 } else 362 sum <<= 1; 363 sum += *ip & 0377; 364 sum &= 0xffff; 365 if (*ip & 0200) { 366 *ip &= 0177; 367 if (*ip < 040) { 368 *op++ = '\174'; 369 *op++ = *ip++ + 0100; 370 } else 371 if (*ip <= 0171) { 372 *op++ = '\175'; 373 *op++ = *ip++; 374 } 375 else { 376 *op++ = '\176'; 377 *op++ = *ip++ - 0100; 378 } 379 nl += 2; 380 } else { 381 if (*ip < 040) { 382 *op++ = '\172'; 383 *op++ = *ip++ + 0100; 384 nl += 2; 385 } else 386 if (*ip <= 0171) { 387 *op++ = *ip++; 388 nl++; 389 } else { 390 *op++ = '\173'; 391 *op++ = *ip++ - 0100; 392 nl += 2; 393 } 394 } 395 } while (--len); 396 fchksum = sum; 397 DEBUG(8, "%d,", nl); 398 ret = write(fn, obuf, nl); 399 return ret == nl ? nl : ret < 0 ? 0 : -ret; 400 } 401 402 static 403 frdblk(ip, fn, rlen) 404 register char *ip; 405 int fn; 406 long *rlen; 407 { 408 register char *op, c; 409 register int sum, len, nl; 410 char buf[5], *erbp = ip; 411 int i; 412 static char special = 0; 413 414 /* call ultouch occasionally */ 415 if (--tc < 0) { 416 tc = TC; 417 ultouch(); 418 } 419 420 if ((len = frdbuf(ip, FBUFSIZ, fn)) == FAIL) { 421 *rlen = 0; 422 goto dcorr; 423 } 424 *rlen = len; 425 DEBUG(8, "%d/", len); 426 op = ip; 427 nl = 0; 428 sum = fchksum; 429 do { 430 if ((*ip &= 0177) >= '\172') { 431 if (special) { 432 DEBUG(8, "%d", nl); 433 special = 0; 434 op = buf; 435 if (*ip++ != '\176' || (i = --len) > 5) 436 goto dcorr; 437 while (i--) 438 *op++ = *ip++; 439 while (len < 5) { 440 i = frdbuf(&buf[len], 5 - len, fn); 441 if (i == FAIL) { 442 len = FAIL; 443 goto dcorr; 444 } 445 DEBUG(8, ",%d", i); 446 len += i; 447 *rlen += i; 448 } 449 if (buf[4] != '\r') 450 goto dcorr; 451 sscanf(buf, "%4x", &fchksum); 452 DEBUG(8, "\nchecksum: %04x\n", sum); 453 if (fchksum == sum) 454 return FBUFSIZ + 1 + nl; 455 else { 456 DEBUG(8, "\n", 0); 457 DEBUG(4, "Bad checksum\n", 0); 458 return FAIL; 459 } 460 } 461 special = *ip++; 462 } else { 463 if (*ip < '\040') { 464 /* error: shouldn't get control chars */ 465 goto dcorr; 466 } 467 switch (special) { 468 case 0: 469 c = *ip++; 470 break; 471 case '\172': 472 c = *ip++ - 0100; 473 break; 474 case '\173': 475 c = *ip++ + 0100; 476 break; 477 case '\174': 478 c = *ip++ + 0100; 479 break; 480 case '\175': 481 c = *ip++ + 0200; 482 break; 483 case '\176': 484 c = *ip++ + 0300; 485 break; 486 } 487 *op++ = c; 488 if (sum & 0x8000) { 489 sum <<= 1; 490 sum++; 491 } else 492 sum <<= 1; 493 sum += c & 0377; 494 sum &= 0xffff; 495 special = 0; 496 nl++; 497 } 498 } while (--len); 499 fchksum = sum; 500 DEBUG(8, "%d,", nl); 501 return nl; 502 dcorr: 503 DEBUG(8, "\n", 0); 504 DEBUG(4, "Data corrupted\n", 0); 505 while (len != FAIL) { 506 if ((len = frdbuf(erbp, FBUFSIZ, fn)) != FAIL) 507 *rlen += len; 508 } 509 return FAIL; 510 } 511