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