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
34#include "telnet_locl.h"
35
36RCSID("$Id$");
37
38#if	defined(IPPROTO_IP) && defined(IP_TOS)
39int tos = -1;
40#endif	/* defined(IPPROTO_IP) && defined(IP_TOS) */
41
42char	*hostname;
43static char _hostname[MaxHostNameLen];
44
45typedef int (*intrtn_t)(int, char**);
46static int call(intrtn_t, ...);
47
48typedef struct {
49	char	*name;		/* command name */
50	char	*help;		/* help string (NULL for no help) */
51	int	(*handler)();	/* routine which executes command */
52	int	needconnect;	/* Do we need to be connected to execute? */
53} Command;
54
55static char line[256];
56static char saveline[256];
57static int margc;
58static char *margv[20];
59
60static void
61makeargv()
62{
63    char *cp, *cp2, c;
64    char **argp = margv;
65
66    margc = 0;
67    cp = line;
68    if (*cp == '!') {		/* Special case shell escape */
69	/* save for shell command */
70	strlcpy(saveline, line, sizeof(saveline));
71	*argp++ = "!";		/* No room in string to get this */
72	margc++;
73	cp++;
74    }
75    while ((c = *cp)) {
76	int inquote = 0;
77	while (isspace((unsigned char)c))
78	    c = *++cp;
79	if (c == '\0')
80	    break;
81	*argp++ = cp;
82	margc += 1;
83	for (cp2 = cp; c != '\0'; c = *++cp) {
84	    if (inquote) {
85		if (c == inquote) {
86		    inquote = 0;
87		    continue;
88		}
89	    } else {
90		if (c == '\\') {
91		    if ((c = *++cp) == '\0')
92			break;
93		} else if (c == '"') {
94		    inquote = '"';
95		    continue;
96		} else if (c == '\'') {
97		    inquote = '\'';
98		    continue;
99		} else if (isspace((unsigned char)c))
100		    break;
101	    }
102	    *cp2++ = c;
103	}
104	*cp2 = '\0';
105	if (c == '\0')
106	    break;
107	cp++;
108    }
109    *argp++ = 0;
110}
111
112/*
113 * Make a character string into a number.
114 *
115 * Todo:  1.  Could take random integers (12, 0x12, 012, 0b1).
116 */
117
118static char
119special(char *s)
120{
121	char c;
122	char b;
123
124	switch (*s) {
125	case '^':
126		b = *++s;
127		if (b == '?') {
128		    c = b | 0x40;		/* DEL */
129		} else {
130		    c = b & 0x1f;
131		}
132		break;
133	default:
134		c = *s;
135		break;
136	}
137	return c;
138}
139
140/*
141 * Construct a control character sequence
142 * for a special character.
143 */
144static char *
145control(cc_t c)
146{
147	static char buf[5];
148	/*
149	 * The only way I could get the Sun 3.5 compiler
150	 * to shut up about
151	 *	if ((unsigned int)c >= 0x80)
152	 * was to assign "c" to an unsigned int variable...
153	 * Arggg....
154	 */
155	unsigned int uic = (unsigned int)c;
156
157	if (uic == 0x7f)
158		return ("^?");
159	if (c == (cc_t)_POSIX_VDISABLE) {
160		return "off";
161	}
162	if (uic >= 0x80) {
163		buf[0] = '\\';
164		buf[1] = ((c>>6)&07) + '0';
165		buf[2] = ((c>>3)&07) + '0';
166		buf[3] = (c&07) + '0';
167		buf[4] = 0;
168	} else if (uic >= 0x20) {
169		buf[0] = c;
170		buf[1] = 0;
171	} else {
172		buf[0] = '^';
173		buf[1] = '@'+c;
174		buf[2] = 0;
175	}
176	return (buf);
177}
178
179
180
181/*
182 *	The following are data structures and routines for
183 *	the "send" command.
184 *
185 */
186
187struct sendlist {
188    char	*name;		/* How user refers to it (case independent) */
189    char	*help;		/* Help information (0 ==> no help) */
190    int		needconnect;	/* Need to be connected */
191    int		narg;		/* Number of arguments */
192    int		(*handler)();	/* Routine to perform (for special ops) */
193    int		nbyte;		/* Number of bytes to send this command */
194    int		what;		/* Character to be sent (<0 ==> special) */
195};
196
197
198static int
199	send_esc (void),
200	send_help (void),
201	send_docmd (char *),
202	send_dontcmd (char *),
203	send_willcmd (char *),
204	send_wontcmd (char *);
205
206static struct sendlist Sendlist[] = {
207    { "ao",	"Send Telnet Abort output",		1, 0, 0, 2, AO },
208    { "ayt",	"Send Telnet 'Are You There'",		1, 0, 0, 2, AYT },
209    { "brk",	"Send Telnet Break",			1, 0, 0, 2, BREAK },
210    { "break",	0,					1, 0, 0, 2, BREAK },
211    { "ec",	"Send Telnet Erase Character",		1, 0, 0, 2, EC },
212    { "el",	"Send Telnet Erase Line",		1, 0, 0, 2, EL },
213    { "escape",	"Send current escape character",	1, 0, send_esc, 1, 0 },
214    { "ga",	"Send Telnet 'Go Ahead' sequence",	1, 0, 0, 2, GA },
215    { "ip",	"Send Telnet Interrupt Process",	1, 0, 0, 2, IP },
216    { "intp",	0,					1, 0, 0, 2, IP },
217    { "interrupt", 0,					1, 0, 0, 2, IP },
218    { "intr",	0,					1, 0, 0, 2, IP },
219    { "nop",	"Send Telnet 'No operation'",		1, 0, 0, 2, NOP },
220    { "eor",	"Send Telnet 'End of Record'",		1, 0, 0, 2, EOR },
221    { "abort",	"Send Telnet 'Abort Process'",		1, 0, 0, 2, ABORT },
222    { "susp",	"Send Telnet 'Suspend Process'",	1, 0, 0, 2, SUSP },
223    { "eof",	"Send Telnet End of File Character",	1, 0, 0, 2, xEOF },
224    { "synch",	"Perform Telnet 'Synch operation'",	1, 0, dosynch, 2, 0 },
225    { "getstatus", "Send request for STATUS",		1, 0, get_status, 6, 0 },
226    { "?",	"Display send options",			0, 0, send_help, 0, 0 },
227    { "help",	0,					0, 0, send_help, 0, 0 },
228    { "do",	0,					0, 1, send_docmd, 3, 0 },
229    { "dont",	0,					0, 1, send_dontcmd, 3, 0 },
230    { "will",	0,					0, 1, send_willcmd, 3, 0 },
231    { "wont",	0,					0, 1, send_wontcmd, 3, 0 },
232    { 0 }
233};
234
235#define	GETSEND(name) ((struct sendlist *) genget(name, (char **) Sendlist, \
236				sizeof(struct sendlist)))
237
238static int
239sendcmd(int argc, char **argv)
240{
241    int count;		/* how many bytes we are going to need to send */
242    int i;
243    struct sendlist *s;	/* pointer to current command */
244    int success = 0;
245    int needconnect = 0;
246
247    if (argc < 2) {
248	printf("need at least one argument for 'send' command\r\n");
249	printf("'send ?' for help\r\n");
250	return 0;
251    }
252    /*
253     * First, validate all the send arguments.
254     * In addition, we see how much space we are going to need, and
255     * whether or not we will be doing a "SYNCH" operation (which
256     * flushes the network queue).
257     */
258    count = 0;
259    for (i = 1; i < argc; i++) {
260	s = GETSEND(argv[i]);
261	if (s == 0) {
262	    printf("Unknown send argument '%s'\r\n'send ?' for help.\r\n",
263			argv[i]);
264	    return 0;
265	} else if (Ambiguous(s)) {
266	    printf("Ambiguous send argument '%s'\r\n'send ?' for help.\r\n",
267			argv[i]);
268	    return 0;
269	}
270	if (i + s->narg >= argc) {
271	    fprintf(stderr,
272	    "Need %d argument%s to 'send %s' command.  'send %s ?' for help.\r\n",
273		s->narg, s->narg == 1 ? "" : "s", s->name, s->name);
274	    return 0;
275	}
276	count += s->nbyte;
277	if (s->handler == send_help) {
278	    send_help();
279	    return 0;
280	}
281
282	i += s->narg;
283	needconnect += s->needconnect;
284    }
285    if (!connected && needconnect) {
286	printf("?Need to be connected first.\r\n");
287	printf("'send ?' for help\r\n");
288	return 0;
289    }
290    /* Now, do we have enough room? */
291    if (NETROOM() < count) {
292	printf("There is not enough room in the buffer TO the network\r\n");
293	printf("to process your request.  Nothing will be done.\r\n");
294	printf("('send synch' will throw away most data in the network\r\n");
295	printf("buffer, if this might help.)\r\n");
296	return 0;
297    }
298    /* OK, they are all OK, now go through again and actually send */
299    count = 0;
300    for (i = 1; i < argc; i++) {
301	if ((s = GETSEND(argv[i])) == 0) {
302	    fprintf(stderr, "Telnet 'send' error - argument disappeared!\r\n");
303	    quit();
304	    /*NOTREACHED*/
305	}
306	if (s->handler) {
307	    count++;
308	    success += (*s->handler)((s->narg > 0) ? argv[i+1] : 0,
309				  (s->narg > 1) ? argv[i+2] : 0);
310	    i += s->narg;
311	} else {
312	    NET2ADD(IAC, s->what);
313	    printoption("SENT", IAC, s->what);
314	}
315    }
316    return (count == success);
317}
318
319static int
320send_tncmd(void (*func)(), char *cmd, char *name);
321
322static int
323send_esc()
324{
325    NETADD(escape);
326    return 1;
327}
328
329static int
330send_docmd(char *name)
331{
332    return(send_tncmd(send_do, "do", name));
333}
334
335static int
336send_dontcmd(char *name)
337{
338    return(send_tncmd(send_dont, "dont", name));
339}
340
341static int
342send_willcmd(char *name)
343{
344    return(send_tncmd(send_will, "will", name));
345}
346
347static int
348send_wontcmd(char *name)
349{
350    return(send_tncmd(send_wont, "wont", name));
351}
352
353extern char *telopts[];		/* XXX */
354
355static int
356send_tncmd(void (*func)(), char *cmd, char *name)
357{
358    char **cpp;
359    int val = 0;
360
361    if (isprefix(name, "help") || isprefix(name, "?")) {
362	int col, len;
363
364	printf("Usage: send %s <value|option>\r\n", cmd);
365	printf("\"value\" must be from 0 to 255\r\n");
366	printf("Valid options are:\r\n\t");
367
368	col = 8;
369	for (cpp = telopts; *cpp; cpp++) {
370	    len = strlen(*cpp) + 3;
371	    if (col + len > 65) {
372		printf("\r\n\t");
373		col = 8;
374	    }
375	    printf(" \"%s\"", *cpp);
376	    col += len;
377	}
378	printf("\r\n");
379	return 0;
380    }
381    cpp = genget(name, telopts, sizeof(char *));
382    if (Ambiguous(cpp)) {
383	fprintf(stderr,"'%s': ambiguous argument ('send %s ?' for help).\r\n",
384					name, cmd);
385	return 0;
386    }
387    if (cpp) {
388	val = cpp - telopts;
389    } else {
390	char *cp = name;
391
392	while (*cp >= '0' && *cp <= '9') {
393	    val *= 10;
394	    val += *cp - '0';
395	    cp++;
396	}
397	if (*cp != 0) {
398	    fprintf(stderr, "'%s': unknown argument ('send %s ?' for help).\r\n",
399					name, cmd);
400	    return 0;
401	} else if (val < 0 || val > 255) {
402	    fprintf(stderr, "'%s': bad value ('send %s ?' for help).\r\n",
403					name, cmd);
404	    return 0;
405	}
406    }
407    if (!connected) {
408	printf("?Need to be connected first.\r\n");
409	return 0;
410    }
411    (*func)(val, 1);
412    return 1;
413}
414
415static int
416send_help()
417{
418    struct sendlist *s;	/* pointer to current command */
419    for (s = Sendlist; s->name; s++) {
420	if (s->help)
421	    printf("%-15s %s\r\n", s->name, s->help);
422    }
423    return(0);
424}
425
426/*
427 * The following are the routines and data structures referred
428 * to by the arguments to the "toggle" command.
429 */
430
431static int
432lclchars()
433{
434    donelclchars = 1;
435    return 1;
436}
437
438static int
439togdebug()
440{
441#ifndef	NOT43
442    if (net > 0 &&
443	(SetSockOpt(net, SOL_SOCKET, SO_DEBUG, debug)) < 0) {
444	    perror("setsockopt (SO_DEBUG)");
445    }
446#else	/* NOT43 */
447    if (debug) {
448	if (net > 0 && SetSockOpt(net, SOL_SOCKET, SO_DEBUG, 0, 0) < 0)
449	    perror("setsockopt (SO_DEBUG)");
450    } else
451	printf("Cannot turn off socket debugging\r\n");
452#endif	/* NOT43 */
453    return 1;
454}
455
456static int
457togcrlf()
458{
459    if (crlf) {
460	printf("Will send carriage returns as telnet <CR><LF>.\r\n");
461    } else {
462	printf("Will send carriage returns as telnet <CR><NUL>.\r\n");
463    }
464    return 1;
465}
466
467int binmode;
468
469static int
470togbinary(int val)
471{
472    donebinarytoggle = 1;
473
474    if (val >= 0) {
475	binmode = val;
476    } else {
477	if (my_want_state_is_will(TELOPT_BINARY) &&
478				my_want_state_is_do(TELOPT_BINARY)) {
479	    binmode = 1;
480	} else if (my_want_state_is_wont(TELOPT_BINARY) &&
481				my_want_state_is_dont(TELOPT_BINARY)) {
482	    binmode = 0;
483	}
484	val = binmode ? 0 : 1;
485    }
486
487    if (val == 1) {
488	if (my_want_state_is_will(TELOPT_BINARY) &&
489					my_want_state_is_do(TELOPT_BINARY)) {
490	    printf("Already operating in binary mode with remote host.\r\n");
491	} else {
492	    printf("Negotiating binary mode with remote host.\r\n");
493	    tel_enter_binary(3);
494	}
495    } else {
496	if (my_want_state_is_wont(TELOPT_BINARY) &&
497					my_want_state_is_dont(TELOPT_BINARY)) {
498	    printf("Already in network ascii mode with remote host.\r\n");
499	} else {
500	    printf("Negotiating network ascii mode with remote host.\r\n");
501	    tel_leave_binary(3);
502	}
503    }
504    return 1;
505}
506
507static int
508togrbinary(int val)
509{
510    donebinarytoggle = 1;
511
512    if (val == -1)
513	val = my_want_state_is_do(TELOPT_BINARY) ? 0 : 1;
514
515    if (val == 1) {
516	if (my_want_state_is_do(TELOPT_BINARY)) {
517	    printf("Already receiving in binary mode.\r\n");
518	} else {
519	    printf("Negotiating binary mode on input.\r\n");
520	    tel_enter_binary(1);
521	}
522    } else {
523	if (my_want_state_is_dont(TELOPT_BINARY)) {
524	    printf("Already receiving in network ascii mode.\r\n");
525	} else {
526	    printf("Negotiating network ascii mode on input.\r\n");
527	    tel_leave_binary(1);
528	}
529    }
530    return 1;
531}
532
533static int
534togxbinary(int val)
535{
536    donebinarytoggle = 1;
537
538    if (val == -1)
539	val = my_want_state_is_will(TELOPT_BINARY) ? 0 : 1;
540
541    if (val == 1) {
542	if (my_want_state_is_will(TELOPT_BINARY)) {
543	    printf("Already transmitting in binary mode.\r\n");
544	} else {
545	    printf("Negotiating binary mode on output.\r\n");
546	    tel_enter_binary(2);
547	}
548    } else {
549	if (my_want_state_is_wont(TELOPT_BINARY)) {
550	    printf("Already transmitting in network ascii mode.\r\n");
551	} else {
552	    printf("Negotiating network ascii mode on output.\r\n");
553	    tel_leave_binary(2);
554	}
555    }
556    return 1;
557}
558
559
560static int togglehelp (void);
561#if	defined(AUTHENTICATION)
562extern int auth_togdebug (int);
563#endif
564#if	defined(ENCRYPTION)
565extern int EncryptAutoEnc (int);
566extern int EncryptAutoDec (int);
567extern int EncryptDebug (int);
568extern int EncryptVerbose (int);
569#endif
570
571struct togglelist {
572    char	*name;		/* name of toggle */
573    char	*help;		/* help message */
574    int		(*handler)();	/* routine to do actual setting */
575    int		*variable;
576    char	*actionexplanation;
577};
578
579static struct togglelist Togglelist[] = {
580    { "autoflush",
581	"flushing of output when sending interrupt characters",
582	    0,
583		&autoflush,
584		    "flush output when sending interrupt characters" },
585    { "autosynch",
586	"automatic sending of interrupt characters in urgent mode",
587	    0,
588		&autosynch,
589		    "send interrupt characters in urgent mode" },
590#if	defined(AUTHENTICATION)
591    { "autologin",
592	"automatic sending of login and/or authentication info",
593	    0,
594		&autologin,
595		    "send login name and/or authentication information" },
596    { "authdebug",
597	"authentication debugging",
598	    auth_togdebug,
599		0,
600		     "print authentication debugging information" },
601#endif
602#if	defined(ENCRYPTION)
603    { "autoencrypt",
604	"automatic encryption of data stream",
605	    EncryptAutoEnc,
606		0,
607		    "automatically encrypt output" },
608    { "autodecrypt",
609	"automatic decryption of data stream",
610	    EncryptAutoDec,
611		0,
612		    "automatically decrypt input" },
613    { "verbose_encrypt",
614	"verbose encryption output",
615	    EncryptVerbose,
616		0,
617		    "print verbose encryption output" },
618    { "encdebug",
619	"encryption debugging",
620	    EncryptDebug,
621		0,
622		    "print encryption debugging information" },
623#endif
624#if defined(KRB5)
625    { "forward",
626	"credentials forwarding",
627	    kerberos5_set_forward,
628		0,
629		    "forward credentials" },
630    { "forwardable",
631	"forwardable flag of forwarded credentials",
632	    kerberos5_set_forwardable,
633		0,
634		    "forward forwardable credentials" },
635#endif
636   { "skiprc",
637	"don't read ~/.telnetrc file",
638	    0,
639		&skiprc,
640		    "skip reading of ~/.telnetrc file" },
641    { "binary",
642	"sending and receiving of binary data",
643	    togbinary,
644		0,
645		    0 },
646    { "inbinary",
647	"receiving of binary data",
648	    togrbinary,
649		0,
650		    0 },
651    { "outbinary",
652	"sending of binary data",
653	    togxbinary,
654		0,
655		    0 },
656    { "crlf",
657	"sending carriage returns as telnet <CR><LF>",
658	    togcrlf,
659		&crlf,
660		    0 },
661    { "crmod",
662	"mapping of received carriage returns",
663	    0,
664		&crmod,
665		    "map carriage return on output" },
666    { "localchars",
667	"local recognition of certain control characters",
668	    lclchars,
669		&localchars,
670		    "recognize certain control characters" },
671    { " ", "", 0 },		/* empty line */
672    { "debug",
673	"debugging",
674	    togdebug,
675		&debug,
676		    "turn on socket level debugging" },
677    { "netdata",
678	"printing of hexadecimal network data (debugging)",
679	    0,
680		&netdata,
681		    "print hexadecimal representation of network traffic" },
682    { "prettydump",
683	"output of \"netdata\" to user readable format (debugging)",
684	    0,
685		&prettydump,
686		    "print user readable output for \"netdata\"" },
687    { "options",
688	"viewing of options processing (debugging)",
689	    0,
690		&showoptions,
691		    "show option processing" },
692    { "termdata",
693	"printing of hexadecimal terminal data (debugging)",
694	    0,
695		&termdata,
696		    "print hexadecimal representation of terminal traffic" },
697    { "?",
698	0,
699	    togglehelp },
700    { "help",
701	0,
702	    togglehelp },
703    { 0 }
704};
705
706static int
707togglehelp()
708{
709    struct togglelist *c;
710
711    for (c = Togglelist; c->name; c++) {
712	if (c->help) {
713	    if (*c->help)
714		printf("%-15s toggle %s\r\n", c->name, c->help);
715	    else
716		printf("\r\n");
717	}
718    }
719    printf("\r\n");
720    printf("%-15s %s\r\n", "?", "display help information");
721    return 0;
722}
723
724static void
725settogglehelp(int set)
726{
727    struct togglelist *c;
728
729    for (c = Togglelist; c->name; c++) {
730	if (c->help) {
731	    if (*c->help)
732		printf("%-15s %s %s\r\n", c->name, set ? "enable" : "disable",
733						c->help);
734	    else
735		printf("\r\n");
736	}
737    }
738}
739
740#define	GETTOGGLE(name) (struct togglelist *) \
741		genget(name, (char **) Togglelist, sizeof(struct togglelist))
742
743static int
744toggle(int argc, char *argv[])
745{
746    int retval = 1;
747    char *name;
748    struct togglelist *c;
749
750    if (argc < 2) {
751	fprintf(stderr,
752	    "Need an argument to 'toggle' command.  'toggle ?' for help.\r\n");
753	return 0;
754    }
755    argc--;
756    argv++;
757    while (argc--) {
758	name = *argv++;
759	c = GETTOGGLE(name);
760	if (Ambiguous(c)) {
761	    fprintf(stderr, "'%s': ambiguous argument ('toggle ?' for help).\r\n",
762					name);
763	    return 0;
764	} else if (c == 0) {
765	    fprintf(stderr, "'%s': unknown argument ('toggle ?' for help).\r\n",
766					name);
767	    return 0;
768	} else {
769	    if (c->variable) {
770		*c->variable = !*c->variable;		/* invert it */
771		if (c->actionexplanation) {
772		    printf("%s %s.\r\n", *c->variable? "Will" : "Won't",
773							c->actionexplanation);
774		}
775	    }
776	    if (c->handler) {
777		retval &= (*c->handler)(-1);
778	    }
779	}
780    }
781    return retval;
782}
783
784/*
785 * The following perform the "set" command.
786 */
787
788struct termios new_tc = { 0 };
789
790struct setlist {
791    char *name;				/* name */
792    char *help;				/* help information */
793    void (*handler)();
794    cc_t *charp;			/* where it is located at */
795};
796
797static struct setlist Setlist[] = {
798#ifdef	KLUDGELINEMODE
799    { "echo", 	"character to toggle local echoing on/off", 0, &echoc },
800#endif
801    { "escape",	"character to escape back to telnet command mode", 0, &escape },
802    { "rlogin", "rlogin escape character", 0, &rlogin },
803    { "tracefile", "file to write trace information to", SetNetTrace, (cc_t *)NetTraceFile},
804    { " ", "" },
805    { " ", "The following need 'localchars' to be toggled true", 0, 0 },
806    { "flushoutput", "character to cause an Abort Output", 0, &termFlushChar },
807    { "interrupt", "character to cause an Interrupt Process", 0, &termIntChar },
808    { "quit",	"character to cause an Abort process", 0, &termQuitChar },
809    { "eof",	"character to cause an EOF ", 0, &termEofChar },
810    { " ", "" },
811    { " ", "The following are for local editing in linemode", 0, 0 },
812    { "erase",	"character to use to erase a character", 0, &termEraseChar },
813    { "kill",	"character to use to erase a line", 0, &termKillChar },
814    { "lnext",	"character to use for literal next", 0, &termLiteralNextChar },
815    { "susp",	"character to cause a Suspend Process", 0, &termSuspChar },
816    { "reprint", "character to use for line reprint", 0, &termRprntChar },
817    { "worderase", "character to use to erase a word", 0, &termWerasChar },
818    { "start",	"character to use for XON", 0, &termStartChar },
819    { "stop",	"character to use for XOFF", 0, &termStopChar },
820    { "forw1",	"alternate end of line character", 0, &termForw1Char },
821    { "forw2",	"alternate end of line character", 0, &termForw2Char },
822    { "ayt",	"alternate AYT character", 0, &termAytChar },
823    { 0 }
824};
825
826static struct setlist *
827getset(char *name)
828{
829    return (struct setlist *)
830		genget(name, (char **) Setlist, sizeof(struct setlist));
831}
832
833void
834set_escape_char(char *s)
835{
836	if (rlogin != _POSIX_VDISABLE) {
837		rlogin = (s && *s) ? special(s) : _POSIX_VDISABLE;
838		printf("Telnet rlogin escape character is '%s'.\r\n",
839					control(rlogin));
840	} else {
841		escape = (s && *s) ? special(s) : _POSIX_VDISABLE;
842		printf("Telnet escape character is '%s'.\r\n", control(escape));
843	}
844}
845
846static int
847setcmd(int argc, char *argv[])
848{
849    int value;
850    struct setlist *ct;
851    struct togglelist *c;
852
853    if (argc < 2 || argc > 3) {
854	printf("Format is 'set Name Value'\r\n'set ?' for help.\r\n");
855	return 0;
856    }
857    if ((argc == 2) && (isprefix(argv[1], "?") || isprefix(argv[1], "help"))) {
858	for (ct = Setlist; ct->name; ct++)
859	    printf("%-15s %s\r\n", ct->name, ct->help);
860	printf("\r\n");
861	settogglehelp(1);
862	printf("%-15s %s\r\n", "?", "display help information");
863	return 0;
864    }
865
866    ct = getset(argv[1]);
867    if (ct == 0) {
868	c = GETTOGGLE(argv[1]);
869	if (c == 0) {
870	    fprintf(stderr, "'%s': unknown argument ('set ?' for help).\r\n",
871			argv[1]);
872	    return 0;
873	} else if (Ambiguous(c)) {
874	    fprintf(stderr, "'%s': ambiguous argument ('set ?' for help).\r\n",
875			argv[1]);
876	    return 0;
877	}
878	if (c->variable) {
879	    if ((argc == 2) || (strcmp("on", argv[2]) == 0))
880		*c->variable = 1;
881	    else if (strcmp("off", argv[2]) == 0)
882		*c->variable = 0;
883	    else {
884		printf("Format is 'set togglename [on|off]'\r\n'set ?' for help.\r\n");
885		return 0;
886	    }
887	    if (c->actionexplanation) {
888		printf("%s %s.\r\n", *c->variable? "Will" : "Won't",
889							c->actionexplanation);
890	    }
891	}
892	if (c->handler)
893	    (*c->handler)(1);
894    } else if (argc != 3) {
895	printf("Format is 'set Name Value'\r\n'set ?' for help.\r\n");
896	return 0;
897    } else if (Ambiguous(ct)) {
898	fprintf(stderr, "'%s': ambiguous argument ('set ?' for help).\r\n",
899			argv[1]);
900	return 0;
901    } else if (ct->handler) {
902	(*ct->handler)(argv[2]);
903	printf("%s set to \"%s\".\r\n", ct->name, (char *)ct->charp);
904    } else {
905	if (strcmp("off", argv[2])) {
906	    value = special(argv[2]);
907	} else {
908	    value = _POSIX_VDISABLE;
909	}
910	*(ct->charp) = (cc_t)value;
911	printf("%s character is '%s'.\r\n", ct->name, control(*(ct->charp)));
912    }
913    slc_check();
914    return 1;
915}
916
917static int
918unsetcmd(int argc, char *argv[])
919{
920    struct setlist *ct;
921    struct togglelist *c;
922    char *name;
923
924    if (argc < 2) {
925	fprintf(stderr,
926	    "Need an argument to 'unset' command.  'unset ?' for help.\r\n");
927	return 0;
928    }
929    if (isprefix(argv[1], "?") || isprefix(argv[1], "help")) {
930	for (ct = Setlist; ct->name; ct++)
931	    printf("%-15s %s\r\n", ct->name, ct->help);
932	printf("\r\n");
933	settogglehelp(0);
934	printf("%-15s %s\r\n", "?", "display help information");
935	return 0;
936    }
937
938    argc--;
939    argv++;
940    while (argc--) {
941	name = *argv++;
942	ct = getset(name);
943	if (ct == 0) {
944	    c = GETTOGGLE(name);
945	    if (c == 0) {
946		fprintf(stderr, "'%s': unknown argument ('unset ?' for help).\r\n",
947			name);
948		return 0;
949	    } else if (Ambiguous(c)) {
950		fprintf(stderr, "'%s': ambiguous argument ('unset ?' for help).\r\n",
951			name);
952		return 0;
953	    }
954	    if (c->variable) {
955		*c->variable = 0;
956		if (c->actionexplanation) {
957		    printf("%s %s.\r\n", *c->variable? "Will" : "Won't",
958							c->actionexplanation);
959		}
960	    }
961	    if (c->handler)
962		(*c->handler)(0);
963	} else if (Ambiguous(ct)) {
964	    fprintf(stderr, "'%s': ambiguous argument ('unset ?' for help).\r\n",
965			name);
966	    return 0;
967	} else if (ct->handler) {
968	    (*ct->handler)(0);
969	    printf("%s reset to \"%s\".\r\n", ct->name, (char *)ct->charp);
970	} else {
971	    *(ct->charp) = _POSIX_VDISABLE;
972	    printf("%s character is '%s'.\r\n", ct->name, control(*(ct->charp)));
973	}
974    }
975    return 1;
976}
977
978/*
979 * The following are the data structures and routines for the
980 * 'mode' command.
981 */
982#ifdef	KLUDGELINEMODE
983
984static int
985dokludgemode(void)
986{
987    kludgelinemode = 1;
988    send_wont(TELOPT_LINEMODE, 1);
989    send_dont(TELOPT_SGA, 1);
990    send_dont(TELOPT_ECHO, 1);
991    return 1;
992}
993#endif
994
995static int
996dolinemode()
997{
998#ifdef	KLUDGELINEMODE
999    if (kludgelinemode)
1000	send_dont(TELOPT_SGA, 1);
1001#endif
1002    send_will(TELOPT_LINEMODE, 1);
1003    send_dont(TELOPT_ECHO, 1);
1004    return 1;
1005}
1006
1007static int
1008docharmode()
1009{
1010#ifdef	KLUDGELINEMODE
1011    if (kludgelinemode)
1012	send_do(TELOPT_SGA, 1);
1013    else
1014#endif
1015    send_wont(TELOPT_LINEMODE, 1);
1016    send_do(TELOPT_ECHO, 1);
1017    return 1;
1018}
1019
1020static int
1021dolmmode(int bit, int on)
1022{
1023    unsigned char c;
1024
1025    if (my_want_state_is_wont(TELOPT_LINEMODE)) {
1026	printf("?Need to have LINEMODE option enabled first.\r\n");
1027	printf("'mode ?' for help.\r\n");
1028 	return 0;
1029    }
1030
1031    if (on)
1032	c = (linemode | bit);
1033    else
1034	c = (linemode & ~bit);
1035    lm_mode(&c, 1, 1);
1036    return 1;
1037}
1038
1039static int
1040tn_setmode(int bit)
1041{
1042    return dolmmode(bit, 1);
1043}
1044
1045static int
1046tn_clearmode(int bit)
1047{
1048    return dolmmode(bit, 0);
1049}
1050
1051struct modelist {
1052	char	*name;		/* command name */
1053	char	*help;		/* help string */
1054	int	(*handler)();	/* routine which executes command */
1055	int	needconnect;	/* Do we need to be connected to execute? */
1056	int	arg1;
1057};
1058
1059static int modehelp(void);
1060
1061static struct modelist ModeList[] = {
1062    { "character", "Disable LINEMODE option",	docharmode, 1 },
1063#ifdef	KLUDGELINEMODE
1064    { "",	"(or disable obsolete line-by-line mode)", 0 },
1065#endif
1066    { "line",	"Enable LINEMODE option",	dolinemode, 1 },
1067#ifdef	KLUDGELINEMODE
1068    { "",	"(or enable obsolete line-by-line mode)", 0 },
1069#endif
1070    { "", "", 0 },
1071    { "",	"These require the LINEMODE option to be enabled", 0 },
1072    { "isig",	"Enable signal trapping",	tn_setmode, 1, MODE_TRAPSIG },
1073    { "+isig",	0,				tn_setmode, 1, MODE_TRAPSIG },
1074    { "-isig",	"Disable signal trapping",	tn_clearmode, 1, MODE_TRAPSIG },
1075    { "edit",	"Enable character editing",	tn_setmode, 1, MODE_EDIT },
1076    { "+edit",	0,				tn_setmode, 1, MODE_EDIT },
1077    { "-edit",	"Disable character editing",	tn_clearmode, 1, MODE_EDIT },
1078    { "softtabs", "Enable tab expansion",	tn_setmode, 1, MODE_SOFT_TAB },
1079    { "+softtabs", 0,				tn_setmode, 1, MODE_SOFT_TAB },
1080    { "-softtabs", "Disable tab expansion",	tn_clearmode, 1, MODE_SOFT_TAB },
1081    { "litecho", "Enable literal character echo", tn_setmode, 1, MODE_LIT_ECHO },
1082    { "+litecho", 0,				tn_setmode, 1, MODE_LIT_ECHO },
1083    { "-litecho", "Disable literal character echo", tn_clearmode, 1, MODE_LIT_ECHO },
1084    { "help",	0,				modehelp, 0 },
1085#ifdef	KLUDGELINEMODE
1086    { "kludgeline", 0,				dokludgemode, 1 },
1087#endif
1088    { "", "", 0 },
1089    { "?",	"Print help information",	modehelp, 0 },
1090    { 0 },
1091};
1092
1093
1094static int
1095modehelp(void)
1096{
1097    struct modelist *mt;
1098
1099    printf("format is:  'mode Mode', where 'Mode' is one of:\r\n\r\n");
1100    for (mt = ModeList; mt->name; mt++) {
1101	if (mt->help) {
1102	    if (*mt->help)
1103		printf("%-15s %s\r\n", mt->name, mt->help);
1104	    else
1105		printf("\r\n");
1106	}
1107    }
1108    return 0;
1109}
1110
1111#define	GETMODECMD(name) (struct modelist *) \
1112		genget(name, (char **) ModeList, sizeof(struct modelist))
1113
1114static int
1115modecmd(int argc, char **argv)
1116{
1117    struct modelist *mt;
1118
1119    if (argc != 2) {
1120	printf("'mode' command requires an argument\r\n");
1121	printf("'mode ?' for help.\r\n");
1122    } else if ((mt = GETMODECMD(argv[1])) == 0) {
1123	fprintf(stderr, "Unknown mode '%s' ('mode ?' for help).\r\n", argv[1]);
1124    } else if (Ambiguous(mt)) {
1125	fprintf(stderr, "Ambiguous mode '%s' ('mode ?' for help).\r\n", argv[1]);
1126    } else if (mt->needconnect && !connected) {
1127	printf("?Need to be connected first.\r\n");
1128	printf("'mode ?' for help.\r\n");
1129    } else if (mt->handler) {
1130	return (*mt->handler)(mt->arg1);
1131    }
1132    return 0;
1133}
1134
1135/*
1136 * The following data structures and routines implement the
1137 * "display" command.
1138 */
1139
1140static int
1141display(int argc, char *argv[])
1142{
1143    struct togglelist *tl;
1144    struct setlist *sl;
1145
1146#define	dotog(tl)	if (tl->variable && tl->actionexplanation) { \
1147			    if (*tl->variable) { \
1148				printf("will"); \
1149			    } else { \
1150				printf("won't"); \
1151			    } \
1152			    printf(" %s.\r\n", tl->actionexplanation); \
1153			}
1154
1155#define	doset(sl)   if (sl->name && *sl->name != ' ') { \
1156			if (sl->handler == 0) \
1157			    printf("%-15s [%s]\r\n", sl->name, control(*sl->charp)); \
1158			else \
1159			    printf("%-15s \"%s\"\r\n", sl->name, (char *)sl->charp); \
1160		    }
1161
1162    if (argc == 1) {
1163	for (tl = Togglelist; tl->name; tl++) {
1164	    dotog(tl);
1165	}
1166	printf("\r\n");
1167	for (sl = Setlist; sl->name; sl++) {
1168	    doset(sl);
1169	}
1170    } else {
1171	int i;
1172
1173	for (i = 1; i < argc; i++) {
1174	    sl = getset(argv[i]);
1175	    tl = GETTOGGLE(argv[i]);
1176	    if (Ambiguous(sl) || Ambiguous(tl)) {
1177		printf("?Ambiguous argument '%s'.\r\n", argv[i]);
1178		return 0;
1179	    } else if (!sl && !tl) {
1180		printf("?Unknown argument '%s'.\r\n", argv[i]);
1181		return 0;
1182	    } else {
1183		if (tl) {
1184		    dotog(tl);
1185		}
1186		if (sl) {
1187		    doset(sl);
1188		}
1189	    }
1190	}
1191    }
1192/*@*/optionstatus();
1193#if	defined(ENCRYPTION)
1194    EncryptStatus();
1195#endif
1196    return 1;
1197#undef	doset
1198#undef	dotog
1199}
1200
1201/*
1202 * The following are the data structures, and many of the routines,
1203 * relating to command processing.
1204 */
1205
1206/*
1207 * Set the escape character.
1208 */
1209static int
1210setescape(int argc, char *argv[])
1211{
1212	char *arg;
1213	char buf[50];
1214
1215	printf(
1216	    "Deprecated usage - please use 'set escape%s%s' in the future.\r\n",
1217				(argc > 2)? " ":"", (argc > 2)? argv[1]: "");
1218	if (argc > 2)
1219		arg = argv[1];
1220	else {
1221		printf("new escape character: ");
1222		fgets(buf, sizeof(buf), stdin);
1223		arg = buf;
1224	}
1225	if (arg[0] != '\0')
1226		escape = arg[0];
1227	printf("Escape character is '%s'.\r\n", control(escape));
1228
1229	fflush(stdout);
1230	return 1;
1231}
1232
1233static int
1234togcrmod()
1235{
1236    crmod = !crmod;
1237    printf("Deprecated usage - please use 'toggle crmod' in the future.\r\n");
1238    printf("%s map carriage return on output.\r\n", crmod ? "Will" : "Won't");
1239    fflush(stdout);
1240    return 1;
1241}
1242
1243static int
1244telnetsuspend()
1245{
1246#ifdef	SIGTSTP
1247    setcommandmode();
1248    {
1249	long oldrows, oldcols, newrows, newcols, err;
1250
1251	err = (TerminalWindowSize(&oldrows, &oldcols) == 0) ? 1 : 0;
1252	kill(0, SIGTSTP);
1253	/*
1254	 * If we didn't get the window size before the SUSPEND, but we
1255	 * can get them now (?), then send the NAWS to make sure that
1256	 * we are set up for the right window size.
1257	 */
1258	if (TerminalWindowSize(&newrows, &newcols) && connected &&
1259	    (err || ((oldrows != newrows) || (oldcols != newcols)))) {
1260		sendnaws();
1261	}
1262    }
1263    /* reget parameters in case they were changed */
1264    TerminalSaveState();
1265    setconnmode(0);
1266#else
1267    printf("Suspend is not supported.  Try the '!' command instead\r\n");
1268#endif
1269    return 1;
1270}
1271
1272static int
1273shell(int argc, char **argv)
1274{
1275    long oldrows, oldcols, newrows, newcols, err;
1276
1277    setcommandmode();
1278
1279    err = (TerminalWindowSize(&oldrows, &oldcols) == 0) ? 1 : 0;
1280    switch(fork()) {
1281    case -1:
1282	perror("Fork failed\r\n");
1283	break;
1284
1285    case 0:
1286	{
1287	    /*
1288	     * Fire up the shell in the child.
1289	     */
1290	    char *shellp, *shellname;
1291
1292	    shellp = getenv("SHELL");
1293	    if (shellp == NULL)
1294		shellp = "/bin/sh";
1295	    if ((shellname = strrchr(shellp, '/')) == 0)
1296		shellname = shellp;
1297	    else
1298		shellname++;
1299	    if (argc > 1)
1300		execl(shellp, shellname, "-c", &saveline[1], NULL);
1301	    else
1302		execl(shellp, shellname, NULL);
1303	    perror("Execl");
1304	    _exit(1);
1305	}
1306    default:
1307	    wait((int *)0);	/* Wait for the shell to complete */
1308
1309	    if (TerminalWindowSize(&newrows, &newcols) && connected &&
1310		(err || ((oldrows != newrows) || (oldcols != newcols)))) {
1311		    sendnaws();
1312	    }
1313	    break;
1314    }
1315    return 1;
1316}
1317
1318static int
1319bye(int argc, char **argv)
1320{
1321    if (connected) {
1322	shutdown(net, 2);
1323	printf("Connection closed.\r\n");
1324	NetClose(net);
1325	connected = 0;
1326	resettermname = 1;
1327#if	defined(AUTHENTICATION) || defined(ENCRYPTION)
1328	auth_encrypt_connect(connected);
1329#endif
1330	/* reset options */
1331	tninit();
1332    }
1333    if ((argc != 2) || (strcmp(argv[1], "fromquit") != 0))
1334	longjmp(toplevel, 1);
1335    return 0;	/* NOTREACHED */
1336}
1337
1338int
1339quit(void)
1340{
1341	call(bye, "bye", "fromquit", 0);
1342	Exit(0);
1343	return 0; /*NOTREACHED*/
1344}
1345
1346static int
1347logout()
1348{
1349	send_do(TELOPT_LOGOUT, 1);
1350	netflush();
1351	return 1;
1352}
1353
1354
1355/*
1356 * The SLC command.
1357 */
1358
1359struct slclist {
1360	char	*name;
1361	char	*help;
1362	void	(*handler)();
1363	int	arg;
1364};
1365
1366static void slc_help(void);
1367
1368struct slclist SlcList[] = {
1369    { "export",	"Use local special character definitions",
1370						slc_mode_export,	0 },
1371    { "import",	"Use remote special character definitions",
1372						slc_mode_import,	1 },
1373    { "check",	"Verify remote special character definitions",
1374						slc_mode_import,	0 },
1375    { "help",	0,				slc_help,		0 },
1376    { "?",	"Print help information",	slc_help,		0 },
1377    { 0 },
1378};
1379
1380static void
1381slc_help(void)
1382{
1383    struct slclist *c;
1384
1385    for (c = SlcList; c->name; c++) {
1386	if (c->help) {
1387	    if (*c->help)
1388		printf("%-15s %s\r\n", c->name, c->help);
1389	    else
1390		printf("\r\n");
1391	}
1392    }
1393}
1394
1395static struct slclist *
1396getslc(char *name)
1397{
1398    return (struct slclist *)
1399		genget(name, (char **) SlcList, sizeof(struct slclist));
1400}
1401
1402static int
1403slccmd(int argc, char **argv)
1404{
1405    struct slclist *c;
1406
1407    if (argc != 2) {
1408	fprintf(stderr,
1409	    "Need an argument to 'slc' command.  'slc ?' for help.\r\n");
1410	return 0;
1411    }
1412    c = getslc(argv[1]);
1413    if (c == 0) {
1414	fprintf(stderr, "'%s': unknown argument ('slc ?' for help).\r\n",
1415    				argv[1]);
1416	return 0;
1417    }
1418    if (Ambiguous(c)) {
1419	fprintf(stderr, "'%s': ambiguous argument ('slc ?' for help).\r\n",
1420    				argv[1]);
1421	return 0;
1422    }
1423    (*c->handler)(c->arg);
1424    slcstate();
1425    return 1;
1426}
1427
1428/*
1429 * The ENVIRON command.
1430 */
1431
1432struct envlist {
1433	char	*name;
1434	char	*help;
1435	void	(*handler)();
1436	int	narg;
1437};
1438
1439static void env_help (void);
1440
1441struct envlist EnvList[] = {
1442    { "define",	"Define an environment variable",
1443						(void (*)())env_define,	2 },
1444    { "undefine", "Undefine an environment variable",
1445						env_undefine,	1 },
1446    { "export",	"Mark an environment variable for automatic export",
1447						env_export,	1 },
1448    { "unexport", "Don't mark an environment variable for automatic export",
1449						env_unexport,	1 },
1450    { "send",	"Send an environment variable", env_send,	1 },
1451    { "list",	"List the current environment variables",
1452						env_list,	0 },
1453    { "help",	0,				env_help,		0 },
1454    { "?",	"Print help information",	env_help,		0 },
1455    { 0 },
1456};
1457
1458static void
1459env_help()
1460{
1461    struct envlist *c;
1462
1463    for (c = EnvList; c->name; c++) {
1464	if (c->help) {
1465	    if (*c->help)
1466		printf("%-15s %s\r\n", c->name, c->help);
1467	    else
1468		printf("\r\n");
1469	}
1470    }
1471}
1472
1473static struct envlist *
1474getenvcmd(char *name)
1475{
1476    return (struct envlist *)
1477		genget(name, (char **) EnvList, sizeof(struct envlist));
1478}
1479
1480static int
1481env_cmd(int argc, char **argv)
1482{
1483    struct envlist *c;
1484
1485    if (argc < 2) {
1486	fprintf(stderr,
1487	    "Need an argument to 'environ' command.  'environ ?' for help.\r\n");
1488	return 0;
1489    }
1490    c = getenvcmd(argv[1]);
1491    if (c == 0) {
1492	fprintf(stderr, "'%s': unknown argument ('environ ?' for help).\r\n",
1493    				argv[1]);
1494	return 0;
1495    }
1496    if (Ambiguous(c)) {
1497	fprintf(stderr, "'%s': ambiguous argument ('environ ?' for help).\r\n",
1498    				argv[1]);
1499	return 0;
1500    }
1501    if (c->narg + 2 != argc) {
1502	fprintf(stderr,
1503	    "Need %s%d argument%s to 'environ %s' command.  'environ ?' for help.\r\n",
1504		c->narg < argc + 2 ? "only " : "",
1505		c->narg, c->narg == 1 ? "" : "s", c->name);
1506	return 0;
1507    }
1508    (*c->handler)(argv[2], argv[3]);
1509    return 1;
1510}
1511
1512struct env_lst {
1513	struct env_lst *next;	/* pointer to next structure */
1514	struct env_lst *prev;	/* pointer to previous structure */
1515	unsigned char *var;	/* pointer to variable name */
1516	unsigned char *value;	/* pointer to variable value */
1517	int export;		/* 1 -> export with default list of variables */
1518	int welldefined;	/* A well defined variable */
1519};
1520
1521struct env_lst envlisthead;
1522
1523struct env_lst *
1524env_find(unsigned char *var)
1525{
1526	struct env_lst *ep;
1527
1528	for (ep = envlisthead.next; ep; ep = ep->next) {
1529		if (strcmp((char *)ep->var, (char *)var) == 0)
1530			return(ep);
1531	}
1532	return(NULL);
1533}
1534
1535#if !HAVE_DECL_ENVIRON
1536extern char **environ;
1537#endif
1538
1539void
1540env_init(void)
1541{
1542	char **epp, *cp;
1543	struct env_lst *ep;
1544
1545	for (epp = environ; *epp; epp++) {
1546		if ((cp = strchr(*epp, '='))) {
1547			*cp = '\0';
1548			ep = env_define((unsigned char *)*epp,
1549					(unsigned char *)cp+1);
1550			ep->export = 0;
1551			*cp = '=';
1552		}
1553	}
1554	/*
1555	 * Special case for DISPLAY variable.  If it is ":0.0" or
1556	 * "unix:0.0", we have to get rid of "unix" and insert our
1557	 * hostname.
1558	 */
1559	if ((ep = env_find((unsigned char*)"DISPLAY"))
1560	    && (*ep->value == ':'
1561	    || strncmp((char *)ep->value, "unix:", 5) == 0)) {
1562		char hbuf[256+1];
1563		char *cp2 = strchr((char *)ep->value, ':');
1564		int error;
1565
1566		/* XXX - should be k_gethostname? */
1567		gethostname(hbuf, 256);
1568		hbuf[256] = '\0';
1569
1570		/* If this is not the full name, try to get it via DNS */
1571		if (strchr(hbuf, '.') == 0) {
1572			struct addrinfo hints, *ai, *a;
1573
1574			memset (&hints, 0, sizeof(hints));
1575			hints.ai_flags = AI_CANONNAME;
1576
1577			error = getaddrinfo (hbuf, NULL, &hints, &ai);
1578			if (error == 0) {
1579				for (a = ai; a != NULL; a = a->ai_next)
1580					if (a->ai_canonname != NULL) {
1581						strlcpy (hbuf,
1582							 ai->ai_canonname,
1583							 256);
1584						break;
1585					}
1586				freeaddrinfo (ai);
1587			}
1588		}
1589
1590		error = asprintf (&cp, "%s%s", hbuf, cp2);
1591		if (error != -1) {
1592		    free (ep->value);
1593		    ep->value = (unsigned char *)cp;
1594		}
1595	}
1596	/*
1597	 * If USER is not defined, but LOGNAME is, then add
1598	 * USER with the value from LOGNAME.  By default, we
1599	 * don't export the USER variable.
1600	 */
1601	if ((env_find((unsigned char*)"USER") == NULL) &&
1602	    (ep = env_find((unsigned char*)"LOGNAME"))) {
1603		env_define((unsigned char *)"USER", ep->value);
1604		env_unexport((unsigned char *)"USER");
1605	}
1606	env_export((unsigned char *)"DISPLAY");
1607	env_export((unsigned char *)"PRINTER");
1608	env_export((unsigned char *)"XAUTHORITY");
1609}
1610
1611struct env_lst *
1612env_define(unsigned char *var, unsigned char *value)
1613{
1614	struct env_lst *ep;
1615
1616	if ((ep = env_find(var))) {
1617		if (ep->var)
1618			free(ep->var);
1619		if (ep->value)
1620			free(ep->value);
1621	} else {
1622		ep = (struct env_lst *)malloc(sizeof(struct env_lst));
1623		ep->next = envlisthead.next;
1624		envlisthead.next = ep;
1625		ep->prev = &envlisthead;
1626		if (ep->next)
1627			ep->next->prev = ep;
1628	}
1629	ep->welldefined = opt_welldefined((char *)var);
1630	ep->export = 1;
1631	ep->var = (unsigned char *)strdup((char *)var);
1632	ep->value = (unsigned char *)strdup((char *)value);
1633	return(ep);
1634}
1635
1636void
1637env_undefine(unsigned char *var)
1638{
1639	struct env_lst *ep;
1640
1641	if ((ep = env_find(var))) {
1642		ep->prev->next = ep->next;
1643		if (ep->next)
1644			ep->next->prev = ep->prev;
1645		if (ep->var)
1646			free(ep->var);
1647		if (ep->value)
1648			free(ep->value);
1649		free(ep);
1650	}
1651}
1652
1653void
1654env_export(unsigned char *var)
1655{
1656	struct env_lst *ep;
1657
1658	if ((ep = env_find(var)))
1659		ep->export = 1;
1660}
1661
1662void
1663env_unexport(unsigned char *var)
1664{
1665	struct env_lst *ep;
1666
1667	if ((ep = env_find(var)))
1668		ep->export = 0;
1669}
1670
1671void
1672env_send(unsigned char *var)
1673{
1674	struct env_lst *ep;
1675
1676	if (my_state_is_wont(TELOPT_NEW_ENVIRON)
1677#ifdef	OLD_ENVIRON
1678	    && my_state_is_wont(TELOPT_OLD_ENVIRON)
1679#endif
1680		) {
1681		fprintf(stderr,
1682		    "Cannot send '%s': Telnet ENVIRON option not enabled\r\n",
1683									var);
1684		return;
1685	}
1686	ep = env_find(var);
1687	if (ep == 0) {
1688		fprintf(stderr, "Cannot send '%s': variable not defined\r\n",
1689									var);
1690		return;
1691	}
1692	env_opt_start_info();
1693	env_opt_add(ep->var);
1694	env_opt_end(0);
1695}
1696
1697void
1698env_list(void)
1699{
1700	struct env_lst *ep;
1701
1702	for (ep = envlisthead.next; ep; ep = ep->next) {
1703		printf("%c %-20s %s\r\n", ep->export ? '*' : ' ',
1704					ep->var, ep->value);
1705	}
1706}
1707
1708unsigned char *
1709env_default(int init, int welldefined)
1710{
1711	static struct env_lst *nep = NULL;
1712
1713	if (init) {
1714		nep = &envlisthead;
1715		return NULL;
1716	}
1717	if (nep) {
1718		while ((nep = nep->next)) {
1719			if (nep->export && (nep->welldefined == welldefined))
1720				return(nep->var);
1721		}
1722	}
1723	return(NULL);
1724}
1725
1726unsigned char *
1727env_getvalue(unsigned char *var)
1728{
1729	struct env_lst *ep;
1730
1731	if ((ep = env_find(var)))
1732		return(ep->value);
1733	return(NULL);
1734}
1735
1736
1737#if	defined(AUTHENTICATION)
1738/*
1739 * The AUTHENTICATE command.
1740 */
1741
1742struct authlist {
1743	char	*name;
1744	char	*help;
1745	int	(*handler)();
1746	int	narg;
1747};
1748
1749static int
1750	auth_help (void);
1751
1752struct authlist AuthList[] = {
1753    { "status",	"Display current status of authentication information",
1754						auth_status,	0 },
1755    { "disable", "Disable an authentication type ('auth disable ?' for more)",
1756						auth_disable,	1 },
1757    { "enable", "Enable an authentication type ('auth enable ?' for more)",
1758						auth_enable,	1 },
1759    { "help",	0,				auth_help,		0 },
1760    { "?",	"Print help information",	auth_help,		0 },
1761    { 0 },
1762};
1763
1764static int
1765auth_help()
1766{
1767    struct authlist *c;
1768
1769    for (c = AuthList; c->name; c++) {
1770	if (c->help) {
1771	    if (*c->help)
1772		printf("%-15s %s\r\n", c->name, c->help);
1773	    else
1774		printf("\r\n");
1775	}
1776    }
1777    return 0;
1778}
1779
1780static int
1781auth_cmd(int argc, char **argv)
1782{
1783    struct authlist *c;
1784
1785    if (argc < 2) {
1786	fprintf(stderr,
1787	    "Need an argument to 'auth' command.  'auth ?' for help.\r\n");
1788	return 0;
1789    }
1790
1791    c = (struct authlist *)
1792		genget(argv[1], (char **) AuthList, sizeof(struct authlist));
1793    if (c == 0) {
1794	fprintf(stderr, "'%s': unknown argument ('auth ?' for help).\r\n",
1795    				argv[1]);
1796	return 0;
1797    }
1798    if (Ambiguous(c)) {
1799	fprintf(stderr, "'%s': ambiguous argument ('auth ?' for help).\r\n",
1800    				argv[1]);
1801	return 0;
1802    }
1803    if (c->narg + 2 != argc) {
1804	fprintf(stderr,
1805	    "Need %s%d argument%s to 'auth %s' command.  'auth ?' for help.\r\n",
1806		c->narg < argc + 2 ? "only " : "",
1807		c->narg, c->narg == 1 ? "" : "s", c->name);
1808	return 0;
1809    }
1810    return((*c->handler)(argv[2], argv[3]));
1811}
1812#endif
1813
1814
1815#if	defined(ENCRYPTION)
1816/*
1817 * The ENCRYPT command.
1818 */
1819
1820struct encryptlist {
1821	char	*name;
1822	char	*help;
1823	int	(*handler)();
1824	int	needconnect;
1825	int	minarg;
1826	int	maxarg;
1827};
1828
1829static int
1830	EncryptHelp (void);
1831
1832struct encryptlist EncryptList[] = {
1833    { "enable", "Enable encryption. ('encrypt enable ?' for more)",
1834						EncryptEnable, 1, 1, 2 },
1835    { "disable", "Disable encryption. ('encrypt enable ?' for more)",
1836						EncryptDisable, 0, 1, 2 },
1837    { "type", "Set encryptiong type. ('encrypt type ?' for more)",
1838						EncryptType, 0, 1, 1 },
1839    { "start", "Start encryption. ('encrypt start ?' for more)",
1840						EncryptStart, 1, 0, 1 },
1841    { "stop", "Stop encryption. ('encrypt stop ?' for more)",
1842						EncryptStop, 1, 0, 1 },
1843    { "input", "Start encrypting the input stream",
1844						EncryptStartInput, 1, 0, 0 },
1845    { "-input", "Stop encrypting the input stream",
1846						EncryptStopInput, 1, 0, 0 },
1847    { "output", "Start encrypting the output stream",
1848						EncryptStartOutput, 1, 0, 0 },
1849    { "-output", "Stop encrypting the output stream",
1850						EncryptStopOutput, 1, 0, 0 },
1851
1852    { "status",	"Display current status of authentication information",
1853						EncryptStatus,	0, 0, 0 },
1854    { "help",	0,				EncryptHelp,	0, 0, 0 },
1855    { "?",	"Print help information",	EncryptHelp,	0, 0, 0 },
1856    { 0 },
1857};
1858
1859static int
1860EncryptHelp()
1861{
1862    struct encryptlist *c;
1863
1864    for (c = EncryptList; c->name; c++) {
1865	if (c->help) {
1866	    if (*c->help)
1867		printf("%-15s %s\r\n", c->name, c->help);
1868	    else
1869		printf("\r\n");
1870	}
1871    }
1872    return 0;
1873}
1874
1875static int
1876encrypt_cmd(int argc, char **argv)
1877{
1878    struct encryptlist *c;
1879
1880    c = (struct encryptlist *)
1881		genget(argv[1], (char **) EncryptList, sizeof(struct encryptlist));
1882    if (c == 0) {
1883        fprintf(stderr, "'%s': unknown argument ('encrypt ?' for help).\r\n",
1884    				argv[1]);
1885        return 0;
1886    }
1887    if (Ambiguous(c)) {
1888        fprintf(stderr, "'%s': ambiguous argument ('encrypt ?' for help).\r\n",
1889    				argv[1]);
1890        return 0;
1891    }
1892    argc -= 2;
1893    if (argc < c->minarg || argc > c->maxarg) {
1894	if (c->minarg == c->maxarg) {
1895	    fprintf(stderr, "Need %s%d argument%s ",
1896		c->minarg < argc ? "only " : "", c->minarg,
1897		c->minarg == 1 ? "" : "s");
1898	} else {
1899	    fprintf(stderr, "Need %s%d-%d arguments ",
1900		c->maxarg < argc ? "only " : "", c->minarg, c->maxarg);
1901	}
1902	fprintf(stderr, "to 'encrypt %s' command.  'encrypt ?' for help.\r\n",
1903		c->name);
1904	return 0;
1905    }
1906    if (c->needconnect && !connected) {
1907	if (!(argc && (isprefix(argv[2], "help") || isprefix(argv[2], "?")))) {
1908	    printf("?Need to be connected first.\r\n");
1909	    return 0;
1910	}
1911    }
1912    return ((*c->handler)(argc > 0 ? argv[2] : 0,
1913			argc > 1 ? argv[3] : 0,
1914			argc > 2 ? argv[4] : 0));
1915}
1916#endif
1917
1918
1919/*
1920 * Print status about the connection.
1921 */
1922
1923static int
1924status(int argc, char **argv)
1925{
1926    if (connected) {
1927	printf("Connected to %s.\r\n", hostname);
1928	if ((argc < 2) || strcmp(argv[1], "notmuch")) {
1929	    int mode = getconnmode();
1930
1931	    if (my_want_state_is_will(TELOPT_LINEMODE)) {
1932		printf("Operating with LINEMODE option\r\n");
1933		printf("%s line editing\r\n", (mode&MODE_EDIT) ? "Local" : "No");
1934		printf("%s catching of signals\r\n",
1935					(mode&MODE_TRAPSIG) ? "Local" : "No");
1936		slcstate();
1937#ifdef	KLUDGELINEMODE
1938	    } else if (kludgelinemode && my_want_state_is_dont(TELOPT_SGA)) {
1939		printf("Operating in obsolete linemode\r\n");
1940#endif
1941	    } else {
1942		printf("Operating in single character mode\r\n");
1943		if (localchars)
1944		    printf("Catching signals locally\r\n");
1945	    }
1946	    printf("%s character echo\r\n", (mode&MODE_ECHO) ? "Local" : "Remote");
1947	    if (my_want_state_is_will(TELOPT_LFLOW))
1948		printf("%s flow control\r\n", (mode&MODE_FLOW) ? "Local" : "No");
1949#if	defined(ENCRYPTION)
1950	    encrypt_display();
1951#endif
1952	}
1953    } else {
1954	printf("No connection.\r\n");
1955    }
1956    printf("Escape character is '%s'.\r\n", control(escape));
1957    fflush(stdout);
1958    return 1;
1959}
1960
1961#ifdef	SIGINFO
1962/*
1963 * Function that gets called when SIGINFO is received.
1964 */
1965RETSIGTYPE
1966ayt_status(int ignore)
1967{
1968    call(status, "status", "notmuch", 0);
1969}
1970#endif
1971
1972static Command *getcmd(char *name);
1973
1974static void
1975cmdrc(char *m1, char *m2)
1976{
1977    static char rcname[128];
1978    Command *c;
1979    FILE *rcfile;
1980    int gotmachine = 0;
1981    int l1 = strlen(m1);
1982    int l2 = strlen(m2);
1983    char m1save[64];
1984
1985    if (skiprc)
1986	return;
1987
1988    strlcpy(m1save, m1, sizeof(m1save));
1989    m1 = m1save;
1990
1991    if (rcname[0] == 0) {
1992	char *home = getenv("HOME");
1993
1994	snprintf (rcname, sizeof(rcname), "%s/.telnetrc",
1995		  home ? home : "");
1996    }
1997
1998    if ((rcfile = fopen(rcname, "r")) == 0) {
1999	return;
2000    }
2001
2002    for (;;) {
2003	if (fgets(line, sizeof(line), rcfile) == NULL)
2004	    break;
2005	if (line[0] == 0)
2006	    break;
2007	if (line[0] == '#')
2008	    continue;
2009	if (gotmachine) {
2010	    if (!isspace((unsigned char)line[0]))
2011		gotmachine = 0;
2012	}
2013	if (gotmachine == 0) {
2014	    if (isspace((unsigned char)line[0]))
2015		continue;
2016	    if (strncasecmp(line, m1, l1) == 0)
2017		strncpy(line, &line[l1], sizeof(line) - l1);
2018	    else if (strncasecmp(line, m2, l2) == 0)
2019		strncpy(line, &line[l2], sizeof(line) - l2);
2020	    else if (strncasecmp(line, "DEFAULT", 7) == 0)
2021		strncpy(line, &line[7], sizeof(line) - 7);
2022	    else
2023		continue;
2024	    if (line[0] != ' ' && line[0] != '\t' && line[0] != '\n')
2025		continue;
2026	    gotmachine = 1;
2027	}
2028	makeargv();
2029	if (margv[0] == 0)
2030	    continue;
2031	c = getcmd(margv[0]);
2032	if (Ambiguous(c)) {
2033	    printf("?Ambiguous command: %s\r\n", margv[0]);
2034	    continue;
2035	}
2036	if (c == 0) {
2037	    printf("?Invalid command: %s\r\n", margv[0]);
2038	    continue;
2039	}
2040	/*
2041	 * This should never happen...
2042	 */
2043	if (c->needconnect && !connected) {
2044	    printf("?Need to be connected first for %s.\r\n", margv[0]);
2045	    continue;
2046	}
2047	(*c->handler)(margc, margv);
2048    }
2049    fclose(rcfile);
2050}
2051
2052int
2053tn(int argc, char **argv)
2054{
2055    struct servent *sp = 0;
2056    char *cmd, *hostp = 0, *portp = 0;
2057    char *user = 0;
2058    int port = 0;
2059
2060    /* clear the socket address prior to use */
2061
2062    if (connected) {
2063	printf("?Already connected to %s\r\n", hostname);
2064	return 0;
2065    }
2066    if (argc < 2) {
2067	strlcpy(line, "open ", sizeof(line));
2068	printf("(to) ");
2069	fgets(&line[strlen(line)], sizeof(line) - strlen(line), stdin);
2070	makeargv();
2071	argc = margc;
2072	argv = margv;
2073    }
2074    cmd = *argv;
2075    --argc; ++argv;
2076    while (argc) {
2077	if (strcmp(*argv, "help") == 0 || isprefix(*argv, "?"))
2078	    goto usage;
2079	if (strcmp(*argv, "-l") == 0) {
2080	    --argc; ++argv;
2081	    if (argc == 0)
2082		goto usage;
2083	    user = strdup(*argv++);
2084	    --argc;
2085	    continue;
2086	}
2087	if (strcmp(*argv, "-a") == 0) {
2088	    --argc; ++argv;
2089	    autologin = 1;
2090	    continue;
2091	}
2092	if (hostp == 0) {
2093	    hostp = *argv++;
2094	    --argc;
2095	    continue;
2096	}
2097	if (portp == 0) {
2098	    portp = *argv++;
2099	    --argc;
2100	    continue;
2101	}
2102    usage:
2103	printf("usage: %s [-l user] [-a] host-name [port]\r\n", cmd);
2104	return 0;
2105    }
2106    if (hostp == 0)
2107	goto usage;
2108
2109    strlcpy (_hostname, hostp, sizeof(_hostname));
2110    hostp = _hostname;
2111    if (hostp[0] == '@' || hostp[0] == '!') {
2112	char *p;
2113	hostname = NULL;
2114	for (p = hostp + 1; *p; p++) {
2115	    if (*p == ',' || *p == '@')
2116		hostname = p;
2117	}
2118	if (hostname == NULL) {
2119	    fprintf(stderr, "%s: bad source route specification\n", hostp);
2120	    return 0;
2121	}
2122	*hostname++ = '\0';
2123    } else
2124	hostname = hostp;
2125
2126    if (portp) {
2127	if (*portp == '-') {
2128	    portp++;
2129	    telnetport = 1;
2130	} else
2131	    telnetport = 0;
2132	port = atoi(portp);
2133	if (port == 0) {
2134	    sp = roken_getservbyname(portp, "tcp");
2135	    if (sp)
2136		port = sp->s_port;
2137	    else {
2138		printf("%s: bad port number\r\n", portp);
2139		return 0;
2140	    }
2141	} else {
2142	    port = htons(port);
2143	}
2144    } else {
2145	if (sp == 0) {
2146	    sp = roken_getservbyname("telnet", "tcp");
2147	    if (sp == 0) {
2148		fprintf(stderr, "telnet: tcp/telnet: unknown service\r\n");
2149		return 0;
2150	    }
2151	    port = sp->s_port;
2152	}
2153	telnetport = 1;
2154    }
2155
2156    {
2157	struct addrinfo *ai, *a, hints;
2158	int error;
2159	char portstr[NI_MAXSERV];
2160
2161	memset (&hints, 0, sizeof(hints));
2162	hints.ai_socktype = SOCK_STREAM;
2163	hints.ai_protocol = IPPROTO_TCP;
2164	hints.ai_flags    = AI_CANONNAME;
2165
2166	snprintf (portstr, sizeof(portstr), "%u", ntohs(port));
2167
2168	error = getaddrinfo (hostname, portstr, &hints, &ai);
2169	if (error) {
2170	    fprintf (stderr, "%s: %s\r\n", hostname, gai_strerror (error));
2171	    return 0;
2172	}
2173
2174	for (a = ai; a != NULL && connected == 0; a = a->ai_next) {
2175	    char addrstr[256];
2176
2177	    if (a->ai_canonname != NULL)
2178		strlcpy (_hostname, a->ai_canonname, sizeof(_hostname));
2179
2180	    if (getnameinfo (a->ai_addr, a->ai_addrlen,
2181			     addrstr, sizeof(addrstr),
2182			     NULL, 0, NI_NUMERICHOST) != 0)
2183		strlcpy (addrstr, "unknown address", sizeof(addrstr));
2184
2185	    printf("Trying %s...\r\n", addrstr);
2186
2187	    net = socket (a->ai_family, a->ai_socktype, a->ai_protocol);
2188	    if (net < 0) {
2189		warn ("socket");
2190		continue;
2191	    }
2192
2193#if	defined(IP_OPTIONS) && defined(IPPROTO_IP) && defined(HAVE_SETSOCKOPT)
2194	if (hostp[0] == '@' || hostp[0] == '!') {
2195	    char *srp = 0;
2196	    int srlen;
2197	    int proto, opt;
2198
2199	    if ((srlen = sourceroute(a, hostp, &srp, &proto, &opt)) < 0) {
2200		(void) NetClose(net);
2201		net = -1;
2202		continue;
2203	    }
2204	    if (srp && setsockopt(net, proto, opt, srp, srlen) < 0)
2205		perror("setsockopt (source route)");
2206	}
2207#endif
2208
2209#if	defined(IPPROTO_IP) && defined(IP_TOS)
2210	    if (a->ai_family == AF_INET) {
2211# if	defined(HAVE_GETTOSBYNAME)
2212		struct tosent *tp;
2213		if (tos < 0 && (tp = gettosbyname("telnet", "tcp")))
2214		    tos = tp->t_tos;
2215# endif
2216		if (tos < 0)
2217		    tos = 020;	/* Low Delay bit */
2218		if (tos
2219		    && (setsockopt(net, IPPROTO_IP, IP_TOS,
2220				   (void *)&tos, sizeof(int)) < 0)
2221		    && (errno != ENOPROTOOPT))
2222		    perror("telnet: setsockopt (IP_TOS) (ignored)");
2223	    }
2224#endif	/* defined(IPPROTO_IP) && defined(IP_TOS) */
2225	    if (debug && SetSockOpt(net, SOL_SOCKET, SO_DEBUG, 1) < 0) {
2226		perror("setsockopt (SO_DEBUG)");
2227	    }
2228
2229	    if (connect (net, a->ai_addr, a->ai_addrlen) < 0) {
2230		fprintf (stderr, "telnet: connect to address %s: %s\n",
2231			 addrstr, strerror(errno));
2232		NetClose(net);
2233		if (a->ai_next != NULL) {
2234		    continue;
2235		} else {
2236		    freeaddrinfo (ai);
2237		    return 0;
2238		}
2239	    }
2240	    ++connected;
2241#if	defined(AUTHENTICATION) || defined(ENCRYPTION)
2242	    auth_encrypt_connect(connected);
2243#endif
2244	}
2245	freeaddrinfo (ai);
2246	if (connected == 0)
2247	    return 0;
2248    }
2249    cmdrc(hostp, hostname);
2250    set_forward_options();
2251    if (autologin && user == NULL)
2252	user = (char *)get_default_username ();
2253    if (user) {
2254	env_define((unsigned char *)"USER", (unsigned char *)user);
2255	env_export((unsigned char *)"USER");
2256    }
2257    call(status, "status", "notmuch", 0);
2258    if (setjmp(peerdied) == 0)
2259	my_telnet((char *)user);
2260    NetClose(net);
2261    ExitString("Connection closed by foreign host.\r\n",1);
2262    /*NOTREACHED*/
2263    return 0;
2264}
2265
2266#define HELPINDENT ((int)sizeof ("connect"))
2267
2268static char
2269	openhelp[] =	"connect to a site",
2270	closehelp[] =	"close current connection",
2271	logouthelp[] =	"forcibly logout remote user and close the connection",
2272	quithelp[] =	"exit telnet",
2273	statushelp[] =	"print status information",
2274	helphelp[] =	"print help information",
2275	sendhelp[] =	"transmit special characters ('send ?' for more)",
2276	sethelp[] = 	"set operating parameters ('set ?' for more)",
2277	unsethelp[] = 	"unset operating parameters ('unset ?' for more)",
2278	togglestring[] ="toggle operating parameters ('toggle ?' for more)",
2279	slchelp[] =	"change state of special charaters ('slc ?' for more)",
2280	displayhelp[] =	"display operating parameters",
2281#if	defined(AUTHENTICATION)
2282	authhelp[] =	"turn on (off) authentication ('auth ?' for more)",
2283#endif
2284#if	defined(ENCRYPTION)
2285	encrypthelp[] =	"turn on (off) encryption ('encrypt ?' for more)",
2286#endif
2287	zhelp[] =	"suspend telnet",
2288	shellhelp[] =	"invoke a subshell",
2289	envhelp[] =	"change environment variables ('environ ?' for more)",
2290	modestring[] = "try to enter line or character mode ('mode ?' for more)";
2291
2292static int help(int argc, char **argv);
2293
2294static Command cmdtab[] = {
2295	{ "close",	closehelp,	bye,		1 },
2296	{ "logout",	logouthelp,	logout,		1 },
2297	{ "display",	displayhelp,	display,	0 },
2298	{ "mode",	modestring,	modecmd,	0 },
2299	{ "open",	openhelp,	tn,		0 },
2300	{ "quit",	quithelp,	quit,		0 },
2301	{ "send",	sendhelp,	sendcmd,	0 },
2302	{ "set",	sethelp,	setcmd,		0 },
2303	{ "unset",	unsethelp,	unsetcmd,	0 },
2304	{ "status",	statushelp,	status,		0 },
2305	{ "toggle",	togglestring,	toggle,		0 },
2306	{ "slc",	slchelp,	slccmd,		0 },
2307#if	defined(AUTHENTICATION)
2308	{ "auth",	authhelp,	auth_cmd,	0 },
2309#endif
2310#if	defined(ENCRYPTION)
2311	{ "encrypt",	encrypthelp,	encrypt_cmd,	0 },
2312#endif
2313	{ "z",		zhelp,		telnetsuspend,	0 },
2314	{ "!",		shellhelp,	shell,		0 },
2315	{ "environ",	envhelp,	env_cmd,	0 },
2316	{ "?",		helphelp,	help,		0 },
2317	{ 0,            0,              0,              0 }
2318};
2319
2320static char	crmodhelp[] =	"deprecated command -- use 'toggle crmod' instead";
2321static char	escapehelp[] =	"deprecated command -- use 'set escape' instead";
2322
2323static Command cmdtab2[] = {
2324	{ "help",	0,		help,		0 },
2325	{ "escape",	escapehelp,	setescape,	0 },
2326	{ "crmod",	crmodhelp,	togcrmod,	0 },
2327	{ 0,            0,		0, 		0 }
2328};
2329
2330
2331/*
2332 * Call routine with argc, argv set from args (terminated by 0).
2333 */
2334
2335static int
2336call(intrtn_t routine, ...)
2337{
2338    va_list ap;
2339    char *args[100];
2340    int argno = 0;
2341
2342    va_start(ap, routine);
2343    while ((args[argno++] = va_arg(ap, char *)) != 0);
2344    va_end(ap);
2345    return (*routine)(argno-1, args);
2346}
2347
2348
2349static Command
2350*getcmd(char *name)
2351{
2352    Command *cm;
2353
2354    if ((cm = (Command *) genget(name, (char **) cmdtab, sizeof(Command))))
2355	return cm;
2356    return (Command *) genget(name, (char **) cmdtab2, sizeof(Command));
2357}
2358
2359void
2360command(int top, char *tbuf, int cnt)
2361{
2362    Command *c;
2363
2364    setcommandmode();
2365    if (!top) {
2366	putchar('\n');
2367    } else {
2368	signal(SIGINT, SIG_DFL);
2369	signal(SIGQUIT, SIG_DFL);
2370    }
2371    for (;;) {
2372	if (rlogin == _POSIX_VDISABLE)
2373		printf("%s> ", prompt);
2374	if (tbuf) {
2375	    char *cp;
2376	    cp = line;
2377	    while (cnt > 0 && (*cp++ = *tbuf++) != '\n')
2378		cnt--;
2379	    tbuf = 0;
2380	    if (cp == line || *--cp != '\n' || cp == line)
2381		goto getline;
2382	    *cp = '\0';
2383	    if (rlogin == _POSIX_VDISABLE)
2384		printf("%s\r\n", line);
2385	} else {
2386	getline:
2387	    if (rlogin != _POSIX_VDISABLE)
2388		printf("%s> ", prompt);
2389	    if (fgets(line, sizeof(line), stdin) == NULL) {
2390		if (feof(stdin) || ferror(stdin)) {
2391		    quit();
2392		    /*NOTREACHED*/
2393		}
2394		break;
2395	    }
2396	}
2397	if (line[0] == 0)
2398	    break;
2399	makeargv();
2400	if (margv[0] == 0) {
2401	    break;
2402	}
2403	c = getcmd(margv[0]);
2404	if (Ambiguous(c)) {
2405	    printf("?Ambiguous command\r\n");
2406	    continue;
2407	}
2408	if (c == 0) {
2409	    printf("?Invalid command\r\n");
2410	    continue;
2411	}
2412	if (c->needconnect && !connected) {
2413	    printf("?Need to be connected first.\r\n");
2414	    continue;
2415	}
2416	if ((*c->handler)(margc, margv)) {
2417	    break;
2418	}
2419    }
2420    if (!top) {
2421	if (!connected) {
2422	    longjmp(toplevel, 1);
2423	    /*NOTREACHED*/
2424	}
2425	setconnmode(0);
2426    }
2427}
2428
2429/*
2430 * Help command.
2431 */
2432static int
2433help(int argc, char **argv)
2434{
2435	Command *c;
2436
2437	if (argc == 1) {
2438		printf("Commands may be abbreviated.  Commands are:\r\n\r\n");
2439		for (c = cmdtab; c->name; c++)
2440			if (c->help) {
2441				printf("%-*s\t%s\r\n", HELPINDENT, c->name,
2442								    c->help);
2443			}
2444		return 0;
2445	}
2446	while (--argc > 0) {
2447		char *arg;
2448		arg = *++argv;
2449		c = getcmd(arg);
2450		if (Ambiguous(c))
2451			printf("?Ambiguous help command %s\r\n", arg);
2452		else if (c == (Command *)0)
2453			printf("?Invalid help command %s\r\n", arg);
2454		else
2455			printf("%s\r\n", c->help);
2456	}
2457	return 0;
2458}
2459
2460
2461#if	defined(IP_OPTIONS) && defined(IPPROTO_IP)
2462
2463/*
2464 * Source route is handed in as
2465 *	[!]@hop1@hop2...@dst
2466 *
2467 * If the leading ! is present, it is a strict source route, otherwise it is
2468 * assmed to be a loose source route.  Note that leading ! is effective
2469 * only for IPv4 case.
2470 *
2471 * We fill in the source route option as
2472 *	hop1,hop2,hop3...dest
2473 * and return a pointer to hop1, which will
2474 * be the address to connect() to.
2475 *
2476 * Arguments:
2477 *	ai:	The address (by struct addrinfo) for the final destination.
2478 *
2479 *	arg:	Pointer to route list to decipher
2480 *
2481 *	cpp: 	Pointer to a pointer, so that sourceroute() can return
2482 *		the address of result buffer (statically alloc'ed).
2483 *
2484 *	protop/optp:
2485 *		Pointer to an integer.  The pointed variable
2486 *	lenp:	pointer to an integer that contains the
2487 *		length of *cpp if *cpp != NULL.
2488 *
2489 * Return values:
2490 *
2491 *	Returns the length of the option pointed to by *cpp.  If the
2492 *	return value is -1, there was a syntax error in the
2493 *	option, either arg contained unknown characters or too many hosts,
2494 *	or hostname cannot be resolved.
2495 *
2496 *	The caller needs to pass return value (len), *cpp, *protop and *optp
2497 *	to setsockopt(2).
2498 *
2499 *	*cpp:	Points to the result buffer.  The region is statically
2500 *		allocated by the function.
2501 *
2502 *	*protop:
2503 *		protocol # to be passed to setsockopt(2).
2504 *
2505 *	*optp:	option # to be passed to setsockopt(2).
2506 *
2507 */
2508int
2509sourceroute(struct addrinfo *ai,
2510	    char *arg,
2511	    char **cpp,
2512	    int *protop,
2513	    int *optp)
2514{
2515	char *cp, *cp2, *lsrp = NULL, *lsrep = NULL;
2516	struct addrinfo hints, *res;
2517	int len, error;
2518	struct sockaddr_in *sin;
2519	register char c;
2520	static char lsr[44];
2521#ifdef INET6
2522	struct cmsghdr *cmsg = NULL;
2523	struct sockaddr_in6 *sin6;
2524	static char rhbuf[1024];
2525#endif
2526
2527	/*
2528	 * Verify the arguments.
2529	 */
2530	if (cpp == NULL)
2531		return -1;
2532
2533	cp = arg;
2534
2535	*cpp = NULL;
2536	switch (ai->ai_family) {
2537	case AF_INET:
2538		lsrp = lsr;
2539		lsrep = lsrp + sizeof(lsr);
2540
2541		/*
2542		 * Next, decide whether we have a loose source
2543		 * route or a strict source route, and fill in
2544		 * the begining of the option.
2545		 */
2546		if (*cp == '!') {
2547			cp++;
2548			*lsrp++ = IPOPT_SSRR;
2549		} else
2550			*lsrp++ = IPOPT_LSRR;
2551		if (*cp != '@')
2552			return -1;
2553		lsrp++;		/* skip over length, we'll fill it in later */
2554		*lsrp++ = 4;
2555		cp++;
2556		*protop = IPPROTO_IP;
2557		*optp = IP_OPTIONS;
2558		break;
2559#ifdef INET6
2560	case AF_INET6:
2561/* this needs to be updated for rfc2292bis */
2562#ifdef IPV6_PKTOPTIONS
2563		cmsg = inet6_rthdr_init(rhbuf, IPV6_RTHDR_TYPE_0);
2564		if (*cp != '@')
2565			return -1;
2566		cp++;
2567		*protop = IPPROTO_IPV6;
2568		*optp = IPV6_PKTOPTIONS;
2569		break;
2570#else
2571		return -1;
2572#endif
2573#endif
2574	default:
2575		return -1;
2576	}
2577
2578	memset(&hints, 0, sizeof(hints));
2579	hints.ai_family = ai->ai_family;
2580	hints.ai_socktype = SOCK_STREAM;
2581
2582	for (c = 0;;) {
2583		if (c == ':')
2584			cp2 = 0;
2585		else for (cp2 = cp; (c = *cp2) != '\0'; cp2++) {
2586			if (c == ',') {
2587				*cp2++ = '\0';
2588				if (*cp2 == '@')
2589					cp2++;
2590			} else if (c == '@') {
2591				*cp2++ = '\0';
2592			}
2593#if 0	/*colon conflicts with IPv6 address*/
2594			else if (c == ':') {
2595				*cp2++ = '\0';
2596			}
2597#endif
2598			else
2599				continue;
2600			break;
2601		}
2602		if (!c)
2603			cp2 = 0;
2604
2605		error = getaddrinfo(cp, NULL, &hints, &res);
2606		if (error) {
2607			fprintf(stderr, "%s: %s\n", cp, gai_strerror(error));
2608			return -1;
2609		}
2610		if (ai->ai_family != res->ai_family) {
2611			freeaddrinfo(res);
2612			return -1;
2613		}
2614		if (ai->ai_family == AF_INET) {
2615			/*
2616			 * Check to make sure there is space for address
2617			 */
2618			if (lsrp + 4 > lsrep) {
2619				freeaddrinfo(res);
2620				return -1;
2621			}
2622			sin = (struct sockaddr_in *)res->ai_addr;
2623			memcpy(lsrp, &sin->sin_addr, sizeof(struct in_addr));
2624			lsrp += sizeof(struct in_addr);
2625		}
2626#ifdef INET6
2627		else if (ai->ai_family == AF_INET6) {
2628			sin6 = (struct sockaddr_in6 *)res->ai_addr;
2629			inet6_rthdr_add(cmsg, &sin6->sin6_addr,
2630				IPV6_RTHDR_LOOSE);
2631		}
2632#endif
2633		else {
2634			freeaddrinfo(res);
2635			return -1;
2636		}
2637		freeaddrinfo(res);
2638		if (cp2)
2639			cp = cp2;
2640		else
2641			break;
2642	}
2643	if (ai->ai_family == AF_INET) {
2644		/* record the last hop */
2645		if (lsrp + 4 > lsrep)
2646			return -1;
2647		sin = (struct sockaddr_in *)ai->ai_addr;
2648		memcpy(lsrp, &sin->sin_addr, sizeof(struct in_addr));
2649		lsrp += sizeof(struct in_addr);
2650#ifndef	sysV88
2651		lsr[IPOPT_OLEN] = lsrp - lsr;
2652		if (lsr[IPOPT_OLEN] <= 7 || lsr[IPOPT_OLEN] > 40)
2653			return -1;
2654		*lsrp++ = IPOPT_NOP;	/*32bit word align*/
2655		len = lsrp - lsr;
2656		*cpp = lsr;
2657#else
2658		ipopt.io_len = lsrp - lsr;
2659		if (ipopt.io_len <= 5)	/*is 3 better?*/
2660			return -1;
2661		*cpp = (char 8)&ipopt;
2662#endif
2663	}
2664#ifdef INET6
2665	else if (ai->ai_family == AF_INET6) {
2666		inet6_rthdr_lasthop(cmsg, IPV6_RTHDR_LOOSE);
2667		len = cmsg->cmsg_len;
2668		*cpp = rhbuf;
2669	}
2670#endif
2671	else
2672		return -1;
2673	return len;
2674}
2675#endif
2676