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. Neither the name of the University nor the names of its contributors
14 *    may be used to endorse or promote products derived from this software
15 *    without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#if 0
31#ifndef lint
32static const char sccsid[] = "@(#)commands.c	8.4 (Berkeley) 5/30/95";
33#endif
34#endif
35#include <sys/cdefs.h>
36__FBSDID("$FreeBSD: stable/11/contrib/telnet/telnet/commands.c 351432 2019-08-23 17:40:47Z emaste $");
37
38#include <sys/param.h>
39#include <sys/un.h>
40#include <sys/file.h>
41#include <sys/socket.h>
42#include <netinet/in.h>
43
44#include <assert.h>
45#include <ctype.h>
46#include <err.h>
47#include <errno.h>
48#include <netdb.h>
49#include <pwd.h>
50#include <signal.h>
51#include <stdarg.h>
52#include <stdlib.h>
53#include <string.h>
54#include <unistd.h>
55
56#include <arpa/telnet.h>
57#include <arpa/inet.h>
58
59#include "general.h"
60
61#include "ring.h"
62
63#include "externs.h"
64#include "defines.h"
65#include "types.h"
66#include "misc.h"
67
68#ifdef	AUTHENTICATION
69#include <libtelnet/auth.h>
70#endif
71#ifdef	ENCRYPTION
72#include <libtelnet/encrypt.h>
73#endif
74
75#include <netinet/in_systm.h>
76#include <netinet/ip.h>
77#include <netinet/ip6.h>
78
79#ifndef       MAXHOSTNAMELEN
80#define       MAXHOSTNAMELEN 256
81#endif
82
83typedef int (*intrtn_t)(int, char **);
84
85#ifdef	AUTHENTICATION
86extern int auth_togdebug(int);
87#endif
88#ifdef	ENCRYPTION
89extern int EncryptAutoEnc(int);
90extern int EncryptAutoDec(int);
91extern int EncryptDebug(int);
92extern int EncryptVerbose(int);
93#endif	/* ENCRYPTION */
94#if	defined(IPPROTO_IP) && defined(IP_TOS)
95int tos = -1;
96#endif	/* defined(IPPROTO_IP) && defined(IP_TOS) */
97
98char	*hostname;
99static char _hostname[MAXHOSTNAMELEN];
100
101static int help(int, char **);
102static int call(intrtn_t, ...);
103static void cmdrc(char *, char *);
104#ifdef INET6
105static int switch_af(struct addrinfo **);
106#endif
107static int togglehelp(void);
108static int send_tncmd(void (*)(int, int), const char *, char *);
109static int setmod(int);
110static int clearmode(int);
111static int modehelp(void);
112static int sourceroute(struct addrinfo *, char *, unsigned char **, int *, int *, int *);
113
114typedef struct {
115	const char *name;	/* command name */
116	const char *help;	/* help string (NULL for no help) */
117	int	(*handler)(int, char **); /* routine which executes command */
118	int	needconnect;	/* Do we need to be connected to execute? */
119} Command;
120
121static char line[256];
122static char saveline[256];
123static int margc;
124static char *margv[20];
125
126#ifdef OPIE
127#include <sys/wait.h>
128#define PATH_OPIEKEY	"/usr/bin/opiekey"
129static int
130opie_calc(int argc, char *argv[])
131{
132	int status;
133
134	if(argc != 3) {
135		printf("%s sequence challenge\n", argv[0]);
136		return (0);
137	}
138
139	switch(fork()) {
140	case 0:
141		execv(PATH_OPIEKEY, argv);
142		exit (1);
143	case -1:
144		perror("fork");
145		break;
146	default:
147		(void) wait(&status);
148		if (WIFEXITED(status))
149			return (WEXITSTATUS(status));
150	}
151	return (0);
152}
153#endif
154
155static void
156makeargv(void)
157{
158    char *cp, *cp2, c;
159    char **argp = margv;
160
161    margc = 0;
162    cp = line;
163    if (*cp == '!') {		/* Special case shell escape */
164	strcpy(saveline, line);	/* save for shell command */
165	*argp++ = strdup("!");		/* No room in string to get this */
166	margc++;
167	cp++;
168    }
169    while ((c = *cp)) {
170	int inquote = 0;
171	while (isspace(c))
172	    c = *++cp;
173	if (c == '\0')
174	    break;
175	*argp++ = cp;
176	margc += 1;
177	for (cp2 = cp; c != '\0'; c = *++cp) {
178	    if (inquote) {
179		if (c == inquote) {
180		    inquote = 0;
181		    continue;
182		}
183	    } else {
184		if (c == '\\') {
185		    if ((c = *++cp) == '\0')
186			break;
187		} else if (c == '"') {
188		    inquote = '"';
189		    continue;
190		} else if (c == '\'') {
191		    inquote = '\'';
192		    continue;
193		} else if (isspace(c))
194		    break;
195	    }
196	    *cp2++ = c;
197	}
198	*cp2 = '\0';
199	if (c == '\0')
200	    break;
201	cp++;
202    }
203    *argp++ = 0;
204}
205
206/*
207 * Make a character string into a number.
208 *
209 * Todo:  1.  Could take random integers (12, 0x12, 012, 0b1).
210 */
211
212static int
213special(char *s)
214{
215	char c;
216	char b;
217
218	switch (*s) {
219	case '^':
220		b = *++s;
221		if (b == '?') {
222		    c = b | 0x40;		/* DEL */
223		} else {
224		    c = b & 0x1f;
225		}
226		break;
227	default:
228		c = *s;
229		break;
230	}
231	return c;
232}
233
234/*
235 * Construct a control character sequence
236 * for a special character.
237 */
238static const char *
239control(cc_t c)
240{
241	static char buf[5];
242	/*
243	 * The only way I could get the Sun 3.5 compiler
244	 * to shut up about
245	 *	if ((unsigned int)c >= 0x80)
246	 * was to assign "c" to an unsigned int variable...
247	 * Arggg....
248	 */
249	unsigned int uic = (unsigned int)c;
250
251	if (uic == 0x7f)
252		return ("^?");
253	if (c == (cc_t)_POSIX_VDISABLE) {
254		return "off";
255	}
256	if (uic >= 0x80) {
257		buf[0] = '\\';
258		buf[1] = ((c>>6)&07) + '0';
259		buf[2] = ((c>>3)&07) + '0';
260		buf[3] = (c&07) + '0';
261		buf[4] = 0;
262	} else if (uic >= 0x20) {
263		buf[0] = c;
264		buf[1] = 0;
265	} else {
266		buf[0] = '^';
267		buf[1] = '@'+c;
268		buf[2] = 0;
269	}
270	return (buf);
271}
272
273/*
274 *	The following are data structures and routines for
275 *	the "send" command.
276 *
277 */
278
279struct sendlist {
280    const char	*name;		/* How user refers to it (case independent) */
281    const char	*help;		/* Help information (0 ==> no help) */
282    int		needconnect;	/* Need to be connected */
283    int		narg;		/* Number of arguments */
284    int		(*handler)(char *, ...); /* Routine to perform (for special ops) */
285    int		nbyte;		/* Number of bytes to send this command */
286    int		what;		/* Character to be sent (<0 ==> special) */
287};
288
289
290static int
291	send_esc(void),
292	send_help(void),
293	send_docmd(char *),
294	send_dontcmd(char *),
295	send_willcmd(char *),
296	send_wontcmd(char *);
297
298static struct sendlist Sendlist[] = {
299    { "ao",	"Send Telnet Abort output",	1, 0, NULL, 2, AO },
300    { "ayt",	"Send Telnet 'Are You There'",	1, 0, NULL, 2, AYT },
301    { "brk",	"Send Telnet Break",		1, 0, NULL, 2, BREAK },
302    { "break",	NULL,				1, 0, NULL, 2, BREAK },
303    { "ec",	"Send Telnet Erase Character",	1, 0, NULL, 2, EC },
304    { "el",	"Send Telnet Erase Line",	1, 0, NULL, 2, EL },
305    { "escape",	"Send current escape character",1, 0, (int (*)(char *, ...))send_esc, 1, 0 },
306    { "ga",	"Send Telnet 'Go Ahead' sequence", 1, 0, NULL, 2, GA },
307    { "ip",	"Send Telnet Interrupt Process",1, 0, NULL, 2, IP },
308    { "intp",	NULL,				1, 0, NULL, 2, IP },
309    { "interrupt", NULL,			1, 0, NULL, 2, IP },
310    { "intr",	NULL,				1, 0, NULL, 2, IP },
311    { "nop",	"Send Telnet 'No operation'",	1, 0, NULL, 2, NOP },
312    { "eor",	"Send Telnet 'End of Record'",	1, 0, NULL, 2, EOR },
313    { "abort",	"Send Telnet 'Abort Process'",	1, 0, NULL, 2, ABORT },
314    { "susp",	"Send Telnet 'Suspend Process'",1, 0, NULL, 2, SUSP },
315    { "eof",	"Send Telnet End of File Character", 1, 0, NULL, 2, xEOF },
316    { "synch",	"Perform Telnet 'Synch operation'", 1, 0, (int (*)(char *, ...))dosynch, 2, 0 },
317    { "getstatus", "Send request for STATUS",	1, 0, (int (*)(char *, ...))get_status, 6, 0 },
318    { "?",	"Display send options",		0, 0, (int (*)(char *, ...))send_help, 0, 0 },
319    { "help",	NULL,				0, 0, (int (*)(char *, ...))send_help, 0, 0 },
320    { "do",	NULL,				0, 1, (int (*)(char *, ...))send_docmd, 3, 0 },
321    { "dont",	NULL,				0, 1, (int (*)(char *, ...))send_dontcmd, 3, 0 },
322    { "will",	NULL,				0, 1, (int (*)(char *, ...))send_willcmd, 3, 0 },
323    { "wont",	NULL,				0, 1, (int (*)(char *, ...))send_wontcmd, 3, 0 },
324    { NULL,	NULL,				0, 0, NULL, 0, 0 }
325};
326
327#define	GETSEND(name) ((struct sendlist *) genget(name, (char **) Sendlist, \
328				sizeof(struct sendlist)))
329
330static int
331sendcmd(int argc, char *argv[])
332{
333    int count;		/* how many bytes we are going to need to send */
334    int i;
335    struct sendlist *s;	/* pointer to current command */
336    int success = 0;
337    int needconnect = 0;
338
339    if (argc < 2) {
340	printf("need at least one argument for 'send' command\n");
341	printf("'send ?' for help\n");
342	return 0;
343    }
344    /*
345     * First, validate all the send arguments.
346     * In addition, we see how much space we are going to need, and
347     * whether or not we will be doing a "SYNCH" operation (which
348     * flushes the network queue).
349     */
350    count = 0;
351    for (i = 1; i < argc; i++) {
352	s = GETSEND(argv[i]);
353	if (s == 0) {
354	    printf("Unknown send argument '%s'\n'send ?' for help.\n",
355			argv[i]);
356	    return 0;
357	} else if (Ambiguous((void *)s)) {
358	    printf("Ambiguous send argument '%s'\n'send ?' for help.\n",
359			argv[i]);
360	    return 0;
361	}
362	if (i + s->narg >= argc) {
363	    fprintf(stderr,
364	    "Need %d argument%s to 'send %s' command.  'send %s ?' for help.\n",
365		s->narg, s->narg == 1 ? "" : "s", s->name, s->name);
366	    return 0;
367	}
368	count += s->nbyte;
369	if ((void *)s->handler == (void *)send_help) {
370	    send_help();
371	    return 0;
372	}
373
374	i += s->narg;
375	needconnect += s->needconnect;
376    }
377    if (!connected && needconnect) {
378	printf("?Need to be connected first.\n");
379	printf("'send ?' for help\n");
380	return 0;
381    }
382    /* Now, do we have enough room? */
383    if (NETROOM() < count) {
384	printf("There is not enough room in the buffer TO the network\n");
385	printf("to process your request.  Nothing will be done.\n");
386	printf("('send synch' will throw away most data in the network\n");
387	printf("buffer, if this might help.)\n");
388	return 0;
389    }
390    /* OK, they are all OK, now go through again and actually send */
391    count = 0;
392    for (i = 1; i < argc; i++) {
393	if ((s = GETSEND(argv[i])) == 0) {
394	    fprintf(stderr, "Telnet 'send' error - argument disappeared!\n");
395	    quit();
396	    /*NOTREACHED*/
397	}
398	if (s->handler) {
399	    count++;
400	    success += (*s->handler)((s->narg > 0) ? argv[i+1] : 0,
401				  (s->narg > 1) ? argv[i+2] : 0);
402	    i += s->narg;
403	} else {
404	    NET2ADD(IAC, s->what);
405	    printoption("SENT", IAC, s->what);
406	}
407    }
408    return (count == success);
409}
410
411static int
412send_esc(void)
413{
414    NETADD(escape);
415    return 1;
416}
417
418static int
419send_docmd(char *name)
420{
421    return(send_tncmd(send_do, "do", name));
422}
423
424static int
425send_dontcmd(name)
426    char *name;
427{
428    return(send_tncmd(send_dont, "dont", name));
429}
430
431static int
432send_willcmd(char *name)
433{
434    return(send_tncmd(send_will, "will", name));
435}
436
437static int
438send_wontcmd(char *name)
439{
440    return(send_tncmd(send_wont, "wont", name));
441}
442
443static int
444send_tncmd(void (*func)(int, int), const char *cmd, char *name)
445{
446    char **cpp;
447    extern char *telopts[];
448    int val = 0;
449
450    if (isprefix(name, "help") || isprefix(name, "?")) {
451	int col, len;
452
453	printf("usage: send %s <value|option>\n", cmd);
454	printf("\"value\" must be from 0 to 255\n");
455	printf("Valid options are:\n\t");
456
457	col = 8;
458	for (cpp = telopts; *cpp; cpp++) {
459	    len = strlen(*cpp) + 3;
460	    if (col + len > 65) {
461		printf("\n\t");
462		col = 8;
463	    }
464	    printf(" \"%s\"", *cpp);
465	    col += len;
466	}
467	printf("\n");
468	return 0;
469    }
470    cpp = (char **)genget(name, telopts, sizeof(char *));
471    if (Ambiguous(cpp)) {
472	fprintf(stderr,"'%s': ambiguous argument ('send %s ?' for help).\n",
473					name, cmd);
474	return 0;
475    }
476    if (cpp) {
477	val = cpp - telopts;
478    } else {
479	char *cp = name;
480
481	while (*cp >= '0' && *cp <= '9') {
482	    val *= 10;
483	    val += *cp - '0';
484	    cp++;
485	}
486	if (*cp != 0) {
487	    fprintf(stderr, "'%s': unknown argument ('send %s ?' for help).\n",
488					name, cmd);
489	    return 0;
490	} else if (val < 0 || val > 255) {
491	    fprintf(stderr, "'%s': bad value ('send %s ?' for help).\n",
492					name, cmd);
493	    return 0;
494	}
495    }
496    if (!connected) {
497	printf("?Need to be connected first.\n");
498	return 0;
499    }
500    (*func)(val, 1);
501    return 1;
502}
503
504static int
505send_help(void)
506{
507    struct sendlist *s;	/* pointer to current command */
508    for (s = Sendlist; s->name; s++) {
509	if (s->help)
510	    printf("%-15s %s\n", s->name, s->help);
511    }
512    return(0);
513}
514
515/*
516 * The following are the routines and data structures referred
517 * to by the arguments to the "toggle" command.
518 */
519
520static int
521lclchars(void)
522{
523    donelclchars = 1;
524    return 1;
525}
526
527static int
528togdebug(void)
529{
530#ifndef	NOT43
531    if (net > 0 &&
532	(SetSockOpt(net, SOL_SOCKET, SO_DEBUG, telnet_debug)) < 0) {
533	    perror("setsockopt (SO_DEBUG)");
534    }
535#else	/* NOT43 */
536    if (telnet_debug) {
537	if (net > 0 && SetSockOpt(net, SOL_SOCKET, SO_DEBUG, 1) < 0)
538	    perror("setsockopt (SO_DEBUG)");
539    } else
540	printf("Cannot turn off socket debugging\n");
541#endif	/* NOT43 */
542    return 1;
543}
544
545
546static int
547togcrlf(void)
548{
549    if (crlf) {
550	printf("Will send carriage returns as telnet <CR><LF>.\n");
551    } else {
552	printf("Will send carriage returns as telnet <CR><NUL>.\n");
553    }
554    return 1;
555}
556
557int binmode;
558
559static int
560togbinary(int val)
561{
562    donebinarytoggle = 1;
563
564    if (val >= 0) {
565	binmode = val;
566    } else {
567	if (my_want_state_is_will(TELOPT_BINARY) &&
568				my_want_state_is_do(TELOPT_BINARY)) {
569	    binmode = 1;
570	} else if (my_want_state_is_wont(TELOPT_BINARY) &&
571				my_want_state_is_dont(TELOPT_BINARY)) {
572	    binmode = 0;
573	}
574	val = binmode ? 0 : 1;
575    }
576
577    if (val == 1) {
578	if (my_want_state_is_will(TELOPT_BINARY) &&
579					my_want_state_is_do(TELOPT_BINARY)) {
580	    printf("Already operating in binary mode with remote host.\n");
581	} else {
582	    printf("Negotiating binary mode with remote host.\n");
583	    tel_enter_binary(3);
584	}
585    } else {
586	if (my_want_state_is_wont(TELOPT_BINARY) &&
587					my_want_state_is_dont(TELOPT_BINARY)) {
588	    printf("Already in network ascii mode with remote host.\n");
589	} else {
590	    printf("Negotiating network ascii mode with remote host.\n");
591	    tel_leave_binary(3);
592	}
593    }
594    return 1;
595}
596
597static int
598togrbinary(int val)
599{
600    donebinarytoggle = 1;
601
602    if (val == -1)
603	val = my_want_state_is_do(TELOPT_BINARY) ? 0 : 1;
604
605    if (val == 1) {
606	if (my_want_state_is_do(TELOPT_BINARY)) {
607	    printf("Already receiving in binary mode.\n");
608	} else {
609	    printf("Negotiating binary mode on input.\n");
610	    tel_enter_binary(1);
611	}
612    } else {
613	if (my_want_state_is_dont(TELOPT_BINARY)) {
614	    printf("Already receiving in network ascii mode.\n");
615	} else {
616	    printf("Negotiating network ascii mode on input.\n");
617	    tel_leave_binary(1);
618	}
619    }
620    return 1;
621}
622
623static int
624togxbinary(int val)
625{
626    donebinarytoggle = 1;
627
628    if (val == -1)
629	val = my_want_state_is_will(TELOPT_BINARY) ? 0 : 1;
630
631    if (val == 1) {
632	if (my_want_state_is_will(TELOPT_BINARY)) {
633	    printf("Already transmitting in binary mode.\n");
634	} else {
635	    printf("Negotiating binary mode on output.\n");
636	    tel_enter_binary(2);
637	}
638    } else {
639	if (my_want_state_is_wont(TELOPT_BINARY)) {
640	    printf("Already transmitting in network ascii mode.\n");
641	} else {
642	    printf("Negotiating network ascii mode on output.\n");
643	    tel_leave_binary(2);
644	}
645    }
646    return 1;
647}
648
649struct togglelist {
650    const char	*name;		/* name of toggle */
651    const char	*help;		/* help message */
652    int		(*handler)(int); /* routine to do actual setting */
653    int		*variable;
654    const char	*actionexplanation;
655};
656
657static struct togglelist Togglelist[] = {
658    { "autoflush",
659	"flushing of output when sending interrupt characters",
660	    0,
661		&autoflush,
662		    "flush output when sending interrupt characters" },
663    { "autosynch",
664	"automatic sending of interrupt characters in urgent mode",
665	    0,
666		&autosynch,
667		    "send interrupt characters in urgent mode" },
668#ifdef	AUTHENTICATION
669    { "autologin",
670	"automatic sending of login and/or authentication info",
671	    0,
672		&autologin,
673		    "send login name and/or authentication information" },
674    { "authdebug",
675	"Toggle authentication debugging",
676	    auth_togdebug,
677		0,
678		     "print authentication debugging information" },
679#endif
680#ifdef	ENCRYPTION
681    { "autoencrypt",
682	"automatic encryption of data stream",
683	    EncryptAutoEnc,
684		0,
685		    "automatically encrypt output" },
686    { "autodecrypt",
687	"automatic decryption of data stream",
688	    EncryptAutoDec,
689		0,
690		    "automatically decrypt input" },
691    { "verbose_encrypt",
692	"Toggle verbose encryption output",
693	    EncryptVerbose,
694		0,
695		    "print verbose encryption output" },
696    { "encdebug",
697	"Toggle encryption debugging",
698	    EncryptDebug,
699		0,
700		    "print encryption debugging information" },
701#endif	/* ENCRYPTION */
702    { "skiprc",
703	"don't read ~/.telnetrc file",
704	    0,
705		&skiprc,
706		    "skip reading of ~/.telnetrc file" },
707    { "binary",
708	"sending and receiving of binary data",
709	    togbinary,
710		0,
711		    0 },
712    { "inbinary",
713	"receiving of binary data",
714	    togrbinary,
715		0,
716		    0 },
717    { "outbinary",
718	"sending of binary data",
719	    togxbinary,
720		0,
721		    0 },
722    { "crlf",
723	"sending carriage returns as telnet <CR><LF>",
724	    (int (*)(int))togcrlf,
725		&crlf,
726		    0 },
727    { "crmod",
728	"mapping of received carriage returns",
729	    0,
730		&crmod,
731		    "map carriage return on output" },
732    { "localchars",
733	"local recognition of certain control characters",
734	    (int (*)(int))lclchars,
735		&localchars,
736		    "recognize certain control characters" },
737    { " ", "", NULL, NULL, NULL },		/* empty line */
738    { "debug",
739	"debugging",
740	    (int (*)(int))togdebug,
741		&telnet_debug,
742		    "turn on socket level debugging" },
743    { "netdata",
744	"printing of hexadecimal network data (debugging)",
745	    0,
746		&netdata,
747		    "print hexadecimal representation of network traffic" },
748    { "prettydump",
749	"output of \"netdata\" to user readable format (debugging)",
750	    0,
751		&prettydump,
752		    "print user readable output for \"netdata\"" },
753    { "options",
754	"viewing of options processing (debugging)",
755	    0,
756		&showoptions,
757		    "show option processing" },
758    { "termdata",
759	"(debugging) toggle printing of hexadecimal terminal data",
760	    0,
761		&termdata,
762		    "print hexadecimal representation of terminal traffic" },
763    { "?",
764	NULL,
765	    (int (*)(int))togglehelp,
766		NULL,
767		    NULL },
768    { NULL, NULL, NULL, NULL, NULL },
769    { "help",
770	NULL,
771	    (int (*)(int))togglehelp,
772		NULL,
773		    NULL },
774    { NULL, NULL, NULL, NULL, NULL }
775};
776
777static int
778togglehelp(void)
779{
780    struct togglelist *c;
781
782    for (c = Togglelist; c->name; c++) {
783	if (c->help) {
784	    if (*c->help)
785		printf("%-15s toggle %s\n", c->name, c->help);
786	    else
787		printf("\n");
788	}
789    }
790    printf("\n");
791    printf("%-15s %s\n", "?", "display help information");
792    return 0;
793}
794
795static void
796settogglehelp(int set)
797{
798    struct togglelist *c;
799
800    for (c = Togglelist; c->name; c++) {
801	if (c->help) {
802	    if (*c->help)
803		printf("%-15s %s %s\n", c->name, set ? "enable" : "disable",
804						c->help);
805	    else
806		printf("\n");
807	}
808    }
809}
810
811#define	GETTOGGLE(name) (struct togglelist *) \
812		genget(name, (char **) Togglelist, sizeof(struct togglelist))
813
814static int
815toggle(int argc, char *argv[])
816{
817    int retval = 1;
818    char *name;
819    struct togglelist *c;
820
821    if (argc < 2) {
822	fprintf(stderr,
823	    "Need an argument to 'toggle' command.  'toggle ?' for help.\n");
824	return 0;
825    }
826    argc--;
827    argv++;
828    while (argc--) {
829	name = *argv++;
830	c = GETTOGGLE(name);
831	if (Ambiguous((void *)c)) {
832	    fprintf(stderr, "'%s': ambiguous argument ('toggle ?' for help).\n",
833					name);
834	    return 0;
835	} else if (c == 0) {
836	    fprintf(stderr, "'%s': unknown argument ('toggle ?' for help).\n",
837					name);
838	    return 0;
839	} else {
840	    if (c->variable) {
841		*c->variable = !*c->variable;		/* invert it */
842		if (c->actionexplanation) {
843		    printf("%s %s.\n", *c->variable? "Will" : "Won't",
844							c->actionexplanation);
845		}
846	    }
847	    if (c->handler) {
848		retval &= (*c->handler)(-1);
849	    }
850	}
851    }
852    return retval;
853}
854
855/*
856 * The following perform the "set" command.
857 */
858
859#ifdef	USE_TERMIO
860struct termio new_tc = { 0, 0, 0, 0, {}, 0, 0 };
861#endif
862
863struct setlist {
864    const char *name;			/* name */
865    const char *help;			/* help information */
866    void (*handler)(char *);
867    cc_t *charp;			/* where it is located at */
868};
869
870static struct setlist Setlist[] = {
871#ifdef	KLUDGELINEMODE
872    { "echo", 	"character to toggle local echoing on/off", NULL, &echoc },
873#endif
874    { "escape",	"character to escape back to telnet command mode", NULL, &escape },
875    { "rlogin", "rlogin escape character", 0, &rlogin },
876    { "tracefile", "file to write trace information to", SetNetTrace, (cc_t *)NetTraceFile},
877    { " ", "", NULL, NULL },
878    { " ", "The following need 'localchars' to be toggled true", NULL, NULL },
879    { "flushoutput", "character to cause an Abort Output", NULL, termFlushCharp },
880    { "interrupt", "character to cause an Interrupt Process", NULL, termIntCharp },
881    { "quit",	"character to cause an Abort process", NULL, termQuitCharp },
882    { "eof",	"character to cause an EOF ", NULL, termEofCharp },
883    { " ", "", NULL, NULL },
884    { " ", "The following are for local editing in linemode", NULL, NULL },
885    { "erase",	"character to use to erase a character", NULL, termEraseCharp },
886    { "kill",	"character to use to erase a line", NULL, termKillCharp },
887    { "lnext",	"character to use for literal next", NULL, termLiteralNextCharp },
888    { "susp",	"character to cause a Suspend Process", NULL, termSuspCharp },
889    { "reprint", "character to use for line reprint", NULL, termRprntCharp },
890    { "worderase", "character to use to erase a word", NULL, termWerasCharp },
891    { "start",	"character to use for XON", NULL, termStartCharp },
892    { "stop",	"character to use for XOFF", NULL, termStopCharp },
893    { "forw1",	"alternate end of line character", NULL, termForw1Charp },
894    { "forw2",	"alternate end of line character", NULL, termForw2Charp },
895    { "ayt",	"alternate AYT character", NULL, termAytCharp },
896    { "baudrate", "set remote baud rate", DoBaudRate, ComPortBaudRate },
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(const unsigned char *, unsigned char *);
1518extern void
1519	env_undefine(unsigned char *),
1520	env_export(const unsigned char *),
1521	env_unexport(const unsigned char *),
1522	env_send(unsigned char *),
1523#if defined(OLD_ENVIRON) && defined(ENV_HACK)
1524	env_varval(unsigned char *),
1525#endif
1526	env_list(void);
1527static void
1528	env_help(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, sizeof(hbuf));
1656		hbuf[sizeof(hbuf)-1] = '\0';
1657		asprintf(&cp, "%s%s", hbuf, cp2);
1658		assert(cp != NULL);
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(char *),
1851	auth_disable(char *),
1852	auth_status(void);
1853static int
1854	auth_help(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(char *, char *),
1934	EncryptDisable(char *, char *),
1935	EncryptType(char *, char *),
1936	EncryptStart(char *),
1937	EncryptStartInput(void),
1938	EncryptStartOutput(void),
1939	EncryptStop(char *),
1940	EncryptStopInput(void),
1941	EncryptStopOutput(void),
1942	EncryptStatus(void);
1943static int
1944	EncryptHelp(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    unsigned 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_family = family;
2240	hints.ai_socktype = SOCK_STREAM;
2241	error = getaddrinfo(src_addr, 0, &hints, &src_res);
2242	if (error == EAI_NONAME) {
2243		hints.ai_flags = 0;
2244		error = getaddrinfo(src_addr, 0, &hints, &src_res);
2245	}
2246	if (error != 0) {
2247		fprintf(stderr, "%s: %s\n", src_addr, gai_strerror(error));
2248		if (error == EAI_SYSTEM)
2249			fprintf(stderr, "%s: %s\n", src_addr, strerror(errno));
2250		setuid(getuid());
2251		return 0;
2252	}
2253	src_res0 = src_res;
2254    }
2255    if (hostp[0] == '/') {
2256	struct sockaddr_un su;
2257
2258	if (strlen(hostp) >= sizeof(su.sun_path)) {
2259	    fprintf(stderr, "hostname too long for unix domain socket: %s",
2260		    hostp);
2261		goto fail;
2262	}
2263	hostname = hostp;
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	if (hostname == NULL) {
2287	    hostname = hostp;
2288	} else {
2289	    hostname++;
2290	    srcroute = 1;
2291	}
2292    } else
2293        hostname = hostp;
2294    if (!portp) {
2295      telnetport = 1;
2296      portp = strdup("telnet");
2297    } else if (*portp == '-') {
2298      portp++;
2299      telnetport = 1;
2300    } else if (*portp == '+') {
2301      portp++;
2302      telnetport = -1;
2303    } else
2304      telnetport = 0;
2305
2306    memset(&hints, 0, sizeof(hints));
2307    hints.ai_flags = AI_NUMERICHOST;
2308    hints.ai_family = family;
2309    hints.ai_socktype = SOCK_STREAM;
2310    error = getaddrinfo(hostname, portp, &hints, &res);
2311    if (error) {
2312        hints.ai_flags = AI_CANONNAME;
2313	error = getaddrinfo(hostname, portp, &hints, &res);
2314    }
2315    if (error != 0) {
2316	fprintf(stderr, "%s: %s\n", hostname, gai_strerror(error));
2317	if (error == EAI_SYSTEM)
2318	    fprintf(stderr, "%s: %s\n", hostname, strerror(errno));
2319	setuid(getuid());
2320	goto fail;
2321    }
2322    if (hints.ai_flags == AI_NUMERICHOST) {
2323	/* hostname has numeric */
2324        int gni_err = 1;
2325
2326	if (doaddrlookup)
2327	    gni_err = getnameinfo(res->ai_addr, res->ai_addr->sa_len,
2328				  _hostname, sizeof(_hostname) - 1, NULL, 0,
2329				  NI_NAMEREQD);
2330	if (gni_err != 0)
2331	    (void) strncpy(_hostname, hostp, sizeof(_hostname) - 1);
2332	_hostname[sizeof(_hostname)-1] = '\0';
2333	hostname = _hostname;
2334    } else {
2335	/* hostname has FQDN */
2336	if (srcroute != 0)
2337	    (void) strncpy(_hostname, hostname, sizeof(_hostname) - 1);
2338	else if (res->ai_canonname != NULL)
2339	  strcpy(_hostname, res->ai_canonname);
2340	else
2341	  (void) strncpy(_hostname, hostp, sizeof(_hostname) - 1);
2342	_hostname[sizeof(_hostname)-1] = '\0';
2343	hostname = _hostname;
2344    }
2345    res0 = res;
2346 #ifdef INET6
2347 af_again:
2348 #endif
2349    if (srcroute != 0) {
2350        static char hostbuf[BUFSIZ];
2351
2352	if (af_error == 0) { /* save intermediate hostnames for retry */
2353		strncpy(hostbuf, hostp, BUFSIZ - 1);
2354		hostbuf[BUFSIZ - 1] = '\0';
2355	} else
2356		hostp = hostbuf;
2357	srp = 0;
2358	result = sourceroute(res, hostp, &srp, &srlen, &proto, &opt);
2359	if (result == 0) {
2360#ifdef INET6
2361	    if (family == AF_UNSPEC && af_error == 0 &&
2362		switch_af(&res) == 1) {
2363	        af_error = 1;
2364		goto af_again;
2365	    }
2366#endif
2367	    setuid(getuid());
2368	    goto fail;
2369	} else if (result == -1) {
2370	    printf("Bad source route option: %s\n", hostp);
2371	    setuid(getuid());
2372	    goto fail;
2373	}
2374    }
2375    do {
2376        printf("Trying %s...\n", sockaddr_ntop(res->ai_addr));
2377	net = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
2378	setuid(getuid());
2379	if (net < 0) {
2380#ifdef INET6
2381	    if (family == AF_UNSPEC && af_error == 0 &&
2382		switch_af(&res) == 1) {
2383	        af_error = 1;
2384		goto af_again;
2385	    }
2386#endif
2387	    perror("telnet: socket");
2388	    goto fail;
2389	}
2390	if (srp && setsockopt(net, proto, opt, (char *)srp, srlen) < 0)
2391		perror("setsockopt (source route)");
2392#if	defined(IPPROTO_IP) && defined(IP_TOS)
2393	if (res->ai_family == PF_INET) {
2394# if	defined(HAS_GETTOS)
2395	    struct tosent *tp;
2396	    if (tos < 0 && (tp = gettosbyname("telnet", "tcp")))
2397		tos = tp->t_tos;
2398# endif
2399	    if (tos < 0)
2400		tos = IPTOS_LOWDELAY;
2401	    if (tos
2402		&& (setsockopt(net, IPPROTO_IP, IP_TOS,
2403		    (char *)&tos, sizeof(int)) < 0)
2404		&& (errno != ENOPROTOOPT))
2405		    perror("telnet: setsockopt (IP_TOS) (ignored)");
2406	}
2407#endif	/* defined(IPPROTO_IP) && defined(IP_TOS) */
2408
2409	if (telnet_debug && SetSockOpt(net, SOL_SOCKET, SO_DEBUG, 1) < 0) {
2410		perror("setsockopt (SO_DEBUG)");
2411	}
2412
2413	if (src_addr != NULL) {
2414	    for (src_res = src_res0; src_res != 0; src_res = src_res->ai_next)
2415	        if (src_res->ai_family == res->ai_family)
2416		    break;
2417	    if (src_res == NULL)
2418		src_res = src_res0;
2419	    if (bind(net, src_res->ai_addr, src_res->ai_addrlen) == -1) {
2420#ifdef INET6
2421	        if (family == AF_UNSPEC && af_error == 0 &&
2422		    switch_af(&res) == 1) {
2423		    af_error = 1;
2424		    (void) NetClose(net);
2425		    goto af_again;
2426		}
2427#endif
2428		perror("bind");
2429		(void) NetClose(net);
2430		goto fail;
2431	    }
2432	}
2433#if defined(IPSEC) && defined(IPSEC_POLICY_IPSEC)
2434	if (setpolicy(net, res, ipsec_policy_in) < 0) {
2435		(void) NetClose(net);
2436		goto fail;
2437	}
2438	if (setpolicy(net, res, ipsec_policy_out) < 0) {
2439		(void) NetClose(net);
2440		goto fail;
2441	}
2442#endif
2443
2444	if (connect(net, res->ai_addr, res->ai_addrlen) < 0) {
2445	    struct addrinfo *next;
2446
2447	    next = res->ai_next;
2448	    /* If already an af failed, only try same af. */
2449	    if (af_error != 0)
2450		while (next != NULL && next->ai_family != res->ai_family)
2451		    next = next->ai_next;
2452	    warn("connect to address %s", sockaddr_ntop(res->ai_addr));
2453	    if (next != NULL) {
2454		res = next;
2455		(void) NetClose(net);
2456		continue;
2457	    }
2458	    warnx("Unable to connect to remote host");
2459	    (void) NetClose(net);
2460	    goto fail;
2461	}
2462	connected++;
2463#ifdef	AUTHENTICATION
2464#ifdef	ENCRYPTION
2465	auth_encrypt_connect(connected);
2466#endif
2467#endif
2468    } while (connected == 0);
2469    freeaddrinfo(res0);
2470    if (src_res0 != NULL)
2471        freeaddrinfo(src_res0);
2472    cmdrc(hostp, hostname);
2473 af_unix:
2474    connected = 1;
2475    if (autologin && user == NULL) {
2476	struct passwd *pw;
2477
2478	user = getenv("USER");
2479	if (user == NULL ||
2480	    ((pw = getpwnam(user)) && pw->pw_uid != getuid())) {
2481		if ((pw = getpwuid(getuid())))
2482			user = pw->pw_name;
2483		else
2484			user = NULL;
2485	}
2486    }
2487    if (user) {
2488	env_define("USER", user);
2489	env_export("USER");
2490    }
2491    (void) call(status, "status", "notmuch", 0);
2492    telnet(user);
2493    (void) NetClose(net);
2494    ExitString("Connection closed by foreign host.\n",1);
2495    /*NOTREACHED*/
2496 fail:
2497    if (res0 != NULL)
2498        freeaddrinfo(res0);
2499    if (src_res0 != NULL)
2500        freeaddrinfo(src_res0);
2501    return 0;
2502}
2503
2504#define HELPINDENT (sizeof ("connect"))
2505
2506static char
2507	openhelp[] =	"connect to a site",
2508	closehelp[] =	"close current connection",
2509	logouthelp[] =	"forcibly logout remote user and close the connection",
2510	quithelp[] =	"exit telnet",
2511	statushelp[] =	"print status information",
2512	helphelp[] =	"print help information",
2513	sendhelp[] =	"transmit special characters ('send ?' for more)",
2514	sethelp[] = 	"set operating parameters ('set ?' for more)",
2515	unsethelp[] = 	"unset operating parameters ('unset ?' for more)",
2516	togglestring[] ="toggle operating parameters ('toggle ?' for more)",
2517	slchelp[] =	"change state of special charaters ('slc ?' for more)",
2518	displayhelp[] =	"display operating parameters",
2519#ifdef	AUTHENTICATION
2520	authhelp[] =	"turn on (off) authentication ('auth ?' for more)",
2521#endif
2522#ifdef	ENCRYPTION
2523	encrypthelp[] =	"turn on (off) encryption ('encrypt ?' for more)",
2524#endif	/* ENCRYPTION */
2525	zhelp[] =	"suspend telnet",
2526#ifdef OPIE
2527	opiehelp[] =    "compute response to OPIE challenge",
2528#endif
2529	shellhelp[] =	"invoke a subshell",
2530	envhelp[] =	"change environment variables ('environ ?' for more)",
2531	modestring[] = "try to enter line or character mode ('mode ?' for more)";
2532
2533static Command cmdtab[] = {
2534	{ "close",	closehelp,	bye,		1 },
2535	{ "logout",	logouthelp,	(int (*)(int, char **))logout,		1 },
2536	{ "display",	displayhelp,	display,	0 },
2537	{ "mode",	modestring,	modecmd,	0 },
2538	{ "telnet",	openhelp,	tn,		0 },
2539	{ "open",	openhelp,	tn,		0 },
2540	{ "quit",	quithelp,	(int (*)(int, char **))quit,		0 },
2541	{ "send",	sendhelp,	sendcmd,	0 },
2542	{ "set",	sethelp,	setcmd,		0 },
2543	{ "unset",	unsethelp,	unsetcmd,	0 },
2544	{ "status",	statushelp,	status,		0 },
2545	{ "toggle",	togglestring,	toggle,		0 },
2546	{ "slc",	slchelp,	slccmd,		0 },
2547#ifdef	AUTHENTICATION
2548	{ "auth",	authhelp,	auth_cmd,	0 },
2549#endif
2550#ifdef	ENCRYPTION
2551	{ "encrypt",	encrypthelp,	encrypt_cmd,	0 },
2552#endif	/* ENCRYPTION */
2553	{ "z",		zhelp,		(int (*)(int, char **))suspend,	0 },
2554	{ "!",		shellhelp,	shell,		1 },
2555	{ "environ",	envhelp,	env_cmd,	0 },
2556	{ "?",		helphelp,	help,		0 },
2557#ifdef OPIE
2558	{ "opie",       opiehelp,       opie_calc,      0 },
2559#endif
2560	{ NULL, NULL, NULL, 0 }
2561};
2562
2563static char	crmodhelp[] =	"deprecated command -- use 'toggle crmod' instead";
2564static char	escapehelp[] =	"deprecated command -- use 'set escape' instead";
2565
2566static Command cmdtab2[] = {
2567	{ "help",	0,		help,		0 },
2568	{ "escape",	escapehelp,	setescape,	0 },
2569	{ "crmod",	crmodhelp,	(int (*)(int, char **))togcrmod,	0 },
2570	{ NULL, NULL, NULL, 0 }
2571};
2572
2573
2574/*
2575 * Call routine with argc, argv set from args (terminated by 0).
2576 */
2577
2578static int
2579call(intrtn_t routine, ...)
2580{
2581    va_list ap;
2582    char *args[100];
2583    int argno = 0;
2584
2585    va_start(ap, routine);
2586    while ((args[argno++] = va_arg(ap, char *)) != 0);
2587    va_end(ap);
2588    return (*routine)(argno-1, args);
2589}
2590
2591
2592static Command *
2593getcmd(char *name)
2594{
2595    Command *cm;
2596
2597    if ((cm = (Command *) genget(name, (char **) cmdtab, sizeof(Command))))
2598	return cm;
2599    return (Command *) genget(name, (char **) cmdtab2, sizeof(Command));
2600}
2601
2602void
2603command(int top, const char *tbuf, int cnt)
2604{
2605    Command *c;
2606
2607    setcommandmode();
2608    if (!top) {
2609	putchar('\n');
2610    } else {
2611	(void) signal(SIGINT, SIG_DFL);
2612	(void) signal(SIGQUIT, SIG_DFL);
2613    }
2614    for (;;) {
2615	if (rlogin == _POSIX_VDISABLE)
2616		printf("%s> ", prompt);
2617	if (tbuf) {
2618	    char *cp;
2619	    cp = line;
2620	    while (cnt > 0 && (*cp++ = *tbuf++) != '\n')
2621		cnt--;
2622	    tbuf = 0;
2623	    if (cp == line || *--cp != '\n' || cp == line)
2624		goto getline;
2625	    *cp = '\0';
2626	    if (rlogin == _POSIX_VDISABLE)
2627		printf("%s\n", line);
2628	} else {
2629	getline:
2630	    if (rlogin != _POSIX_VDISABLE)
2631		printf("%s> ", prompt);
2632	    if (fgets(line, sizeof(line), stdin) == NULL) {
2633		if (feof(stdin) || ferror(stdin)) {
2634		    (void) quit();
2635		    /*NOTREACHED*/
2636		}
2637		break;
2638	    }
2639	}
2640	if (line[0] == 0)
2641	    break;
2642	makeargv();
2643	if (margv[0] == 0) {
2644	    break;
2645	}
2646	c = getcmd(margv[0]);
2647	if (Ambiguous((void *)c)) {
2648	    printf("?Ambiguous command\n");
2649	    continue;
2650	}
2651	if (c == 0) {
2652	    printf("?Invalid command\n");
2653	    continue;
2654	}
2655	if (c->needconnect && !connected) {
2656	    printf("?Need to be connected first.\n");
2657	    continue;
2658	}
2659	if ((*c->handler)(margc, margv)) {
2660	    break;
2661	}
2662    }
2663    if (!top) {
2664	if (!connected) {
2665	    longjmp(toplevel, 1);
2666	    /*NOTREACHED*/
2667	}
2668	setconnmode(0);
2669    }
2670}
2671
2672/*
2673 * Help command.
2674 */
2675static int
2676help(int argc, char *argv[])
2677{
2678	Command *c;
2679
2680	if (argc == 1) {
2681		printf("Commands may be abbreviated.  Commands are:\n\n");
2682		for (c = cmdtab; c->name; c++)
2683			if (c->help) {
2684				printf("%-*s\t%s\n", (int)HELPINDENT, c->name,
2685								    c->help);
2686			}
2687		return 0;
2688	}
2689	else while (--argc > 0) {
2690		char *arg;
2691		arg = *++argv;
2692		c = getcmd(arg);
2693		if (Ambiguous((void *)c))
2694			printf("?Ambiguous help command %s\n", arg);
2695		else if (c == (Command *)0)
2696			printf("?Invalid help command %s\n", arg);
2697		else
2698			printf("%s\n", c->help);
2699	}
2700	return 0;
2701}
2702
2703static char *rcname = 0;
2704static char rcbuf[128];
2705
2706void
2707cmdrc(char *m1, char *m2)
2708{
2709    Command *c;
2710    FILE *rcfile;
2711    int gotmachine = 0;
2712    int l1 = strlen(m1);
2713    int l2 = strlen(m2);
2714    char m1save[MAXHOSTNAMELEN];
2715
2716    if (skiprc)
2717	return;
2718
2719    strlcpy(m1save, m1, sizeof(m1save));
2720    m1 = m1save;
2721
2722    if (rcname == 0) {
2723	rcname = getenv("HOME");
2724	if (rcname && (strlen(rcname) + 10) < sizeof(rcbuf))
2725	    strcpy(rcbuf, rcname);
2726	else
2727	    rcbuf[0] = '\0';
2728	strcat(rcbuf, "/.telnetrc");
2729	rcname = rcbuf;
2730    }
2731
2732    if ((rcfile = fopen(rcname, "r")) == 0) {
2733	return;
2734    }
2735
2736    for (;;) {
2737	if (fgets(line, sizeof(line), rcfile) == NULL)
2738	    break;
2739	if (line[0] == 0)
2740	    break;
2741	if (line[0] == '#')
2742	    continue;
2743	if (gotmachine) {
2744	    if (!isspace(line[0]))
2745		gotmachine = 0;
2746	}
2747	if (gotmachine == 0) {
2748	    if (isspace(line[0]))
2749		continue;
2750	    if (strncasecmp(line, m1, l1) == 0)
2751		strncpy(line, &line[l1], sizeof(line) - l1);
2752	    else if (strncasecmp(line, m2, l2) == 0)
2753		strncpy(line, &line[l2], sizeof(line) - l2);
2754	    else if (strncasecmp(line, "DEFAULT", 7) == 0)
2755		strncpy(line, &line[7], sizeof(line) - 7);
2756	    else
2757		continue;
2758	    if (line[0] != ' ' && line[0] != '\t' && line[0] != '\n')
2759		continue;
2760	    gotmachine = 1;
2761	}
2762	makeargv();
2763	if (margv[0] == 0)
2764	    continue;
2765	c = getcmd(margv[0]);
2766	if (Ambiguous((void *)c)) {
2767	    printf("?Ambiguous command: %s\n", margv[0]);
2768	    continue;
2769	}
2770	if (c == 0) {
2771	    printf("?Invalid command: %s\n", margv[0]);
2772	    continue;
2773	}
2774	/*
2775	 * This should never happen...
2776	 */
2777	if (c->needconnect && !connected) {
2778	    printf("?Need to be connected first for %s.\n", margv[0]);
2779	    continue;
2780	}
2781	(*c->handler)(margc, margv);
2782    }
2783    fclose(rcfile);
2784}
2785
2786/*
2787 * Source route is handed in as
2788 *	[!]@hop1@hop2...[@|:]dst
2789 * If the leading ! is present, it is a
2790 * strict source route, otherwise it is
2791 * assmed to be a loose source route.
2792 *
2793 * We fill in the source route option as
2794 *	hop1,hop2,hop3...dest
2795 * and return a pointer to hop1, which will
2796 * be the address to connect() to.
2797 *
2798 * Arguments:
2799 *
2800 *	res:	ponter to addrinfo structure which contains sockaddr to
2801 *		the host to connect to.
2802 *
2803 *	arg:	pointer to route list to decipher
2804 *
2805 *	cpp: 	If *cpp is not equal to NULL, this is a
2806 *		pointer to a pointer to a character array
2807 *		that should be filled in with the option.
2808 *
2809 *	lenp:	pointer to an integer that contains the
2810 *		length of *cpp if *cpp != NULL.
2811 *
2812 *	protop:	pointer to an integer that should be filled in with
2813 *		appropriate protocol for setsockopt, as socket
2814 *		protocol family.
2815 *
2816 *	optp:	pointer to an integer that should be filled in with
2817 *		appropriate option for setsockopt, as socket protocol
2818 *		family.
2819 *
2820 * Return values:
2821 *
2822 *	If the return value is 1, then all operations are
2823 *	successful. If the
2824 *	return value is -1, there was a syntax error in the
2825 *	option, either unknown characters, or too many hosts.
2826 *	If the return value is 0, one of the hostnames in the
2827 *	path is unknown, and *cpp is set to point to the bad
2828 *	hostname.
2829 *
2830 *	*cpp:	If *cpp was equal to NULL, it will be filled
2831 *		in with a pointer to our static area that has
2832 *		the option filled in.  This will be 32bit aligned.
2833 *
2834 *	*lenp:	This will be filled in with how long the option
2835 *		pointed to by *cpp is.
2836 *
2837 *	*protop: This will be filled in with appropriate protocol for
2838 *		 setsockopt, as socket protocol family.
2839 *
2840 *	*optp:	This will be filled in with appropriate option for
2841 *		setsockopt, as socket protocol family.
2842 */
2843static int
2844sourceroute(struct addrinfo *ai, char *arg, unsigned char **cpp, int *lenp, int *protop, int *optp)
2845{
2846	static char buf[1024 + ALIGNBYTES];	/*XXX*/
2847	unsigned char *cp, *cp2, *lsrp, *ep;
2848	struct sockaddr_in *_sin;
2849#ifdef INET6
2850	struct sockaddr_in6 *sin6;
2851	struct ip6_rthdr *rth;
2852#endif
2853	struct addrinfo hints, *res;
2854	int error;
2855	char c;
2856
2857	/*
2858	 * Verify the arguments, and make sure we have
2859	 * at least 7 bytes for the option.
2860	 */
2861	if (cpp == NULL || lenp == NULL)
2862		return -1;
2863	if (*cpp != NULL) {
2864		switch (res->ai_family) {
2865		case AF_INET:
2866			if (*lenp < 7)
2867				return -1;
2868			break;
2869#ifdef INET6
2870		case AF_INET6:
2871			if (*lenp < (int)CMSG_SPACE(sizeof(struct ip6_rthdr) +
2872				               sizeof(struct in6_addr)))
2873				return -1;
2874			break;
2875#endif
2876		}
2877	}
2878	/*
2879	 * Decide whether we have a buffer passed to us,
2880	 * or if we need to use our own static buffer.
2881	 */
2882	if (*cpp) {
2883		lsrp = *cpp;
2884		ep = lsrp + *lenp;
2885	} else {
2886		*cpp = lsrp = (char *)ALIGN(buf);
2887		ep = lsrp + 1024;
2888	}
2889
2890	cp = arg;
2891
2892#ifdef INET6
2893	if (ai->ai_family == AF_INET6) {
2894		if ((rth = inet6_rth_init((void *)*cpp, sizeof(buf),
2895					  IPV6_RTHDR_TYPE_0, 0)) == NULL)
2896			return -1;
2897		if (*cp != '@')
2898			return -1;
2899		*protop = IPPROTO_IPV6;
2900		*optp = IPV6_RTHDR;
2901	} else
2902#endif
2903      {
2904	/*
2905	 * Next, decide whether we have a loose source
2906	 * route or a strict source route, and fill in
2907	 * the begining of the option.
2908	 */
2909	if (*cp == '!') {
2910		cp++;
2911		*lsrp++ = IPOPT_SSRR;
2912	} else
2913		*lsrp++ = IPOPT_LSRR;
2914
2915	if (*cp != '@')
2916		return -1;
2917
2918	lsrp++;		/* skip over length, we'll fill it in later */
2919	*lsrp++ = 4;
2920	*protop = IPPROTO_IP;
2921	*optp = IP_OPTIONS;
2922      }
2923
2924	cp++;
2925	memset(&hints, 0, sizeof(hints));
2926	hints.ai_family = ai->ai_family;
2927	hints.ai_socktype = SOCK_STREAM;
2928	for (c = 0;;) {
2929		if (
2930#ifdef INET6
2931		    ai->ai_family != AF_INET6 &&
2932#endif
2933		    c == ':')
2934			cp2 = 0;
2935		else for (cp2 = cp; (c = *cp2); cp2++) {
2936			if (c == ',') {
2937				*cp2++ = '\0';
2938				if (*cp2 == '@')
2939					cp2++;
2940			} else if (c == '@') {
2941				*cp2++ = '\0';
2942			} else if (
2943#ifdef INET6
2944				   ai->ai_family != AF_INET6 &&
2945#endif
2946				   c == ':') {
2947				*cp2++ = '\0';
2948			} else
2949				continue;
2950			break;
2951		}
2952		if (!c)
2953			cp2 = 0;
2954
2955		hints.ai_flags = AI_NUMERICHOST;
2956		error = getaddrinfo(cp, NULL, &hints, &res);
2957		if (error == EAI_NONAME) {
2958			hints.ai_flags = 0;
2959			error = getaddrinfo(cp, NULL, &hints, &res);
2960		}
2961		if (error != 0) {
2962			fprintf(stderr, "%s: %s\n", cp, gai_strerror(error));
2963			if (error == EAI_SYSTEM)
2964				fprintf(stderr, "%s: %s\n", cp,
2965					strerror(errno));
2966			*cpp = cp;
2967			return(0);
2968		}
2969#ifdef INET6
2970		if (res->ai_family == AF_INET6) {
2971			sin6 = (struct sockaddr_in6 *)res->ai_addr;
2972			if (inet6_rth_add((void *)rth, &sin6->sin6_addr) == -1)
2973				return(0);
2974		} else
2975#endif
2976	      {
2977		_sin = (struct sockaddr_in *)res->ai_addr;
2978		memcpy(lsrp, (char *)&_sin->sin_addr, 4);
2979		lsrp += 4;
2980	      }
2981		if (cp2)
2982			cp = cp2;
2983		else
2984			break;
2985		/*
2986		 * Check to make sure there is space for next address
2987		 */
2988		if (lsrp + 4 > ep)
2989			return -1;
2990		freeaddrinfo(res);
2991	}
2992#ifdef INET6
2993	if (res->ai_family == AF_INET6) {
2994		rth->ip6r_len = rth->ip6r_segleft * 2;
2995		*lenp = (rth->ip6r_len + 1) << 3;
2996	} else
2997#endif
2998      {
2999	if ((*(*cpp+IPOPT_OLEN) = lsrp - *cpp) <= 7) {
3000		*cpp = 0;
3001		*lenp = 0;
3002		return -1;
3003	}
3004	*lsrp++ = IPOPT_NOP; /* 32 bit word align it */
3005	*lenp = lsrp - *cpp;
3006      }
3007	freeaddrinfo(res);
3008	return 1;
3009}
3010