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