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
falarm(sig)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
fturnon(void)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
fturnoff(void)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
fwrmsg(type,str,fn)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
frdmsg(str,fn)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
fwrdata(fp1,fn)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
frddata(fn,fp2)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
frdbuf(blk,len,fn)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
fwrblk(fn,fp,lenp)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
frdblk(ip,fn,rlen)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