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