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