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