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