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