xref: /netbsd-src/usr.bin/telnet/utilities.c (revision d710132b4b8ce7f7cccaaf660cb16aa16b4077a0)
1 /*	$NetBSD: utilities.c,v 1.15 2003/06/18 20:51:01 christos Exp $	*/
2 
3 /*
4  * Copyright (c) 1988, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *	This product includes software developed by the University of
18  *	California, Berkeley and its contributors.
19  * 4. Neither the name of the University nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 
36 #include <sys/cdefs.h>
37 #ifndef lint
38 #if 0
39 static char sccsid[] = "@(#)utilities.c	8.3 (Berkeley) 5/30/95";
40 #else
41 __RCSID("$NetBSD: utilities.c,v 1.15 2003/06/18 20:51:01 christos Exp $");
42 #endif
43 #endif /* not lint */
44 
45 #define	TELOPTS
46 #define	TELCMDS
47 #define	SLC_NAMES
48 #include <arpa/telnet.h>
49 #include <sys/types.h>
50 #include <sys/time.h>
51 #ifndef	NOT43
52 #include <sys/socket.h>
53 #endif
54 #include <unistd.h>
55 #include <poll.h>
56 
57 #include <ctype.h>
58 
59 #include "general.h"
60 
61 #include "fdset.h"
62 
63 #include "ring.h"
64 
65 #include "defines.h"
66 
67 #include "externs.h"
68 
69 #if defined(TN3270)
70 #include "../sys_curses/telextrn.h"
71 #endif
72 
73 #ifdef AUTHENTICATION
74 #include <libtelnet/auth.h>
75 #endif
76 #ifdef ENCRYPTION
77 #include <libtelnet/encrypt.h>
78 #endif
79 
80 FILE	*NetTrace = 0;		/* Not in bss, since needs to stay */
81 int	prettydump;
82 
83 /*
84  * upcase()
85  *
86  *	Upcase (in place) the argument.
87  */
88 
89 void
90 upcase(char *argument)
91 {
92     int c;
93 
94     while ((c = *argument) != 0) {
95 	if (islower(c)) {
96 	    *argument = toupper(c);
97 	}
98 	argument++;
99     }
100 }
101 
102 /*
103  * SetSockOpt()
104  *
105  * Compensate for differences in 4.2 and 4.3 systems.
106  */
107 
108 int
109 SetSockOpt(int fd, int level, int option, int yesno)
110 {
111 #ifndef	NOT43
112     return setsockopt(fd, level, option,
113 				(char *)&yesno, sizeof yesno);
114 #else	/* NOT43 */
115     if (yesno == 0) {		/* Can't do that in 4.2! */
116 	fprintf(stderr, "Error: attempt to turn off an option 0x%x.\n",
117 				option);
118 	return -1;
119     }
120     return setsockopt(fd, level, option, 0, 0);
121 #endif	/* NOT43 */
122 }
123 
124 /*
125  * The following are routines used to print out debugging information.
126  */
127 
128 unsigned char NetTraceFile[256] = "(standard output)";
129 
130 void
131 SetNetTrace(char *file)
132 {
133     if (NetTrace && NetTrace != stdout)
134 	fclose(NetTrace);
135     if (file  && (strcmp(file, "-") != 0)) {
136 	NetTrace = fopen(file, "w");
137 	if (NetTrace) {
138 	    strcpy((char *)NetTraceFile, file);
139 	    return;
140 	}
141 	fprintf(stderr, "Cannot open %s.\n", file);
142     }
143     NetTrace = stdout;
144     strcpy((char *)NetTraceFile, "(standard output)");
145 }
146 
147 void
148 Dump(int direction, unsigned char *buffer, int length)
149 {
150 #   define BYTES_PER_LINE	32
151 #   define min(x,y)	((x<y)? x:y)
152     unsigned char *pThis;
153     int offset;
154 
155     offset = 0;
156 
157     while (length) {
158 	/* print one line */
159 	fprintf(NetTrace, "%c 0x%x\t", direction, offset);
160 	pThis = buffer;
161 	if (prettydump) {
162 	    buffer = buffer + min(length, BYTES_PER_LINE/2);
163 	    while (pThis < buffer) {
164 		fprintf(NetTrace, "%c%.2x",
165 		    (((*pThis)&0xff) == 0xff) ? '*' : ' ',
166 		    (*pThis)&0xff);
167 		pThis++;
168 	    }
169 	    length -= BYTES_PER_LINE/2;
170 	    offset += BYTES_PER_LINE/2;
171 	} else {
172 	    buffer = buffer + min(length, BYTES_PER_LINE);
173 	    while (pThis < buffer) {
174 		fprintf(NetTrace, "%.2x", (*pThis)&0xff);
175 		pThis++;
176 	    }
177 	    length -= BYTES_PER_LINE;
178 	    offset += BYTES_PER_LINE;
179 	}
180 	if (NetTrace == stdout) {
181 	    fprintf(NetTrace, "\r\n");
182 	} else {
183 	    fprintf(NetTrace, "\n");
184 	}
185 	if (length < 0) {
186 	    fflush(NetTrace);
187 	    return;
188 	}
189 	/* find next unique line */
190     }
191     fflush(NetTrace);
192 }
193 
194 
195 void
196 printoption(char *direction, int cmd, int option)
197 {
198 	if (!showoptions)
199 		return;
200 	if (cmd == IAC) {
201 		if (TELCMD_OK(option))
202 		    fprintf(NetTrace, "%s IAC %s", direction, TELCMD(option));
203 		else
204 		    fprintf(NetTrace, "%s IAC %d", direction, option);
205 	} else {
206 		char *fmt;
207 		fmt = (cmd == WILL) ? "WILL" : (cmd == WONT) ? "WONT" :
208 			(cmd == DO) ? "DO" : (cmd == DONT) ? "DONT" : 0;
209 		if (fmt) {
210 		    fprintf(NetTrace, "%s %s ", direction, fmt);
211 		    if (TELOPT_OK(option))
212 			fprintf(NetTrace, "%s", TELOPT(option));
213 		    else if (option == TELOPT_EXOPL)
214 			fprintf(NetTrace, "EXOPL");
215 		    else
216 			fprintf(NetTrace, "%d", option);
217 		} else
218 		    fprintf(NetTrace, "%s %d %d", direction, cmd, option);
219 	}
220 	if (NetTrace == stdout) {
221 	    fprintf(NetTrace, "\r\n");
222 	    fflush(NetTrace);
223 	} else {
224 	    fprintf(NetTrace, "\n");
225 	}
226 	return;
227 }
228 
229 void
230 optionstatus(void)
231 {
232     int i;
233     extern char will_wont_resp[], do_dont_resp[];
234 
235     for (i = 0; i < 256; i++) {
236 	if (do_dont_resp[i]) {
237 	    if (TELOPT_OK(i))
238 		printf("resp DO_DONT %s: %d\n", TELOPT(i), do_dont_resp[i]);
239 	    else if (TELCMD_OK(i))
240 		printf("resp DO_DONT %s: %d\n", TELCMD(i), do_dont_resp[i]);
241 	    else
242 		printf("resp DO_DONT %d: %d\n", i,
243 				do_dont_resp[i]);
244 	    if (my_want_state_is_do(i)) {
245 		if (TELOPT_OK(i))
246 		    printf("want DO   %s\n", TELOPT(i));
247 		else if (TELCMD_OK(i))
248 		    printf("want DO   %s\n", TELCMD(i));
249 		else
250 		    printf("want DO   %d\n", i);
251 	    } else {
252 		if (TELOPT_OK(i))
253 		    printf("want DONT %s\n", TELOPT(i));
254 		else if (TELCMD_OK(i))
255 		    printf("want DONT %s\n", TELCMD(i));
256 		else
257 		    printf("want DONT %d\n", i);
258 	    }
259 	} else {
260 	    if (my_state_is_do(i)) {
261 		if (TELOPT_OK(i))
262 		    printf("     DO   %s\n", TELOPT(i));
263 		else if (TELCMD_OK(i))
264 		    printf("     DO   %s\n", TELCMD(i));
265 		else
266 		    printf("     DO   %d\n", i);
267 	    }
268 	}
269 	if (will_wont_resp[i]) {
270 	    if (TELOPT_OK(i))
271 		printf("resp WILL_WONT %s: %d\n", TELOPT(i), will_wont_resp[i]);
272 	    else if (TELCMD_OK(i))
273 		printf("resp WILL_WONT %s: %d\n", TELCMD(i), will_wont_resp[i]);
274 	    else
275 		printf("resp WILL_WONT %d: %d\n",
276 				i, will_wont_resp[i]);
277 	    if (my_want_state_is_will(i)) {
278 		if (TELOPT_OK(i))
279 		    printf("want WILL %s\n", TELOPT(i));
280 		else if (TELCMD_OK(i))
281 		    printf("want WILL %s\n", TELCMD(i));
282 		else
283 		    printf("want WILL %d\n", i);
284 	    } else {
285 		if (TELOPT_OK(i))
286 		    printf("want WONT %s\n", TELOPT(i));
287 		else if (TELCMD_OK(i))
288 		    printf("want WONT %s\n", TELCMD(i));
289 		else
290 		    printf("want WONT %d\n", i);
291 	    }
292 	} else {
293 	    if (my_state_is_will(i)) {
294 		if (TELOPT_OK(i))
295 		    printf("     WILL %s\n", TELOPT(i));
296 		else if (TELCMD_OK(i))
297 		    printf("     WILL %s\n", TELCMD(i));
298 		else
299 		    printf("     WILL %d\n", i);
300 	    }
301 	}
302     }
303 
304 }
305 
306 void
307 printsub(
308     int direction,	/* '<' or '>' */
309     unsigned char *pointer,	/* where suboption data sits */
310     int		  length)	/* length of suboption data */
311 {
312     int i;
313 #ifdef	ENCRYPTION
314     char buf[512];
315 #endif	/* ENCRYPTION */
316     extern int want_status_response;
317 
318     if (showoptions || direction == 0 ||
319 	(want_status_response && (pointer[0] == TELOPT_STATUS))) {
320 	if (direction) {
321 	    fprintf(NetTrace, "%s IAC SB ",
322 				(direction == '<')? "RCVD":"SENT");
323 	    if (length >= 3) {
324 		int j;
325 
326 		i = pointer[length-2];
327 		j = pointer[length-1];
328 
329 		if (i != IAC || j != SE) {
330 		    fprintf(NetTrace, "(terminated by ");
331 		    if (TELOPT_OK(i))
332 			fprintf(NetTrace, "%s ", TELOPT(i));
333 		    else if (TELCMD_OK(i))
334 			fprintf(NetTrace, "%s ", TELCMD(i));
335 		    else
336 			fprintf(NetTrace, "%d ", i);
337 		    if (TELOPT_OK(j))
338 			fprintf(NetTrace, "%s", TELOPT(j));
339 		    else if (TELCMD_OK(j))
340 			fprintf(NetTrace, "%s", TELCMD(j));
341 		    else
342 			fprintf(NetTrace, "%d", j);
343 		    fprintf(NetTrace, ", not IAC SE!) ");
344 		}
345 	    }
346 	    length -= 2;
347 	}
348 	if (length < 1) {
349 	    fprintf(NetTrace, "(Empty suboption??\?)");
350 	    if (NetTrace == stdout)
351 		fflush(NetTrace);
352 	    return;
353 	}
354 	switch (pointer[0]) {
355 	case TELOPT_TTYPE:
356 	    fprintf(NetTrace, "TERMINAL-TYPE ");
357 	    switch (pointer[1]) {
358 	    case TELQUAL_IS:
359 		fprintf(NetTrace, "IS \"%.*s\"", length-2, (char *)pointer+2);
360 		break;
361 	    case TELQUAL_SEND:
362 		fprintf(NetTrace, "SEND");
363 		break;
364 	    default:
365 		fprintf(NetTrace,
366 				"- unknown qualifier %d (0x%x).",
367 				pointer[1], pointer[1]);
368 	    }
369 	    break;
370 	case TELOPT_TSPEED:
371 	    fprintf(NetTrace, "TERMINAL-SPEED");
372 	    if (length < 2) {
373 		fprintf(NetTrace, " (empty suboption??\?)");
374 		break;
375 	    }
376 	    switch (pointer[1]) {
377 	    case TELQUAL_IS:
378 		fprintf(NetTrace, " IS ");
379 		fprintf(NetTrace, "%.*s", length-2, (char *)pointer+2);
380 		break;
381 	    default:
382 		if (pointer[1] == 1)
383 		    fprintf(NetTrace, " SEND");
384 		else
385 		    fprintf(NetTrace, " %d (unknown)", pointer[1]);
386 		for (i = 2; i < length; i++)
387 		    fprintf(NetTrace, " ?%d?", pointer[i]);
388 		break;
389 	    }
390 	    break;
391 
392 	case TELOPT_LFLOW:
393 	    fprintf(NetTrace, "TOGGLE-FLOW-CONTROL");
394 	    if (length < 2) {
395 		fprintf(NetTrace, " (empty suboption??\?)");
396 		break;
397 	    }
398 	    switch (pointer[1]) {
399 	    case LFLOW_OFF:
400 		fprintf(NetTrace, " OFF"); break;
401 	    case LFLOW_ON:
402 		fprintf(NetTrace, " ON"); break;
403 	    case LFLOW_RESTART_ANY:
404 		fprintf(NetTrace, " RESTART-ANY"); break;
405 	    case LFLOW_RESTART_XON:
406 		fprintf(NetTrace, " RESTART-XON"); break;
407 	    default:
408 		fprintf(NetTrace, " %d (unknown)", pointer[1]);
409 	    }
410 	    for (i = 2; i < length; i++)
411 		fprintf(NetTrace, " ?%d?", pointer[i]);
412 	    break;
413 
414 	case TELOPT_NAWS:
415 	    fprintf(NetTrace, "NAWS");
416 	    if (length < 2) {
417 		fprintf(NetTrace, " (empty suboption??\?)");
418 		break;
419 	    }
420 	    if (length == 2) {
421 		fprintf(NetTrace, " ?%d?", pointer[1]);
422 		break;
423 	    }
424 	    fprintf(NetTrace, " %d %d (%d)",
425 		pointer[1], pointer[2],
426 		(int)((((unsigned int)pointer[1])<<8)|((unsigned int)pointer[2])));
427 	    if (length == 4) {
428 		fprintf(NetTrace, " ?%d?", pointer[3]);
429 		break;
430 	    }
431 	    fprintf(NetTrace, " %d %d (%d)",
432 		pointer[3], pointer[4],
433 		(int)((((unsigned int)pointer[3])<<8)|((unsigned int)pointer[4])));
434 	    for (i = 5; i < length; i++)
435 		fprintf(NetTrace, " ?%d?", pointer[i]);
436 	    break;
437 
438 #if	defined(AUTHENTICATION)
439 	case TELOPT_AUTHENTICATION:
440 	    fprintf(NetTrace, "AUTHENTICATION");
441 	    if (length < 2) {
442 		fprintf(NetTrace, " (empty suboption??\?)");
443 		break;
444 	    }
445 	    switch (pointer[1]) {
446 	    case TELQUAL_REPLY:
447 	    case TELQUAL_IS:
448 		fprintf(NetTrace, " %s ", (pointer[1] == TELQUAL_IS) ?
449 							"IS" : "REPLY");
450 		if (AUTHTYPE_NAME_OK(pointer[2]))
451 		    fprintf(NetTrace, "%s ", AUTHTYPE_NAME(pointer[2]));
452 		else
453 		    fprintf(NetTrace, "%d ", pointer[2]);
454 		if (length < 3) {
455 		    fprintf(NetTrace, "(partial suboption??\?)");
456 		    break;
457 		}
458 		fprintf(NetTrace, "%s|%s",
459 			((pointer[3] & AUTH_WHO_MASK) == AUTH_WHO_CLIENT) ?
460 			"CLIENT" : "SERVER",
461 			((pointer[3] & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) ?
462 			"MUTUAL" : "ONE-WAY");
463 
464 		auth_printsub(&pointer[1], length - 1, buf, sizeof(buf));
465 		fprintf(NetTrace, "%s", buf);
466 		break;
467 
468 	    case TELQUAL_SEND:
469 		i = 2;
470 		fprintf(NetTrace, " SEND ");
471 		while (i < length) {
472 		    if (AUTHTYPE_NAME_OK(pointer[i]))
473 			fprintf(NetTrace, "%s ", AUTHTYPE_NAME(pointer[i]));
474 		    else
475 			fprintf(NetTrace, "%d ", pointer[i]);
476 		    if (++i >= length) {
477 			fprintf(NetTrace, "(partial suboption??\?)");
478 			break;
479 		    }
480 		    fprintf(NetTrace, "%s|%s ",
481 			((pointer[i] & AUTH_WHO_MASK) == AUTH_WHO_CLIENT) ?
482 							"CLIENT" : "SERVER",
483 			((pointer[i] & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) ?
484 							"MUTUAL" : "ONE-WAY");
485 		    ++i;
486 		}
487 		break;
488 
489 	    case TELQUAL_NAME:
490 		i = 2;
491 		fprintf(NetTrace, " NAME \"");
492 		while (i < length)
493 		    putc(pointer[i++], NetTrace);
494 		putc('"', NetTrace);
495 		break;
496 
497 	    default:
498 		    for (i = 2; i < length; i++)
499 			fprintf(NetTrace, " ?%d?", pointer[i]);
500 		    break;
501 	    }
502 	    break;
503 #endif
504 
505 #ifdef	ENCRYPTION
506 	case TELOPT_ENCRYPT:
507 		fprintf(NetTrace, "ENCRYPT");
508 		if (length < 2) {
509 			fprintf(NetTrace, " (empty suboption??\?)");
510 			break;
511 		}
512 		switch (pointer[1]) {
513 		case ENCRYPT_START:
514 			fprintf(NetTrace, " START");
515 			break;
516 
517 		case ENCRYPT_END:
518 			fprintf(NetTrace, " END");
519 			break;
520 
521 		case ENCRYPT_REQSTART:
522 			fprintf(NetTrace, " REQUEST-START");
523 			break;
524 
525 		case ENCRYPT_REQEND:
526 			fprintf(NetTrace, " REQUEST-END");
527 			break;
528 
529 		case ENCRYPT_IS:
530 		case ENCRYPT_REPLY:
531 			fprintf(NetTrace, " %s ", (pointer[1] == ENCRYPT_IS) ?
532 			    "IS" : "REPLY");
533 			if (length < 3) {
534 				fprintf(NetTrace, " (partial suboption??\?)");
535 				break;
536 			}
537 			if (ENCTYPE_NAME_OK(pointer[2]))
538 				fprintf(NetTrace, "%s ",
539 				    ENCTYPE_NAME(pointer[2]));
540 			else
541 				fprintf(NetTrace, " %d (unknown)", pointer[2]);
542 
543 			encrypt_printsub(&pointer[1], length - 1, buf,
544 			    sizeof(buf));
545 			fprintf(NetTrace, "%s", buf);
546 			break;
547 
548 		case ENCRYPT_SUPPORT:
549 			i = 2;
550 			fprintf(NetTrace, " SUPPORT ");
551 			while (i < length) {
552 				if (ENCTYPE_NAME_OK(pointer[i]))
553 					fprintf(NetTrace, "%s ",
554 					    ENCTYPE_NAME(pointer[i]));
555 				else
556 					fprintf(NetTrace, "%d ", pointer[i]);
557 				i++;
558 			}
559 			break;
560 
561 		case ENCRYPT_ENC_KEYID:
562 			fprintf(NetTrace, " ENC_KEYID ");
563 			goto encommon;
564 
565 		case ENCRYPT_DEC_KEYID:
566 			fprintf(NetTrace, " DEC_KEYID ");
567 			goto encommon;
568 
569 		default:
570 			fprintf(NetTrace, " %d (unknown)", pointer[1]);
571 		encommon:
572 			for (i = 2; i < length; i++)
573 				fprintf(NetTrace, " %d", pointer[i]);
574 			break;
575 		}
576 		break;
577 #endif	/* ENCRYPTION */
578 
579 	case TELOPT_LINEMODE:
580 	    fprintf(NetTrace, "LINEMODE ");
581 	    if (length < 2) {
582 		fprintf(NetTrace, " (empty suboption??\?)");
583 		break;
584 	    }
585 	    switch (pointer[1]) {
586 	    case WILL:
587 		fprintf(NetTrace, "WILL ");
588 		goto common;
589 	    case WONT:
590 		fprintf(NetTrace, "WONT ");
591 		goto common;
592 	    case DO:
593 		fprintf(NetTrace, "DO ");
594 		goto common;
595 	    case DONT:
596 		fprintf(NetTrace, "DONT ");
597 	    common:
598 		if (length < 3) {
599 		    fprintf(NetTrace, "(no option??\?)");
600 		    break;
601 		}
602 		switch (pointer[2]) {
603 		case LM_FORWARDMASK:
604 		    fprintf(NetTrace, "Forward Mask");
605 		    for (i = 3; i < length; i++)
606 			fprintf(NetTrace, " %x", pointer[i]);
607 		    break;
608 		default:
609 		    fprintf(NetTrace, "%d (unknown)", pointer[2]);
610 		    for (i = 3; i < length; i++)
611 			fprintf(NetTrace, " %d", pointer[i]);
612 		    break;
613 		}
614 		break;
615 
616 	    case LM_SLC:
617 		fprintf(NetTrace, "SLC");
618 		for (i = 2; i < length - 2; i += 3) {
619 		    if (SLC_NAME_OK(pointer[i+SLC_FUNC]))
620 			fprintf(NetTrace, " %s", SLC_NAME(pointer[i+SLC_FUNC]));
621 		    else
622 			fprintf(NetTrace, " %d", pointer[i+SLC_FUNC]);
623 		    switch (pointer[i+SLC_FLAGS]&SLC_LEVELBITS) {
624 		    case SLC_NOSUPPORT:
625 			fprintf(NetTrace, " NOSUPPORT"); break;
626 		    case SLC_CANTCHANGE:
627 			fprintf(NetTrace, " CANTCHANGE"); break;
628 		    case SLC_VARIABLE:
629 			fprintf(NetTrace, " VARIABLE"); break;
630 		    case SLC_DEFAULT:
631 			fprintf(NetTrace, " DEFAULT"); break;
632 		    }
633 		    fprintf(NetTrace, "%s%s%s",
634 			pointer[i+SLC_FLAGS]&SLC_ACK ? "|ACK" : "",
635 			pointer[i+SLC_FLAGS]&SLC_FLUSHIN ? "|FLUSHIN" : "",
636 			pointer[i+SLC_FLAGS]&SLC_FLUSHOUT ? "|FLUSHOUT" : "");
637 		    if (pointer[i+SLC_FLAGS]& ~(SLC_ACK|SLC_FLUSHIN|
638 						SLC_FLUSHOUT| SLC_LEVELBITS))
639 			fprintf(NetTrace, "(0x%x)", pointer[i+SLC_FLAGS]);
640 		    fprintf(NetTrace, " %d;", pointer[i+SLC_VALUE]);
641 		    if ((pointer[i+SLC_VALUE] == IAC) &&
642 			(pointer[i+SLC_VALUE+1] == IAC))
643 				i++;
644 		}
645 		for (; i < length; i++)
646 		    fprintf(NetTrace, " ?%d?", pointer[i]);
647 		break;
648 
649 	    case LM_MODE:
650 		fprintf(NetTrace, "MODE ");
651 		if (length < 3) {
652 		    fprintf(NetTrace, "(no mode??\?)");
653 		    break;
654 		}
655 		{
656 		    char tbuf[64];
657 		    sprintf(tbuf, "%s%s%s%s%s",
658 			pointer[2]&MODE_EDIT ? "|EDIT" : "",
659 			pointer[2]&MODE_TRAPSIG ? "|TRAPSIG" : "",
660 			pointer[2]&MODE_SOFT_TAB ? "|SOFT_TAB" : "",
661 			pointer[2]&MODE_LIT_ECHO ? "|LIT_ECHO" : "",
662 			pointer[2]&MODE_ACK ? "|ACK" : "");
663 		    fprintf(NetTrace, "%s", tbuf[1] ? &tbuf[1] : "0");
664 		}
665 		if (pointer[2]&~(MODE_MASK))
666 		    fprintf(NetTrace, " (0x%x)", pointer[2]);
667 		for (i = 3; i < length; i++)
668 		    fprintf(NetTrace, " ?0x%x?", pointer[i]);
669 		break;
670 	    default:
671 		fprintf(NetTrace, "%d (unknown)", pointer[1]);
672 		for (i = 2; i < length; i++)
673 		    fprintf(NetTrace, " %d", pointer[i]);
674 	    }
675 	    break;
676 
677 	case TELOPT_STATUS: {
678 	    char *cp;
679 	    int j, k;
680 
681 	    fprintf(NetTrace, "STATUS");
682 
683 	    switch (pointer[1]) {
684 	    default:
685 		if (pointer[1] == TELQUAL_SEND)
686 		    fprintf(NetTrace, " SEND");
687 		else
688 		    fprintf(NetTrace, " %d (unknown)", pointer[1]);
689 		for (i = 2; i < length; i++)
690 		    fprintf(NetTrace, " ?%d?", pointer[i]);
691 		break;
692 	    case TELQUAL_IS:
693 		if (--want_status_response < 0)
694 		    want_status_response = 0;
695 		if (NetTrace == stdout)
696 		    fprintf(NetTrace, " IS\r\n");
697 		else
698 		    fprintf(NetTrace, " IS\n");
699 
700 		for (i = 2; i < length; i++) {
701 		    switch(pointer[i]) {
702 		    case DO:	cp = "DO"; goto common2;
703 		    case DONT:	cp = "DONT"; goto common2;
704 		    case WILL:	cp = "WILL"; goto common2;
705 		    case WONT:	cp = "WONT"; goto common2;
706 		    common2:
707 			i++;
708 			if (TELOPT_OK((int)pointer[i]))
709 			    fprintf(NetTrace, " %s %s", cp, TELOPT(pointer[i]));
710 			else
711 			    fprintf(NetTrace, " %s %d", cp, pointer[i]);
712 
713 			if (NetTrace == stdout)
714 			    fprintf(NetTrace, "\r\n");
715 			else
716 			    fprintf(NetTrace, "\n");
717 			break;
718 
719 		    case SB:
720 			fprintf(NetTrace, " SB ");
721 			i++;
722 			j = k = i;
723 			while (j < length) {
724 			    if (pointer[j] == SE) {
725 				if (j+1 == length)
726 				    break;
727 				if (pointer[j+1] == SE)
728 				    j++;
729 				else
730 				    break;
731 			    }
732 			    pointer[k++] = pointer[j++];
733 			}
734 			printsub(0, &pointer[i], k - i);
735 			if (i < length) {
736 			    fprintf(NetTrace, " SE");
737 			    i = j;
738 			} else
739 			    i = j - 1;
740 
741 			if (NetTrace == stdout)
742 			    fprintf(NetTrace, "\r\n");
743 			else
744 			    fprintf(NetTrace, "\n");
745 
746 			break;
747 
748 		    default:
749 			fprintf(NetTrace, " %d", pointer[i]);
750 			break;
751 		    }
752 		}
753 		break;
754 	    }
755 	    break;
756 	  }
757 
758 	case TELOPT_XDISPLOC:
759 	    fprintf(NetTrace, "X-DISPLAY-LOCATION ");
760 	    switch (pointer[1]) {
761 	    case TELQUAL_IS:
762 		fprintf(NetTrace, "IS \"%.*s\"", length-2, (char *)pointer+2);
763 		break;
764 	    case TELQUAL_SEND:
765 		fprintf(NetTrace, "SEND");
766 		break;
767 	    default:
768 		fprintf(NetTrace, "- unknown qualifier %d (0x%x).",
769 				pointer[1], pointer[1]);
770 	    }
771 	    break;
772 
773 	case TELOPT_NEW_ENVIRON:
774 	    fprintf(NetTrace, "NEW-ENVIRON ");
775 #ifdef	OLD_ENVIRON
776 	    goto env_common1;
777 	case TELOPT_OLD_ENVIRON:
778 	    fprintf(NetTrace, "OLD-ENVIRON");
779 	env_common1:
780 #endif
781 	    switch (pointer[1]) {
782 	    case TELQUAL_IS:
783 		fprintf(NetTrace, "IS ");
784 		goto env_common;
785 	    case TELQUAL_SEND:
786 		fprintf(NetTrace, "SEND ");
787 		goto env_common;
788 	    case TELQUAL_INFO:
789 		fprintf(NetTrace, "INFO ");
790 	    env_common:
791 		{
792 		    int noquote = 2;
793 #if defined(ENV_HACK) && defined(OLD_ENVIRON)
794 		    extern int old_env_var, old_env_value;
795 #endif
796 		    for (i = 2; i < length; i++ ) {
797 			switch (pointer[i]) {
798 			case NEW_ENV_VALUE:
799 #ifdef OLD_ENVIRON
800 		     /*	case NEW_ENV_OVAR: */
801 			    if (pointer[0] == TELOPT_OLD_ENVIRON) {
802 # ifdef	ENV_HACK
803 				if (old_env_var == OLD_ENV_VALUE)
804 				    fprintf(NetTrace, "\" (VALUE) " + noquote);
805 				else
806 # endif
807 				    fprintf(NetTrace, "\" VAR " + noquote);
808 			    } else
809 #endif /* OLD_ENVIRON */
810 				fprintf(NetTrace, "\" VALUE " + noquote);
811 			    noquote = 2;
812 			    break;
813 
814 			case NEW_ENV_VAR:
815 #ifdef OLD_ENVIRON
816 		     /* case OLD_ENV_VALUE: */
817 			    if (pointer[0] == TELOPT_OLD_ENVIRON) {
818 # ifdef	ENV_HACK
819 				if (old_env_value == OLD_ENV_VAR)
820 				    fprintf(NetTrace, "\" (VAR) " + noquote);
821 				else
822 # endif
823 				    fprintf(NetTrace, "\" VALUE " + noquote);
824 			    } else
825 #endif /* OLD_ENVIRON */
826 				fprintf(NetTrace, "\" VAR " + noquote);
827 			    noquote = 2;
828 			    break;
829 
830 			case ENV_ESC:
831 			    fprintf(NetTrace, "\" ESC " + noquote);
832 			    noquote = 2;
833 			    break;
834 
835 			case ENV_USERVAR:
836 			    fprintf(NetTrace, "\" USERVAR " + noquote);
837 			    noquote = 2;
838 			    break;
839 
840 			default:
841 			    if (isprint(pointer[i]) && pointer[i] != '"') {
842 				if (noquote) {
843 				    putc('"', NetTrace);
844 				    noquote = 0;
845 				}
846 				putc(pointer[i], NetTrace);
847 			    } else {
848 				fprintf(NetTrace, "\" %03o " + noquote,
849 							pointer[i]);
850 				noquote = 2;
851 			    }
852 			    break;
853 			}
854 		    }
855 		    if (!noquote)
856 			putc('"', NetTrace);
857 		    break;
858 		}
859 	    }
860 	    break;
861 
862 	default:
863 	    if (TELOPT_OK(pointer[0]))
864 		fprintf(NetTrace, "%s (unknown)", TELOPT(pointer[0]));
865 	    else
866 		fprintf(NetTrace, "%d (unknown)", pointer[0]);
867 	    for (i = 1; i < length; i++)
868 		fprintf(NetTrace, " %d", pointer[i]);
869 	    break;
870 	}
871 	if (direction) {
872 	    if (NetTrace == stdout)
873 		fprintf(NetTrace, "\r\n");
874 	    else
875 		fprintf(NetTrace, "\n");
876 	}
877 	if (NetTrace == stdout)
878 	    fflush(NetTrace);
879     }
880 }
881 
882 /* EmptyTerminal - called to make sure that the terminal buffer is empty.
883  *			Note that we consider the buffer to run all the
884  *			way to the kernel (thus the select).
885  */
886 
887 void
888 EmptyTerminal(void)
889 {
890 #if	defined(unix)
891     struct pollfd set[1];
892 
893     set[0].fd = tout;
894     set[0].events = POLLOUT;
895 #endif	/* defined(unix) */
896 
897     if (TTYBYTES() == 0) {
898 #if	defined(unix)
899 	(void) poll(set, 1, INFTIM);
900 #endif	/* defined(unix) */
901     } else {
902 	while (TTYBYTES()) {
903 	    (void) ttyflush(0);
904 #if	defined(unix)
905 	    (void) poll(set, 1, INFTIM);
906 #endif	/* defined(unix) */
907 	}
908     }
909 }
910 
911 void
912 SetForExit(void)
913 {
914     setconnmode(0);
915 #if	defined(TN3270)
916     if (In3270) {
917 	Finish3270();
918     }
919 #else	/* defined(TN3270) */
920     do {
921 	(void)telrcv();			/* Process any incoming data */
922 	EmptyTerminal();
923     } while (ring_full_count(&netiring));	/* While there is any */
924 #endif	/* defined(TN3270) */
925     setcommandmode();
926     fflush(stdout);
927     fflush(stderr);
928 #if	defined(TN3270)
929     if (In3270) {
930 	StopScreen(1);
931     }
932 #endif	/* defined(TN3270) */
933     setconnmode(0);
934     EmptyTerminal();			/* Flush the path to the tty */
935     setcommandmode();
936 }
937 
938 void
939 Exit(int returnCode)
940 {
941     SetForExit();
942     exit(returnCode);
943 }
944 
945 void
946 ExitString(char *string, int returnCode)
947 {
948     SetForExit();
949     fwrite(string, 1, strlen(string), stderr);
950     exit(returnCode);
951 }
952