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