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