xref: /original-bsd/contrib/usr.x25/x29d/x29d.c (revision a1c2194a)
1 /*
2  * X.29 server
3  *
4  * Frank Pronk (...!ubc-vision!pronk)
5  * April, September 1984
6  *
7  * Laboratory for Computational Vision
8  * University of British Columbia
9  * Copyright (c)
10  */
11 
12 #include <sys/param.h>
13 #include <sys/socket.h>
14 #include <sys/stat.h>
15 #include <sys/wait.h>
16 
17 #include <netccitt/x25.h>
18 
19 #include <errno.h>
20 #include <netdb.h>
21 #include <signal.h>
22 #include <sys/ioctl.h>
23 #include <sys/termios.h>
24 #include <paths.h>
25 
26 #include "../h/x29.h"
27 
28 #define BUFSIZ		1024
29 #define MAXARGS		10	/* maximum size of server argument list */
30 
31 #define X25NET		0	/* no ITI parameters */
32 #define CCITT1978	1	/* 1978 CCITT standard parameter set */
33 #define CCITT1980	2	/* 1980 CCITT standard parameter set */
34 
35 
36 char	pibuf[BUFSIZ], fibuf[BUFSIZ];
37 int	pty, net;
38 extern	char **environ;
39 extern	int errno;
40 char	line[MAXPATHLEN];
41 char	console[] = "/dev/console";
42 short	packet_size;
43 short	debug;
44 char	*tracefn;		/* trace file name */
45 char	*server;
46 short	send_banner;
47 struct	termios pt, old_pt;
48 struct	sockaddr_x25 sock;
49 
50 int	reapchild();
51 struct	net *lookup ();
52 
53 char	ccitt1978_prof[] = {		/* initial profile */
54 	Q_BIT,	X29_SET_AND_READ_PARMS,
55 	X29_ECHO_CODE,			1,	/* echo on */
56 	X29_FORWARDING_SIGNAL_CODE,	126,	/* forward on all cntl */
57 	X29_IDLE_TIMER_CODE,		0,	/* off */
58 	X29_AUX_DEV_CONTROL_CODE,	0,	/* off */
59 	X29_RECEIVE_NET_MSGS_CODE,	1,	/* xmit network msgs */
60 	X29_BREAK_PROCEDURE_CODE,	21,
61 	X29_PADDING_CODE,		0,	/* off */
62 	X29_LINE_FOLDING_CODE,		0,	/* off */
63 	X29_TRANSMISSION_SPEED_CODE,	0,
64 	X29_XON_XOFF_CODE,		1,	/* enable XON/XOFF */
65 };
66 
67 char	ccitt1980_prof[] = {		/* initial profile */
68 	Q_BIT,	X29_SET_AND_READ_PARMS,
69 	X29_ECHO_CODE,			1,	/* echo on */
70 	X29_FORWARDING_SIGNAL_CODE,	126,	/* forward on all cntl */
71 	X29_IDLE_TIMER_CODE,		0,	/* off */
72 	X29_AUX_DEV_CONTROL_CODE,	0,	/* off */
73 	X29_RECEIVE_NET_MSGS_CODE,	1,	/* xmit network msgs */
74 	X29_BREAK_PROCEDURE_CODE,	21,
75 	X29_PADDING_CODE,		0,	/* off */
76 	X29_LINE_FOLDING_CODE,		0,	/* off */
77 	X29_TRANSMISSION_SPEED_CODE,	0,
78 	X29_XON_XOFF_CODE,		1,	/* enable XON/XOFF */
79 
80 	X29_LF_AFTER_CR,		4,	/* lf after cr from terminal */
81 	X29_EDITING,			1,	/* on */
82 	X29_CHARACTER_DELETE,		CERASE,
83 	X29_LINE_DELETE,		CKILL,
84 	X29_LINE_DISPLAY,		CRPRNT,
85 };
86 
87 char	datapac_prof[] = {		/* Canadian X.25 network */
88 	Q_BIT,	X29_SET_AND_READ_PARMS,
89 	X29_ECHO_CODE,			1,	/* echo on */
90 	X29_FORWARDING_SIGNAL_CODE,	126,	/* forward on all cntl */
91 	X29_IDLE_TIMER_CODE,		0,	/* off */
92 	X29_AUX_DEV_CONTROL_CODE,	0,	/* off */
93 	X29_RECEIVE_NET_MSGS_CODE,	1,	/* xmit network msgs */
94 	X29_BREAK_PROCEDURE_CODE,	21,
95 	X29_PADDING_CODE,		0,	/* off */
96 	X29_LINE_FOLDING_CODE,		0,	/* off */
97 	X29_TRANSMISSION_SPEED_CODE,	0,
98 	X29_XON_XOFF_CODE,		1,	/* enable XON/XOFF */
99 
100 	X29_LF_AFTER_CR,		4,	/* lf after cr from terminal */
101 	X29_EDITING,			1,	/* on */
102 	X29_CHARACTER_DELETE,		CERASE,
103 	X29_LINE_DELETE,		CKILL,
104 	X29_LINE_DISPLAY,		CRPRNT,
105 
106 	/*
107 	 * This rubbish can be removed when Datapac
108 	 * adopts the 1980 standard parameter set.
109 	 */
110 
111 	0,				0,	/* national parameter marker */
112 	123,				0,	/* parity off */
113 };
114 
115 struct	net {
116 	char	*n_name;	/* generic name */
117 	short	n_type;		/* see defines above */
118 	char	*n_profile;	/* initial profile */
119 	short	n_proflen;	/* length of n_profile */
120 } *netp, nets[] = {
121 	"x.25",		X25NET,		0,		0,
122 	"1978",		CCITT1978,	ccitt1978_prof,	sizeof(ccitt1978_prof),
123 	"ccitt1978",	CCITT1978,	ccitt1978_prof,	sizeof(ccitt1978_prof),
124 	"1980",		CCITT1980,	ccitt1980_prof,	sizeof(ccitt1980_prof),
125 	"ccitt1980",	CCITT1980,	ccitt1980_prof,	sizeof(ccitt1980_prof),
126 	"datapac",	CCITT1980,	datapac_prof,	sizeof(datapac_prof),
127 	0,		0,		0,		0
128 };
129 
130 main(argc, argv)
131 register char **argv;
132 {
133 	register int s, pid;
134 	register char *p;
135 
136 	/*
137 	 * If this host doesn't support X.25, give up.
138 	 */
139 	s = socket(AF_CCITT, SOCK_STREAM, 0);
140 	if (s < 0 && errno == EPROTONOSUPPORT)
141 		fatal(2, "X.25 is not supported on this machine");
142 	close(s);
143 	netp = lookup ("ccitt1978");
144 	sock.x25_family = AF_CCITT;
145 	sock.x25_len = sizeof(sock);
146 	sock.x25_opts.op_flags = X25_MQBIT;
147 	sock.x25_udata[0] = ITI_CALL;
148 	sock.x25_udlen = 4;
149 
150 	for (argv++; argc > 1; argc--, argv++)
151 		if (**argv == '-')
152 			for (p = *argv+1; *p; p++)
153 			switch (*p) {
154 			case 'b':
155 				send_banner++;
156 				break;
157 
158 			case 'c':
159 				if (argc > 1) {
160 					argc--; argv++;
161 					if ((netp = lookup (*argv)) == 0)
162 						fatal(1, "Unknown network type");
163 				}
164 				break;
165 
166 			case 'p':
167 				if (argc > 1) {
168 					argc--; argv++;
169 					strcpy (sock.x25_udata, *argv);
170 				}
171 				break;
172 
173 			case 'r':
174 				sock.x25_opts.op_flags |= X25_REVERSE_CHARGE;
175 				break;
176 
177 			case 'd':
178 				debug++;
179 				break;
180 
181 			case 't':
182 				if (argc > 1) {
183 					argc--; argv++;
184 					tracefn = *argv;
185 				}
186 				else fatal(1, "missing trace file");
187 				break;
188 
189 			default:
190 				fatal (1, "usage: x29d -b -c nettype -p protocol -r -t trace_file server");
191 			}
192 		else
193 			server = *argv;
194 	if (server == 0)
195 		fatal (1, "no server specified");
196 	if (debug == 0)
197 		daemon(0, 0);
198 
199 	while ((s = socket(AF_CCITT, SOCK_STREAM, 0)) < 0)
200 		sleep(60);
201 	while (bind(s, (caddr_t)&sock, sizeof (sock)) < 0)
202 		sleep(60);
203 	signal(SIGCHLD, reapchild);
204 	listen(s, 5);
205 
206 	for (;;) {
207 		struct sockaddr_x25 from;
208 		int fromlen = sizeof (from);
209 
210 		if ((net = accept(s, (caddr_t)&from, &fromlen)) < 0) {
211 			if (errno != EINTR)
212 				sleep (60);
213 			continue;
214 		}
215 		while ((pid = fork()) < 0)
216 			sleep(60);
217 		if (pid == 0) {
218 			signal(SIGCHLD, SIG_DFL);
219 			doit(&from);
220 		}
221 		close(net);
222 	}
223 	/*NOTREACHED*/
224 }
225 
226 struct net *
227 lookup (name)
228 char *name;
229 {
230 	register struct net *np;
231 
232 	for (np = nets; np->n_name; np++)
233 		if (strcmp (np->n_name, name) == 0)
234 			return (np);
235 	return (0);
236 }
237 
238 reapchild()
239 {
240 	union wait status;
241 
242 	while (wait3(&status, WNOHANG, 0) > 0)
243 		;
244 }
245 
246 char	*envinit[] = { "TERM=ccitt", 0 };
247 int	cleanup();
248 struct termios term;
249 
250 /*
251  * Get a pty, scan input lines.
252  */
253 doit(who)
254 struct sockaddr_x25 *who;
255 {
256 	register char *cp;
257 	int i, p, t;
258 
259 	packet_size = 1 << who->x25_opts.op_psize;
260 	i = forkpty(&pty, line, &term, 0);
261 	if (i > 0)
262 		x29d();
263 	if (i < 0)
264 		fatalperror("fork", errno);
265 	environ = envinit;
266 	call_server (who);
267 	/*NOTREACHED*/
268 }
269 
270 call_server (who)
271 struct sockaddr_x25 *who;
272 {
273 	register struct hostent *hp = 0;
274 	register char *p, **ap;
275 	char *args[MAXARGS];
276 	struct stat st;
277 	struct hostent *getx25hostbyaddr();
278 	int ccitt = 0;
279 
280 	p = server;
281 	while (*p && *p != ' ' && *p != '\t')	/* split program from args */
282 		p++;
283 	if (*p)
284 		*p++ = '\0';
285 	ap = args;
286 	while (*p) {
287 		while (*p == ' ' || *p == '\t')
288 			p++;
289 		if (ap < &args[MAXARGS-2])
290 			*ap++ = p;
291 		if (strcmp(p, "-ccitt") == 0)
292 			ccitt = 1;
293 		while (*p && *p != ' ' && *p != '\t')
294 			p++;
295 		if (*p)
296 			*p++ = '\0';
297 	}
298 	if (stat (server, &st) < 0)
299 		fatalperror (server, errno);
300 	/*
301 	 * For security: if running as root, switch to user
302 	 * and group id of server.  This prevents privately
303 	 * maintainted or bogus servers from getting super-
304 	 * user permissions.
305 	 */
306 	if (getuid() == 0) {
307 		setgid (st.st_gid);
308 		setuid (st.st_uid);
309 	}
310 	if (hp = getx25hostbyaddr (who->x25_addr))
311 		*ap++ = hp->h_name;
312 	else
313 		*ap++ = (char *)who->x25_addr;
314 	/*
315 	 * If the -ccitt flag was given, add another argument
316 	 * to tell login if charging is being reversed or not.
317 	 */
318 	if (ccitt)
319 		*ap++ = (who->x25_opts.op_flags & X25_REVERSE_CHARGE) ? "y" : "n";
320 	*ap = 0;
321 	execv (server, args);
322 	fatalperror (server, errno);
323 	/*NOTREACHED*/
324 }
325 
326 fatal(f, msg)
327 	int f;
328 	char *msg;
329 {
330 	register char *p;
331 	char buf[BUFSIZ], *index();
332 
333 	p = buf;
334 	if (f == net)
335 		*p++ = 0;
336 	strcpy(p, "x29d: ");
337 	strcat(p, msg);
338 	strcat(p, "\n");
339 	(void) write(f, p, (index(p, '\n')-p)+1);
340 	exit(1);
341 }
342 
343 fatalperror(msg, err)
344 char *msg;
345 {
346 	char buf[BUFSIZ];
347 	extern char *sys_errlist[];
348 
349 	strcpy(buf, msg);
350 	strcat(buf, ": ");
351 	strcat(buf, sys_errlist[err]);
352 	fatal(net, buf);
353 }
354 
355 /*
356  * Main loop.  Select from pty and network, and
357  * hand data to iti receiver.
358  */
359 x29d()
360 {
361 	register int pcc, fcc, cc;
362 	register char *fbp;
363 	int pgrp, x25_interrupt(), on = 1;
364 	char hostname[32];
365 
366 	ioctl(net, FIONBIO, (char *)&on);
367 	ioctl(pty, FIONBIO, (char *)&on);
368 	/*ioctl(pty, TIOCREMECHO, (char *)&on);	/* enable special pty mode */
369 	/* new equivalent is no processing in pty, no echo, but let
370 	   user set modes and have either remote end do line mode processing
371 	   or do it in daemon */
372 	ioctl(pty, TIOCEXT, (char *)&on);
373 	ioctl(pty, TIOCPKT, (char *)&on);
374 	ioctl(pty, TIOCGETA, (char *)&pt);
375 	signal(SIGPIPE, SIG_IGN);	/* why not cleanup?  --kwl */
376 	signal(SIGTSTP, SIG_IGN);
377 	signal(SIGCHLD, cleanup);
378 	signal(SIGHUP, cleanup);
379 
380 	signal(SIGTTOU, SIG_IGN);
381 	signal(SIGURG, x25_interrupt);	/* for out-of-band data */
382 
383 	if (netp->n_proflen)
384 		(void) write(net, netp->n_profile, netp->n_proflen);
385 
386 	/*
387 	 * Show banner that getty never gave.
388 	 */
389 	if (send_banner) {
390 		gethostname(hostname, sizeof (hostname));
391 #ifdef BSD4_3
392 		strcpy(pibuf+1, "\r\n\r\n4.3 BSD UNIX (");
393 #else
394 		strcpy(pibuf+1, "\r\n\r\n4.2 BSD UNIX (");
395 #endif
396 		strcat(pibuf+1, hostname);
397 		strcat(pibuf+1, ")\r\n\r\n");
398 		pcc = strlen(pibuf+1) + 1;
399 	} else
400 		pcc = 0;
401 
402 	fcc = 0;
403 	for (;;) {
404 		int ibits, obits;
405 
406 		ibits = obits = 0;
407 		/*
408 		 * Never look for input if there's still
409 		 * stuff in the corresponding output buffer
410 		 */
411 		if (fcc >= 0)			/* net connection alive? */
412 			if (fcc && pcc >= 0)	/* output pending? */
413 				obits |= (1 << pty);
414 			else
415 				if (pcc >= 0)	/* pty still alive? */
416 					ibits |= (1 << net);
417 		if (pcc >= 0)			/* pty connection alive? */
418 			if (pcc && fcc >= 0)	/* output pending? */
419 				obits |= (1 << net);
420 			else
421 				if (fcc >= 0)	/* net still alive? */
422 					ibits |= (1 << pty);
423 		if (ibits == 0 && obits == 0)
424 			break;
425 		(void) select(16, &ibits, &obits, (int *)0, 0);
426 		if (ibits == 0 && obits == 0) {
427 			sleep(5);
428 			continue;
429 		}
430 
431 		/*
432 		 * Something to read from the network...
433 		 */
434 		if (fcc == 0 && (ibits & (1 << net))) {
435 			fcc = read(net, fibuf, BUFSIZ);
436 			fbp = fibuf+1;
437 			if (fcc < 0 && errno == EWOULDBLOCK)
438 				fcc = 0;
439 			else if (fcc <= 0)
440 				fcc = -1;
441 			else {
442 				if (tracefn)
443 					x29d_trace("netread", fibuf, fcc);
444 				if (fibuf[0] & Q_BIT) {
445 					x29_qbit(fcc);
446 					fcc = 0;
447 				} else
448 					fcc--;
449 			}
450 		}
451 
452 		/*
453 		 * Something to read from the pty...
454 		 */
455 		if (ibits & (1 << pty)) {
456 			pcc = read(pty, pibuf, packet_size+1);
457 			if (pcc < 0 && errno == EWOULDBLOCK)
458 				pcc = 0;
459 			else if (pcc <= 0)
460 				pcc = -1;
461 			else if (pibuf[0] != 0) {	/* non-data packet */
462 				if (pibuf[0] & TIOCPKT_IOCTL) {
463 					if (--pcc > sizeof(pt))
464 						pcc = sizeof(pt);
465 					old_pt = pt;
466 					bcopy(pibuf + 1, (char *)&pt, pcc);
467 					pcc = set_x29_parameters();
468 				} else
469 					pcc = 0;
470 			} else				/* data packet */
471 				pibuf[0] = 0;
472 		}
473 
474 		if ((obits & (1<<net)) && pcc > 0)
475 			if ((cc = write(net, pibuf, pcc)) == pcc) {
476 				if (tracefn)
477 					x29d_trace("netwrite", pibuf, pcc);
478 				pcc = 0;
479 			} else {
480 				extern char *sys_errlist[];
481 
482 				if (tracefn)
483 					x29d_trace("netwrite",
484 						sys_errlist[errno],
485 						strlen(sys_errlist[errno]));
486 
487 			}
488 
489 		if ((obits & (1 << pty)) && fcc > 0) {
490 			cc = ptywrite(fbp, fcc);
491 			if (cc > 0) {
492 				fcc -= cc;
493 				fbp += cc;
494 			}
495 		}
496 	}
497 	cleanup();
498 }
499 
500 
501 /*
502  * Send interrupt to process on other side of pty.
503  * If it is in raw mode, just write NULL;
504  * otherwise, write intr char.
505  */
506 
507 x25_interrupt()
508 {
509 	struct termios tt;
510 	int zero = 0;
511 
512 	signal(SIGURG, x25_interrupt);
513 	tcgetattr(pty, &tt);
514 	if (tt.c_lflag & ISIG) {
515 		tcsetattr(pty, TCSAFLUSH, &tt);
516 		(void) write(pty, &tt.c_cc[VINTR], 1);
517 	} else
518 		(void) write(pty, "\0", 1);
519 }
520 
521 cleanup()
522 {
523 	char *p;
524 
525 	p = line + sizeof(_PATH_DEV) - 1;
526 	if (logout(p))
527 		logwtmp(p, "", "");
528 	(void)chmod(line, 0666);
529 	(void)chown(line, 0, 0);
530 	*p = 'p';
531 	(void)chmod(line, 0666);
532 	(void)chown(line, 0, 0);
533 	shutdown(net, 2);
534 	exit(1);
535 }
536 
537 /*
538  * Map unix tty modes and special characters
539  * into x29 parameters.
540  */
541 
542 set_x29_parameters()
543 {
544 	register char *p;
545 	int f;
546 	char *lim = p + sizeof (pt);
547 
548 	if (netp->n_type == X25NET)
549 		return (0);
550 	if ((old_pt.c_lflag & ICANON) != (pt.c_lflag & ICANON)) {
551 		f = pt.c_lflag & ICANON;
552 		ioctl(pty, TIOCEXT, &f);
553 		/* this precipitates more junk of the same
554 		 * sort that caused our call here, but we can't
555 		 * turn it off since something may be going on in our progeny.
556 		 *
557 		 * Instead, we'll check the next time around to see if nothing
558 		 * has changed, and skip informing the network.
559 		 */
560 	}
561 	if (bcmp((char *)&pt, (char *)&old_pt, sizeof (pt)) == 0)
562 		return;
563 	p = pibuf;
564 	*p++ = Q_BIT;
565 	*p++ = X29_SET_PARMS;
566 	/* *p++ = X29_ESCAPE_TO_CMD_CODE; *p++ = (f & (RAW|CBREAK)) == 0;*/
567 	*p++ = X29_ESCAPE_TO_CMD_CODE; *p++ = (pt.c_lflag & ICANON) != 0;
568 
569 	*p++ = X29_ECHO_CODE; *p++ = (pt.c_lflag & ECHO) != 0;
570 	*p++ = X29_FORWARDING_SIGNAL_CODE;
571 			*p++ = (pt.c_lflag & ISIG) ? 0 : 126;
572 
573 	/*
574 	 * The value of 10 (0.5 seconds) for the idle timer when
575 	 * in raw or cbreak mode is a compromise value.  For good
576 	 * interactive response this value should be as low as
577 	 * possible; for reasonable efficiency with file transfers
578 	 * this value should be at fairly high.  This number should
579 	 * be changed to suit local requirements.
580 	 */
581 
582 	/**p++ = X29_IDLE_TIMER_CODE;	*p++ = (f & (RAW|CBREAK)) ? 10 : 0;*/
583 	*p++ = X29_IDLE_TIMER_CODE; *p++ = (pt.c_lflag & ICANON)  ? 0 : 10;
584 
585 	/**p++ = X29_AUX_DEV_CONTROL_CODE;*p++ = (f & TANDEM) != 0;*/
586 	*p++ = X29_AUX_DEV_CONTROL_CODE;*p++ = (pt.c_iflag & IXOFF) != 0;
587 	*p++ = X29_XON_XOFF_CODE;	*p++ = (pt.c_iflag & IXON) != 0;
588 	if (netp->n_type == CCITT1980) {
589 		*p++ = X29_LF_AFTER_CR;
590 		/* *p++ = (f & (RAW|CBREAK) || (f & ECHO) == 0) ? 0 : 4; */
591 		*p++ = ((pt.c_lflag & (ICANON | ECHO)) != (ICANON | ECHO)) ?
592 			0 : 4;
593 
594 		*p++ = X29_EDITING; *p++ = (pt.c_lflag & ICANON) != 0;
595 #define ctlchar(x) \
596   (0 == (pt.c_lflag & ICANON) || pt.c_cc[x] == _POSIX_VDISABLE) ? 0 : pt.c_cc[x]
597 		*p++ = X29_CHARACTER_DELETE; *p++ = ctlchar(VERASE);
598 		*p++ = X29_LINE_DELETE; *p++ = ctlchar(VKILL);
599 		*p++ = X29_LINE_DISPLAY; *p++ = ctlchar(VREPRINT);
600 	}
601 #undef ctlchar
602 	return (p - pibuf);
603 }
604 
605 /* Have to be careful writing to pty.  The pad will forward control
606  * characters without necessarily sending an interrupt so if ISIG and
607  * ICANNON are set, must inspect line for quit or interrupt or suspend.
608  */
609 ptywrite(buf, n)
610 char *buf;
611 int n;
612 {
613 	register char *cp, *lim;
614 	char *last;
615 #define is_ctl(x) (pt.c_cc[x] == *(cc_t *)cp)
616 
617 	if ((pt.c_lflag & EXTPROC) && (pt.c_lflag & ISIG)) {
618 		for (cp = buf, lim = buf + n; cp < lim; cp ++) {
619 			if (is_ctl(VLNEXT))
620 				{ cp++; continue; }
621 			if (is_ctl(VSUSP) || is_ctl(VDSUSP) ||
622 			    is_ctl(VINTR) || is_ctl(VQUIT))  {
623 				int onoff = 0;
624 				tcflag_t old_echo = pt.c_lflag & ECHO;
625 
626 				ioctl(pty, TIOCPKT, (char *)&onoff);
627 				ioctl(pty, FIONBIO, (char *)&onoff);
628 				ioctl(pty, TIOCEXT, (char *)&onoff);
629 				if (old_echo) {
630 					pt.c_lflag &= ~ECHO;
631 					ioctl(pty, TIOCSETA, (char *)&pt);
632 				}
633 				n = write(pty, buf, n);
634 				onoff = 1;
635 				if (old_echo) {
636 					pt.c_lflag |= ECHO;
637 					ioctl(pty, TIOCSETA, (char *)&pt);
638 				}
639 				ioctl(pty, TIOCEXT, (char *)&onoff);
640 				ioctl(pty, FIONBIO, (char *)&onoff);
641 				ioctl(pty, TIOCPKT, (char *)&onoff);
642 				return (n);
643 			}
644 		}
645 	}
646 	return write(pty, buf, n);
647 }
648 
649 /*
650  * Process Q BIT (control) packets from the net.
651  * The only message that we are interested in are
652  * those indicating output is being discarded.
653  */
654 
655 x29_qbit(n)
656 {
657 	register char *p;
658 
659 	switch (fibuf[1]) {
660 	case X29_SET_PARMS:
661 	case X29_SET_AND_READ_PARMS:
662 	case X29_PARAMETER_INDICATION:
663 	case X29_INDICATION_OF_BREAK:
664 		for (p = &fibuf[2]; p < fibuf+n; p++) {
665 			if (*p == X29_TRANSMISSION_SPEED_CODE) {
666 				static char speeds[] = {
667 					B110, B0, B300, B1200, B600,
668 					B0, B0, B0, B0, B0, B0, B0,
669 					B2400, B4800, B9600, EXTA };
670 
671 				if (*++p >= 0 && *p < sizeof(speeds)) {
672 					cfsetspeed(&pt, speeds[*p]);
673 					tcsetattr(pty, TCSANOW, &pt);
674 				}
675 			} else if (*p == X29_DISCARD_OUTPUT_CODE && *++p != 0) {
676 				char message[4];
677 
678 				/*
679 				 * Always re-enable normal output
680 				 */
681 				message[0] = Q_BIT;
682 				message[1] = X29_SET_PARMS;
683 				message[2] = X29_DISCARD_OUTPUT_CODE;
684 				message[3] = 0;
685 				(void) write(net, message, sizeof(message));
686 				if (tracefn)
687 					x29d_trace("netwrite", message, 4);
688 			}
689 		}
690 		return;
691 
692 	default: {
693 			register char *p2;
694 			char buf[BUFSIZ*4];
695 			static int fd;
696 
697 			/*
698 			 * Bad news - we received an x29 error message or
699 			 * some other unknown packet.  Dump the contents
700 			 * of the packet on the console.
701 			 */
702 			p = buf;
703 			for (p2 = "x29d: unknown q-bit packet: "; *p++ = *p2++; );
704 			for (p2 = fibuf+1; p2 < fibuf+n; p2++)
705 				if (*p2 >= ' ' && *p2 < 0177)
706 					*p++ = *p2;
707 				else {
708 					*p++ = '\\';
709 					*p++ = ((*p2 & 0300) >> 6) + '0';
710 					*p++ = ((*p2 & 070) >> 3) + '0';
711 					*p++ = (*p2 & 07) + '0';
712 				}
713 			*p++ = '\n';
714 			if (fd <= 0)
715 				fd = open(console, 1);
716 			(void) write(fd, buf, p-buf);
717 		}
718 	}
719 }
720 
721 x29d_trace(s, bp, n)
722 char *s, *bp;
723 {
724 	static int fd;
725 	char buf[BUFSIZ*4];
726 	register char *p1, *p2;
727 
728 	for (p1 = buf; *s; *p1++ = *s++);
729 	*p1++ = ':';
730 	*p1++ = ' ';
731 	for (p2=bp; p2 < bp+n; p2++)
732 		if (*p2 >= ' ' && *p2 < 0177)
733 			*p1++ = *p2;
734 		else {
735 			*p1++ = '\\';
736 			*p1++ = ((*p2 & 0300) >> 6) + '0';
737 			*p1++ = ((*p2 & 070) >> 3) + '0';
738 			*p1++ = (*p2 & 07) + '0';
739 		}
740 	*p1++ = '\n';
741 	if (fd <= 0)
742 		fd = creat(tracefn, 0666);
743 	(void) write(fd, buf, p1-buf);
744 }
745