xref: /illumos-gate/usr/src/cmd/cmd-inet/usr.bin/telnet/commands.c (revision 66582b606a8194f7f3ba5b3a3a6dca5b0d346361)
1 /*
2  * Copyright (c) 1988, 1990, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *	This product includes software developed by the University of
16  *	California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
34  * Use is subject to license terms.
35  */
36 
37 #include <sys/param.h>
38 #include <sys/file.h>
39 #include <sys/socket.h>
40 #include <sys/sysmacros.h>
41 #include <netinet/in.h>
42 
43 #include <signal.h>
44 #include <netdb.h>
45 #include <ctype.h>
46 #include <pwd.h>
47 #include <errno.h>
48 #include <strings.h>
49 
50 #include <arpa/telnet.h>
51 #include <arpa/inet.h>
52 
53 #include "general.h"
54 
55 #include "ring.h"
56 
57 #include "externs.h"
58 #include "defines.h"
59 #include "types.h"
60 
61 extern	char *telnet_krb5_realm;
62 extern	void krb5_profile_get_options(char *, char *,
63 		profile_options_boolean*);
64 
65 #include <k5-int.h>
66 #include <profile/prof_int.h>
67 
68 profile_options_boolean config_file_options[] = {
69 	{ "forwardable", &forwardable_flag, 0},
70 	{ "forward", &forward_flag, 0},
71 	{ "encrypt", &encrypt_flag, 0 },
72 	{ "autologin", &autologin, 0 },
73 	{ NULL, NULL, 0}
74 };
75 
76 #include <netinet/ip.h>
77 
78 /*
79  * Number of maximum IPv4 gateways user can specify. This number is limited by
80  * the maximum size of the IPv4 options in the IPv4 header.
81  */
82 #define	MAX_GATEWAY	8
83 /*
84  * Number of maximum IPv6 gateways user can specify. This number is limited by
85  * the maximum header extension length of the IPv6 routing header.
86  */
87 #define	MAX_GATEWAY6	127
88 #define	MAXMAX_GATEWAY	MAX(MAX_GATEWAY, MAX_GATEWAY6)
89 
90 /*
91  * Depending on the address resolutions of the target and gateways,
92  * we determine which addresses of the target we'll try connecting to.
93  */
94 #define	ALL_ADDRS	0	/* try all addrs of target */
95 #define	ONLY_V4		1	/* try only IPv4 addrs of target */
96 #define	ONLY_V6		2	/* try only IPv6 addrs of target */
97 
98 #if defined(USE_TOS)
99 int tos = -1;
100 #endif
101 
102 char	*hostname;
103 static char _hostname[MAXHOSTNAMELEN];
104 
105 static int send_tncmd(void (*func)(), char *, char *);
106 static void call(int n_ptrs, ...);
107 static int cmdrc(char *, char *);
108 
109 typedef struct {
110 	char	*name;		/* command name */
111 	char	*help;		/* help string (NULL for no help) */
112 	int	(*handler)();	/* routine which executes command */
113 	int	needconnect;	/* Do we need to be connected to execute? */
114 } Command;
115 
116 /*
117  * storage for IPv6 and/or IPv4 addresses of gateways
118  */
119 struct gateway {
120 	struct in6_addr	gw_addr6;
121 	struct in_addr	gw_addr;
122 };
123 
124 /*
125  * IPv4 source routing option.
126  * In order to avoid padding for the alignment of IPv4 addresses, ipsr_addrs
127  * is defined as a 2-D array of uint8_t, instead of 1-D array of struct in_addr.
128  * If it were defined as "struct in_addr ipsr_addrs[1]", "ipsr_ptr" would be
129  * followed by one byte of padding to avoid misaligned struct in_addr.
130  */
131 struct ip_sourceroute {
132 	uint8_t ipsr_code;
133 	uint8_t ipsr_len;
134 	uint8_t ipsr_ptr;
135 	/* up to 9 IPv4 addresses */
136 	uint8_t ipsr_addrs[1][sizeof (struct in_addr)];
137 };
138 
139 static char *line = NULL;
140 static unsigned linesize = 0;
141 static int margc;
142 static char **margv = NULL;
143 static unsigned margvlen = 0;
144 static int doing_rc = 0;   /* .telnetrc file is being read and processed */
145 
146 static void
147 Close(int *fd)
148 {
149 	if (*fd != -1) {
150 		(void) close(*fd);
151 		*fd = -1;
152 	}
153 }
154 
155 static void
156 Free(char **p)
157 {
158 	if (*p != NULL) {
159 		free(*p);
160 		*p = NULL;
161 	}
162 }
163 
164 static void
165 FreeHostnameList(char *list[])
166 {
167 	unsigned i;
168 	for (i = 0; i <= MAXMAX_GATEWAY && list[i] != NULL; i++)
169 		Free(&list[i]);
170 }
171 
172 #define	MARGV_CHUNK_SIZE 8
173 
174 static void
175 set_argv(str)
176 char *str;
177 {
178 	if (margc == margvlen) {
179 		char **newmargv;
180 
181 		margvlen += MARGV_CHUNK_SIZE;
182 
183 		if ((newmargv = realloc(margv, margvlen * sizeof (char *)))
184 			== NULL)
185 			ExitString("telnet: no space for arguments",
186 				EXIT_FAILURE);
187 
188 		margv = newmargv;
189 	}
190 
191 	margv[margc] = str;
192 	if (str != NULL)
193 		margc++;
194 }
195 
196 static void
197 makeargv()
198 {
199 	char *cp, *cp2, c;
200 	boolean_t shellcmd = B_FALSE;
201 
202 	margc = 0;
203 	cp = line;
204 	if (*cp == '!') {		/* Special case shell escape */
205 		set_argv("!");		/* No room in string to get this */
206 		cp++;
207 		shellcmd = B_TRUE;
208 	}
209 	while ((c = *cp) != '\0') {
210 		register int inquote = 0;
211 		while (isspace(c))
212 			c = *++cp;
213 		if (c == '\0')
214 			break;
215 		set_argv(cp);
216 		/*
217 		 * For the shell escape, put the rest of the line, less
218 		 * leading space, into a single argument, breaking out from
219 		 * the loop to prevent the rest of the line being split up
220 		 * into smaller arguments.
221 		 */
222 		if (shellcmd)
223 			break;
224 		for (cp2 = cp; c != '\0'; c = *++cp) {
225 			if (inquote) {
226 				if (c == inquote) {
227 					inquote = 0;
228 					continue;
229 				}
230 			} else {
231 				if (c == '\\') {
232 					if ((c = *++cp) == '\0')
233 						break;
234 				} else if (c == '"') {
235 					inquote = '"';
236 					continue;
237 				} else if (c == '\'') {
238 					inquote = '\'';
239 					continue;
240 				} else if (isspace(c))
241 					break;
242 			}
243 			*cp2++ = c;
244 		}
245 		*cp2 = '\0';
246 		if (c == '\0')
247 			break;
248 		cp++;
249 	}
250 	set_argv((char *)NULL);
251 }
252 
253 /*
254  * Make a character string into a number.
255  *
256  * Todo:  1.  Could take random integers (12, 0x12, 012, 0b1).
257  */
258 
259 	static int
260 special(s)
261 	register char *s;
262 {
263 	register char c;
264 	char b;
265 
266 	switch (*s) {
267 	case '^':
268 		b = *++s;
269 		if (b == '?') {
270 		    c = b | 0x40;		/* DEL */
271 		} else {
272 		    c = b & 0x1f;
273 		}
274 		break;
275 	default:
276 		c = *s;
277 		break;
278 	}
279 	return (c);
280 }
281 
282 /*
283  * Construct a control character sequence
284  * for a special character.
285  */
286 	static char *
287 control(c)
288 	register cc_t c;
289 {
290 	static char buf[5];
291 	/*
292 	 * The only way I could get the Sun 3.5 compiler
293 	 * to shut up about
294 	 *	if ((unsigned int)c >= 0x80)
295 	 * was to assign "c" to an unsigned int variable...
296 	 * Arggg....
297 	 */
298 	register unsigned int uic = (unsigned int)c;
299 
300 	if (uic == 0x7f)
301 		return ("^?");
302 	if (c == (cc_t)_POSIX_VDISABLE) {
303 		return ("off");
304 	}
305 	if (uic >= 0x80) {
306 		buf[0] = '\\';
307 		buf[1] = ((c>>6)&07) + '0';
308 		buf[2] = ((c>>3)&07) + '0';
309 		buf[3] = (c&07) + '0';
310 		buf[4] = 0;
311 	} else if (uic >= 0x20) {
312 		buf[0] = c;
313 		buf[1] = 0;
314 	} else {
315 		buf[0] = '^';
316 		buf[1] = '@'+c;
317 		buf[2] = 0;
318 	}
319 	return (buf);
320 }
321 
322 /*
323  * Same as control() except that its only used for escape handling, which uses
324  * _POSIX_VDISABLE differently and is aided by the use of the state variable
325  * escape_valid.
326  */
327 	static char *
328 esc_control(c)
329 	register cc_t c;
330 {
331 	static char buf[5];
332 	/*
333 	 * The only way I could get the Sun 3.5 compiler
334 	 * to shut up about
335 	 *	if ((unsigned int)c >= 0x80)
336 	 * was to assign "c" to an unsigned int variable...
337 	 * Arggg....
338 	 */
339 	register unsigned int uic = (unsigned int)c;
340 
341 	if (escape_valid == B_FALSE)
342 		return ("off");
343 	if (uic == 0x7f)
344 		return ("^?");
345 	if (uic >= 0x80) {
346 		buf[0] = '\\';
347 		buf[1] = ((c>>6)&07) + '0';
348 		buf[2] = ((c>>3)&07) + '0';
349 		buf[3] = (c&07) + '0';
350 		buf[4] = 0;
351 	} else if (uic >= 0x20) {
352 		buf[0] = c;
353 		buf[1] = 0;
354 	} else {
355 		buf[0] = '^';
356 		buf[1] = '@'+c;
357 		buf[2] = 0;
358 	}
359 	return (buf);
360 }
361 
362 /*
363  *	The following are data structures and routines for
364  *	the "send" command.
365  *
366  */
367 
368 struct sendlist {
369 	char	*name;		/* How user refers to it (case independent) */
370 	char	*help;		/* Help information (0 ==> no help) */
371 	int	needconnect;	/* Need to be connected */
372 	int	narg;		/* Number of arguments */
373 	int	(*handler)();	/* Routine to perform (for special ops) */
374 	int	nbyte;		/* Number of bytes to send this command */
375 	int	what;		/* Character to be sent (<0 ==> special) */
376 };
377 
378 
379 static int send_esc(void);
380 static int send_help(void);
381 static int send_docmd(char *);
382 static int send_dontcmd(char *);
383 static int send_willcmd(char *);
384 static int send_wontcmd(char *);
385 
386 static struct sendlist Sendlist[] = {
387 	{ "ao",	"Send Telnet Abort output",		1, 0, 0, 2, AO },
388 	{ "ayt", "Send Telnet 'Are You There'",		1, 0, 0, 2, AYT },
389 	{ "b", 0,					1, 0, 0, 2, BREAK },
390 	{ "br", 0,					1, 0, 0, 2, BREAK },
391 	{ "break", 0,					1, 0, 0, 2, BREAK },
392 	{ "brk", "Send Telnet Break",			1, 0, 0, 2, BREAK },
393 	{ "ec",	"Send Telnet Erase Character",		1, 0, 0, 2, EC },
394 	{ "el",	"Send Telnet Erase Line",		1, 0, 0, 2, EL },
395 	{ "escape", "Send current escape character",	1, 0, send_esc, 1, 0 },
396 	{ "ga",	"Send Telnet 'Go Ahead' sequence",	1, 0, 0, 2, GA },
397 	{ "ip",	"Send Telnet Interrupt Process",	1, 0, 0, 2, IP },
398 	{ "intp", 0,					1, 0, 0, 2, IP },
399 	{ "interrupt", 0,				1, 0, 0, 2, IP },
400 	{ "intr",	0,				1, 0, 0, 2, IP },
401 	{ "nop", "Send Telnet 'No operation'",		1, 0, 0, 2, NOP },
402 	{ "eor", "Send Telnet 'End of Record'",		1, 0, 0, 2, EOR },
403 	{ "abort", "Send Telnet 'Abort Process'",	1, 0, 0, 2, ABORT },
404 	{ "susp", "Send Telnet 'Suspend Process'",	1, 0, 0, 2, SUSP },
405 	{ "eof", "Send Telnet End of File Character",	1, 0, 0, 2, xEOF },
406 	{ "synch", "Perform Telnet 'Synch operation'",	1, 0, dosynch, 2, 0 },
407 	{ "getstatus", "Send request for STATUS", 1, 0, get_status, 6, 0 },
408 	{ "?",	"Display send options",			0, 0, send_help, 0, 0 },
409 	{ "help",	0,				0, 0, send_help, 0, 0 },
410 	{ "do",	0,				0, 1, send_docmd, 3, 0 },
411 	{ "dont", 0,				0, 1, send_dontcmd, 3, 0 },
412 	{ "will", 0,				0, 1, send_willcmd, 3, 0 },
413 	{ "wont", 0,				0, 1, send_wontcmd, 3, 0 },
414 	{ 0 }
415 };
416 
417 #define	GETSEND(name) ((struct sendlist *)genget(name, (char **)Sendlist, \
418 				sizeof (struct sendlist)))
419 
420 static int
421 sendcmd(argc, argv)
422 	int  argc;
423 	char **argv;
424 {
425 	int count;	/* how many bytes we are going to need to send */
426 	int i;
427 	struct sendlist *s;	/* pointer to current command */
428 	int success = 0;
429 	int needconnect = 0;
430 
431 	if (argc < 2) {
432 		(void) printf(
433 		    "need at least one argument for 'send' command\n");
434 		(void) printf("'send ?' for help\n");
435 		return (0);
436 	}
437 	/*
438 	 * First, validate all the send arguments.
439 	 * In addition, we see how much space we are going to need, and
440 	 * whether or not we will be doing a "SYNCH" operation (which
441 	 * flushes the network queue).
442 	 */
443 	count = 0;
444 	for (i = 1; i < argc; i++) {
445 		s = GETSEND(argv[i]);
446 		if (s == 0) {
447 			(void) printf("Unknown send argument '%s'\n'send ?' "
448 			    "for help.\n", argv[i]);
449 			return (0);
450 		} else if (Ambiguous(s)) {
451 			(void) printf("Ambiguous send argument '%s'\n'send ?' "
452 			    "for help.\n", argv[i]);
453 			return (0);
454 		}
455 		if (i + s->narg >= argc) {
456 			(void) fprintf(stderr,
457 			    "Need %d argument%s to 'send %s' "
458 			    "command.  'send %s ?' for help.\n",
459 			    s->narg, s->narg == 1 ? "" : "s", s->name, s->name);
460 			return (0);
461 		}
462 		count += s->nbyte;
463 		if (s->handler == send_help) {
464 			(void) send_help();
465 			return (0);
466 		}
467 
468 		i += s->narg;
469 		needconnect += s->needconnect;
470 	}
471 	if (!connected && needconnect) {
472 		(void) printf("?Need to be connected first.\n");
473 		(void) printf("'send ?' for help\n");
474 		return (0);
475 	}
476 	/* Now, do we have enough room? */
477 	if (NETROOM() < count) {
478 		(void) printf("There is not enough room in the buffer "
479 		    "TO the network\n");
480 		(void) printf(
481 		    "to process your request.  Nothing will be done.\n");
482 		(void) printf("('send synch' will throw away most "
483 		    "data in the network\n");
484 		(void) printf("buffer, if this might help.)\n");
485 		return (0);
486 	}
487 	/* OK, they are all OK, now go through again and actually send */
488 	count = 0;
489 	for (i = 1; i < argc; i++) {
490 		if ((s = GETSEND(argv[i])) == 0) {
491 			(void) fprintf(stderr,
492 			    "Telnet 'send' error - argument disappeared!\n");
493 			(void) quit();
494 			/*NOTREACHED*/
495 		}
496 		if (s->handler) {
497 			count++;
498 			success += (*s->handler)((s->narg > 0) ? argv[i+1] : 0,
499 			    (s->narg > 1) ? argv[i+2] : 0);
500 			i += s->narg;
501 		} else {
502 			NET2ADD(IAC, s->what);
503 			printoption("SENT", IAC, s->what);
504 		}
505 	}
506 	return (count == success);
507 }
508 
509 static int
510 send_esc()
511 {
512 	NETADD(escape);
513 	return (1);
514 }
515 
516 static int
517 send_docmd(name)
518 	char *name;
519 {
520 	return (send_tncmd(send_do, "do", name));
521 }
522 
523 static int
524 send_dontcmd(name)
525 	char *name;
526 {
527 	return (send_tncmd(send_dont, "dont", name));
528 }
529 
530 static int
531 send_willcmd(name)
532 	char *name;
533 {
534 	return (send_tncmd(send_will, "will", name));
535 }
536 
537 static int
538 send_wontcmd(name)
539 	char *name;
540 {
541 	return (send_tncmd(send_wont, "wont", name));
542 }
543 
544 int
545 send_tncmd(func, cmd, name)
546 	void	(*func)();
547 	char	*cmd, *name;
548 {
549 	char **cpp;
550 	extern char *telopts[];
551 	register int val = 0;
552 
553 	if (isprefix(name, "help") || isprefix(name, "?")) {
554 		register int col, len;
555 
556 		(void) printf("Usage: send %s <value|option>\n", cmd);
557 		(void) printf("\"value\" must be from 0 to 255\n");
558 		(void) printf("Valid options are:\n\t");
559 
560 		col = 8;
561 		for (cpp = telopts; *cpp; cpp++) {
562 			len = strlen(*cpp) + 3;
563 			if (col + len > 65) {
564 				(void) printf("\n\t");
565 				col = 8;
566 			}
567 			(void) printf(" \"%s\"", *cpp);
568 			col += len;
569 		}
570 		(void) printf("\n");
571 		return (0);
572 	}
573 	cpp = (char **)genget(name, telopts, sizeof (char *));
574 	if (Ambiguous(cpp)) {
575 		(void) fprintf(stderr,
576 		    "'%s': ambiguous argument ('send %s ?' for help).\n",
577 		    name, cmd);
578 		return (0);
579 	}
580 	if (cpp) {
581 		val = cpp - telopts;
582 	} else {
583 		register char *cp = name;
584 
585 		while (*cp >= '0' && *cp <= '9') {
586 			val *= 10;
587 			val += *cp - '0';
588 			cp++;
589 		}
590 		if (*cp != 0) {
591 			(void) fprintf(stderr,
592 			    "'%s': unknown argument ('send %s ?' for help).\n",
593 			    name, cmd);
594 			return (0);
595 		} else if (val < 0 || val > 255) {
596 			(void) fprintf(stderr,
597 			    "'%s': bad value ('send %s ?' for help).\n",
598 			    name, cmd);
599 			return (0);
600 		}
601 	}
602 	if (!connected) {
603 		(void) printf("?Need to be connected first.\n");
604 		return (0);
605 	}
606 	(*func)(val, 1);
607 	return (1);
608 }
609 
610 static int
611 send_help()
612 {
613 	struct sendlist *s;	/* pointer to current command */
614 	for (s = Sendlist; s->name; s++) {
615 		if (s->help)
616 			(void) printf("%-15s %s\n", s->name, s->help);
617 	}
618 	return (0);
619 }
620 
621 /*
622  * The following are the routines and data structures referred
623  * to by the arguments to the "toggle" command.
624  */
625 
626 static int
627 lclchars()
628 {
629 	donelclchars = 1;
630 	return (1);
631 }
632 
633 static int
634 togdebug()
635 {
636 	if (net > 0 &&
637 	    (SetSockOpt(net, SOL_SOCKET, SO_DEBUG, debug)) < 0) {
638 		perror("setsockopt (SO_DEBUG)");
639 	}
640 	return (1);
641 }
642 
643 
644 static int
645 togcrlf()
646 {
647 	if (crlf) {
648 		(void) printf(
649 		    "Will send carriage returns as telnet <CR><LF>.\n");
650 	} else {
651 		(void) printf(
652 		    "Will send carriage returns as telnet <CR><NUL>.\n");
653 	}
654 	return (1);
655 }
656 
657 static int binmode;
658 
659 static int
660 togbinary(val)
661 	int val;
662 {
663 	donebinarytoggle = 1;
664 
665 	if (val >= 0) {
666 		binmode = val;
667 	} else {
668 		if (my_want_state_is_will(TELOPT_BINARY) &&
669 		    my_want_state_is_do(TELOPT_BINARY)) {
670 			binmode = 1;
671 		} else if (my_want_state_is_wont(TELOPT_BINARY) &&
672 		    my_want_state_is_dont(TELOPT_BINARY)) {
673 			binmode = 0;
674 		}
675 		val = binmode ? 0 : 1;
676 	}
677 
678 	if (val == 1) {
679 		if (my_want_state_is_will(TELOPT_BINARY) &&
680 		    my_want_state_is_do(TELOPT_BINARY)) {
681 			(void) printf("Already operating in binary mode "
682 			    "with remote host.\n");
683 		} else {
684 			(void) printf(
685 			    "Negotiating binary mode with remote host.\n");
686 			tel_enter_binary(3);
687 		}
688 	} else {
689 		if (my_want_state_is_wont(TELOPT_BINARY) &&
690 		    my_want_state_is_dont(TELOPT_BINARY)) {
691 			(void) printf("Already in network ascii mode "
692 			    "with remote host.\n");
693 		} else {
694 			(void) printf("Negotiating network ascii mode "
695 			    "with remote host.\n");
696 			tel_leave_binary(3);
697 		}
698 	}
699 	return (1);
700 }
701 
702 static int
703 togrbinary(val)
704 	int val;
705 {
706 	donebinarytoggle = 1;
707 
708 	if (val == -1)
709 		val = my_want_state_is_do(TELOPT_BINARY) ? 0 : 1;
710 
711 	if (val == 1) {
712 		if (my_want_state_is_do(TELOPT_BINARY)) {
713 			(void) printf("Already receiving in binary mode.\n");
714 		} else {
715 			(void) printf("Negotiating binary mode on input.\n");
716 			tel_enter_binary(1);
717 		}
718 	} else {
719 		if (my_want_state_is_dont(TELOPT_BINARY)) {
720 			(void) printf(
721 			    "Already receiving in network ascii mode.\n");
722 		} else {
723 			(void) printf(
724 			    "Negotiating network ascii mode on input.\n");
725 			    tel_leave_binary(1);
726 		}
727 	}
728 	return (1);
729 }
730 
731 static int
732 togxbinary(val)
733 	int val;
734 {
735 	donebinarytoggle = 1;
736 
737 	if (val == -1)
738 		val = my_want_state_is_will(TELOPT_BINARY) ? 0 : 1;
739 
740 	if (val == 1) {
741 		if (my_want_state_is_will(TELOPT_BINARY)) {
742 			(void) printf("Already transmitting in binary mode.\n");
743 		} else {
744 			(void) printf("Negotiating binary mode on output.\n");
745 			tel_enter_binary(2);
746 		}
747 	} else {
748 		if (my_want_state_is_wont(TELOPT_BINARY)) {
749 			(void) printf(
750 			    "Already transmitting in network ascii mode.\n");
751 		} else {
752 			(void) printf(
753 			    "Negotiating network ascii mode on output.\n");
754 			tel_leave_binary(2);
755 		}
756 	}
757 	return (1);
758 }
759 
760 
761 static int togglehelp(void);
762 extern int auth_togdebug(int);
763 
764 struct togglelist {
765 	char	*name;		/* name of toggle */
766 	char	*help;		/* help message */
767 	int	(*handler)();	/* routine to do actual setting */
768 	int	*variable;
769 	char	*actionexplanation;
770 };
771 
772 static struct togglelist Togglelist[] = {
773 	{ "autoflush",
774 	"flushing of output when sending interrupt characters",
775 	    0,
776 		&autoflush,
777 		    "flush output when sending interrupt characters" },
778 	{ "autosynch",
779 	"automatic sending of interrupt characters in urgent mode",
780 	    0,
781 		&autosynch,
782 		    "send interrupt characters in urgent mode" },
783 	{ "autologin",
784 	"automatic sending of login and/or authentication info",
785 	    0,
786 		&autologin,
787 		    "send login name and/or authentication information" },
788 	{ "authdebug",
789 	"authentication debugging",
790 	    auth_togdebug,
791 		0,
792 		    "print authentication debugging information" },
793 	{ "autoencrypt",
794 	"automatic encryption of data stream",
795 	    EncryptAutoEnc,
796 		0,
797 		    "automatically encrypt output" },
798 	{ "autodecrypt",
799 	"automatic decryption of data stream",
800 	    EncryptAutoDec,
801 		0,
802 		    "automatically decrypt input" },
803 	{ "verbose_encrypt",
804 	"verbose encryption output",
805 	    EncryptVerbose,
806 		0,
807 		    "print verbose encryption output" },
808 	{ "encdebug",
809 	"encryption debugging",
810 	    EncryptDebug,
811 		0,
812 		    "print encryption debugging information" },
813 	{ "skiprc",
814 	"don't read ~/.telnetrc file",
815 	    0,
816 		&skiprc,
817 		    "skip reading of ~/.telnetrc file" },
818 	{ "binary",
819 	"sending and receiving of binary data",
820 	    togbinary,
821 		0,
822 		    0 },
823 	{ "inbinary",
824 	"receiving of binary data",
825 	    togrbinary,
826 		0,
827 		    0 },
828 	{ "outbinary",
829 	"sending of binary data",
830 	    togxbinary,
831 		0,
832 		    0 },
833 	{ "crlf",
834 	"sending carriage returns as telnet <CR><LF>",
835 	    togcrlf,
836 		&crlf,
837 		    0 },
838 	{ "crmod",
839 	"mapping of received carriage returns",
840 	    0,
841 		&crmod,
842 		    "map carriage return on output" },
843 	{ "localchars",
844 	"local recognition of certain control characters",
845 	    lclchars,
846 		&localchars,
847 		    "recognize certain control characters" },
848 	{ " ", "", 0 },		/* empty line */
849 	{ "debug",
850 	"debugging",
851 	    togdebug,
852 		&debug,
853 		    "turn on socket level debugging" },
854 	{ "netdata",
855 	"printing of hexadecimal network data (debugging)",
856 	    0,
857 		&netdata,
858 		    "print hexadecimal representation of network traffic" },
859 	{ "prettydump",
860 	"output of \"netdata\" to user readable format (debugging)",
861 	    0,
862 		&prettydump,
863 		    "print user readable output for \"netdata\"" },
864 	{ "options",
865 	"viewing of options processing (debugging)",
866 	    0,
867 		&showoptions,
868 		    "show option processing" },
869 	{ "termdata",
870 	"(debugging) toggle printing of hexadecimal terminal data",
871 	    0,
872 		&termdata,
873 		    "print hexadecimal representation of terminal traffic" },
874 	{ "?",
875 	0,
876 	    togglehelp },
877 	{ "help",
878 	0,
879 	    togglehelp },
880 	{ 0 }
881 };
882 
883 static int
884 togglehelp()
885 {
886 	struct togglelist *c;
887 
888 	for (c = Togglelist; c->name; c++) {
889 		if (c->help) {
890 			if (*c->help)
891 				(void) printf(
892 					"%-15s toggle %s\n", c->name, c->help);
893 			else
894 				(void) printf("\n");
895 		}
896 	}
897 	(void) printf("\n");
898 	(void) printf("%-15s %s\n", "?", "display help information");
899 	return (0);
900 }
901 
902 static void
903 settogglehelp(set)
904 	int set;
905 {
906 	struct togglelist *c;
907 
908 	for (c = Togglelist; c->name; c++) {
909 		if (c->help) {
910 			if (*c->help)
911 				(void) printf("%-15s %s %s\n", c->name,
912 				    set ? "enable" : "disable", c->help);
913 			else
914 				(void) printf("\n");
915 		}
916 	}
917 }
918 
919 #define	GETTOGGLE(name) (struct togglelist *) \
920 		genget(name, (char **)Togglelist, sizeof (struct togglelist))
921 
922 static int
923 toggle(argc, argv)
924 	int  argc;
925 	char *argv[];
926 {
927 	int retval = 1;
928 	char *name;
929 	struct togglelist *c;
930 
931 	if (argc < 2) {
932 		(void) fprintf(stderr,
933 		    "Need an argument to 'toggle' command.  "
934 		    "'toggle ?' for help.\n");
935 		return (0);
936 	}
937 	argc--;
938 	argv++;
939 	while (argc--) {
940 		name = *argv++;
941 		c = GETTOGGLE(name);
942 		if (Ambiguous(c)) {
943 			(void) fprintf(stderr, "'%s': ambiguous argument "
944 			    "('toggle ?' for help).\n", name);
945 			return (0);
946 		} else if (c == 0) {
947 			(void) fprintf(stderr, "'%s': unknown argument "
948 			    "('toggle ?' for help).\n", name);
949 			return (0);
950 		} else {
951 			if (c->variable) {
952 				*c->variable = !*c->variable;	/* invert it */
953 				if (c->actionexplanation) {
954 			(void) printf("%s %s.\n",
955 				*c->variable ? "Will" : "Won't",
956 							c->actionexplanation);
957 				}
958 			}
959 			if (c->handler) {
960 				retval &= (*c->handler)(-1);
961 			}
962 		}
963 	}
964 	return (retval);
965 }
966 
967 /*
968  * The following perform the "set" command.
969  */
970 
971 #ifdef	USE_TERMIO
972 struct termio new_tc = { 0 };
973 #endif
974 
975 struct setlist {
976 	char *name;		/* name */
977 	char *help;		/* help information */
978 	void (*handler)();
979 	cc_t *charp;		/* where it is located at */
980 };
981 
982 static struct setlist Setlist[] = {
983 #ifdef	KLUDGELINEMODE
984 	{ "echo",  "character to toggle local echoing on/off", 0, &echoc },
985 #endif
986 	{ "escape", "character to escape back to telnet command mode", 0,
987 	    &escape },
988 	{ "rlogin", "rlogin escape character", 0, &rlogin },
989 	{ "tracefile", "file to write trace information to", SetNetTrace,
990 	    (cc_t *)NetTraceFile},
991 	{ " ", "" },
992 	{ " ", "The following need 'localchars' to be toggled true", 0, 0 },
993 	{ "flushoutput", "character to cause an Abort Output", 0,
994 	    termFlushCharp },
995 	{ "interrupt", "character to cause an Interrupt Process", 0,
996 	    termIntCharp },
997 	{ "quit", "character to cause an Abort process", 0, termQuitCharp },
998 	{ "eof", "character to cause an EOF ", 0, termEofCharp },
999 	{ " ", "" },
1000 	{ " ", "The following are for local editing in linemode", 0, 0 },
1001 	{ "erase", "character to use to erase a character", 0, termEraseCharp },
1002 	{ "kill", "character to use to erase a line", 0, termKillCharp },
1003 	{ "lnext", "character to use for literal next", 0,
1004 	    termLiteralNextCharp },
1005 	{ "susp", "character to cause a Suspend Process", 0, termSuspCharp },
1006 	{ "reprint", "character to use for line reprint", 0, termRprntCharp },
1007 	{ "worderase", "character to use to erase a word", 0, termWerasCharp },
1008 	{ "start",	"character to use for XON", 0, termStartCharp },
1009 	{ "stop",	"character to use for XOFF", 0, termStopCharp },
1010 	{ "forw1",	"alternate end of line character", 0, termForw1Charp },
1011 	{ "forw2",	"alternate end of line character", 0, termForw2Charp },
1012 	{ "ayt",	"alternate AYT character", 0, termAytCharp },
1013 	{ 0 }
1014 };
1015 
1016 static struct setlist *
1017 getset(name)
1018     char *name;
1019 {
1020 	return ((struct setlist *)
1021 	    genget(name, (char **)Setlist, sizeof (struct setlist)));
1022 }
1023 
1024     void
1025 set_escape_char(s)
1026     char *s;
1027 {
1028 	if (rlogin != _POSIX_VDISABLE) {
1029 		rlogin = (s && *s) ? special(s) : _POSIX_VDISABLE;
1030 		(void) printf("Telnet rlogin escape character is '%s'.\n",
1031 					control(rlogin));
1032 	} else {
1033 		escape = (s && *s) ? special(s) : _POSIX_VDISABLE;
1034 		(void) printf("Telnet escape character is '%s'.\n",
1035 		    esc_control(escape));
1036 	}
1037 }
1038 
1039 static int
1040 setcmd(argc, argv)
1041 	int  argc;
1042 	char *argv[];
1043 {
1044 	int value;
1045 	struct setlist *ct;
1046 	struct togglelist *c;
1047 
1048 	if (argc < 2 || argc > 3) {
1049 		(void) printf(
1050 			"Format is 'set Name Value'\n'set ?' for help.\n");
1051 		return (0);
1052 	}
1053 	if ((argc == 2) &&
1054 	    (isprefix(argv[1], "?") || isprefix(argv[1], "help"))) {
1055 		for (ct = Setlist; ct->name; ct++)
1056 			(void) printf("%-15s %s\n", ct->name, ct->help);
1057 		(void) printf("\n");
1058 		settogglehelp(1);
1059 		(void) printf("%-15s %s\n", "?", "display help information");
1060 		return (0);
1061 	}
1062 
1063 	ct = getset(argv[1]);
1064 	if (ct == 0) {
1065 		c = GETTOGGLE(argv[1]);
1066 		if (c == 0) {
1067 			(void) fprintf(stderr, "'%s': unknown argument "
1068 			    "('set ?' for help).\n", argv[1]);
1069 			return (0);
1070 		} else if (Ambiguous(c)) {
1071 			(void) fprintf(stderr, "'%s': ambiguous argument "
1072 			    "('set ?' for help).\n", argv[1]);
1073 			return (0);
1074 		}
1075 		if (c->variable) {
1076 			if ((argc == 2) || (strcmp("on", argv[2]) == 0))
1077 				*c->variable = 1;
1078 			else if (strcmp("off", argv[2]) == 0)
1079 				*c->variable = 0;
1080 			else {
1081 				(void) printf(
1082 				    "Format is 'set togglename [on|off]'\n"
1083 				    "'set ?' for help.\n");
1084 				return (0);
1085 			}
1086 			if (c->actionexplanation) {
1087 				(void) printf("%s %s.\n",
1088 				    *c->variable? "Will" : "Won't",
1089 				    c->actionexplanation);
1090 			}
1091 		}
1092 		if (c->handler)
1093 			(*c->handler)(1);
1094 	} else if (argc != 3) {
1095 		(void) printf(
1096 			"Format is 'set Name Value'\n'set ?' for help.\n");
1097 		return (0);
1098 	} else if (Ambiguous(ct)) {
1099 		(void) fprintf(stderr,
1100 		    "'%s': ambiguous argument ('set ?' for help).\n", argv[1]);
1101 		return (0);
1102 	} else if (ct->handler) {
1103 		(*ct->handler)(argv[2]);
1104 		(void) printf(
1105 		    "%s set to \"%s\".\n", ct->name, (char *)ct->charp);
1106 	} else {
1107 		if (strcmp("off", argv[2])) {
1108 			value = special(argv[2]);
1109 		} else {
1110 			value = _POSIX_VDISABLE;
1111 		}
1112 		*(ct->charp) = (cc_t)value;
1113 		(void) printf("%s character is '%s'.\n", ct->name,
1114 		    control(*(ct->charp)));
1115 	}
1116 	slc_check();
1117 	return (1);
1118 }
1119 
1120 static int
1121 unsetcmd(argc, argv)
1122 	int  argc;
1123 	char *argv[];
1124 {
1125 	struct setlist *ct;
1126 	struct togglelist *c;
1127 	register char *name;
1128 
1129 	if (argc < 2) {
1130 		(void) fprintf(stderr, "Need an argument to 'unset' command.  "
1131 		    "'unset ?' for help.\n");
1132 		return (0);
1133 	}
1134 	if (isprefix(argv[1], "?") || isprefix(argv[1], "help")) {
1135 		for (ct = Setlist; ct->name; ct++)
1136 			(void) printf("%-15s %s\n", ct->name, ct->help);
1137 		(void) printf("\n");
1138 		settogglehelp(0);
1139 		(void) printf("%-15s %s\n", "?", "display help information");
1140 		return (0);
1141 	}
1142 
1143 	argc--;
1144 	argv++;
1145 	while (argc--) {
1146 		name = *argv++;
1147 		ct = getset(name);
1148 		if (ct == 0) {
1149 			c = GETTOGGLE(name);
1150 			if (c == 0) {
1151 				(void) fprintf(stderr, "'%s': unknown argument "
1152 				    "('unset ?' for help).\n", name);
1153 				return (0);
1154 			} else if (Ambiguous(c)) {
1155 				(void) fprintf(stderr,
1156 				    "'%s': ambiguous argument "
1157 				    "('unset ?' for help).\n", name);
1158 				return (0);
1159 			}
1160 			if (c->variable) {
1161 				*c->variable = 0;
1162 				if (c->actionexplanation) {
1163 					(void) printf("%s %s.\n",
1164 					    *c->variable? "Will" : "Won't",
1165 					    c->actionexplanation);
1166 				}
1167 			}
1168 			if (c->handler)
1169 				(*c->handler)(0);
1170 		} else if (Ambiguous(ct)) {
1171 			(void) fprintf(stderr, "'%s': ambiguous argument "
1172 			    "('unset ?' for help).\n", name);
1173 			return (0);
1174 		} else if (ct->handler) {
1175 			(*ct->handler)(0);
1176 			(void) printf("%s reset to \"%s\".\n", ct->name,
1177 			    (char *)ct->charp);
1178 		} else {
1179 			*(ct->charp) = _POSIX_VDISABLE;
1180 			(void) printf("%s character is '%s'.\n", ct->name,
1181 			    control(*(ct->charp)));
1182 		}
1183 	}
1184 	return (1);
1185 }
1186 
1187 /*
1188  * The following are the data structures and routines for the
1189  * 'mode' command.
1190  */
1191 extern int reqd_linemode;
1192 
1193 #ifdef	KLUDGELINEMODE
1194 extern int kludgelinemode;
1195 
1196 static int
1197 dokludgemode()
1198 {
1199 	kludgelinemode = 1;
1200 	send_wont(TELOPT_LINEMODE, 1);
1201 	send_dont(TELOPT_SGA, 1);
1202 	send_dont(TELOPT_ECHO, 1);
1203 	/*
1204 	 * If processing the .telnetrc file, keep track of linemode and/or
1205 	 * kludgelinemode requests which are processed before initial option
1206 	 * negotiations occur.
1207 	 */
1208 	if (doing_rc)
1209 		reqd_linemode = 1;
1210 	return (1);
1211 }
1212 #endif
1213 
1214 static int
1215 dolinemode()
1216 {
1217 #ifdef	KLUDGELINEMODE
1218 	if (kludgelinemode)
1219 		send_dont(TELOPT_SGA, 1);
1220 #endif
1221 	send_will(TELOPT_LINEMODE, 1);
1222 	send_dont(TELOPT_ECHO, 1);
1223 
1224 	/*
1225 	 * If processing the .telnetrc file, keep track of linemode and/or
1226 	 * kludgelinemode requests which are processed before initial option
1227 	 * negotiations occur.
1228 	 */
1229 	if (doing_rc)
1230 		reqd_linemode = 1;
1231 	return (1);
1232 }
1233 
1234 static int
1235 docharmode()
1236 {
1237 #ifdef	KLUDGELINEMODE
1238 	if (kludgelinemode)
1239 		send_do(TELOPT_SGA, 1);
1240 	else
1241 #endif
1242 		send_wont(TELOPT_LINEMODE, 1);
1243 	send_do(TELOPT_ECHO, 1);
1244 	reqd_linemode = 0;
1245 	return (1);
1246 }
1247 
1248 static int
1249 dolmmode(bit, on)
1250 	int bit, on;
1251 {
1252 	unsigned char c;
1253 	extern int linemode;
1254 
1255 	if (my_want_state_is_wont(TELOPT_LINEMODE)) {
1256 		(void) printf("?Need to have LINEMODE option enabled first.\n");
1257 		(void) printf("'mode ?' for help.\n");
1258 		return (0);
1259 	}
1260 
1261 	if (on)
1262 		c = (linemode | bit);
1263 	else
1264 		c = (linemode & ~bit);
1265 	lm_mode(&c, 1, 1);
1266 	return (1);
1267 }
1268 
1269 static int
1270 setmode(bit)
1271 {
1272 	return (dolmmode(bit, 1));
1273 }
1274 
1275 static int
1276 clearmode(bit)
1277 {
1278 	return (dolmmode(bit, 0));
1279 }
1280 
1281 struct modelist {
1282 	char	*name;		/* command name */
1283 	char	*help;		/* help string */
1284 	int	(*handler)();	/* routine which executes command */
1285 	int	needconnect;	/* Do we need to be connected to execute? */
1286 	int	arg1;
1287 };
1288 
1289 static int modehelp();
1290 
1291 static struct modelist ModeList[] = {
1292 	{ "character", "Disable LINEMODE option",	docharmode, 1 },
1293 #ifdef	KLUDGELINEMODE
1294 	{ "",	"(or disable obsolete line-by-line mode)", 0 },
1295 #endif
1296 	{ "line",	"Enable LINEMODE option",	dolinemode, 1 },
1297 #ifdef	KLUDGELINEMODE
1298 	{ "",	"(or enable obsolete line-by-line mode)", 0 },
1299 #endif
1300 	{ "", "", 0 },
1301 	{ "",	"These require the LINEMODE option to be enabled", 0 },
1302 	{ "isig",	"Enable signal trapping", setmode, 1, MODE_TRAPSIG },
1303 	{ "+isig",	0,			setmode, 1, MODE_TRAPSIG },
1304 	{ "-isig",	"Disable signal trapping", clearmode, 1, MODE_TRAPSIG },
1305 	{ "edit",	"Enable character editing",	setmode, 1, MODE_EDIT },
1306 	{ "+edit",	0,			setmode, 1, MODE_EDIT },
1307 	{ "-edit",	"Disable character editing", clearmode, 1, MODE_EDIT },
1308 	{ "softtabs",	"Enable tab expansion",	setmode, 1, MODE_SOFT_TAB },
1309 	{ "+softtabs",	0,			setmode, 1, MODE_SOFT_TAB },
1310 	{ "-softtabs",	"Disable tab expansion",
1311 						clearmode, 1, MODE_SOFT_TAB },
1312 	{ "litecho",	"Enable literal character echo",
1313 						setmode, 1, MODE_LIT_ECHO },
1314 	{ "+litecho",	0,			setmode, 1, MODE_LIT_ECHO },
1315 	{ "-litecho",	"Disable literal character echo", clearmode, 1,
1316 		MODE_LIT_ECHO },
1317 	{ "help",	0,			modehelp, 0 },
1318 #ifdef	KLUDGELINEMODE
1319 	{ "kludgeline", 0,				dokludgemode, 1 },
1320 #endif
1321 	{ "", "", 0 },
1322 	{ "?",	"Print help information",	modehelp, 0 },
1323 	{ 0 },
1324 };
1325 
1326 
1327 static int
1328 modehelp()
1329 {
1330 	struct modelist *mt;
1331 
1332 	(void) printf("format is:  'mode Mode', where 'Mode' is one of:\n\n");
1333 	for (mt = ModeList; mt->name; mt++) {
1334 		if (mt->help) {
1335 			if (*mt->help)
1336 				(void) printf("%-15s %s\n", mt->name, mt->help);
1337 			else
1338 				(void) printf("\n");
1339 		}
1340 	}
1341 	return (0);
1342 }
1343 
1344 #define	GETMODECMD(name) (struct modelist *) \
1345 		genget(name, (char **)ModeList, sizeof (struct modelist))
1346 
1347 static int
1348 modecmd(argc, argv)
1349 	int  argc;
1350 	char *argv[];
1351 {
1352 	struct modelist *mt;
1353 
1354 	if (argc != 2) {
1355 		(void) printf("'mode' command requires an argument\n");
1356 		(void) printf("'mode ?' for help.\n");
1357 	} else if ((mt = GETMODECMD(argv[1])) == 0) {
1358 		(void) fprintf(stderr,
1359 		    "Unknown mode '%s' ('mode ?' for help).\n", argv[1]);
1360 	} else if (Ambiguous(mt)) {
1361 		(void) fprintf(stderr,
1362 		    "Ambiguous mode '%s' ('mode ?' for help).\n", argv[1]);
1363 	} else if (mt->needconnect && !connected) {
1364 		(void) printf("?Need to be connected first.\n");
1365 		(void) printf("'mode ?' for help.\n");
1366 	} else if (mt->handler) {
1367 		return (*mt->handler)(mt->arg1);
1368 	}
1369 	return (0);
1370 }
1371 
1372 /*
1373  * The following data structures and routines implement the
1374  * "display" command.
1375  */
1376 
1377 static int
1378 display(argc, argv)
1379 	int  argc;
1380 	char *argv[];
1381 {
1382 	struct togglelist *tl;
1383 	struct setlist *sl;
1384 
1385 #define	dotog(tl)	if (tl->variable && tl->actionexplanation) { \
1386 			    if (*tl->variable) { \
1387 				(void) printf("will"); \
1388 			    } else { \
1389 				(void) printf("won't"); \
1390 			    } \
1391 			    (void) printf(" %s.\n", tl->actionexplanation); \
1392 			}
1393 
1394 #define	doset(sl)   if (sl->name && *sl->name != ' ') { \
1395 			if (sl->handler == 0) \
1396 			    (void) printf("%-15s [%s]\n", sl->name, \
1397 			    control(*sl->charp)); \
1398 			else \
1399 			    (void) printf("%-15s \"%s\"\n", sl->name, \
1400 			    (char *)sl->charp); \
1401 		    }
1402 
1403 	if (argc == 1) {
1404 		for (tl = Togglelist; tl->name; tl++) {
1405 			dotog(tl);
1406 		}
1407 		(void) printf("\n");
1408 		for (sl = Setlist; sl->name; sl++) {
1409 			doset(sl);
1410 		}
1411 	} else {
1412 		int i;
1413 
1414 		for (i = 1; i < argc; i++) {
1415 			sl = getset(argv[i]);
1416 			tl = GETTOGGLE(argv[i]);
1417 			if (Ambiguous(sl) || Ambiguous(tl)) {
1418 				(void) printf(
1419 				    "?Ambiguous argument '%s'.\n", argv[i]);
1420 				return (0);
1421 			} else if (!sl && !tl) {
1422 				(void) printf(
1423 				    "?Unknown argument '%s'.\n", argv[i]);
1424 				return (0);
1425 			} else {
1426 				if (tl) {
1427 					dotog(tl);
1428 				}
1429 				if (sl) {
1430 				    doset(sl);
1431 				}
1432 			}
1433 		}
1434 	}
1435 	optionstatus();
1436 	(void) EncryptStatus();
1437 	return (1);
1438 #undef	doset
1439 #undef	dotog
1440 }
1441 
1442 /*
1443  * The following are the data structures, and many of the routines,
1444  * relating to command processing.
1445  */
1446 
1447 /*
1448  * Set the escape character.
1449  */
1450 	static int
1451 setescape(argc, argv)
1452 	int argc;
1453 	char *argv[];
1454 {
1455 	register char *arg;
1456 	char *buf = NULL;
1457 
1458 	if (argc > 2)
1459 		arg = argv[1];
1460 	else {
1461 		(void) printf("new escape character: ");
1462 		if (GetString(&buf, NULL, stdin) == NULL) {
1463 			if (!feof(stdin)) {
1464 				perror("can't set escape character");
1465 				goto setescape_exit;
1466 			}
1467 		}
1468 		arg = buf;
1469 	}
1470 	/* we place no limitations on what escape can be. */
1471 	escape = arg[0];
1472 	(void) printf("Escape character is '%s'.\n", esc_control(escape));
1473 	(void) fflush(stdout);
1474 setescape_exit:
1475 	Free(&buf);
1476 	return (1);
1477 }
1478 
1479 /*ARGSUSED*/
1480 static int
1481 togcrmod(argc, argv)
1482 	int argc;
1483 	char *argv[];
1484 {
1485 	crmod = !crmod;
1486 	(void) printf(
1487 	    "%s map carriage return on output.\n", crmod ? "Will" : "Won't");
1488 	(void) fflush(stdout);
1489 	return (1);
1490 }
1491 
1492 /*ARGSUSED*/
1493 static int
1494 suspend(argc, argv)
1495 	int argc;
1496 	char *argv[];
1497 {
1498 	setcommandmode();
1499 	{
1500 		unsigned short oldrows, oldcols, newrows, newcols;
1501 		int err;
1502 
1503 		err = (TerminalWindowSize(&oldrows, &oldcols) == 0) ? 1 : 0;
1504 		(void) kill(0, SIGTSTP);
1505 		/*
1506 		 * If we didn't get the window size before the SUSPEND, but we
1507 		 * can get them now (?), then send the NAWS to make sure that
1508 		 * we are set up for the right window size.
1509 		 */
1510 		if (TerminalWindowSize(&newrows, &newcols) && connected &&
1511 		    (err || ((oldrows != newrows) || (oldcols != newcols)))) {
1512 			sendnaws();
1513 		}
1514 	}
1515 	/* reget parameters in case they were changed */
1516 	TerminalSaveState();
1517 	setconnmode(0);
1518 	return (1);
1519 }
1520 
1521 /*ARGSUSED*/
1522 static int
1523 shell(argc, argv)
1524 	int argc;
1525 	char *argv[];
1526 {
1527 	unsigned short oldrows, oldcols, newrows, newcols;
1528 	int err;
1529 
1530 	setcommandmode();
1531 
1532 	err = (TerminalWindowSize(&oldrows, &oldcols) == 0) ? 1 : 0;
1533 	switch (vfork()) {
1534 	case -1:
1535 		perror("Fork failed\n");
1536 		break;
1537 
1538 	case 0:
1539 		{
1540 		/*
1541 		 * Fire up the shell in the child.
1542 		 */
1543 		register char *shellp, *shellname;
1544 
1545 		shellp = getenv("SHELL");
1546 		if (shellp == NULL)
1547 			shellp = "/bin/sh";
1548 		if ((shellname = strrchr(shellp, '/')) == 0)
1549 			shellname = shellp;
1550 		else
1551 			shellname++;
1552 		if (argc > 1)
1553 			(void) execl(shellp, shellname, "-c", argv[1], 0);
1554 		else
1555 			(void) execl(shellp, shellname, 0);
1556 		perror("Execl");
1557 		_exit(EXIT_FAILURE);
1558 		}
1559 	default:
1560 		(void) wait((int *)0);	/* Wait for the shell to complete */
1561 
1562 		if (TerminalWindowSize(&newrows, &newcols) && connected &&
1563 		(err || ((oldrows != newrows) || (oldcols != newcols)))) {
1564 			sendnaws();
1565 		}
1566 		break;
1567 	}
1568 	return (1);
1569 }
1570 
1571 static int
1572 bye(argc, argv)
1573 	int  argc;	/* Number of arguments */
1574 	char *argv[];	/* arguments */
1575 {
1576 	extern int resettermname;
1577 
1578 	if (connected) {
1579 		(void) shutdown(net, 2);
1580 		(void) printf("Connection to %.*s closed.\n", MAXHOSTNAMELEN,
1581 		    hostname);
1582 		Close(&net);
1583 		connected = 0;
1584 		resettermname = 1;
1585 		/* reset options */
1586 		(void) tninit();
1587 	}
1588 	if ((argc != 2) || (strcmp(argv[1], "fromquit") != 0)) {
1589 		longjmp(toplevel, 1);
1590 		/* NOTREACHED */
1591 	}
1592 	return (1);			/* Keep lint, etc., happy */
1593 }
1594 
1595 /*VARARGS*/
1596 int
1597 quit()
1598 {
1599 	(void) call(3, bye, "bye", "fromquit");
1600 	Exit(EXIT_SUCCESS);
1601 	/*NOTREACHED*/
1602 	return (1);
1603 }
1604 
1605 /*ARGSUSED*/
1606 static int
1607 logout(argc, argv)
1608 	int argc;
1609 	char *argv[];
1610 {
1611 	send_do(TELOPT_LOGOUT, 1);
1612 	(void) netflush();
1613 	return (1);
1614 }
1615 
1616 
1617 /*
1618  * The SLC command.
1619  */
1620 
1621 struct slclist {
1622 	char	*name;
1623 	char	*help;
1624 	void	(*handler)();
1625 	int	arg;
1626 };
1627 
1628 static void slc_help();
1629 
1630 static struct slclist SlcList[] = {
1631 	{ "export",	"Use local special character definitions",
1632 						slc_mode_export,	0 },
1633 	{ "import",	"Use remote special character definitions",
1634 						slc_mode_import,	1 },
1635 	{ "check",	"Verify remote special character definitions",
1636 						slc_mode_import,	0 },
1637 	{ "help",	0,			slc_help,		0 },
1638 	{ "?",	"Print help information",	slc_help,		0 },
1639 	{ 0 },
1640 };
1641 
1642 static void
1643 slc_help()
1644 {
1645 	struct slclist *c;
1646 
1647 	for (c = SlcList; c->name; c++) {
1648 		if (c->help) {
1649 			if (*c->help)
1650 				(void) printf("%-15s %s\n", c->name, c->help);
1651 			else
1652 				(void) printf("\n");
1653 		}
1654 	}
1655 }
1656 
1657 static struct slclist *
1658 getslc(name)
1659 	char *name;
1660 {
1661 	return ((struct slclist *)
1662 	    genget(name, (char **)SlcList, sizeof (struct slclist)));
1663 }
1664 
1665 static int
1666 slccmd(argc, argv)
1667 	int  argc;
1668 	char *argv[];
1669 {
1670 	struct slclist *c;
1671 
1672 	if (argc != 2) {
1673 		(void) fprintf(stderr,
1674 		    "Need an argument to 'slc' command.  'slc ?' for help.\n");
1675 		return (0);
1676 	}
1677 	c = getslc(argv[1]);
1678 	if (c == 0) {
1679 		(void) fprintf(stderr,
1680 		    "'%s': unknown argument ('slc ?' for help).\n",
1681 		    argv[1]);
1682 		return (0);
1683 	}
1684 	if (Ambiguous(c)) {
1685 		(void) fprintf(stderr,
1686 		    "'%s': ambiguous argument ('slc ?' for help).\n", argv[1]);
1687 		return (0);
1688 	}
1689 	(*c->handler)(c->arg);
1690 	slcstate();
1691 	return (1);
1692 }
1693 
1694 /*
1695  * The ENVIRON command.
1696  */
1697 
1698 struct envlist {
1699 	char	*name;
1700 	char	*help;
1701 	void	(*handler)();
1702 	int	narg;
1703 };
1704 
1705 static struct env_lst *env_define(unsigned char *, unsigned char *);
1706 static void env_undefine(unsigned char *);
1707 static void env_export(unsigned char *);
1708 static void env_unexport(unsigned char *);
1709 static void env_send(unsigned char *);
1710 #if defined(OLD_ENVIRON) && defined(ENV_HACK)
1711 static void env_varval(unsigned char *);
1712 #endif
1713 static void env_list(void);
1714 
1715 static void env_help(void);
1716 
1717 static struct envlist EnvList[] = {
1718 	{ "define",	"Define an environment variable",
1719 						(void (*)())env_define,	2 },
1720 	{ "undefine", "Undefine an environment variable",
1721 						env_undefine,	1 },
1722 	{ "export",	"Mark an environment variable for automatic export",
1723 						env_export,	1 },
1724 	{ "unexport", "Don't mark an environment variable for automatic export",
1725 						env_unexport,	1 },
1726 	{ "send",	"Send an environment variable", env_send,	1 },
1727 	{ "list",	"List the current environment variables",
1728 						env_list,	0 },
1729 #if defined(OLD_ENVIRON) && defined(ENV_HACK)
1730 	{ "varval", "Reverse VAR and VALUE (auto, right, wrong, status)",
1731 						env_varval,    1 },
1732 #endif
1733 	{ "help",	0,			env_help,		0 },
1734 	{ "?",	"Print help information",	env_help,		0 },
1735 	{ 0 },
1736 };
1737 
1738 static void
1739 env_help()
1740 {
1741 	struct envlist *c;
1742 
1743 	for (c = EnvList; c->name; c++) {
1744 		if (c->help) {
1745 			if (*c->help)
1746 				(void) printf("%-15s %s\n", c->name, c->help);
1747 			else
1748 				(void) printf("\n");
1749 		}
1750 	}
1751 }
1752 
1753 static struct envlist *
1754 getenvcmd(name)
1755     char *name;
1756 {
1757 	return ((struct envlist *)
1758 	    genget(name, (char **)EnvList, sizeof (struct envlist)));
1759 }
1760 
1761 static int
1762 env_cmd(argc, argv)
1763 	int  argc;
1764 	char *argv[];
1765 {
1766 	struct envlist *c;
1767 
1768 	if (argc < 2) {
1769 		(void) fprintf(stderr,
1770 		    "Need an argument to 'environ' command.  "
1771 		    "'environ ?' for help.\n");
1772 		return (0);
1773 	}
1774 	c = getenvcmd(argv[1]);
1775 	if (c == 0) {
1776 		(void) fprintf(stderr, "'%s': unknown argument "
1777 		    "('environ ?' for help).\n", argv[1]);
1778 		return (0);
1779 	}
1780 	if (Ambiguous(c)) {
1781 		(void) fprintf(stderr, "'%s': ambiguous argument "
1782 		    "('environ ?' for help).\n", argv[1]);
1783 		return (0);
1784 	}
1785 	if (c->narg + 2 != argc) {
1786 		(void) fprintf(stderr,
1787 		    "Need %s%d argument%s to 'environ %s' command.  "
1788 		    "'environ ?' for help.\n",
1789 		    c->narg + 2 < argc ? "only " : "",
1790 		    c->narg, c->narg == 1 ? "" : "s", c->name);
1791 		return (0);
1792 	}
1793 	(*c->handler)(argv[2], argv[3]);
1794 	return (1);
1795 }
1796 
1797 struct env_lst {
1798 	struct env_lst *next;	/* pointer to next structure */
1799 	struct env_lst *prev;	/* pointer to previous structure */
1800 	unsigned char *var;	/* pointer to variable name */
1801 	unsigned char *value;	/* pointer to variable value */
1802 	int export;		/* 1 -> export with default list of variables */
1803 	int welldefined;	/* A well defined variable */
1804 };
1805 
1806 static struct env_lst envlisthead;
1807 
1808 static struct env_lst *
1809 env_find(var)
1810 	unsigned char *var;
1811 {
1812 	register struct env_lst *ep;
1813 
1814 	for (ep = envlisthead.next; ep; ep = ep->next) {
1815 		if (strcmp((char *)ep->var, (char *)var) == 0)
1816 			return (ep);
1817 	}
1818 	return (NULL);
1819 }
1820 
1821 int
1822 env_init()
1823 {
1824 #ifdef	lint
1825 	char **environ = NULL;
1826 #else	/* lint */
1827 	extern char **environ;
1828 #endif	/* lint */
1829 	char **epp, *cp;
1830 	struct env_lst *ep;
1831 
1832 	for (epp = environ; *epp; epp++) {
1833 		if (cp = strchr(*epp, '=')) {
1834 			*cp = '\0';
1835 
1836 			ep = env_define((unsigned char *)*epp,
1837 					(unsigned char *)cp+1);
1838 			if (ep == NULL)
1839 				return (0);
1840 			ep->export = 0;
1841 			*cp = '=';
1842 		}
1843 	}
1844 	/*
1845 	 * Special case for DISPLAY variable.  If it is ":0.0" or
1846 	 * "unix:0.0", we have to get rid of "unix" and insert our
1847 	 * hostname.
1848 	 */
1849 	if (((ep = env_find((uchar_t *)"DISPLAY")) != NULL) &&
1850 	    ((*ep->value == ':') ||
1851 	    (strncmp((char *)ep->value, "unix:", 5) == 0))) {
1852 		char hbuf[MAXHOSTNAMELEN];
1853 		char *cp2 = strchr((char *)ep->value, ':');
1854 
1855 		if (gethostname(hbuf, MAXHOSTNAMELEN) == -1) {
1856 			perror("telnet: cannot get hostname");
1857 			return (0);
1858 		}
1859 		hbuf[MAXHOSTNAMELEN-1] = '\0';
1860 		cp = malloc(strlen(hbuf) + strlen(cp2) + 1);
1861 		if (cp == NULL) {
1862 			perror("telnet: cannot define DISPLAY variable");
1863 			return (0);
1864 		}
1865 		(void) sprintf((char *)cp, "%s%s", hbuf, cp2);
1866 		free(ep->value);
1867 		ep->value = (unsigned char *)cp;
1868 	}
1869 	/*
1870 	 * If LOGNAME is defined, but USER is not, then add
1871 	 * USER with the value from LOGNAME.  We do this because the "accepted
1872 	 * practice" is to always pass USER on the wire, but SVR4 uses
1873 	 * LOGNAME by default.
1874 	 */
1875 	if ((ep = env_find((uchar_t *)"LOGNAME")) != NULL &&
1876 		env_find((uchar_t *)"USER") == NULL) {
1877 		if (env_define((unsigned char *)"USER", ep->value) != NULL)
1878 			env_unexport((unsigned char *)"USER");
1879 	}
1880 	env_export((unsigned char *)"DISPLAY");
1881 	env_export((unsigned char *)"PRINTER");
1882 
1883 	return (1);
1884 }
1885 
1886 static struct env_lst *
1887 env_define(var, value)
1888 	unsigned char *var, *value;
1889 {
1890 	unsigned char *tmp_value;
1891 	unsigned char *tmp_var;
1892 	struct env_lst *ep;
1893 
1894 	/*
1895 	 * Allocate copies of arguments first, to make cleanup easier
1896 	 * in the case of allocation errors.
1897 	 */
1898 	tmp_var = (unsigned char *)strdup((char *)var);
1899 	if (tmp_var == NULL) {
1900 		perror("telnet: can't copy environment variable name");
1901 		return (NULL);
1902 	}
1903 
1904 	tmp_value = (unsigned char *)strdup((char *)value);
1905 	if (tmp_value == NULL) {
1906 		free(tmp_var);
1907 		perror("telnet: can't copy environment variable value");
1908 		return (NULL);
1909 	}
1910 
1911 	if (ep = env_find(var)) {
1912 		if (ep->var)
1913 			free(ep->var);
1914 		if (ep->value)
1915 			free(ep->value);
1916 	} else {
1917 		ep = malloc(sizeof (struct env_lst));
1918 		if (ep == NULL) {
1919 			perror("telnet: can't define environment variable");
1920 			free(tmp_var);
1921 			free(tmp_value);
1922 			return (NULL);
1923 		}
1924 
1925 		ep->next = envlisthead.next;
1926 		envlisthead.next = ep;
1927 		ep->prev = &envlisthead;
1928 		if (ep->next)
1929 			ep->next->prev = ep;
1930 	}
1931 	ep->welldefined = opt_welldefined((char *)var);
1932 	ep->export = 1;
1933 	ep->var = tmp_var;
1934 	ep->value = tmp_value;
1935 
1936 	return (ep);
1937 }
1938 
1939 static void
1940 env_undefine(var)
1941 	unsigned char *var;
1942 {
1943 	register struct env_lst *ep;
1944 
1945 	if (ep = env_find(var)) {
1946 		ep->prev->next = ep->next;
1947 		if (ep->next)
1948 			ep->next->prev = ep->prev;
1949 		if (ep->var)
1950 			free(ep->var);
1951 		if (ep->value)
1952 			free(ep->value);
1953 		free(ep);
1954 	}
1955 }
1956 
1957 static void
1958 env_export(var)
1959 	unsigned char *var;
1960 {
1961 	register struct env_lst *ep;
1962 
1963 	if (ep = env_find(var))
1964 		ep->export = 1;
1965 }
1966 
1967 static void
1968 env_unexport(var)
1969 	unsigned char *var;
1970 {
1971 	register struct env_lst *ep;
1972 
1973 	if (ep = env_find(var))
1974 		ep->export = 0;
1975 }
1976 
1977 static void
1978 env_send(var)
1979 	unsigned char *var;
1980 {
1981 	register struct env_lst *ep;
1982 
1983 	if (my_state_is_wont(TELOPT_NEW_ENVIRON)
1984 #ifdef	OLD_ENVIRON
1985 	    /* old style */ && my_state_is_wont(TELOPT_OLD_ENVIRON)
1986 #endif
1987 		/* no environ */) {
1988 		(void) fprintf(stderr,
1989 		    "Cannot send '%s': Telnet ENVIRON option not enabled\n",
1990 									var);
1991 		return;
1992 	}
1993 	ep = env_find(var);
1994 	if (ep == 0) {
1995 		(void) fprintf(stderr,
1996 		    "Cannot send '%s': variable not defined\n", var);
1997 		return;
1998 	}
1999 	env_opt_start_info();
2000 	env_opt_add(ep->var);
2001 	env_opt_end(0);
2002 }
2003 
2004 static void
2005 env_list()
2006 {
2007 	register struct env_lst *ep;
2008 
2009 	for (ep = envlisthead.next; ep; ep = ep->next) {
2010 		(void) printf("%c %-20s %s\n", ep->export ? '*' : ' ',
2011 					ep->var, ep->value);
2012 	}
2013 }
2014 
2015 	unsigned char *
2016 env_default(init, welldefined)
2017 	int init;
2018 {
2019 	static struct env_lst *nep = NULL;
2020 
2021 	if (init) {
2022 		/* return value is not used */
2023 		nep = &envlisthead;
2024 		return (NULL);
2025 	}
2026 	if (nep) {
2027 		while ((nep = nep->next) != NULL) {
2028 			if (nep->export && (nep->welldefined == welldefined))
2029 				return (nep->var);
2030 		}
2031 	}
2032 	return (NULL);
2033 }
2034 
2035 	unsigned char *
2036 env_getvalue(var)
2037 	unsigned char *var;
2038 {
2039 	register struct env_lst *ep;
2040 
2041 	if (ep = env_find(var))
2042 		return (ep->value);
2043 	return (NULL);
2044 }
2045 
2046 #if defined(OLD_ENVIRON) && defined(ENV_HACK)
2047 static void
2048 env_varval(what)
2049 	unsigned char *what;
2050 {
2051 	extern int old_env_var, old_env_value, env_auto;
2052 	int len = strlen((char *)what);
2053 
2054 	if (len == 0)
2055 		goto unknown;
2056 
2057 	if (strncasecmp((char *)what, "status", len) == 0) {
2058 		if (env_auto)
2059 			(void) printf("%s%s", "VAR and VALUE are/will be ",
2060 					"determined automatically\n");
2061 		if (old_env_var == OLD_ENV_VAR)
2062 			(void) printf(
2063 			    "VAR and VALUE set to correct definitions\n");
2064 		else
2065 			(void) printf(
2066 			    "VAR and VALUE definitions are reversed\n");
2067 	} else if (strncasecmp((char *)what, "auto", len) == 0) {
2068 		env_auto = 1;
2069 		old_env_var = OLD_ENV_VALUE;
2070 		old_env_value = OLD_ENV_VAR;
2071 	} else if (strncasecmp((char *)what, "right", len) == 0) {
2072 		env_auto = 0;
2073 		old_env_var = OLD_ENV_VAR;
2074 		old_env_value = OLD_ENV_VALUE;
2075 	} else if (strncasecmp((char *)what, "wrong", len) == 0) {
2076 		env_auto = 0;
2077 		old_env_var = OLD_ENV_VALUE;
2078 		old_env_value = OLD_ENV_VAR;
2079 	} else {
2080 unknown:
2081 		(void) printf(
2082 		    "Unknown \"varval\" command. (\"auto\", \"right\", "
2083 		    "\"wrong\", \"status\")\n");
2084 	}
2085 }
2086 #endif	/* OLD_ENVIRON && ENV_HACK */
2087 
2088 /*
2089  * The AUTHENTICATE command.
2090  */
2091 
2092 struct authlist {
2093 	char	*name;
2094 	char	*help;
2095 	int	(*handler)();
2096 	int	narg;
2097 };
2098 
2099 extern int auth_enable(char *);
2100 extern int auth_disable(char *);
2101 extern int auth_status(void);
2102 
2103 static int auth_help(void);
2104 
2105 static struct authlist AuthList[] = {
2106 	{ "status",
2107 	    "Display current status of authentication information",
2108 	    auth_status,	0 },
2109 	{ "disable",
2110 	    "Disable an authentication type ('auth disable ?' for more)",
2111 	    auth_disable,	1 },
2112 	{ "enable",
2113 	    "Enable an authentication type ('auth enable ?' for more)",
2114 	    auth_enable,	1 },
2115 	{ "help",	0,			auth_help,		0 },
2116 	{ "?",	"Print help information",	auth_help,		0 },
2117 	{ 0 },
2118 };
2119 
2120 static int
2121 auth_help(void)
2122 {
2123 	struct authlist *c;
2124 
2125 	for (c = AuthList; c->name; c++) {
2126 		if (c->help) {
2127 			if (*c->help)
2128 				(void) printf("%-15s %s\n", c->name, c->help);
2129 			else
2130 				(void) printf("\n");
2131 		}
2132 	}
2133 	return (0);
2134 }
2135 
2136 
2137 static int
2138 auth_cmd(argc, argv)
2139 	int  argc;
2140 	char *argv[];
2141 {
2142 	struct authlist *c;
2143 
2144 	if (argc < 2) {
2145 		(void) fprintf(stderr, "Need an argument to 'auth' "
2146 			"command.  'auth ?' for help.\n");
2147 		return (0);
2148 	}
2149 
2150 	c = (struct authlist *)
2151 		genget(argv[1], (char **)AuthList, sizeof (struct authlist));
2152 	if (c == 0) {
2153 		(void) fprintf(stderr,
2154 		    "'%s': unknown argument ('auth ?' for help).\n",
2155 		    argv[1]);
2156 		return (0);
2157 	}
2158 	if (Ambiguous(c)) {
2159 		(void) fprintf(stderr,
2160 		    "'%s': ambiguous argument ('auth ?' for help).\n", argv[1]);
2161 		return (0);
2162 	}
2163 	if (c->narg + 2 != argc) {
2164 		(void) fprintf(stderr,
2165 		    "Need %s%d argument%s to 'auth %s' command."
2166 		    " 'auth ?' for help.\n",
2167 		    c->narg + 2 < argc ? "only " : "",
2168 		    c->narg, c->narg == 1 ? "" : "s", c->name);
2169 		return (0);
2170 	}
2171 	return ((*c->handler)(argv[2], argv[3]));
2172 }
2173 
2174 /*
2175  * The FORWARD command.
2176  */
2177 
2178 extern int forward_flags;
2179 
2180 struct forwlist {
2181 	char *name;
2182 	char *help;
2183 	int (*handler)();
2184 	int f_flags;
2185 };
2186 
2187 static int forw_status(void);
2188 static int forw_set(int);
2189 static int forw_help(void);
2190 
2191 static struct forwlist ForwList[] = {
2192 	{"status",
2193 		"Display current status of credential forwarding",
2194 		forw_status, 0},
2195 	{"disable",
2196 		"Disable credential forwarding",
2197 		forw_set, 0},
2198 	{"enable",
2199 		"Enable credential forwarding",
2200 		forw_set, OPTS_FORWARD_CREDS},
2201 	{"forwardable",
2202 		"Enable credential forwarding of "
2203 				"forwardable credentials",
2204 		forw_set, OPTS_FORWARD_CREDS |	OPTS_FORWARDABLE_CREDS},
2205 	{"help",
2206 		0,
2207 		forw_help, 0},
2208 	{"?",
2209 		"Print help information",
2210 		forw_help, 0},
2211 	{0},
2212 };
2213 
2214 static int
2215 forw_status(void)
2216 {
2217 	if (forward_flags & OPTS_FORWARD_CREDS) {
2218 		if (forward_flags & OPTS_FORWARDABLE_CREDS)
2219 			(void) printf(gettext(
2220 				"Credential forwarding of "
2221 				"forwardable credentials enabled\n"));
2222 		else
2223 			(void) printf(gettext(
2224 				"Credential forwarding enabled\n"));
2225 	} else
2226 		(void) printf(gettext("Credential forwarding disabled\n"));
2227 	return (0);
2228 }
2229 
2230 static int
2231 forw_set(int f_flags)
2232 {
2233 	forward_flags = f_flags;
2234 	return (0);
2235 }
2236 
2237 static int
2238 forw_help(void)
2239 {
2240 	struct forwlist *c;
2241 
2242 	for (c = ForwList; c->name; c++) {
2243 		if (c->help) {
2244 			if (*c->help)
2245 				(void) printf("%-15s %s\r\n", c->name, c->help);
2246 			else
2247 				(void) printf("\n");
2248 		}
2249 	}
2250 	return (0);
2251 }
2252 
2253 static int
2254 forw_cmd(int argc, char *argv[])
2255 {
2256 	struct forwlist *c;
2257 
2258 	if (argc < 2) {
2259 		(void) fprintf(stderr, gettext(
2260 			"Need an argument to 'forward' "
2261 			"command.  'forward ?' for help.\n"));
2262 		return (0);
2263 	}
2264 	c = (struct forwlist *)genget(argv[1], (char **)ForwList,
2265 		sizeof (struct forwlist));
2266 	if (c == 0) {
2267 		(void) fprintf(stderr, gettext(
2268 		    "'%s': unknown argument ('forward ?' for help).\n"),
2269 		    argv[1]);
2270 		return (0);
2271 	}
2272 	if (Ambiguous(c)) {
2273 		(void) fprintf(stderr, gettext(
2274 		    "'%s': ambiguous argument ('forward ?' for help).\n"),
2275 		    argv[1]);
2276 		return (0);
2277 	}
2278 	if (argc != 2) {
2279 		(void) fprintf(stderr, gettext(
2280 		    "No arguments needed to 'forward %s' command.  "
2281 		    "'forward ?' for help.\n"), c->name);
2282 		return (0);
2283 	}
2284 	return ((*c->handler) (c->f_flags));
2285 }
2286 
2287 /*
2288  * The ENCRYPT command.
2289  */
2290 
2291 struct encryptlist {
2292 	char	*name;
2293 	char	*help;
2294 	int	(*handler)();
2295 	int	needconnect;
2296 	int	minarg;
2297 	int	maxarg;
2298 };
2299 
2300 static int EncryptHelp(void);
2301 
2302 static struct encryptlist EncryptList[] = {
2303 	{ "enable", "Enable encryption. ('encrypt enable ?' for more)",
2304 						EncryptEnable, 1, 1, 2 },
2305 	{ "disable", "Disable encryption. ('encrypt disable ?' for more)",
2306 						EncryptDisable, 0, 1, 2 },
2307 	{ "type", "Set encryption type. ('encrypt type ?' for more)",
2308 						EncryptType, 0, 1, 2 },
2309 	{ "start", "Start encryption. ('encrypt start ?' for more)",
2310 						EncryptStart, 1, 0, 1 },
2311 	{ "stop", "Stop encryption. ('encrypt stop ?' for more)",
2312 						EncryptStop, 1, 0, 1 },
2313 	{ "input", "Start encrypting the input stream",
2314 						EncryptStartInput, 1, 0, 0 },
2315 	{ "-input", "Stop encrypting the input stream",
2316 						EncryptStopInput, 1, 0, 0 },
2317 	{ "output", "Start encrypting the output stream",
2318 						EncryptStartOutput, 1, 0, 0 },
2319 	{ "-output", "Stop encrypting the output stream",
2320 						EncryptStopOutput, 1, 0, 0 },
2321 
2322 	{ "status",	"Display current status of encryption information",
2323 						EncryptStatus,	0, 0, 0 },
2324 	{ "help",	0,
2325 						EncryptHelp,	0, 0, 0 },
2326 	{ "?",	"Print help information",	EncryptHelp,	0, 0, 0 },
2327 	{ 0 },
2328 };
2329 
2330 static int
2331 EncryptHelp(void)
2332 {
2333 	struct encryptlist *c;
2334 
2335 	for (c = EncryptList; c->name; c++) {
2336 		if (c->help) {
2337 			if (*c->help)
2338 				(void) printf("%-15s %s\n", c->name, c->help);
2339 			else
2340 				(void) printf("\n");
2341 		}
2342 	}
2343 	return (0);
2344 }
2345 
2346 static int
2347 encrypt_cmd(int  argc, char *argv[])
2348 {
2349 	struct encryptlist *c;
2350 
2351 	if (argc < 2) {
2352 		(void) fprintf(stderr, gettext(
2353 			"Need an argument to 'encrypt' command.  "
2354 			"'encrypt ?' for help.\n"));
2355 		return (0);
2356 	}
2357 
2358 	c = (struct encryptlist *)
2359 	    genget(argv[1], (char **)EncryptList, sizeof (struct encryptlist));
2360 	if (c == 0) {
2361 		(void) fprintf(stderr, gettext(
2362 		    "'%s': unknown argument ('encrypt ?' for help).\n"),
2363 		    argv[1]);
2364 		return (0);
2365 	}
2366 	if (Ambiguous(c)) {
2367 		(void) fprintf(stderr, gettext(
2368 		    "'%s': ambiguous argument ('encrypt ?' for help).\n"),
2369 		    argv[1]);
2370 		return (0);
2371 	}
2372 	argc -= 2;
2373 	if (argc < c->minarg || argc > c->maxarg) {
2374 		if (c->minarg == c->maxarg) {
2375 			(void) fprintf(stderr, gettext("Need %s%d %s "),
2376 			c->minarg < argc ?
2377 			gettext("only ") : "", c->minarg,
2378 			c->minarg == 1 ?
2379 			gettext("argument") : gettext("arguments"));
2380 		} else {
2381 			(void) fprintf(stderr,
2382 			    gettext("Need %s%d-%d arguments "),
2383 			c->maxarg < argc ?
2384 			gettext("only ") : "", c->minarg, c->maxarg);
2385 		}
2386 		(void) fprintf(stderr, gettext(
2387 		    "to 'encrypt %s' command.  'encrypt ?' for help.\n"),
2388 		    c->name);
2389 		return (0);
2390 	}
2391 	if (c->needconnect && !connected) {
2392 		if (!(argc &&
2393 		    (isprefix(argv[2], "help") || isprefix(argv[2], "?")))) {
2394 			(void) printf(
2395 			    gettext("?Need to be connected first.\n"));
2396 			return (0);
2397 		}
2398 	}
2399 	return ((*c->handler)(argc > 0 ? argv[2] : 0,
2400 	    argc > 1 ? argv[3] : 0, argc > 2 ? argv[4] : 0));
2401 }
2402 
2403 /*
2404  * Print status about the connection.
2405  */
2406 static int
2407 status(int argc, char *argv[])
2408 {
2409 	if (connected) {
2410 		(void) printf("Connected to %s.\n", hostname);
2411 		if ((argc < 2) || strcmp(argv[1], "notmuch")) {
2412 			int mode = getconnmode();
2413 
2414 			if (my_want_state_is_will(TELOPT_LINEMODE)) {
2415 				(void) printf(
2416 				    "Operating with LINEMODE option\n");
2417 				(void) printf(
2418 				    "%s line editing\n", (mode&MODE_EDIT) ?
2419 				    "Local" : "No");
2420 				(void) printf("%s catching of signals\n",
2421 				    (mode&MODE_TRAPSIG) ? "Local" : "No");
2422 				slcstate();
2423 #ifdef	KLUDGELINEMODE
2424 			} else if (kludgelinemode &&
2425 			    my_want_state_is_dont(TELOPT_SGA)) {
2426 				(void) printf(
2427 				    "Operating in obsolete linemode\n");
2428 #endif
2429 			} else {
2430 				(void) printf(
2431 					"Operating in single character mode\n");
2432 				if (localchars)
2433 					(void) printf(
2434 					"Catching signals locally\n");
2435 			}
2436 			(void) printf("%s character echo\n", (mode&MODE_ECHO) ?
2437 			    "Local" : "Remote");
2438 			if (my_want_state_is_will(TELOPT_LFLOW))
2439 				(void) printf("%s flow control\n",
2440 				    (mode&MODE_FLOW) ? "Local" : "No");
2441 
2442 			encrypt_display();
2443 		}
2444 	} else {
2445 		(void) printf("No connection.\n");
2446 	}
2447 	if (rlogin != _POSIX_VDISABLE)
2448 		(void) printf("Escape character is '%s'.\n", control(rlogin));
2449 	else
2450 		(void) printf(
2451 		    "Escape character is '%s'.\n", esc_control(escape));
2452 	(void) fflush(stdout);
2453 	return (1);
2454 }
2455 
2456 /*
2457  * Parse the user input (cmd_line_input) which should:
2458  * - start with the target host, or with "@" or "!@" followed by at least one
2459  *   gateway.
2460  * - each host (can be literal address or hostname) can be separated by ",",
2461  *   "@", or ",@".
2462  * Note that the last host is the target, all the others (if any ) are the
2463  * gateways.
2464  *
2465  * Returns:	-1	if a library call fails, too many gateways, or parse
2466  *			error
2467  *		num_gw	otherwise
2468  * On successful return, hostname_list points to a list of hosts (last one being
2469  * the target, others gateways), src_rtng_type points to the type of source
2470  * routing (strict vs. loose)
2471  */
2472 static int
2473 parse_input(char *cmd_line_input, char **hostname_list, uchar_t *src_rtng_type)
2474 {
2475 	char hname[MAXHOSTNAMELEN + 1];
2476 	char *cp;
2477 	int gw_count;
2478 	int i;
2479 
2480 	gw_count = 0;
2481 	cp = cmd_line_input;
2482 
2483 	/*
2484 	 * Defining ICMD generates the Itelnet binary, the special version of
2485 	 * telnet which is used with firewall proxy.
2486 	 * If ICMD is defined, parse_input will treat the whole cmd_line_input
2487 	 * as the target host and set the num_gw to 0. Therefore, none of the
2488 	 * source routing related code paths will be executed.
2489 	 */
2490 #ifndef ICMD
2491 	if (*cp == '@') {
2492 		*src_rtng_type = IPOPT_LSRR;
2493 		cp++;
2494 	} else if (*cp == '!') {
2495 		*src_rtng_type = IPOPT_SSRR;
2496 
2497 		/* "!" must be followed by '@' */
2498 		if (*(cp + 1) != '@')
2499 			goto parse_error;
2500 		cp += 2;
2501 	} else {
2502 #endif	/* ICMD */
2503 		/* no gateways, just the target */
2504 		hostname_list[0] = strdup(cp);
2505 		if (hostname_list[0] == NULL) {
2506 			perror("telnet: copying host name");
2507 			return (-1);
2508 		}
2509 		return (0);
2510 #ifndef ICMD
2511 	}
2512 
2513 	while (*cp != '\0') {
2514 		/*
2515 		 * Identify each gateway separated by ",", "@" or ",@" and
2516 		 * store in hname[].
2517 		 */
2518 		i = 0;
2519 		while (*cp != '@' && *cp != ',' && *cp != '\0') {
2520 			hname[i++] = *cp++;
2521 			if (i > MAXHOSTNAMELEN)
2522 				goto parse_error;
2523 		}
2524 		hname[i] = '\0';
2525 
2526 		/*
2527 		 * Two consecutive delimiters which result in a 0 length hname
2528 		 * is a parse error.
2529 		 */
2530 		if (i == 0)
2531 			goto parse_error;
2532 
2533 		hostname_list[gw_count] = strdup(hname);
2534 		if (hostname_list[gw_count] == NULL) {
2535 			perror("telnet: copying hostname from list");
2536 			return (-1);
2537 		}
2538 
2539 		if (++gw_count > MAXMAX_GATEWAY) {
2540 			(void) fprintf(stderr, "telnet: too many gateways\n");
2541 			return (-1);
2542 		}
2543 
2544 		/* Jump over the next delimiter. */
2545 		if (*cp != '\0') {
2546 			/* ...gw1,@gw2... accepted */
2547 			if (*cp == ',' && *(cp + 1) == '@')
2548 				cp += 2;
2549 			else
2550 				cp++;
2551 		}
2552 	}
2553 
2554 	/* discount the target */
2555 	gw_count--;
2556 
2557 	/* Any input starting with '!@' or '@' must have at least one gateway */
2558 	if (gw_count <= 0)
2559 		goto parse_error;
2560 
2561 	return (gw_count);
2562 
2563 parse_error:
2564 	(void) printf("Bad source route option: %s\n", cmd_line_input);
2565 	return (-1);
2566 #endif	/* ICMD */
2567 }
2568 
2569 /*
2570  * Resolves the target and gateway addresses, determines what type of addresses
2571  * (ALL_ADDRS, ONLY_V6, ONLY_V4) telnet will be trying to connect.
2572  *
2573  * Returns:	pointer to resolved target	if name resolutions succeed
2574  *		NULL				if name resolutions fail or
2575  *						a library function call fails
2576  *
2577  * The last host in the hostname_list is the target. After resolving the target,
2578  * determines for what type of addresses it should try to resolve gateways. It
2579  * resolves gateway addresses and picks one address for each desired address
2580  * type and stores in the array pointed by gw_addrsp. Also, this 'type of
2581  * addresses' is pointed by addr_type argument on successful return.
2582  */
2583 static struct addrinfo *
2584 resolve_hosts(char **hostname_list, int num_gw, struct gateway **gw_addrsp,
2585     int *addr_type, const char *portp)
2586 {
2587 	struct gateway *gw_addrs = NULL;
2588 	struct gateway *gw;
2589 	/* whether we already picked an IPv4 address for the current gateway */
2590 	boolean_t got_v4_addr;
2591 	boolean_t got_v6_addr;
2592 	/* whether we need to get an IPv4 address for the current gateway */
2593 	boolean_t need_v4_addr = B_FALSE;
2594 	boolean_t need_v6_addr = B_FALSE;
2595 	int res_failed_at4;	/* save which gateway failed to resolve */
2596 	int res_failed_at6;
2597 	boolean_t is_v4mapped;
2598 	struct in6_addr *v6addrp;
2599 	struct in_addr *v4addrp;
2600 	int error_num;
2601 	int i;
2602 	int rc;
2603 	struct addrinfo *res, *host, *gateway, *addr;
2604 	struct addrinfo hints;
2605 
2606 	*addr_type = ALL_ADDRS;
2607 
2608 	memset(&hints, 0, sizeof (hints));
2609 	hints.ai_flags = AI_CANONNAME; /* used for config files, diags */
2610 	hints.ai_socktype = SOCK_STREAM;
2611 	rc = getaddrinfo(hostname_list[num_gw],
2612 	    (portp != NULL) ? portp : "telnet", &hints, &res);
2613 	if (rc != 0) {
2614 		if (hostname_list[num_gw] != NULL &&
2615 		    *hostname_list[num_gw] != '\0')
2616 			(void) fprintf(stderr, "%s: ", hostname_list[num_gw]);
2617 		(void) fprintf(stderr, "%s\n", gai_strerror(rc));
2618 		return (NULL);
2619 	}
2620 
2621 	/*
2622 	 * Let's see what type of addresses we got for the target. This
2623 	 * determines what type of addresses we'd like to resolve gateways
2624 	 * later.
2625 	 */
2626 	for (host = res; host != NULL; host = host->ai_next) {
2627 		struct sockaddr_in6 *s6;
2628 
2629 		s6 = (struct sockaddr_in6 *)host->ai_addr;
2630 
2631 		if (host->ai_addr->sa_family == AF_INET ||
2632 		    IN6_IS_ADDR_V4MAPPED(&s6->sin6_addr))
2633 			need_v4_addr = B_TRUE;
2634 		else
2635 			need_v6_addr = B_TRUE;
2636 
2637 		/*
2638 		 * Let's stop after seeing we need both IPv6 and IPv4.
2639 		 */
2640 		if (need_v4_addr && need_v6_addr)
2641 			break;
2642 	}
2643 
2644 	if (num_gw > 0) {
2645 		/*
2646 		 * In the prepare_optbuf(), we'll store the IPv4 address of the
2647 		 * target in the last slot of gw_addrs array. Therefore we need
2648 		 * space for num_gw+1 hosts.
2649 		 */
2650 		gw_addrs = calloc(num_gw + 1, sizeof (struct gateway));
2651 		if (gw_addrs == NULL) {
2652 			perror("telnet: calloc");
2653 			freeaddrinfo(res);
2654 			return (NULL);
2655 		}
2656 	}
2657 
2658 	/*
2659 	 * Now we'll go through all the gateways and try to resolve them to
2660 	 * the desired address types.
2661 	 */
2662 	gw = gw_addrs;
2663 
2664 	/* -1 means 'no address resolution failure yet' */
2665 	res_failed_at4 = -1;
2666 	res_failed_at6 = -1;
2667 	for (i = 0; i < num_gw; i++) {
2668 		rc = getaddrinfo(hostname_list[i], NULL, NULL, &gateway);
2669 		if (rc != 0) {
2670 			if (hostname_list[i] != NULL &&
2671 			    *hostname_list[i] != '\0')
2672 				(void) fprintf(stderr, "%s: ",
2673 				    hostname_list[i]);
2674 			(void) fprintf(stderr, "bad address\n");
2675 			return (NULL);
2676 		}
2677 
2678 		/*
2679 		 * Initially we have no address of any type for this gateway.
2680 		 */
2681 		got_v6_addr = B_FALSE;
2682 		got_v4_addr = B_FALSE;
2683 
2684 		/*
2685 		 * Let's go through all the addresses of this gateway.
2686 		 * Use the first address which matches the needed family.
2687 		 */
2688 		for (addr = gateway; addr != NULL; addr = addr->ai_next) {
2689 			/*LINTED*/
2690 			v6addrp = &((struct sockaddr_in6 *)addr->ai_addr)->
2691 			    sin6_addr;
2692 			v4addrp = &((struct sockaddr_in *)addr->ai_addr)->
2693 			    sin_addr;
2694 
2695 			if (addr->ai_family == AF_INET6)
2696 				is_v4mapped = IN6_IS_ADDR_V4MAPPED(v6addrp);
2697 			else
2698 				is_v4mapped = B_FALSE;
2699 
2700 			/*
2701 			 * If we need to determine an IPv4 address and haven't
2702 			 * found one yet and this is a IPv4-mapped IPv6 address,
2703 			 * then bingo!
2704 			 */
2705 			if (need_v4_addr && !got_v4_addr) {
2706 				if (is_v4mapped) {
2707 					IN6_V4MAPPED_TO_INADDR(v6addrp,
2708 					    &gw->gw_addr);
2709 					got_v4_addr = B_TRUE;
2710 				} else if (addr->ai_family = AF_INET) {
2711 					gw->gw_addr = *v4addrp;
2712 					got_v4_addr = B_TRUE;
2713 				}
2714 			}
2715 
2716 			if (need_v6_addr && !got_v6_addr &&
2717 			    addr->ai_family == AF_INET6) {
2718 				gw->gw_addr6 = *v6addrp;
2719 				got_v6_addr = B_TRUE;
2720 			}
2721 
2722 			/*
2723 			 * Let's stop if we got all what we looked for.
2724 			 */
2725 			if ((!need_v4_addr || got_v4_addr) &&
2726 			    (!need_v6_addr || got_v6_addr))
2727 				break;
2728 		}
2729 
2730 		/*
2731 		 * We needed an IPv4 address for this gateway but couldn't
2732 		 * find one.
2733 		 */
2734 		if (need_v4_addr && !got_v4_addr) {
2735 			res_failed_at4 = i;
2736 			/*
2737 			 * Since we couldn't resolve a gateway to IPv4 address
2738 			 * we can't use IPv4 at all. Therefore we no longer
2739 			 * need IPv4 addresses for any of the gateways.
2740 			 */
2741 			need_v4_addr = B_FALSE;
2742 		}
2743 
2744 		if (need_v6_addr && !got_v6_addr) {
2745 			res_failed_at6 = i;
2746 			need_v6_addr = B_FALSE;
2747 		}
2748 
2749 		/*
2750 		 * If some gateways don't resolve to any of the desired
2751 		 * address types, we fail.
2752 		 */
2753 		if (!need_v4_addr && !need_v6_addr) {
2754 			if (res_failed_at6 != -1) {
2755 				(void) fprintf(stderr,
2756 				    "%s: Host doesn't have any IPv6 address\n",
2757 				    hostname_list[res_failed_at6]);
2758 			}
2759 			if (res_failed_at4 != -1) {
2760 				(void) fprintf(stderr,
2761 				    "%s: Host doesn't have any IPv4 address\n",
2762 				    hostname_list[res_failed_at4]);
2763 			}
2764 			free(gw_addrs);
2765 			return (NULL);
2766 		}
2767 
2768 		gw++;
2769 	}
2770 
2771 	*gw_addrsp = gw_addrs;
2772 
2773 	/*
2774 	 * When we get here, need_v4_addr and need_v6_addr have their final
2775 	 * values based on the name resolution of the target and gateways.
2776 	 */
2777 	if (need_v4_addr && need_v6_addr)
2778 		*addr_type = ALL_ADDRS;
2779 	else if (need_v4_addr && !need_v6_addr)
2780 		*addr_type = ONLY_V4;
2781 	else if (!need_v4_addr && need_v6_addr)
2782 		*addr_type = ONLY_V6;
2783 
2784 	return (res);
2785 }
2786 
2787 
2788 /*
2789  * Initializes the buffer pointed by opt_bufpp for a IPv4 option of type
2790  * src_rtng_type using the gateway addresses stored in gw_addrs. If no buffer
2791  * is passed, it allocates one. If a buffer is passed, checks if it's big
2792  * enough.
2793  * On return opt_buf_len points to the buffer length which we need later for the
2794  * setsockopt() call, and opt_bufpp points to the newly allocated or already
2795  * passed buffer. Returns B_FALSE if a library function call fails or passed
2796  * buffer is not big enough, B_TRUE otherwise.
2797  */
2798 static boolean_t
2799 prepare_optbuf(struct gateway *gw_addrs, int num_gw, char **opt_bufpp,
2800     size_t *opt_buf_len, struct in_addr *target, uchar_t src_rtng_type)
2801 {
2802 	struct ip_sourceroute *sr_opt;
2803 	size_t needed_buflen;
2804 	int i;
2805 
2806 	/*
2807 	 * We have (num_gw + 1) IP addresses in the buffer because the number
2808 	 * of gateway addresses we put in the option buffer includes the target
2809 	 * address.
2810 	 * At the time of setsockopt() call, passed option length needs to be
2811 	 * multiple of 4 bytes. Therefore we need one IPOPT_NOP before (or
2812 	 * after) IPOPT_LSRR.
2813 	 * 1 = preceding 1 byte of IPOPT_NOP
2814 	 * 3 = 1 (code) + 1 (len) + 1 (ptr)
2815 	 */
2816 	needed_buflen = 1 + 3 + (num_gw + 1) * sizeof (struct in_addr);
2817 
2818 	if (*opt_bufpp != NULL) {
2819 		/* check if the passed buffer is big enough */
2820 		if (*opt_buf_len < needed_buflen) {
2821 			(void) fprintf(stderr,
2822 			    "telnet: buffer too small for IPv4 source routing "
2823 			    "option\n");
2824 			return (B_FALSE);
2825 		}
2826 	} else {
2827 		*opt_bufpp = malloc(needed_buflen);
2828 		if (*opt_bufpp == NULL) {
2829 			perror("telnet: malloc");
2830 			return (B_FALSE);
2831 		}
2832 	}
2833 
2834 	*opt_buf_len = needed_buflen;
2835 
2836 	/* final hop is the target */
2837 	gw_addrs[num_gw].gw_addr = *target;
2838 
2839 	*opt_bufpp[0] = IPOPT_NOP;
2840 	/* IPOPT_LSRR starts right after IPOPT_NOP */
2841 	sr_opt = (struct ip_sourceroute *)(*opt_bufpp + 1);
2842 	sr_opt->ipsr_code = src_rtng_type;
2843 	/* discount the 1 byte of IPOPT_NOP */
2844 	sr_opt->ipsr_len = needed_buflen - 1;
2845 	sr_opt->ipsr_ptr = IPOPT_MINOFF;
2846 
2847 	/* copy the gateways into the optlist */
2848 	for (i = 0; i < num_gw + 1; i++) {
2849 		(void) bcopy(&gw_addrs[i].gw_addr, &sr_opt->ipsr_addrs[i],
2850 		    sizeof (struct in_addr));
2851 	}
2852 
2853 	return (B_TRUE);
2854 }
2855 
2856 /*
2857  * Initializes the buffer pointed by opt_bufpp for a IPv6 routing header option
2858  * using the gateway addresses stored in gw_addrs. If no buffer is passed, it
2859  * allocates one. If a buffer is passed, checks if it's big enough.
2860  * On return opt_buf_len points to the buffer length which we need later for the
2861  * setsockopt() call, and opt_bufpp points to the newly allocated or already
2862  * passed buffer. Returns B_FALSE if a library function call fails or passed
2863  * buffer is not big enough, B_TRUE otherwise.
2864  */
2865 static boolean_t
2866 prepare_optbuf6(struct gateway *gw_addrs, int num_gw, char **opt_bufpp,
2867     size_t *opt_buf_len)
2868 {
2869 	char *opt_bufp;
2870 	size_t needed_buflen;
2871 	int i;
2872 
2873 	needed_buflen = inet6_rth_space(IPV6_RTHDR_TYPE_0, num_gw);
2874 
2875 	if (*opt_bufpp != NULL) {
2876 		/* check if the passed buffer is big enough */
2877 		if (*opt_buf_len < needed_buflen) {
2878 			(void) fprintf(stderr,
2879 			    "telnet: buffer too small for IPv6 routing "
2880 			    "header option\n");
2881 			return (B_FALSE);
2882 		}
2883 	} else {
2884 		*opt_bufpp = malloc(needed_buflen);
2885 		if (*opt_bufpp == NULL) {
2886 			perror("telnet: malloc");
2887 			return (B_FALSE);
2888 		}
2889 	}
2890 	*opt_buf_len = needed_buflen;
2891 	opt_bufp = *opt_bufpp;
2892 
2893 	/*
2894 	 * Initialize the buffer to be used for IPv6 routing header type 0.
2895 	 */
2896 	if (inet6_rth_init(opt_bufp, needed_buflen, IPV6_RTHDR_TYPE_0,
2897 	    num_gw) == NULL) {
2898 		perror("telnet: inet6_rth_init");
2899 		return (B_FALSE);
2900 	}
2901 
2902 	/*
2903 	 * Add gateways one by one.
2904 	 */
2905 	for (i = 0; i < num_gw; i++) {
2906 		if (inet6_rth_add(opt_bufp, &gw_addrs[i].gw_addr6) == -1) {
2907 			perror("telnet: inet6_rth_add");
2908 			return (B_FALSE);
2909 		}
2910 	}
2911 
2912 	/* successful operation */
2913 	return (B_TRUE);
2914 }
2915 
2916 int
2917 tn(argc, argv)
2918 	int argc;
2919 	char *argv[];
2920 {
2921 	struct addrinfo *host = NULL;
2922 	struct addrinfo *h;
2923 	struct sockaddr_in6 sin6;
2924 	struct sockaddr_in sin;
2925 	struct in6_addr addr6;
2926 	struct in_addr addr;
2927 	void *addrp;
2928 	struct gateway *gw_addrs;
2929 	char *hostname_list[MAXMAX_GATEWAY + 1] = {NULL};
2930 	char *opt_buf6 = NULL;		/* used for IPv6 routing header */
2931 	size_t opt_buf_len6 = 0;
2932 	uchar_t src_rtng_type;		/* type of IPv4 source routing */
2933 	struct servent *sp = 0;
2934 	char *opt_buf = NULL;		/* used for IPv4 source routing */
2935 	size_t opt_buf_len = 0;
2936 	char *cmd;
2937 	char *hostp = NULL;
2938 	char *portp = NULL;
2939 	char *user = NULL;
2940 #ifdef	ICMD
2941 	char *itelnet_host;
2942 	char *real_host;
2943 	unsigned short dest_port;
2944 #endif	/* ICMD */
2945 	/*
2946 	 * The two strings at the end of this function are 24 and 39
2947 	 * characters long (minus the %.*s in the format strings).  Add
2948 	 * one for the null terminator making the longest print string 40.
2949 	 */
2950 	char buf[MAXHOSTNAMELEN+40];
2951 	/*
2952 	 * In the case of ICMD defined, dest_port will contain the real port
2953 	 * we are trying to telnet to, and target_port will contain
2954 	 * "telnet-passthru" port.
2955 	 */
2956 	unsigned short target_port;
2957 	char abuf[INET6_ADDRSTRLEN];
2958 	int num_gw;
2959 	int ret_val;
2960 	boolean_t is_v4mapped;
2961 	/*
2962 	 * Type of addresses we'll try to connect to (ALL_ADDRS, ONLY_V6,
2963 	 * ONLY_V4).
2964 	 */
2965 	int addr_type;
2966 
2967 	/* clear the socket address prior to use */
2968 	(void) memset(&sin6, '\0', sizeof (sin6));
2969 	sin6.sin6_family = AF_INET6;
2970 
2971 	(void) memset(&sin, '\0', sizeof (sin));
2972 	sin.sin_family = AF_INET;
2973 
2974 	if (connected) {
2975 		(void) printf("?Already connected to %s\n", hostname);
2976 		return (0);
2977 	}
2978 #ifdef	ICMD
2979 	itelnet_host = getenv("INTERNET_HOST");
2980 	if (itelnet_host == NULL || itelnet_host[0] == '\0') {
2981 		(void) printf("INTERNET_HOST environment variable undefined\n");
2982 		goto tn_exit;
2983 	}
2984 #endif
2985 	if (argc < 2) {
2986 		(void) printf("(to) ");
2987 		if (GetAndAppendString(&line, &linesize, "open ",
2988 			stdin) == NULL) {
2989 			if (!feof(stdin)) {
2990 				perror("telnet");
2991 				goto tn_exit;
2992 			}
2993 		}
2994 		makeargv();
2995 		argc = margc;
2996 		argv = margv;
2997 	}
2998 	cmd = *argv;
2999 	--argc; ++argv;
3000 	while (argc) {
3001 		if (isprefix(*argv, "help") == 4 || isprefix(*argv, "?") == 1)
3002 			goto usage;
3003 		if (strcmp(*argv, "-l") == 0) {
3004 			--argc; ++argv;
3005 			if (argc == 0)
3006 				goto usage;
3007 			user = *argv++;
3008 			--argc;
3009 			continue;
3010 		}
3011 		if (strcmp(*argv, "-a") == 0) {
3012 			--argc; ++argv;
3013 			autologin = autologin_set = 1;
3014 			continue;
3015 		}
3016 		if (hostp == 0) {
3017 			hostp = *argv++;
3018 			--argc;
3019 			continue;
3020 		}
3021 		if (portp == 0) {
3022 			portp = *argv++;
3023 			--argc;
3024 			/*
3025 			 * Do we treat this like a telnet port or raw?
3026 			 */
3027 			if (*portp == '-') {
3028 				portp++;
3029 				telnetport = 1;
3030 			} else
3031 				telnetport = 0;
3032 			continue;
3033 		}
3034 usage:
3035 		(void) printf(
3036 		    "usage: %s [-l user] [-a] host-name [port]\n", cmd);
3037 		goto tn_exit;
3038 	}
3039 	if (hostp == 0)
3040 		goto usage;
3041 
3042 #ifdef ICMD
3043 	/*
3044 	 * For setup phase treat the relay host as the target host.
3045 	 */
3046 	real_host = hostp;
3047 	hostp = itelnet_host;
3048 #endif
3049 	num_gw = parse_input(hostp, hostname_list, &src_rtng_type);
3050 	if (num_gw < 0) {
3051 		goto tn_exit;
3052 	}
3053 
3054 	/* Last host in the hostname_list is the target */
3055 	hostp = hostname_list[num_gw];
3056 
3057 	host = resolve_hosts(hostname_list, num_gw, &gw_addrs, &addr_type,
3058 	    portp);
3059 	if (host == NULL) {
3060 		goto tn_exit;
3061 	}
3062 
3063 	/*
3064 	 * Check if number of gateways is less than max. available
3065 	 */
3066 	if ((addr_type == ALL_ADDRS || addr_type == ONLY_V6) &&
3067 	    num_gw > MAX_GATEWAY6) {
3068 		(void) fprintf(stderr, "telnet: too many IPv6 gateways\n");
3069 		goto tn_exit;
3070 	}
3071 
3072 	if ((addr_type == ALL_ADDRS || addr_type == ONLY_V4) &&
3073 	    num_gw > MAX_GATEWAY) {
3074 		(void) fprintf(stderr, "telnet: too many IPv4 gateways\n");
3075 		goto tn_exit;
3076 	}
3077 
3078 	/*
3079 	 * If we pass a literal IPv4 address to getaddrinfo(), in the
3080 	 * returned addrinfo structure, hostname is the IPv4-mapped IPv6
3081 	 * address string. We prefer to preserve the literal IPv4 address
3082 	 * string as the hostname.  Also, if the hostname entered by the
3083 	 * user is IPv4-mapped IPv6 address, we'll downgrade it to IPv4
3084 	 * address.
3085 	 */
3086 	if (inet_addr(hostp) != (in_addr_t)-1) {
3087 		/* this is a literal IPv4 address */
3088 		(void) strlcpy(_hostname, hostp, sizeof (_hostname));
3089 	} else if ((inet_pton(AF_INET6, hostp, &addr6) > 0) &&
3090 		    IN6_IS_ADDR_V4MAPPED(&addr6)) {
3091 		/* this is a IPv4-mapped IPv6 address */
3092 		IN6_V4MAPPED_TO_INADDR(&addr6, &addr);
3093 		(void) inet_ntop(AF_INET, &addr, _hostname, sizeof (_hostname));
3094 	} else {
3095 		(void) strlcpy(_hostname, host->ai_canonname,
3096 		    sizeof (_hostname));
3097 	}
3098 	hostname = _hostname;
3099 
3100 	if (portp == NULL) {
3101 		telnetport = 1;
3102 	}
3103 
3104 	if (host->ai_family == AF_INET) {
3105 		target_port = ((struct sockaddr_in *)(host->ai_addr))->sin_port;
3106 	} else {
3107 		target_port = ((struct sockaddr_in6 *)(host->ai_addr))
3108 		    ->sin6_port;
3109 	}
3110 
3111 #ifdef ICMD
3112 	/*
3113 	 * Since we pass the port number as an ascii string to the proxy,
3114 	 *  we need it in host format.
3115 	 */
3116 	dest_port = ntohs(target_port);
3117 	sp = getservbyname("telnet-passthru", "tcp");
3118 	if (sp == 0) {
3119 		(void) fprintf(stderr,
3120 		    "telnet: tcp/telnet-passthru: unknown service\n");
3121 			goto tn_exit;
3122 	}
3123 	target_port = sp->s_port;
3124 #endif
3125 	h = host;
3126 
3127 	/*
3128 	 * For IPv6 source routing, we need to initialize option buffer only
3129 	 * once.
3130 	 */
3131 	if (num_gw > 0 && (addr_type == ALL_ADDRS || addr_type == ONLY_V6)) {
3132 		if (!prepare_optbuf6(gw_addrs, num_gw, &opt_buf6,
3133 		    &opt_buf_len6)) {
3134 			goto tn_exit;
3135 		}
3136 	}
3137 
3138 	/*
3139 	 * We procure the Kerberos config files options only
3140 	 * if the user has choosen Krb5 authentication.
3141 	 */
3142 	if (krb5auth_flag > 0) {
3143 		krb5_profile_get_options(hostname, telnet_krb5_realm,
3144 			config_file_options);
3145 	}
3146 
3147 	if (encrypt_flag) {
3148 		extern boolean_t auth_enable_encrypt;
3149 		if (krb5_privacy_allowed()) {
3150 			encrypt_auto(1);
3151 			decrypt_auto(1);
3152 			wantencryption = B_TRUE;
3153 			autologin = 1;
3154 			auth_enable_encrypt = B_TRUE;
3155 		} else {
3156 			(void) fprintf(stderr, gettext(
3157 			    "%s:Encryption not supported.\n"), prompt);
3158 			exit(1);
3159 			}
3160 	}
3161 
3162 	if (forward_flag && forwardable_flag) {
3163 		(void) fprintf(stderr, gettext(
3164 			"Error in krb5 configuration file. "
3165 			"Both forward and forwardable are set.\n"));
3166 		exit(1);
3167 	}
3168 	if (forwardable_flag) {
3169 		forward_flags |= OPTS_FORWARD_CREDS | OPTS_FORWARDABLE_CREDS;
3170 	} else if (forward_flag)
3171 		forward_flags |= OPTS_FORWARD_CREDS;
3172 
3173 
3174 	do {
3175 		/*
3176 		 * Search for an address of desired type in the IP address list
3177 		 * of the target.
3178 		 */
3179 		while (h != NULL) {
3180 			struct sockaddr_in6 *addr;
3181 
3182 			addr = (struct sockaddr_in6 *)h->ai_addr;
3183 
3184 			if (h->ai_family == AF_INET6)
3185 				is_v4mapped =
3186 				    IN6_IS_ADDR_V4MAPPED(&addr->sin6_addr);
3187 			else
3188 				is_v4mapped = B_FALSE;
3189 
3190 			if (addr_type == ALL_ADDRS ||
3191 			    (addr_type == ONLY_V6 &&
3192 				h->ai_family == AF_INET6) ||
3193 			    (addr_type == ONLY_V4 &&
3194 				(h->ai_family == AF_INET || is_v4mapped)))
3195 				break;
3196 
3197 			/* skip undesired typed addresses */
3198 			h = h->ai_next;
3199 		}
3200 
3201 		if (h == NULL) {
3202 			fprintf(stderr,
3203 			    "telnet: Unable to connect to remote host");
3204 			goto tn_exit;
3205 		}
3206 
3207 		/*
3208 		 * We need to open a socket with a family matching the type of
3209 		 * address we are trying to connect to. This is because we
3210 		 * deal with IPv4 options and IPv6 extension headers.
3211 		 */
3212 		if (h->ai_family == AF_INET) {
3213 			addrp = &((struct sockaddr_in *)(h->ai_addr))->sin_addr;
3214 			((struct sockaddr_in *)(h->ai_addr))->sin_port =
3215 			    target_port;
3216 		} else {
3217 			addrp = &((struct sockaddr_in6 *)(h->ai_addr))
3218 			    ->sin6_addr;
3219 			((struct sockaddr_in6 *)(h->ai_addr))->sin6_port =
3220 			    target_port;
3221 		}
3222 
3223 		(void) printf("Trying %s...\n", inet_ntop(h->ai_family,
3224 		    addrp, abuf, sizeof (abuf)));
3225 
3226 		net = socket(h->ai_family, SOCK_STREAM, 0);
3227 
3228 		if (net < 0) {
3229 			perror("telnet: socket");
3230 			goto tn_exit;
3231 		}
3232 #ifndef ICMD
3233 		if (num_gw > 0) {
3234 			if (h->ai_family == AF_INET || is_v4mapped) {
3235 				if (!prepare_optbuf(gw_addrs, num_gw, &opt_buf,
3236 				    &opt_buf_len, addrp, src_rtng_type)) {
3237 					goto tn_exit;
3238 				}
3239 
3240 				if (setsockopt(net, IPPROTO_IP, IP_OPTIONS,
3241 				    opt_buf, opt_buf_len) < 0)
3242 					perror("setsockopt (IP_OPTIONS)");
3243 			} else {
3244 				if (setsockopt(net, IPPROTO_IPV6, IPV6_RTHDR,
3245 				    opt_buf6, opt_buf_len6) < 0)
3246 					perror("setsockopt (IPV6_RTHDR)");
3247 			}
3248 		}
3249 #endif
3250 #if	defined(USE_TOS)
3251 		if (is_v4mapped) {
3252 			if (tos < 0)
3253 				tos = 020;	/* Low Delay bit */
3254 			if (tos &&
3255 			    (setsockopt(net, IPPROTO_IP, IP_TOS,
3256 			    &tos, sizeof (int)) < 0) &&
3257 			    (errno != ENOPROTOOPT))
3258 				perror("telnet: setsockopt (IP_TOS) (ignored)");
3259 		}
3260 #endif	/* defined(USE_TOS) */
3261 
3262 		if (debug && SetSockOpt(net, SOL_SOCKET, SO_DEBUG, 1) < 0) {
3263 			perror("setsockopt (SO_DEBUG)");
3264 		}
3265 
3266 		ret_val = connect(net, h->ai_addr, h->ai_addrlen);
3267 
3268 		/*
3269 		 * If failed, try the next address of the target.
3270 		 */
3271 		if (ret_val < 0) {
3272 			Close(&net);
3273 			if (h->ai_next != NULL) {
3274 
3275 				int oerrno = errno;
3276 
3277 				(void) fprintf(stderr,
3278 				    "telnet: connect to address %s: ", abuf);
3279 				errno = oerrno;
3280 				perror((char *)0);
3281 
3282 				h = h->ai_next;
3283 				continue;
3284 			}
3285 			perror("telnet: Unable to connect to remote host");
3286 			goto tn_exit;
3287 		}
3288 		connected++;
3289 	} while (connected == 0);
3290 	freeaddrinfo(host);
3291 	host = NULL;
3292 #ifdef ICMD
3293 	/*
3294 	 * Do initial protocol to connect to farther end...
3295 	 */
3296 	{
3297 		char buf[1024];
3298 		(void) sprintf(buf, "%s %d\n", real_host, (int)dest_port);
3299 		write(net, buf, strlen(buf));
3300 	}
3301 #endif
3302 	if (cmdrc(hostp, hostname) != 0)
3303 		goto tn_exit;
3304 	FreeHostnameList(hostname_list);
3305 	if (autologin && user == NULL) {
3306 		struct passwd *pw;
3307 
3308 		user = getenv("LOGNAME");
3309 		if (user == NULL ||
3310 		    ((pw = getpwnam(user)) != NULL) &&
3311 		    pw->pw_uid != getuid()) {
3312 			if (pw = getpwuid(getuid()))
3313 				user = pw->pw_name;
3314 			else
3315 				user = NULL;
3316 		}
3317 	}
3318 
3319 	if (user) {
3320 		if (env_define((unsigned char *)"USER", (unsigned char *)user))
3321 			env_export((unsigned char *)"USER");
3322 		else {
3323 			/* Clean up and exit. */
3324 			Close(&net);
3325 			(void) snprintf(buf, sizeof (buf),
3326 			    "Connection to %.*s closed.\n",
3327 			    MAXHOSTNAMELEN, hostname);
3328 			ExitString(buf, EXIT_FAILURE);
3329 
3330 			/* NOTREACHED */
3331 		}
3332 	}
3333 	(void) call(3, status, "status", "notmuch");
3334 	if (setjmp(peerdied) == 0)
3335 		telnet(user);
3336 
3337 	Close(&net);
3338 
3339 	(void) snprintf(buf, sizeof (buf),
3340 	    "Connection to %.*s closed by foreign host.\n",
3341 	    MAXHOSTNAMELEN, hostname);
3342 	ExitString(buf, EXIT_FAILURE);
3343 
3344 	/*NOTREACHED*/
3345 
3346 tn_exit:
3347 	FreeHostnameList(hostname_list);
3348 	Close(&net);
3349 	connected = 0;
3350 	if (host != NULL)
3351 		freeaddrinfo(host);
3352 	return (0);
3353 }
3354 
3355 #define	HELPINDENT (sizeof ("connect"))
3356 
3357 static char openhelp[] = "connect to a site";
3358 static char closehelp[] = "close current connection";
3359 static char logouthelp[] =
3360 	    "forcibly logout remote user and close the connection";
3361 static char quithelp[] = "exit telnet";
3362 static char statushelp[] = "print status information";
3363 static char helphelp[] = "print help information";
3364 static char sendhelp[] =
3365 	    "transmit special characters ('send ?' for more)";
3366 static char sethelp[] =  "set operating parameters ('set ?' for more)";
3367 static char unsethelp[] = "unset operating parameters ('unset ?' for more)";
3368 static char togglestring[] =
3369 	    "toggle operating parameters ('toggle ?' for more)";
3370 static char slchelp[] = "change state of special charaters ('slc ?' for more)";
3371 static char displayhelp[] = "display operating parameters";
3372 static char authhelp[] =
3373 	    "turn on (off) authentication ('auth ?' for more)";
3374 static char forwardhelp[] =
3375 	    "turn on (off) credential forwarding ('forward ?' for more)";
3376 static char encrypthelp[] =
3377 	    "turn on (off) encryption ('encrypt ?' for more)";
3378 static char zhelp[] = "suspend telnet";
3379 static char shellhelp[] = "invoke a subshell";
3380 static char envhelp[] = "change environment variables ('environ ?' for more)";
3381 static char modestring[] =
3382 	    "try to enter line or character mode ('mode ?' for more)";
3383 
3384 static int	help();
3385 
3386 static Command cmdtab[] = {
3387 	{ "close",	closehelp,	bye,		1 },
3388 	{ "logout",	logouthelp,	logout,		1 },
3389 	{ "display",	displayhelp,	display,	0 },
3390 	{ "mode",	modestring,	modecmd,	0 },
3391 	{ "open",	openhelp,	tn,		0 },
3392 	{ "quit",	quithelp,	quit,		0 },
3393 	{ "send",	sendhelp,	sendcmd,	0 },
3394 	{ "set",	sethelp,	setcmd,		0 },
3395 	{ "unset",	unsethelp,	unsetcmd,	0 },
3396 	{ "status",	statushelp,	status,		0 },
3397 	{ "toggle",	togglestring,	toggle,		0 },
3398 	{ "slc",	slchelp,	slccmd,		0 },
3399 	{ "auth",	authhelp,	auth_cmd,	0 },
3400 	{ "encrypt",	encrypthelp,	encrypt_cmd,	0 },
3401 	{ "forward",	forwardhelp,	forw_cmd,	0 },
3402 	{ "z",		zhelp,		suspend,	0 },
3403 	{ "!",		shellhelp,	shell,		0 },
3404 	{ "environ",	envhelp,	env_cmd,	0 },
3405 	{ "?",		helphelp,	help,		0 },
3406 	0
3407 };
3408 
3409 
3410 static Command cmdtab2[] = {
3411 	{ "help",	0,		help,		0 },
3412 	{ "escape",	0,		setescape,	0 },
3413 	{ "crmod",	0,		togcrmod,	0 },
3414 	0
3415 };
3416 
3417 
3418 /*
3419  * Call routine with argc, argv set from args.
3420  * Uses /usr/include/stdarg.h
3421  */
3422 #define	MAXVARGS	100
3423 /*VARARGS1*/
3424 static void
3425 call(int n_ptrs, ...)
3426 {
3427 	va_list ap;
3428 	typedef int (*intrtn_t)();
3429 	intrtn_t routine;
3430 	char *args[MAXVARGS+1];	/* leave 1 for trailing NULL */
3431 	int argno = 0;
3432 
3433 	if (n_ptrs > MAXVARGS)
3434 		n_ptrs = MAXVARGS;
3435 	va_start(ap, n_ptrs);
3436 
3437 	routine = (va_arg(ap, intrtn_t)); /* extract the routine's name */
3438 	n_ptrs--;
3439 
3440 	while (argno < n_ptrs)	/* extract the routine's args */
3441 		args[argno++] = va_arg(ap, char *);
3442 	args[argno] = NULL;	/* NULL terminate for good luck */
3443 	va_end(ap);
3444 
3445 	(*routine)(argno, args);
3446 }
3447 
3448 
3449 static Command *
3450 getcmd(name)
3451 	char *name;
3452 {
3453 	Command *cm;
3454 
3455 	if (cm = (Command *) genget(name, (char **)cmdtab, sizeof (Command)))
3456 		return (cm);
3457 	return (Command *) genget(name, (char **)cmdtab2, sizeof (Command));
3458 }
3459 
3460 void
3461 command(top, tbuf, cnt)
3462 	int top;
3463 	char *tbuf;
3464 	int cnt;
3465 {
3466 	Command *c;
3467 
3468 	setcommandmode();
3469 	if (!top) {
3470 		(void) putchar('\n');
3471 	} else {
3472 		(void) signal(SIGINT, SIG_DFL);
3473 		(void) signal(SIGQUIT, SIG_DFL);
3474 	}
3475 	for (;;) {
3476 		if (rlogin == _POSIX_VDISABLE)
3477 			(void) printf("%s> ", prompt);
3478 		if (tbuf) {
3479 			char *cp;
3480 			if (AllocStringBuffer(&line, &linesize, cnt) == NULL)
3481 				goto command_exit;
3482 			cp = line;
3483 			while (cnt > 0 && (*cp++ = *tbuf++) != '\n')
3484 				cnt--;
3485 			tbuf = 0;
3486 			if (cp == line || *--cp != '\n' || cp == line)
3487 				goto getline;
3488 			*cp = '\0';
3489 			if (rlogin == _POSIX_VDISABLE)
3490 				(void) printf("%s\n", line);
3491 		} else {
3492 getline:
3493 			if (rlogin != _POSIX_VDISABLE)
3494 				(void) printf("%s> ", prompt);
3495 			if (GetString(&line, &linesize, stdin) == NULL) {
3496 				if (!feof(stdin))
3497 					perror("telnet");
3498 				(void) quit();
3499 				/*NOTREACHED*/
3500 				break;
3501 			}
3502 		}
3503 		if (line[0] == 0)
3504 			break;
3505 		makeargv();
3506 		if (margv[0] == 0) {
3507 			break;
3508 		}
3509 		c = getcmd(margv[0]);
3510 		if (Ambiguous(c)) {
3511 			(void) printf("?Ambiguous command\n");
3512 			continue;
3513 		}
3514 		if (c == 0) {
3515 			(void) printf("?Invalid command\n");
3516 			continue;
3517 		}
3518 		if (c->needconnect && !connected) {
3519 			(void) printf("?Need to be connected first.\n");
3520 			continue;
3521 		}
3522 		if ((*c->handler)(margc, margv)) {
3523 			break;
3524 		}
3525 	}
3526 command_exit:
3527 	if (!top) {
3528 		if (!connected) {
3529 			longjmp(toplevel, 1);
3530 			/*NOTREACHED*/
3531 		}
3532 		setconnmode(0);
3533 	}
3534 }
3535 
3536 /*
3537  * Help command.
3538  */
3539 	static int
3540 help(argc, argv)
3541 	int argc;
3542 	char *argv[];
3543 {
3544 	register Command *c;
3545 
3546 	if (argc == 1) {
3547 		(void) printf(
3548 		    "Commands may be abbreviated.  Commands are:\n\n");
3549 		for (c = cmdtab; c->name; c++)
3550 			if (c->help) {
3551 				(void) printf("%-*s\t%s\n", HELPINDENT,
3552 					c->name, c->help);
3553 			}
3554 		(void) printf("<return>\tleave command mode\n");
3555 		return (0);
3556 	}
3557 	while (--argc > 0) {
3558 		register char *arg;
3559 		arg = *++argv;
3560 		c = getcmd(arg);
3561 		if (Ambiguous(c))
3562 			(void) printf("?Ambiguous help command %s\n", arg);
3563 		else if (c == (Command *)0)
3564 			(void) printf("?Invalid help command %s\n", arg);
3565 		else if (c->help) {
3566 			(void) printf("%s\n", c->help);
3567 		} else  {
3568 			(void) printf("No additional help on %s\n", arg);
3569 		}
3570 	}
3571 	return (0);
3572 }
3573 
3574 static char *rcname = NULL;
3575 #define	TELNETRC_NAME "telnetrc"
3576 #define	TELNETRC_COMP "/." TELNETRC_NAME
3577 
3578 static int
3579 cmdrc(char *m1, char *m2)
3580 {
3581 	Command *c;
3582 	FILE *rcfile = NULL;
3583 	int gotmachine = 0;
3584 	int l1 = strlen(m1);
3585 	int l2 = strlen(m2);
3586 	char m1save[MAXHOSTNAMELEN];
3587 	int ret = 0;
3588 	char def[] = "DEFAULT";
3589 
3590 	if (skiprc)
3591 		goto cmdrc_exit;
3592 
3593 	doing_rc = 1;
3594 
3595 	(void) strlcpy(m1save, m1, sizeof (m1save));
3596 	m1 = m1save;
3597 
3598 	if (rcname == NULL) {
3599 		char *homedir;
3600 		unsigned rcbuflen;
3601 
3602 		if ((homedir = getenv("HOME")) == NULL)
3603 			homedir = "";
3604 
3605 		rcbuflen = strlen(homedir) + strlen(TELNETRC_COMP) + 1;
3606 		if ((rcname = malloc(rcbuflen)) == NULL) {
3607 			perror("telnet: can't process " TELNETRC_NAME);
3608 			ret = 1;
3609 			goto cmdrc_exit;
3610 		}
3611 		(void) strcpy(rcname, homedir);
3612 		(void) strcat(rcname, TELNETRC_COMP);
3613 	}
3614 
3615 	if ((rcfile = fopen(rcname, "r")) == NULL)
3616 		goto cmdrc_exit;
3617 
3618 	for (;;) {
3619 		if (GetString(&line, &linesize, rcfile) == NULL) {
3620 			if (!feof(rcfile)) {
3621 				perror("telnet: error reading " TELNETRC_NAME);
3622 				ret = 1;
3623 				goto cmdrc_exit;
3624 			}
3625 			break;
3626 		}
3627 		if (line[0] == 0)
3628 			continue;
3629 		if (line[0] == '#')
3630 			continue;
3631 		if (gotmachine) {
3632 			if (!isspace(line[0]))
3633 			gotmachine = 0;
3634 		}
3635 		if (gotmachine == 0) {
3636 			if (isspace(line[0]))
3637 				continue;
3638 			if (strncasecmp(line, m1, l1) == 0)
3639 				(void) strcpy(line, &line[l1]);
3640 			else if (strncasecmp(line, m2, l2) == 0)
3641 				(void) strcpy(line, &line[l2]);
3642 			else if (strncasecmp(line, def, sizeof (def) - 1) == 0)
3643 				(void) strcpy(line, &line[sizeof (def) - 1]);
3644 			else
3645 				continue;
3646 			if (line[0] != ' ' && line[0] != '\t' &&
3647 			    line[0] != '\n')
3648 				continue;
3649 			gotmachine = 1;
3650 		}
3651 		makeargv();
3652 		if (margv[0] == 0)
3653 			continue;
3654 		c = getcmd(margv[0]);
3655 		if (Ambiguous(c)) {
3656 			(void) printf("?Ambiguous command: %s\n", margv[0]);
3657 			continue;
3658 		}
3659 		if (c == 0) {
3660 			(void) printf("?Invalid command: %s\n", margv[0]);
3661 			continue;
3662 		}
3663 		/*
3664 		 * This should never happen...
3665 		 */
3666 		if (c->needconnect && !connected) {
3667 			(void) printf("?Need to be connected first for %s.\n",
3668 			    margv[0]);
3669 			continue;
3670 		}
3671 		(*c->handler)(margc, margv);
3672 	}
3673 cmdrc_exit:
3674 	if (rcfile != NULL)
3675 		(void) fclose(rcfile);
3676 	doing_rc = 0;
3677 
3678 	return (ret);
3679 }
3680