xref: /original-bsd/usr.bin/uucp/uucico/fio.c (revision d272e02a)
1 /*-
2  * Copyright (c) 1985 The Regents of the University of California.
3  * 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	5.8 (Berkeley) 03/15/91";
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
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 
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 
140 fturnoff()
141 {
142 	if (!IsTcpIp)
143 		ioctl(Ifn, TCSETAF, &ttbuf);
144 	(void) signal(SIGALRM, fsig);
145 	sleep(2);
146 	return SUCCESS;
147 }
148 
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 
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 
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 
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
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
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
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