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