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