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