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