xref: /illumos-gate/usr/src/cmd/bnu/pk1.c (revision 21d7f835c9bac5f9e80c72fc972ee5b288845983)
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 
38 extern void pkfail(), pkzero(), pkoutput(), pkreset(), pkcntl(), pkgetpack();
39 extern int pksack();
40 static void pkdata();
41 static int pkcget();
42 static void xlatestate(struct pack *, int);
43 void 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  */
50 struct {
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 
69 extern char _Protocol[];	/* Protocol string with (options) */
70 
71 #define PKMAXSTMSG 40
72 int Connodata = 0;		/* Continuous Non Valid Data Count */
73 int 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 
77 extern jmp_buf Getjbuf;
78 
79 /*
80  * start initial synchronization.
81  */
82 struct pack *
83 pkopen(ifn, ofn)
84 int 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  */
166 void
167 pkgetpack(ipk)
168 struct 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", "");
272 retransmit:
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  */
294 static void
295 xlatestate(pk, dbglvl)
296 struct pack *pk;
297 int 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 
318 static void
319 pkdata(c, sum, pk, bp)
320 struct pack *pk;
321 unsigned short sum;
322 char c;
323 char **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 	}
339 drop:
340 	*bp = (char *)pk->p_ipool;
341 	pk->p_ipool = bp;
342 	return;
343 
344 slot:
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  */
359 void
360 pkxstart(pk, cntl, x)
361 struct pack *pk;
362 int x;
363 char 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 
431 static int
432 pkcget(fn, b, n)
433 int n;
434 char *b;
435 int 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  */
499 void
500 xlatecntl(role, cntl)
501 int role;
502 int 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