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