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