10Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 20Sstevel@tonic-gate 30Sstevel@tonic-gate /* 40Sstevel@tonic-gate * Copyright (c) 1988, 1990, 1993 50Sstevel@tonic-gate * The Regents of the University of California. All rights reserved. 60Sstevel@tonic-gate * 70Sstevel@tonic-gate * Redistribution and use in source and binary forms, with or without 80Sstevel@tonic-gate * modification, are permitted provided that the following conditions 90Sstevel@tonic-gate * are met: 100Sstevel@tonic-gate * 1. Redistributions of source code must retain the above copyright 110Sstevel@tonic-gate * notice, this list of conditions and the following disclaimer. 120Sstevel@tonic-gate * 2. Redistributions in binary form must reproduce the above copyright 130Sstevel@tonic-gate * notice, this list of conditions and the following disclaimer in the 140Sstevel@tonic-gate * documentation and/or other materials provided with the distribution. 150Sstevel@tonic-gate * 3. All advertising materials mentioning features or use of this software 160Sstevel@tonic-gate * must display the following acknowledgement: 170Sstevel@tonic-gate * This product includes software developed by the University of 180Sstevel@tonic-gate * California, Berkeley and its contributors. 190Sstevel@tonic-gate * 4. Neither the name of the University nor the names of its contributors 200Sstevel@tonic-gate * may be used to endorse or promote products derived from this software 210Sstevel@tonic-gate * without specific prior written permission. 220Sstevel@tonic-gate * 230Sstevel@tonic-gate * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 240Sstevel@tonic-gate * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 250Sstevel@tonic-gate * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 260Sstevel@tonic-gate * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 270Sstevel@tonic-gate * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 280Sstevel@tonic-gate * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 290Sstevel@tonic-gate * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 300Sstevel@tonic-gate * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 310Sstevel@tonic-gate * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 320Sstevel@tonic-gate * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 330Sstevel@tonic-gate * SUCH DAMAGE. 340Sstevel@tonic-gate * 35*473Sbw * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 360Sstevel@tonic-gate * Use is subject to license terms. 370Sstevel@tonic-gate */ 380Sstevel@tonic-gate 390Sstevel@tonic-gate #ifndef lint 400Sstevel@tonic-gate static char sccsid[] = "@(#)commands.c 8.1 (Berkeley) 6/6/93"; 410Sstevel@tonic-gate #endif /* not lint */ 420Sstevel@tonic-gate 430Sstevel@tonic-gate #include <sys/param.h> 440Sstevel@tonic-gate #include <sys/file.h> 450Sstevel@tonic-gate #include <sys/socket.h> 460Sstevel@tonic-gate #include <sys/sysmacros.h> 470Sstevel@tonic-gate #include <netinet/in.h> 480Sstevel@tonic-gate 490Sstevel@tonic-gate #include <signal.h> 500Sstevel@tonic-gate #include <netdb.h> 510Sstevel@tonic-gate #include <ctype.h> 520Sstevel@tonic-gate #include <pwd.h> 530Sstevel@tonic-gate #include <errno.h> 540Sstevel@tonic-gate #include <strings.h> 550Sstevel@tonic-gate 560Sstevel@tonic-gate #include <arpa/telnet.h> 570Sstevel@tonic-gate #include <arpa/inet.h> 580Sstevel@tonic-gate 590Sstevel@tonic-gate #include "general.h" 600Sstevel@tonic-gate 610Sstevel@tonic-gate #include "ring.h" 620Sstevel@tonic-gate 630Sstevel@tonic-gate #include "externs.h" 640Sstevel@tonic-gate #include "defines.h" 650Sstevel@tonic-gate #include "types.h" 660Sstevel@tonic-gate 670Sstevel@tonic-gate extern char *telnet_krb5_realm; 680Sstevel@tonic-gate extern void krb5_profile_get_options(char *, char *, 690Sstevel@tonic-gate profile_options_boolean*); 700Sstevel@tonic-gate 710Sstevel@tonic-gate #include <k5-int.h> 720Sstevel@tonic-gate #include <profile/prof_int.h> 730Sstevel@tonic-gate 740Sstevel@tonic-gate profile_options_boolean config_file_options[] = { 750Sstevel@tonic-gate { "forwardable", &forwardable_flag, 0}, 760Sstevel@tonic-gate { "forward", &forward_flag, 0}, 770Sstevel@tonic-gate { "encrypt", &encrypt_flag, 0 }, 780Sstevel@tonic-gate { "autologin", &autologin, 0 }, 790Sstevel@tonic-gate { NULL, NULL, 0} 800Sstevel@tonic-gate }; 810Sstevel@tonic-gate 820Sstevel@tonic-gate #include <netinet/ip.h> 830Sstevel@tonic-gate 840Sstevel@tonic-gate /* 850Sstevel@tonic-gate * Number of maximum IPv4 gateways user can specify. This number is limited by 860Sstevel@tonic-gate * the maximum size of the IPv4 options in the IPv4 header. 870Sstevel@tonic-gate */ 880Sstevel@tonic-gate #define MAX_GATEWAY 8 890Sstevel@tonic-gate /* 900Sstevel@tonic-gate * Number of maximum IPv6 gateways user can specify. This number is limited by 910Sstevel@tonic-gate * the maximum header extension length of the IPv6 routing header. 920Sstevel@tonic-gate */ 930Sstevel@tonic-gate #define MAX_GATEWAY6 127 940Sstevel@tonic-gate #define MAXMAX_GATEWAY MAX(MAX_GATEWAY, MAX_GATEWAY6) 950Sstevel@tonic-gate 960Sstevel@tonic-gate /* 970Sstevel@tonic-gate * Depending on the address resolutions of the target and gateways, 980Sstevel@tonic-gate * we determine which addresses of the target we'll try connecting to. 990Sstevel@tonic-gate */ 1000Sstevel@tonic-gate #define ALL_ADDRS 0 /* try all addrs of target */ 1010Sstevel@tonic-gate #define ONLY_V4 1 /* try only IPv4 addrs of target */ 1020Sstevel@tonic-gate #define ONLY_V6 2 /* try only IPv6 addrs of target */ 1030Sstevel@tonic-gate 1040Sstevel@tonic-gate #if defined(USE_TOS) 1050Sstevel@tonic-gate int tos = -1; 1060Sstevel@tonic-gate #endif 1070Sstevel@tonic-gate 1080Sstevel@tonic-gate char *hostname; 1090Sstevel@tonic-gate static char _hostname[MAXHOSTNAMELEN]; 1100Sstevel@tonic-gate 1110Sstevel@tonic-gate static int send_tncmd(void (*func)(), char *, char *); 1120Sstevel@tonic-gate static void call(int n_ptrs, ...); 1130Sstevel@tonic-gate static int cmdrc(char *, char *); 1140Sstevel@tonic-gate 1150Sstevel@tonic-gate typedef struct { 1160Sstevel@tonic-gate char *name; /* command name */ 1170Sstevel@tonic-gate char *help; /* help string (NULL for no help) */ 1180Sstevel@tonic-gate int (*handler)(); /* routine which executes command */ 1190Sstevel@tonic-gate int needconnect; /* Do we need to be connected to execute? */ 1200Sstevel@tonic-gate } Command; 1210Sstevel@tonic-gate 1220Sstevel@tonic-gate /* 1230Sstevel@tonic-gate * storage for IPv6 and/or IPv4 addresses of gateways 1240Sstevel@tonic-gate */ 1250Sstevel@tonic-gate struct gateway { 1260Sstevel@tonic-gate struct in6_addr gw_addr6; 1270Sstevel@tonic-gate struct in_addr gw_addr; 1280Sstevel@tonic-gate }; 1290Sstevel@tonic-gate 1300Sstevel@tonic-gate /* 1310Sstevel@tonic-gate * IPv4 source routing option. 1320Sstevel@tonic-gate * In order to avoid padding for the alignment of IPv4 addresses, ipsr_addrs 1330Sstevel@tonic-gate * is defined as a 2-D array of uint8_t, instead of 1-D array of struct in_addr. 1340Sstevel@tonic-gate * If it were defined as "struct in_addr ipsr_addrs[1]", "ipsr_ptr" would be 1350Sstevel@tonic-gate * followed by one byte of padding to avoid misaligned struct in_addr. 1360Sstevel@tonic-gate */ 1370Sstevel@tonic-gate struct ip_sourceroute { 1380Sstevel@tonic-gate uint8_t ipsr_code; 1390Sstevel@tonic-gate uint8_t ipsr_len; 1400Sstevel@tonic-gate uint8_t ipsr_ptr; 1410Sstevel@tonic-gate /* up to 9 IPv4 addresses */ 1420Sstevel@tonic-gate uint8_t ipsr_addrs[1][sizeof (struct in_addr)]; 1430Sstevel@tonic-gate }; 1440Sstevel@tonic-gate 1450Sstevel@tonic-gate static char *line = NULL; 1460Sstevel@tonic-gate static unsigned linesize = 0; 1470Sstevel@tonic-gate static int margc; 1480Sstevel@tonic-gate static char **margv = NULL; 1490Sstevel@tonic-gate static unsigned margvlen = 0; 1500Sstevel@tonic-gate static int doing_rc = 0; /* .telnetrc file is being read and processed */ 1510Sstevel@tonic-gate 1520Sstevel@tonic-gate static void 1530Sstevel@tonic-gate Close(int *fd) 1540Sstevel@tonic-gate { 1550Sstevel@tonic-gate if (*fd != -1) { 1560Sstevel@tonic-gate (void) close(*fd); 1570Sstevel@tonic-gate *fd = -1; 1580Sstevel@tonic-gate } 1590Sstevel@tonic-gate } 1600Sstevel@tonic-gate 1610Sstevel@tonic-gate static void 1620Sstevel@tonic-gate Free(char **p) 1630Sstevel@tonic-gate { 1640Sstevel@tonic-gate if (*p != NULL) { 1650Sstevel@tonic-gate free(*p); 1660Sstevel@tonic-gate *p = NULL; 1670Sstevel@tonic-gate } 1680Sstevel@tonic-gate } 1690Sstevel@tonic-gate 1700Sstevel@tonic-gate static void 1710Sstevel@tonic-gate FreeHostnameList(char *list[]) 1720Sstevel@tonic-gate { 1730Sstevel@tonic-gate unsigned i; 1740Sstevel@tonic-gate for (i = 0; i <= MAXMAX_GATEWAY && list[i] != NULL; i++) 1750Sstevel@tonic-gate Free(&list[i]); 1760Sstevel@tonic-gate } 1770Sstevel@tonic-gate 1780Sstevel@tonic-gate #define MARGV_CHUNK_SIZE 8 1790Sstevel@tonic-gate 1800Sstevel@tonic-gate static void 1810Sstevel@tonic-gate set_argv(str) 1820Sstevel@tonic-gate char *str; 1830Sstevel@tonic-gate { 1840Sstevel@tonic-gate if (margc == margvlen) { 1850Sstevel@tonic-gate char **newmargv; 1860Sstevel@tonic-gate 1870Sstevel@tonic-gate margvlen += MARGV_CHUNK_SIZE; 1880Sstevel@tonic-gate 1890Sstevel@tonic-gate if ((newmargv = realloc(margv, margvlen * sizeof (char *))) 1900Sstevel@tonic-gate == NULL) 1910Sstevel@tonic-gate ExitString("telnet: no space for arguments", 1920Sstevel@tonic-gate EXIT_FAILURE); 1930Sstevel@tonic-gate 1940Sstevel@tonic-gate margv = newmargv; 1950Sstevel@tonic-gate } 1960Sstevel@tonic-gate 1970Sstevel@tonic-gate margv[margc] = str; 1980Sstevel@tonic-gate if (str != NULL) 1990Sstevel@tonic-gate margc++; 2000Sstevel@tonic-gate } 2010Sstevel@tonic-gate 2020Sstevel@tonic-gate static void 2030Sstevel@tonic-gate makeargv() 2040Sstevel@tonic-gate { 2050Sstevel@tonic-gate char *cp, *cp2, c; 2060Sstevel@tonic-gate boolean_t shellcmd = B_FALSE; 2070Sstevel@tonic-gate 2080Sstevel@tonic-gate margc = 0; 2090Sstevel@tonic-gate cp = line; 2100Sstevel@tonic-gate if (*cp == '!') { /* Special case shell escape */ 2110Sstevel@tonic-gate set_argv("!"); /* No room in string to get this */ 2120Sstevel@tonic-gate cp++; 2130Sstevel@tonic-gate shellcmd = B_TRUE; 2140Sstevel@tonic-gate } 2150Sstevel@tonic-gate while ((c = *cp) != '\0') { 2160Sstevel@tonic-gate register int inquote = 0; 2170Sstevel@tonic-gate while (isspace(c)) 2180Sstevel@tonic-gate c = *++cp; 2190Sstevel@tonic-gate if (c == '\0') 2200Sstevel@tonic-gate break; 2210Sstevel@tonic-gate set_argv(cp); 2220Sstevel@tonic-gate /* 2230Sstevel@tonic-gate * For the shell escape, put the rest of the line, less 2240Sstevel@tonic-gate * leading space, into a single argument, breaking out from 2250Sstevel@tonic-gate * the loop to prevent the rest of the line being split up 2260Sstevel@tonic-gate * into smaller arguments. 2270Sstevel@tonic-gate */ 2280Sstevel@tonic-gate if (shellcmd) 2290Sstevel@tonic-gate break; 2300Sstevel@tonic-gate for (cp2 = cp; c != '\0'; c = *++cp) { 2310Sstevel@tonic-gate if (inquote) { 2320Sstevel@tonic-gate if (c == inquote) { 2330Sstevel@tonic-gate inquote = 0; 2340Sstevel@tonic-gate continue; 2350Sstevel@tonic-gate } 2360Sstevel@tonic-gate } else { 2370Sstevel@tonic-gate if (c == '\\') { 2380Sstevel@tonic-gate if ((c = *++cp) == '\0') 2390Sstevel@tonic-gate break; 2400Sstevel@tonic-gate } else if (c == '"') { 2410Sstevel@tonic-gate inquote = '"'; 2420Sstevel@tonic-gate continue; 2430Sstevel@tonic-gate } else if (c == '\'') { 2440Sstevel@tonic-gate inquote = '\''; 2450Sstevel@tonic-gate continue; 2460Sstevel@tonic-gate } else if (isspace(c)) 2470Sstevel@tonic-gate break; 2480Sstevel@tonic-gate } 2490Sstevel@tonic-gate *cp2++ = c; 2500Sstevel@tonic-gate } 2510Sstevel@tonic-gate *cp2 = '\0'; 2520Sstevel@tonic-gate if (c == '\0') 2530Sstevel@tonic-gate break; 2540Sstevel@tonic-gate cp++; 2550Sstevel@tonic-gate } 2560Sstevel@tonic-gate set_argv((char *)NULL); 2570Sstevel@tonic-gate } 2580Sstevel@tonic-gate 2590Sstevel@tonic-gate /* 2600Sstevel@tonic-gate * Make a character string into a number. 2610Sstevel@tonic-gate * 2620Sstevel@tonic-gate * Todo: 1. Could take random integers (12, 0x12, 012, 0b1). 2630Sstevel@tonic-gate */ 2640Sstevel@tonic-gate 265*473Sbw static int 2660Sstevel@tonic-gate special(s) 2670Sstevel@tonic-gate register char *s; 2680Sstevel@tonic-gate { 2690Sstevel@tonic-gate register char c; 2700Sstevel@tonic-gate char b; 2710Sstevel@tonic-gate 2720Sstevel@tonic-gate switch (*s) { 2730Sstevel@tonic-gate case '^': 2740Sstevel@tonic-gate b = *++s; 2750Sstevel@tonic-gate if (b == '?') { 2760Sstevel@tonic-gate c = b | 0x40; /* DEL */ 2770Sstevel@tonic-gate } else { 2780Sstevel@tonic-gate c = b & 0x1f; 2790Sstevel@tonic-gate } 2800Sstevel@tonic-gate break; 2810Sstevel@tonic-gate default: 2820Sstevel@tonic-gate c = *s; 2830Sstevel@tonic-gate break; 2840Sstevel@tonic-gate } 2850Sstevel@tonic-gate return (c); 2860Sstevel@tonic-gate } 2870Sstevel@tonic-gate 2880Sstevel@tonic-gate /* 2890Sstevel@tonic-gate * Construct a control character sequence 2900Sstevel@tonic-gate * for a special character. 2910Sstevel@tonic-gate */ 2920Sstevel@tonic-gate static char * 2930Sstevel@tonic-gate control(c) 2940Sstevel@tonic-gate register cc_t c; 2950Sstevel@tonic-gate { 2960Sstevel@tonic-gate static char buf[5]; 2970Sstevel@tonic-gate /* 2980Sstevel@tonic-gate * The only way I could get the Sun 3.5 compiler 2990Sstevel@tonic-gate * to shut up about 3000Sstevel@tonic-gate * if ((unsigned int)c >= 0x80) 3010Sstevel@tonic-gate * was to assign "c" to an unsigned int variable... 3020Sstevel@tonic-gate * Arggg.... 3030Sstevel@tonic-gate */ 3040Sstevel@tonic-gate register unsigned int uic = (unsigned int)c; 3050Sstevel@tonic-gate 3060Sstevel@tonic-gate if (uic == 0x7f) 3070Sstevel@tonic-gate return ("^?"); 3080Sstevel@tonic-gate if (c == (cc_t)_POSIX_VDISABLE) { 3090Sstevel@tonic-gate return ("off"); 3100Sstevel@tonic-gate } 3110Sstevel@tonic-gate if (uic >= 0x80) { 3120Sstevel@tonic-gate buf[0] = '\\'; 3130Sstevel@tonic-gate buf[1] = ((c>>6)&07) + '0'; 3140Sstevel@tonic-gate buf[2] = ((c>>3)&07) + '0'; 3150Sstevel@tonic-gate buf[3] = (c&07) + '0'; 3160Sstevel@tonic-gate buf[4] = 0; 3170Sstevel@tonic-gate } else if (uic >= 0x20) { 3180Sstevel@tonic-gate buf[0] = c; 3190Sstevel@tonic-gate buf[1] = 0; 3200Sstevel@tonic-gate } else { 3210Sstevel@tonic-gate buf[0] = '^'; 3220Sstevel@tonic-gate buf[1] = '@'+c; 3230Sstevel@tonic-gate buf[2] = 0; 3240Sstevel@tonic-gate } 3250Sstevel@tonic-gate return (buf); 3260Sstevel@tonic-gate } 3270Sstevel@tonic-gate 3280Sstevel@tonic-gate /* 3290Sstevel@tonic-gate * Same as control() except that its only used for escape handling, which uses 3300Sstevel@tonic-gate * _POSIX_VDISABLE differently and is aided by the use of the state variable 3310Sstevel@tonic-gate * escape_valid. 3320Sstevel@tonic-gate */ 3330Sstevel@tonic-gate static char * 3340Sstevel@tonic-gate esc_control(c) 3350Sstevel@tonic-gate register cc_t c; 3360Sstevel@tonic-gate { 3370Sstevel@tonic-gate static char buf[5]; 3380Sstevel@tonic-gate /* 3390Sstevel@tonic-gate * The only way I could get the Sun 3.5 compiler 3400Sstevel@tonic-gate * to shut up about 3410Sstevel@tonic-gate * if ((unsigned int)c >= 0x80) 3420Sstevel@tonic-gate * was to assign "c" to an unsigned int variable... 3430Sstevel@tonic-gate * Arggg.... 3440Sstevel@tonic-gate */ 3450Sstevel@tonic-gate register unsigned int uic = (unsigned int)c; 3460Sstevel@tonic-gate 3470Sstevel@tonic-gate if (escape_valid == B_FALSE) 3480Sstevel@tonic-gate return ("off"); 3490Sstevel@tonic-gate if (uic == 0x7f) 3500Sstevel@tonic-gate return ("^?"); 3510Sstevel@tonic-gate if (uic >= 0x80) { 3520Sstevel@tonic-gate buf[0] = '\\'; 3530Sstevel@tonic-gate buf[1] = ((c>>6)&07) + '0'; 3540Sstevel@tonic-gate buf[2] = ((c>>3)&07) + '0'; 3550Sstevel@tonic-gate buf[3] = (c&07) + '0'; 3560Sstevel@tonic-gate buf[4] = 0; 3570Sstevel@tonic-gate } else if (uic >= 0x20) { 3580Sstevel@tonic-gate buf[0] = c; 3590Sstevel@tonic-gate buf[1] = 0; 3600Sstevel@tonic-gate } else { 3610Sstevel@tonic-gate buf[0] = '^'; 3620Sstevel@tonic-gate buf[1] = '@'+c; 3630Sstevel@tonic-gate buf[2] = 0; 3640Sstevel@tonic-gate } 3650Sstevel@tonic-gate return (buf); 3660Sstevel@tonic-gate } 3670Sstevel@tonic-gate 3680Sstevel@tonic-gate /* 3690Sstevel@tonic-gate * The following are data structures and routines for 3700Sstevel@tonic-gate * the "send" command. 3710Sstevel@tonic-gate * 3720Sstevel@tonic-gate */ 3730Sstevel@tonic-gate 3740Sstevel@tonic-gate struct sendlist { 3750Sstevel@tonic-gate char *name; /* How user refers to it (case independent) */ 3760Sstevel@tonic-gate char *help; /* Help information (0 ==> no help) */ 3770Sstevel@tonic-gate int needconnect; /* Need to be connected */ 3780Sstevel@tonic-gate int narg; /* Number of arguments */ 3790Sstevel@tonic-gate int (*handler)(); /* Routine to perform (for special ops) */ 3800Sstevel@tonic-gate int nbyte; /* Number of bytes to send this command */ 3810Sstevel@tonic-gate int what; /* Character to be sent (<0 ==> special) */ 3820Sstevel@tonic-gate }; 3830Sstevel@tonic-gate 3840Sstevel@tonic-gate 3850Sstevel@tonic-gate static int send_esc(void); 3860Sstevel@tonic-gate static int send_help(void); 3870Sstevel@tonic-gate static int send_docmd(char *); 3880Sstevel@tonic-gate static int send_dontcmd(char *); 3890Sstevel@tonic-gate static int send_willcmd(char *); 3900Sstevel@tonic-gate static int send_wontcmd(char *); 3910Sstevel@tonic-gate 3920Sstevel@tonic-gate static struct sendlist Sendlist[] = { 3930Sstevel@tonic-gate { "ao", "Send Telnet Abort output", 1, 0, 0, 2, AO }, 3940Sstevel@tonic-gate { "ayt", "Send Telnet 'Are You There'", 1, 0, 0, 2, AYT }, 3950Sstevel@tonic-gate { "b", 0, 1, 0, 0, 2, BREAK }, 3960Sstevel@tonic-gate { "br", 0, 1, 0, 0, 2, BREAK }, 3970Sstevel@tonic-gate { "break", 0, 1, 0, 0, 2, BREAK }, 3980Sstevel@tonic-gate { "brk", "Send Telnet Break", 1, 0, 0, 2, BREAK }, 3990Sstevel@tonic-gate { "ec", "Send Telnet Erase Character", 1, 0, 0, 2, EC }, 4000Sstevel@tonic-gate { "el", "Send Telnet Erase Line", 1, 0, 0, 2, EL }, 4010Sstevel@tonic-gate { "escape", "Send current escape character", 1, 0, send_esc, 1, 0 }, 4020Sstevel@tonic-gate { "ga", "Send Telnet 'Go Ahead' sequence", 1, 0, 0, 2, GA }, 4030Sstevel@tonic-gate { "ip", "Send Telnet Interrupt Process", 1, 0, 0, 2, IP }, 4040Sstevel@tonic-gate { "intp", 0, 1, 0, 0, 2, IP }, 4050Sstevel@tonic-gate { "interrupt", 0, 1, 0, 0, 2, IP }, 4060Sstevel@tonic-gate { "intr", 0, 1, 0, 0, 2, IP }, 4070Sstevel@tonic-gate { "nop", "Send Telnet 'No operation'", 1, 0, 0, 2, NOP }, 4080Sstevel@tonic-gate { "eor", "Send Telnet 'End of Record'", 1, 0, 0, 2, EOR }, 4090Sstevel@tonic-gate { "abort", "Send Telnet 'Abort Process'", 1, 0, 0, 2, ABORT }, 4100Sstevel@tonic-gate { "susp", "Send Telnet 'Suspend Process'", 1, 0, 0, 2, SUSP }, 4110Sstevel@tonic-gate { "eof", "Send Telnet End of File Character", 1, 0, 0, 2, xEOF }, 4120Sstevel@tonic-gate { "synch", "Perform Telnet 'Synch operation'", 1, 0, dosynch, 2, 0 }, 4130Sstevel@tonic-gate { "getstatus", "Send request for STATUS", 1, 0, get_status, 6, 0 }, 4140Sstevel@tonic-gate { "?", "Display send options", 0, 0, send_help, 0, 0 }, 4150Sstevel@tonic-gate { "help", 0, 0, 0, send_help, 0, 0 }, 4160Sstevel@tonic-gate { "do", 0, 0, 1, send_docmd, 3, 0 }, 4170Sstevel@tonic-gate { "dont", 0, 0, 1, send_dontcmd, 3, 0 }, 4180Sstevel@tonic-gate { "will", 0, 0, 1, send_willcmd, 3, 0 }, 4190Sstevel@tonic-gate { "wont", 0, 0, 1, send_wontcmd, 3, 0 }, 4200Sstevel@tonic-gate { 0 } 4210Sstevel@tonic-gate }; 4220Sstevel@tonic-gate 4230Sstevel@tonic-gate #define GETSEND(name) ((struct sendlist *)genget(name, (char **)Sendlist, \ 4240Sstevel@tonic-gate sizeof (struct sendlist))) 4250Sstevel@tonic-gate 4260Sstevel@tonic-gate static int 4270Sstevel@tonic-gate sendcmd(argc, argv) 4280Sstevel@tonic-gate int argc; 4290Sstevel@tonic-gate char **argv; 4300Sstevel@tonic-gate { 4310Sstevel@tonic-gate int count; /* how many bytes we are going to need to send */ 4320Sstevel@tonic-gate int i; 4330Sstevel@tonic-gate struct sendlist *s; /* pointer to current command */ 4340Sstevel@tonic-gate int success = 0; 4350Sstevel@tonic-gate int needconnect = 0; 4360Sstevel@tonic-gate 4370Sstevel@tonic-gate if (argc < 2) { 4380Sstevel@tonic-gate (void) printf( 4390Sstevel@tonic-gate "need at least one argument for 'send' command\n"); 4400Sstevel@tonic-gate (void) printf("'send ?' for help\n"); 4410Sstevel@tonic-gate return (0); 4420Sstevel@tonic-gate } 4430Sstevel@tonic-gate /* 4440Sstevel@tonic-gate * First, validate all the send arguments. 4450Sstevel@tonic-gate * In addition, we see how much space we are going to need, and 4460Sstevel@tonic-gate * whether or not we will be doing a "SYNCH" operation (which 4470Sstevel@tonic-gate * flushes the network queue). 4480Sstevel@tonic-gate */ 4490Sstevel@tonic-gate count = 0; 4500Sstevel@tonic-gate for (i = 1; i < argc; i++) { 4510Sstevel@tonic-gate s = GETSEND(argv[i]); 4520Sstevel@tonic-gate if (s == 0) { 4530Sstevel@tonic-gate (void) printf("Unknown send argument '%s'\n'send ?' " 4540Sstevel@tonic-gate "for help.\n", argv[i]); 4550Sstevel@tonic-gate return (0); 4560Sstevel@tonic-gate } else if (Ambiguous(s)) { 4570Sstevel@tonic-gate (void) printf("Ambiguous send argument '%s'\n'send ?' " 4580Sstevel@tonic-gate "for help.\n", argv[i]); 4590Sstevel@tonic-gate return (0); 4600Sstevel@tonic-gate } 4610Sstevel@tonic-gate if (i + s->narg >= argc) { 4620Sstevel@tonic-gate (void) fprintf(stderr, 4630Sstevel@tonic-gate "Need %d argument%s to 'send %s' " 4640Sstevel@tonic-gate "command. 'send %s ?' for help.\n", 4650Sstevel@tonic-gate s->narg, s->narg == 1 ? "" : "s", s->name, s->name); 4660Sstevel@tonic-gate return (0); 4670Sstevel@tonic-gate } 4680Sstevel@tonic-gate count += s->nbyte; 4690Sstevel@tonic-gate if (s->handler == send_help) { 4700Sstevel@tonic-gate (void) send_help(); 4710Sstevel@tonic-gate return (0); 4720Sstevel@tonic-gate } 4730Sstevel@tonic-gate 4740Sstevel@tonic-gate i += s->narg; 4750Sstevel@tonic-gate needconnect += s->needconnect; 4760Sstevel@tonic-gate } 4770Sstevel@tonic-gate if (!connected && needconnect) { 4780Sstevel@tonic-gate (void) printf("?Need to be connected first.\n"); 4790Sstevel@tonic-gate (void) printf("'send ?' for help\n"); 4800Sstevel@tonic-gate return (0); 4810Sstevel@tonic-gate } 4820Sstevel@tonic-gate /* Now, do we have enough room? */ 4830Sstevel@tonic-gate if (NETROOM() < count) { 4840Sstevel@tonic-gate (void) printf("There is not enough room in the buffer " 4850Sstevel@tonic-gate "TO the network\n"); 4860Sstevel@tonic-gate (void) printf( 4870Sstevel@tonic-gate "to process your request. Nothing will be done.\n"); 4880Sstevel@tonic-gate (void) printf("('send synch' will throw away most " 4890Sstevel@tonic-gate "data in the network\n"); 4900Sstevel@tonic-gate (void) printf("buffer, if this might help.)\n"); 4910Sstevel@tonic-gate return (0); 4920Sstevel@tonic-gate } 4930Sstevel@tonic-gate /* OK, they are all OK, now go through again and actually send */ 4940Sstevel@tonic-gate count = 0; 4950Sstevel@tonic-gate for (i = 1; i < argc; i++) { 4960Sstevel@tonic-gate if ((s = GETSEND(argv[i])) == 0) { 4970Sstevel@tonic-gate (void) fprintf(stderr, 4980Sstevel@tonic-gate "Telnet 'send' error - argument disappeared!\n"); 4990Sstevel@tonic-gate (void) quit(); 5000Sstevel@tonic-gate /*NOTREACHED*/ 5010Sstevel@tonic-gate } 5020Sstevel@tonic-gate if (s->handler) { 5030Sstevel@tonic-gate count++; 5040Sstevel@tonic-gate success += (*s->handler)((s->narg > 0) ? argv[i+1] : 0, 5050Sstevel@tonic-gate (s->narg > 1) ? argv[i+2] : 0); 5060Sstevel@tonic-gate i += s->narg; 5070Sstevel@tonic-gate } else { 5080Sstevel@tonic-gate NET2ADD(IAC, s->what); 5090Sstevel@tonic-gate printoption("SENT", IAC, s->what); 5100Sstevel@tonic-gate } 5110Sstevel@tonic-gate } 5120Sstevel@tonic-gate return (count == success); 5130Sstevel@tonic-gate } 5140Sstevel@tonic-gate 5150Sstevel@tonic-gate static int 5160Sstevel@tonic-gate send_esc() 5170Sstevel@tonic-gate { 5180Sstevel@tonic-gate NETADD(escape); 5190Sstevel@tonic-gate return (1); 5200Sstevel@tonic-gate } 5210Sstevel@tonic-gate 5220Sstevel@tonic-gate static int 5230Sstevel@tonic-gate send_docmd(name) 5240Sstevel@tonic-gate char *name; 5250Sstevel@tonic-gate { 5260Sstevel@tonic-gate return (send_tncmd(send_do, "do", name)); 5270Sstevel@tonic-gate } 5280Sstevel@tonic-gate 5290Sstevel@tonic-gate static int 5300Sstevel@tonic-gate send_dontcmd(name) 5310Sstevel@tonic-gate char *name; 5320Sstevel@tonic-gate { 5330Sstevel@tonic-gate return (send_tncmd(send_dont, "dont", name)); 5340Sstevel@tonic-gate } 5350Sstevel@tonic-gate 5360Sstevel@tonic-gate static int 5370Sstevel@tonic-gate send_willcmd(name) 5380Sstevel@tonic-gate char *name; 5390Sstevel@tonic-gate { 5400Sstevel@tonic-gate return (send_tncmd(send_will, "will", name)); 5410Sstevel@tonic-gate } 5420Sstevel@tonic-gate 5430Sstevel@tonic-gate static int 5440Sstevel@tonic-gate send_wontcmd(name) 5450Sstevel@tonic-gate char *name; 5460Sstevel@tonic-gate { 5470Sstevel@tonic-gate return (send_tncmd(send_wont, "wont", name)); 5480Sstevel@tonic-gate } 5490Sstevel@tonic-gate 5500Sstevel@tonic-gate int 5510Sstevel@tonic-gate send_tncmd(func, cmd, name) 5520Sstevel@tonic-gate void (*func)(); 5530Sstevel@tonic-gate char *cmd, *name; 5540Sstevel@tonic-gate { 5550Sstevel@tonic-gate char **cpp; 5560Sstevel@tonic-gate extern char *telopts[]; 5570Sstevel@tonic-gate register int val = 0; 5580Sstevel@tonic-gate 5590Sstevel@tonic-gate if (isprefix(name, "help") || isprefix(name, "?")) { 5600Sstevel@tonic-gate register int col, len; 5610Sstevel@tonic-gate 5620Sstevel@tonic-gate (void) printf("Usage: send %s <value|option>\n", cmd); 5630Sstevel@tonic-gate (void) printf("\"value\" must be from 0 to 255\n"); 5640Sstevel@tonic-gate (void) printf("Valid options are:\n\t"); 5650Sstevel@tonic-gate 5660Sstevel@tonic-gate col = 8; 5670Sstevel@tonic-gate for (cpp = telopts; *cpp; cpp++) { 5680Sstevel@tonic-gate len = strlen(*cpp) + 3; 5690Sstevel@tonic-gate if (col + len > 65) { 5700Sstevel@tonic-gate (void) printf("\n\t"); 5710Sstevel@tonic-gate col = 8; 5720Sstevel@tonic-gate } 5730Sstevel@tonic-gate (void) printf(" \"%s\"", *cpp); 5740Sstevel@tonic-gate col += len; 5750Sstevel@tonic-gate } 5760Sstevel@tonic-gate (void) printf("\n"); 5770Sstevel@tonic-gate return (0); 5780Sstevel@tonic-gate } 5790Sstevel@tonic-gate cpp = (char **)genget(name, telopts, sizeof (char *)); 5800Sstevel@tonic-gate if (Ambiguous(cpp)) { 5810Sstevel@tonic-gate (void) fprintf(stderr, 5820Sstevel@tonic-gate "'%s': ambiguous argument ('send %s ?' for help).\n", 5830Sstevel@tonic-gate name, cmd); 5840Sstevel@tonic-gate return (0); 5850Sstevel@tonic-gate } 5860Sstevel@tonic-gate if (cpp) { 5870Sstevel@tonic-gate val = cpp - telopts; 5880Sstevel@tonic-gate } else { 5890Sstevel@tonic-gate register char *cp = name; 5900Sstevel@tonic-gate 5910Sstevel@tonic-gate while (*cp >= '0' && *cp <= '9') { 5920Sstevel@tonic-gate val *= 10; 5930Sstevel@tonic-gate val += *cp - '0'; 5940Sstevel@tonic-gate cp++; 5950Sstevel@tonic-gate } 5960Sstevel@tonic-gate if (*cp != 0) { 5970Sstevel@tonic-gate (void) fprintf(stderr, 5980Sstevel@tonic-gate "'%s': unknown argument ('send %s ?' for help).\n", 5990Sstevel@tonic-gate name, cmd); 6000Sstevel@tonic-gate return (0); 6010Sstevel@tonic-gate } else if (val < 0 || val > 255) { 6020Sstevel@tonic-gate (void) fprintf(stderr, 6030Sstevel@tonic-gate "'%s': bad value ('send %s ?' for help).\n", 6040Sstevel@tonic-gate name, cmd); 6050Sstevel@tonic-gate return (0); 6060Sstevel@tonic-gate } 6070Sstevel@tonic-gate } 6080Sstevel@tonic-gate if (!connected) { 6090Sstevel@tonic-gate (void) printf("?Need to be connected first.\n"); 6100Sstevel@tonic-gate return (0); 6110Sstevel@tonic-gate } 6120Sstevel@tonic-gate (*func)(val, 1); 6130Sstevel@tonic-gate return (1); 6140Sstevel@tonic-gate } 6150Sstevel@tonic-gate 6160Sstevel@tonic-gate static int 6170Sstevel@tonic-gate send_help() 6180Sstevel@tonic-gate { 6190Sstevel@tonic-gate struct sendlist *s; /* pointer to current command */ 6200Sstevel@tonic-gate for (s = Sendlist; s->name; s++) { 6210Sstevel@tonic-gate if (s->help) 6220Sstevel@tonic-gate (void) printf("%-15s %s\n", s->name, s->help); 6230Sstevel@tonic-gate } 6240Sstevel@tonic-gate return (0); 6250Sstevel@tonic-gate } 6260Sstevel@tonic-gate 6270Sstevel@tonic-gate /* 6280Sstevel@tonic-gate * The following are the routines and data structures referred 6290Sstevel@tonic-gate * to by the arguments to the "toggle" command. 6300Sstevel@tonic-gate */ 6310Sstevel@tonic-gate 6320Sstevel@tonic-gate static int 6330Sstevel@tonic-gate lclchars() 6340Sstevel@tonic-gate { 6350Sstevel@tonic-gate donelclchars = 1; 6360Sstevel@tonic-gate return (1); 6370Sstevel@tonic-gate } 6380Sstevel@tonic-gate 6390Sstevel@tonic-gate static int 6400Sstevel@tonic-gate togdebug() 6410Sstevel@tonic-gate { 6420Sstevel@tonic-gate if (net > 0 && 6430Sstevel@tonic-gate (SetSockOpt(net, SOL_SOCKET, SO_DEBUG, debug)) < 0) { 6440Sstevel@tonic-gate perror("setsockopt (SO_DEBUG)"); 6450Sstevel@tonic-gate } 6460Sstevel@tonic-gate return (1); 6470Sstevel@tonic-gate } 6480Sstevel@tonic-gate 6490Sstevel@tonic-gate 6500Sstevel@tonic-gate static int 6510Sstevel@tonic-gate togcrlf() 6520Sstevel@tonic-gate { 6530Sstevel@tonic-gate if (crlf) { 6540Sstevel@tonic-gate (void) printf( 6550Sstevel@tonic-gate "Will send carriage returns as telnet <CR><LF>.\n"); 6560Sstevel@tonic-gate } else { 6570Sstevel@tonic-gate (void) printf( 6580Sstevel@tonic-gate "Will send carriage returns as telnet <CR><NUL>.\n"); 6590Sstevel@tonic-gate } 6600Sstevel@tonic-gate return (1); 6610Sstevel@tonic-gate } 6620Sstevel@tonic-gate 6630Sstevel@tonic-gate static int binmode; 6640Sstevel@tonic-gate 6650Sstevel@tonic-gate static int 6660Sstevel@tonic-gate togbinary(val) 6670Sstevel@tonic-gate int val; 6680Sstevel@tonic-gate { 6690Sstevel@tonic-gate donebinarytoggle = 1; 6700Sstevel@tonic-gate 6710Sstevel@tonic-gate if (val >= 0) { 6720Sstevel@tonic-gate binmode = val; 6730Sstevel@tonic-gate } else { 6740Sstevel@tonic-gate if (my_want_state_is_will(TELOPT_BINARY) && 6750Sstevel@tonic-gate my_want_state_is_do(TELOPT_BINARY)) { 6760Sstevel@tonic-gate binmode = 1; 6770Sstevel@tonic-gate } else if (my_want_state_is_wont(TELOPT_BINARY) && 6780Sstevel@tonic-gate my_want_state_is_dont(TELOPT_BINARY)) { 6790Sstevel@tonic-gate binmode = 0; 6800Sstevel@tonic-gate } 6810Sstevel@tonic-gate val = binmode ? 0 : 1; 6820Sstevel@tonic-gate } 6830Sstevel@tonic-gate 6840Sstevel@tonic-gate if (val == 1) { 6850Sstevel@tonic-gate if (my_want_state_is_will(TELOPT_BINARY) && 6860Sstevel@tonic-gate my_want_state_is_do(TELOPT_BINARY)) { 6870Sstevel@tonic-gate (void) printf("Already operating in binary mode " 6880Sstevel@tonic-gate "with remote host.\n"); 6890Sstevel@tonic-gate } else { 6900Sstevel@tonic-gate (void) printf( 6910Sstevel@tonic-gate "Negotiating binary mode with remote host.\n"); 6920Sstevel@tonic-gate tel_enter_binary(3); 6930Sstevel@tonic-gate } 6940Sstevel@tonic-gate } else { 6950Sstevel@tonic-gate if (my_want_state_is_wont(TELOPT_BINARY) && 6960Sstevel@tonic-gate my_want_state_is_dont(TELOPT_BINARY)) { 6970Sstevel@tonic-gate (void) printf("Already in network ascii mode " 6980Sstevel@tonic-gate "with remote host.\n"); 6990Sstevel@tonic-gate } else { 7000Sstevel@tonic-gate (void) printf("Negotiating network ascii mode " 7010Sstevel@tonic-gate "with remote host.\n"); 7020Sstevel@tonic-gate tel_leave_binary(3); 7030Sstevel@tonic-gate } 7040Sstevel@tonic-gate } 7050Sstevel@tonic-gate return (1); 7060Sstevel@tonic-gate } 7070Sstevel@tonic-gate 7080Sstevel@tonic-gate static int 7090Sstevel@tonic-gate togrbinary(val) 7100Sstevel@tonic-gate int val; 7110Sstevel@tonic-gate { 7120Sstevel@tonic-gate donebinarytoggle = 1; 7130Sstevel@tonic-gate 7140Sstevel@tonic-gate if (val == -1) 7150Sstevel@tonic-gate val = my_want_state_is_do(TELOPT_BINARY) ? 0 : 1; 7160Sstevel@tonic-gate 7170Sstevel@tonic-gate if (val == 1) { 7180Sstevel@tonic-gate if (my_want_state_is_do(TELOPT_BINARY)) { 7190Sstevel@tonic-gate (void) printf("Already receiving in binary mode.\n"); 7200Sstevel@tonic-gate } else { 7210Sstevel@tonic-gate (void) printf("Negotiating binary mode on input.\n"); 7220Sstevel@tonic-gate tel_enter_binary(1); 7230Sstevel@tonic-gate } 7240Sstevel@tonic-gate } else { 7250Sstevel@tonic-gate if (my_want_state_is_dont(TELOPT_BINARY)) { 7260Sstevel@tonic-gate (void) printf( 7270Sstevel@tonic-gate "Already receiving in network ascii mode.\n"); 7280Sstevel@tonic-gate } else { 7290Sstevel@tonic-gate (void) printf( 7300Sstevel@tonic-gate "Negotiating network ascii mode on input.\n"); 7310Sstevel@tonic-gate tel_leave_binary(1); 7320Sstevel@tonic-gate } 7330Sstevel@tonic-gate } 7340Sstevel@tonic-gate return (1); 7350Sstevel@tonic-gate } 7360Sstevel@tonic-gate 7370Sstevel@tonic-gate static int 7380Sstevel@tonic-gate togxbinary(val) 7390Sstevel@tonic-gate int val; 7400Sstevel@tonic-gate { 7410Sstevel@tonic-gate donebinarytoggle = 1; 7420Sstevel@tonic-gate 7430Sstevel@tonic-gate if (val == -1) 7440Sstevel@tonic-gate val = my_want_state_is_will(TELOPT_BINARY) ? 0 : 1; 7450Sstevel@tonic-gate 7460Sstevel@tonic-gate if (val == 1) { 7470Sstevel@tonic-gate if (my_want_state_is_will(TELOPT_BINARY)) { 7480Sstevel@tonic-gate (void) printf("Already transmitting in binary mode.\n"); 7490Sstevel@tonic-gate } else { 7500Sstevel@tonic-gate (void) printf("Negotiating binary mode on output.\n"); 7510Sstevel@tonic-gate tel_enter_binary(2); 7520Sstevel@tonic-gate } 7530Sstevel@tonic-gate } else { 7540Sstevel@tonic-gate if (my_want_state_is_wont(TELOPT_BINARY)) { 7550Sstevel@tonic-gate (void) printf( 7560Sstevel@tonic-gate "Already transmitting in network ascii mode.\n"); 7570Sstevel@tonic-gate } else { 7580Sstevel@tonic-gate (void) printf( 7590Sstevel@tonic-gate "Negotiating network ascii mode on output.\n"); 7600Sstevel@tonic-gate tel_leave_binary(2); 7610Sstevel@tonic-gate } 7620Sstevel@tonic-gate } 7630Sstevel@tonic-gate return (1); 7640Sstevel@tonic-gate } 7650Sstevel@tonic-gate 7660Sstevel@tonic-gate 7670Sstevel@tonic-gate static int togglehelp(void); 7680Sstevel@tonic-gate extern int auth_togdebug(int); 7690Sstevel@tonic-gate 7700Sstevel@tonic-gate struct togglelist { 7710Sstevel@tonic-gate char *name; /* name of toggle */ 7720Sstevel@tonic-gate char *help; /* help message */ 7730Sstevel@tonic-gate int (*handler)(); /* routine to do actual setting */ 7740Sstevel@tonic-gate int *variable; 7750Sstevel@tonic-gate char *actionexplanation; 7760Sstevel@tonic-gate }; 7770Sstevel@tonic-gate 7780Sstevel@tonic-gate static struct togglelist Togglelist[] = { 7790Sstevel@tonic-gate { "autoflush", 7800Sstevel@tonic-gate "flushing of output when sending interrupt characters", 7810Sstevel@tonic-gate 0, 7820Sstevel@tonic-gate &autoflush, 7830Sstevel@tonic-gate "flush output when sending interrupt characters" }, 7840Sstevel@tonic-gate { "autosynch", 7850Sstevel@tonic-gate "automatic sending of interrupt characters in urgent mode", 7860Sstevel@tonic-gate 0, 7870Sstevel@tonic-gate &autosynch, 7880Sstevel@tonic-gate "send interrupt characters in urgent mode" }, 7890Sstevel@tonic-gate { "autologin", 7900Sstevel@tonic-gate "automatic sending of login and/or authentication info", 7910Sstevel@tonic-gate 0, 7920Sstevel@tonic-gate &autologin, 7930Sstevel@tonic-gate "send login name and/or authentication information" }, 7940Sstevel@tonic-gate { "authdebug", 7950Sstevel@tonic-gate "authentication debugging", 7960Sstevel@tonic-gate auth_togdebug, 7970Sstevel@tonic-gate 0, 7980Sstevel@tonic-gate "print authentication debugging information" }, 7990Sstevel@tonic-gate { "autoencrypt", 8000Sstevel@tonic-gate "automatic encryption of data stream", 8010Sstevel@tonic-gate EncryptAutoEnc, 8020Sstevel@tonic-gate 0, 8030Sstevel@tonic-gate "automatically encrypt output" }, 8040Sstevel@tonic-gate { "autodecrypt", 8050Sstevel@tonic-gate "automatic decryption of data stream", 8060Sstevel@tonic-gate EncryptAutoDec, 8070Sstevel@tonic-gate 0, 8080Sstevel@tonic-gate "automatically decrypt input" }, 8090Sstevel@tonic-gate { "verbose_encrypt", 8100Sstevel@tonic-gate "verbose encryption output", 8110Sstevel@tonic-gate EncryptVerbose, 8120Sstevel@tonic-gate 0, 8130Sstevel@tonic-gate "print verbose encryption output" }, 8140Sstevel@tonic-gate { "encdebug", 8150Sstevel@tonic-gate "encryption debugging", 8160Sstevel@tonic-gate EncryptDebug, 8170Sstevel@tonic-gate 0, 8180Sstevel@tonic-gate "print encryption debugging information" }, 8190Sstevel@tonic-gate { "skiprc", 8200Sstevel@tonic-gate "don't read ~/.telnetrc file", 8210Sstevel@tonic-gate 0, 8220Sstevel@tonic-gate &skiprc, 8230Sstevel@tonic-gate "skip reading of ~/.telnetrc file" }, 8240Sstevel@tonic-gate { "binary", 8250Sstevel@tonic-gate "sending and receiving of binary data", 8260Sstevel@tonic-gate togbinary, 8270Sstevel@tonic-gate 0, 8280Sstevel@tonic-gate 0 }, 8290Sstevel@tonic-gate { "inbinary", 8300Sstevel@tonic-gate "receiving of binary data", 8310Sstevel@tonic-gate togrbinary, 8320Sstevel@tonic-gate 0, 8330Sstevel@tonic-gate 0 }, 8340Sstevel@tonic-gate { "outbinary", 8350Sstevel@tonic-gate "sending of binary data", 8360Sstevel@tonic-gate togxbinary, 8370Sstevel@tonic-gate 0, 8380Sstevel@tonic-gate 0 }, 8390Sstevel@tonic-gate { "crlf", 8400Sstevel@tonic-gate "sending carriage returns as telnet <CR><LF>", 8410Sstevel@tonic-gate togcrlf, 8420Sstevel@tonic-gate &crlf, 8430Sstevel@tonic-gate 0 }, 8440Sstevel@tonic-gate { "crmod", 8450Sstevel@tonic-gate "mapping of received carriage returns", 8460Sstevel@tonic-gate 0, 8470Sstevel@tonic-gate &crmod, 8480Sstevel@tonic-gate "map carriage return on output" }, 8490Sstevel@tonic-gate { "localchars", 8500Sstevel@tonic-gate "local recognition of certain control characters", 8510Sstevel@tonic-gate lclchars, 8520Sstevel@tonic-gate &localchars, 8530Sstevel@tonic-gate "recognize certain control characters" }, 8540Sstevel@tonic-gate { " ", "", 0 }, /* empty line */ 8550Sstevel@tonic-gate { "debug", 8560Sstevel@tonic-gate "debugging", 8570Sstevel@tonic-gate togdebug, 8580Sstevel@tonic-gate &debug, 8590Sstevel@tonic-gate "turn on socket level debugging" }, 8600Sstevel@tonic-gate { "netdata", 8610Sstevel@tonic-gate "printing of hexadecimal network data (debugging)", 8620Sstevel@tonic-gate 0, 8630Sstevel@tonic-gate &netdata, 8640Sstevel@tonic-gate "print hexadecimal representation of network traffic" }, 8650Sstevel@tonic-gate { "prettydump", 8660Sstevel@tonic-gate "output of \"netdata\" to user readable format (debugging)", 8670Sstevel@tonic-gate 0, 8680Sstevel@tonic-gate &prettydump, 8690Sstevel@tonic-gate "print user readable output for \"netdata\"" }, 8700Sstevel@tonic-gate { "options", 8710Sstevel@tonic-gate "viewing of options processing (debugging)", 8720Sstevel@tonic-gate 0, 8730Sstevel@tonic-gate &showoptions, 8740Sstevel@tonic-gate "show option processing" }, 8750Sstevel@tonic-gate { "termdata", 8760Sstevel@tonic-gate "(debugging) toggle printing of hexadecimal terminal data", 8770Sstevel@tonic-gate 0, 8780Sstevel@tonic-gate &termdata, 8790Sstevel@tonic-gate "print hexadecimal representation of terminal traffic" }, 8800Sstevel@tonic-gate { "?", 8810Sstevel@tonic-gate 0, 8820Sstevel@tonic-gate togglehelp }, 8830Sstevel@tonic-gate { "help", 8840Sstevel@tonic-gate 0, 8850Sstevel@tonic-gate togglehelp }, 8860Sstevel@tonic-gate { 0 } 8870Sstevel@tonic-gate }; 8880Sstevel@tonic-gate 8890Sstevel@tonic-gate static int 8900Sstevel@tonic-gate togglehelp() 8910Sstevel@tonic-gate { 8920Sstevel@tonic-gate struct togglelist *c; 8930Sstevel@tonic-gate 8940Sstevel@tonic-gate for (c = Togglelist; c->name; c++) { 8950Sstevel@tonic-gate if (c->help) { 8960Sstevel@tonic-gate if (*c->help) 8970Sstevel@tonic-gate (void) printf( 8980Sstevel@tonic-gate "%-15s toggle %s\n", c->name, c->help); 8990Sstevel@tonic-gate else 9000Sstevel@tonic-gate (void) printf("\n"); 9010Sstevel@tonic-gate } 9020Sstevel@tonic-gate } 9030Sstevel@tonic-gate (void) printf("\n"); 9040Sstevel@tonic-gate (void) printf("%-15s %s\n", "?", "display help information"); 9050Sstevel@tonic-gate return (0); 9060Sstevel@tonic-gate } 9070Sstevel@tonic-gate 9080Sstevel@tonic-gate static void 9090Sstevel@tonic-gate settogglehelp(set) 9100Sstevel@tonic-gate int set; 9110Sstevel@tonic-gate { 9120Sstevel@tonic-gate struct togglelist *c; 9130Sstevel@tonic-gate 9140Sstevel@tonic-gate for (c = Togglelist; c->name; c++) { 9150Sstevel@tonic-gate if (c->help) { 9160Sstevel@tonic-gate if (*c->help) 9170Sstevel@tonic-gate (void) printf("%-15s %s %s\n", c->name, 9180Sstevel@tonic-gate set ? "enable" : "disable", c->help); 9190Sstevel@tonic-gate else 9200Sstevel@tonic-gate (void) printf("\n"); 9210Sstevel@tonic-gate } 9220Sstevel@tonic-gate } 9230Sstevel@tonic-gate } 9240Sstevel@tonic-gate 9250Sstevel@tonic-gate #define GETTOGGLE(name) (struct togglelist *) \ 9260Sstevel@tonic-gate genget(name, (char **)Togglelist, sizeof (struct togglelist)) 9270Sstevel@tonic-gate 9280Sstevel@tonic-gate static int 9290Sstevel@tonic-gate toggle(argc, argv) 9300Sstevel@tonic-gate int argc; 9310Sstevel@tonic-gate char *argv[]; 9320Sstevel@tonic-gate { 9330Sstevel@tonic-gate int retval = 1; 9340Sstevel@tonic-gate char *name; 9350Sstevel@tonic-gate struct togglelist *c; 9360Sstevel@tonic-gate 9370Sstevel@tonic-gate if (argc < 2) { 9380Sstevel@tonic-gate (void) fprintf(stderr, 9390Sstevel@tonic-gate "Need an argument to 'toggle' command. " 9400Sstevel@tonic-gate "'toggle ?' for help.\n"); 9410Sstevel@tonic-gate return (0); 9420Sstevel@tonic-gate } 9430Sstevel@tonic-gate argc--; 9440Sstevel@tonic-gate argv++; 9450Sstevel@tonic-gate while (argc--) { 9460Sstevel@tonic-gate name = *argv++; 9470Sstevel@tonic-gate c = GETTOGGLE(name); 9480Sstevel@tonic-gate if (Ambiguous(c)) { 9490Sstevel@tonic-gate (void) fprintf(stderr, "'%s': ambiguous argument " 9500Sstevel@tonic-gate "('toggle ?' for help).\n", name); 9510Sstevel@tonic-gate return (0); 9520Sstevel@tonic-gate } else if (c == 0) { 9530Sstevel@tonic-gate (void) fprintf(stderr, "'%s': unknown argument " 9540Sstevel@tonic-gate "('toggle ?' for help).\n", name); 9550Sstevel@tonic-gate return (0); 9560Sstevel@tonic-gate } else { 9570Sstevel@tonic-gate if (c->variable) { 9580Sstevel@tonic-gate *c->variable = !*c->variable; /* invert it */ 9590Sstevel@tonic-gate if (c->actionexplanation) { 9600Sstevel@tonic-gate (void) printf("%s %s.\n", 9610Sstevel@tonic-gate *c->variable ? "Will" : "Won't", 9620Sstevel@tonic-gate c->actionexplanation); 9630Sstevel@tonic-gate } 9640Sstevel@tonic-gate } 9650Sstevel@tonic-gate if (c->handler) { 9660Sstevel@tonic-gate retval &= (*c->handler)(-1); 9670Sstevel@tonic-gate } 9680Sstevel@tonic-gate } 9690Sstevel@tonic-gate } 9700Sstevel@tonic-gate return (retval); 9710Sstevel@tonic-gate } 9720Sstevel@tonic-gate 9730Sstevel@tonic-gate /* 9740Sstevel@tonic-gate * The following perform the "set" command. 9750Sstevel@tonic-gate */ 9760Sstevel@tonic-gate 9770Sstevel@tonic-gate #ifdef USE_TERMIO 9780Sstevel@tonic-gate struct termio new_tc = { 0 }; 9790Sstevel@tonic-gate #endif 9800Sstevel@tonic-gate 9810Sstevel@tonic-gate struct setlist { 9820Sstevel@tonic-gate char *name; /* name */ 9830Sstevel@tonic-gate char *help; /* help information */ 9840Sstevel@tonic-gate void (*handler)(); 9850Sstevel@tonic-gate cc_t *charp; /* where it is located at */ 9860Sstevel@tonic-gate }; 9870Sstevel@tonic-gate 9880Sstevel@tonic-gate static struct setlist Setlist[] = { 9890Sstevel@tonic-gate #ifdef KLUDGELINEMODE 9900Sstevel@tonic-gate { "echo", "character to toggle local echoing on/off", 0, &echoc }, 9910Sstevel@tonic-gate #endif 9920Sstevel@tonic-gate { "escape", "character to escape back to telnet command mode", 0, 9930Sstevel@tonic-gate &escape }, 9940Sstevel@tonic-gate { "rlogin", "rlogin escape character", 0, &rlogin }, 9950Sstevel@tonic-gate { "tracefile", "file to write trace information to", SetNetTrace, 9960Sstevel@tonic-gate (cc_t *)NetTraceFile}, 9970Sstevel@tonic-gate { " ", "" }, 9980Sstevel@tonic-gate { " ", "The following need 'localchars' to be toggled true", 0, 0 }, 9990Sstevel@tonic-gate { "flushoutput", "character to cause an Abort Output", 0, 10000Sstevel@tonic-gate termFlushCharp }, 10010Sstevel@tonic-gate { "interrupt", "character to cause an Interrupt Process", 0, 10020Sstevel@tonic-gate termIntCharp }, 10030Sstevel@tonic-gate { "quit", "character to cause an Abort process", 0, termQuitCharp }, 10040Sstevel@tonic-gate { "eof", "character to cause an EOF ", 0, termEofCharp }, 10050Sstevel@tonic-gate { " ", "" }, 10060Sstevel@tonic-gate { " ", "The following are for local editing in linemode", 0, 0 }, 10070Sstevel@tonic-gate { "erase", "character to use to erase a character", 0, termEraseCharp }, 10080Sstevel@tonic-gate { "kill", "character to use to erase a line", 0, termKillCharp }, 10090Sstevel@tonic-gate { "lnext", "character to use for literal next", 0, 10100Sstevel@tonic-gate termLiteralNextCharp }, 10110Sstevel@tonic-gate { "susp", "character to cause a Suspend Process", 0, termSuspCharp }, 10120Sstevel@tonic-gate { "reprint", "character to use for line reprint", 0, termRprntCharp }, 10130Sstevel@tonic-gate { "worderase", "character to use to erase a word", 0, termWerasCharp }, 10140Sstevel@tonic-gate { "start", "character to use for XON", 0, termStartCharp }, 10150Sstevel@tonic-gate { "stop", "character to use for XOFF", 0, termStopCharp }, 10160Sstevel@tonic-gate { "forw1", "alternate end of line character", 0, termForw1Charp }, 10170Sstevel@tonic-gate { "forw2", "alternate end of line character", 0, termForw2Charp }, 10180Sstevel@tonic-gate { "ayt", "alternate AYT character", 0, termAytCharp }, 10190Sstevel@tonic-gate { 0 } 10200Sstevel@tonic-gate }; 10210Sstevel@tonic-gate 10220Sstevel@tonic-gate static struct setlist * 10230Sstevel@tonic-gate getset(name) 10240Sstevel@tonic-gate char *name; 10250Sstevel@tonic-gate { 10260Sstevel@tonic-gate return ((struct setlist *) 10270Sstevel@tonic-gate genget(name, (char **)Setlist, sizeof (struct setlist))); 10280Sstevel@tonic-gate } 10290Sstevel@tonic-gate 10300Sstevel@tonic-gate void 10310Sstevel@tonic-gate set_escape_char(s) 10320Sstevel@tonic-gate char *s; 10330Sstevel@tonic-gate { 10340Sstevel@tonic-gate if (rlogin != _POSIX_VDISABLE) { 10350Sstevel@tonic-gate rlogin = (s && *s) ? special(s) : _POSIX_VDISABLE; 10360Sstevel@tonic-gate (void) printf("Telnet rlogin escape character is '%s'.\n", 10370Sstevel@tonic-gate control(rlogin)); 10380Sstevel@tonic-gate } else { 10390Sstevel@tonic-gate escape = (s && *s) ? special(s) : _POSIX_VDISABLE; 10400Sstevel@tonic-gate (void) printf("Telnet escape character is '%s'.\n", 10410Sstevel@tonic-gate esc_control(escape)); 10420Sstevel@tonic-gate } 10430Sstevel@tonic-gate } 10440Sstevel@tonic-gate 10450Sstevel@tonic-gate static int 10460Sstevel@tonic-gate setcmd(argc, argv) 10470Sstevel@tonic-gate int argc; 10480Sstevel@tonic-gate char *argv[]; 10490Sstevel@tonic-gate { 10500Sstevel@tonic-gate int value; 10510Sstevel@tonic-gate struct setlist *ct; 10520Sstevel@tonic-gate struct togglelist *c; 10530Sstevel@tonic-gate 10540Sstevel@tonic-gate if (argc < 2 || argc > 3) { 10550Sstevel@tonic-gate (void) printf( 10560Sstevel@tonic-gate "Format is 'set Name Value'\n'set ?' for help.\n"); 10570Sstevel@tonic-gate return (0); 10580Sstevel@tonic-gate } 10590Sstevel@tonic-gate if ((argc == 2) && 10600Sstevel@tonic-gate (isprefix(argv[1], "?") || isprefix(argv[1], "help"))) { 10610Sstevel@tonic-gate for (ct = Setlist; ct->name; ct++) 10620Sstevel@tonic-gate (void) printf("%-15s %s\n", ct->name, ct->help); 10630Sstevel@tonic-gate (void) printf("\n"); 10640Sstevel@tonic-gate settogglehelp(1); 10650Sstevel@tonic-gate (void) printf("%-15s %s\n", "?", "display help information"); 10660Sstevel@tonic-gate return (0); 10670Sstevel@tonic-gate } 10680Sstevel@tonic-gate 10690Sstevel@tonic-gate ct = getset(argv[1]); 10700Sstevel@tonic-gate if (ct == 0) { 10710Sstevel@tonic-gate c = GETTOGGLE(argv[1]); 10720Sstevel@tonic-gate if (c == 0) { 10730Sstevel@tonic-gate (void) fprintf(stderr, "'%s': unknown argument " 10740Sstevel@tonic-gate "('set ?' for help).\n", argv[1]); 10750Sstevel@tonic-gate return (0); 10760Sstevel@tonic-gate } else if (Ambiguous(c)) { 10770Sstevel@tonic-gate (void) fprintf(stderr, "'%s': ambiguous argument " 10780Sstevel@tonic-gate "('set ?' for help).\n", argv[1]); 10790Sstevel@tonic-gate return (0); 10800Sstevel@tonic-gate } 10810Sstevel@tonic-gate if (c->variable) { 10820Sstevel@tonic-gate if ((argc == 2) || (strcmp("on", argv[2]) == 0)) 10830Sstevel@tonic-gate *c->variable = 1; 10840Sstevel@tonic-gate else if (strcmp("off", argv[2]) == 0) 10850Sstevel@tonic-gate *c->variable = 0; 10860Sstevel@tonic-gate else { 10870Sstevel@tonic-gate (void) printf( 10880Sstevel@tonic-gate "Format is 'set togglename [on|off]'\n" 10890Sstevel@tonic-gate "'set ?' for help.\n"); 10900Sstevel@tonic-gate return (0); 10910Sstevel@tonic-gate } 10920Sstevel@tonic-gate if (c->actionexplanation) { 10930Sstevel@tonic-gate (void) printf("%s %s.\n", 10940Sstevel@tonic-gate *c->variable? "Will" : "Won't", 10950Sstevel@tonic-gate c->actionexplanation); 10960Sstevel@tonic-gate } 10970Sstevel@tonic-gate } 10980Sstevel@tonic-gate if (c->handler) 10990Sstevel@tonic-gate (*c->handler)(1); 11000Sstevel@tonic-gate } else if (argc != 3) { 11010Sstevel@tonic-gate (void) printf( 11020Sstevel@tonic-gate "Format is 'set Name Value'\n'set ?' for help.\n"); 11030Sstevel@tonic-gate return (0); 11040Sstevel@tonic-gate } else if (Ambiguous(ct)) { 11050Sstevel@tonic-gate (void) fprintf(stderr, 11060Sstevel@tonic-gate "'%s': ambiguous argument ('set ?' for help).\n", argv[1]); 11070Sstevel@tonic-gate return (0); 11080Sstevel@tonic-gate } else if (ct->handler) { 11090Sstevel@tonic-gate (*ct->handler)(argv[2]); 11100Sstevel@tonic-gate (void) printf( 11110Sstevel@tonic-gate "%s set to \"%s\".\n", ct->name, (char *)ct->charp); 11120Sstevel@tonic-gate } else { 11130Sstevel@tonic-gate if (strcmp("off", argv[2])) { 11140Sstevel@tonic-gate value = special(argv[2]); 11150Sstevel@tonic-gate } else { 11160Sstevel@tonic-gate value = _POSIX_VDISABLE; 11170Sstevel@tonic-gate } 11180Sstevel@tonic-gate *(ct->charp) = (cc_t)value; 11190Sstevel@tonic-gate (void) printf("%s character is '%s'.\n", ct->name, 11200Sstevel@tonic-gate control(*(ct->charp))); 11210Sstevel@tonic-gate } 11220Sstevel@tonic-gate slc_check(); 11230Sstevel@tonic-gate return (1); 11240Sstevel@tonic-gate } 11250Sstevel@tonic-gate 11260Sstevel@tonic-gate static int 11270Sstevel@tonic-gate unsetcmd(argc, argv) 11280Sstevel@tonic-gate int argc; 11290Sstevel@tonic-gate char *argv[]; 11300Sstevel@tonic-gate { 11310Sstevel@tonic-gate struct setlist *ct; 11320Sstevel@tonic-gate struct togglelist *c; 11330Sstevel@tonic-gate register char *name; 11340Sstevel@tonic-gate 11350Sstevel@tonic-gate if (argc < 2) { 11360Sstevel@tonic-gate (void) fprintf(stderr, "Need an argument to 'unset' command. " 11370Sstevel@tonic-gate "'unset ?' for help.\n"); 11380Sstevel@tonic-gate return (0); 11390Sstevel@tonic-gate } 11400Sstevel@tonic-gate if (isprefix(argv[1], "?") || isprefix(argv[1], "help")) { 11410Sstevel@tonic-gate for (ct = Setlist; ct->name; ct++) 11420Sstevel@tonic-gate (void) printf("%-15s %s\n", ct->name, ct->help); 11430Sstevel@tonic-gate (void) printf("\n"); 11440Sstevel@tonic-gate settogglehelp(0); 11450Sstevel@tonic-gate (void) printf("%-15s %s\n", "?", "display help information"); 11460Sstevel@tonic-gate return (0); 11470Sstevel@tonic-gate } 11480Sstevel@tonic-gate 11490Sstevel@tonic-gate argc--; 11500Sstevel@tonic-gate argv++; 11510Sstevel@tonic-gate while (argc--) { 11520Sstevel@tonic-gate name = *argv++; 11530Sstevel@tonic-gate ct = getset(name); 11540Sstevel@tonic-gate if (ct == 0) { 11550Sstevel@tonic-gate c = GETTOGGLE(name); 11560Sstevel@tonic-gate if (c == 0) { 11570Sstevel@tonic-gate (void) fprintf(stderr, "'%s': unknown argument " 11580Sstevel@tonic-gate "('unset ?' for help).\n", name); 11590Sstevel@tonic-gate return (0); 11600Sstevel@tonic-gate } else if (Ambiguous(c)) { 11610Sstevel@tonic-gate (void) fprintf(stderr, 11620Sstevel@tonic-gate "'%s': ambiguous argument " 11630Sstevel@tonic-gate "('unset ?' for help).\n", name); 11640Sstevel@tonic-gate return (0); 11650Sstevel@tonic-gate } 11660Sstevel@tonic-gate if (c->variable) { 11670Sstevel@tonic-gate *c->variable = 0; 11680Sstevel@tonic-gate if (c->actionexplanation) { 11690Sstevel@tonic-gate (void) printf("%s %s.\n", 11700Sstevel@tonic-gate *c->variable? "Will" : "Won't", 11710Sstevel@tonic-gate c->actionexplanation); 11720Sstevel@tonic-gate } 11730Sstevel@tonic-gate } 11740Sstevel@tonic-gate if (c->handler) 11750Sstevel@tonic-gate (*c->handler)(0); 11760Sstevel@tonic-gate } else if (Ambiguous(ct)) { 11770Sstevel@tonic-gate (void) fprintf(stderr, "'%s': ambiguous argument " 11780Sstevel@tonic-gate "('unset ?' for help).\n", name); 11790Sstevel@tonic-gate return (0); 11800Sstevel@tonic-gate } else if (ct->handler) { 11810Sstevel@tonic-gate (*ct->handler)(0); 11820Sstevel@tonic-gate (void) printf("%s reset to \"%s\".\n", ct->name, 11830Sstevel@tonic-gate (char *)ct->charp); 11840Sstevel@tonic-gate } else { 11850Sstevel@tonic-gate *(ct->charp) = _POSIX_VDISABLE; 11860Sstevel@tonic-gate (void) printf("%s character is '%s'.\n", ct->name, 11870Sstevel@tonic-gate control(*(ct->charp))); 11880Sstevel@tonic-gate } 11890Sstevel@tonic-gate } 11900Sstevel@tonic-gate return (1); 11910Sstevel@tonic-gate } 11920Sstevel@tonic-gate 11930Sstevel@tonic-gate /* 11940Sstevel@tonic-gate * The following are the data structures and routines for the 11950Sstevel@tonic-gate * 'mode' command. 11960Sstevel@tonic-gate */ 11970Sstevel@tonic-gate extern int reqd_linemode; 11980Sstevel@tonic-gate 11990Sstevel@tonic-gate #ifdef KLUDGELINEMODE 12000Sstevel@tonic-gate extern int kludgelinemode; 12010Sstevel@tonic-gate 12020Sstevel@tonic-gate static int 12030Sstevel@tonic-gate dokludgemode() 12040Sstevel@tonic-gate { 12050Sstevel@tonic-gate kludgelinemode = 1; 12060Sstevel@tonic-gate send_wont(TELOPT_LINEMODE, 1); 12070Sstevel@tonic-gate send_dont(TELOPT_SGA, 1); 12080Sstevel@tonic-gate send_dont(TELOPT_ECHO, 1); 12090Sstevel@tonic-gate /* 12100Sstevel@tonic-gate * If processing the .telnetrc file, keep track of linemode and/or 12110Sstevel@tonic-gate * kludgelinemode requests which are processed before initial option 12120Sstevel@tonic-gate * negotiations occur. 12130Sstevel@tonic-gate */ 12140Sstevel@tonic-gate if (doing_rc) 12150Sstevel@tonic-gate reqd_linemode = 1; 12160Sstevel@tonic-gate return (1); 12170Sstevel@tonic-gate } 12180Sstevel@tonic-gate #endif 12190Sstevel@tonic-gate 12200Sstevel@tonic-gate static int 12210Sstevel@tonic-gate dolinemode() 12220Sstevel@tonic-gate { 12230Sstevel@tonic-gate #ifdef KLUDGELINEMODE 12240Sstevel@tonic-gate if (kludgelinemode) 12250Sstevel@tonic-gate send_dont(TELOPT_SGA, 1); 12260Sstevel@tonic-gate #endif 12270Sstevel@tonic-gate send_will(TELOPT_LINEMODE, 1); 12280Sstevel@tonic-gate send_dont(TELOPT_ECHO, 1); 12290Sstevel@tonic-gate 12300Sstevel@tonic-gate /* 12310Sstevel@tonic-gate * If processing the .telnetrc file, keep track of linemode and/or 12320Sstevel@tonic-gate * kludgelinemode requests which are processed before initial option 12330Sstevel@tonic-gate * negotiations occur. 12340Sstevel@tonic-gate */ 12350Sstevel@tonic-gate if (doing_rc) 12360Sstevel@tonic-gate reqd_linemode = 1; 12370Sstevel@tonic-gate return (1); 12380Sstevel@tonic-gate } 12390Sstevel@tonic-gate 12400Sstevel@tonic-gate static int 12410Sstevel@tonic-gate docharmode() 12420Sstevel@tonic-gate { 12430Sstevel@tonic-gate #ifdef KLUDGELINEMODE 12440Sstevel@tonic-gate if (kludgelinemode) 12450Sstevel@tonic-gate send_do(TELOPT_SGA, 1); 12460Sstevel@tonic-gate else 12470Sstevel@tonic-gate #endif 12480Sstevel@tonic-gate send_wont(TELOPT_LINEMODE, 1); 12490Sstevel@tonic-gate send_do(TELOPT_ECHO, 1); 12500Sstevel@tonic-gate reqd_linemode = 0; 12510Sstevel@tonic-gate return (1); 12520Sstevel@tonic-gate } 12530Sstevel@tonic-gate 12540Sstevel@tonic-gate static int 12550Sstevel@tonic-gate dolmmode(bit, on) 12560Sstevel@tonic-gate int bit, on; 12570Sstevel@tonic-gate { 12580Sstevel@tonic-gate unsigned char c; 12590Sstevel@tonic-gate extern int linemode; 12600Sstevel@tonic-gate 12610Sstevel@tonic-gate if (my_want_state_is_wont(TELOPT_LINEMODE)) { 12620Sstevel@tonic-gate (void) printf("?Need to have LINEMODE option enabled first.\n"); 12630Sstevel@tonic-gate (void) printf("'mode ?' for help.\n"); 12640Sstevel@tonic-gate return (0); 12650Sstevel@tonic-gate } 12660Sstevel@tonic-gate 12670Sstevel@tonic-gate if (on) 12680Sstevel@tonic-gate c = (linemode | bit); 12690Sstevel@tonic-gate else 12700Sstevel@tonic-gate c = (linemode & ~bit); 12710Sstevel@tonic-gate lm_mode(&c, 1, 1); 12720Sstevel@tonic-gate return (1); 12730Sstevel@tonic-gate } 12740Sstevel@tonic-gate 12750Sstevel@tonic-gate static int 12760Sstevel@tonic-gate setmode(bit) 12770Sstevel@tonic-gate { 12780Sstevel@tonic-gate return (dolmmode(bit, 1)); 12790Sstevel@tonic-gate } 12800Sstevel@tonic-gate 12810Sstevel@tonic-gate static int 12820Sstevel@tonic-gate clearmode(bit) 12830Sstevel@tonic-gate { 12840Sstevel@tonic-gate return (dolmmode(bit, 0)); 12850Sstevel@tonic-gate } 12860Sstevel@tonic-gate 12870Sstevel@tonic-gate struct modelist { 12880Sstevel@tonic-gate char *name; /* command name */ 12890Sstevel@tonic-gate char *help; /* help string */ 12900Sstevel@tonic-gate int (*handler)(); /* routine which executes command */ 12910Sstevel@tonic-gate int needconnect; /* Do we need to be connected to execute? */ 12920Sstevel@tonic-gate int arg1; 12930Sstevel@tonic-gate }; 12940Sstevel@tonic-gate 12950Sstevel@tonic-gate static int modehelp(); 12960Sstevel@tonic-gate 12970Sstevel@tonic-gate static struct modelist ModeList[] = { 12980Sstevel@tonic-gate { "character", "Disable LINEMODE option", docharmode, 1 }, 12990Sstevel@tonic-gate #ifdef KLUDGELINEMODE 13000Sstevel@tonic-gate { "", "(or disable obsolete line-by-line mode)", 0 }, 13010Sstevel@tonic-gate #endif 13020Sstevel@tonic-gate { "line", "Enable LINEMODE option", dolinemode, 1 }, 13030Sstevel@tonic-gate #ifdef KLUDGELINEMODE 13040Sstevel@tonic-gate { "", "(or enable obsolete line-by-line mode)", 0 }, 13050Sstevel@tonic-gate #endif 13060Sstevel@tonic-gate { "", "", 0 }, 13070Sstevel@tonic-gate { "", "These require the LINEMODE option to be enabled", 0 }, 13080Sstevel@tonic-gate { "isig", "Enable signal trapping", setmode, 1, MODE_TRAPSIG }, 13090Sstevel@tonic-gate { "+isig", 0, setmode, 1, MODE_TRAPSIG }, 13100Sstevel@tonic-gate { "-isig", "Disable signal trapping", clearmode, 1, MODE_TRAPSIG }, 13110Sstevel@tonic-gate { "edit", "Enable character editing", setmode, 1, MODE_EDIT }, 13120Sstevel@tonic-gate { "+edit", 0, setmode, 1, MODE_EDIT }, 13130Sstevel@tonic-gate { "-edit", "Disable character editing", clearmode, 1, MODE_EDIT }, 13140Sstevel@tonic-gate { "softtabs", "Enable tab expansion", setmode, 1, MODE_SOFT_TAB }, 13150Sstevel@tonic-gate { "+softtabs", 0, setmode, 1, MODE_SOFT_TAB }, 13160Sstevel@tonic-gate { "-softtabs", "Disable tab expansion", 13170Sstevel@tonic-gate clearmode, 1, MODE_SOFT_TAB }, 13180Sstevel@tonic-gate { "litecho", "Enable literal character echo", 13190Sstevel@tonic-gate setmode, 1, MODE_LIT_ECHO }, 13200Sstevel@tonic-gate { "+litecho", 0, setmode, 1, MODE_LIT_ECHO }, 13210Sstevel@tonic-gate { "-litecho", "Disable literal character echo", clearmode, 1, 13220Sstevel@tonic-gate MODE_LIT_ECHO }, 13230Sstevel@tonic-gate { "help", 0, modehelp, 0 }, 13240Sstevel@tonic-gate #ifdef KLUDGELINEMODE 13250Sstevel@tonic-gate { "kludgeline", 0, dokludgemode, 1 }, 13260Sstevel@tonic-gate #endif 13270Sstevel@tonic-gate { "", "", 0 }, 13280Sstevel@tonic-gate { "?", "Print help information", modehelp, 0 }, 13290Sstevel@tonic-gate { 0 }, 13300Sstevel@tonic-gate }; 13310Sstevel@tonic-gate 13320Sstevel@tonic-gate 13330Sstevel@tonic-gate static int 13340Sstevel@tonic-gate modehelp() 13350Sstevel@tonic-gate { 13360Sstevel@tonic-gate struct modelist *mt; 13370Sstevel@tonic-gate 13380Sstevel@tonic-gate (void) printf("format is: 'mode Mode', where 'Mode' is one of:\n\n"); 13390Sstevel@tonic-gate for (mt = ModeList; mt->name; mt++) { 13400Sstevel@tonic-gate if (mt->help) { 13410Sstevel@tonic-gate if (*mt->help) 13420Sstevel@tonic-gate (void) printf("%-15s %s\n", mt->name, mt->help); 13430Sstevel@tonic-gate else 13440Sstevel@tonic-gate (void) printf("\n"); 13450Sstevel@tonic-gate } 13460Sstevel@tonic-gate } 13470Sstevel@tonic-gate return (0); 13480Sstevel@tonic-gate } 13490Sstevel@tonic-gate 13500Sstevel@tonic-gate #define GETMODECMD(name) (struct modelist *) \ 13510Sstevel@tonic-gate genget(name, (char **)ModeList, sizeof (struct modelist)) 13520Sstevel@tonic-gate 13530Sstevel@tonic-gate static int 13540Sstevel@tonic-gate modecmd(argc, argv) 13550Sstevel@tonic-gate int argc; 13560Sstevel@tonic-gate char *argv[]; 13570Sstevel@tonic-gate { 13580Sstevel@tonic-gate struct modelist *mt; 13590Sstevel@tonic-gate 13600Sstevel@tonic-gate if (argc != 2) { 13610Sstevel@tonic-gate (void) printf("'mode' command requires an argument\n"); 13620Sstevel@tonic-gate (void) printf("'mode ?' for help.\n"); 13630Sstevel@tonic-gate } else if ((mt = GETMODECMD(argv[1])) == 0) { 13640Sstevel@tonic-gate (void) fprintf(stderr, 13650Sstevel@tonic-gate "Unknown mode '%s' ('mode ?' for help).\n", argv[1]); 13660Sstevel@tonic-gate } else if (Ambiguous(mt)) { 13670Sstevel@tonic-gate (void) fprintf(stderr, 13680Sstevel@tonic-gate "Ambiguous mode '%s' ('mode ?' for help).\n", argv[1]); 13690Sstevel@tonic-gate } else if (mt->needconnect && !connected) { 13700Sstevel@tonic-gate (void) printf("?Need to be connected first.\n"); 13710Sstevel@tonic-gate (void) printf("'mode ?' for help.\n"); 13720Sstevel@tonic-gate } else if (mt->handler) { 13730Sstevel@tonic-gate return (*mt->handler)(mt->arg1); 13740Sstevel@tonic-gate } 13750Sstevel@tonic-gate return (0); 13760Sstevel@tonic-gate } 13770Sstevel@tonic-gate 13780Sstevel@tonic-gate /* 13790Sstevel@tonic-gate * The following data structures and routines implement the 13800Sstevel@tonic-gate * "display" command. 13810Sstevel@tonic-gate */ 13820Sstevel@tonic-gate 13830Sstevel@tonic-gate static int 13840Sstevel@tonic-gate display(argc, argv) 13850Sstevel@tonic-gate int argc; 13860Sstevel@tonic-gate char *argv[]; 13870Sstevel@tonic-gate { 13880Sstevel@tonic-gate struct togglelist *tl; 13890Sstevel@tonic-gate struct setlist *sl; 13900Sstevel@tonic-gate 13910Sstevel@tonic-gate #define dotog(tl) if (tl->variable && tl->actionexplanation) { \ 13920Sstevel@tonic-gate if (*tl->variable) { \ 13930Sstevel@tonic-gate (void) printf("will"); \ 13940Sstevel@tonic-gate } else { \ 13950Sstevel@tonic-gate (void) printf("won't"); \ 13960Sstevel@tonic-gate } \ 13970Sstevel@tonic-gate (void) printf(" %s.\n", tl->actionexplanation); \ 13980Sstevel@tonic-gate } 13990Sstevel@tonic-gate 14000Sstevel@tonic-gate #define doset(sl) if (sl->name && *sl->name != ' ') { \ 14010Sstevel@tonic-gate if (sl->handler == 0) \ 14020Sstevel@tonic-gate (void) printf("%-15s [%s]\n", sl->name, \ 14030Sstevel@tonic-gate control(*sl->charp)); \ 14040Sstevel@tonic-gate else \ 14050Sstevel@tonic-gate (void) printf("%-15s \"%s\"\n", sl->name, \ 14060Sstevel@tonic-gate (char *)sl->charp); \ 14070Sstevel@tonic-gate } 14080Sstevel@tonic-gate 14090Sstevel@tonic-gate if (argc == 1) { 14100Sstevel@tonic-gate for (tl = Togglelist; tl->name; tl++) { 14110Sstevel@tonic-gate dotog(tl); 14120Sstevel@tonic-gate } 14130Sstevel@tonic-gate (void) printf("\n"); 14140Sstevel@tonic-gate for (sl = Setlist; sl->name; sl++) { 14150Sstevel@tonic-gate doset(sl); 14160Sstevel@tonic-gate } 14170Sstevel@tonic-gate } else { 14180Sstevel@tonic-gate int i; 14190Sstevel@tonic-gate 14200Sstevel@tonic-gate for (i = 1; i < argc; i++) { 14210Sstevel@tonic-gate sl = getset(argv[i]); 14220Sstevel@tonic-gate tl = GETTOGGLE(argv[i]); 14230Sstevel@tonic-gate if (Ambiguous(sl) || Ambiguous(tl)) { 14240Sstevel@tonic-gate (void) printf( 14250Sstevel@tonic-gate "?Ambiguous argument '%s'.\n", argv[i]); 14260Sstevel@tonic-gate return (0); 14270Sstevel@tonic-gate } else if (!sl && !tl) { 14280Sstevel@tonic-gate (void) printf( 14290Sstevel@tonic-gate "?Unknown argument '%s'.\n", argv[i]); 14300Sstevel@tonic-gate return (0); 14310Sstevel@tonic-gate } else { 14320Sstevel@tonic-gate if (tl) { 14330Sstevel@tonic-gate dotog(tl); 14340Sstevel@tonic-gate } 14350Sstevel@tonic-gate if (sl) { 14360Sstevel@tonic-gate doset(sl); 14370Sstevel@tonic-gate } 14380Sstevel@tonic-gate } 14390Sstevel@tonic-gate } 14400Sstevel@tonic-gate } 14410Sstevel@tonic-gate optionstatus(); 14420Sstevel@tonic-gate (void) EncryptStatus(); 14430Sstevel@tonic-gate return (1); 14440Sstevel@tonic-gate #undef doset 14450Sstevel@tonic-gate #undef dotog 14460Sstevel@tonic-gate } 14470Sstevel@tonic-gate 14480Sstevel@tonic-gate /* 14490Sstevel@tonic-gate * The following are the data structures, and many of the routines, 14500Sstevel@tonic-gate * relating to command processing. 14510Sstevel@tonic-gate */ 14520Sstevel@tonic-gate 14530Sstevel@tonic-gate /* 14540Sstevel@tonic-gate * Set the escape character. 14550Sstevel@tonic-gate */ 14560Sstevel@tonic-gate static int 14570Sstevel@tonic-gate setescape(argc, argv) 14580Sstevel@tonic-gate int argc; 14590Sstevel@tonic-gate char *argv[]; 14600Sstevel@tonic-gate { 14610Sstevel@tonic-gate register char *arg; 14620Sstevel@tonic-gate char *buf = NULL; 14630Sstevel@tonic-gate 14640Sstevel@tonic-gate if (argc > 2) 14650Sstevel@tonic-gate arg = argv[1]; 14660Sstevel@tonic-gate else { 14670Sstevel@tonic-gate (void) printf("new escape character: "); 14680Sstevel@tonic-gate if (GetString(&buf, NULL, stdin) == NULL) { 14690Sstevel@tonic-gate if (!feof(stdin)) { 14700Sstevel@tonic-gate perror("can't set escape character"); 14710Sstevel@tonic-gate goto setescape_exit; 14720Sstevel@tonic-gate } 14730Sstevel@tonic-gate } 14740Sstevel@tonic-gate arg = buf; 14750Sstevel@tonic-gate } 14760Sstevel@tonic-gate /* we place no limitations on what escape can be. */ 14770Sstevel@tonic-gate escape = arg[0]; 14780Sstevel@tonic-gate (void) printf("Escape character is '%s'.\n", esc_control(escape)); 14790Sstevel@tonic-gate (void) fflush(stdout); 14800Sstevel@tonic-gate setescape_exit: 14810Sstevel@tonic-gate Free(&buf); 14820Sstevel@tonic-gate return (1); 14830Sstevel@tonic-gate } 14840Sstevel@tonic-gate 14850Sstevel@tonic-gate /*ARGSUSED*/ 14860Sstevel@tonic-gate static int 14870Sstevel@tonic-gate togcrmod(argc, argv) 14880Sstevel@tonic-gate int argc; 14890Sstevel@tonic-gate char *argv[]; 14900Sstevel@tonic-gate { 14910Sstevel@tonic-gate crmod = !crmod; 14920Sstevel@tonic-gate (void) printf( 14930Sstevel@tonic-gate "%s map carriage return on output.\n", crmod ? "Will" : "Won't"); 14940Sstevel@tonic-gate (void) fflush(stdout); 14950Sstevel@tonic-gate return (1); 14960Sstevel@tonic-gate } 14970Sstevel@tonic-gate 14980Sstevel@tonic-gate /*ARGSUSED*/ 14990Sstevel@tonic-gate static int 15000Sstevel@tonic-gate suspend(argc, argv) 15010Sstevel@tonic-gate int argc; 15020Sstevel@tonic-gate char *argv[]; 15030Sstevel@tonic-gate { 15040Sstevel@tonic-gate setcommandmode(); 15050Sstevel@tonic-gate { 15060Sstevel@tonic-gate unsigned short oldrows, oldcols, newrows, newcols; 15070Sstevel@tonic-gate int err; 15080Sstevel@tonic-gate 15090Sstevel@tonic-gate err = (TerminalWindowSize(&oldrows, &oldcols) == 0) ? 1 : 0; 15100Sstevel@tonic-gate (void) kill(0, SIGTSTP); 15110Sstevel@tonic-gate /* 15120Sstevel@tonic-gate * If we didn't get the window size before the SUSPEND, but we 15130Sstevel@tonic-gate * can get them now (?), then send the NAWS to make sure that 15140Sstevel@tonic-gate * we are set up for the right window size. 15150Sstevel@tonic-gate */ 15160Sstevel@tonic-gate if (TerminalWindowSize(&newrows, &newcols) && connected && 15170Sstevel@tonic-gate (err || ((oldrows != newrows) || (oldcols != newcols)))) { 15180Sstevel@tonic-gate sendnaws(); 15190Sstevel@tonic-gate } 15200Sstevel@tonic-gate } 15210Sstevel@tonic-gate /* reget parameters in case they were changed */ 15220Sstevel@tonic-gate TerminalSaveState(); 15230Sstevel@tonic-gate setconnmode(0); 15240Sstevel@tonic-gate return (1); 15250Sstevel@tonic-gate } 15260Sstevel@tonic-gate 15270Sstevel@tonic-gate /*ARGSUSED*/ 15280Sstevel@tonic-gate static int 15290Sstevel@tonic-gate shell(argc, argv) 15300Sstevel@tonic-gate int argc; 15310Sstevel@tonic-gate char *argv[]; 15320Sstevel@tonic-gate { 15330Sstevel@tonic-gate unsigned short oldrows, oldcols, newrows, newcols; 15340Sstevel@tonic-gate int err; 15350Sstevel@tonic-gate 15360Sstevel@tonic-gate setcommandmode(); 15370Sstevel@tonic-gate 15380Sstevel@tonic-gate err = (TerminalWindowSize(&oldrows, &oldcols) == 0) ? 1 : 0; 15390Sstevel@tonic-gate switch (vfork()) { 15400Sstevel@tonic-gate case -1: 15410Sstevel@tonic-gate perror("Fork failed\n"); 15420Sstevel@tonic-gate break; 15430Sstevel@tonic-gate 15440Sstevel@tonic-gate case 0: 15450Sstevel@tonic-gate { 15460Sstevel@tonic-gate /* 15470Sstevel@tonic-gate * Fire up the shell in the child. 15480Sstevel@tonic-gate */ 15490Sstevel@tonic-gate register char *shellp, *shellname; 15500Sstevel@tonic-gate 15510Sstevel@tonic-gate shellp = getenv("SHELL"); 15520Sstevel@tonic-gate if (shellp == NULL) 15530Sstevel@tonic-gate shellp = "/bin/sh"; 15540Sstevel@tonic-gate if ((shellname = strrchr(shellp, '/')) == 0) 15550Sstevel@tonic-gate shellname = shellp; 15560Sstevel@tonic-gate else 15570Sstevel@tonic-gate shellname++; 15580Sstevel@tonic-gate if (argc > 1) 15590Sstevel@tonic-gate (void) execl(shellp, shellname, "-c", argv[1], 0); 15600Sstevel@tonic-gate else 15610Sstevel@tonic-gate (void) execl(shellp, shellname, 0); 15620Sstevel@tonic-gate perror("Execl"); 15630Sstevel@tonic-gate _exit(EXIT_FAILURE); 15640Sstevel@tonic-gate } 15650Sstevel@tonic-gate default: 15660Sstevel@tonic-gate (void) wait((int *)0); /* Wait for the shell to complete */ 15670Sstevel@tonic-gate 15680Sstevel@tonic-gate if (TerminalWindowSize(&newrows, &newcols) && connected && 15690Sstevel@tonic-gate (err || ((oldrows != newrows) || (oldcols != newcols)))) { 15700Sstevel@tonic-gate sendnaws(); 15710Sstevel@tonic-gate } 15720Sstevel@tonic-gate break; 15730Sstevel@tonic-gate } 15740Sstevel@tonic-gate return (1); 15750Sstevel@tonic-gate } 15760Sstevel@tonic-gate 15770Sstevel@tonic-gate static int 15780Sstevel@tonic-gate bye(argc, argv) 15790Sstevel@tonic-gate int argc; /* Number of arguments */ 15800Sstevel@tonic-gate char *argv[]; /* arguments */ 15810Sstevel@tonic-gate { 15820Sstevel@tonic-gate extern int resettermname; 15830Sstevel@tonic-gate 15840Sstevel@tonic-gate if (connected) { 15850Sstevel@tonic-gate (void) shutdown(net, 2); 15860Sstevel@tonic-gate (void) printf("Connection to %.*s closed.\n", MAXHOSTNAMELEN, 15870Sstevel@tonic-gate hostname); 15880Sstevel@tonic-gate Close(&net); 15890Sstevel@tonic-gate connected = 0; 15900Sstevel@tonic-gate resettermname = 1; 15910Sstevel@tonic-gate /* reset options */ 15920Sstevel@tonic-gate (void) tninit(); 15930Sstevel@tonic-gate } 15940Sstevel@tonic-gate if ((argc != 2) || (strcmp(argv[1], "fromquit") != 0)) { 15950Sstevel@tonic-gate longjmp(toplevel, 1); 15960Sstevel@tonic-gate /* NOTREACHED */ 15970Sstevel@tonic-gate } 15980Sstevel@tonic-gate return (1); /* Keep lint, etc., happy */ 15990Sstevel@tonic-gate } 16000Sstevel@tonic-gate 16010Sstevel@tonic-gate /*VARARGS*/ 16020Sstevel@tonic-gate int 16030Sstevel@tonic-gate quit() 16040Sstevel@tonic-gate { 16050Sstevel@tonic-gate (void) call(3, bye, "bye", "fromquit"); 16060Sstevel@tonic-gate Exit(EXIT_SUCCESS); 16070Sstevel@tonic-gate /*NOTREACHED*/ 16080Sstevel@tonic-gate return (1); 16090Sstevel@tonic-gate } 16100Sstevel@tonic-gate 16110Sstevel@tonic-gate /*ARGSUSED*/ 16120Sstevel@tonic-gate static int 16130Sstevel@tonic-gate logout(argc, argv) 16140Sstevel@tonic-gate int argc; 16150Sstevel@tonic-gate char *argv[]; 16160Sstevel@tonic-gate { 16170Sstevel@tonic-gate send_do(TELOPT_LOGOUT, 1); 16180Sstevel@tonic-gate (void) netflush(); 16190Sstevel@tonic-gate return (1); 16200Sstevel@tonic-gate } 16210Sstevel@tonic-gate 16220Sstevel@tonic-gate 16230Sstevel@tonic-gate /* 16240Sstevel@tonic-gate * The SLC command. 16250Sstevel@tonic-gate */ 16260Sstevel@tonic-gate 16270Sstevel@tonic-gate struct slclist { 16280Sstevel@tonic-gate char *name; 16290Sstevel@tonic-gate char *help; 16300Sstevel@tonic-gate void (*handler)(); 16310Sstevel@tonic-gate int arg; 16320Sstevel@tonic-gate }; 16330Sstevel@tonic-gate 16340Sstevel@tonic-gate static void slc_help(); 16350Sstevel@tonic-gate 16360Sstevel@tonic-gate static struct slclist SlcList[] = { 16370Sstevel@tonic-gate { "export", "Use local special character definitions", 16380Sstevel@tonic-gate slc_mode_export, 0 }, 16390Sstevel@tonic-gate { "import", "Use remote special character definitions", 16400Sstevel@tonic-gate slc_mode_import, 1 }, 16410Sstevel@tonic-gate { "check", "Verify remote special character definitions", 16420Sstevel@tonic-gate slc_mode_import, 0 }, 16430Sstevel@tonic-gate { "help", 0, slc_help, 0 }, 16440Sstevel@tonic-gate { "?", "Print help information", slc_help, 0 }, 16450Sstevel@tonic-gate { 0 }, 16460Sstevel@tonic-gate }; 16470Sstevel@tonic-gate 16480Sstevel@tonic-gate static void 16490Sstevel@tonic-gate slc_help() 16500Sstevel@tonic-gate { 16510Sstevel@tonic-gate struct slclist *c; 16520Sstevel@tonic-gate 16530Sstevel@tonic-gate for (c = SlcList; c->name; c++) { 16540Sstevel@tonic-gate if (c->help) { 16550Sstevel@tonic-gate if (*c->help) 16560Sstevel@tonic-gate (void) printf("%-15s %s\n", c->name, c->help); 16570Sstevel@tonic-gate else 16580Sstevel@tonic-gate (void) printf("\n"); 16590Sstevel@tonic-gate } 16600Sstevel@tonic-gate } 16610Sstevel@tonic-gate } 16620Sstevel@tonic-gate 16630Sstevel@tonic-gate static struct slclist * 16640Sstevel@tonic-gate getslc(name) 16650Sstevel@tonic-gate char *name; 16660Sstevel@tonic-gate { 16670Sstevel@tonic-gate return ((struct slclist *) 16680Sstevel@tonic-gate genget(name, (char **)SlcList, sizeof (struct slclist))); 16690Sstevel@tonic-gate } 16700Sstevel@tonic-gate 1671*473Sbw static int 16720Sstevel@tonic-gate slccmd(argc, argv) 16730Sstevel@tonic-gate int argc; 16740Sstevel@tonic-gate char *argv[]; 16750Sstevel@tonic-gate { 16760Sstevel@tonic-gate struct slclist *c; 16770Sstevel@tonic-gate 16780Sstevel@tonic-gate if (argc != 2) { 16790Sstevel@tonic-gate (void) fprintf(stderr, 16800Sstevel@tonic-gate "Need an argument to 'slc' command. 'slc ?' for help.\n"); 16810Sstevel@tonic-gate return (0); 16820Sstevel@tonic-gate } 16830Sstevel@tonic-gate c = getslc(argv[1]); 16840Sstevel@tonic-gate if (c == 0) { 16850Sstevel@tonic-gate (void) fprintf(stderr, 16860Sstevel@tonic-gate "'%s': unknown argument ('slc ?' for help).\n", 16870Sstevel@tonic-gate argv[1]); 16880Sstevel@tonic-gate return (0); 16890Sstevel@tonic-gate } 16900Sstevel@tonic-gate if (Ambiguous(c)) { 16910Sstevel@tonic-gate (void) fprintf(stderr, 16920Sstevel@tonic-gate "'%s': ambiguous argument ('slc ?' for help).\n", argv[1]); 16930Sstevel@tonic-gate return (0); 16940Sstevel@tonic-gate } 16950Sstevel@tonic-gate (*c->handler)(c->arg); 16960Sstevel@tonic-gate slcstate(); 16970Sstevel@tonic-gate return (1); 16980Sstevel@tonic-gate } 16990Sstevel@tonic-gate 17000Sstevel@tonic-gate /* 17010Sstevel@tonic-gate * The ENVIRON command. 17020Sstevel@tonic-gate */ 17030Sstevel@tonic-gate 17040Sstevel@tonic-gate struct envlist { 17050Sstevel@tonic-gate char *name; 17060Sstevel@tonic-gate char *help; 17070Sstevel@tonic-gate void (*handler)(); 17080Sstevel@tonic-gate int narg; 17090Sstevel@tonic-gate }; 17100Sstevel@tonic-gate 17110Sstevel@tonic-gate static struct env_lst *env_define(unsigned char *, unsigned char *); 17120Sstevel@tonic-gate static void env_undefine(unsigned char *); 17130Sstevel@tonic-gate static void env_export(unsigned char *); 17140Sstevel@tonic-gate static void env_unexport(unsigned char *); 17150Sstevel@tonic-gate static void env_send(unsigned char *); 17160Sstevel@tonic-gate #if defined(OLD_ENVIRON) && defined(ENV_HACK) 17170Sstevel@tonic-gate static void env_varval(unsigned char *); 17180Sstevel@tonic-gate #endif 17190Sstevel@tonic-gate static void env_list(void); 17200Sstevel@tonic-gate 17210Sstevel@tonic-gate static void env_help(void); 17220Sstevel@tonic-gate 17230Sstevel@tonic-gate static struct envlist EnvList[] = { 17240Sstevel@tonic-gate { "define", "Define an environment variable", 17250Sstevel@tonic-gate (void (*)())env_define, 2 }, 17260Sstevel@tonic-gate { "undefine", "Undefine an environment variable", 17270Sstevel@tonic-gate env_undefine, 1 }, 17280Sstevel@tonic-gate { "export", "Mark an environment variable for automatic export", 17290Sstevel@tonic-gate env_export, 1 }, 17300Sstevel@tonic-gate { "unexport", "Don't mark an environment variable for automatic export", 17310Sstevel@tonic-gate env_unexport, 1 }, 17320Sstevel@tonic-gate { "send", "Send an environment variable", env_send, 1 }, 17330Sstevel@tonic-gate { "list", "List the current environment variables", 17340Sstevel@tonic-gate env_list, 0 }, 17350Sstevel@tonic-gate #if defined(OLD_ENVIRON) && defined(ENV_HACK) 17360Sstevel@tonic-gate { "varval", "Reverse VAR and VALUE (auto, right, wrong, status)", 17370Sstevel@tonic-gate env_varval, 1 }, 17380Sstevel@tonic-gate #endif 17390Sstevel@tonic-gate { "help", 0, env_help, 0 }, 17400Sstevel@tonic-gate { "?", "Print help information", env_help, 0 }, 17410Sstevel@tonic-gate { 0 }, 17420Sstevel@tonic-gate }; 17430Sstevel@tonic-gate 17440Sstevel@tonic-gate static void 17450Sstevel@tonic-gate env_help() 17460Sstevel@tonic-gate { 17470Sstevel@tonic-gate struct envlist *c; 17480Sstevel@tonic-gate 17490Sstevel@tonic-gate for (c = EnvList; c->name; c++) { 17500Sstevel@tonic-gate if (c->help) { 17510Sstevel@tonic-gate if (*c->help) 17520Sstevel@tonic-gate (void) printf("%-15s %s\n", c->name, c->help); 17530Sstevel@tonic-gate else 17540Sstevel@tonic-gate (void) printf("\n"); 17550Sstevel@tonic-gate } 17560Sstevel@tonic-gate } 17570Sstevel@tonic-gate } 17580Sstevel@tonic-gate 17590Sstevel@tonic-gate static struct envlist * 17600Sstevel@tonic-gate getenvcmd(name) 17610Sstevel@tonic-gate char *name; 17620Sstevel@tonic-gate { 17630Sstevel@tonic-gate return ((struct envlist *) 17640Sstevel@tonic-gate genget(name, (char **)EnvList, sizeof (struct envlist))); 17650Sstevel@tonic-gate } 17660Sstevel@tonic-gate 17670Sstevel@tonic-gate static int 17680Sstevel@tonic-gate env_cmd(argc, argv) 17690Sstevel@tonic-gate int argc; 17700Sstevel@tonic-gate char *argv[]; 17710Sstevel@tonic-gate { 17720Sstevel@tonic-gate struct envlist *c; 17730Sstevel@tonic-gate 17740Sstevel@tonic-gate if (argc < 2) { 17750Sstevel@tonic-gate (void) fprintf(stderr, 17760Sstevel@tonic-gate "Need an argument to 'environ' command. " 17770Sstevel@tonic-gate "'environ ?' for help.\n"); 17780Sstevel@tonic-gate return (0); 17790Sstevel@tonic-gate } 17800Sstevel@tonic-gate c = getenvcmd(argv[1]); 17810Sstevel@tonic-gate if (c == 0) { 17820Sstevel@tonic-gate (void) fprintf(stderr, "'%s': unknown argument " 17830Sstevel@tonic-gate "('environ ?' for help).\n", argv[1]); 17840Sstevel@tonic-gate return (0); 17850Sstevel@tonic-gate } 17860Sstevel@tonic-gate if (Ambiguous(c)) { 17870Sstevel@tonic-gate (void) fprintf(stderr, "'%s': ambiguous argument " 17880Sstevel@tonic-gate "('environ ?' for help).\n", argv[1]); 17890Sstevel@tonic-gate return (0); 17900Sstevel@tonic-gate } 17910Sstevel@tonic-gate if (c->narg + 2 != argc) { 17920Sstevel@tonic-gate (void) fprintf(stderr, 17930Sstevel@tonic-gate "Need %s%d argument%s to 'environ %s' command. " 17940Sstevel@tonic-gate "'environ ?' for help.\n", 17950Sstevel@tonic-gate c->narg + 2 < argc ? "only " : "", 17960Sstevel@tonic-gate c->narg, c->narg == 1 ? "" : "s", c->name); 17970Sstevel@tonic-gate return (0); 17980Sstevel@tonic-gate } 17990Sstevel@tonic-gate (*c->handler)(argv[2], argv[3]); 18000Sstevel@tonic-gate return (1); 18010Sstevel@tonic-gate } 18020Sstevel@tonic-gate 18030Sstevel@tonic-gate struct env_lst { 18040Sstevel@tonic-gate struct env_lst *next; /* pointer to next structure */ 18050Sstevel@tonic-gate struct env_lst *prev; /* pointer to previous structure */ 18060Sstevel@tonic-gate unsigned char *var; /* pointer to variable name */ 18070Sstevel@tonic-gate unsigned char *value; /* pointer to variable value */ 18080Sstevel@tonic-gate int export; /* 1 -> export with default list of variables */ 18090Sstevel@tonic-gate int welldefined; /* A well defined variable */ 18100Sstevel@tonic-gate }; 18110Sstevel@tonic-gate 18120Sstevel@tonic-gate static struct env_lst envlisthead; 18130Sstevel@tonic-gate 18140Sstevel@tonic-gate static struct env_lst * 18150Sstevel@tonic-gate env_find(var) 18160Sstevel@tonic-gate unsigned char *var; 18170Sstevel@tonic-gate { 18180Sstevel@tonic-gate register struct env_lst *ep; 18190Sstevel@tonic-gate 18200Sstevel@tonic-gate for (ep = envlisthead.next; ep; ep = ep->next) { 18210Sstevel@tonic-gate if (strcmp((char *)ep->var, (char *)var) == 0) 18220Sstevel@tonic-gate return (ep); 18230Sstevel@tonic-gate } 18240Sstevel@tonic-gate return (NULL); 18250Sstevel@tonic-gate } 18260Sstevel@tonic-gate 18270Sstevel@tonic-gate int 18280Sstevel@tonic-gate env_init() 18290Sstevel@tonic-gate { 18300Sstevel@tonic-gate #ifdef lint 18310Sstevel@tonic-gate char **environ = NULL; 18320Sstevel@tonic-gate #else /* lint */ 18330Sstevel@tonic-gate extern char **environ; 18340Sstevel@tonic-gate #endif /* lint */ 18350Sstevel@tonic-gate char **epp, *cp; 18360Sstevel@tonic-gate struct env_lst *ep; 18370Sstevel@tonic-gate 18380Sstevel@tonic-gate for (epp = environ; *epp; epp++) { 18390Sstevel@tonic-gate if (cp = strchr(*epp, '=')) { 18400Sstevel@tonic-gate *cp = '\0'; 18410Sstevel@tonic-gate 18420Sstevel@tonic-gate ep = env_define((unsigned char *)*epp, 18430Sstevel@tonic-gate (unsigned char *)cp+1); 18440Sstevel@tonic-gate if (ep == NULL) 18450Sstevel@tonic-gate return (0); 18460Sstevel@tonic-gate ep->export = 0; 18470Sstevel@tonic-gate *cp = '='; 18480Sstevel@tonic-gate } 18490Sstevel@tonic-gate } 18500Sstevel@tonic-gate /* 18510Sstevel@tonic-gate * Special case for DISPLAY variable. If it is ":0.0" or 18520Sstevel@tonic-gate * "unix:0.0", we have to get rid of "unix" and insert our 18530Sstevel@tonic-gate * hostname. 18540Sstevel@tonic-gate */ 18550Sstevel@tonic-gate if (((ep = env_find((uchar_t *)"DISPLAY")) != NULL) && 18560Sstevel@tonic-gate ((*ep->value == ':') || 18570Sstevel@tonic-gate (strncmp((char *)ep->value, "unix:", 5) == 0))) { 18580Sstevel@tonic-gate char hbuf[MAXHOSTNAMELEN]; 18590Sstevel@tonic-gate char *cp2 = strchr((char *)ep->value, ':'); 18600Sstevel@tonic-gate 18610Sstevel@tonic-gate if (gethostname(hbuf, MAXHOSTNAMELEN) == -1) { 18620Sstevel@tonic-gate perror("telnet: cannot get hostname"); 18630Sstevel@tonic-gate return (0); 18640Sstevel@tonic-gate } 18650Sstevel@tonic-gate hbuf[MAXHOSTNAMELEN-1] = '\0'; 18660Sstevel@tonic-gate cp = malloc(strlen(hbuf) + strlen(cp2) + 1); 18670Sstevel@tonic-gate if (cp == NULL) { 18680Sstevel@tonic-gate perror("telnet: cannot define DISPLAY variable"); 18690Sstevel@tonic-gate return (0); 18700Sstevel@tonic-gate } 18710Sstevel@tonic-gate (void) sprintf((char *)cp, "%s%s", hbuf, cp2); 18720Sstevel@tonic-gate free(ep->value); 18730Sstevel@tonic-gate ep->value = (unsigned char *)cp; 18740Sstevel@tonic-gate } 18750Sstevel@tonic-gate /* 18760Sstevel@tonic-gate * If LOGNAME is defined, but USER is not, then add 18770Sstevel@tonic-gate * USER with the value from LOGNAME. We do this because the "accepted 18780Sstevel@tonic-gate * practice" is to always pass USER on the wire, but SVR4 uses 18790Sstevel@tonic-gate * LOGNAME by default. 18800Sstevel@tonic-gate */ 18810Sstevel@tonic-gate if ((ep = env_find((uchar_t *)"LOGNAME")) != NULL && 18820Sstevel@tonic-gate env_find((uchar_t *)"USER") == NULL) { 18830Sstevel@tonic-gate if (env_define((unsigned char *)"USER", ep->value) != NULL) 18840Sstevel@tonic-gate env_unexport((unsigned char *)"USER"); 18850Sstevel@tonic-gate } 18860Sstevel@tonic-gate env_export((unsigned char *)"DISPLAY"); 18870Sstevel@tonic-gate env_export((unsigned char *)"PRINTER"); 18880Sstevel@tonic-gate 18890Sstevel@tonic-gate return (1); 18900Sstevel@tonic-gate } 18910Sstevel@tonic-gate 18920Sstevel@tonic-gate static struct env_lst * 18930Sstevel@tonic-gate env_define(var, value) 18940Sstevel@tonic-gate unsigned char *var, *value; 18950Sstevel@tonic-gate { 18960Sstevel@tonic-gate unsigned char *tmp_value; 18970Sstevel@tonic-gate unsigned char *tmp_var; 18980Sstevel@tonic-gate struct env_lst *ep; 18990Sstevel@tonic-gate 19000Sstevel@tonic-gate /* 19010Sstevel@tonic-gate * Allocate copies of arguments first, to make cleanup easier 19020Sstevel@tonic-gate * in the case of allocation errors. 19030Sstevel@tonic-gate */ 19040Sstevel@tonic-gate tmp_var = (unsigned char *)strdup((char *)var); 19050Sstevel@tonic-gate if (tmp_var == NULL) { 19060Sstevel@tonic-gate perror("telnet: can't copy environment variable name"); 19070Sstevel@tonic-gate return (NULL); 19080Sstevel@tonic-gate } 19090Sstevel@tonic-gate 19100Sstevel@tonic-gate tmp_value = (unsigned char *)strdup((char *)value); 19110Sstevel@tonic-gate if (tmp_value == NULL) { 19120Sstevel@tonic-gate free(tmp_var); 19130Sstevel@tonic-gate perror("telnet: can't copy environment variable value"); 19140Sstevel@tonic-gate return (NULL); 19150Sstevel@tonic-gate } 19160Sstevel@tonic-gate 19170Sstevel@tonic-gate if (ep = env_find(var)) { 19180Sstevel@tonic-gate if (ep->var) 19190Sstevel@tonic-gate free(ep->var); 19200Sstevel@tonic-gate if (ep->value) 19210Sstevel@tonic-gate free(ep->value); 19220Sstevel@tonic-gate } else { 19230Sstevel@tonic-gate ep = malloc(sizeof (struct env_lst)); 19240Sstevel@tonic-gate if (ep == NULL) { 19250Sstevel@tonic-gate perror("telnet: can't define environment variable"); 19260Sstevel@tonic-gate free(tmp_var); 19270Sstevel@tonic-gate free(tmp_value); 19280Sstevel@tonic-gate return (NULL); 19290Sstevel@tonic-gate } 19300Sstevel@tonic-gate 19310Sstevel@tonic-gate ep->next = envlisthead.next; 19320Sstevel@tonic-gate envlisthead.next = ep; 19330Sstevel@tonic-gate ep->prev = &envlisthead; 19340Sstevel@tonic-gate if (ep->next) 19350Sstevel@tonic-gate ep->next->prev = ep; 19360Sstevel@tonic-gate } 19370Sstevel@tonic-gate ep->welldefined = opt_welldefined((char *)var); 19380Sstevel@tonic-gate ep->export = 1; 19390Sstevel@tonic-gate ep->var = tmp_var; 19400Sstevel@tonic-gate ep->value = tmp_value; 19410Sstevel@tonic-gate 19420Sstevel@tonic-gate return (ep); 19430Sstevel@tonic-gate } 19440Sstevel@tonic-gate 19450Sstevel@tonic-gate static void 19460Sstevel@tonic-gate env_undefine(var) 19470Sstevel@tonic-gate unsigned char *var; 19480Sstevel@tonic-gate { 19490Sstevel@tonic-gate register struct env_lst *ep; 19500Sstevel@tonic-gate 19510Sstevel@tonic-gate if (ep = env_find(var)) { 19520Sstevel@tonic-gate ep->prev->next = ep->next; 19530Sstevel@tonic-gate if (ep->next) 19540Sstevel@tonic-gate ep->next->prev = ep->prev; 19550Sstevel@tonic-gate if (ep->var) 19560Sstevel@tonic-gate free(ep->var); 19570Sstevel@tonic-gate if (ep->value) 19580Sstevel@tonic-gate free(ep->value); 19590Sstevel@tonic-gate free(ep); 19600Sstevel@tonic-gate } 19610Sstevel@tonic-gate } 19620Sstevel@tonic-gate 19630Sstevel@tonic-gate static void 19640Sstevel@tonic-gate env_export(var) 19650Sstevel@tonic-gate unsigned char *var; 19660Sstevel@tonic-gate { 19670Sstevel@tonic-gate register struct env_lst *ep; 19680Sstevel@tonic-gate 19690Sstevel@tonic-gate if (ep = env_find(var)) 19700Sstevel@tonic-gate ep->export = 1; 19710Sstevel@tonic-gate } 19720Sstevel@tonic-gate 19730Sstevel@tonic-gate static void 19740Sstevel@tonic-gate env_unexport(var) 19750Sstevel@tonic-gate unsigned char *var; 19760Sstevel@tonic-gate { 19770Sstevel@tonic-gate register struct env_lst *ep; 19780Sstevel@tonic-gate 19790Sstevel@tonic-gate if (ep = env_find(var)) 19800Sstevel@tonic-gate ep->export = 0; 19810Sstevel@tonic-gate } 19820Sstevel@tonic-gate 19830Sstevel@tonic-gate static void 19840Sstevel@tonic-gate env_send(var) 19850Sstevel@tonic-gate unsigned char *var; 19860Sstevel@tonic-gate { 19870Sstevel@tonic-gate register struct env_lst *ep; 19880Sstevel@tonic-gate 19890Sstevel@tonic-gate if (my_state_is_wont(TELOPT_NEW_ENVIRON) 19900Sstevel@tonic-gate #ifdef OLD_ENVIRON 19910Sstevel@tonic-gate /* old style */ && my_state_is_wont(TELOPT_OLD_ENVIRON) 19920Sstevel@tonic-gate #endif 19930Sstevel@tonic-gate /* no environ */) { 19940Sstevel@tonic-gate (void) fprintf(stderr, 19950Sstevel@tonic-gate "Cannot send '%s': Telnet ENVIRON option not enabled\n", 19960Sstevel@tonic-gate var); 19970Sstevel@tonic-gate return; 19980Sstevel@tonic-gate } 19990Sstevel@tonic-gate ep = env_find(var); 20000Sstevel@tonic-gate if (ep == 0) { 20010Sstevel@tonic-gate (void) fprintf(stderr, 20020Sstevel@tonic-gate "Cannot send '%s': variable not defined\n", var); 20030Sstevel@tonic-gate return; 20040Sstevel@tonic-gate } 20050Sstevel@tonic-gate env_opt_start_info(); 20060Sstevel@tonic-gate env_opt_add(ep->var); 20070Sstevel@tonic-gate env_opt_end(0); 20080Sstevel@tonic-gate } 20090Sstevel@tonic-gate 20100Sstevel@tonic-gate static void 20110Sstevel@tonic-gate env_list() 20120Sstevel@tonic-gate { 20130Sstevel@tonic-gate register struct env_lst *ep; 20140Sstevel@tonic-gate 20150Sstevel@tonic-gate for (ep = envlisthead.next; ep; ep = ep->next) { 20160Sstevel@tonic-gate (void) printf("%c %-20s %s\n", ep->export ? '*' : ' ', 20170Sstevel@tonic-gate ep->var, ep->value); 20180Sstevel@tonic-gate } 20190Sstevel@tonic-gate } 20200Sstevel@tonic-gate 20210Sstevel@tonic-gate unsigned char * 20220Sstevel@tonic-gate env_default(init, welldefined) 20230Sstevel@tonic-gate int init; 20240Sstevel@tonic-gate { 20250Sstevel@tonic-gate static struct env_lst *nep = NULL; 20260Sstevel@tonic-gate 20270Sstevel@tonic-gate if (init) { 20280Sstevel@tonic-gate /* return value is not used */ 20290Sstevel@tonic-gate nep = &envlisthead; 20300Sstevel@tonic-gate return (NULL); 20310Sstevel@tonic-gate } 20320Sstevel@tonic-gate if (nep) { 20330Sstevel@tonic-gate while ((nep = nep->next) != NULL) { 20340Sstevel@tonic-gate if (nep->export && (nep->welldefined == welldefined)) 20350Sstevel@tonic-gate return (nep->var); 20360Sstevel@tonic-gate } 20370Sstevel@tonic-gate } 20380Sstevel@tonic-gate return (NULL); 20390Sstevel@tonic-gate } 20400Sstevel@tonic-gate 20410Sstevel@tonic-gate unsigned char * 20420Sstevel@tonic-gate env_getvalue(var) 20430Sstevel@tonic-gate unsigned char *var; 20440Sstevel@tonic-gate { 20450Sstevel@tonic-gate register struct env_lst *ep; 20460Sstevel@tonic-gate 20470Sstevel@tonic-gate if (ep = env_find(var)) 20480Sstevel@tonic-gate return (ep->value); 20490Sstevel@tonic-gate return (NULL); 20500Sstevel@tonic-gate } 20510Sstevel@tonic-gate 20520Sstevel@tonic-gate #if defined(OLD_ENVIRON) && defined(ENV_HACK) 20530Sstevel@tonic-gate static void 20540Sstevel@tonic-gate env_varval(what) 20550Sstevel@tonic-gate unsigned char *what; 20560Sstevel@tonic-gate { 20570Sstevel@tonic-gate extern int old_env_var, old_env_value, env_auto; 20580Sstevel@tonic-gate int len = strlen((char *)what); 20590Sstevel@tonic-gate 20600Sstevel@tonic-gate if (len == 0) 20610Sstevel@tonic-gate goto unknown; 20620Sstevel@tonic-gate 20630Sstevel@tonic-gate if (strncasecmp((char *)what, "status", len) == 0) { 20640Sstevel@tonic-gate if (env_auto) 20650Sstevel@tonic-gate (void) printf("%s%s", "VAR and VALUE are/will be ", 20660Sstevel@tonic-gate "determined automatically\n"); 20670Sstevel@tonic-gate if (old_env_var == OLD_ENV_VAR) 20680Sstevel@tonic-gate (void) printf( 20690Sstevel@tonic-gate "VAR and VALUE set to correct definitions\n"); 20700Sstevel@tonic-gate else 20710Sstevel@tonic-gate (void) printf( 20720Sstevel@tonic-gate "VAR and VALUE definitions are reversed\n"); 20730Sstevel@tonic-gate } else if (strncasecmp((char *)what, "auto", len) == 0) { 20740Sstevel@tonic-gate env_auto = 1; 20750Sstevel@tonic-gate old_env_var = OLD_ENV_VALUE; 20760Sstevel@tonic-gate old_env_value = OLD_ENV_VAR; 20770Sstevel@tonic-gate } else if (strncasecmp((char *)what, "right", len) == 0) { 20780Sstevel@tonic-gate env_auto = 0; 20790Sstevel@tonic-gate old_env_var = OLD_ENV_VAR; 20800Sstevel@tonic-gate old_env_value = OLD_ENV_VALUE; 20810Sstevel@tonic-gate } else if (strncasecmp((char *)what, "wrong", len) == 0) { 20820Sstevel@tonic-gate env_auto = 0; 20830Sstevel@tonic-gate old_env_var = OLD_ENV_VALUE; 20840Sstevel@tonic-gate old_env_value = OLD_ENV_VAR; 20850Sstevel@tonic-gate } else { 20860Sstevel@tonic-gate unknown: 20870Sstevel@tonic-gate (void) printf( 20880Sstevel@tonic-gate "Unknown \"varval\" command. (\"auto\", \"right\", " 20890Sstevel@tonic-gate "\"wrong\", \"status\")\n"); 20900Sstevel@tonic-gate } 20910Sstevel@tonic-gate } 20920Sstevel@tonic-gate #endif /* OLD_ENVIRON && ENV_HACK */ 20930Sstevel@tonic-gate 20940Sstevel@tonic-gate /* 20950Sstevel@tonic-gate * The AUTHENTICATE command. 20960Sstevel@tonic-gate */ 20970Sstevel@tonic-gate 20980Sstevel@tonic-gate struct authlist { 20990Sstevel@tonic-gate char *name; 21000Sstevel@tonic-gate char *help; 21010Sstevel@tonic-gate int (*handler)(); 21020Sstevel@tonic-gate int narg; 21030Sstevel@tonic-gate }; 21040Sstevel@tonic-gate 21050Sstevel@tonic-gate extern int auth_enable(char *); 21060Sstevel@tonic-gate extern int auth_disable(char *); 21070Sstevel@tonic-gate extern int auth_status(void); 21080Sstevel@tonic-gate 21090Sstevel@tonic-gate static int auth_help(void); 21100Sstevel@tonic-gate 21110Sstevel@tonic-gate static struct authlist AuthList[] = { 21120Sstevel@tonic-gate { "status", 21130Sstevel@tonic-gate "Display current status of authentication information", 21140Sstevel@tonic-gate auth_status, 0 }, 21150Sstevel@tonic-gate { "disable", 21160Sstevel@tonic-gate "Disable an authentication type ('auth disable ?' for more)", 21170Sstevel@tonic-gate auth_disable, 1 }, 21180Sstevel@tonic-gate { "enable", 21190Sstevel@tonic-gate "Enable an authentication type ('auth enable ?' for more)", 21200Sstevel@tonic-gate auth_enable, 1 }, 21210Sstevel@tonic-gate { "help", 0, auth_help, 0 }, 21220Sstevel@tonic-gate { "?", "Print help information", auth_help, 0 }, 21230Sstevel@tonic-gate { 0 }, 21240Sstevel@tonic-gate }; 21250Sstevel@tonic-gate 21260Sstevel@tonic-gate static int 21270Sstevel@tonic-gate auth_help(void) 21280Sstevel@tonic-gate { 21290Sstevel@tonic-gate struct authlist *c; 21300Sstevel@tonic-gate 21310Sstevel@tonic-gate for (c = AuthList; c->name; c++) { 21320Sstevel@tonic-gate if (c->help) { 21330Sstevel@tonic-gate if (*c->help) 21340Sstevel@tonic-gate (void) printf("%-15s %s\n", c->name, c->help); 21350Sstevel@tonic-gate else 21360Sstevel@tonic-gate (void) printf("\n"); 21370Sstevel@tonic-gate } 21380Sstevel@tonic-gate } 21390Sstevel@tonic-gate return (0); 21400Sstevel@tonic-gate } 21410Sstevel@tonic-gate 21420Sstevel@tonic-gate 21430Sstevel@tonic-gate static int 21440Sstevel@tonic-gate auth_cmd(argc, argv) 21450Sstevel@tonic-gate int argc; 21460Sstevel@tonic-gate char *argv[]; 21470Sstevel@tonic-gate { 21480Sstevel@tonic-gate struct authlist *c; 21490Sstevel@tonic-gate 21500Sstevel@tonic-gate if (argc < 2) { 21510Sstevel@tonic-gate (void) fprintf(stderr, "Need an argument to 'auth' " 21520Sstevel@tonic-gate "command. 'auth ?' for help.\n"); 21530Sstevel@tonic-gate return (0); 21540Sstevel@tonic-gate } 21550Sstevel@tonic-gate 21560Sstevel@tonic-gate c = (struct authlist *) 21570Sstevel@tonic-gate genget(argv[1], (char **)AuthList, sizeof (struct authlist)); 21580Sstevel@tonic-gate if (c == 0) { 21590Sstevel@tonic-gate (void) fprintf(stderr, 21600Sstevel@tonic-gate "'%s': unknown argument ('auth ?' for help).\n", 21610Sstevel@tonic-gate argv[1]); 21620Sstevel@tonic-gate return (0); 21630Sstevel@tonic-gate } 21640Sstevel@tonic-gate if (Ambiguous(c)) { 21650Sstevel@tonic-gate (void) fprintf(stderr, 21660Sstevel@tonic-gate "'%s': ambiguous argument ('auth ?' for help).\n", argv[1]); 21670Sstevel@tonic-gate return (0); 21680Sstevel@tonic-gate } 21690Sstevel@tonic-gate if (c->narg + 2 != argc) { 21700Sstevel@tonic-gate (void) fprintf(stderr, 21710Sstevel@tonic-gate "Need %s%d argument%s to 'auth %s' command." 21720Sstevel@tonic-gate " 'auth ?' for help.\n", 21730Sstevel@tonic-gate c->narg + 2 < argc ? "only " : "", 21740Sstevel@tonic-gate c->narg, c->narg == 1 ? "" : "s", c->name); 21750Sstevel@tonic-gate return (0); 21760Sstevel@tonic-gate } 21770Sstevel@tonic-gate return ((*c->handler)(argv[2], argv[3])); 21780Sstevel@tonic-gate } 21790Sstevel@tonic-gate 21800Sstevel@tonic-gate /* 21810Sstevel@tonic-gate * The FORWARD command. 21820Sstevel@tonic-gate */ 21830Sstevel@tonic-gate 21840Sstevel@tonic-gate extern int forward_flags; 21850Sstevel@tonic-gate 21860Sstevel@tonic-gate struct forwlist { 21870Sstevel@tonic-gate char *name; 21880Sstevel@tonic-gate char *help; 21890Sstevel@tonic-gate int (*handler)(); 21900Sstevel@tonic-gate int f_flags; 21910Sstevel@tonic-gate }; 21920Sstevel@tonic-gate 21930Sstevel@tonic-gate static int forw_status(void); 21940Sstevel@tonic-gate static int forw_set(int); 21950Sstevel@tonic-gate static int forw_help(void); 21960Sstevel@tonic-gate 21970Sstevel@tonic-gate static struct forwlist ForwList[] = { 21980Sstevel@tonic-gate {"status", 21990Sstevel@tonic-gate "Display current status of credential forwarding", 22000Sstevel@tonic-gate forw_status, 0}, 22010Sstevel@tonic-gate {"disable", 22020Sstevel@tonic-gate "Disable credential forwarding", 22030Sstevel@tonic-gate forw_set, 0}, 22040Sstevel@tonic-gate {"enable", 22050Sstevel@tonic-gate "Enable credential forwarding", 22060Sstevel@tonic-gate forw_set, OPTS_FORWARD_CREDS}, 22070Sstevel@tonic-gate {"forwardable", 22080Sstevel@tonic-gate "Enable credential forwarding of " 22090Sstevel@tonic-gate "forwardable credentials", 22100Sstevel@tonic-gate forw_set, OPTS_FORWARD_CREDS | OPTS_FORWARDABLE_CREDS}, 22110Sstevel@tonic-gate {"help", 22120Sstevel@tonic-gate 0, 22130Sstevel@tonic-gate forw_help, 0}, 22140Sstevel@tonic-gate {"?", 22150Sstevel@tonic-gate "Print help information", 22160Sstevel@tonic-gate forw_help, 0}, 22170Sstevel@tonic-gate {0}, 22180Sstevel@tonic-gate }; 22190Sstevel@tonic-gate 22200Sstevel@tonic-gate static int 22210Sstevel@tonic-gate forw_status(void) 22220Sstevel@tonic-gate { 22230Sstevel@tonic-gate if (forward_flags & OPTS_FORWARD_CREDS) { 22240Sstevel@tonic-gate if (forward_flags & OPTS_FORWARDABLE_CREDS) 22250Sstevel@tonic-gate (void) printf(gettext( 22260Sstevel@tonic-gate "Credential forwarding of " 22270Sstevel@tonic-gate "forwardable credentials enabled\n")); 22280Sstevel@tonic-gate else 22290Sstevel@tonic-gate (void) printf(gettext( 22300Sstevel@tonic-gate "Credential forwarding enabled\n")); 22310Sstevel@tonic-gate } else 22320Sstevel@tonic-gate (void) printf(gettext("Credential forwarding disabled\n")); 22330Sstevel@tonic-gate return (0); 22340Sstevel@tonic-gate } 22350Sstevel@tonic-gate 2236*473Sbw static int 22370Sstevel@tonic-gate forw_set(int f_flags) 22380Sstevel@tonic-gate { 22390Sstevel@tonic-gate forward_flags = f_flags; 22400Sstevel@tonic-gate return (0); 22410Sstevel@tonic-gate } 22420Sstevel@tonic-gate 22430Sstevel@tonic-gate static int 22440Sstevel@tonic-gate forw_help(void) 22450Sstevel@tonic-gate { 22460Sstevel@tonic-gate struct forwlist *c; 22470Sstevel@tonic-gate 22480Sstevel@tonic-gate for (c = ForwList; c->name; c++) { 22490Sstevel@tonic-gate if (c->help) { 22500Sstevel@tonic-gate if (*c->help) 22510Sstevel@tonic-gate (void) printf("%-15s %s\r\n", c->name, c->help); 22520Sstevel@tonic-gate else 22530Sstevel@tonic-gate (void) printf("\n"); 22540Sstevel@tonic-gate } 22550Sstevel@tonic-gate } 22560Sstevel@tonic-gate return (0); 22570Sstevel@tonic-gate } 22580Sstevel@tonic-gate 22590Sstevel@tonic-gate static int 22600Sstevel@tonic-gate forw_cmd(int argc, char *argv[]) 22610Sstevel@tonic-gate { 22620Sstevel@tonic-gate struct forwlist *c; 22630Sstevel@tonic-gate 22640Sstevel@tonic-gate if (argc < 2) { 22650Sstevel@tonic-gate (void) fprintf(stderr, gettext( 22660Sstevel@tonic-gate "Need an argument to 'forward' " 22670Sstevel@tonic-gate "command. 'forward ?' for help.\n")); 22680Sstevel@tonic-gate return (0); 22690Sstevel@tonic-gate } 22700Sstevel@tonic-gate c = (struct forwlist *)genget(argv[1], (char **)ForwList, 22710Sstevel@tonic-gate sizeof (struct forwlist)); 22720Sstevel@tonic-gate if (c == 0) { 22730Sstevel@tonic-gate (void) fprintf(stderr, gettext( 22740Sstevel@tonic-gate "'%s': unknown argument ('forward ?' for help).\n"), 22750Sstevel@tonic-gate argv[1]); 22760Sstevel@tonic-gate return (0); 22770Sstevel@tonic-gate } 22780Sstevel@tonic-gate if (Ambiguous(c)) { 22790Sstevel@tonic-gate (void) fprintf(stderr, gettext( 22800Sstevel@tonic-gate "'%s': ambiguous argument ('forward ?' for help).\n"), 22810Sstevel@tonic-gate argv[1]); 22820Sstevel@tonic-gate return (0); 22830Sstevel@tonic-gate } 22840Sstevel@tonic-gate if (argc != 2) { 22850Sstevel@tonic-gate (void) fprintf(stderr, gettext( 22860Sstevel@tonic-gate "No arguments needed to 'forward %s' command. " 22870Sstevel@tonic-gate "'forward ?' for help.\n"), c->name); 22880Sstevel@tonic-gate return (0); 22890Sstevel@tonic-gate } 22900Sstevel@tonic-gate return ((*c->handler) (c->f_flags)); 22910Sstevel@tonic-gate } 22920Sstevel@tonic-gate 22930Sstevel@tonic-gate /* 22940Sstevel@tonic-gate * The ENCRYPT command. 22950Sstevel@tonic-gate */ 22960Sstevel@tonic-gate 22970Sstevel@tonic-gate struct encryptlist { 22980Sstevel@tonic-gate char *name; 22990Sstevel@tonic-gate char *help; 23000Sstevel@tonic-gate int (*handler)(); 23010Sstevel@tonic-gate int needconnect; 23020Sstevel@tonic-gate int minarg; 23030Sstevel@tonic-gate int maxarg; 23040Sstevel@tonic-gate }; 23050Sstevel@tonic-gate 23060Sstevel@tonic-gate static int EncryptHelp(void); 23070Sstevel@tonic-gate 23080Sstevel@tonic-gate static struct encryptlist EncryptList[] = { 23090Sstevel@tonic-gate { "enable", "Enable encryption. ('encrypt enable ?' for more)", 23100Sstevel@tonic-gate EncryptEnable, 1, 1, 2 }, 23110Sstevel@tonic-gate { "disable", "Disable encryption. ('encrypt disable ?' for more)", 23120Sstevel@tonic-gate EncryptDisable, 0, 1, 2 }, 23130Sstevel@tonic-gate { "type", "Set encryption type. ('encrypt type ?' for more)", 23140Sstevel@tonic-gate EncryptType, 0, 1, 2 }, 23150Sstevel@tonic-gate { "start", "Start encryption. ('encrypt start ?' for more)", 23160Sstevel@tonic-gate EncryptStart, 1, 0, 1 }, 23170Sstevel@tonic-gate { "stop", "Stop encryption. ('encrypt stop ?' for more)", 23180Sstevel@tonic-gate EncryptStop, 1, 0, 1 }, 23190Sstevel@tonic-gate { "input", "Start encrypting the input stream", 23200Sstevel@tonic-gate EncryptStartInput, 1, 0, 0 }, 23210Sstevel@tonic-gate { "-input", "Stop encrypting the input stream", 23220Sstevel@tonic-gate EncryptStopInput, 1, 0, 0 }, 23230Sstevel@tonic-gate { "output", "Start encrypting the output stream", 23240Sstevel@tonic-gate EncryptStartOutput, 1, 0, 0 }, 23250Sstevel@tonic-gate { "-output", "Stop encrypting the output stream", 23260Sstevel@tonic-gate EncryptStopOutput, 1, 0, 0 }, 23270Sstevel@tonic-gate 23280Sstevel@tonic-gate { "status", "Display current status of encryption information", 23290Sstevel@tonic-gate EncryptStatus, 0, 0, 0 }, 23300Sstevel@tonic-gate { "help", 0, 23310Sstevel@tonic-gate EncryptHelp, 0, 0, 0 }, 23320Sstevel@tonic-gate { "?", "Print help information", EncryptHelp, 0, 0, 0 }, 23330Sstevel@tonic-gate { 0 }, 23340Sstevel@tonic-gate }; 23350Sstevel@tonic-gate 23360Sstevel@tonic-gate static int 23370Sstevel@tonic-gate EncryptHelp(void) 23380Sstevel@tonic-gate { 23390Sstevel@tonic-gate struct encryptlist *c; 23400Sstevel@tonic-gate 23410Sstevel@tonic-gate for (c = EncryptList; c->name; c++) { 23420Sstevel@tonic-gate if (c->help) { 23430Sstevel@tonic-gate if (*c->help) 23440Sstevel@tonic-gate (void) printf("%-15s %s\n", c->name, c->help); 23450Sstevel@tonic-gate else 23460Sstevel@tonic-gate (void) printf("\n"); 23470Sstevel@tonic-gate } 23480Sstevel@tonic-gate } 23490Sstevel@tonic-gate return (0); 23500Sstevel@tonic-gate } 23510Sstevel@tonic-gate 23520Sstevel@tonic-gate static int 23530Sstevel@tonic-gate encrypt_cmd(int argc, char *argv[]) 23540Sstevel@tonic-gate { 23550Sstevel@tonic-gate struct encryptlist *c; 23560Sstevel@tonic-gate 23570Sstevel@tonic-gate if (argc < 2) { 23580Sstevel@tonic-gate (void) fprintf(stderr, gettext( 23590Sstevel@tonic-gate "Need an argument to 'encrypt' command. " 23600Sstevel@tonic-gate "'encrypt ?' for help.\n")); 23610Sstevel@tonic-gate return (0); 23620Sstevel@tonic-gate } 23630Sstevel@tonic-gate 23640Sstevel@tonic-gate c = (struct encryptlist *) 23650Sstevel@tonic-gate genget(argv[1], (char **)EncryptList, sizeof (struct encryptlist)); 23660Sstevel@tonic-gate if (c == 0) { 23670Sstevel@tonic-gate (void) fprintf(stderr, gettext( 23680Sstevel@tonic-gate "'%s': unknown argument ('encrypt ?' for help).\n"), 23690Sstevel@tonic-gate argv[1]); 23700Sstevel@tonic-gate return (0); 23710Sstevel@tonic-gate } 23720Sstevel@tonic-gate if (Ambiguous(c)) { 23730Sstevel@tonic-gate (void) fprintf(stderr, gettext( 23740Sstevel@tonic-gate "'%s': ambiguous argument ('encrypt ?' for help).\n"), 23750Sstevel@tonic-gate argv[1]); 23760Sstevel@tonic-gate return (0); 23770Sstevel@tonic-gate } 23780Sstevel@tonic-gate argc -= 2; 23790Sstevel@tonic-gate if (argc < c->minarg || argc > c->maxarg) { 23800Sstevel@tonic-gate if (c->minarg == c->maxarg) { 23810Sstevel@tonic-gate (void) fprintf(stderr, gettext("Need %s%d %s "), 23820Sstevel@tonic-gate c->minarg < argc ? 23830Sstevel@tonic-gate gettext("only ") : "", c->minarg, 23840Sstevel@tonic-gate c->minarg == 1 ? 23850Sstevel@tonic-gate gettext("argument") : gettext("arguments")); 23860Sstevel@tonic-gate } else { 23870Sstevel@tonic-gate (void) fprintf(stderr, 23880Sstevel@tonic-gate gettext("Need %s%d-%d arguments "), 23890Sstevel@tonic-gate c->maxarg < argc ? 23900Sstevel@tonic-gate gettext("only ") : "", c->minarg, c->maxarg); 23910Sstevel@tonic-gate } 23920Sstevel@tonic-gate (void) fprintf(stderr, gettext( 23930Sstevel@tonic-gate "to 'encrypt %s' command. 'encrypt ?' for help.\n"), 23940Sstevel@tonic-gate c->name); 23950Sstevel@tonic-gate return (0); 23960Sstevel@tonic-gate } 23970Sstevel@tonic-gate if (c->needconnect && !connected) { 23980Sstevel@tonic-gate if (!(argc && 23990Sstevel@tonic-gate (isprefix(argv[2], "help") || isprefix(argv[2], "?")))) { 24000Sstevel@tonic-gate (void) printf( 24010Sstevel@tonic-gate gettext("?Need to be connected first.\n")); 24020Sstevel@tonic-gate return (0); 24030Sstevel@tonic-gate } 24040Sstevel@tonic-gate } 24050Sstevel@tonic-gate return ((*c->handler)(argc > 0 ? argv[2] : 0, 24060Sstevel@tonic-gate argc > 1 ? argv[3] : 0, argc > 2 ? argv[4] : 0)); 24070Sstevel@tonic-gate } 24080Sstevel@tonic-gate 24090Sstevel@tonic-gate /* 24100Sstevel@tonic-gate * Print status about the connection. 24110Sstevel@tonic-gate */ 2412*473Sbw static int 24130Sstevel@tonic-gate status(int argc, char *argv[]) 24140Sstevel@tonic-gate { 24150Sstevel@tonic-gate if (connected) { 24160Sstevel@tonic-gate (void) printf("Connected to %s.\n", hostname); 24170Sstevel@tonic-gate if ((argc < 2) || strcmp(argv[1], "notmuch")) { 24180Sstevel@tonic-gate int mode = getconnmode(); 24190Sstevel@tonic-gate 24200Sstevel@tonic-gate if (my_want_state_is_will(TELOPT_LINEMODE)) { 24210Sstevel@tonic-gate (void) printf( 24220Sstevel@tonic-gate "Operating with LINEMODE option\n"); 24230Sstevel@tonic-gate (void) printf( 24240Sstevel@tonic-gate "%s line editing\n", (mode&MODE_EDIT) ? 24250Sstevel@tonic-gate "Local" : "No"); 24260Sstevel@tonic-gate (void) printf("%s catching of signals\n", 24270Sstevel@tonic-gate (mode&MODE_TRAPSIG) ? "Local" : "No"); 24280Sstevel@tonic-gate slcstate(); 24290Sstevel@tonic-gate #ifdef KLUDGELINEMODE 24300Sstevel@tonic-gate } else if (kludgelinemode && 24310Sstevel@tonic-gate my_want_state_is_dont(TELOPT_SGA)) { 24320Sstevel@tonic-gate (void) printf( 24330Sstevel@tonic-gate "Operating in obsolete linemode\n"); 24340Sstevel@tonic-gate #endif 24350Sstevel@tonic-gate } else { 24360Sstevel@tonic-gate (void) printf( 24370Sstevel@tonic-gate "Operating in single character mode\n"); 24380Sstevel@tonic-gate if (localchars) 24390Sstevel@tonic-gate (void) printf( 24400Sstevel@tonic-gate "Catching signals locally\n"); 24410Sstevel@tonic-gate } 24420Sstevel@tonic-gate (void) printf("%s character echo\n", (mode&MODE_ECHO) ? 24430Sstevel@tonic-gate "Local" : "Remote"); 24440Sstevel@tonic-gate if (my_want_state_is_will(TELOPT_LFLOW)) 24450Sstevel@tonic-gate (void) printf("%s flow control\n", 24460Sstevel@tonic-gate (mode&MODE_FLOW) ? "Local" : "No"); 24470Sstevel@tonic-gate 24480Sstevel@tonic-gate encrypt_display(); 24490Sstevel@tonic-gate } 24500Sstevel@tonic-gate } else { 24510Sstevel@tonic-gate (void) printf("No connection.\n"); 24520Sstevel@tonic-gate } 24530Sstevel@tonic-gate if (rlogin != _POSIX_VDISABLE) 24540Sstevel@tonic-gate (void) printf("Escape character is '%s'.\n", control(rlogin)); 24550Sstevel@tonic-gate else 24560Sstevel@tonic-gate (void) printf( 24570Sstevel@tonic-gate "Escape character is '%s'.\n", esc_control(escape)); 24580Sstevel@tonic-gate (void) fflush(stdout); 24590Sstevel@tonic-gate return (1); 24600Sstevel@tonic-gate } 24610Sstevel@tonic-gate 24620Sstevel@tonic-gate /* 24630Sstevel@tonic-gate * Parse the user input (cmd_line_input) which should: 24640Sstevel@tonic-gate * - start with the target host, or with "@" or "!@" followed by at least one 24650Sstevel@tonic-gate * gateway. 24660Sstevel@tonic-gate * - each host (can be literal address or hostname) can be separated by ",", 24670Sstevel@tonic-gate * "@", or ",@". 24680Sstevel@tonic-gate * Note that the last host is the target, all the others (if any ) are the 24690Sstevel@tonic-gate * gateways. 24700Sstevel@tonic-gate * 24710Sstevel@tonic-gate * Returns: -1 if a library call fails, too many gateways, or parse 24720Sstevel@tonic-gate * error 24730Sstevel@tonic-gate * num_gw otherwise 24740Sstevel@tonic-gate * On successful return, hostname_list points to a list of hosts (last one being 24750Sstevel@tonic-gate * the target, others gateways), src_rtng_type points to the type of source 24760Sstevel@tonic-gate * routing (strict vs. loose) 24770Sstevel@tonic-gate */ 24780Sstevel@tonic-gate static int 24790Sstevel@tonic-gate parse_input(char *cmd_line_input, char **hostname_list, uchar_t *src_rtng_type) 24800Sstevel@tonic-gate { 24810Sstevel@tonic-gate char hname[MAXHOSTNAMELEN + 1]; 24820Sstevel@tonic-gate char *cp; 24830Sstevel@tonic-gate int gw_count; 24840Sstevel@tonic-gate int i; 24850Sstevel@tonic-gate 24860Sstevel@tonic-gate gw_count = 0; 24870Sstevel@tonic-gate cp = cmd_line_input; 24880Sstevel@tonic-gate 24890Sstevel@tonic-gate /* 24900Sstevel@tonic-gate * Defining ICMD generates the Itelnet binary, the special version of 24910Sstevel@tonic-gate * telnet which is used with firewall proxy. 24920Sstevel@tonic-gate * If ICMD is defined, parse_input will treat the whole cmd_line_input 24930Sstevel@tonic-gate * as the target host and set the num_gw to 0. Therefore, none of the 24940Sstevel@tonic-gate * source routing related code paths will be executed. 24950Sstevel@tonic-gate */ 24960Sstevel@tonic-gate #ifndef ICMD 24970Sstevel@tonic-gate if (*cp == '@') { 24980Sstevel@tonic-gate *src_rtng_type = IPOPT_LSRR; 24990Sstevel@tonic-gate cp++; 25000Sstevel@tonic-gate } else if (*cp == '!') { 25010Sstevel@tonic-gate *src_rtng_type = IPOPT_SSRR; 25020Sstevel@tonic-gate 25030Sstevel@tonic-gate /* "!" must be followed by '@' */ 25040Sstevel@tonic-gate if (*(cp + 1) != '@') 25050Sstevel@tonic-gate goto parse_error; 25060Sstevel@tonic-gate cp += 2; 25070Sstevel@tonic-gate } else { 25080Sstevel@tonic-gate #endif /* ICMD */ 25090Sstevel@tonic-gate /* no gateways, just the target */ 25100Sstevel@tonic-gate hostname_list[0] = strdup(cp); 25110Sstevel@tonic-gate if (hostname_list[0] == NULL) { 25120Sstevel@tonic-gate perror("telnet: copying host name"); 25130Sstevel@tonic-gate return (-1); 25140Sstevel@tonic-gate } 25150Sstevel@tonic-gate return (0); 25160Sstevel@tonic-gate #ifndef ICMD 25170Sstevel@tonic-gate } 25180Sstevel@tonic-gate 25190Sstevel@tonic-gate while (*cp != '\0') { 25200Sstevel@tonic-gate /* 25210Sstevel@tonic-gate * Identify each gateway separated by ",", "@" or ",@" and 25220Sstevel@tonic-gate * store in hname[]. 25230Sstevel@tonic-gate */ 25240Sstevel@tonic-gate i = 0; 25250Sstevel@tonic-gate while (*cp != '@' && *cp != ',' && *cp != '\0') { 25260Sstevel@tonic-gate hname[i++] = *cp++; 25270Sstevel@tonic-gate if (i > MAXHOSTNAMELEN) 25280Sstevel@tonic-gate goto parse_error; 25290Sstevel@tonic-gate } 25300Sstevel@tonic-gate hname[i] = '\0'; 25310Sstevel@tonic-gate 25320Sstevel@tonic-gate /* 25330Sstevel@tonic-gate * Two consecutive delimiters which result in a 0 length hname 25340Sstevel@tonic-gate * is a parse error. 25350Sstevel@tonic-gate */ 25360Sstevel@tonic-gate if (i == 0) 25370Sstevel@tonic-gate goto parse_error; 25380Sstevel@tonic-gate 25390Sstevel@tonic-gate hostname_list[gw_count] = strdup(hname); 25400Sstevel@tonic-gate if (hostname_list[gw_count] == NULL) { 25410Sstevel@tonic-gate perror("telnet: copying hostname from list"); 25420Sstevel@tonic-gate return (-1); 25430Sstevel@tonic-gate } 25440Sstevel@tonic-gate 25450Sstevel@tonic-gate if (++gw_count > MAXMAX_GATEWAY) { 25460Sstevel@tonic-gate (void) fprintf(stderr, "telnet: too many gateways\n"); 25470Sstevel@tonic-gate return (-1); 25480Sstevel@tonic-gate } 25490Sstevel@tonic-gate 25500Sstevel@tonic-gate /* Jump over the next delimiter. */ 25510Sstevel@tonic-gate if (*cp != '\0') { 25520Sstevel@tonic-gate /* ...gw1,@gw2... accepted */ 25530Sstevel@tonic-gate if (*cp == ',' && *(cp + 1) == '@') 25540Sstevel@tonic-gate cp += 2; 25550Sstevel@tonic-gate else 25560Sstevel@tonic-gate cp++; 25570Sstevel@tonic-gate } 25580Sstevel@tonic-gate } 25590Sstevel@tonic-gate 25600Sstevel@tonic-gate /* discount the target */ 25610Sstevel@tonic-gate gw_count--; 25620Sstevel@tonic-gate 25630Sstevel@tonic-gate /* Any input starting with '!@' or '@' must have at least one gateway */ 25640Sstevel@tonic-gate if (gw_count <= 0) 25650Sstevel@tonic-gate goto parse_error; 25660Sstevel@tonic-gate 25670Sstevel@tonic-gate return (gw_count); 25680Sstevel@tonic-gate 25690Sstevel@tonic-gate parse_error: 25700Sstevel@tonic-gate (void) printf("Bad source route option: %s\n", cmd_line_input); 25710Sstevel@tonic-gate return (-1); 25720Sstevel@tonic-gate #endif /* ICMD */ 25730Sstevel@tonic-gate } 25740Sstevel@tonic-gate 25750Sstevel@tonic-gate /* 25760Sstevel@tonic-gate * Resolves the target and gateway addresses, determines what type of addresses 25770Sstevel@tonic-gate * (ALL_ADDRS, ONLY_V6, ONLY_V4) telnet will be trying to connect. 25780Sstevel@tonic-gate * 25790Sstevel@tonic-gate * Returns: pointer to resolved target if name resolutions succeed 25800Sstevel@tonic-gate * NULL if name resolutions fail or 25810Sstevel@tonic-gate * a library function call fails 25820Sstevel@tonic-gate * 25830Sstevel@tonic-gate * The last host in the hostname_list is the target. After resolving the target, 25840Sstevel@tonic-gate * determines for what type of addresses it should try to resolve gateways. It 25850Sstevel@tonic-gate * resolves gateway addresses and picks one address for each desired address 25860Sstevel@tonic-gate * type and stores in the array pointed by gw_addrsp. Also, this 'type of 25870Sstevel@tonic-gate * addresses' is pointed by addr_type argument on successful return. 25880Sstevel@tonic-gate */ 25890Sstevel@tonic-gate static struct addrinfo * 25900Sstevel@tonic-gate resolve_hosts(char **hostname_list, int num_gw, struct gateway **gw_addrsp, 25910Sstevel@tonic-gate int *addr_type, const char *portp) 25920Sstevel@tonic-gate { 25930Sstevel@tonic-gate struct gateway *gw_addrs = NULL; 25940Sstevel@tonic-gate struct gateway *gw; 25950Sstevel@tonic-gate /* whether we already picked an IPv4 address for the current gateway */ 25960Sstevel@tonic-gate boolean_t got_v4_addr; 25970Sstevel@tonic-gate boolean_t got_v6_addr; 25980Sstevel@tonic-gate /* whether we need to get an IPv4 address for the current gateway */ 25990Sstevel@tonic-gate boolean_t need_v4_addr = B_FALSE; 26000Sstevel@tonic-gate boolean_t need_v6_addr = B_FALSE; 26010Sstevel@tonic-gate int res_failed_at4; /* save which gateway failed to resolve */ 26020Sstevel@tonic-gate int res_failed_at6; 26030Sstevel@tonic-gate boolean_t is_v4mapped; 26040Sstevel@tonic-gate struct in6_addr *v6addrp; 26050Sstevel@tonic-gate struct in_addr *v4addrp; 26060Sstevel@tonic-gate int error_num; 26070Sstevel@tonic-gate int i; 26080Sstevel@tonic-gate int rc; 26090Sstevel@tonic-gate struct addrinfo *res, *host, *gateway, *addr; 26100Sstevel@tonic-gate struct addrinfo hints; 26110Sstevel@tonic-gate 26120Sstevel@tonic-gate *addr_type = ALL_ADDRS; 26130Sstevel@tonic-gate 26140Sstevel@tonic-gate memset(&hints, 0, sizeof (hints)); 26150Sstevel@tonic-gate hints.ai_flags = AI_CANONNAME; /* used for config files, diags */ 26160Sstevel@tonic-gate hints.ai_socktype = SOCK_STREAM; 26170Sstevel@tonic-gate rc = getaddrinfo(hostname_list[num_gw], 26180Sstevel@tonic-gate (portp != NULL) ? portp : "telnet", &hints, &res); 26190Sstevel@tonic-gate if (rc != 0) { 26200Sstevel@tonic-gate if (hostname_list[num_gw] != NULL && 26210Sstevel@tonic-gate *hostname_list[num_gw] != '\0') 26220Sstevel@tonic-gate (void) fprintf(stderr, "%s: ", hostname_list[num_gw]); 26230Sstevel@tonic-gate (void) fprintf(stderr, "%s\n", gai_strerror(rc)); 26240Sstevel@tonic-gate return (NULL); 26250Sstevel@tonic-gate } 26260Sstevel@tonic-gate 26270Sstevel@tonic-gate /* 26280Sstevel@tonic-gate * Let's see what type of addresses we got for the target. This 26290Sstevel@tonic-gate * determines what type of addresses we'd like to resolve gateways 26300Sstevel@tonic-gate * later. 26310Sstevel@tonic-gate */ 26320Sstevel@tonic-gate for (host = res; host != NULL; host = host->ai_next) { 26330Sstevel@tonic-gate struct sockaddr_in6 *s6; 26340Sstevel@tonic-gate 26350Sstevel@tonic-gate s6 = (struct sockaddr_in6 *)host->ai_addr; 26360Sstevel@tonic-gate 26370Sstevel@tonic-gate if (host->ai_addr->sa_family == AF_INET || 26380Sstevel@tonic-gate IN6_IS_ADDR_V4MAPPED(&s6->sin6_addr)) 26390Sstevel@tonic-gate need_v4_addr = B_TRUE; 26400Sstevel@tonic-gate else 26410Sstevel@tonic-gate need_v6_addr = B_TRUE; 26420Sstevel@tonic-gate 26430Sstevel@tonic-gate /* 26440Sstevel@tonic-gate * Let's stop after seeing we need both IPv6 and IPv4. 26450Sstevel@tonic-gate */ 26460Sstevel@tonic-gate if (need_v4_addr && need_v6_addr) 26470Sstevel@tonic-gate break; 26480Sstevel@tonic-gate } 26490Sstevel@tonic-gate 26500Sstevel@tonic-gate if (num_gw > 0) { 26510Sstevel@tonic-gate /* 26520Sstevel@tonic-gate * In the prepare_optbuf(), we'll store the IPv4 address of the 26530Sstevel@tonic-gate * target in the last slot of gw_addrs array. Therefore we need 26540Sstevel@tonic-gate * space for num_gw+1 hosts. 26550Sstevel@tonic-gate */ 26560Sstevel@tonic-gate gw_addrs = calloc(num_gw + 1, sizeof (struct gateway)); 26570Sstevel@tonic-gate if (gw_addrs == NULL) { 26580Sstevel@tonic-gate perror("telnet: calloc"); 26590Sstevel@tonic-gate freeaddrinfo(res); 26600Sstevel@tonic-gate return (NULL); 26610Sstevel@tonic-gate } 26620Sstevel@tonic-gate } 26630Sstevel@tonic-gate 26640Sstevel@tonic-gate /* 26650Sstevel@tonic-gate * Now we'll go through all the gateways and try to resolve them to 26660Sstevel@tonic-gate * the desired address types. 26670Sstevel@tonic-gate */ 26680Sstevel@tonic-gate gw = gw_addrs; 26690Sstevel@tonic-gate 26700Sstevel@tonic-gate /* -1 means 'no address resolution failure yet' */ 26710Sstevel@tonic-gate res_failed_at4 = -1; 26720Sstevel@tonic-gate res_failed_at6 = -1; 26730Sstevel@tonic-gate for (i = 0; i < num_gw; i++) { 26740Sstevel@tonic-gate rc = getaddrinfo(hostname_list[i], NULL, NULL, &gateway); 26750Sstevel@tonic-gate if (rc != 0) { 26760Sstevel@tonic-gate if (hostname_list[i] != NULL && 26770Sstevel@tonic-gate *hostname_list[i] != '\0') 26780Sstevel@tonic-gate (void) fprintf(stderr, "%s: ", 26790Sstevel@tonic-gate hostname_list[i]); 26800Sstevel@tonic-gate (void) fprintf(stderr, "bad address\n"); 26810Sstevel@tonic-gate return (NULL); 26820Sstevel@tonic-gate } 26830Sstevel@tonic-gate 26840Sstevel@tonic-gate /* 26850Sstevel@tonic-gate * Initially we have no address of any type for this gateway. 26860Sstevel@tonic-gate */ 26870Sstevel@tonic-gate got_v6_addr = B_FALSE; 26880Sstevel@tonic-gate got_v4_addr = B_FALSE; 26890Sstevel@tonic-gate 26900Sstevel@tonic-gate /* 26910Sstevel@tonic-gate * Let's go through all the addresses of this gateway. 26920Sstevel@tonic-gate * Use the first address which matches the needed family. 26930Sstevel@tonic-gate */ 26940Sstevel@tonic-gate for (addr = gateway; addr != NULL; addr = addr->ai_next) { 26950Sstevel@tonic-gate /*LINTED*/ 26960Sstevel@tonic-gate v6addrp = &((struct sockaddr_in6 *)addr->ai_addr)-> 26970Sstevel@tonic-gate sin6_addr; 26980Sstevel@tonic-gate v4addrp = &((struct sockaddr_in *)addr->ai_addr)-> 26990Sstevel@tonic-gate sin_addr; 27000Sstevel@tonic-gate 27010Sstevel@tonic-gate if (addr->ai_family == AF_INET6) 27020Sstevel@tonic-gate is_v4mapped = IN6_IS_ADDR_V4MAPPED(v6addrp); 27030Sstevel@tonic-gate else 27040Sstevel@tonic-gate is_v4mapped = B_FALSE; 27050Sstevel@tonic-gate 27060Sstevel@tonic-gate /* 27070Sstevel@tonic-gate * If we need to determine an IPv4 address and haven't 27080Sstevel@tonic-gate * found one yet and this is a IPv4-mapped IPv6 address, 27090Sstevel@tonic-gate * then bingo! 27100Sstevel@tonic-gate */ 27110Sstevel@tonic-gate if (need_v4_addr && !got_v4_addr) { 27120Sstevel@tonic-gate if (is_v4mapped) { 27130Sstevel@tonic-gate IN6_V4MAPPED_TO_INADDR(v6addrp, 27140Sstevel@tonic-gate &gw->gw_addr); 27150Sstevel@tonic-gate got_v4_addr = B_TRUE; 27160Sstevel@tonic-gate } else if (addr->ai_family = AF_INET) { 27170Sstevel@tonic-gate gw->gw_addr = *v4addrp; 27180Sstevel@tonic-gate got_v4_addr = B_TRUE; 27190Sstevel@tonic-gate } 27200Sstevel@tonic-gate } 27210Sstevel@tonic-gate 27220Sstevel@tonic-gate if (need_v6_addr && !got_v6_addr && 27230Sstevel@tonic-gate addr->ai_family == AF_INET6) { 27240Sstevel@tonic-gate gw->gw_addr6 = *v6addrp; 27250Sstevel@tonic-gate got_v6_addr = B_TRUE; 27260Sstevel@tonic-gate } 27270Sstevel@tonic-gate 27280Sstevel@tonic-gate /* 27290Sstevel@tonic-gate * Let's stop if we got all what we looked for. 27300Sstevel@tonic-gate */ 27310Sstevel@tonic-gate if ((!need_v4_addr || got_v4_addr) && 27320Sstevel@tonic-gate (!need_v6_addr || got_v6_addr)) 27330Sstevel@tonic-gate break; 27340Sstevel@tonic-gate } 27350Sstevel@tonic-gate 27360Sstevel@tonic-gate /* 27370Sstevel@tonic-gate * We needed an IPv4 address for this gateway but couldn't 27380Sstevel@tonic-gate * find one. 27390Sstevel@tonic-gate */ 27400Sstevel@tonic-gate if (need_v4_addr && !got_v4_addr) { 27410Sstevel@tonic-gate res_failed_at4 = i; 27420Sstevel@tonic-gate /* 27430Sstevel@tonic-gate * Since we couldn't resolve a gateway to IPv4 address 27440Sstevel@tonic-gate * we can't use IPv4 at all. Therefore we no longer 27450Sstevel@tonic-gate * need IPv4 addresses for any of the gateways. 27460Sstevel@tonic-gate */ 27470Sstevel@tonic-gate need_v4_addr = B_FALSE; 27480Sstevel@tonic-gate } 27490Sstevel@tonic-gate 27500Sstevel@tonic-gate if (need_v6_addr && !got_v6_addr) { 27510Sstevel@tonic-gate res_failed_at6 = i; 27520Sstevel@tonic-gate need_v6_addr = B_FALSE; 27530Sstevel@tonic-gate } 27540Sstevel@tonic-gate 27550Sstevel@tonic-gate /* 27560Sstevel@tonic-gate * If some gateways don't resolve to any of the desired 27570Sstevel@tonic-gate * address types, we fail. 27580Sstevel@tonic-gate */ 27590Sstevel@tonic-gate if (!need_v4_addr && !need_v6_addr) { 27600Sstevel@tonic-gate if (res_failed_at6 != -1) { 27610Sstevel@tonic-gate (void) fprintf(stderr, 27620Sstevel@tonic-gate "%s: Host doesn't have any IPv6 address\n", 27630Sstevel@tonic-gate hostname_list[res_failed_at6]); 27640Sstevel@tonic-gate } 27650Sstevel@tonic-gate if (res_failed_at4 != -1) { 27660Sstevel@tonic-gate (void) fprintf(stderr, 27670Sstevel@tonic-gate "%s: Host doesn't have any IPv4 address\n", 27680Sstevel@tonic-gate hostname_list[res_failed_at4]); 27690Sstevel@tonic-gate } 27700Sstevel@tonic-gate free(gw_addrs); 27710Sstevel@tonic-gate return (NULL); 27720Sstevel@tonic-gate } 27730Sstevel@tonic-gate 27740Sstevel@tonic-gate gw++; 27750Sstevel@tonic-gate } 27760Sstevel@tonic-gate 27770Sstevel@tonic-gate *gw_addrsp = gw_addrs; 27780Sstevel@tonic-gate 27790Sstevel@tonic-gate /* 27800Sstevel@tonic-gate * When we get here, need_v4_addr and need_v6_addr have their final 27810Sstevel@tonic-gate * values based on the name resolution of the target and gateways. 27820Sstevel@tonic-gate */ 27830Sstevel@tonic-gate if (need_v4_addr && need_v6_addr) 27840Sstevel@tonic-gate *addr_type = ALL_ADDRS; 27850Sstevel@tonic-gate else if (need_v4_addr && !need_v6_addr) 27860Sstevel@tonic-gate *addr_type = ONLY_V4; 27870Sstevel@tonic-gate else if (!need_v4_addr && need_v6_addr) 27880Sstevel@tonic-gate *addr_type = ONLY_V6; 27890Sstevel@tonic-gate 27900Sstevel@tonic-gate return (res); 27910Sstevel@tonic-gate } 27920Sstevel@tonic-gate 27930Sstevel@tonic-gate 27940Sstevel@tonic-gate /* 27950Sstevel@tonic-gate * Initializes the buffer pointed by opt_bufpp for a IPv4 option of type 27960Sstevel@tonic-gate * src_rtng_type using the gateway addresses stored in gw_addrs. If no buffer 27970Sstevel@tonic-gate * is passed, it allocates one. If a buffer is passed, checks if it's big 27980Sstevel@tonic-gate * enough. 27990Sstevel@tonic-gate * On return opt_buf_len points to the buffer length which we need later for the 28000Sstevel@tonic-gate * setsockopt() call, and opt_bufpp points to the newly allocated or already 28010Sstevel@tonic-gate * passed buffer. Returns B_FALSE if a library function call fails or passed 28020Sstevel@tonic-gate * buffer is not big enough, B_TRUE otherwise. 28030Sstevel@tonic-gate */ 28040Sstevel@tonic-gate static boolean_t 28050Sstevel@tonic-gate prepare_optbuf(struct gateway *gw_addrs, int num_gw, char **opt_bufpp, 28060Sstevel@tonic-gate size_t *opt_buf_len, struct in_addr *target, uchar_t src_rtng_type) 28070Sstevel@tonic-gate { 28080Sstevel@tonic-gate struct ip_sourceroute *sr_opt; 28090Sstevel@tonic-gate size_t needed_buflen; 28100Sstevel@tonic-gate int i; 28110Sstevel@tonic-gate 28120Sstevel@tonic-gate /* 28130Sstevel@tonic-gate * We have (num_gw + 1) IP addresses in the buffer because the number 28140Sstevel@tonic-gate * of gateway addresses we put in the option buffer includes the target 28150Sstevel@tonic-gate * address. 28160Sstevel@tonic-gate * At the time of setsockopt() call, passed option length needs to be 28170Sstevel@tonic-gate * multiple of 4 bytes. Therefore we need one IPOPT_NOP before (or 28180Sstevel@tonic-gate * after) IPOPT_LSRR. 28190Sstevel@tonic-gate * 1 = preceding 1 byte of IPOPT_NOP 28200Sstevel@tonic-gate * 3 = 1 (code) + 1 (len) + 1 (ptr) 28210Sstevel@tonic-gate */ 28220Sstevel@tonic-gate needed_buflen = 1 + 3 + (num_gw + 1) * sizeof (struct in_addr); 28230Sstevel@tonic-gate 28240Sstevel@tonic-gate if (*opt_bufpp != NULL) { 28250Sstevel@tonic-gate /* check if the passed buffer is big enough */ 28260Sstevel@tonic-gate if (*opt_buf_len < needed_buflen) { 28270Sstevel@tonic-gate (void) fprintf(stderr, 28280Sstevel@tonic-gate "telnet: buffer too small for IPv4 source routing " 28290Sstevel@tonic-gate "option\n"); 28300Sstevel@tonic-gate return (B_FALSE); 28310Sstevel@tonic-gate } 28320Sstevel@tonic-gate } else { 28330Sstevel@tonic-gate *opt_bufpp = malloc(needed_buflen); 28340Sstevel@tonic-gate if (*opt_bufpp == NULL) { 28350Sstevel@tonic-gate perror("telnet: malloc"); 28360Sstevel@tonic-gate return (B_FALSE); 28370Sstevel@tonic-gate } 28380Sstevel@tonic-gate } 28390Sstevel@tonic-gate 28400Sstevel@tonic-gate *opt_buf_len = needed_buflen; 28410Sstevel@tonic-gate 28420Sstevel@tonic-gate /* final hop is the target */ 28430Sstevel@tonic-gate gw_addrs[num_gw].gw_addr = *target; 28440Sstevel@tonic-gate 28450Sstevel@tonic-gate *opt_bufpp[0] = IPOPT_NOP; 28460Sstevel@tonic-gate /* IPOPT_LSRR starts right after IPOPT_NOP */ 28470Sstevel@tonic-gate sr_opt = (struct ip_sourceroute *)(*opt_bufpp + 1); 28480Sstevel@tonic-gate sr_opt->ipsr_code = src_rtng_type; 28490Sstevel@tonic-gate /* discount the 1 byte of IPOPT_NOP */ 28500Sstevel@tonic-gate sr_opt->ipsr_len = needed_buflen - 1; 28510Sstevel@tonic-gate sr_opt->ipsr_ptr = IPOPT_MINOFF; 28520Sstevel@tonic-gate 28530Sstevel@tonic-gate /* copy the gateways into the optlist */ 28540Sstevel@tonic-gate for (i = 0; i < num_gw + 1; i++) { 28550Sstevel@tonic-gate (void) bcopy(&gw_addrs[i].gw_addr, &sr_opt->ipsr_addrs[i], 28560Sstevel@tonic-gate sizeof (struct in_addr)); 28570Sstevel@tonic-gate } 28580Sstevel@tonic-gate 28590Sstevel@tonic-gate return (B_TRUE); 28600Sstevel@tonic-gate } 28610Sstevel@tonic-gate 28620Sstevel@tonic-gate /* 28630Sstevel@tonic-gate * Initializes the buffer pointed by opt_bufpp for a IPv6 routing header option 28640Sstevel@tonic-gate * using the gateway addresses stored in gw_addrs. If no buffer is passed, it 28650Sstevel@tonic-gate * allocates one. If a buffer is passed, checks if it's big enough. 28660Sstevel@tonic-gate * On return opt_buf_len points to the buffer length which we need later for the 28670Sstevel@tonic-gate * setsockopt() call, and opt_bufpp points to the newly allocated or already 28680Sstevel@tonic-gate * passed buffer. Returns B_FALSE if a library function call fails or passed 28690Sstevel@tonic-gate * buffer is not big enough, B_TRUE otherwise. 28700Sstevel@tonic-gate */ 28710Sstevel@tonic-gate static boolean_t 28720Sstevel@tonic-gate prepare_optbuf6(struct gateway *gw_addrs, int num_gw, char **opt_bufpp, 28730Sstevel@tonic-gate size_t *opt_buf_len) 28740Sstevel@tonic-gate { 28750Sstevel@tonic-gate char *opt_bufp; 28760Sstevel@tonic-gate size_t needed_buflen; 28770Sstevel@tonic-gate int i; 28780Sstevel@tonic-gate 28790Sstevel@tonic-gate needed_buflen = inet6_rth_space(IPV6_RTHDR_TYPE_0, num_gw); 28800Sstevel@tonic-gate 28810Sstevel@tonic-gate if (*opt_bufpp != NULL) { 28820Sstevel@tonic-gate /* check if the passed buffer is big enough */ 28830Sstevel@tonic-gate if (*opt_buf_len < needed_buflen) { 28840Sstevel@tonic-gate (void) fprintf(stderr, 28850Sstevel@tonic-gate "telnet: buffer too small for IPv6 routing " 28860Sstevel@tonic-gate "header option\n"); 28870Sstevel@tonic-gate return (B_FALSE); 28880Sstevel@tonic-gate } 28890Sstevel@tonic-gate } else { 28900Sstevel@tonic-gate *opt_bufpp = malloc(needed_buflen); 28910Sstevel@tonic-gate if (*opt_bufpp == NULL) { 28920Sstevel@tonic-gate perror("telnet: malloc"); 28930Sstevel@tonic-gate return (B_FALSE); 28940Sstevel@tonic-gate } 28950Sstevel@tonic-gate } 28960Sstevel@tonic-gate *opt_buf_len = needed_buflen; 28970Sstevel@tonic-gate opt_bufp = *opt_bufpp; 28980Sstevel@tonic-gate 28990Sstevel@tonic-gate /* 29000Sstevel@tonic-gate * Initialize the buffer to be used for IPv6 routing header type 0. 29010Sstevel@tonic-gate */ 29020Sstevel@tonic-gate if (inet6_rth_init(opt_bufp, needed_buflen, IPV6_RTHDR_TYPE_0, 29030Sstevel@tonic-gate num_gw) == NULL) { 29040Sstevel@tonic-gate perror("telnet: inet6_rth_init"); 29050Sstevel@tonic-gate return (B_FALSE); 29060Sstevel@tonic-gate } 29070Sstevel@tonic-gate 29080Sstevel@tonic-gate /* 29090Sstevel@tonic-gate * Add gateways one by one. 29100Sstevel@tonic-gate */ 29110Sstevel@tonic-gate for (i = 0; i < num_gw; i++) { 29120Sstevel@tonic-gate if (inet6_rth_add(opt_bufp, &gw_addrs[i].gw_addr6) == -1) { 29130Sstevel@tonic-gate perror("telnet: inet6_rth_add"); 29140Sstevel@tonic-gate return (B_FALSE); 29150Sstevel@tonic-gate } 29160Sstevel@tonic-gate } 29170Sstevel@tonic-gate 29180Sstevel@tonic-gate /* successful operation */ 29190Sstevel@tonic-gate return (B_TRUE); 29200Sstevel@tonic-gate } 29210Sstevel@tonic-gate 29220Sstevel@tonic-gate int 29230Sstevel@tonic-gate tn(argc, argv) 29240Sstevel@tonic-gate int argc; 29250Sstevel@tonic-gate char *argv[]; 29260Sstevel@tonic-gate { 29270Sstevel@tonic-gate struct addrinfo *host = NULL; 29280Sstevel@tonic-gate struct addrinfo *h; 29290Sstevel@tonic-gate struct sockaddr_in6 sin6; 29300Sstevel@tonic-gate struct sockaddr_in sin; 29310Sstevel@tonic-gate struct in6_addr addr6; 29320Sstevel@tonic-gate struct in_addr addr; 29330Sstevel@tonic-gate void *addrp; 29340Sstevel@tonic-gate struct gateway *gw_addrs; 29350Sstevel@tonic-gate char *hostname_list[MAXMAX_GATEWAY + 1] = {NULL}; 29360Sstevel@tonic-gate char *opt_buf6 = NULL; /* used for IPv6 routing header */ 29370Sstevel@tonic-gate size_t opt_buf_len6 = 0; 29380Sstevel@tonic-gate uchar_t src_rtng_type; /* type of IPv4 source routing */ 29390Sstevel@tonic-gate struct servent *sp = 0; 29400Sstevel@tonic-gate char *opt_buf = NULL; /* used for IPv4 source routing */ 29410Sstevel@tonic-gate size_t opt_buf_len = 0; 29420Sstevel@tonic-gate char *cmd; 29430Sstevel@tonic-gate char *hostp = NULL; 29440Sstevel@tonic-gate char *portp = NULL; 29450Sstevel@tonic-gate char *user = NULL; 29460Sstevel@tonic-gate #ifdef ICMD 29470Sstevel@tonic-gate char *itelnet_host; 29480Sstevel@tonic-gate char *real_host; 29490Sstevel@tonic-gate unsigned short dest_port; 29500Sstevel@tonic-gate #endif /* ICMD */ 29510Sstevel@tonic-gate /* 29520Sstevel@tonic-gate * The two strings at the end of this function are 24 and 39 29530Sstevel@tonic-gate * characters long (minus the %.*s in the format strings). Add 29540Sstevel@tonic-gate * one for the null terminator making the longest print string 40. 29550Sstevel@tonic-gate */ 29560Sstevel@tonic-gate char buf[MAXHOSTNAMELEN+40]; 29570Sstevel@tonic-gate /* 29580Sstevel@tonic-gate * In the case of ICMD defined, dest_port will contain the real port 29590Sstevel@tonic-gate * we are trying to telnet to, and target_port will contain 29600Sstevel@tonic-gate * "telnet-passthru" port. 29610Sstevel@tonic-gate */ 29620Sstevel@tonic-gate unsigned short target_port; 29630Sstevel@tonic-gate char abuf[INET6_ADDRSTRLEN]; 29640Sstevel@tonic-gate int num_gw; 29650Sstevel@tonic-gate int ret_val; 29660Sstevel@tonic-gate boolean_t is_v4mapped; 29670Sstevel@tonic-gate /* 29680Sstevel@tonic-gate * Type of addresses we'll try to connect to (ALL_ADDRS, ONLY_V6, 29690Sstevel@tonic-gate * ONLY_V4). 29700Sstevel@tonic-gate */ 29710Sstevel@tonic-gate int addr_type; 29720Sstevel@tonic-gate 29730Sstevel@tonic-gate /* clear the socket address prior to use */ 29740Sstevel@tonic-gate (void) memset(&sin6, '\0', sizeof (sin6)); 29750Sstevel@tonic-gate sin6.sin6_family = AF_INET6; 29760Sstevel@tonic-gate 29770Sstevel@tonic-gate (void) memset(&sin, '\0', sizeof (sin)); 29780Sstevel@tonic-gate sin.sin_family = AF_INET; 29790Sstevel@tonic-gate 29800Sstevel@tonic-gate if (connected) { 29810Sstevel@tonic-gate (void) printf("?Already connected to %s\n", hostname); 29820Sstevel@tonic-gate return (0); 29830Sstevel@tonic-gate } 29840Sstevel@tonic-gate #ifdef ICMD 29850Sstevel@tonic-gate itelnet_host = getenv("INTERNET_HOST"); 29860Sstevel@tonic-gate if (itelnet_host == NULL || itelnet_host[0] == '\0') { 29870Sstevel@tonic-gate (void) printf("INTERNET_HOST environment variable undefined\n"); 29880Sstevel@tonic-gate goto tn_exit; 29890Sstevel@tonic-gate } 29900Sstevel@tonic-gate #endif 29910Sstevel@tonic-gate if (argc < 2) { 29920Sstevel@tonic-gate (void) printf("(to) "); 29930Sstevel@tonic-gate if (GetAndAppendString(&line, &linesize, "open ", 29940Sstevel@tonic-gate stdin) == NULL) { 29950Sstevel@tonic-gate if (!feof(stdin)) { 29960Sstevel@tonic-gate perror("telnet"); 29970Sstevel@tonic-gate goto tn_exit; 29980Sstevel@tonic-gate } 29990Sstevel@tonic-gate } 30000Sstevel@tonic-gate makeargv(); 30010Sstevel@tonic-gate argc = margc; 30020Sstevel@tonic-gate argv = margv; 30030Sstevel@tonic-gate } 30040Sstevel@tonic-gate cmd = *argv; 30050Sstevel@tonic-gate --argc; ++argv; 30060Sstevel@tonic-gate while (argc) { 30070Sstevel@tonic-gate if (isprefix(*argv, "help") == 4 || isprefix(*argv, "?") == 1) 30080Sstevel@tonic-gate goto usage; 30090Sstevel@tonic-gate if (strcmp(*argv, "-l") == 0) { 30100Sstevel@tonic-gate --argc; ++argv; 30110Sstevel@tonic-gate if (argc == 0) 30120Sstevel@tonic-gate goto usage; 30130Sstevel@tonic-gate user = *argv++; 30140Sstevel@tonic-gate --argc; 30150Sstevel@tonic-gate continue; 30160Sstevel@tonic-gate } 30170Sstevel@tonic-gate if (strcmp(*argv, "-a") == 0) { 30180Sstevel@tonic-gate --argc; ++argv; 30190Sstevel@tonic-gate autologin = autologin_set = 1; 30200Sstevel@tonic-gate continue; 30210Sstevel@tonic-gate } 30220Sstevel@tonic-gate if (hostp == 0) { 30230Sstevel@tonic-gate hostp = *argv++; 30240Sstevel@tonic-gate --argc; 30250Sstevel@tonic-gate continue; 30260Sstevel@tonic-gate } 30270Sstevel@tonic-gate if (portp == 0) { 30280Sstevel@tonic-gate portp = *argv++; 30290Sstevel@tonic-gate --argc; 30300Sstevel@tonic-gate /* 30310Sstevel@tonic-gate * Do we treat this like a telnet port or raw? 30320Sstevel@tonic-gate */ 30330Sstevel@tonic-gate if (*portp == '-') { 30340Sstevel@tonic-gate portp++; 30350Sstevel@tonic-gate telnetport = 1; 30360Sstevel@tonic-gate } else 30370Sstevel@tonic-gate telnetport = 0; 30380Sstevel@tonic-gate continue; 30390Sstevel@tonic-gate } 30400Sstevel@tonic-gate usage: 30410Sstevel@tonic-gate (void) printf( 30420Sstevel@tonic-gate "usage: %s [-l user] [-a] host-name [port]\n", cmd); 30430Sstevel@tonic-gate goto tn_exit; 30440Sstevel@tonic-gate } 30450Sstevel@tonic-gate if (hostp == 0) 30460Sstevel@tonic-gate goto usage; 30470Sstevel@tonic-gate 30480Sstevel@tonic-gate #ifdef ICMD 30490Sstevel@tonic-gate /* 30500Sstevel@tonic-gate * For setup phase treat the relay host as the target host. 30510Sstevel@tonic-gate */ 30520Sstevel@tonic-gate real_host = hostp; 30530Sstevel@tonic-gate hostp = itelnet_host; 30540Sstevel@tonic-gate #endif 30550Sstevel@tonic-gate num_gw = parse_input(hostp, hostname_list, &src_rtng_type); 30560Sstevel@tonic-gate if (num_gw < 0) { 30570Sstevel@tonic-gate goto tn_exit; 30580Sstevel@tonic-gate } 30590Sstevel@tonic-gate 30600Sstevel@tonic-gate /* Last host in the hostname_list is the target */ 30610Sstevel@tonic-gate hostp = hostname_list[num_gw]; 30620Sstevel@tonic-gate 30630Sstevel@tonic-gate host = resolve_hosts(hostname_list, num_gw, &gw_addrs, &addr_type, 30640Sstevel@tonic-gate portp); 30650Sstevel@tonic-gate if (host == NULL) { 30660Sstevel@tonic-gate goto tn_exit; 30670Sstevel@tonic-gate } 30680Sstevel@tonic-gate 30690Sstevel@tonic-gate /* 30700Sstevel@tonic-gate * Check if number of gateways is less than max. available 30710Sstevel@tonic-gate */ 30720Sstevel@tonic-gate if ((addr_type == ALL_ADDRS || addr_type == ONLY_V6) && 30730Sstevel@tonic-gate num_gw > MAX_GATEWAY6) { 30740Sstevel@tonic-gate (void) fprintf(stderr, "telnet: too many IPv6 gateways\n"); 30750Sstevel@tonic-gate goto tn_exit; 30760Sstevel@tonic-gate } 30770Sstevel@tonic-gate 30780Sstevel@tonic-gate if ((addr_type == ALL_ADDRS || addr_type == ONLY_V4) && 30790Sstevel@tonic-gate num_gw > MAX_GATEWAY) { 30800Sstevel@tonic-gate (void) fprintf(stderr, "telnet: too many IPv4 gateways\n"); 30810Sstevel@tonic-gate goto tn_exit; 30820Sstevel@tonic-gate } 30830Sstevel@tonic-gate 30840Sstevel@tonic-gate /* 30850Sstevel@tonic-gate * If we pass a literal IPv4 address to getaddrinfo(), in the 30860Sstevel@tonic-gate * returned addrinfo structure, hostname is the IPv4-mapped IPv6 30870Sstevel@tonic-gate * address string. We prefer to preserve the literal IPv4 address 30880Sstevel@tonic-gate * string as the hostname. Also, if the hostname entered by the 30890Sstevel@tonic-gate * user is IPv4-mapped IPv6 address, we'll downgrade it to IPv4 30900Sstevel@tonic-gate * address. 30910Sstevel@tonic-gate */ 30920Sstevel@tonic-gate if (inet_addr(hostp) != (in_addr_t)-1) { 30930Sstevel@tonic-gate /* this is a literal IPv4 address */ 30940Sstevel@tonic-gate (void) strlcpy(_hostname, hostp, sizeof (_hostname)); 30950Sstevel@tonic-gate } else if ((inet_pton(AF_INET6, hostp, &addr6) > 0) && 30960Sstevel@tonic-gate IN6_IS_ADDR_V4MAPPED(&addr6)) { 30970Sstevel@tonic-gate /* this is a IPv4-mapped IPv6 address */ 30980Sstevel@tonic-gate IN6_V4MAPPED_TO_INADDR(&addr6, &addr); 30990Sstevel@tonic-gate (void) inet_ntop(AF_INET, &addr, _hostname, sizeof (_hostname)); 31000Sstevel@tonic-gate } else { 31010Sstevel@tonic-gate (void) strlcpy(_hostname, host->ai_canonname, 31020Sstevel@tonic-gate sizeof (_hostname)); 31030Sstevel@tonic-gate } 31040Sstevel@tonic-gate hostname = _hostname; 31050Sstevel@tonic-gate 31060Sstevel@tonic-gate if (portp == NULL) { 31070Sstevel@tonic-gate telnetport = 1; 31080Sstevel@tonic-gate } 31090Sstevel@tonic-gate 31100Sstevel@tonic-gate if (host->ai_family == AF_INET) { 31110Sstevel@tonic-gate target_port = ((struct sockaddr_in *)(host->ai_addr))->sin_port; 31120Sstevel@tonic-gate } else { 31130Sstevel@tonic-gate target_port = ((struct sockaddr_in6 *)(host->ai_addr)) 31140Sstevel@tonic-gate ->sin6_port; 31150Sstevel@tonic-gate } 31160Sstevel@tonic-gate 31170Sstevel@tonic-gate #ifdef ICMD 31180Sstevel@tonic-gate /* 31190Sstevel@tonic-gate * Since we pass the port number as an ascii string to the proxy, 31200Sstevel@tonic-gate * we need it in host format. 31210Sstevel@tonic-gate */ 31220Sstevel@tonic-gate dest_port = ntohs(target_port); 31230Sstevel@tonic-gate sp = getservbyname("telnet-passthru", "tcp"); 31240Sstevel@tonic-gate if (sp == 0) { 31250Sstevel@tonic-gate (void) fprintf(stderr, 31260Sstevel@tonic-gate "telnet: tcp/telnet-passthru: unknown service\n"); 31270Sstevel@tonic-gate goto tn_exit; 31280Sstevel@tonic-gate } 31290Sstevel@tonic-gate target_port = sp->s_port; 31300Sstevel@tonic-gate #endif 31310Sstevel@tonic-gate h = host; 31320Sstevel@tonic-gate 31330Sstevel@tonic-gate /* 31340Sstevel@tonic-gate * For IPv6 source routing, we need to initialize option buffer only 31350Sstevel@tonic-gate * once. 31360Sstevel@tonic-gate */ 31370Sstevel@tonic-gate if (num_gw > 0 && (addr_type == ALL_ADDRS || addr_type == ONLY_V6)) { 31380Sstevel@tonic-gate if (!prepare_optbuf6(gw_addrs, num_gw, &opt_buf6, 31390Sstevel@tonic-gate &opt_buf_len6)) { 31400Sstevel@tonic-gate goto tn_exit; 31410Sstevel@tonic-gate } 31420Sstevel@tonic-gate } 31430Sstevel@tonic-gate 31440Sstevel@tonic-gate /* 31450Sstevel@tonic-gate * We procure the Kerberos config files options only 31460Sstevel@tonic-gate * if the user has choosen Krb5 authentication. 31470Sstevel@tonic-gate */ 31480Sstevel@tonic-gate if (krb5auth_flag > 0) { 31490Sstevel@tonic-gate krb5_profile_get_options(hostname, telnet_krb5_realm, 31500Sstevel@tonic-gate config_file_options); 31510Sstevel@tonic-gate } 31520Sstevel@tonic-gate 31530Sstevel@tonic-gate if (encrypt_flag) { 31540Sstevel@tonic-gate extern boolean_t auth_enable_encrypt; 31550Sstevel@tonic-gate if (krb5_privacy_allowed()) { 31560Sstevel@tonic-gate encrypt_auto(1); 31570Sstevel@tonic-gate decrypt_auto(1); 31580Sstevel@tonic-gate wantencryption = B_TRUE; 31590Sstevel@tonic-gate autologin = 1; 31600Sstevel@tonic-gate auth_enable_encrypt = B_TRUE; 31610Sstevel@tonic-gate } else { 31620Sstevel@tonic-gate (void) fprintf(stderr, gettext( 31630Sstevel@tonic-gate "%s:Encryption not supported.\n"), prompt); 31640Sstevel@tonic-gate exit(1); 31650Sstevel@tonic-gate } 31660Sstevel@tonic-gate } 31670Sstevel@tonic-gate 31680Sstevel@tonic-gate if (forward_flag && forwardable_flag) { 31690Sstevel@tonic-gate (void) fprintf(stderr, gettext( 31700Sstevel@tonic-gate "Error in krb5 configuration file. " 31710Sstevel@tonic-gate "Both forward and forwardable are set.\n")); 31720Sstevel@tonic-gate exit(1); 31730Sstevel@tonic-gate } 31740Sstevel@tonic-gate if (forwardable_flag) { 31750Sstevel@tonic-gate forward_flags |= OPTS_FORWARD_CREDS | OPTS_FORWARDABLE_CREDS; 31760Sstevel@tonic-gate } else if (forward_flag) 31770Sstevel@tonic-gate forward_flags |= OPTS_FORWARD_CREDS; 31780Sstevel@tonic-gate 31790Sstevel@tonic-gate 31800Sstevel@tonic-gate do { 31810Sstevel@tonic-gate /* 31820Sstevel@tonic-gate * Search for an address of desired type in the IP address list 31830Sstevel@tonic-gate * of the target. 31840Sstevel@tonic-gate */ 31850Sstevel@tonic-gate while (h != NULL) { 31860Sstevel@tonic-gate struct sockaddr_in6 *addr; 31870Sstevel@tonic-gate 31880Sstevel@tonic-gate addr = (struct sockaddr_in6 *)h->ai_addr; 31890Sstevel@tonic-gate 31900Sstevel@tonic-gate if (h->ai_family == AF_INET6) 31910Sstevel@tonic-gate is_v4mapped = 31920Sstevel@tonic-gate IN6_IS_ADDR_V4MAPPED(&addr->sin6_addr); 31930Sstevel@tonic-gate else 31940Sstevel@tonic-gate is_v4mapped = B_FALSE; 31950Sstevel@tonic-gate 31960Sstevel@tonic-gate if (addr_type == ALL_ADDRS || 31970Sstevel@tonic-gate (addr_type == ONLY_V6 && 31980Sstevel@tonic-gate h->ai_family == AF_INET6) || 31990Sstevel@tonic-gate (addr_type == ONLY_V4 && 32000Sstevel@tonic-gate (h->ai_family == AF_INET || is_v4mapped))) 32010Sstevel@tonic-gate break; 32020Sstevel@tonic-gate 32030Sstevel@tonic-gate /* skip undesired typed addresses */ 32040Sstevel@tonic-gate h = h->ai_next; 32050Sstevel@tonic-gate } 32060Sstevel@tonic-gate 32070Sstevel@tonic-gate if (h == NULL) { 32080Sstevel@tonic-gate fprintf(stderr, 32090Sstevel@tonic-gate "telnet: Unable to connect to remote host"); 32100Sstevel@tonic-gate goto tn_exit; 32110Sstevel@tonic-gate } 32120Sstevel@tonic-gate 32130Sstevel@tonic-gate /* 32140Sstevel@tonic-gate * We need to open a socket with a family matching the type of 32150Sstevel@tonic-gate * address we are trying to connect to. This is because we 32160Sstevel@tonic-gate * deal with IPv4 options and IPv6 extension headers. 32170Sstevel@tonic-gate */ 32180Sstevel@tonic-gate if (h->ai_family == AF_INET) { 32190Sstevel@tonic-gate addrp = &((struct sockaddr_in *)(h->ai_addr))->sin_addr; 32200Sstevel@tonic-gate ((struct sockaddr_in *)(h->ai_addr))->sin_port = 32210Sstevel@tonic-gate target_port; 32220Sstevel@tonic-gate } else { 32230Sstevel@tonic-gate addrp = &((struct sockaddr_in6 *)(h->ai_addr)) 32240Sstevel@tonic-gate ->sin6_addr; 32250Sstevel@tonic-gate ((struct sockaddr_in6 *)(h->ai_addr))->sin6_port = 32260Sstevel@tonic-gate target_port; 32270Sstevel@tonic-gate } 32280Sstevel@tonic-gate 32290Sstevel@tonic-gate (void) printf("Trying %s...\n", inet_ntop(h->ai_family, 32300Sstevel@tonic-gate addrp, abuf, sizeof (abuf))); 32310Sstevel@tonic-gate 32320Sstevel@tonic-gate net = socket(h->ai_family, SOCK_STREAM, 0); 32330Sstevel@tonic-gate 32340Sstevel@tonic-gate if (net < 0) { 32350Sstevel@tonic-gate perror("telnet: socket"); 32360Sstevel@tonic-gate goto tn_exit; 32370Sstevel@tonic-gate } 32380Sstevel@tonic-gate #ifndef ICMD 32390Sstevel@tonic-gate if (num_gw > 0) { 32400Sstevel@tonic-gate if (h->ai_family == AF_INET || is_v4mapped) { 32410Sstevel@tonic-gate if (!prepare_optbuf(gw_addrs, num_gw, &opt_buf, 32420Sstevel@tonic-gate &opt_buf_len, addrp, src_rtng_type)) { 32430Sstevel@tonic-gate goto tn_exit; 32440Sstevel@tonic-gate } 32450Sstevel@tonic-gate 32460Sstevel@tonic-gate if (setsockopt(net, IPPROTO_IP, IP_OPTIONS, 32470Sstevel@tonic-gate opt_buf, opt_buf_len) < 0) 32480Sstevel@tonic-gate perror("setsockopt (IP_OPTIONS)"); 32490Sstevel@tonic-gate } else { 32500Sstevel@tonic-gate if (setsockopt(net, IPPROTO_IPV6, IPV6_RTHDR, 32510Sstevel@tonic-gate opt_buf6, opt_buf_len6) < 0) 32520Sstevel@tonic-gate perror("setsockopt (IPV6_RTHDR)"); 32530Sstevel@tonic-gate } 32540Sstevel@tonic-gate } 32550Sstevel@tonic-gate #endif 32560Sstevel@tonic-gate #if defined(USE_TOS) 32570Sstevel@tonic-gate if (is_v4mapped) { 32580Sstevel@tonic-gate if (tos < 0) 32590Sstevel@tonic-gate tos = 020; /* Low Delay bit */ 32600Sstevel@tonic-gate if (tos && 32610Sstevel@tonic-gate (setsockopt(net, IPPROTO_IP, IP_TOS, 32620Sstevel@tonic-gate &tos, sizeof (int)) < 0) && 32630Sstevel@tonic-gate (errno != ENOPROTOOPT)) 32640Sstevel@tonic-gate perror("telnet: setsockopt (IP_TOS) (ignored)"); 32650Sstevel@tonic-gate } 32660Sstevel@tonic-gate #endif /* defined(USE_TOS) */ 32670Sstevel@tonic-gate 32680Sstevel@tonic-gate if (debug && SetSockOpt(net, SOL_SOCKET, SO_DEBUG, 1) < 0) { 32690Sstevel@tonic-gate perror("setsockopt (SO_DEBUG)"); 32700Sstevel@tonic-gate } 32710Sstevel@tonic-gate 32720Sstevel@tonic-gate ret_val = connect(net, h->ai_addr, h->ai_addrlen); 32730Sstevel@tonic-gate 32740Sstevel@tonic-gate /* 32750Sstevel@tonic-gate * If failed, try the next address of the target. 32760Sstevel@tonic-gate */ 32770Sstevel@tonic-gate if (ret_val < 0) { 32780Sstevel@tonic-gate Close(&net); 32790Sstevel@tonic-gate if (h->ai_next != NULL) { 32800Sstevel@tonic-gate 32810Sstevel@tonic-gate int oerrno = errno; 32820Sstevel@tonic-gate 32830Sstevel@tonic-gate (void) fprintf(stderr, 32840Sstevel@tonic-gate "telnet: connect to address %s: ", abuf); 32850Sstevel@tonic-gate errno = oerrno; 32860Sstevel@tonic-gate perror((char *)0); 32870Sstevel@tonic-gate 32880Sstevel@tonic-gate h = h->ai_next; 32890Sstevel@tonic-gate continue; 32900Sstevel@tonic-gate } 32910Sstevel@tonic-gate perror("telnet: Unable to connect to remote host"); 32920Sstevel@tonic-gate goto tn_exit; 32930Sstevel@tonic-gate } 32940Sstevel@tonic-gate connected++; 32950Sstevel@tonic-gate } while (connected == 0); 32960Sstevel@tonic-gate freeaddrinfo(host); 32970Sstevel@tonic-gate host = NULL; 32980Sstevel@tonic-gate #ifdef ICMD 32990Sstevel@tonic-gate /* 33000Sstevel@tonic-gate * Do initial protocol to connect to farther end... 33010Sstevel@tonic-gate */ 33020Sstevel@tonic-gate { 33030Sstevel@tonic-gate char buf[1024]; 33040Sstevel@tonic-gate (void) sprintf(buf, "%s %d\n", real_host, (int)dest_port); 33050Sstevel@tonic-gate write(net, buf, strlen(buf)); 33060Sstevel@tonic-gate } 33070Sstevel@tonic-gate #endif 33080Sstevel@tonic-gate if (cmdrc(hostp, hostname) != 0) 33090Sstevel@tonic-gate goto tn_exit; 33100Sstevel@tonic-gate FreeHostnameList(hostname_list); 33110Sstevel@tonic-gate if (autologin && user == NULL) { 33120Sstevel@tonic-gate struct passwd *pw; 33130Sstevel@tonic-gate 33140Sstevel@tonic-gate user = getenv("LOGNAME"); 33150Sstevel@tonic-gate if (user == NULL || 33160Sstevel@tonic-gate ((pw = getpwnam(user)) != NULL) && 33170Sstevel@tonic-gate pw->pw_uid != getuid()) { 33180Sstevel@tonic-gate if (pw = getpwuid(getuid())) 33190Sstevel@tonic-gate user = pw->pw_name; 33200Sstevel@tonic-gate else 33210Sstevel@tonic-gate user = NULL; 33220Sstevel@tonic-gate } 33230Sstevel@tonic-gate } 33240Sstevel@tonic-gate 33250Sstevel@tonic-gate if (user) { 33260Sstevel@tonic-gate if (env_define((unsigned char *)"USER", (unsigned char *)user)) 33270Sstevel@tonic-gate env_export((unsigned char *)"USER"); 33280Sstevel@tonic-gate else { 33290Sstevel@tonic-gate /* Clean up and exit. */ 33300Sstevel@tonic-gate Close(&net); 33310Sstevel@tonic-gate (void) snprintf(buf, sizeof (buf), 33320Sstevel@tonic-gate "Connection to %.*s closed.\n", 33330Sstevel@tonic-gate MAXHOSTNAMELEN, hostname); 33340Sstevel@tonic-gate ExitString(buf, EXIT_FAILURE); 33350Sstevel@tonic-gate 33360Sstevel@tonic-gate /* NOTREACHED */ 33370Sstevel@tonic-gate } 33380Sstevel@tonic-gate } 33390Sstevel@tonic-gate (void) call(3, status, "status", "notmuch"); 33400Sstevel@tonic-gate if (setjmp(peerdied) == 0) 33410Sstevel@tonic-gate telnet(user); 33420Sstevel@tonic-gate 33430Sstevel@tonic-gate Close(&net); 33440Sstevel@tonic-gate 33450Sstevel@tonic-gate (void) snprintf(buf, sizeof (buf), 33460Sstevel@tonic-gate "Connection to %.*s closed by foreign host.\n", 33470Sstevel@tonic-gate MAXHOSTNAMELEN, hostname); 33480Sstevel@tonic-gate ExitString(buf, EXIT_FAILURE); 33490Sstevel@tonic-gate 33500Sstevel@tonic-gate /*NOTREACHED*/ 33510Sstevel@tonic-gate 33520Sstevel@tonic-gate tn_exit: 33530Sstevel@tonic-gate FreeHostnameList(hostname_list); 33540Sstevel@tonic-gate Close(&net); 33550Sstevel@tonic-gate connected = 0; 33560Sstevel@tonic-gate if (host != NULL) 33570Sstevel@tonic-gate freeaddrinfo(host); 33580Sstevel@tonic-gate return (0); 33590Sstevel@tonic-gate } 33600Sstevel@tonic-gate 33610Sstevel@tonic-gate #define HELPINDENT (sizeof ("connect")) 33620Sstevel@tonic-gate 33630Sstevel@tonic-gate static char openhelp[] = "connect to a site"; 33640Sstevel@tonic-gate static char closehelp[] = "close current connection"; 33650Sstevel@tonic-gate static char logouthelp[] = 33660Sstevel@tonic-gate "forcibly logout remote user and close the connection"; 33670Sstevel@tonic-gate static char quithelp[] = "exit telnet"; 33680Sstevel@tonic-gate static char statushelp[] = "print status information"; 33690Sstevel@tonic-gate static char helphelp[] = "print help information"; 33700Sstevel@tonic-gate static char sendhelp[] = 33710Sstevel@tonic-gate "transmit special characters ('send ?' for more)"; 33720Sstevel@tonic-gate static char sethelp[] = "set operating parameters ('set ?' for more)"; 33730Sstevel@tonic-gate static char unsethelp[] = "unset operating parameters ('unset ?' for more)"; 33740Sstevel@tonic-gate static char togglestring[] = 33750Sstevel@tonic-gate "toggle operating parameters ('toggle ?' for more)"; 33760Sstevel@tonic-gate static char slchelp[] = "change state of special charaters ('slc ?' for more)"; 33770Sstevel@tonic-gate static char displayhelp[] = "display operating parameters"; 33780Sstevel@tonic-gate static char authhelp[] = 33790Sstevel@tonic-gate "turn on (off) authentication ('auth ?' for more)"; 33800Sstevel@tonic-gate static char forwardhelp[] = 33810Sstevel@tonic-gate "turn on (off) credential forwarding ('forward ?' for more)"; 33820Sstevel@tonic-gate static char encrypthelp[] = 33830Sstevel@tonic-gate "turn on (off) encryption ('encrypt ?' for more)"; 33840Sstevel@tonic-gate static char zhelp[] = "suspend telnet"; 33850Sstevel@tonic-gate static char shellhelp[] = "invoke a subshell"; 33860Sstevel@tonic-gate static char envhelp[] = "change environment variables ('environ ?' for more)"; 33870Sstevel@tonic-gate static char modestring[] = 33880Sstevel@tonic-gate "try to enter line or character mode ('mode ?' for more)"; 33890Sstevel@tonic-gate 33900Sstevel@tonic-gate static int help(); 33910Sstevel@tonic-gate 33920Sstevel@tonic-gate static Command cmdtab[] = { 33930Sstevel@tonic-gate { "close", closehelp, bye, 1 }, 33940Sstevel@tonic-gate { "logout", logouthelp, logout, 1 }, 33950Sstevel@tonic-gate { "display", displayhelp, display, 0 }, 33960Sstevel@tonic-gate { "mode", modestring, modecmd, 0 }, 33970Sstevel@tonic-gate { "open", openhelp, tn, 0 }, 33980Sstevel@tonic-gate { "quit", quithelp, quit, 0 }, 33990Sstevel@tonic-gate { "send", sendhelp, sendcmd, 0 }, 34000Sstevel@tonic-gate { "set", sethelp, setcmd, 0 }, 34010Sstevel@tonic-gate { "unset", unsethelp, unsetcmd, 0 }, 34020Sstevel@tonic-gate { "status", statushelp, status, 0 }, 34030Sstevel@tonic-gate { "toggle", togglestring, toggle, 0 }, 34040Sstevel@tonic-gate { "slc", slchelp, slccmd, 0 }, 34050Sstevel@tonic-gate { "auth", authhelp, auth_cmd, 0 }, 34060Sstevel@tonic-gate { "encrypt", encrypthelp, encrypt_cmd, 0 }, 34070Sstevel@tonic-gate { "forward", forwardhelp, forw_cmd, 0 }, 34080Sstevel@tonic-gate { "z", zhelp, suspend, 0 }, 34090Sstevel@tonic-gate { "!", shellhelp, shell, 0 }, 34100Sstevel@tonic-gate { "environ", envhelp, env_cmd, 0 }, 34110Sstevel@tonic-gate { "?", helphelp, help, 0 }, 34120Sstevel@tonic-gate 0 34130Sstevel@tonic-gate }; 34140Sstevel@tonic-gate 34150Sstevel@tonic-gate 34160Sstevel@tonic-gate static Command cmdtab2[] = { 34170Sstevel@tonic-gate { "help", 0, help, 0 }, 34180Sstevel@tonic-gate { "escape", 0, setescape, 0 }, 34190Sstevel@tonic-gate { "crmod", 0, togcrmod, 0 }, 34200Sstevel@tonic-gate 0 34210Sstevel@tonic-gate }; 34220Sstevel@tonic-gate 34230Sstevel@tonic-gate 34240Sstevel@tonic-gate /* 34250Sstevel@tonic-gate * Call routine with argc, argv set from args. 34260Sstevel@tonic-gate * Uses /usr/include/stdarg.h 34270Sstevel@tonic-gate */ 34280Sstevel@tonic-gate #define MAXVARGS 100 34290Sstevel@tonic-gate /*VARARGS1*/ 34300Sstevel@tonic-gate static void 34310Sstevel@tonic-gate call(int n_ptrs, ...) 34320Sstevel@tonic-gate { 34330Sstevel@tonic-gate va_list ap; 34340Sstevel@tonic-gate typedef int (*intrtn_t)(); 34350Sstevel@tonic-gate intrtn_t routine; 34360Sstevel@tonic-gate char *args[MAXVARGS+1]; /* leave 1 for trailing NULL */ 34370Sstevel@tonic-gate int argno = 0; 34380Sstevel@tonic-gate 34390Sstevel@tonic-gate if (n_ptrs > MAXVARGS) 34400Sstevel@tonic-gate n_ptrs = MAXVARGS; 3441*473Sbw va_start(ap, n_ptrs); 34420Sstevel@tonic-gate 34430Sstevel@tonic-gate routine = (va_arg(ap, intrtn_t)); /* extract the routine's name */ 34440Sstevel@tonic-gate n_ptrs--; 34450Sstevel@tonic-gate 34460Sstevel@tonic-gate while (argno < n_ptrs) /* extract the routine's args */ 34470Sstevel@tonic-gate args[argno++] = va_arg(ap, char *); 34480Sstevel@tonic-gate args[argno] = NULL; /* NULL terminate for good luck */ 34490Sstevel@tonic-gate va_end(ap); 34500Sstevel@tonic-gate 34510Sstevel@tonic-gate (*routine)(argno, args); 34520Sstevel@tonic-gate } 34530Sstevel@tonic-gate 34540Sstevel@tonic-gate 34550Sstevel@tonic-gate static Command * 34560Sstevel@tonic-gate getcmd(name) 34570Sstevel@tonic-gate char *name; 34580Sstevel@tonic-gate { 34590Sstevel@tonic-gate Command *cm; 34600Sstevel@tonic-gate 34610Sstevel@tonic-gate if (cm = (Command *) genget(name, (char **)cmdtab, sizeof (Command))) 34620Sstevel@tonic-gate return (cm); 34630Sstevel@tonic-gate return (Command *) genget(name, (char **)cmdtab2, sizeof (Command)); 34640Sstevel@tonic-gate } 34650Sstevel@tonic-gate 34660Sstevel@tonic-gate void 34670Sstevel@tonic-gate command(top, tbuf, cnt) 34680Sstevel@tonic-gate int top; 34690Sstevel@tonic-gate char *tbuf; 34700Sstevel@tonic-gate int cnt; 34710Sstevel@tonic-gate { 34720Sstevel@tonic-gate Command *c; 34730Sstevel@tonic-gate 34740Sstevel@tonic-gate setcommandmode(); 34750Sstevel@tonic-gate if (!top) { 34760Sstevel@tonic-gate (void) putchar('\n'); 34770Sstevel@tonic-gate } else { 34780Sstevel@tonic-gate (void) signal(SIGINT, SIG_DFL); 34790Sstevel@tonic-gate (void) signal(SIGQUIT, SIG_DFL); 34800Sstevel@tonic-gate } 34810Sstevel@tonic-gate for (;;) { 34820Sstevel@tonic-gate if (rlogin == _POSIX_VDISABLE) 34830Sstevel@tonic-gate (void) printf("%s> ", prompt); 34840Sstevel@tonic-gate if (tbuf) { 34850Sstevel@tonic-gate char *cp; 34860Sstevel@tonic-gate if (AllocStringBuffer(&line, &linesize, cnt) == NULL) 34870Sstevel@tonic-gate goto command_exit; 34880Sstevel@tonic-gate cp = line; 34890Sstevel@tonic-gate while (cnt > 0 && (*cp++ = *tbuf++) != '\n') 34900Sstevel@tonic-gate cnt--; 34910Sstevel@tonic-gate tbuf = 0; 34920Sstevel@tonic-gate if (cp == line || *--cp != '\n' || cp == line) 34930Sstevel@tonic-gate goto getline; 34940Sstevel@tonic-gate *cp = '\0'; 34950Sstevel@tonic-gate if (rlogin == _POSIX_VDISABLE) 34960Sstevel@tonic-gate (void) printf("%s\n", line); 34970Sstevel@tonic-gate } else { 34980Sstevel@tonic-gate getline: 34990Sstevel@tonic-gate if (rlogin != _POSIX_VDISABLE) 35000Sstevel@tonic-gate (void) printf("%s> ", prompt); 35010Sstevel@tonic-gate if (GetString(&line, &linesize, stdin) == NULL) { 35020Sstevel@tonic-gate if (!feof(stdin)) 35030Sstevel@tonic-gate perror("telnet"); 35040Sstevel@tonic-gate (void) quit(); 35050Sstevel@tonic-gate /*NOTREACHED*/ 35060Sstevel@tonic-gate break; 35070Sstevel@tonic-gate } 35080Sstevel@tonic-gate } 35090Sstevel@tonic-gate if (line[0] == 0) 35100Sstevel@tonic-gate break; 35110Sstevel@tonic-gate makeargv(); 35120Sstevel@tonic-gate if (margv[0] == 0) { 35130Sstevel@tonic-gate break; 35140Sstevel@tonic-gate } 35150Sstevel@tonic-gate c = getcmd(margv[0]); 35160Sstevel@tonic-gate if (Ambiguous(c)) { 35170Sstevel@tonic-gate (void) printf("?Ambiguous command\n"); 35180Sstevel@tonic-gate continue; 35190Sstevel@tonic-gate } 35200Sstevel@tonic-gate if (c == 0) { 35210Sstevel@tonic-gate (void) printf("?Invalid command\n"); 35220Sstevel@tonic-gate continue; 35230Sstevel@tonic-gate } 35240Sstevel@tonic-gate if (c->needconnect && !connected) { 35250Sstevel@tonic-gate (void) printf("?Need to be connected first.\n"); 35260Sstevel@tonic-gate continue; 35270Sstevel@tonic-gate } 35280Sstevel@tonic-gate if ((*c->handler)(margc, margv)) { 35290Sstevel@tonic-gate break; 35300Sstevel@tonic-gate } 35310Sstevel@tonic-gate } 35320Sstevel@tonic-gate command_exit: 35330Sstevel@tonic-gate if (!top) { 35340Sstevel@tonic-gate if (!connected) { 35350Sstevel@tonic-gate longjmp(toplevel, 1); 35360Sstevel@tonic-gate /*NOTREACHED*/ 35370Sstevel@tonic-gate } 35380Sstevel@tonic-gate setconnmode(0); 35390Sstevel@tonic-gate } 35400Sstevel@tonic-gate } 35410Sstevel@tonic-gate 35420Sstevel@tonic-gate /* 35430Sstevel@tonic-gate * Help command. 35440Sstevel@tonic-gate */ 3545*473Sbw static int 35460Sstevel@tonic-gate help(argc, argv) 35470Sstevel@tonic-gate int argc; 35480Sstevel@tonic-gate char *argv[]; 35490Sstevel@tonic-gate { 35500Sstevel@tonic-gate register Command *c; 35510Sstevel@tonic-gate 35520Sstevel@tonic-gate if (argc == 1) { 35530Sstevel@tonic-gate (void) printf( 35540Sstevel@tonic-gate "Commands may be abbreviated. Commands are:\n\n"); 35550Sstevel@tonic-gate for (c = cmdtab; c->name; c++) 35560Sstevel@tonic-gate if (c->help) { 35570Sstevel@tonic-gate (void) printf("%-*s\t%s\n", HELPINDENT, 35580Sstevel@tonic-gate c->name, c->help); 35590Sstevel@tonic-gate } 35600Sstevel@tonic-gate (void) printf("<return>\tleave command mode\n"); 35610Sstevel@tonic-gate return (0); 35620Sstevel@tonic-gate } 35630Sstevel@tonic-gate while (--argc > 0) { 35640Sstevel@tonic-gate register char *arg; 35650Sstevel@tonic-gate arg = *++argv; 35660Sstevel@tonic-gate c = getcmd(arg); 35670Sstevel@tonic-gate if (Ambiguous(c)) 35680Sstevel@tonic-gate (void) printf("?Ambiguous help command %s\n", arg); 35690Sstevel@tonic-gate else if (c == (Command *)0) 35700Sstevel@tonic-gate (void) printf("?Invalid help command %s\n", arg); 35710Sstevel@tonic-gate else if (c->help) { 35720Sstevel@tonic-gate (void) printf("%s\n", c->help); 35730Sstevel@tonic-gate } else { 35740Sstevel@tonic-gate (void) printf("No additional help on %s\n", arg); 35750Sstevel@tonic-gate } 35760Sstevel@tonic-gate } 35770Sstevel@tonic-gate return (0); 35780Sstevel@tonic-gate } 35790Sstevel@tonic-gate 35800Sstevel@tonic-gate static char *rcname = NULL; 35810Sstevel@tonic-gate #define TELNETRC_NAME "telnetrc" 35820Sstevel@tonic-gate #define TELNETRC_COMP "/." TELNETRC_NAME 35830Sstevel@tonic-gate 35840Sstevel@tonic-gate static int 35850Sstevel@tonic-gate cmdrc(char *m1, char *m2) 35860Sstevel@tonic-gate { 35870Sstevel@tonic-gate Command *c; 35880Sstevel@tonic-gate FILE *rcfile = NULL; 35890Sstevel@tonic-gate int gotmachine = 0; 35900Sstevel@tonic-gate int l1 = strlen(m1); 35910Sstevel@tonic-gate int l2 = strlen(m2); 35920Sstevel@tonic-gate char m1save[MAXHOSTNAMELEN]; 35930Sstevel@tonic-gate int ret = 0; 35940Sstevel@tonic-gate char def[] = "DEFAULT"; 35950Sstevel@tonic-gate 35960Sstevel@tonic-gate if (skiprc) 35970Sstevel@tonic-gate goto cmdrc_exit; 35980Sstevel@tonic-gate 35990Sstevel@tonic-gate doing_rc = 1; 36000Sstevel@tonic-gate 36010Sstevel@tonic-gate (void) strlcpy(m1save, m1, sizeof (m1save)); 36020Sstevel@tonic-gate m1 = m1save; 36030Sstevel@tonic-gate 36040Sstevel@tonic-gate if (rcname == NULL) { 36050Sstevel@tonic-gate char *homedir; 36060Sstevel@tonic-gate unsigned rcbuflen; 36070Sstevel@tonic-gate 36080Sstevel@tonic-gate if ((homedir = getenv("HOME")) == NULL) 36090Sstevel@tonic-gate homedir = ""; 36100Sstevel@tonic-gate 36110Sstevel@tonic-gate rcbuflen = strlen(homedir) + strlen(TELNETRC_COMP) + 1; 36120Sstevel@tonic-gate if ((rcname = malloc(rcbuflen)) == NULL) { 36130Sstevel@tonic-gate perror("telnet: can't process " TELNETRC_NAME); 36140Sstevel@tonic-gate ret = 1; 36150Sstevel@tonic-gate goto cmdrc_exit; 36160Sstevel@tonic-gate } 36170Sstevel@tonic-gate (void) strcpy(rcname, homedir); 36180Sstevel@tonic-gate (void) strcat(rcname, TELNETRC_COMP); 36190Sstevel@tonic-gate } 36200Sstevel@tonic-gate 36210Sstevel@tonic-gate if ((rcfile = fopen(rcname, "r")) == NULL) 36220Sstevel@tonic-gate goto cmdrc_exit; 36230Sstevel@tonic-gate 36240Sstevel@tonic-gate for (;;) { 36250Sstevel@tonic-gate if (GetString(&line, &linesize, rcfile) == NULL) { 36260Sstevel@tonic-gate if (!feof(rcfile)) { 36270Sstevel@tonic-gate perror("telnet: error reading " TELNETRC_NAME); 36280Sstevel@tonic-gate ret = 1; 36290Sstevel@tonic-gate goto cmdrc_exit; 36300Sstevel@tonic-gate } 36310Sstevel@tonic-gate break; 36320Sstevel@tonic-gate } 36330Sstevel@tonic-gate if (line[0] == 0) 36340Sstevel@tonic-gate continue; 36350Sstevel@tonic-gate if (line[0] == '#') 36360Sstevel@tonic-gate continue; 36370Sstevel@tonic-gate if (gotmachine) { 36380Sstevel@tonic-gate if (!isspace(line[0])) 36390Sstevel@tonic-gate gotmachine = 0; 36400Sstevel@tonic-gate } 36410Sstevel@tonic-gate if (gotmachine == 0) { 36420Sstevel@tonic-gate if (isspace(line[0])) 36430Sstevel@tonic-gate continue; 36440Sstevel@tonic-gate if (strncasecmp(line, m1, l1) == 0) 36450Sstevel@tonic-gate (void) strcpy(line, &line[l1]); 36460Sstevel@tonic-gate else if (strncasecmp(line, m2, l2) == 0) 36470Sstevel@tonic-gate (void) strcpy(line, &line[l2]); 36480Sstevel@tonic-gate else if (strncasecmp(line, def, sizeof (def) - 1) == 0) 36490Sstevel@tonic-gate (void) strcpy(line, &line[sizeof (def) - 1]); 36500Sstevel@tonic-gate else 36510Sstevel@tonic-gate continue; 36520Sstevel@tonic-gate if (line[0] != ' ' && line[0] != '\t' && 36530Sstevel@tonic-gate line[0] != '\n') 36540Sstevel@tonic-gate continue; 36550Sstevel@tonic-gate gotmachine = 1; 36560Sstevel@tonic-gate } 36570Sstevel@tonic-gate makeargv(); 36580Sstevel@tonic-gate if (margv[0] == 0) 36590Sstevel@tonic-gate continue; 36600Sstevel@tonic-gate c = getcmd(margv[0]); 36610Sstevel@tonic-gate if (Ambiguous(c)) { 36620Sstevel@tonic-gate (void) printf("?Ambiguous command: %s\n", margv[0]); 36630Sstevel@tonic-gate continue; 36640Sstevel@tonic-gate } 36650Sstevel@tonic-gate if (c == 0) { 36660Sstevel@tonic-gate (void) printf("?Invalid command: %s\n", margv[0]); 36670Sstevel@tonic-gate continue; 36680Sstevel@tonic-gate } 36690Sstevel@tonic-gate /* 36700Sstevel@tonic-gate * This should never happen... 36710Sstevel@tonic-gate */ 36720Sstevel@tonic-gate if (c->needconnect && !connected) { 36730Sstevel@tonic-gate (void) printf("?Need to be connected first for %s.\n", 36740Sstevel@tonic-gate margv[0]); 36750Sstevel@tonic-gate continue; 36760Sstevel@tonic-gate } 36770Sstevel@tonic-gate (*c->handler)(margc, margv); 36780Sstevel@tonic-gate } 36790Sstevel@tonic-gate cmdrc_exit: 36800Sstevel@tonic-gate if (rcfile != NULL) 36810Sstevel@tonic-gate (void) fclose(rcfile); 36820Sstevel@tonic-gate doing_rc = 0; 36830Sstevel@tonic-gate 36840Sstevel@tonic-gate return (ret); 36850Sstevel@tonic-gate } 3686