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