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