xref: /illumos-gate/usr/src/cmd/cmd-inet/usr.bin/telnet/utilities.c (revision 88f8b78a88cbdc6d8c1af5c3e54bc49d25095c98)
1 /*
2  * Copyright 1994-2002 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 #pragma ident	"%Z%%M%	%I%	%E% SMI"
7 
8 /*
9  * usr/src/cmd/cmd-inet/usr.bin/telnet/utilities.c
10  */
11 
12 /*
13  * Copyright (c) 1988, 1993
14  *	The Regents of the University of California.  All rights reserved.
15  *
16  * Redistribution and use in source and binary forms, with or without
17  * modification, are permitted provided that the following conditions
18  * are met:
19  * 1. Redistributions of source code must retain the above copyright
20  *    notice, this list of conditions and the following disclaimer.
21  * 2. Redistributions in binary form must reproduce the above copyright
22  *    notice, this list of conditions and the following disclaimer in the
23  *    documentation and/or other materials provided with the distribution.
24  * 3. All advertising materials mentioning features or use of this software
25  *    must display the following acknowledgement:
26  *	This product includes software developed by the University of
27  *	California, Berkeley and its contributors.
28  * 4. Neither the name of the University nor the names of its contributors
29  *    may be used to endorse or promote products derived from this software
30  *    without specific prior written permission.
31  *
32  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
33  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
34  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
35  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
36  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
37  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
38  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
39  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
40  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
41  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
42  * SUCH DAMAGE.
43  */
44 
45 #ifndef lint
46 static char sccsid[] = "@(#)utilities.c	8.1 (Berkeley) 6/6/93";
47 #endif /* not lint */
48 
49 #define	TELOPTS
50 #ifdef	lint
51 static char *telcmds[] = {0};
52 static char *slc_names[] = {0};
53 static char *encrypt_names[] = {0};
54 static char *enctype_names[] = {0};
55 #else	/* lint */
56 #define	TELCMDS
57 #define	SLC_NAMES
58 #endif	/* lint */
59 #include <arpa/telnet.h>
60 #include <sys/types.h>
61 #include <sys/time.h>
62 #include <sys/param.h>
63 #include <sys/socket.h>
64 #include <errno.h>
65 
66 #include <ctype.h>
67 
68 #include "general.h"
69 
70 #include "ring.h"
71 
72 #include "defines.h"
73 
74 #include "externs.h"
75 
76 FILE	*NetTrace = 0;		/* Not in bss, since needs to stay */
77 int	prettydump;
78 
79 /*
80  * upcase()
81  *
82  *	Upcase (in place) the argument.
83  */
84 
85     void
86 upcase(argument)
87 	register char *argument;
88 {
89 	register int c;
90 
91 	while ((c = *argument) != 0) {
92 		if (islower(c)) {
93 			*argument = toupper(c);
94 		}
95 	argument++;
96 	}
97 }
98 
99 /*
100  * SetSockOpt()
101  *
102  * Compensate for differences in 4.2 and 4.3 systems.
103  */
104 
105     int
106 SetSockOpt(fd, level, option, yesno)
107     int fd, level, option, yesno;
108 {
109 	return (setsockopt(fd, level, option, &yesno, sizeof (yesno)));
110 }
111 
112 /*
113  * The following are routines used to print out debugging information.
114  */
115 
116 unsigned char NetTraceFile[MAXPATHLEN] = "(standard output)";
117 
118     void
119 SetNetTrace(file)
120     register char *file;
121 {
122 	if (NetTrace && NetTrace != stdout)
123 		(void) fclose(NetTrace);
124 	if (file && (strcmp(file, "-") != 0)) {
125 		NetTrace = fopen(file, "w");
126 		if (NetTrace) {
127 			(void) strcpy((char *)NetTraceFile, file);
128 			return;
129 		}
130 		(void) fprintf(stderr, "Cannot open %s.\n", file);
131 	}
132 	NetTrace = stdout;
133 	(void) strcpy((char *)NetTraceFile, "(standard output)");
134 }
135 
136     void
137 Dump(direction, buffer, length)
138     char direction;
139     unsigned char *buffer;
140     int length;
141 {
142 #define	BYTES_PER_LINE	32
143 #define	min(x, y)	((x < y) ? x:y)
144 	unsigned char *pThis;
145 	int offset;
146 
147 	offset = 0;
148 
149 	while (length) {
150 		/* print one line */
151 		(void) fprintf(NetTrace, "%c 0x%x\t", direction, offset);
152 		pThis = buffer;
153 		if (prettydump) {
154 			buffer = buffer + min(length, BYTES_PER_LINE/2);
155 			while (pThis < buffer) {
156 				(void) fprintf(NetTrace, "%c%.2x",
157 				    (((*pThis)&0xff) == 0xff) ? '*' : ' ',
158 				    (*pThis)&0xff);
159 				pThis++;
160 			}
161 			length -= BYTES_PER_LINE/2;
162 			offset += BYTES_PER_LINE/2;
163 		} else {
164 			buffer = buffer + min(length, BYTES_PER_LINE);
165 			while (pThis < buffer) {
166 				(void) fprintf(NetTrace, "%.2x", (*pThis)&0xff);
167 				pThis++;
168 			}
169 			length -= BYTES_PER_LINE;
170 			offset += BYTES_PER_LINE;
171 		}
172 		if (NetTrace == stdout) {
173 			(void) fprintf(NetTrace, "\r\n");
174 		} else {
175 			(void) fprintf(NetTrace, "\n");
176 		}
177 		if (length < 0) {
178 			(void) fflush(NetTrace);
179 			return;
180 		}
181 		/* find next unique line */
182 	}
183 	(void) fflush(NetTrace);
184 }
185 
186 
187 	void
188 printoption(direction, cmd, option)
189 	char *direction;
190 	int cmd, option;
191 {
192 	if (!showoptions)
193 		return;
194 	if (cmd == IAC) {
195 		if (TELCMD_OK(option))
196 			(void) fprintf(NetTrace, "%s IAC %s", direction,
197 			    TELCMD(option));
198 		else
199 			(void) fprintf(NetTrace, "%s IAC %d", direction,
200 				option);
201 	} else {
202 		register char *fmt;
203 		fmt = (cmd == WILL) ? "WILL" : (cmd == WONT) ? "WONT" :
204 			(cmd == DO) ? "DO" : (cmd == DONT) ? "DONT" : 0;
205 		if (fmt) {
206 		    (void) fprintf(NetTrace, "%s %s ", direction, fmt);
207 		    if (TELOPT_OK(option))
208 			(void) fprintf(NetTrace, "%s", TELOPT(option));
209 		    else if (option == TELOPT_EXOPL)
210 			(void) fprintf(NetTrace, "EXOPL");
211 		    else
212 			(void) fprintf(NetTrace, "%d", option);
213 		} else
214 			(void) fprintf(NetTrace, "%s %d %d", direction, cmd,
215 			    option);
216 	}
217 	if (NetTrace == stdout) {
218 	    (void) fprintf(NetTrace, "\r\n");
219 	    (void) fflush(NetTrace);
220 	} else {
221 	    (void) fprintf(NetTrace, "\n");
222 	}
223 }
224 
225     void
226 optionstatus()
227 {
228 	register int i;
229 	extern char will_wont_resp[], do_dont_resp[];
230 
231 	for (i = 0; i < SUBBUFSIZE; i++) {
232 		if (do_dont_resp[i]) {
233 			if (TELOPT_OK(i))
234 				(void) printf("resp DO_DONT %s: %d\n",
235 				    TELOPT(i), do_dont_resp[i]);
236 			else if (TELCMD_OK(i))
237 				(void) printf("resp DO_DONT %s: %d\n",
238 				    TELCMD(i), do_dont_resp[i]);
239 			else
240 				(void) printf("resp DO_DONT %d: %d\n", i,
241 				    do_dont_resp[i]);
242 			if (my_want_state_is_do(i)) {
243 				if (TELOPT_OK(i))
244 					(void) printf("want DO   %s\n",
245 					    TELOPT(i));
246 				else if (TELCMD_OK(i))
247 					(void) printf("want DO   %s\n",
248 						TELCMD(i));
249 				else
250 					(void) printf("want DO   %d\n", i);
251 			} else {
252 				if (TELOPT_OK(i))
253 					(void) printf("want DONT %s\n",
254 					    TELOPT(i));
255 				else if (TELCMD_OK(i))
256 					(void) printf("want DONT %s\n",
257 					    TELCMD(i));
258 				else
259 					(void) printf("want DONT %d\n", i);
260 			}
261 		} else {
262 			if (my_state_is_do(i)) {
263 				if (TELOPT_OK(i))
264 					(void) printf("     DO   %s\n",
265 					    TELOPT(i));
266 				else if (TELCMD_OK(i))
267 					(void) printf("     DO   %s\n",
268 					    TELCMD(i));
269 				else
270 					(void) printf("     DO   %d\n", i);
271 			}
272 		}
273 		if (will_wont_resp[i]) {
274 			if (TELOPT_OK(i))
275 				(void) printf("resp WILL_WONT %s: %d\n",
276 				    TELOPT(i), will_wont_resp[i]);
277 			else if (TELCMD_OK(i))
278 				(void) printf("resp WILL_WONT %s: %d\n",
279 				    TELCMD(i), will_wont_resp[i]);
280 			else
281 				(void) printf("resp WILL_WONT %d: %d\n",
282 				    i, will_wont_resp[i]);
283 			if (my_want_state_is_will(i)) {
284 				if (TELOPT_OK(i))
285 					(void) printf("want WILL %s\n",
286 					    TELOPT(i));
287 				else if (TELCMD_OK(i))
288 					(void) printf("want WILL %s\n",
289 					    TELCMD(i));
290 				else
291 					(void) printf("want WILL %d\n", i);
292 			} else {
293 				if (TELOPT_OK(i))
294 					(void) printf("want WONT %s\n",
295 					    TELOPT(i));
296 				else if (TELCMD_OK(i))
297 					(void) printf("want WONT %s\n",
298 					    TELCMD(i));
299 				else
300 					(void) printf("want WONT %d\n", i);
301 			}
302 		} else {
303 			if (my_state_is_will(i)) {
304 				if (TELOPT_OK(i))
305 					(void) printf("     WILL %s\n",
306 					    TELOPT(i));
307 				else if (TELCMD_OK(i))
308 					(void) printf("     WILL %s\n",
309 					    TELCMD(i));
310 				else
311 					(void) printf("     WILL %d\n", i);
312 			}
313 		}
314 	}
315 
316 }
317 
318     void
319 printsub(direction, pointer, length)
320 	char direction;	/* '<' or '>' */
321 	unsigned char *pointer;	/* where suboption data sits */
322 	int	  length;	/* length of suboption data */
323 {
324 	register int i;
325 	char buf[512];
326 	extern int want_status_response;
327 
328 	if (showoptions || direction == 0 ||
329 	    (want_status_response && (pointer[0] == TELOPT_STATUS))) {
330 		if (direction) {
331 			(void) fprintf(NetTrace, "%s IAC SB ",
332 				(direction == '<')? "RCVD":"SENT");
333 			if (length >= 3) {
334 				register int j;
335 
336 				i = pointer[length-2];
337 				j = pointer[length-1];
338 
339 				if (i != IAC || j != SE) {
340 					(void) fprintf(NetTrace,
341 					    "(terminated by ");
342 					if (TELOPT_OK(i))
343 						(void) fprintf(NetTrace, "%s ",
344 						    TELOPT(i));
345 					else if (TELCMD_OK(i))
346 						(void) fprintf(NetTrace, "%s ",
347 						    TELCMD(i));
348 					else
349 						(void) fprintf(NetTrace, "%d ",
350 						    i);
351 					if (TELOPT_OK(j))
352 						(void) fprintf(NetTrace, "%s",
353 						    TELOPT(j));
354 					else if (TELCMD_OK(j))
355 						(void) fprintf(NetTrace, "%s",
356 						    TELCMD(j));
357 					else
358 						(void) fprintf(NetTrace, "%d",
359 						    j);
360 					(void) fprintf(NetTrace,
361 					    ", not IAC SE!) ");
362 				}
363 			}
364 			length -= 2;
365 		}
366 		if (length < 1) {
367 			(void) fprintf(NetTrace, "(Empty suboption??\?)");
368 			if (NetTrace == stdout)
369 				(void) fflush(NetTrace);
370 			return;
371 		}
372 		switch (pointer[0]) {
373 		case TELOPT_TTYPE:
374 			(void) fprintf(NetTrace, "TERMINAL-TYPE ");
375 			switch (pointer[1]) {
376 			case TELQUAL_IS:
377 				(void) fprintf(NetTrace, "IS \"%.*s\"",
378 				    length-2,
379 				    (char *)pointer+2);
380 				break;
381 			case TELQUAL_SEND:
382 				(void) fprintf(NetTrace, "SEND");
383 				break;
384 			default:
385 				(void) fprintf(NetTrace,
386 				    "- unknown qualifier %d (0x%x).",
387 				    pointer[1], pointer[1]);
388 			}
389 			break;
390 		case TELOPT_TSPEED:
391 			(void) fprintf(NetTrace, "TERMINAL-SPEED");
392 			if (length < 2) {
393 				(void) fprintf(NetTrace,
394 				    " (empty suboption??\?)");
395 				break;
396 			}
397 			switch (pointer[1]) {
398 			case TELQUAL_IS:
399 				(void) fprintf(NetTrace, " IS ");
400 				(void) fprintf(NetTrace, "%.*s", length-2,
401 				    (char *)pointer+2);
402 				break;
403 			default:
404 				if (pointer[1] == 1)
405 					(void) fprintf(NetTrace, " SEND");
406 				else
407 					(void) fprintf(NetTrace,
408 					    " %d (unknown)", pointer[1]);
409 				for (i = 2; i < length; i++)
410 					(void) fprintf(NetTrace, " ?%d?",
411 					    pointer[i]);
412 				break;
413 			}
414 			break;
415 
416 		case TELOPT_LFLOW:
417 			(void) fprintf(NetTrace, "TOGGLE-FLOW-CONTROL");
418 			if (length < 2) {
419 				(void) fprintf(NetTrace,
420 				    " (empty suboption??\?)");
421 				break;
422 			}
423 			switch (pointer[1]) {
424 			case LFLOW_OFF:
425 				(void) fprintf(NetTrace, " OFF");
426 				break;
427 			case LFLOW_ON:
428 				(void) fprintf(NetTrace, " ON");
429 				break;
430 			case LFLOW_RESTART_ANY:
431 				(void) fprintf(NetTrace, " RESTART-ANY");
432 				break;
433 			case LFLOW_RESTART_XON:
434 				(void) fprintf(NetTrace, " RESTART-XON");
435 				break;
436 			default:
437 				(void) fprintf(NetTrace, " %d (unknown)",
438 				    pointer[1]);
439 			}
440 			for (i = 2; i < length; i++)
441 				(void) fprintf(NetTrace, " ?%d?",
442 				    pointer[i]);
443 			break;
444 
445 		case TELOPT_NAWS:
446 			(void) fprintf(NetTrace, "NAWS");
447 			if (length < 2) {
448 				(void) fprintf(NetTrace,
449 				    " (empty suboption??\?)");
450 				break;
451 			}
452 			if (length == 2) {
453 				(void) fprintf(NetTrace, " ?%d?", pointer[1]);
454 				break;
455 			}
456 			(void) fprintf(NetTrace, " %d %d (%d)",
457 			    pointer[1], pointer[2],
458 			    (int)((((unsigned int)pointer[1])<<8)|
459 			    ((unsigned int)pointer[2])));
460 			if (length == 4) {
461 				(void) fprintf(NetTrace, " ?%d?", pointer[3]);
462 				break;
463 			}
464 			(void) fprintf(NetTrace, " %d %d (%d)",
465 			    pointer[3], pointer[4],
466 			    (int)((((unsigned int)pointer[3])<<8)|
467 			    ((unsigned int)pointer[4])));
468 			for (i = 5; i < length; i++)
469 				(void) fprintf(NetTrace, " ?%d?", pointer[i]);
470 			break;
471 
472 		case TELOPT_AUTHENTICATION:
473 			(void) fprintf(NetTrace, "AUTHENTICATION");
474 			if (length < 2) {
475 				(void) fprintf(NetTrace,
476 					" (empty suboption??\?)");
477 				break;
478 			}
479 			switch (pointer[1]) {
480 			case TELQUAL_REPLY:
481 			case TELQUAL_IS:
482 				(void) fprintf(NetTrace, " %s ",
483 				    (pointer[1] == TELQUAL_IS) ?
484 				    "IS" : "REPLY");
485 				if (AUTHTYPE_NAME_OK(pointer[2]))
486 					(void) fprintf(NetTrace, "%s ",
487 					    AUTHTYPE_NAME(pointer[2]));
488 				else
489 					(void) fprintf(NetTrace, "%d ",
490 						pointer[2]);
491 				if (length < 3) {
492 					(void) fprintf(NetTrace,
493 					    "(partial suboption??\?)");
494 					break;
495 				}
496 				(void) fprintf(NetTrace, "%s|%s",
497 				    ((pointer[3] & AUTH_WHO_MASK) ==
498 				    AUTH_WHO_CLIENT) ? "CLIENT" : "SERVER",
499 				    ((pointer[3] & AUTH_HOW_MASK) ==
500 				    AUTH_HOW_MUTUAL) ? "MUTUAL" : "ONE-WAY");
501 
502 				auth_printsub(&pointer[1], length - 1,
503 				    (uchar_t *)buf, sizeof (buf));
504 				(void) fprintf(NetTrace, "%s", buf);
505 				break;
506 
507 			case TELQUAL_SEND:
508 				i = 2;
509 				(void) fprintf(NetTrace, " SEND ");
510 				while (i < length) {
511 					if (AUTHTYPE_NAME_OK(pointer[i]))
512 						(void) fprintf(NetTrace, "%s ",
513 						    AUTHTYPE_NAME(pointer[i]));
514 					else
515 						(void) fprintf(NetTrace, "%d ",
516 						    pointer[i]);
517 					if (++i >= length) {
518 						(void) fprintf(NetTrace,
519 						    "(partial "
520 						    "suboption??\?)");
521 						break;
522 					}
523 					(void) fprintf(NetTrace, "%s|%s ",
524 					    ((pointer[i] & AUTH_WHO_MASK) ==
525 					    AUTH_WHO_CLIENT) ?
526 					    "CLIENT" : "SERVER",
527 					    ((pointer[i] & AUTH_HOW_MASK) ==
528 					    AUTH_HOW_MUTUAL) ?
529 					    "MUTUAL" : "ONE-WAY");
530 					++i;
531 				}
532 				break;
533 
534 			case TELQUAL_NAME:
535 				i = 2;
536 				(void) fprintf(NetTrace, " NAME \"");
537 				while (i < length)
538 					(void) putc(pointer[i++], NetTrace);
539 				(void) putc('"', NetTrace);
540 				break;
541 
542 			default:
543 				for (i = 2; i < length; i++)
544 				(void) fprintf(NetTrace, " ?%d?", pointer[i]);
545 				break;
546 			}
547 			break;
548 
549 		case TELOPT_ENCRYPT:
550 			(void) fprintf(NetTrace, "ENCRYPT");
551 			if (length < 2) {
552 				(void) fprintf(NetTrace,
553 				    " (empty suboption??\?)");
554 				break;
555 			}
556 			switch (pointer[1]) {
557 			case ENCRYPT_START:
558 				(void) fprintf(NetTrace, " START");
559 				break;
560 
561 			case ENCRYPT_END:
562 				(void) fprintf(NetTrace, " END");
563 				break;
564 
565 			case ENCRYPT_REQSTART:
566 				(void) fprintf(NetTrace, " REQUEST-START");
567 				break;
568 
569 			case ENCRYPT_REQEND:
570 				(void) fprintf(NetTrace, " REQUEST-END");
571 				break;
572 
573 			case ENCRYPT_IS:
574 			case ENCRYPT_REPLY:
575 				(void) fprintf(NetTrace, " %s ",
576 				    (pointer[1] == ENCRYPT_IS) ?
577 				    "IS" : "REPLY");
578 				if (length < 3) {
579 					(void) fprintf(NetTrace, " (partial "
580 					    "suboption??\?)");
581 					break;
582 				}
583 				if (ENCTYPE_NAME_OK(pointer[2]))
584 					(void) fprintf(NetTrace, "%s ",
585 					    ENCTYPE_NAME(pointer[2]));
586 				else
587 					(void) fprintf(NetTrace,
588 					    " %d (unknown)", pointer[2]);
589 
590 				encrypt_printsub(&pointer[1], length - 1,
591 				    (uchar_t *)buf, sizeof (buf));
592 				(void) fprintf(NetTrace, "%s", buf);
593 				break;
594 
595 			case ENCRYPT_SUPPORT:
596 				i = 2;
597 				(void) fprintf(NetTrace, " SUPPORT ");
598 				while (i < length) {
599 					if (ENCTYPE_NAME_OK(pointer[i]))
600 						(void) fprintf(NetTrace, "%s ",
601 						    ENCTYPE_NAME(pointer[i]));
602 					else
603 						(void) fprintf(NetTrace, "%d ",
604 						    pointer[i]);
605 					i++;
606 				}
607 				break;
608 
609 			case ENCRYPT_ENC_KEYID:
610 				(void) fprintf(NetTrace, " ENC_KEYID ");
611 				goto encommon;
612 
613 			case ENCRYPT_DEC_KEYID:
614 				(void) fprintf(NetTrace, " DEC_KEYID ");
615 				goto encommon;
616 
617 			default:
618 				(void) fprintf(NetTrace, " %d (unknown)",
619 				    pointer[1]);
620 			encommon:
621 				for (i = 2; i < length; i++)
622 					(void) fprintf(NetTrace, " %d",
623 					    pointer[i]);
624 				break;
625 			}
626 			break;
627 
628 		case TELOPT_LINEMODE:
629 			(void) fprintf(NetTrace, "LINEMODE ");
630 			if (length < 2) {
631 				(void) fprintf(NetTrace,
632 				    " (empty suboption??\?)");
633 				break;
634 			}
635 			switch (pointer[1]) {
636 			case WILL:
637 				(void) fprintf(NetTrace, "WILL ");
638 				goto common;
639 			case WONT:
640 				(void) fprintf(NetTrace, "WONT ");
641 				goto common;
642 			case DO:
643 				(void) fprintf(NetTrace, "DO ");
644 				goto common;
645 			case DONT:
646 				(void) fprintf(NetTrace, "DONT ");
647 common:
648 				if (length < 3) {
649 					(void) fprintf(NetTrace,
650 						"(no option??\?)");
651 					break;
652 				}
653 				switch (pointer[2]) {
654 				case LM_FORWARDMASK:
655 					(void) fprintf(NetTrace,
656 					    "Forward Mask");
657 					for (i = 3; i < length; i++)
658 						(void) fprintf(NetTrace, " %x",
659 						    pointer[i]);
660 					break;
661 				default:
662 					(void) fprintf(NetTrace, "%d (unknown)",
663 					    pointer[2]);
664 					for (i = 3; i < length; i++)
665 					(void) fprintf(NetTrace,
666 					    " %d", pointer[i]);
667 					break;
668 				}
669 				break;
670 
671 			case LM_SLC:
672 				(void) fprintf(NetTrace, "SLC");
673 				for (i = 2; i < length - 2; i += 3) {
674 					if (SLC_NAME_OK(pointer[i+SLC_FUNC]))
675 						(void) fprintf(NetTrace, " %s",
676 						    SLC_NAME(pointer[
677 						    i+SLC_FUNC]));
678 					else
679 						(void) fprintf(NetTrace, " %d",
680 						    pointer[i+SLC_FUNC]);
681 					switch (pointer[i+SLC_FLAGS] &
682 					    SLC_LEVELBITS) {
683 					case SLC_NOSUPPORT:
684 						(void) fprintf(NetTrace,
685 						    " NOSUPPORT");
686 						break;
687 					case SLC_CANTCHANGE:
688 						(void) fprintf(NetTrace,
689 						    " CANTCHANGE");
690 						break;
691 					case SLC_VARIABLE:
692 						(void) fprintf(NetTrace,
693 						    " VARIABLE");
694 						break;
695 					case SLC_DEFAULT:
696 						(void) fprintf(NetTrace,
697 						    " DEFAULT");
698 						break;
699 					}
700 					(void) fprintf(NetTrace, "%s%s%s",
701 					    pointer[i+SLC_FLAGS]&SLC_ACK ?
702 						"|ACK" : "",
703 					    pointer[i+SLC_FLAGS]&SLC_FLUSHIN ?
704 						"|FLUSHIN" : "",
705 					    pointer[i+SLC_FLAGS]&SLC_FLUSHOUT ?
706 						"|FLUSHOUT" : "");
707 					if (pointer[i+SLC_FLAGS] &
708 					    ~(SLC_ACK|SLC_FLUSHIN|
709 					    SLC_FLUSHOUT| SLC_LEVELBITS))
710 					(void) fprintf(NetTrace, "(0x%x)",
711 					    pointer[i+SLC_FLAGS]);
712 					(void) fprintf(NetTrace, " %d;",
713 					    pointer[i+SLC_VALUE]);
714 					if ((pointer[i+SLC_VALUE] == IAC) &&
715 					    (pointer[i+SLC_VALUE+1] == IAC))
716 						i++;
717 				}
718 				for (; i < length; i++)
719 					(void) fprintf(NetTrace, " ?%d?",
720 					    pointer[i]);
721 				break;
722 
723 			case LM_MODE:
724 				(void) fprintf(NetTrace, "MODE ");
725 				if (length < 3) {
726 					(void) fprintf(NetTrace,
727 					    "(no mode??\?)");
728 					break;
729 				}
730 				{
731 					char tbuf[64];
732 					(void) sprintf(tbuf, "%s%s%s%s%s",
733 					    pointer[2]&MODE_EDIT ? "|EDIT" : "",
734 					    pointer[2]&MODE_TRAPSIG ?
735 					    "|TRAPSIG" : "",
736 					    pointer[2]&MODE_SOFT_TAB ?
737 					    "|SOFT_TAB" : "",
738 					    pointer[2]&MODE_LIT_ECHO ?
739 					    "|LIT_ECHO" : "",
740 					    pointer[2]&MODE_ACK ? "|ACK" : "");
741 					(void) fprintf(NetTrace, "%s", tbuf[1] ?
742 					    &tbuf[1] : "0");
743 				}
744 				if (pointer[2]&~(MODE_MASK))
745 					(void) fprintf(NetTrace, " (0x%x)",
746 					    pointer[2]);
747 				for (i = 3; i < length; i++)
748 					(void) fprintf(NetTrace, " ?0x%x?",
749 					    pointer[i]);
750 				break;
751 			default:
752 				(void) fprintf(NetTrace, "%d (unknown)",
753 				    pointer[1]);
754 				for (i = 2; i < length; i++)
755 					(void) fprintf(NetTrace, " %d",
756 					    pointer[i]);
757 				}
758 				break;
759 
760 		case TELOPT_STATUS: {
761 				register char *cp;
762 				register int j, k;
763 
764 				(void) fprintf(NetTrace, "STATUS");
765 
766 				switch (pointer[1]) {
767 				default:
768 					if (pointer[1] == TELQUAL_SEND)
769 						(void) fprintf(NetTrace,
770 						    " SEND");
771 					else
772 						(void) fprintf(NetTrace,
773 						    " %d (unknown)",
774 						    pointer[1]);
775 					for (i = 2; i < length; i++)
776 					(void) fprintf(NetTrace, " ?%d?",
777 					    pointer[i]);
778 					break;
779 				case TELQUAL_IS:
780 					if (--want_status_response < 0)
781 						want_status_response = 0;
782 					if (NetTrace == stdout)
783 						(void) fprintf(NetTrace,
784 						    " IS\r\n");
785 					else
786 						(void) fprintf(NetTrace,
787 						    " IS\n");
788 
789 					for (i = 2; i < length; i++) {
790 						switch (pointer[i]) {
791 						case DO:
792 							cp = "DO";
793 							goto common2;
794 						case DONT:
795 							cp = "DONT";
796 							goto common2;
797 						case WILL:
798 							cp = "WILL";
799 							goto common2;
800 						case WONT:
801 							cp = "WONT";
802 							goto common2;
803 common2:
804 							i++;
805 							if (TELOPT_OK(
806 							    (int)pointer[i]))
807 								(void) fprintf(
808 								    NetTrace,
809 								    " %s %s",
810 								    cp,
811 								    TELOPT(
812 								    pointer[
813 								    i]));
814 							else
815 								(void) fprintf(
816 								    NetTrace,
817 								    " %s %d",
818 								    cp,
819 								    pointer[i]);
820 
821 							if (NetTrace == stdout)
822 								(void) fprintf(
823 								    NetTrace,
824 								    "\r\n");
825 							else
826 								(void) fprintf(
827 								    NetTrace,
828 								    "\n");
829 							break;
830 
831 						case SB:
832 							(void) fprintf(NetTrace,
833 							    " SB ");
834 							i++;
835 							j = k = i;
836 							while (j < length) {
837 			if (pointer[j] == SE) {
838 				if (j+1 == length)
839 					break;
840 				if (pointer[j+1] == SE)
841 					j++;
842 				else
843 					break;
844 				}
845 				pointer[k++] = pointer[j++];
846 							}
847 							printsub(0,
848 							    &pointer[i], k - i);
849 							if (i < length) {
850 						(void) fprintf(NetTrace, " SE");
851 				i = j;
852 			} else
853 				i = j - 1;
854 
855 			if (NetTrace == stdout)
856 				(void) fprintf(NetTrace, "\r\n");
857 			else
858 				(void) fprintf(NetTrace, "\n");
859 
860 							break;
861 
862 						default:
863 							(void) fprintf(NetTrace,
864 							    " %d", pointer[i]);
865 							break;
866 						}
867 					}
868 					break;
869 				}
870 				break;
871 			}
872 
873 		case TELOPT_XDISPLOC:
874 			(void) fprintf(NetTrace, "X-DISPLAY-LOCATION ");
875 			switch (pointer[1]) {
876 			case TELQUAL_IS:
877 				(void) fprintf(NetTrace, "IS \"%.*s\"",
878 				    length-2, (char *)pointer+2);
879 				break;
880 			case TELQUAL_SEND:
881 				(void) fprintf(NetTrace, "SEND");
882 				break;
883 			default:
884 				(void) fprintf(NetTrace,
885 				    "- unknown qualifier %d (0x%x).",
886 				    pointer[1], pointer[1]);
887 			}
888 			break;
889 
890 		case TELOPT_NEW_ENVIRON:
891 	    (void) fprintf(NetTrace, "NEW-ENVIRON ");
892 #ifdef	OLD_ENVIRON
893 	    goto env_common1;
894 	case TELOPT_OLD_ENVIRON:
895 	    (void) fprintf(NetTrace, "OLD-ENVIRON ");
896 	env_common1:
897 #endif
898 	    switch (pointer[1]) {
899 	    case TELQUAL_IS:
900 		(void) fprintf(NetTrace, "IS ");
901 		goto env_common;
902 	    case TELQUAL_SEND:
903 		(void) fprintf(NetTrace, "SEND ");
904 		goto env_common;
905 	    case TELQUAL_INFO:
906 		(void) fprintf(NetTrace, "INFO ");
907 	    env_common:
908 		{
909 		    register int noquote = 2;
910 #if defined(ENV_HACK) && defined(OLD_ENVIRON)
911 		    extern int old_env_var, old_env_value;
912 #endif
913 		    for (i = 2; i < length; i++) {
914 			switch (pointer[i]) {
915 			case NEW_ENV_VALUE:
916 #ifdef OLD_ENVIRON
917 		    /*	case NEW_ENV_OVAR: */
918 			    if (pointer[0] == TELOPT_OLD_ENVIRON) {
919 #ifdef	ENV_HACK
920 				if (old_env_var == OLD_ENV_VALUE)
921 					(void) fprintf(NetTrace,
922 					    "\" (VALUE) " + noquote);
923 				else
924 #endif
925 					(void) fprintf(NetTrace,
926 					    "\" VAR " + noquote);
927 			    } else
928 #endif /* OLD_ENVIRON */
929 				(void) fprintf(NetTrace, "\" VALUE " + noquote);
930 			    noquote = 2;
931 			    break;
932 
933 			case NEW_ENV_VAR:
934 #ifdef OLD_ENVIRON
935 		    /* case OLD_ENV_VALUE: */
936 			    if (pointer[0] == TELOPT_OLD_ENVIRON) {
937 #ifdef	ENV_HACK
938 				if (old_env_value == OLD_ENV_VAR)
939 					(void) fprintf(NetTrace,
940 					    "\" (VAR) " + noquote);
941 				else
942 #endif
943 					(void) fprintf(NetTrace,
944 					    "\" VALUE " + noquote);
945 			    } else
946 #endif /* OLD_ENVIRON */
947 				(void) fprintf(NetTrace, "\" VAR " + noquote);
948 			    noquote = 2;
949 			    break;
950 
951 			case ENV_ESC:
952 			    (void) fprintf(NetTrace, "\" ESC " + noquote);
953 			    noquote = 2;
954 			    break;
955 
956 			case ENV_USERVAR:
957 			    (void) fprintf(NetTrace, "\" USERVAR " + noquote);
958 			    noquote = 2;
959 			    break;
960 
961 			default:
962 			def_case:
963 			    if (isprint(pointer[i]) && pointer[i] != '"') {
964 				if (noquote) {
965 				    (void) putc('"', NetTrace);
966 				    noquote = 0;
967 				}
968 				(void) putc(pointer[i], NetTrace);
969 			    } else {
970 				(void) fprintf(NetTrace, "\" %03o " + noquote,
971 							pointer[i]);
972 				noquote = 2;
973 			    }
974 			    break;
975 			}
976 		    }
977 		    if (!noquote)
978 			(void) putc('"', NetTrace);
979 		    break;
980 		}
981 	    }
982 	    break;
983 
984 	default:
985 	    if (TELOPT_OK(pointer[0]))
986 		(void) fprintf(NetTrace, "%s (unknown)", TELOPT(pointer[0]));
987 	    else
988 		(void) fprintf(NetTrace, "%d (unknown)", pointer[0]);
989 	    for (i = 1; i < length; i++)
990 		(void) fprintf(NetTrace, " %d", pointer[i]);
991 	    break;
992 	}
993 	if (direction) {
994 	    if (NetTrace == stdout)
995 		(void) fprintf(NetTrace, "\r\n");
996 	    else
997 		(void) fprintf(NetTrace, "\n");
998 	}
999 	if (NetTrace == stdout)
1000 	    (void) fflush(NetTrace);
1001 	}
1002 }
1003 
1004 /*
1005  * EmptyTerminal - called to make sure that the terminal buffer is empty.
1006  *			Note that we consider the buffer to run all the
1007  *			way to the kernel (thus the select).
1008  */
1009 
1010 static void
1011 EmptyTerminal()
1012 {
1013 	fd_set	o;
1014 
1015 	FD_ZERO(&o);
1016 
1017 	if (TTYBYTES() == 0) {
1018 		FD_SET(tout, &o);
1019 		/* wait for TTLOWAT */
1020 		(void) select(tout+1, NULL, &o, NULL, NULL);
1021 	} else {
1022 		while (TTYBYTES()) {
1023 			if (ttyflush(0) == -2) {
1024 				/* This will not return. */
1025 				fatal_tty_error("write");
1026 			}
1027 			FD_SET(tout, &o);
1028 			/* wait for TTLOWAT */
1029 			(void) select(tout+1, NULL, &o, NULL, NULL);
1030 		}
1031 	}
1032 }
1033 
1034 static void
1035 SetForExit()
1036 {
1037 	setconnmode(0);
1038 	do {
1039 		(void) telrcv();		/* Process any incoming data */
1040 		EmptyTerminal();
1041 	} while (ring_full_count(&netiring));	/* While there is any */
1042 	setcommandmode();
1043 	(void) fflush(stdout);
1044 	(void) fflush(stderr);
1045 	setconnmode(0);
1046 	EmptyTerminal();			/* Flush the path to the tty */
1047 	setcommandmode();
1048 }
1049 
1050 void
1051 Exit(returnCode)
1052 	int returnCode;
1053 {
1054 	SetForExit();
1055 	exit(returnCode);
1056 }
1057 
1058 void
1059 ExitString(string, returnCode)
1060 	char *string;
1061 	int returnCode;
1062 {
1063 	SetForExit();
1064 	(void) fwrite(string, 1, strlen(string), stderr);
1065 	exit(returnCode);
1066 }
1067 
1068 #define	BUFFER_CHUNK_SIZE 64
1069 
1070 /* Round up to a multiple of BUFFER_CHUNK_SIZE */
1071 #define	ROUND_CHUNK_SIZE(s) ((((s) + BUFFER_CHUNK_SIZE - 1) / \
1072 		BUFFER_CHUNK_SIZE) * BUFFER_CHUNK_SIZE)
1073 
1074 /*
1075  * Optionally allocate a buffer, and optionally read a string from a stream
1076  * into the buffer, starting at the given offset.  If the buffer isn't
1077  * large enough for the given offset, or if buffer space is exhausted
1078  * when reading the string, the size of the buffer is increased.
1079  *
1080  * A buffer can be supplied when the function is called, passing the
1081  * buffer address via the first argument.  The buffer size can be
1082  * passed as well, in the second argument.  If the second argument is
1083  * NULL, the function makes no assumptions about the buffer size.
1084  * The address of the buffer is returned via the first argument, and the
1085  * buffer size via the second argument if this is not NULL.
1086  * These returned values may differ from the supplied values if the buffer
1087  * was reallocated.
1088  *
1089  * If no buffer is to be supplied, specify a buffer address of NULL, via
1090  * the first argument.
1091  *
1092  * If the pointer to the buffer address is NULL, the function just returns
1093  * NULL, and performs no other processing.
1094  *
1095  * If a NULL stream is passed, the function will just make sure the
1096  * supplied buffer is large enough to hold the supplied offset,
1097  * reallocating it if is too small or too large.
1098  *
1099  * The returned buffer will be a multiple of BUFFER_CHUNK_SIZE in size.
1100  *
1101  * The function stops reading from the stream when a newline is read,
1102  * end of file is reached, or an error occurs.  The newline is not
1103  * returned in the buffer.  The returned string will be NULL terminated.
1104  *
1105  * The function returns the address of the buffer if any characters
1106  * are read and no error occurred, otherwise it returns NULL.
1107  *
1108  * If the function returns NULL, a buffer may have been allocated.  The
1109  * buffer address will be returned via the first argument, together with
1110  * the buffer size if the second argument is not NULL.
1111  *
1112  */
1113 static char *
1114 GetStringAtOffset(bufp, cbufsiz, off, st)
1115 	char **bufp;
1116 	unsigned int *cbufsiz;
1117 	unsigned int off;
1118 	FILE *st;
1119 {
1120 	unsigned int bufsiz;
1121 	char *buf;
1122 	char *nbuf;
1123 	unsigned int idx = off;
1124 
1125 	if (bufp == NULL)
1126 		return (NULL);
1127 
1128 	buf = *bufp;
1129 
1130 	bufsiz = ROUND_CHUNK_SIZE(off + 1);
1131 
1132 	if (buf == NULL || cbufsiz == NULL || *cbufsiz != bufsiz) {
1133 		if ((nbuf = realloc(buf, bufsiz)) == NULL)
1134 			return (NULL);
1135 
1136 		buf = nbuf;
1137 		*bufp = buf;
1138 		if (cbufsiz != NULL)
1139 			*cbufsiz = bufsiz;
1140 	}
1141 
1142 
1143 	if (st == NULL)
1144 		return (buf);
1145 
1146 	clearerr(st);
1147 	for (;;) {
1148 		int c = getc(st);
1149 
1150 		/* Expand the buffer as needed. */
1151 		if (idx == bufsiz) {
1152 			bufsiz += BUFFER_CHUNK_SIZE;
1153 			if ((nbuf = realloc(buf, bufsiz)) == NULL) {
1154 				/* Discard everything we read. */
1155 				buf[off] = 0;
1156 				buf = NULL;
1157 				break;
1158 			}
1159 			buf = nbuf;
1160 			*bufp = buf;
1161 			if (cbufsiz != NULL)
1162 				*cbufsiz = bufsiz;
1163 		}
1164 
1165 		if (c == EOF || c == '\n') {
1166 			buf[idx] = 0;
1167 			if (ferror(st) != 0) {
1168 				/* Retry if interrupted by a signal. */
1169 				if (errno == EINTR) {
1170 					clearerr(st);
1171 					continue;
1172 				}
1173 				buf = NULL;
1174 			} else if (feof(st) != 0) {
1175 				/* No characters transferred? */
1176 				if (off == idx)
1177 					buf = NULL;
1178 			}
1179 			break;
1180 		}
1181 		buf[idx++] = c;
1182 	}
1183 	return (buf);
1184 }
1185 
1186 /*
1187  * Read a string from the supplied stream.  Stop reading when a newline
1188  * is read, end of file reached, or an error occurs.
1189  *
1190  * A buffer can be supplied by specifying the buffer address via the
1191  * first argument. The buffer size can be passed via the second argument.
1192  * If the second argument is NULL, the function makes no assumptions
1193  * about the buffer size. The buffer will be reallocated if it is too
1194  * small or too large for the returned string.
1195  *
1196  * If no buffer is to be supplied, specify a buffer address of NULL,
1197  * via the first argument.
1198  *
1199  * If the first argument is NULL, the function just returns NULL, and
1200  * performs no other processing.
1201  *
1202  * The function returns the address of the buffer if any characters are
1203  * read and no error occurred.
1204  *
1205  * If the function returns NULL, a buffer may have been allocated.  The
1206  * buffer address and buffer size will be returned via the first argument,
1207  * and the buffer size via the second argument, if this isn't NULL.
1208  */
1209 char *
1210 GetString(bufp, bufsiz, st)
1211 	char **bufp;
1212 	unsigned int *bufsiz;
1213 	FILE *st;
1214 {
1215 	return (GetStringAtOffset(bufp, bufsiz, 0, st));
1216 }
1217 
1218 /*
1219  * Allocate a buffer to hold a string of given length.
1220  *
1221  * An existing buffer can be reallocated by passing its address and via
1222  * the first argument.  The buffer size can be passed via the second
1223  * argument.  If the second argument is NULL, the function makes no
1224  * assumptions about the buffer size.
1225  *
1226  * If no existing buffer is to be supplied, pass a NULL buffer address via
1227  * the first argument.
1228  *
1229  * If the first argument is NULL, the function just returns NULL,
1230  * and performs no other processing.
1231  */
1232 char *
1233 AllocStringBuffer(bufp, bufsiz, size)
1234 	char **bufp;
1235 	unsigned int *bufsiz;
1236 	unsigned int size;
1237 {
1238 	return (GetStringAtOffset(bufp, bufsiz, size, (FILE *)NULL));
1239 }
1240 
1241 /*
1242  * This function is similar to GetString(), except that the string read
1243  * from the stream is appended to the supplied string.
1244  */
1245 char *
1246 GetAndAppendString(bufp, bufsiz, str, st)
1247 	char **bufp;
1248 	unsigned int *bufsiz;
1249 	char *str;
1250 	FILE *st;
1251 {
1252 	unsigned int off = strlen(str);
1253 
1254 	if (GetStringAtOffset(bufp, bufsiz, off, st) == NULL)
1255 		return (NULL);
1256 
1257 	return (memcpy(*bufp, str, off));
1258 }
1259