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/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
28/*	  All Rights Reserved  	*/
29
30
31#pragma ident	"%Z%%M%	%I%	%E% SMI"
32
33#include "uucp.h"
34
35#include "pk.h"
36#include <sys/buf.h>
37
38extern void pkfail(), pkzero(), pkoutput(), pkreset(), pkcntl(), pkgetpack();
39extern int pksack();
40static void pkdata();
41static int pkcget();
42static void xlatestate(struct pack *, int);
43void xlatecntl(int, int);
44
45/*
46 * Code added to allow translation of states from numbers to
47 * letters, to be done in such a way as to be meaningful to
48 * John Q. Public
49 */
50struct {
51	int state;
52	char *msg;
53} st_trans[] = {
54	DEAD,	"Dead!",
55	INITa,	"INIT code a",
56	INITb,	"INIT code b",
57	LIVE,	"O.K.",
58	RXMIT,	"Rcv/Xmit",
59	RREJ,	"RREJ?",
60	PDEBUG,	"PDEBUG?",
61	DRAINO,	"Draino...",
62	WAITO,	"Waiting",
63	DOWN,	"Link down",
64	RCLOSE,	"RCLOSE?",
65	BADFRAME,	"Bad frame",
66	-1,	"End of the line",
67};
68
69extern char _Protocol[];	/* Protocol string with (options) */
70
71#define PKMAXSTMSG 40
72int Connodata = 0;		/* Continuous Non Valid Data Count */
73int Ntimeout = 0;
74#define CONNODATA	20	/* Max Continuous Non Valid Data Count */
75#define NTIMEOUT	50	/* This is not currently used, but maybe future */
76
77extern jmp_buf Getjbuf;
78
79/*
80 * start initial synchronization.
81 */
82struct pack *
83pkopen(ifn, ofn)
84int ifn, ofn;
85{
86	struct pack *pk;
87	char **bp;
88	int i;
89	int windows = WINDOWS;
90	extern int xpacksize, packsize;
91
92	if ((pk = (struct pack *) calloc(1, sizeof (struct pack))) == NULL)
93		return(NULL);
94	pk->p_ifn = ifn;
95	pk->p_ofn = ofn;
96	DEBUG(7, "Setting up protocol parameters '%s'\n", _Protocol);
97	if ( _Protocol[1] == '(' ) {
98	    if (sscanf(_Protocol, "%*c(%d,%d)", &windows, &packsize) == 0)
99	    sscanf(_Protocol, "%*c(,%d)", &packsize);
100	    windows = ( windows < MINWINDOWS ? WINDOWS :
101			( windows > MAXWINDOWS ? WINDOWS : windows ) );
102	    packsize = ( packsize < MINPACKSIZE ? PACKSIZE :
103			( packsize > MAXPACKSIZE ? PACKSIZE : packsize ) );
104	}
105	if ( (_Protocol[0] == 'g') && (packsize > OLDPACKSIZE) ) {
106	    /*
107	     * We reset to OLDPACKSIZE to maintain compatibility
108	     * with old limited implementations. Maybe we should
109	     * just warn the administrator and continue?
110	     */
111	    packsize = OLDPACKSIZE;
112	}
113	pk->p_xsize = pk->p_rsize = xpacksize = packsize;
114	pk->p_rwindow = pk->p_swindow = windows;
115
116	/*
117	 * allocate input window
118	 */
119	for (i = 0; i < pk->p_rwindow; i++) {
120		if ((bp = (char **) malloc((unsigned) pk->p_xsize)) == NULL)
121			break;
122		*bp = (char *) pk->p_ipool;
123		pk->p_ipool = bp;
124	}
125	if (i == 0)
126		return(NULL);
127	pk->p_rwindow = i;
128
129	/*
130	 * start synchronization
131	 */
132	pk->p_msg = pk->p_rmsg = M_INITA;
133	pkoutput(pk);
134
135	for (i = 0; i < PKMAXSTMSG; i++) {
136		pkgetpack(pk);
137		if ((pk->p_state & LIVE) != 0)
138			break;
139	}
140	if (i >= PKMAXSTMSG)
141		return(NULL);
142
143	pkreset(pk);
144	return(pk);
145}
146
147/*
148 * input framing and block checking.
149 * frame layout for most devices is:
150 *
151 *	S|K|X|Y|C|Z|  ... data ... |
152 *
153 *	where 	S	== initial synch byte
154 *		K	== encoded frame size (indexes pksizes[])
155 *		X, Y	== block check bytes
156 *		C	== control byte
157 *		Z	== XOR of header (K^X^Y^C)
158 *		data	== 0 or more data bytes
159 *
160 */
161#define GETRIES 10
162
163/*
164 * Byte collection.
165 */
166void
167pkgetpack(ipk)
168struct pack *ipk;
169{
170	char *p;
171	struct pack *pk;
172	struct header *h;
173	unsigned short sum;
174	int k, tries, ifn, noise;
175	char **bp, hdchk;
176
177	pk = ipk;
178	/*
179	 * If we are known to be DOWN, or if we've received too many garbage
180	 * packets or timeouts, give up without a fight.
181	 */
182	if ((pk->p_state & DOWN) || Connodata > CONNODATA  || Ntimeout > NTIMEOUT)
183		pkfail();
184	ifn = pk->p_ifn;
185	h = &pk->p_ihbuf;
186
187	/*
188	 * Attempt no more than GETRIES times to read a packet.  The only valid
189	 * exit from this loop is a return.  Break forces a failure.
190	 */
191	for (tries = 0; tries < GETRIES; tries++) {
192		/*
193		 * Read header.
194		 * First look for SYN.  If more than 3 * packetsize characters
195		 * go by w/o a SYN, request a retransmit.
196		 */
197		p = (caddr_t) h;
198		noise = 0;
199		for ( ; ; ) {
200			if (pkcget(ifn, p, HDRSIZ) != SUCCESS) {
201				DEBUG(7,
202		"Alarm while looking for SYN -- request RXMIT\n%s", "");
203				goto retransmit;
204			}
205			if (*p == SYN)
206				break;		/* got it */
207			else {
208				char *pp, *pend;
209
210				DEBUG(7, "first char not SYN (%x)\n", *p&0xff);
211				if ((pp = memchr(p, SYN, HDRSIZ)) != NULL) {
212					pend = p + HDRSIZ;
213					while (pp < pend)
214						*p++ = *pp++;
215					/* Now look for remainder of header */
216					if (pkcget(ifn, p, pend - p) !=
217					    SUCCESS) {
218						DEBUG(7,
219		"Alarm while looking for header -- request RXMIT\n%s", "");
220						goto retransmit;
221					}
222					p = (caddr_t) h;
223					break;	/* got entire header */
224				}
225			}
226			if ((noise += HDRSIZ) > 3 * pk->p_rsize) {
227				DEBUG(7,
228			"No SYN in %d characters -- request RXMIT\n", noise);
229				goto retransmit;
230			}
231		}
232		/* Validate the header */
233		Connodata++;
234		hdchk = p[1] ^ p[2] ^ p[3] ^ p[4];
235		sum = ((unsigned) p[2] & 0377) | ((unsigned) p[3] << 8);
236		h->sum = sum;
237		k = h->ksize;
238		if (hdchk != h->ccntl) {
239			/* bad header */
240			DEBUG(7, "bad header checksum\n%s", "");
241			return;
242		}
243
244		if (k == 9) {	/* control packet */
245			if (((h->sum + h->cntl) & 0xffff) == CHECK) {
246				pkcntl(h->cntl, pk);
247				xlatestate(pk, 7);
248			} else {
249				/* bad header */
250				DEBUG(7, "bad header (k == 9) 0%o\n", h->cntl&0xff);
251				pk->p_state |= BADFRAME;
252			}
253			return;
254		}
255		/* data packet */
256		if (k && pksizes[k] != pk->p_rsize)
257			return;
258		pk->p_rpr = h->cntl & MOD8;
259		pksack(pk);
260		if ((bp = pk->p_ipool) == NULL) {
261			DEBUG(7, "bp NULL\n%s", "");
262			return;
263		}
264		pk->p_ipool = (char **) *bp;
265		/* Header checks out, go for data */
266		if (pkcget(pk->p_ifn, (char *) bp, pk->p_rsize) == SUCCESS) {
267			pkdata(h->cntl, h->sum, pk, bp);
268			Ntimeout = 0;
269			return;
270		}
271		DEBUG(7, "Alarm while reading data -- request RXMIT\n%s", "");
272retransmit:
273		/*
274		 * Transmission error or excessive noise.  Send a RXMIT
275		 * and try again.
276		 */
277/*
278		Retries++;
279*/
280		pk->p_msg |= pk->p_rmsg;
281		if (pk->p_msg == 0)
282			pk->p_msg |= M_RR;
283		if ((pk->p_state & LIVE) == LIVE)
284			pk->p_state |= RXMIT;
285		pkoutput(pk);
286	}
287	DEBUG(7, "pkgetpack failed after %d tries\n", tries);
288	pkfail();
289}
290
291/*
292 * Translate pk->p_state into something printable.
293 */
294static void
295xlatestate(pk, dbglvl)
296struct pack *pk;
297int dbglvl;
298{
299	int i;
300	char delimc = ' ', msgline[80], *buf = msgline;
301
302	if (Debug < dbglvl)
303		return;
304	sprintf(buf, "state -");
305	buf += strlen(buf);
306	for(i = 0; st_trans[i].state != -1; i++) {
307		if (pk->p_state&st_trans[i].state){
308			sprintf(buf, "%c[%s]", delimc, st_trans[i].msg);
309			buf += strlen(buf);
310			delimc = '&';
311		}
312	}
313	sprintf(buf, " (0%o)\n", pk->p_state);
314	DEBUG(dbglvl, "%s", msgline);
315	return;
316}
317
318static void
319pkdata(c, sum, pk, bp)
320struct pack *pk;
321unsigned short sum;
322char c;
323char **bp;
324{
325	int x;
326	int t;
327	char m;
328
329	if (pk->p_state & DRAINO || !(pk->p_state & LIVE)) {
330		pk->p_msg |= pk->p_rmsg;
331		pkoutput(pk);
332		goto drop;
333	}
334	t = next[pk->p_pr];
335	for(x=pk->p_pr; x!=t; x = (x-1)&7) {
336		if (pk->p_is[x] == 0)
337			goto slot;
338	}
339drop:
340	*bp = (char *)pk->p_ipool;
341	pk->p_ipool = bp;
342	return;
343
344slot:
345	m = mask[x];
346	pk->p_imap |= m;
347	pk->p_is[x] = c;
348	pk->p_isum[x] = sum;
349	pk->p_ib[x] = (char *)bp;
350}
351
352/*
353 * Start transmission on output device associated with pk.
354 * For asynch devices (t_line==1) framing is
355 * imposed.  For devices with framing and crc
356 * in the driver (t_line==2) the transfer is
357 * passed on to the driver.
358 */
359void
360pkxstart(pk, cntl, x)
361struct pack *pk;
362int x;
363char cntl;
364{
365	char *p;
366	short checkword;
367	char hdchk;
368
369	p = (caddr_t) &pk->p_ohbuf;
370	*p++ = SYN;
371	if (x < 0) {
372		*p++ = hdchk = 9;
373		checkword = cntl;
374	} else {
375		*p++ = hdchk = pk->p_lpsize;
376		checkword = pk->p_osum[x] ^ (unsigned)(cntl & 0377);
377	}
378	checkword = CHECK - checkword;
379	*p = checkword;
380	hdchk ^= *p++;
381	*p = checkword>>8;
382	hdchk ^= *p++;
383	*p = cntl;
384	hdchk ^= *p++;
385	*p = hdchk;
386
387 /*
388  * writes
389  */
390	if (Debug >= 9)
391		xlatecntl(1, cntl);
392
393	p = (caddr_t) & pk->p_ohbuf;
394	if (x < 0) {
395		if ((*Write)(pk->p_ofn, p, HDRSIZ) != HDRSIZ) {
396			DEBUG(4, "pkxstart, write failed, %s\n",
397			    strerror(errno));
398			logent(strerror(errno), "PKXSTART WRITE");
399			pkfail();
400			/* NOT REACHED */
401		}
402	} else {
403		char buf[MAXPACKSIZE + HDRSIZ];
404
405		memcpy(buf, p, HDRSIZ);
406		memcpy(buf+HDRSIZ, pk->p_ob[x], pk->p_xsize);
407		if ((*Write)(pk->p_ofn, buf, pk->p_xsize + HDRSIZ) !=
408		    pk->p_xsize + HDRSIZ) {
409			DEBUG(4, "pkxstart, write failed, %s\n",
410			    strerror(errno));
411			logent(strerror(errno), "PKXSTART WRITE");
412			pkfail();
413			/* NOT REACHED */
414		}
415		Connodata = 0;
416	}
417	if (pk->p_msg)
418		pkoutput(pk);
419}
420
421/*
422 * get n characters from input
423 *	b	-> buffer for characters
424 *	fn	-> file descriptor
425 *	n	-> requested number of characters
426 * return:
427 *	SUCCESS	-> n chars successfully read
428 *	FAIL	-> o.w.
429 */
430
431static int
432pkcget(fn, b, n)
433int n;
434char *b;
435int fn;
436{
437	int ret;
438#ifdef PKSPEEDUP
439	extern int linebaudrate;
440	int donap = (linebaudrate > 0 && linebaudrate < 4800);
441#endif /*  PKSPEEDUP  */
442
443	if (n == 0)
444		return(SUCCESS);
445	if (setjmp(Getjbuf)) {
446		Ntimeout++;
447		DEBUG(4, "pkcget: alarm %d\n", Ntimeout);
448		return(FAIL);
449	}
450
451	(void) alarm( (unsigned) ( 10 + (n >> 7)) );
452
453	for (;;) {
454		ret = (*Read)(fn, b, n);
455		(void) alarm(0);
456		if (ret == 0) {
457			DEBUG(4, "pkcget, read failed, EOF\n", 0);
458			/*
459			 * Device has decided that the connection has no
460			 * more data to send.  Any further tries are futile...
461			 * (The only other way to get a zero return value
462			 * is to read a zero length message from a STREAM.
463			 * However, uucp *never* sends zero length messages
464			 * over any sort of channel...)
465			 */
466			pkfail();
467			/* NOT REACHED */
468		}
469		if (ret < 0) {
470			DEBUG(4, "pkcget, read failed, %s\n",
471			    strerror(errno));
472			logent(strerror(errno), "PKCGET READ");
473			pkfail();
474			/* NOT REACHED */
475		}
476		if ((n -= ret) <= 0)
477			break;
478#ifdef PKSPEEDUP
479		if (donap) {
480#if defined(BSD4_2) || defined(ATTSVR4)
481			/* wait for more chars to come in */
482			nap((n * HZ * 10) / linebaudrate); /* n char times */
483#else
484			sleep(1);
485#endif
486		}
487#endif /*  PKSPEEDUP  */
488		b += ret;
489		(void) alarm( (unsigned) ( 10 + (n >> 7)) );
490	}
491	(void) alarm(0);
492	return(SUCCESS);
493}
494
495/*
496 * role == 0: receive
497 * role == 1: send
498 */
499void
500xlatecntl(role, cntl)
501int role;
502int cntl;
503{
504	static char *cntltype[4] = {"CNTL, ", "ALT, ", "DATA, ", "SHORT, "};
505	static char *cntlxxx[8] = {"ZERO, ", "CLOSE, ", "RJ, ", "SRJ, ",
506				   "RR, ", "INITC, ", "INITB, ", "INITA, "};
507	char dbgbuf[128];
508	char *ptr;
509
510	ptr = dbgbuf;
511	strcpy(ptr, role ? "send " : "recv ");
512	ptr += strlen(ptr);
513
514	strcpy(ptr, cntltype[(cntl&0300)>>6]);
515	ptr += strlen(ptr);
516
517	if (cntl&0300) {
518		/* data packet */
519		if (role)
520			sprintf(ptr, "loc %o, rem %o\n", (cntl & 070) >> 3, cntl & 7);
521		else
522			sprintf(ptr, "loc %o, rem %o\n", cntl & 7, (cntl & 070) >> 3);
523	} else {
524		/* control packet */
525		strcpy(ptr, cntlxxx[(cntl&070)>>3]);
526		ptr += strlen(ptr);
527		sprintf(ptr, "val %o\n", cntl & 7);
528	}
529
530	DEBUG(1, dbgbuf, 0);
531}
532