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