diff --git a/usr.bin/telnet/commands.c b/usr.bin/telnet/commands.c index 165bec2e4428..ebacc42bc0d9 100644 --- a/usr.bin/telnet/commands.c +++ b/usr.bin/telnet/commands.c @@ -1,2824 +1,2825 @@ /* * Copyright (c) 1988, 1990, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static char sccsid[] = "@(#)commands.c 8.2 (Berkeley) 12/15/93"; #endif /* not lint */ #if defined(unix) #include #if defined(CRAY) || defined(sysV88) #include #endif #include #else #include #endif /* defined(unix) */ #include #include #ifdef CRAY #include #endif /* CRAY */ #include #include #include #include #include #include #include #include #include #include "general.h" #include "ring.h" #include "externs.h" #include "defines.h" #include "types.h" #if !defined(CRAY) && !defined(sysV88) #include # if (defined(vax) || defined(tahoe) || defined(hp300)) && !defined(ultrix) # include # endif /* vax */ #endif /* !defined(CRAY) && !defined(sysV88) */ #include #ifndef MAXHOSTNAMELEN #define MAXHOSTNAMELEN 64 #endif MAXHOSTNAMELEN #if defined(IPPROTO_IP) && defined(IP_TOS) int tos = -1; #endif /* defined(IPPROTO_IP) && defined(IP_TOS) */ char *hostname; static char _hostname[MAXHOSTNAMELEN]; extern char *getenv(); extern int isprefix(); extern char **genget(); extern int Ambiguous(); static call(); typedef struct { char *name; /* command name */ char *help; /* help string (NULL for no help) */ int (*handler)(); /* routine which executes command */ int needconnect; /* Do we need to be connected to execute? */ } Command; static char line[256]; static char saveline[256]; static int margc; static char *margv[20]; #if defined(SKEY) #include #define PATH_SKEY "/usr/bin/key" int skey_calc(argc, argv) int argc; char **argv; { int status; if(argc != 3) { printf("%s sequence challenge\n", argv[0]); return; } switch(fork()) { case 0: execv(PATH_SKEY, argv); exit (1); case -1: perror("fork"); break; default: (void) wait(&status); if (WIFEXITED(status)) return (WEXITSTATUS(status)); return (0); } } #endif static void makeargv() { register char *cp, *cp2, c; register char **argp = margv; margc = 0; cp = line; if (*cp == '!') { /* Special case shell escape */ strcpy(saveline, line); /* save for shell command */ *argp++ = "!"; /* No room in string to get this */ margc++; cp++; } while (c = *cp) { register int inquote = 0; while (isspace(c)) c = *++cp; if (c == '\0') break; *argp++ = cp; margc += 1; for (cp2 = cp; c != '\0'; c = *++cp) { if (inquote) { if (c == inquote) { inquote = 0; continue; } } else { if (c == '\\') { if ((c = *++cp) == '\0') break; } else if (c == '"') { inquote = '"'; continue; } else if (c == '\'') { inquote = '\''; continue; } else if (isspace(c)) break; } *cp2++ = c; } *cp2 = '\0'; if (c == '\0') break; cp++; } *argp++ = 0; } /* * Make a character string into a number. * * Todo: 1. Could take random integers (12, 0x12, 012, 0b1). */ static special(s) register char *s; { register char c; char b; switch (*s) { case '^': b = *++s; if (b == '?') { c = b | 0x40; /* DEL */ } else { c = b & 0x1f; } break; default: c = *s; break; } return c; } /* * Construct a control character sequence * for a special character. */ static char * control(c) register cc_t c; { static char buf[5]; /* * The only way I could get the Sun 3.5 compiler * to shut up about * if ((unsigned int)c >= 0x80) * was to assign "c" to an unsigned int variable... * Arggg.... */ register unsigned int uic = (unsigned int)c; if (uic == 0x7f) return ("^?"); if (c == (cc_t)_POSIX_VDISABLE) { return "off"; } if (uic >= 0x80) { buf[0] = '\\'; buf[1] = ((c>>6)&07) + '0'; buf[2] = ((c>>3)&07) + '0'; buf[3] = (c&07) + '0'; buf[4] = 0; } else if (uic >= 0x20) { buf[0] = c; buf[1] = 0; } else { buf[0] = '^'; buf[1] = '@'+c; buf[2] = 0; } return (buf); } /* * The following are data structures and routines for * the "send" command. * */ struct sendlist { char *name; /* How user refers to it (case independent) */ char *help; /* Help information (0 ==> no help) */ int needconnect; /* Need to be connected */ int narg; /* Number of arguments */ int (*handler)(); /* Routine to perform (for special ops) */ int nbyte; /* Number of bytes to send this command */ int what; /* Character to be sent (<0 ==> special) */ }; static int send_esc P((void)), send_help P((void)), send_docmd P((char *)), send_dontcmd P((char *)), send_willcmd P((char *)), send_wontcmd P((char *)); static struct sendlist Sendlist[] = { { "ao", "Send Telnet Abort output", 1, 0, 0, 2, AO }, { "ayt", "Send Telnet 'Are You There'", 1, 0, 0, 2, AYT }, { "brk", "Send Telnet Break", 1, 0, 0, 2, BREAK }, { "break", 0, 1, 0, 0, 2, BREAK }, { "ec", "Send Telnet Erase Character", 1, 0, 0, 2, EC }, { "el", "Send Telnet Erase Line", 1, 0, 0, 2, EL }, { "escape", "Send current escape character", 1, 0, send_esc, 1, 0 }, { "ga", "Send Telnet 'Go Ahead' sequence", 1, 0, 0, 2, GA }, { "ip", "Send Telnet Interrupt Process", 1, 0, 0, 2, IP }, { "intp", 0, 1, 0, 0, 2, IP }, { "interrupt", 0, 1, 0, 0, 2, IP }, { "intr", 0, 1, 0, 0, 2, IP }, { "nop", "Send Telnet 'No operation'", 1, 0, 0, 2, NOP }, { "eor", "Send Telnet 'End of Record'", 1, 0, 0, 2, EOR }, { "abort", "Send Telnet 'Abort Process'", 1, 0, 0, 2, ABORT }, { "susp", "Send Telnet 'Suspend Process'", 1, 0, 0, 2, SUSP }, { "eof", "Send Telnet End of File Character", 1, 0, 0, 2, xEOF }, { "synch", "Perform Telnet 'Synch operation'", 1, 0, dosynch, 2, 0 }, { "getstatus", "Send request for STATUS", 1, 0, get_status, 6, 0 }, { "?", "Display send options", 0, 0, send_help, 0, 0 }, { "help", 0, 0, 0, send_help, 0, 0 }, { "do", 0, 0, 1, send_docmd, 3, 0 }, { "dont", 0, 0, 1, send_dontcmd, 3, 0 }, { "will", 0, 0, 1, send_willcmd, 3, 0 }, { "wont", 0, 0, 1, send_wontcmd, 3, 0 }, { 0 } }; #define GETSEND(name) ((struct sendlist *) genget(name, (char **) Sendlist, \ sizeof(struct sendlist))) static int sendcmd(argc, argv) int argc; char **argv; { int count; /* how many bytes we are going to need to send */ int i; int question = 0; /* was at least one argument a question */ struct sendlist *s; /* pointer to current command */ int success = 0; int needconnect = 0; if (argc < 2) { printf("need at least one argument for 'send' command\n"); printf("'send ?' for help\n"); return 0; } /* * First, validate all the send arguments. * In addition, we see how much space we are going to need, and * whether or not we will be doing a "SYNCH" operation (which * flushes the network queue). */ count = 0; for (i = 1; i < argc; i++) { s = GETSEND(argv[i]); if (s == 0) { printf("Unknown send argument '%s'\n'send ?' for help.\n", argv[i]); return 0; } else if (Ambiguous(s)) { printf("Ambiguous send argument '%s'\n'send ?' for help.\n", argv[i]); return 0; } if (i + s->narg >= argc) { fprintf(stderr, "Need %d argument%s to 'send %s' command. 'send %s ?' for help.\n", s->narg, s->narg == 1 ? "" : "s", s->name, s->name); return 0; } count += s->nbyte; if (s->handler == send_help) { send_help(); return 0; } i += s->narg; needconnect += s->needconnect; } if (!connected && needconnect) { printf("?Need to be connected first.\n"); printf("'send ?' for help\n"); return 0; } /* Now, do we have enough room? */ if (NETROOM() < count) { printf("There is not enough room in the buffer TO the network\n"); printf("to process your request. Nothing will be done.\n"); printf("('send synch' will throw away most data in the network\n"); printf("buffer, if this might help.)\n"); return 0; } /* OK, they are all OK, now go through again and actually send */ count = 0; for (i = 1; i < argc; i++) { if ((s = GETSEND(argv[i])) == 0) { fprintf(stderr, "Telnet 'send' error - argument disappeared!\n"); (void) quit(); /*NOTREACHED*/ } if (s->handler) { count++; success += (*s->handler)((s->narg > 0) ? argv[i+1] : 0, (s->narg > 1) ? argv[i+2] : 0); i += s->narg; } else { NET2ADD(IAC, s->what); printoption("SENT", IAC, s->what); } } return (count == success); } static int send_esc() { NETADD(escape); return 1; } static int send_docmd(name) char *name; { return(send_tncmd(send_do, "do", name)); } static int send_dontcmd(name) char *name; { return(send_tncmd(send_dont, "dont", name)); } static int send_willcmd(name) char *name; { return(send_tncmd(send_will, "will", name)); } static int send_wontcmd(name) char *name; { return(send_tncmd(send_wont, "wont", name)); } int send_tncmd(func, cmd, name) void (*func)(); char *cmd, *name; { char **cpp; extern char *telopts[]; register int val = 0; if (isprefix(name, "help") || isprefix(name, "?")) { register int col, len; printf("Usage: send %s \n", cmd); printf("\"value\" must be from 0 to 255\n"); printf("Valid options are:\n\t"); col = 8; for (cpp = telopts; *cpp; cpp++) { len = strlen(*cpp) + 3; if (col + len > 65) { printf("\n\t"); col = 8; } printf(" \"%s\"", *cpp); col += len; } printf("\n"); return 0; } cpp = (char **)genget(name, telopts, sizeof(char *)); if (Ambiguous(cpp)) { fprintf(stderr,"'%s': ambiguous argument ('send %s ?' for help).\n", name, cmd); return 0; } if (cpp) { val = cpp - telopts; } else { register char *cp = name; while (*cp >= '0' && *cp <= '9') { val *= 10; val += *cp - '0'; cp++; } if (*cp != 0) { fprintf(stderr, "'%s': unknown argument ('send %s ?' for help).\n", name, cmd); return 0; } else if (val < 0 || val > 255) { fprintf(stderr, "'%s': bad value ('send %s ?' for help).\n", name, cmd); return 0; } } if (!connected) { printf("?Need to be connected first.\n"); return 0; } (*func)(val, 1); return 1; } static int send_help() { struct sendlist *s; /* pointer to current command */ for (s = Sendlist; s->name; s++) { if (s->help) printf("%-15s %s\n", s->name, s->help); } return(0); } /* * The following are the routines and data structures referred * to by the arguments to the "toggle" command. */ static int lclchars() { donelclchars = 1; return 1; } static int togdebug() { #ifndef NOT43 if (net > 0 && (SetSockOpt(net, SOL_SOCKET, SO_DEBUG, debug)) < 0) { perror("setsockopt (SO_DEBUG)"); } #else /* NOT43 */ if (debug) { if (net > 0 && SetSockOpt(net, SOL_SOCKET, SO_DEBUG, 1) < 0) perror("setsockopt (SO_DEBUG)"); } else printf("Cannot turn off socket debugging\n"); #endif /* NOT43 */ return 1; } static int togcrlf() { if (crlf) { printf("Will send carriage returns as telnet .\n"); } else { printf("Will send carriage returns as telnet .\n"); } return 1; } int binmode; static int togbinary(val) int val; { donebinarytoggle = 1; if (val >= 0) { binmode = val; } else { if (my_want_state_is_will(TELOPT_BINARY) && my_want_state_is_do(TELOPT_BINARY)) { binmode = 1; } else if (my_want_state_is_wont(TELOPT_BINARY) && my_want_state_is_dont(TELOPT_BINARY)) { binmode = 0; } val = binmode ? 0 : 1; } if (val == 1) { if (my_want_state_is_will(TELOPT_BINARY) && my_want_state_is_do(TELOPT_BINARY)) { printf("Already operating in binary mode with remote host.\n"); } else { printf("Negotiating binary mode with remote host.\n"); tel_enter_binary(3); } } else { if (my_want_state_is_wont(TELOPT_BINARY) && my_want_state_is_dont(TELOPT_BINARY)) { printf("Already in network ascii mode with remote host.\n"); } else { printf("Negotiating network ascii mode with remote host.\n"); tel_leave_binary(3); } } return 1; } static int togrbinary(val) int val; { donebinarytoggle = 1; if (val == -1) val = my_want_state_is_do(TELOPT_BINARY) ? 0 : 1; if (val == 1) { if (my_want_state_is_do(TELOPT_BINARY)) { printf("Already receiving in binary mode.\n"); } else { printf("Negotiating binary mode on input.\n"); tel_enter_binary(1); } } else { if (my_want_state_is_dont(TELOPT_BINARY)) { printf("Already receiving in network ascii mode.\n"); } else { printf("Negotiating network ascii mode on input.\n"); tel_leave_binary(1); } } return 1; } static int togxbinary(val) int val; { donebinarytoggle = 1; if (val == -1) val = my_want_state_is_will(TELOPT_BINARY) ? 0 : 1; if (val == 1) { if (my_want_state_is_will(TELOPT_BINARY)) { printf("Already transmitting in binary mode.\n"); } else { printf("Negotiating binary mode on output.\n"); tel_enter_binary(2); } } else { if (my_want_state_is_wont(TELOPT_BINARY)) { printf("Already transmitting in network ascii mode.\n"); } else { printf("Negotiating network ascii mode on output.\n"); tel_leave_binary(2); } } return 1; } static int togglehelp P((void)); #if defined(AUTHENTICATION) extern int auth_togdebug P((int)); #endif struct togglelist { char *name; /* name of toggle */ char *help; /* help message */ int (*handler)(); /* routine to do actual setting */ int *variable; char *actionexplanation; }; static struct togglelist Togglelist[] = { { "autoflush", "flushing of output when sending interrupt characters", 0, &autoflush, "flush output when sending interrupt characters" }, { "autosynch", "automatic sending of interrupt characters in urgent mode", 0, &autosynch, "send interrupt characters in urgent mode" }, #if defined(AUTHENTICATION) { "autologin", "automatic sending of login and/or authentication info", 0, &autologin, "send login name and/or authentication information" }, { "authdebug", "Toggle authentication debugging", auth_togdebug, 0, "print authentication debugging information" }, #endif { "skiprc", "don't read ~/.telnetrc file", 0, &skiprc, "skip reading of ~/.telnetrc file" }, { "binary", "sending and receiving of binary data", togbinary, 0, 0 }, { "inbinary", "receiving of binary data", togrbinary, 0, 0 }, { "outbinary", "sending of binary data", togxbinary, 0, 0 }, { "crlf", "sending carriage returns as telnet ", togcrlf, &crlf, 0 }, { "crmod", "mapping of received carriage returns", 0, &crmod, "map carriage return on output" }, { "localchars", "local recognition of certain control characters", lclchars, &localchars, "recognize certain control characters" }, { " ", "", 0 }, /* empty line */ #if defined(unix) && defined(TN3270) { "apitrace", "(debugging) toggle tracing of API transactions", 0, &apitrace, "trace API transactions" }, { "cursesdata", "(debugging) toggle printing of hexadecimal curses data", 0, &cursesdata, "print hexadecimal representation of curses data" }, #endif /* defined(unix) && defined(TN3270) */ { "debug", "debugging", togdebug, &debug, "turn on socket level debugging" }, { "netdata", "printing of hexadecimal network data (debugging)", 0, &netdata, "print hexadecimal representation of network traffic" }, { "prettydump", "output of \"netdata\" to user readable format (debugging)", 0, &prettydump, "print user readable output for \"netdata\"" }, { "options", "viewing of options processing (debugging)", 0, &showoptions, "show option processing" }, #if defined(unix) { "termdata", "(debugging) toggle printing of hexadecimal terminal data", 0, &termdata, "print hexadecimal representation of terminal traffic" }, #endif /* defined(unix) */ { "?", 0, togglehelp }, { "help", 0, togglehelp }, { 0 } }; static int togglehelp() { struct togglelist *c; for (c = Togglelist; c->name; c++) { if (c->help) { if (*c->help) printf("%-15s toggle %s\n", c->name, c->help); else printf("\n"); } } printf("\n"); printf("%-15s %s\n", "?", "display help information"); return 0; } static void settogglehelp(set) int set; { struct togglelist *c; for (c = Togglelist; c->name; c++) { if (c->help) { if (*c->help) printf("%-15s %s %s\n", c->name, set ? "enable" : "disable", c->help); else printf("\n"); } } } #define GETTOGGLE(name) (struct togglelist *) \ genget(name, (char **) Togglelist, sizeof(struct togglelist)) static int toggle(argc, argv) int argc; char *argv[]; { int retval = 1; char *name; struct togglelist *c; if (argc < 2) { fprintf(stderr, "Need an argument to 'toggle' command. 'toggle ?' for help.\n"); return 0; } argc--; argv++; while (argc--) { name = *argv++; c = GETTOGGLE(name); if (Ambiguous(c)) { fprintf(stderr, "'%s': ambiguous argument ('toggle ?' for help).\n", name); return 0; } else if (c == 0) { fprintf(stderr, "'%s': unknown argument ('toggle ?' for help).\n", name); return 0; } else { if (c->variable) { *c->variable = !*c->variable; /* invert it */ if (c->actionexplanation) { printf("%s %s.\n", *c->variable? "Will" : "Won't", c->actionexplanation); } } if (c->handler) { retval &= (*c->handler)(-1); } } } return retval; } /* * The following perform the "set" command. */ #ifdef USE_TERMIO struct termio new_tc = { 0 }; #endif struct setlist { char *name; /* name */ char *help; /* help information */ void (*handler)(); cc_t *charp; /* where it is located at */ }; static struct setlist Setlist[] = { #ifdef KLUDGELINEMODE { "echo", "character to toggle local echoing on/off", 0, &echoc }, #endif { "escape", "character to escape back to telnet command mode", 0, &escape }, { "rlogin", "rlogin escape character", 0, &rlogin }, { "tracefile", "file to write trace information to", SetNetTrace, (cc_t *)NetTraceFile}, { " ", "" }, { " ", "The following need 'localchars' to be toggled true", 0, 0 }, { "flushoutput", "character to cause an Abort Output", 0, termFlushCharp }, { "interrupt", "character to cause an Interrupt Process", 0, termIntCharp }, { "quit", "character to cause an Abort process", 0, termQuitCharp }, { "eof", "character to cause an EOF ", 0, termEofCharp }, { " ", "" }, { " ", "The following are for local editing in linemode", 0, 0 }, { "erase", "character to use to erase a character", 0, termEraseCharp }, { "kill", "character to use to erase a line", 0, termKillCharp }, { "lnext", "character to use for literal next", 0, termLiteralNextCharp }, { "susp", "character to cause a Suspend Process", 0, termSuspCharp }, { "reprint", "character to use for line reprint", 0, termRprntCharp }, { "worderase", "character to use to erase a word", 0, termWerasCharp }, { "start", "character to use for XON", 0, termStartCharp }, { "stop", "character to use for XOFF", 0, termStopCharp }, { "forw1", "alternate end of line character", 0, termForw1Charp }, { "forw2", "alternate end of line character", 0, termForw2Charp }, { "ayt", "alternate AYT character", 0, termAytCharp }, { 0 } }; #if defined(CRAY) && !defined(__STDC__) /* Work around compiler bug in pcc 4.1.5 */ void _setlist_init() { #ifndef KLUDGELINEMODE #define N 5 #else #define N 6 #endif Setlist[N+0].charp = &termFlushChar; Setlist[N+1].charp = &termIntChar; Setlist[N+2].charp = &termQuitChar; Setlist[N+3].charp = &termEofChar; Setlist[N+6].charp = &termEraseChar; Setlist[N+7].charp = &termKillChar; Setlist[N+8].charp = &termLiteralNextChar; Setlist[N+9].charp = &termSuspChar; Setlist[N+10].charp = &termRprntChar; Setlist[N+11].charp = &termWerasChar; Setlist[N+12].charp = &termStartChar; Setlist[N+13].charp = &termStopChar; Setlist[N+14].charp = &termForw1Char; Setlist[N+15].charp = &termForw2Char; Setlist[N+16].charp = &termAytChar; #undef N } #endif /* defined(CRAY) && !defined(__STDC__) */ static struct setlist * getset(name) char *name; { return (struct setlist *) genget(name, (char **) Setlist, sizeof(struct setlist)); } void set_escape_char(s) char *s; { if (rlogin != _POSIX_VDISABLE) { rlogin = (s && *s) ? special(s) : _POSIX_VDISABLE; printf("Telnet rlogin escape character is '%s'.\n", control(rlogin)); } else { escape = (s && *s) ? special(s) : _POSIX_VDISABLE; printf("Telnet escape character is '%s'.\n", control(escape)); } } static int setcmd(argc, argv) int argc; char *argv[]; { int value; struct setlist *ct; struct togglelist *c; if (argc < 2 || argc > 3) { printf("Format is 'set Name Value'\n'set ?' for help.\n"); return 0; } if ((argc == 2) && (isprefix(argv[1], "?") || isprefix(argv[1], "help"))) { for (ct = Setlist; ct->name; ct++) printf("%-15s %s\n", ct->name, ct->help); printf("\n"); settogglehelp(1); printf("%-15s %s\n", "?", "display help information"); return 0; } ct = getset(argv[1]); if (ct == 0) { c = GETTOGGLE(argv[1]); if (c == 0) { fprintf(stderr, "'%s': unknown argument ('set ?' for help).\n", argv[1]); return 0; } else if (Ambiguous(c)) { fprintf(stderr, "'%s': ambiguous argument ('set ?' for help).\n", argv[1]); return 0; } if (c->variable) { if ((argc == 2) || (strcmp("on", argv[2]) == 0)) *c->variable = 1; else if (strcmp("off", argv[2]) == 0) *c->variable = 0; else { printf("Format is 'set togglename [on|off]'\n'set ?' for help.\n"); return 0; } if (c->actionexplanation) { printf("%s %s.\n", *c->variable? "Will" : "Won't", c->actionexplanation); } } if (c->handler) (*c->handler)(1); } else if (argc != 3) { printf("Format is 'set Name Value'\n'set ?' for help.\n"); return 0; } else if (Ambiguous(ct)) { fprintf(stderr, "'%s': ambiguous argument ('set ?' for help).\n", argv[1]); return 0; } else if (ct->handler) { (*ct->handler)(argv[2]); printf("%s set to \"%s\".\n", ct->name, (char *)ct->charp); } else { if (strcmp("off", argv[2])) { value = special(argv[2]); } else { value = _POSIX_VDISABLE; } *(ct->charp) = (cc_t)value; printf("%s character is '%s'.\n", ct->name, control(*(ct->charp))); } slc_check(); return 1; } static int unsetcmd(argc, argv) int argc; char *argv[]; { struct setlist *ct; struct togglelist *c; register char *name; if (argc < 2) { fprintf(stderr, "Need an argument to 'unset' command. 'unset ?' for help.\n"); return 0; } if (isprefix(argv[1], "?") || isprefix(argv[1], "help")) { for (ct = Setlist; ct->name; ct++) printf("%-15s %s\n", ct->name, ct->help); printf("\n"); settogglehelp(0); printf("%-15s %s\n", "?", "display help information"); return 0; } argc--; argv++; while (argc--) { name = *argv++; ct = getset(name); if (ct == 0) { c = GETTOGGLE(name); if (c == 0) { fprintf(stderr, "'%s': unknown argument ('unset ?' for help).\n", name); return 0; } else if (Ambiguous(c)) { fprintf(stderr, "'%s': ambiguous argument ('unset ?' for help).\n", name); return 0; } if (c->variable) { *c->variable = 0; if (c->actionexplanation) { printf("%s %s.\n", *c->variable? "Will" : "Won't", c->actionexplanation); } } if (c->handler) (*c->handler)(0); } else if (Ambiguous(ct)) { fprintf(stderr, "'%s': ambiguous argument ('unset ?' for help).\n", name); return 0; } else if (ct->handler) { (*ct->handler)(0); printf("%s reset to \"%s\".\n", ct->name, (char *)ct->charp); } else { *(ct->charp) = _POSIX_VDISABLE; printf("%s character is '%s'.\n", ct->name, control(*(ct->charp))); } } return 1; } /* * The following are the data structures and routines for the * 'mode' command. */ #ifdef KLUDGELINEMODE extern int kludgelinemode; static int dokludgemode() { kludgelinemode = 1; send_wont(TELOPT_LINEMODE, 1); send_dont(TELOPT_SGA, 1); send_dont(TELOPT_ECHO, 1); } #endif static int dolinemode() { #ifdef KLUDGELINEMODE if (kludgelinemode) send_dont(TELOPT_SGA, 1); #endif send_will(TELOPT_LINEMODE, 1); send_dont(TELOPT_ECHO, 1); return 1; } static int docharmode() { #ifdef KLUDGELINEMODE if (kludgelinemode) send_do(TELOPT_SGA, 1); else #endif send_wont(TELOPT_LINEMODE, 1); send_do(TELOPT_ECHO, 1); return 1; } static int dolmmode(bit, on) int bit, on; { unsigned char c; extern int linemode; if (my_want_state_is_wont(TELOPT_LINEMODE)) { printf("?Need to have LINEMODE option enabled first.\n"); printf("'mode ?' for help.\n"); return 0; } if (on) c = (linemode | bit); else c = (linemode & ~bit); lm_mode(&c, 1, 1); return 1; } int setmode(bit) { return dolmmode(bit, 1); } int clearmode(bit) { return dolmmode(bit, 0); } struct modelist { char *name; /* command name */ char *help; /* help string */ int (*handler)(); /* routine which executes command */ int needconnect; /* Do we need to be connected to execute? */ int arg1; }; extern int modehelp(); static struct modelist ModeList[] = { { "character", "Disable LINEMODE option", docharmode, 1 }, #ifdef KLUDGELINEMODE { "", "(or disable obsolete line-by-line mode)", 0 }, #endif { "line", "Enable LINEMODE option", dolinemode, 1 }, #ifdef KLUDGELINEMODE { "", "(or enable obsolete line-by-line mode)", 0 }, #endif { "", "", 0 }, { "", "These require the LINEMODE option to be enabled", 0 }, { "isig", "Enable signal trapping", setmode, 1, MODE_TRAPSIG }, { "+isig", 0, setmode, 1, MODE_TRAPSIG }, { "-isig", "Disable signal trapping", clearmode, 1, MODE_TRAPSIG }, { "edit", "Enable character editing", setmode, 1, MODE_EDIT }, { "+edit", 0, setmode, 1, MODE_EDIT }, { "-edit", "Disable character editing", clearmode, 1, MODE_EDIT }, { "softtabs", "Enable tab expansion", setmode, 1, MODE_SOFT_TAB }, { "+softtabs", 0, setmode, 1, MODE_SOFT_TAB }, { "-softtabs", "Disable character editing", clearmode, 1, MODE_SOFT_TAB }, { "litecho", "Enable literal character echo", setmode, 1, MODE_LIT_ECHO }, { "+litecho", 0, setmode, 1, MODE_LIT_ECHO }, { "-litecho", "Disable literal character echo", clearmode, 1, MODE_LIT_ECHO }, { "help", 0, modehelp, 0 }, #ifdef KLUDGELINEMODE { "kludgeline", 0, dokludgemode, 1 }, #endif { "", "", 0 }, { "?", "Print help information", modehelp, 0 }, { 0 }, }; int modehelp() { struct modelist *mt; printf("format is: 'mode Mode', where 'Mode' is one of:\n\n"); for (mt = ModeList; mt->name; mt++) { if (mt->help) { if (*mt->help) printf("%-15s %s\n", mt->name, mt->help); else printf("\n"); } } return 0; } #define GETMODECMD(name) (struct modelist *) \ genget(name, (char **) ModeList, sizeof(struct modelist)) static int modecmd(argc, argv) int argc; char *argv[]; { struct modelist *mt; if (argc != 2) { printf("'mode' command requires an argument\n"); printf("'mode ?' for help.\n"); } else if ((mt = GETMODECMD(argv[1])) == 0) { fprintf(stderr, "Unknown mode '%s' ('mode ?' for help).\n", argv[1]); } else if (Ambiguous(mt)) { fprintf(stderr, "Ambiguous mode '%s' ('mode ?' for help).\n", argv[1]); } else if (mt->needconnect && !connected) { printf("?Need to be connected first.\n"); printf("'mode ?' for help.\n"); } else if (mt->handler) { return (*mt->handler)(mt->arg1); } return 0; } /* * The following data structures and routines implement the * "display" command. */ static int display(argc, argv) int argc; char *argv[]; { struct togglelist *tl; struct setlist *sl; #define dotog(tl) if (tl->variable && tl->actionexplanation) { \ if (*tl->variable) { \ printf("will"); \ } else { \ printf("won't"); \ } \ printf(" %s.\n", tl->actionexplanation); \ } #define doset(sl) if (sl->name && *sl->name != ' ') { \ if (sl->handler == 0) \ printf("%-15s [%s]\n", sl->name, control(*sl->charp)); \ else \ printf("%-15s \"%s\"\n", sl->name, (char *)sl->charp); \ } if (argc == 1) { for (tl = Togglelist; tl->name; tl++) { dotog(tl); } printf("\n"); for (sl = Setlist; sl->name; sl++) { doset(sl); } } else { int i; for (i = 1; i < argc; i++) { sl = getset(argv[i]); tl = GETTOGGLE(argv[i]); if (Ambiguous(sl) || Ambiguous(tl)) { printf("?Ambiguous argument '%s'.\n", argv[i]); return 0; } else if (!sl && !tl) { printf("?Unknown argument '%s'.\n", argv[i]); return 0; } else { if (tl) { dotog(tl); } if (sl) { doset(sl); } } } } /*@*/optionstatus(); return 1; #undef doset #undef dotog } /* * The following are the data structures, and many of the routines, * relating to command processing. */ /* * Set the escape character. */ static int setescape(argc, argv) int argc; char *argv[]; { register char *arg; char buf[50]; printf( "Deprecated usage - please use 'set escape%s%s' in the future.\n", (argc > 2)? " ":"", (argc > 2)? argv[1]: ""); if (argc > 2) arg = argv[1]; else { printf("new escape character: "); (void) fgets(buf, sizeof(buf), stdin); arg = buf; } if (arg[0] != '\0') escape = arg[0]; if (!In3270) { printf("Escape character is '%s'.\n", control(escape)); } (void) fflush(stdout); return 1; } /*VARARGS*/ static int togcrmod() { crmod = !crmod; printf("Deprecated usage - please use 'toggle crmod' in the future.\n"); printf("%s map carriage return on output.\n", crmod ? "Will" : "Won't"); (void) fflush(stdout); return 1; } /*VARARGS*/ int suspend() { #ifdef SIGTSTP setcommandmode(); { long oldrows, oldcols, newrows, newcols, err; err = (TerminalWindowSize(&oldrows, &oldcols) == 0) ? 1 : 0; (void) kill(0, SIGTSTP); /* * If we didn't get the window size before the SUSPEND, but we * can get them now (???), then send the NAWS to make sure that * we are set up for the right window size. */ if (TerminalWindowSize(&newrows, &newcols) && connected && (err || ((oldrows != newrows) || (oldcols != newcols)))) { sendnaws(); } } /* reget parameters in case they were changed */ TerminalSaveState(); setconnmode(0); #else printf("Suspend is not supported. Try the '!' command instead\n"); #endif return 1; } #if !defined(TN3270) /*ARGSUSED*/ int shell(argc, argv) int argc; char *argv[]; { long oldrows, oldcols, newrows, newcols, err; setcommandmode(); err = (TerminalWindowSize(&oldrows, &oldcols) == 0) ? 1 : 0; switch(vfork()) { case -1: perror("Fork failed\n"); break; case 0: { /* * Fire up the shell in the child. */ register char *shellp, *shellname; extern char *rindex(); shellp = getenv("SHELL"); if (shellp == NULL) shellp = "/bin/sh"; if ((shellname = rindex(shellp, '/')) == 0) shellname = shellp; else shellname++; if (argc > 1) execl(shellp, shellname, "-c", &saveline[1], 0); else execl(shellp, shellname, 0); perror("Execl"); _exit(1); } default: (void)wait((int *)0); /* Wait for the shell to complete */ if (TerminalWindowSize(&newrows, &newcols) && connected && (err || ((oldrows != newrows) || (oldcols != newcols)))) { sendnaws(); } break; } return 1; } #else /* !defined(TN3270) */ extern int shell(); #endif /* !defined(TN3270) */ /*VARARGS*/ static bye(argc, argv) int argc; /* Number of arguments */ char *argv[]; /* arguments */ { extern int resettermname; if (connected) { (void) shutdown(net, 2); printf("Connection closed.\n"); (void) NetClose(net); connected = 0; resettermname = 1; #if defined(AUTHENTICATION) auth_encrypt_connect(connected); #endif /* defined(AUTHENTICATION) */ /* reset options */ tninit(); #if defined(TN3270) SetIn3270(); /* Get out of 3270 mode */ #endif /* defined(TN3270) */ } if ((argc != 2) || (strcmp(argv[1], "fromquit") != 0)) { longjmp(toplevel, 1); /* NOTREACHED */ } return 1; /* Keep lint, etc., happy */ } /*VARARGS*/ quit() { (void) call(bye, "bye", "fromquit", 0); Exit(0); /*NOTREACHED*/ } /*VARARGS*/ int logout() { send_do(TELOPT_LOGOUT, 1); (void) netflush(); return 1; } /* * The SLC command. */ struct slclist { char *name; char *help; void (*handler)(); int arg; }; static void slc_help(); struct slclist SlcList[] = { { "export", "Use local special character definitions", slc_mode_export, 0 }, { "import", "Use remote special character definitions", slc_mode_import, 1 }, { "check", "Verify remote special character definitions", slc_mode_import, 0 }, { "help", 0, slc_help, 0 }, { "?", "Print help information", slc_help, 0 }, { 0 }, }; static void slc_help() { struct slclist *c; for (c = SlcList; c->name; c++) { if (c->help) { if (*c->help) printf("%-15s %s\n", c->name, c->help); else printf("\n"); } } } static struct slclist * getslc(name) char *name; { return (struct slclist *) genget(name, (char **) SlcList, sizeof(struct slclist)); } static slccmd(argc, argv) int argc; char *argv[]; { struct slclist *c; if (argc != 2) { fprintf(stderr, "Need an argument to 'slc' command. 'slc ?' for help.\n"); return 0; } c = getslc(argv[1]); if (c == 0) { fprintf(stderr, "'%s': unknown argument ('slc ?' for help).\n", argv[1]); return 0; } if (Ambiguous(c)) { fprintf(stderr, "'%s': ambiguous argument ('slc ?' for help).\n", argv[1]); return 0; } (*c->handler)(c->arg); slcstate(); return 1; } /* * The ENVIRON command. */ struct envlist { char *name; char *help; void (*handler)(); int narg; }; extern struct env_lst * env_define P((unsigned char *, unsigned char *)); extern void env_undefine P((unsigned char *)), env_export P((unsigned char *)), env_unexport P((unsigned char *)), env_send P((unsigned char *)), #if defined(OLD_ENVIRON) && defined(ENV_HACK) env_varval P((unsigned char *)), #endif env_list P((void)); static void env_help P((void)); struct envlist EnvList[] = { { "define", "Define an environment variable", (void (*)())env_define, 2 }, { "undefine", "Undefine an environment variable", env_undefine, 1 }, { "export", "Mark an environment variable for automatic export", env_export, 1 }, { "unexport", "Don't mark an environment variable for automatic export", env_unexport, 1 }, { "send", "Send an environment variable", env_send, 1 }, { "list", "List the current environment variables", env_list, 0 }, #if defined(OLD_ENVIRON) && defined(ENV_HACK) { "varval", "Reverse VAR and VALUE (auto, right, wrong, status)", env_varval, 1 }, #endif { "help", 0, env_help, 0 }, { "?", "Print help information", env_help, 0 }, { 0 }, }; static void env_help() { struct envlist *c; for (c = EnvList; c->name; c++) { if (c->help) { if (*c->help) printf("%-15s %s\n", c->name, c->help); else printf("\n"); } } } static struct envlist * getenvcmd(name) char *name; { return (struct envlist *) genget(name, (char **) EnvList, sizeof(struct envlist)); } env_cmd(argc, argv) int argc; char *argv[]; { struct envlist *c; if (argc < 2) { fprintf(stderr, "Need an argument to 'environ' command. 'environ ?' for help.\n"); return 0; } c = getenvcmd(argv[1]); if (c == 0) { fprintf(stderr, "'%s': unknown argument ('environ ?' for help).\n", argv[1]); return 0; } if (Ambiguous(c)) { fprintf(stderr, "'%s': ambiguous argument ('environ ?' for help).\n", argv[1]); return 0; } if (c->narg + 2 != argc) { fprintf(stderr, "Need %s%d argument%s to 'environ %s' command. 'environ ?' for help.\n", c->narg < argc + 2 ? "only " : "", c->narg, c->narg == 1 ? "" : "s", c->name); return 0; } (*c->handler)(argv[2], argv[3]); return 1; } struct env_lst { struct env_lst *next; /* pointer to next structure */ struct env_lst *prev; /* pointer to previous structure */ unsigned char *var; /* pointer to variable name */ unsigned char *value; /* pointer to variable value */ int export; /* 1 -> export with default list of variables */ int welldefined; /* A well defined variable */ }; struct env_lst envlisthead; struct env_lst * env_find(var) unsigned char *var; { register struct env_lst *ep; for (ep = envlisthead.next; ep; ep = ep->next) { if (strcmp((char *)ep->var, (char *)var) == 0) return(ep); } return(NULL); } void env_init() { extern char **environ; register char **epp, *cp; register struct env_lst *ep; extern char *index(); for (epp = environ; *epp; epp++) { if (cp = index(*epp, '=')) { *cp = '\0'; ep = env_define((unsigned char *)*epp, (unsigned char *)cp+1); ep->export = 0; *cp = '='; } } /* * Special case for DISPLAY variable. If it is ":0.0" or * "unix:0.0", we have to get rid of "unix" and insert our * hostname. */ if ((ep = env_find("DISPLAY")) && ((*ep->value == ':') || (strncmp((char *)ep->value, "unix:", 5) == 0))) { char hbuf[256+1]; char *cp2 = index((char *)ep->value, ':'); gethostname(hbuf, 256); hbuf[256] = '\0'; cp = (char *)malloc(strlen(hbuf) + strlen(cp2) + 1); sprintf((char *)cp, "%s%s", hbuf, cp2); free(ep->value); ep->value = (unsigned char *)cp; } /* * If USER is not defined, but LOGNAME is, then add * USER with the value from LOGNAME. By default, we * don't export the USER variable. */ if ((env_find("USER") == NULL) && (ep = env_find("LOGNAME"))) { env_define((unsigned char *)"USER", ep->value); env_unexport((unsigned char *)"USER"); } env_export((unsigned char *)"DISPLAY"); env_export((unsigned char *)"PRINTER"); } struct env_lst * env_define(var, value) unsigned char *var, *value; { register struct env_lst *ep; if (ep = env_find(var)) { if (ep->var) free(ep->var); if (ep->value) free(ep->value); } else { ep = (struct env_lst *)malloc(sizeof(struct env_lst)); ep->next = envlisthead.next; envlisthead.next = ep; ep->prev = &envlisthead; if (ep->next) ep->next->prev = ep; } ep->welldefined = opt_welldefined(var); ep->export = 1; ep->var = (unsigned char *)strdup((char *)var); ep->value = (unsigned char *)strdup((char *)value); return(ep); } void env_undefine(var) unsigned char *var; { register struct env_lst *ep; if (ep = env_find(var)) { ep->prev->next = ep->next; if (ep->next) ep->next->prev = ep->prev; if (ep->var) free(ep->var); if (ep->value) free(ep->value); free(ep); } } void env_export(var) unsigned char *var; { register struct env_lst *ep; if (ep = env_find(var)) ep->export = 1; } void env_unexport(var) unsigned char *var; { register struct env_lst *ep; if (ep = env_find(var)) ep->export = 0; } void env_send(var) unsigned char *var; { register struct env_lst *ep; if (my_state_is_wont(TELOPT_NEW_ENVIRON) #ifdef OLD_ENVIRON && my_state_is_wont(TELOPT_OLD_ENVIRON) #endif ) { fprintf(stderr, "Cannot send '%s': Telnet ENVIRON option not enabled\n", var); return; } ep = env_find(var); if (ep == 0) { fprintf(stderr, "Cannot send '%s': variable not defined\n", var); return; } env_opt_start_info(); env_opt_add(ep->var); env_opt_end(0); } void env_list() { register struct env_lst *ep; for (ep = envlisthead.next; ep; ep = ep->next) { printf("%c %-20s %s\n", ep->export ? '*' : ' ', ep->var, ep->value); } } unsigned char * env_default(init, welldefined) int init; { static struct env_lst *nep = NULL; if (init) { nep = &envlisthead; return; } if (nep) { while (nep = nep->next) { if (nep->export && (nep->welldefined == welldefined)) return(nep->var); } } return(NULL); } unsigned char * env_getvalue(var) unsigned char *var; { register struct env_lst *ep; if (ep = env_find(var)) return(ep->value); return(NULL); } #if defined(OLD_ENVIRON) && defined(ENV_HACK) void env_varval(what) unsigned char *what; { extern int old_env_var, old_env_value, env_auto; int len = strlen((char *)what); if (len == 0) goto unknown; if (strncasecmp((char *)what, "status", len) == 0) { if (env_auto) printf("%s%s", "VAR and VALUE are/will be ", "determined automatically\n"); if (old_env_var == OLD_ENV_VAR) printf("VAR and VALUE set to correct definitions\n"); else printf("VAR and VALUE definitions are reversed\n"); } else if (strncasecmp((char *)what, "auto", len) == 0) { env_auto = 1; old_env_var = OLD_ENV_VALUE; old_env_value = OLD_ENV_VAR; } else if (strncasecmp((char *)what, "right", len) == 0) { env_auto = 0; old_env_var = OLD_ENV_VAR; old_env_value = OLD_ENV_VALUE; } else if (strncasecmp((char *)what, "wrong", len) == 0) { env_auto = 0; old_env_var = OLD_ENV_VALUE; old_env_value = OLD_ENV_VAR; } else { unknown: printf("Unknown \"varval\" command. (\"auto\", \"right\", \"wrong\", \"status\")\n"); } } #endif #if defined(AUTHENTICATION) /* * The AUTHENTICATE command. */ struct authlist { char *name; char *help; int (*handler)(); int narg; }; extern int auth_enable P((int)), auth_disable P((int)), auth_status P((void)); static int auth_help P((void)); struct authlist AuthList[] = { { "status", "Display current status of authentication information", auth_status, 0 }, { "disable", "Disable an authentication type ('auth disable ?' for more)", auth_disable, 1 }, { "enable", "Enable an authentication type ('auth enable ?' for more)", auth_enable, 1 }, { "help", 0, auth_help, 0 }, { "?", "Print help information", auth_help, 0 }, { 0 }, }; static int auth_help() { struct authlist *c; for (c = AuthList; c->name; c++) { if (c->help) { if (*c->help) printf("%-15s %s\n", c->name, c->help); else printf("\n"); } } return 0; } auth_cmd(argc, argv) int argc; char *argv[]; { struct authlist *c; c = (struct authlist *) genget(argv[1], (char **) AuthList, sizeof(struct authlist)); if (c == 0) { fprintf(stderr, "'%s': unknown argument ('auth ?' for help).\n", argv[1]); return 0; } if (Ambiguous(c)) { fprintf(stderr, "'%s': ambiguous argument ('auth ?' for help).\n", argv[1]); return 0; } if (c->narg + 2 != argc) { fprintf(stderr, "Need %s%d argument%s to 'auth %s' command. 'auth ?' for help.\n", c->narg < argc + 2 ? "only " : "", c->narg, c->narg == 1 ? "" : "s", c->name); return 0; } return((*c->handler)(argv[2], argv[3])); } #endif #if defined(unix) && defined(TN3270) static void filestuff(fd) int fd; { int res; #ifdef F_GETOWN setconnmode(0); res = fcntl(fd, F_GETOWN, 0); setcommandmode(); if (res == -1) { perror("fcntl"); return; } printf("\tOwner is %d.\n", res); #endif setconnmode(0); res = fcntl(fd, F_GETFL, 0); setcommandmode(); if (res == -1) { perror("fcntl"); return; } #ifdef notdef printf("\tFlags are 0x%x: %s\n", res, decodeflags(res)); #endif } #endif /* defined(unix) && defined(TN3270) */ /* * Print status about the connection. */ /*ARGSUSED*/ static status(argc, argv) int argc; char *argv[]; { if (connected) { printf("Connected to %s.\n", hostname); if ((argc < 2) || strcmp(argv[1], "notmuch")) { int mode = getconnmode(); if (my_want_state_is_will(TELOPT_LINEMODE)) { printf("Operating with LINEMODE option\n"); printf("%s line editing\n", (mode&MODE_EDIT) ? "Local" : "No"); printf("%s catching of signals\n", (mode&MODE_TRAPSIG) ? "Local" : "No"); slcstate(); #ifdef KLUDGELINEMODE } else if (kludgelinemode && my_want_state_is_dont(TELOPT_SGA)) { printf("Operating in obsolete linemode\n"); #endif } else { printf("Operating in single character mode\n"); if (localchars) printf("Catching signals locally\n"); } printf("%s character echo\n", (mode&MODE_ECHO) ? "Local" : "Remote"); if (my_want_state_is_will(TELOPT_LFLOW)) printf("%s flow control\n", (mode&MODE_FLOW) ? "Local" : "No"); } } else { printf("No connection.\n"); } # if !defined(TN3270) printf("Escape character is '%s'.\n", control(escape)); (void) fflush(stdout); # else /* !defined(TN3270) */ if ((!In3270) && ((argc < 2) || strcmp(argv[1], "notmuch"))) { printf("Escape character is '%s'.\n", control(escape)); } # if defined(unix) if ((argc >= 2) && !strcmp(argv[1], "everything")) { printf("SIGIO received %d time%s.\n", sigiocount, (sigiocount == 1)? "":"s"); if (In3270) { printf("Process ID %d, process group %d.\n", getpid(), getpgrp(getpid())); printf("Terminal input:\n"); filestuff(tin); printf("Terminal output:\n"); filestuff(tout); printf("Network socket:\n"); filestuff(net); } } if (In3270 && transcom) { printf("Transparent mode command is '%s'.\n", transcom); } # endif /* defined(unix) */ (void) fflush(stdout); if (In3270) { return 0; } # endif /* defined(TN3270) */ return 1; } #ifdef SIGINFO /* * Function that gets called when SIGINFO is received. */ ayt_status() { (void) call(status, "status", "notmuch", 0); } #endif int tn(argc, argv) int argc; char *argv[]; { register struct hostent *host = 0; struct sockaddr_in sin; struct servent *sp = 0; unsigned long temp; #if defined(IP_OPTIONS) && defined(IPPROTO_IP) char *srp = 0, *strrchr(); unsigned long sourceroute(), srlen; #endif char *cmd, *hostp = 0, *portp = 0, *user = 0; /* clear the socket address prior to use */ bzero((char *)&sin, sizeof(sin)); if (connected) { printf("?Already connected to %s\n", hostname); setuid(getuid()); return 0; } if (argc < 2) { (void) strcpy(line, "open "); printf("(to) "); (void) fgets(&line[strlen(line)], sizeof(line) - strlen(line), stdin); makeargv(); argc = margc; argv = margv; } cmd = *argv; --argc; ++argv; while (argc) { if (isprefix(*argv, "help") || isprefix(*argv, "?")) goto usage; if (strcmp(*argv, "-l") == 0) { --argc; ++argv; if (argc == 0) goto usage; user = *argv++; --argc; continue; } if (strcmp(*argv, "-a") == 0) { --argc; ++argv; autologin = 1; continue; } if (hostp == 0) { hostp = *argv++; --argc; continue; } if (portp == 0) { portp = *argv++; --argc; continue; } usage: printf("usage: telnet [-l user] [-a] host-name [port]\n"); setuid(getuid()); return 0; } if (hostp == 0) goto usage; #if defined(IP_OPTIONS) && defined(IPPROTO_IP) if (hostp[0] == '@' || hostp[0] == '!') { if ((hostname = strrchr(hostp, ':')) == NULL) hostname = strrchr(hostp, '@'); hostname++; srp = 0; temp = sourceroute(hostp, &srp, &srlen); if (temp == 0) { herror(srp); setuid(getuid()); return 0; } else if (temp == -1) { printf("Bad source route option: %s\n", hostp); setuid(getuid()); return 0; } else { sin.sin_addr.s_addr = temp; sin.sin_family = AF_INET; } } else { #endif temp = inet_addr(hostp); if (temp != INADDR_NONE) { sin.sin_addr.s_addr = temp; sin.sin_family = AF_INET; - host = gethostbyaddr((char *)&temp, sizeof(temp), AF_INET); + if (doaddrlookup) + host = gethostbyaddr((char *)&temp, sizeof(temp), AF_INET); if (host) (void) strncpy(_hostname, host->h_name, sizeof(_hostname)); else (void) strncpy(_hostname, hostp, sizeof(_hostname)); _hostname[sizeof(_hostname)-1] = '\0'; hostname = _hostname; } else { host = gethostbyname(hostp); if (host) { sin.sin_family = host->h_addrtype; #if defined(h_addr) /* In 4.3, this is a #define */ memmove((caddr_t)&sin.sin_addr, host->h_addr_list[0], MIN(host->h_length, sizeof(sin.sin_addr))); #else /* defined(h_addr) */ memmove((caddr_t)&sin.sin_addr, host->h_addr, MIN(host->h_length, sizeof(sin.sin_addr))); #endif /* defined(h_addr) */ strncpy(_hostname, host->h_name, sizeof(_hostname)); _hostname[sizeof(_hostname)-1] = '\0'; hostname = _hostname; } else { herror(hostp); setuid(getuid()); return 0; } } #if defined(IP_OPTIONS) && defined(IPPROTO_IP) } #endif if (portp) { if (*portp == '-') { portp++; telnetport = 1; } else telnetport = 0; sin.sin_port = atoi(portp); if (sin.sin_port == 0) { sp = getservbyname(portp, "tcp"); if (sp) sin.sin_port = sp->s_port; else { printf("%s: bad port number\n", portp); setuid(getuid()); return 0; } } else { #if !defined(htons) u_short htons P((unsigned short)); #endif /* !defined(htons) */ sin.sin_port = htons(sin.sin_port); } } else { if (sp == 0) { sp = getservbyname("telnet", "tcp"); if (sp == 0) { fprintf(stderr, "telnet: tcp/telnet: unknown service\n"); setuid(getuid()); return 0; } sin.sin_port = sp->s_port; } telnetport = 1; } printf("Trying %s...\n", inet_ntoa(sin.sin_addr)); do { net = socket(AF_INET, SOCK_STREAM, 0); setuid(getuid()); if (net < 0) { perror("telnet: socket"); return 0; } #if defined(IP_OPTIONS) && defined(IPPROTO_IP) if (srp && setsockopt(net, IPPROTO_IP, IP_OPTIONS, (char *)srp, srlen) < 0) perror("setsockopt (IP_OPTIONS)"); #endif #if defined(IPPROTO_IP) && defined(IP_TOS) { # if defined(HAS_GETTOS) struct tosent *tp; if (tos < 0 && (tp = gettosbyname("telnet", "tcp"))) tos = tp->t_tos; # endif if (tos < 0) tos = IPTOS_LOWDELAY; if (tos && (setsockopt(net, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0) && (errno != ENOPROTOOPT)) perror("telnet: setsockopt (IP_TOS) (ignored)"); } #endif /* defined(IPPROTO_IP) && defined(IP_TOS) */ if (debug && SetSockOpt(net, SOL_SOCKET, SO_DEBUG, 1) < 0) { perror("setsockopt (SO_DEBUG)"); } if (connect(net, (struct sockaddr *)&sin, sizeof (sin)) < 0) { #if defined(h_addr) /* In 4.3, this is a #define */ if (host && host->h_addr_list[1]) { int oerrno = errno; fprintf(stderr, "telnet: connect to address %s: ", inet_ntoa(sin.sin_addr)); errno = oerrno; perror((char *)0); host->h_addr_list++; memcpy((caddr_t)&sin.sin_addr, host->h_addr_list[0], MIN(host->h_length, sizeof(sin.sin_addr))); (void) NetClose(net); continue; } #endif /* defined(h_addr) */ perror("telnet: Unable to connect to remote host"); return 0; } connected++; #if defined(AUTHENTICATION) auth_encrypt_connect(connected); #endif /* defined(AUTHENTICATION) */ } while (connected == 0); cmdrc(hostp, hostname); if (autologin && user == NULL) { struct passwd *pw; user = getenv("USER"); if (user == NULL || (pw = getpwnam(user)) && pw->pw_uid != getuid()) { if (pw = getpwuid(getuid())) user = pw->pw_name; else user = NULL; } } if (user) { env_define((unsigned char *)"USER", (unsigned char *)user); env_export((unsigned char *)"USER"); } (void) call(status, "status", "notmuch", 0); if (setjmp(peerdied) == 0) telnet(user); (void) NetClose(net); ExitString("Connection closed by foreign host.\n",1); /*NOTREACHED*/ } #define HELPINDENT (sizeof ("connect")) static char openhelp[] = "connect to a site", closehelp[] = "close current connection", logouthelp[] = "forcibly logout remote user and close the connection", quithelp[] = "exit telnet", statushelp[] = "print status information", helphelp[] = "print help information", sendhelp[] = "transmit special characters ('send ?' for more)", sethelp[] = "set operating parameters ('set ?' for more)", unsethelp[] = "unset operating parameters ('unset ?' for more)", togglestring[] ="toggle operating parameters ('toggle ?' for more)", slchelp[] = "change state of special charaters ('slc ?' for more)", displayhelp[] = "display operating parameters", #if defined(TN3270) && defined(unix) transcomhelp[] = "specify Unix command for transparent mode pipe", #endif /* defined(TN3270) && defined(unix) */ #if defined(AUTHENTICATION) authhelp[] = "turn on (off) authentication ('auth ?' for more)", #endif #if defined(unix) zhelp[] = "suspend telnet", #endif /* defined(unix) */ #if defined(SKEY) skeyhelp[] = "compute response to s/key challenge", #endif shellhelp[] = "invoke a subshell", envhelp[] = "change environment variables ('environ ?' for more)", modestring[] = "try to enter line or character mode ('mode ?' for more)"; static int help(); static Command cmdtab[] = { { "close", closehelp, bye, 1 }, { "logout", logouthelp, logout, 1 }, { "display", displayhelp, display, 0 }, { "mode", modestring, modecmd, 0 }, { "telnet", openhelp, tn, 0 }, { "open", openhelp, tn, 0 }, { "quit", quithelp, quit, 0 }, { "send", sendhelp, sendcmd, 0 }, { "set", sethelp, setcmd, 0 }, { "unset", unsethelp, unsetcmd, 0 }, { "status", statushelp, status, 0 }, { "toggle", togglestring, toggle, 0 }, { "slc", slchelp, slccmd, 0 }, #if defined(TN3270) && defined(unix) { "transcom", transcomhelp, settranscom, 0 }, #endif /* defined(TN3270) && defined(unix) */ #if defined(AUTHENTICATION) { "auth", authhelp, auth_cmd, 0 }, #endif #if defined(unix) { "z", zhelp, suspend, 0 }, #endif /* defined(unix) */ #if defined(TN3270) { "!", shellhelp, shell, 1 }, #else { "!", shellhelp, shell, 0 }, #endif { "environ", envhelp, env_cmd, 0 }, { "?", helphelp, help, 0 }, #if defined(SKEY) { "skey", skeyhelp, skey_calc, 0 }, #endif 0 }; static char crmodhelp[] = "deprecated command -- use 'toggle crmod' instead"; static char escapehelp[] = "deprecated command -- use 'set escape' instead"; static Command cmdtab2[] = { { "help", 0, help, 0 }, { "escape", escapehelp, setescape, 0 }, { "crmod", crmodhelp, togcrmod, 0 }, 0 }; /* * Call routine with argc, argv set from args (terminated by 0). */ /*VARARGS1*/ static call(va_alist) va_dcl { va_list ap; typedef int (*intrtn_t)(); intrtn_t routine; char *args[100]; int argno = 0; va_start(ap); routine = (va_arg(ap, intrtn_t)); while ((args[argno++] = va_arg(ap, char *)) != 0) { ; } va_end(ap); return (*routine)(argno-1, args); } static Command * getcmd(name) char *name; { Command *cm; if (cm = (Command *) genget(name, (char **) cmdtab, sizeof(Command))) return cm; return (Command *) genget(name, (char **) cmdtab2, sizeof(Command)); } void command(top, tbuf, cnt) int top; char *tbuf; int cnt; { register Command *c; setcommandmode(); if (!top) { putchar('\n'); #if defined(unix) } else { (void) signal(SIGINT, SIG_DFL); (void) signal(SIGQUIT, SIG_DFL); #endif /* defined(unix) */ } for (;;) { if (rlogin == _POSIX_VDISABLE) printf("%s> ", prompt); if (tbuf) { register char *cp; cp = line; while (cnt > 0 && (*cp++ = *tbuf++) != '\n') cnt--; tbuf = 0; if (cp == line || *--cp != '\n' || cp == line) goto getline; *cp = '\0'; if (rlogin == _POSIX_VDISABLE) printf("%s\n", line); } else { getline: if (rlogin != _POSIX_VDISABLE) printf("%s> ", prompt); if (fgets(line, sizeof(line), stdin) == NULL) { if (feof(stdin) || ferror(stdin)) { (void) quit(); /*NOTREACHED*/ } break; } } if (line[0] == 0) break; makeargv(); if (margv[0] == 0) { break; } c = getcmd(margv[0]); if (Ambiguous(c)) { printf("?Ambiguous command\n"); continue; } if (c == 0) { printf("?Invalid command\n"); continue; } if (c->needconnect && !connected) { printf("?Need to be connected first.\n"); continue; } if ((*c->handler)(margc, margv)) { break; } } if (!top) { if (!connected) { longjmp(toplevel, 1); /*NOTREACHED*/ } #if defined(TN3270) if (shell_active == 0) { setconnmode(0); } #else /* defined(TN3270) */ setconnmode(0); #endif /* defined(TN3270) */ } } /* * Help command. */ static help(argc, argv) int argc; char *argv[]; { register Command *c; if (argc == 1) { printf("Commands may be abbreviated. Commands are:\n\n"); for (c = cmdtab; c->name; c++) if (c->help) { printf("%-*s\t%s\n", HELPINDENT, c->name, c->help); } return 0; } while (--argc > 0) { register char *arg; arg = *++argv; c = getcmd(arg); if (Ambiguous(c)) printf("?Ambiguous help command %s\n", arg); else if (c == (Command *)0) printf("?Invalid help command %s\n", arg); else printf("%s\n", c->help); } return 0; } static char *rcname = 0; static char rcbuf[128]; cmdrc(m1, m2) char *m1, *m2; { register Command *c; FILE *rcfile; int gotmachine = 0; int l1 = strlen(m1); int l2 = strlen(m2); char m1save[64]; if (skiprc) return; strcpy(m1save, m1); m1 = m1save; if (rcname == 0) { rcname = getenv("HOME"); if (rcname && (strlen(rcname) + 10) < sizeof(rcbuf)) strcpy(rcbuf, rcname); else rcbuf[0] = '\0'; strcat(rcbuf, "/.telnetrc"); rcname = rcbuf; } if ((rcfile = fopen(rcname, "r")) == 0) { return; } for (;;) { if (fgets(line, sizeof(line), rcfile) == NULL) break; if (line[0] == 0) break; if (line[0] == '#') continue; if (gotmachine) { if (!isspace(line[0])) gotmachine = 0; } if (gotmachine == 0) { if (isspace(line[0])) continue; if (strncasecmp(line, m1, l1) == 0) strncpy(line, &line[l1], sizeof(line) - l1); else if (strncasecmp(line, m2, l2) == 0) strncpy(line, &line[l2], sizeof(line) - l2); else if (strncasecmp(line, "DEFAULT", 7) == 0) strncpy(line, &line[7], sizeof(line) - 7); else continue; if (line[0] != ' ' && line[0] != '\t' && line[0] != '\n') continue; gotmachine = 1; } makeargv(); if (margv[0] == 0) continue; c = getcmd(margv[0]); if (Ambiguous(c)) { printf("?Ambiguous command: %s\n", margv[0]); continue; } if (c == 0) { printf("?Invalid command: %s\n", margv[0]); continue; } /* * This should never happen... */ if (c->needconnect && !connected) { printf("?Need to be connected first for %s.\n", margv[0]); continue; } (*c->handler)(margc, margv); } fclose(rcfile); } #if defined(IP_OPTIONS) && defined(IPPROTO_IP) /* * Source route is handed in as * [!]@hop1@hop2...[@|:]dst * If the leading ! is present, it is a * strict source route, otherwise it is * assmed to be a loose source route. * * We fill in the source route option as * hop1,hop2,hop3...dest * and return a pointer to hop1, which will * be the address to connect() to. * * Arguments: * arg: pointer to route list to decipher * * cpp: If *cpp is not equal to NULL, this is a * pointer to a pointer to a character array * that should be filled in with the option. * * lenp: pointer to an integer that contains the * length of *cpp if *cpp != NULL. * * Return values: * * Returns the address of the host to connect to. If the * return value is -1, there was a syntax error in the * option, either unknown characters, or too many hosts. * If the return value is 0, one of the hostnames in the * path is unknown, and *cpp is set to point to the bad * hostname. * * *cpp: If *cpp was equal to NULL, it will be filled * in with a pointer to our static area that has * the option filled in. This will be 32bit aligned. * * *lenp: This will be filled in with how long the option * pointed to by *cpp is. * */ unsigned long sourceroute(arg, cpp, lenp) char *arg; char **cpp; int *lenp; { static char lsr[44]; #ifdef sysV88 static IOPTN ipopt; #endif char *cp, *cp2, *lsrp, *lsrep; register int tmp; struct in_addr sin_addr; register struct hostent *host = 0; register char c; /* * Verify the arguments, and make sure we have * at least 7 bytes for the option. */ if (cpp == NULL || lenp == NULL) return((unsigned long)-1); if (*cpp != NULL && *lenp < 7) return((unsigned long)-1); /* * Decide whether we have a buffer passed to us, * or if we need to use our own static buffer. */ if (*cpp) { lsrp = *cpp; lsrep = lsrp + *lenp; } else { *cpp = lsrp = lsr; lsrep = lsrp + 44; } cp = arg; /* * Next, decide whether we have a loose source * route or a strict source route, and fill in * the begining of the option. */ #ifndef sysV88 if (*cp == '!') { cp++; *lsrp++ = IPOPT_SSRR; } else *lsrp++ = IPOPT_LSRR; #else if (*cp == '!') { cp++; ipopt.io_type = IPOPT_SSRR; } else ipopt.io_type = IPOPT_LSRR; #endif if (*cp != '@') return((unsigned long)-1); #ifndef sysV88 lsrp++; /* skip over length, we'll fill it in later */ *lsrp++ = 4; #endif cp++; sin_addr.s_addr = 0; for (c = 0;;) { if (c == ':') cp2 = 0; else for (cp2 = cp; c = *cp2; cp2++) { if (c == ',') { *cp2++ = '\0'; if (*cp2 == '@') cp2++; } else if (c == '@') { *cp2++ = '\0'; } else if (c == ':') { *cp2++ = '\0'; } else continue; break; } if (!c) cp2 = 0; if ((tmp = inet_addr(cp)) != -1) { sin_addr.s_addr = tmp; } else if (host = gethostbyname(cp)) { #if defined(h_addr) memcpy((caddr_t)&sin_addr, host->h_addr_list[0], MIN(host->h_length,sizeof(sin_addr))); #else memcpy((caddr_t)&sin_addr, host->h_addr, MIN(host->h_length,sizeof(sin_addr))); #endif } else { *cpp = cp; return(0); } memcpy(lsrp, (char *)&sin_addr, 4); lsrp += 4; if (cp2) cp = cp2; else break; /* * Check to make sure there is space for next address */ if (lsrp + 4 > lsrep) return((unsigned long)-1); } #ifndef sysV88 if ((*(*cpp+IPOPT_OLEN) = lsrp - *cpp) <= 7) { *cpp = 0; *lenp = 0; return((unsigned long)-1); } *lsrp++ = IPOPT_NOP; /* 32 bit word align it */ *lenp = lsrp - *cpp; #else ipopt.io_len = lsrp - *cpp; if (ipopt.io_len <= 5) { /* Is 3 better ? */ *cpp = 0; *lenp = 0; return((unsigned long)-1); } *lenp = sizeof(ipopt); *cpp = (char *) &ipopt; #endif return(sin_addr.s_addr); } #endif diff --git a/usr.bin/telnet/externs.h b/usr.bin/telnet/externs.h index 43fdb43287be..a8c3f24f57fd 100644 --- a/usr.bin/telnet/externs.h +++ b/usr.bin/telnet/externs.h @@ -1,478 +1,479 @@ /* * Copyright (c) 1988, 1990, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)externs.h 8.2 (Berkeley) 12/15/93 */ #ifndef BSD # define BSD 43 #endif /* * ucb stdio.h defines BSD as something wierd */ #if defined(sun) && defined(__svr4__) #define BSD 43 #endif #ifndef USE_TERMIO # if BSD > 43 || defined(SYSV_TERMIO) # define USE_TERMIO # endif #endif #include #include #if defined(CRAY) && !defined(NO_BSD_SETJMP) #include #endif #ifndef FILIO_H #include #else #include #endif #ifdef CRAY # include #endif /* CRAY */ #ifdef USE_TERMIO # ifndef VINTR # ifdef SYSV_TERMIO # include # else # include # define termio termios # endif # endif #endif #if defined(NO_CC_T) || !defined(USE_TERMIO) # if !defined(USE_TERMIO) typedef char cc_t; # else typedef unsigned char cc_t; # endif #endif #ifndef NO_STRING_H #include #endif #include #ifndef _POSIX_VDISABLE # ifdef sun # include /* pick up VDISABLE definition, mayby */ # endif # ifdef VDISABLE # define _POSIX_VDISABLE VDISABLE # else # define _POSIX_VDISABLE ((cc_t)'\377') # endif #endif #define SUBBUFSIZE 256 #ifndef CRAY extern int errno; /* outside this world */ #endif /* !CRAY */ #if !defined(P) # ifdef __STDC__ # define P(x) x # else # define P(x) () # endif #endif extern int autologin, /* Autologin enabled */ skiprc, /* Don't process the ~/.telnetrc file */ eight, /* use eight bit mode (binary in and/or out */ flushout, /* flush output */ connected, /* Are we connected to the other side? */ globalmode, /* Mode tty should be in */ In3270, /* Are we in 3270 mode? */ telnetport, /* Are we connected to the telnet port? */ localflow, /* Flow control handled locally */ restartany, /* If flow control, restart output on any character */ localchars, /* we recognize interrupt/quit */ donelclchars, /* the user has set "localchars" */ showoptions, net, /* Network file descriptor */ tin, /* Terminal input file descriptor */ tout, /* Terminal output file descriptor */ crlf, /* Should '\r' be mapped to (or )? */ autoflush, /* flush output when interrupting? */ autosynch, /* send interrupt characters with SYNCH? */ SYNCHing, /* Is the stream in telnet SYNCH mode? */ donebinarytoggle, /* the user has put us in binary */ dontlecho, /* do we suppress local echoing right now? */ crmod, netdata, /* Print out network data flow */ prettydump, /* Print "netdata" output in user readable format */ #if defined(unix) #if defined(TN3270) cursesdata, /* Print out curses data flow */ apitrace, /* Trace API transactions */ #endif /* defined(TN3270) */ termdata, /* Print out terminal data flow */ #endif /* defined(unix) */ debug, /* Debug level */ + doaddrlookup, /* do a reverse lookup? */ clienteof; /* Client received EOF */ extern cc_t escape; /* Escape to command mode */ extern cc_t rlogin; /* Rlogin mode escape character */ #ifdef KLUDGELINEMODE extern cc_t echoc; /* Toggle local echoing */ #endif extern char *prompt; /* Prompt for command. */ extern char doopt[], dont[], will[], wont[], options[], /* All the little options */ *hostname; /* Who are we connected to? */ /* * We keep track of each side of the option negotiation. */ #define MY_STATE_WILL 0x01 #define MY_WANT_STATE_WILL 0x02 #define MY_STATE_DO 0x04 #define MY_WANT_STATE_DO 0x08 /* * Macros to check the current state of things */ #define my_state_is_do(opt) (options[opt]&MY_STATE_DO) #define my_state_is_will(opt) (options[opt]&MY_STATE_WILL) #define my_want_state_is_do(opt) (options[opt]&MY_WANT_STATE_DO) #define my_want_state_is_will(opt) (options[opt]&MY_WANT_STATE_WILL) #define my_state_is_dont(opt) (!my_state_is_do(opt)) #define my_state_is_wont(opt) (!my_state_is_will(opt)) #define my_want_state_is_dont(opt) (!my_want_state_is_do(opt)) #define my_want_state_is_wont(opt) (!my_want_state_is_will(opt)) #define set_my_state_do(opt) {options[opt] |= MY_STATE_DO;} #define set_my_state_will(opt) {options[opt] |= MY_STATE_WILL;} #define set_my_want_state_do(opt) {options[opt] |= MY_WANT_STATE_DO;} #define set_my_want_state_will(opt) {options[opt] |= MY_WANT_STATE_WILL;} #define set_my_state_dont(opt) {options[opt] &= ~MY_STATE_DO;} #define set_my_state_wont(opt) {options[opt] &= ~MY_STATE_WILL;} #define set_my_want_state_dont(opt) {options[opt] &= ~MY_WANT_STATE_DO;} #define set_my_want_state_wont(opt) {options[opt] &= ~MY_WANT_STATE_WILL;} /* * Make everything symetrical */ #define HIS_STATE_WILL MY_STATE_DO #define HIS_WANT_STATE_WILL MY_WANT_STATE_DO #define HIS_STATE_DO MY_STATE_WILL #define HIS_WANT_STATE_DO MY_WANT_STATE_WILL #define his_state_is_do my_state_is_will #define his_state_is_will my_state_is_do #define his_want_state_is_do my_want_state_is_will #define his_want_state_is_will my_want_state_is_do #define his_state_is_dont my_state_is_wont #define his_state_is_wont my_state_is_dont #define his_want_state_is_dont my_want_state_is_wont #define his_want_state_is_wont my_want_state_is_dont #define set_his_state_do set_my_state_will #define set_his_state_will set_my_state_do #define set_his_want_state_do set_my_want_state_will #define set_his_want_state_will set_my_want_state_do #define set_his_state_dont set_my_state_wont #define set_his_state_wont set_my_state_dont #define set_his_want_state_dont set_my_want_state_wont #define set_his_want_state_wont set_my_want_state_dont extern FILE *NetTrace; /* Where debugging output goes */ extern unsigned char NetTraceFile[]; /* Name of file where debugging output goes */ extern void SetNetTrace P((char *)); /* Function to change where debugging goes */ extern jmp_buf peerdied, toplevel; /* For error conditions. */ extern void command P((int, char *, int)), Dump P((int, unsigned char *, int)), init_3270 P((void)), printoption P((char *, int, int)), printsub P((int, unsigned char *, int)), sendnaws P((void)), setconnmode P((int)), setcommandmode P((void)), setneturg P((void)), sys_telnet_init P((void)), telnet P((char *)), tel_enter_binary P((int)), TerminalFlushOutput P((void)), TerminalNewMode P((int)), TerminalRestoreState P((void)), TerminalSaveState P((void)), tninit P((void)), upcase P((char *)), willoption P((int)), wontoption P((int)); extern void send_do P((int, int)), send_dont P((int, int)), send_will P((int, int)), send_wont P((int, int)); extern void lm_will P((unsigned char *, int)), lm_wont P((unsigned char *, int)), lm_do P((unsigned char *, int)), lm_dont P((unsigned char *, int)), lm_mode P((unsigned char *, int, int)); extern void slc_init P((void)), slcstate P((void)), slc_mode_export P((void)), slc_mode_import P((int)), slc_import P((int)), slc_export P((void)), slc P((unsigned char *, int)), slc_check P((void)), slc_start_reply P((void)), slc_add_reply P((int, int, int)), slc_end_reply P((void)); extern int slc_update P((void)); extern void env_opt P((unsigned char *, int)), env_opt_start P((void)), env_opt_start_info P((void)), env_opt_add P((unsigned char *)), env_opt_end P((int)); extern unsigned char *env_default P((int, int)), *env_getvalue P((unsigned char *)); extern int get_status P((void)), dosynch P((void)); extern cc_t *tcval P((int)); #ifndef USE_TERMIO extern struct tchars ntc; extern struct ltchars nltc; extern struct sgttyb nttyb; # define termEofChar ntc.t_eofc # define termEraseChar nttyb.sg_erase # define termFlushChar nltc.t_flushc # define termIntChar ntc.t_intrc # define termKillChar nttyb.sg_kill # define termLiteralNextChar nltc.t_lnextc # define termQuitChar ntc.t_quitc # define termSuspChar nltc.t_suspc # define termRprntChar nltc.t_rprntc # define termWerasChar nltc.t_werasc # define termStartChar ntc.t_startc # define termStopChar ntc.t_stopc # define termForw1Char ntc.t_brkc extern cc_t termForw2Char; extern cc_t termAytChar; # define termEofCharp (cc_t *)&ntc.t_eofc # define termEraseCharp (cc_t *)&nttyb.sg_erase # define termFlushCharp (cc_t *)&nltc.t_flushc # define termIntCharp (cc_t *)&ntc.t_intrc # define termKillCharp (cc_t *)&nttyb.sg_kill # define termLiteralNextCharp (cc_t *)&nltc.t_lnextc # define termQuitCharp (cc_t *)&ntc.t_quitc # define termSuspCharp (cc_t *)&nltc.t_suspc # define termRprntCharp (cc_t *)&nltc.t_rprntc # define termWerasCharp (cc_t *)&nltc.t_werasc # define termStartCharp (cc_t *)&ntc.t_startc # define termStopCharp (cc_t *)&ntc.t_stopc # define termForw1Charp (cc_t *)&ntc.t_brkc # define termForw2Charp (cc_t *)&termForw2Char # define termAytCharp (cc_t *)&termAytChar # else extern struct termio new_tc; # define termEofChar new_tc.c_cc[VEOF] # define termEraseChar new_tc.c_cc[VERASE] # define termIntChar new_tc.c_cc[VINTR] # define termKillChar new_tc.c_cc[VKILL] # define termQuitChar new_tc.c_cc[VQUIT] # ifndef VSUSP extern cc_t termSuspChar; # else # define termSuspChar new_tc.c_cc[VSUSP] # endif # if defined(VFLUSHO) && !defined(VDISCARD) # define VDISCARD VFLUSHO # endif # ifndef VDISCARD extern cc_t termFlushChar; # else # define termFlushChar new_tc.c_cc[VDISCARD] # endif # ifndef VWERASE extern cc_t termWerasChar; # else # define termWerasChar new_tc.c_cc[VWERASE] # endif # ifndef VREPRINT extern cc_t termRprntChar; # else # define termRprntChar new_tc.c_cc[VREPRINT] # endif # ifndef VLNEXT extern cc_t termLiteralNextChar; # else # define termLiteralNextChar new_tc.c_cc[VLNEXT] # endif # ifndef VSTART extern cc_t termStartChar; # else # define termStartChar new_tc.c_cc[VSTART] # endif # ifndef VSTOP extern cc_t termStopChar; # else # define termStopChar new_tc.c_cc[VSTOP] # endif # ifndef VEOL extern cc_t termForw1Char; # else # define termForw1Char new_tc.c_cc[VEOL] # endif # ifndef VEOL2 extern cc_t termForw2Char; # else # define termForw2Char new_tc.c_cc[VEOL] # endif # ifndef VSTATUS extern cc_t termAytChar; #else # define termAytChar new_tc.c_cc[VSTATUS] #endif # if !defined(CRAY) || defined(__STDC__) # define termEofCharp &termEofChar # define termEraseCharp &termEraseChar # define termIntCharp &termIntChar # define termKillCharp &termKillChar # define termQuitCharp &termQuitChar # define termSuspCharp &termSuspChar # define termFlushCharp &termFlushChar # define termWerasCharp &termWerasChar # define termRprntCharp &termRprntChar # define termLiteralNextCharp &termLiteralNextChar # define termStartCharp &termStartChar # define termStopCharp &termStopChar # define termForw1Charp &termForw1Char # define termForw2Charp &termForw2Char # define termAytCharp &termAytChar # else /* Work around a compiler bug */ # define termEofCharp 0 # define termEraseCharp 0 # define termIntCharp 0 # define termKillCharp 0 # define termQuitCharp 0 # define termSuspCharp 0 # define termFlushCharp 0 # define termWerasCharp 0 # define termRprntCharp 0 # define termLiteralNextCharp 0 # define termStartCharp 0 # define termStopCharp 0 # define termForw1Charp 0 # define termForw2Charp 0 # define termAytCharp 0 # endif #endif /* Ring buffer structures which are shared */ extern Ring netoring, netiring, ttyoring, ttyiring; /* Tn3270 section */ #if defined(TN3270) extern int HaveInput, /* Whether an asynchronous I/O indication came in */ noasynchtty, /* Don't do signals on I/O (SIGURG, SIGIO) */ noasynchnet, /* Don't do signals on I/O (SIGURG, SIGIO) */ sigiocount, /* Count of SIGIO receptions */ shell_active; /* Subshell is active */ extern char *Ibackp, /* Oldest byte of 3270 data */ Ibuf[], /* 3270 buffer */ *Ifrontp, /* Where next 3270 byte goes */ tline[], *transcom; /* Transparent command */ extern int settranscom P((int, char**)); extern void inputAvailable P((int)); #endif /* defined(TN3270) */ diff --git a/usr.bin/telnet/main.c b/usr.bin/telnet/main.c index 3c4d8df281de..7f7c9ff0059b 100644 --- a/usr.bin/telnet/main.c +++ b/usr.bin/telnet/main.c @@ -1,313 +1,316 @@ /* * Copyright (c) 1988, 1990, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static char copyright[] = "@(#) Copyright (c) 1988, 1990, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint static char sccsid[] = "@(#)main.c 8.2 (Berkeley) 12/15/93"; #endif /* not lint */ #include #include "ring.h" #include "externs.h" #include "defines.h" /* These values need to be the same as defined in libtelnet/kerberos5.c */ /* Either define them in both places, or put in some common header file. */ #define OPTS_FORWARD_CREDS 0x00000002 #define OPTS_FORWARDABLE_CREDS 0x00000001 #if 0 #define FORWARD #endif /* * Initialize variables. */ void tninit() { init_terminal(); init_network(); init_telnet(); init_sys(); #if defined(TN3270) init_3270(); #endif } void usage() { fprintf(stderr, "Usage: %s %s%s%s%s\n", prompt, #ifdef AUTHENTICATION "[-8] [-E] [-K] [-L] [-S tos] [-X atype] [-a] [-c] [-d] [-e char]", "\n\t[-k realm] [-l user] [-f/-F] [-n tracefile] ", #else "[-8] [-E] [-L] [-S tos] [-a] [-c] [-d] [-e char] [-l user]", "\n\t[-n tracefile]", #endif #if defined(TN3270) && defined(unix) # ifdef AUTHENTICATION "[-noasynch] [-noasynctty]\n\t[-noasyncnet] [-r] [-t transcom] ", # else "[-noasynch] [-noasynctty] [-noasyncnet] [-r]\n\t[-t transcom]", # endif #else "[-r] ", #endif "[host-name [port]]" ); exit(1); } /* * main. Parse arguments, invoke the protocol or command parser. */ main(argc, argv) int argc; char *argv[]; { extern char *optarg; extern int optind; int ch; char *user, *strrchr(); #ifdef FORWARD extern int forward_flags; #endif /* FORWARD */ tninit(); /* Clear out things */ #if defined(CRAY) && !defined(__STDC__) _setlist_init(); /* Work around compiler bug */ #endif TerminalSaveState(); if (prompt = strrchr(argv[0], '/')) ++prompt; else prompt = argv[0]; user = NULL; rlogin = (strncmp(prompt, "rlog", 4) == 0) ? '~' : _POSIX_VDISABLE; autologin = -1; - while ((ch = getopt(argc, argv, "8EKLS:X:acde:fFk:l:n:rt:x")) != -1) { + while ((ch = getopt(argc, argv, "8EKLNS:X:acde:fFk:l:n:rt:x")) != -1) { switch(ch) { case '8': eight = 3; /* binary output and input */ break; case 'E': rlogin = escape = _POSIX_VDISABLE; break; case 'K': #ifdef AUTHENTICATION autologin = 0; #endif break; case 'L': eight |= 2; /* binary output only */ break; + case 'N': + doaddrlookup = 0; + break; case 'S': { #ifdef HAS_GETTOS extern int tos; if ((tos = parsetos(optarg, "tcp")) < 0) fprintf(stderr, "%s%s%s%s\n", prompt, ": Bad TOS argument '", optarg, "; will try to use default TOS"); #else fprintf(stderr, "%s: Warning: -S ignored, no parsetos() support.\n", prompt); #endif } break; case 'X': #ifdef AUTHENTICATION auth_disable_name(optarg); #endif break; case 'a': autologin = 1; break; case 'c': skiprc = 1; break; case 'd': debug = 1; break; case 'e': set_escape_char(optarg); break; case 'f': #if defined(AUTHENTICATION) && defined(KRB5) && defined(FORWARD) if (forward_flags & OPTS_FORWARD_CREDS) { fprintf(stderr, "%s: Only one of -f and -F allowed.\n", prompt); usage(); } forward_flags |= OPTS_FORWARD_CREDS; #else fprintf(stderr, "%s: Warning: -f ignored, no Kerberos V5 support.\n", prompt); #endif break; case 'F': #if defined(AUTHENTICATION) && defined(KRB5) && defined(FORWARD) if (forward_flags & OPTS_FORWARD_CREDS) { fprintf(stderr, "%s: Only one of -f and -F allowed.\n", prompt); usage(); } forward_flags |= OPTS_FORWARD_CREDS; forward_flags |= OPTS_FORWARDABLE_CREDS; #else fprintf(stderr, "%s: Warning: -F ignored, no Kerberos V5 support.\n", prompt); #endif break; case 'k': #if defined(AUTHENTICATION) && defined(KRB4) { extern char *dest_realm, dst_realm_buf[], dst_realm_sz; dest_realm = dst_realm_buf; (void)strncpy(dest_realm, optarg, dst_realm_sz); } #else fprintf(stderr, "%s: Warning: -k ignored, no Kerberos V4 support.\n", prompt); #endif break; case 'l': autologin = 1; user = optarg; break; case 'n': #if defined(TN3270) && defined(unix) /* distinguish between "-n oasynch" and "-noasynch" */ if (argv[optind - 1][0] == '-' && argv[optind - 1][1] == 'n' && argv[optind - 1][2] == 'o') { if (!strcmp(optarg, "oasynch")) { noasynchtty = 1; noasynchnet = 1; } else if (!strcmp(optarg, "oasynchtty")) noasynchtty = 1; else if (!strcmp(optarg, "oasynchnet")) noasynchnet = 1; } else #endif /* defined(TN3270) && defined(unix) */ SetNetTrace(optarg); break; case 'r': rlogin = '~'; break; case 't': #if defined(TN3270) && defined(unix) transcom = tline; (void)strcpy(transcom, optarg); #else fprintf(stderr, "%s: Warning: -t ignored, no TN3270 support.\n", prompt); #endif break; case 'x': fprintf(stderr, "%s: Warning: -x ignored, no ENCRYPT support.\n", prompt); break; case '?': default: usage(); /* NOTREACHED */ } } if (autologin == -1) autologin = (rlogin == _POSIX_VDISABLE) ? 0 : 1; argc -= optind; argv += optind; if (argc) { char *args[7], **argp = args; if (argc > 2) usage(); *argp++ = prompt; if (user) { *argp++ = "-l"; *argp++ = user; } *argp++ = argv[0]; /* host */ if (argc > 1) *argp++ = argv[1]; /* port */ *argp = 0; if (setjmp(toplevel) != 0) Exit(0); if (tn(argp - args, args) == 1) return (0); else return (1); } (void)setjmp(toplevel); for (;;) { #ifdef TN3270 if (shell_active) shell_continue(); else #endif command(1, 0, 0); } } diff --git a/usr.bin/telnet/telnet.1 b/usr.bin/telnet/telnet.1 index b2349195b2ce..de161e850807 100644 --- a/usr.bin/telnet/telnet.1 +++ b/usr.bin/telnet/telnet.1 @@ -1,1389 +1,1392 @@ .\" Copyright (c) 1983, 1990, 1993 .\" The Regents of the University of California. All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" 3. All advertising materials mentioning features or use of this software .\" must display the following acknowledgement: .\" This product includes software developed by the University of .\" California, Berkeley and its contributors. .\" 4. Neither the name of the University nor the names of its contributors .\" may be used to endorse or promote products derived from this software .\" without specific prior written permission. .\" .\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" .\" @(#)telnet.1 8.5 (Berkeley) 3/1/94 -.\" $Id: telnet.1,v 1.8 1997/11/11 05:00:59 steve Exp $ +.\" $Id: telnet.1,v 1.9 1997/12/27 18:58:27 steve Exp $ .\" .Dd March 1, 1994 .Dt TELNET 1 .Os BSD 4.2 .Sh NAME .Nm telnet .Nd user interface to the .Tn TELNET protocol .Sh SYNOPSIS .Nm .Op Fl 8EFKLacdfrx .Op Fl S Ar tos .Op Fl X Ar authtype .Op Fl e Ar escapechar .Op Fl k Ar realm .Op Fl l Ar user .Op Fl n Ar tracefile .Oo .Ar host .Op Ar port .Oc .Sh DESCRIPTION The .Nm command is used to communicate with another host using the .Tn TELNET protocol. If .Nm is invoked without the .Ar host argument, it enters command mode, indicated by its prompt .Pq Nm telnet\&> . In this mode, it accepts and executes the commands listed below. If it is invoked with arguments, it performs an .Ic open command with those arguments. .Pp Options: .Bl -tag -width indent .It Fl 8 Specifies an 8-bit data path. This causes an attempt to negotiate the .Dv TELNET BINARY option on both input and output. .It Fl E Stops any character from being recognized as an escape character. .It Fl F If Kerberos V5 authentication is being used, the .Fl F option allows the local credentials to be forwarded to the remote system, including any credentials that have already been forwarded into the local environment. .It Fl K Specifies no automatic login to the remote system. .It Fl L Specifies an 8-bit data path on output. This causes the .Dv BINARY option to be negotiated on output. +.It Fl N +Prevents IP address to name lookup when destination host is given +as an IP address. .It Fl S Ar tos Sets the IP type-of-service (TOS) option for the telnet connection to the value .Ar tos, which can be a numeric TOS value or, on systems that support it, a symbolic TOS name found in the .Pa /etc/iptos file. .It Fl X Ar atype Disables the .Ar atype type of authentication. .It Fl a Attempt automatic login. Currently, this sends the user name via the .Ev USER variable of the .Ev ENVIRON option if supported by the remote system. The name used is that of the current user as returned by .Xr getlogin 2 if it agrees with the current user ID, otherwise it is the name associated with the user ID. .It Fl c Disables the reading of the user's .Pa \&.telnetrc file. (See the .Ic toggle skiprc command on this man page.) .It Fl d Sets the initial value of the .Ic debug toggle to .Dv TRUE . .It Fl e Ar escapechar Sets the initial .Nm escape character to .Ar escapechar . If .Ar escapechar is omitted, then there will be no escape character. .It Fl f If Kerberos V5 authentication is being used, the .Fl f option allows the local credentials to be forwarded to the remote system. .It Fl k Ar realm If Kerberos authentication is being used, the .Fl k option requests that .Nm obtain tickets for the remote host in realm .Ar realm instead of the remote host's realm, as determined by .Xr krb_realmofhost 3 . .It Fl l Ar user When connecting to the remote system, if the remote system understands the .Ev ENVIRON option, then .Ar user will be sent to the remote system as the value for the variable .Ev USER . This option implies the .Fl a option. This option may also be used with the .Ic open command. .It Fl n Ar tracefile Opens .Ar tracefile for recording trace information. See the .Ic set tracefile command below. .It Fl r Specifies a user interface similar to .Xr rlogin 1 . In this mode, the escape character is set to the tilde (~) character, unless modified by the .Fl e option. .It Fl x Turns on encryption of the data stream if possible. This option is not available outside of the United States and Canada. .It Ar host Indicates the official name, an alias, or the Internet address of a remote host. .It Ar port Indicates a port number (address of an application). If a number is not specified, the default .Nm port is used. .El .Pp When in rlogin mode, a line of the form ~. disconnects from the remote host; ~ is the .Nm escape character. Similarly, the line ~^Z suspends the .Nm session. The line ~^] escapes to the normal .Nm escape prompt. .Pp Once a connection has been opened, .Nm will attempt to enable the .Dv TELNET LINEMODE option. If this fails, then .Nm will revert to one of two input modes: either \*(Lqcharacter at a time\*(Rq or \*(Lqold line by line\*(Rq depending on what the remote system supports. .Pp When .Dv LINEMODE is enabled, character processing is done on the local system, under the control of the remote system. When input editing or character echoing is to be disabled, the remote system will relay that information. The remote system will also relay changes to any special characters that happen on the remote system, so that they can take effect on the local system. .Pp In \*(Lqcharacter at a time\*(Rq mode, most text typed is immediately sent to the remote host for processing. .Pp In \*(Lqold line by line\*(Rq mode, all text is echoed locally, and (normally) only completed lines are sent to the remote host. The \*(Lqlocal echo character\*(Rq (initially \*(Lq^E\*(Rq) may be used to turn off and on the local echo (this would mostly be used to enter passwords without the password being echoed). .Pp If the .Dv LINEMODE option is enabled, or if the .Ic localchars toggle is .Dv TRUE (the default for \*(Lqold line by line\*(Rq; see below), the user's .Ic quit , .Ic intr , and .Ic flush characters are trapped locally, and sent as .Tn TELNET protocol sequences to the remote side. If .Dv LINEMODE has ever been enabled, then the user's .Ic susp and .Ic eof are also sent as .Tn TELNET protocol sequences, and .Ic quit is sent as a .Dv TELNET ABORT instead of .Dv BREAK . There are options (see .Ic toggle .Ic autoflush and .Ic toggle .Ic autosynch below) which cause this action to flush subsequent output to the terminal (until the remote host acknowledges the .Tn TELNET sequence) and flush previous terminal input (in the case of .Ic quit and .Ic intr ) . .Pp While connected to a remote host, .Nm command mode may be entered by typing the .Nm \*(Lqescape character\*(Rq (initially \*(Lq^]\*(Rq). When in command mode, the normal terminal editing conventions are available. .Pp The following .Nm commands are available. Only enough of each command to uniquely identify it need be typed (this is also true for arguments to the .Ic mode , .Ic set , .Ic toggle , .Ic unset , .Ic slc , .Ic environ , and .Ic display commands). .Pp .Bl -tag -width "mode type" .It Ic auth Ar argument ... The auth command manipulates the information sent through the .Dv TELNET AUTHENTICATE option. Valid arguments for the .Ic auth command are: .Bl -tag -width "disable type" .It Ic disable Ar type Disables the specified type of authentication. To obtain a list of available types, use the .Ic auth disable \&? command. .It Ic enable Ar type Enables the specified type of authentication. To obtain a list of available types, use the .Ic auth enable \&? command. .It Ic status Lists the current status of the various types of authentication. .El .It Ic close Close a .Tn TELNET session and return to command mode. .It Ic display Ar argument ... Displays all, or some, of the .Ic set and .Ic toggle values (see below). .It Ic encrypt Ar argument ... The encrypt command manipulates the information sent through the .Dv TELNET ENCRYPT option. .Pp Note: Because of export controls, the .Dv TELNET ENCRYPT option is not supported outside of the United States and Canada. .Pp Valid arguments for the .Ic encrypt command are: .Bl -tag -width Ar .It Ic disable Ar type Ic [input|output] Disables the specified type of encryption. If you omit the input and output, both input and output are disabled. To obtain a list of available types, use the .Ic encrypt disable \&? command. .It Ic enable Ar type Ic [input|output] Enables the specified type of encryption. If you omit input and output, both input and output are enabled. To obtain a list of available types, use the .Ic encrypt enable \&? command. .It Ic input This is the same as the .Ic encrypt start input command. .It Ic -input This is the same as the .Ic encrypt stop input command. .It Ic output This is the same as the .Ic encrypt start output command. .It Ic -output This is the same as the .Ic encrypt stop output command. .It Ic start Ic [input|output] Attempts to start encryption. If you omit .Ic input and .Ic output, both input and output are enabled. To obtain a list of available types, use the .Ic encrypt enable \&? command. .It Ic status Lists the current status of encryption. .It Ic stop Ic [input|output] Stops encryption. If you omit input and output, encryption is on both input and output. .It Ic type Ar type Sets the default type of encryption to be used with later .Ic encrypt start or .Ic encrypt stop commands. .El .It Ic environ Ar arguments... The .Ic environ command is used to manipulate the variables that my be sent through the .Dv TELNET ENVIRON option. The initial set of variables is taken from the users environment, with only the .Ev DISPLAY and .Ev PRINTER variables being exported by default. The .Ev USER variable is also exported if the .Fl a or .Fl l options are used. .Pp Valid arguments for the .Ic environ command are: .Bl -tag -width Fl .It Ic define Ar variable value Define the variable .Ar variable to have a value of .Ar value. Any variables defined by this command are automatically exported. The .Ar value may be enclosed in single or double quotes so that tabs and spaces may be included. .It Ic undefine Ar variable Remove .Ar variable from the list of environment variables. .It Ic export Ar variable Mark the variable .Ar variable to be exported to the remote side. .It Ic unexport Ar variable Mark the variable .Ar variable to not be exported unless explicitly asked for by the remote side. .It Ic list List the current set of environment variables. Those marked with a .Cm * will be sent automatically, other variables will only be sent if explicitly requested. .It Ic \&? Prints out help information for the .Ic environ command. .El .It Ic logout Sends the .Dv TELNET LOGOUT option to the remote side. This command is similar to a .Ic close command; however, if the remote side does not support the .Dv LOGOUT option, nothing happens. If, however, the remote side does support the .Dv LOGOUT option, this command should cause the remote side to close the .Tn TELNET connection. If the remote side also supports the concept of suspending a user's session for later reattachment, the logout argument indicates that you should terminate the session immediately. .It Ic mode Ar type .Ar Type is one of several options, depending on the state of the .Tn TELNET session. The remote host is asked for permission to go into the requested mode. If the remote host is capable of entering that mode, the requested mode will be entered. .Bl -tag -width Ar .It Ic character Disable the .Dv TELNET LINEMODE option, or, if the remote side does not understand the .Dv LINEMODE option, then enter \*(Lqcharacter at a time\*(Rq mode. .It Ic line Enable the .Dv TELNET LINEMODE option, or, if the remote side does not understand the .Dv LINEMODE option, then attempt to enter \*(Lqold-line-by-line\*(Rq mode. .It Ic isig Pq Ic \-isig Attempt to enable (disable) the .Dv TRAPSIG mode of the .Dv LINEMODE option. This requires that the .Dv LINEMODE option be enabled. .It Ic edit Pq Ic \-edit Attempt to enable (disable) the .Dv EDIT mode of the .Dv LINEMODE option. This requires that the .Dv LINEMODE option be enabled. .It Ic softtabs Pq Ic \-softtabs Attempt to enable (disable) the .Dv SOFT_TAB mode of the .Dv LINEMODE option. This requires that the .Dv LINEMODE option be enabled. .It Ic litecho Pq Ic \-litecho Attempt to enable (disable) the .Dv LIT_ECHO mode of the .Dv LINEMODE option. This requires that the .Dv LINEMODE option be enabled. .It Ic \&? Prints out help information for the .Ic mode command. .El .It Xo .Ic open Ar host .Op Fl l Ar user .Oo Op Fl .Ar port Oc .Xc Open a connection to the named host. If no port number is specified, .Nm will attempt to contact a .Tn TELNET server at the default port. The host specification may be either a host name (see .Xr hosts 5 ) or an Internet address specified in the \*(Lqdot notation\*(Rq (see .Xr inet 3 ) . The .Fl l option may be used to specify the user name to be passed to the remote system via the .Ev ENVIRON option. When connecting to a non-standard port, .Nm omits any automatic initiation of .Tn TELNET options. When the port number is preceded by a minus sign, the initial option negotiation is done. After establishing a connection, the file .Pa \&.telnetrc in the users home directory is opened. Lines beginning with a # are comment lines. Blank lines are ignored. Lines that begin without white space are the start of a machine entry. The first thing on the line is the name of the machine that is being connected to. The rest of the line, and successive lines that begin with white space are assumed to be .Nm commands and are processed as if they had been typed in manually to the .Nm command prompt. .It Ic quit Close any open .Tn TELNET session and exit .Nm telnet . An end of file (in command mode) will also close a session and exit. .It Ic send Ar arguments Sends one or more special character sequences to the remote host. The following are the arguments which may be specified (more than one argument may be specified at a time): .Pp .Bl -tag -width escape .It Ic abort Sends the .Dv TELNET ABORT (Abort processes) sequence. .It Ic ao Sends the .Dv TELNET AO (Abort Output) sequence, which should cause the remote system to flush all output .Em from the remote system .Em to the user's terminal. .It Ic ayt Sends the .Dv TELNET AYT (Are You There) sequence, to which the remote system may or may not choose to respond. .It Ic brk Sends the .Dv TELNET BRK (Break) sequence, which may have significance to the remote system. .It Ic ec Sends the .Dv TELNET EC (Erase Character) sequence, which should cause the remote system to erase the last character entered. .It Ic el Sends the .Dv TELNET EL (Erase Line) sequence, which should cause the remote system to erase the line currently being entered. .It Ic eof Sends the .Dv TELNET EOF (End Of File) sequence. .It Ic eor Sends the .Dv TELNET EOR (End of Record) sequence. .It Ic escape Sends the current .Nm escape character (initially \*(Lq^\*(Rq). .It Ic ga Sends the .Dv TELNET GA (Go Ahead) sequence, which likely has no significance to the remote system. .It Ic getstatus If the remote side supports the .Dv TELNET STATUS command, .Ic getstatus will send the subnegotiation to request that the server send its current option status. .It Ic ip Sends the .Dv TELNET IP (Interrupt Process) sequence, which should cause the remote system to abort the currently running process. .It Ic nop Sends the .Dv TELNET NOP (No OPeration) sequence. .It Ic susp Sends the .Dv TELNET SUSP (SUSPend process) sequence. .It Ic synch Sends the .Dv TELNET SYNCH sequence. This sequence causes the remote system to discard all previously typed (but not yet read) input. This sequence is sent as .Tn TCP urgent data (and may not work if the remote system is a .Bx 4.2 system -- if it doesn't work, a lower case \*(Lqr\*(Rq may be echoed on the terminal). .It Ic do Ar cmd .It Ic dont Ar cmd .It Ic will Ar cmd .It Ic wont Ar cmd Sends the .Dv TELNET DO .Ar cmd sequence. .Ar Cmd can be either a decimal number between 0 and 255, or a symbolic name for a specific .Dv TELNET command. .Ar Cmd can also be either .Ic help or .Ic \&? to print out help information, including a list of known symbolic names. .It Ic \&? Prints out help information for the .Ic send command. .El .It Ic set Ar argument value .It Ic unset Ar argument value The .Ic set command will set any one of a number of .Nm variables to a specific value or to .Dv TRUE . The special value .Ic off turns off the function associated with the variable, this is equivalent to using the .Ic unset command. The .Ic unset command will disable or set to .Dv FALSE any of the specified functions. The values of variables may be interrogated with the .Ic display command. The variables which may be set or unset, but not toggled, are listed here. In addition, any of the variables for the .Ic toggle command may be explicitly set or unset using the .Ic set and .Ic unset commands. .Bl -tag -width escape .It Ic ayt If .Tn TELNET is in localchars mode, or .Dv LINEMODE is enabled, and the status character is typed, a .Dv TELNET AYT sequence (see .Ic send ayt preceding) is sent to the remote host. The initial value for the \*(LqAre You There\*(Rq character is the terminal's status character. .It Ic echo This is the value (initially \*(Lq^E\*(Rq) which, when in \*(Lqline by line\*(Rq mode, toggles between doing local echoing of entered characters (for normal processing), and suppressing echoing of entered characters (for entering, say, a password). .It Ic eof If .Nm is operating in .Dv LINEMODE or \*(Lqold line by line\*(Rq mode, entering this character as the first character on a line will cause this character to be sent to the remote system. The initial value of the eof character is taken to be the terminal's .Ic eof character. .It Ic erase If .Nm is in .Ic localchars mode (see .Ic toggle .Ic localchars below), .Sy and if .Nm is operating in \*(Lqcharacter at a time\*(Rq mode, then when this character is typed, a .Dv TELNET EC sequence (see .Ic send .Ic ec above) is sent to the remote system. The initial value for the erase character is taken to be the terminal's .Ic erase character. .It Ic escape This is the .Nm escape character (initially \*(Lq^[\*(Rq) which causes entry into .Nm command mode (when connected to a remote system). .It Ic flushoutput If .Nm is in .Ic localchars mode (see .Ic toggle .Ic localchars below) and the .Ic flushoutput character is typed, a .Dv TELNET AO sequence (see .Ic send .Ic ao above) is sent to the remote host. The initial value for the flush character is taken to be the terminal's .Ic flush character. .It Ic forw1 .It Ic forw2 If .Nm is operating in .Dv LINEMODE , these are the characters that, when typed, cause partial lines to be forwarded to the remote system. The initial value for the forwarding characters are taken from the terminal's eol and eol2 characters. .It Ic interrupt If .Nm is in .Ic localchars mode (see .Ic toggle .Ic localchars below) and the .Ic interrupt character is typed, a .Dv TELNET IP sequence (see .Ic send .Ic ip above) is sent to the remote host. The initial value for the interrupt character is taken to be the terminal's .Ic intr character. .It Ic kill If .Nm is in .Ic localchars mode (see .Ic toggle .Ic localchars below), .Ic and if .Nm is operating in \*(Lqcharacter at a time\*(Rq mode, then when this character is typed, a .Dv TELNET EL sequence (see .Ic send .Ic el above) is sent to the remote system. The initial value for the kill character is taken to be the terminal's .Ic kill character. .It Ic lnext If .Nm is operating in .Dv LINEMODE or \*(Lqold line by line\*(Rq mode, then this character is taken to be the terminal's .Ic lnext character. The initial value for the lnext character is taken to be the terminal's .Ic lnext character. .It Ic quit If .Nm is in .Ic localchars mode (see .Ic toggle .Ic localchars below) and the .Ic quit character is typed, a .Dv TELNET BRK sequence (see .Ic send .Ic brk above) is sent to the remote host. The initial value for the quit character is taken to be the terminal's .Ic quit character. .It Ic reprint If .Nm is operating in .Dv LINEMODE or \*(Lqold line by line\*(Rq mode, then this character is taken to be the terminal's .Ic reprint character. The initial value for the reprint character is taken to be the terminal's .Ic reprint character. .It Ic rlogin This is the rlogin escape character. If set, the normal .Nm escape character is ignored unless it is preceded by this character at the beginning of a line. This character, at the beginning of a line followed by a "." closes the connection; when followed by a ^Z it suspends the .Nm command. The initial state is to disable the .Nm rlogin escape character. .It Ic start If the .Dv TELNET TOGGLE-FLOW-CONTROL option has been enabled, then this character is taken to be the terminal's .Ic start character. The initial value for the start character is taken to be the terminal's .Ic start character. .It Ic stop If the .Dv TELNET TOGGLE-FLOW-CONTROL option has been enabled, then this character is taken to be the terminal's .Ic stop character. The initial value for the stop character is taken to be the terminal's .Ic stop character. .It Ic susp If .Nm is in .Ic localchars mode, or .Dv LINEMODE is enabled, and the .Ic suspend character is typed, a .Dv TELNET SUSP sequence (see .Ic send .Ic susp above) is sent to the remote host. The initial value for the suspend character is taken to be the terminal's .Ic suspend character. .It Ic tracefile This is the file to which the output, caused by .Ic netdata or .Ic option tracing being .Dv TRUE , will be written. If it is set to .Dq Fl , then tracing information will be written to standard output (the default). .It Ic worderase If .Nm is operating in .Dv LINEMODE or \*(Lqold line by line\*(Rq mode, then this character is taken to be the terminal's .Ic worderase character. The initial value for the worderase character is taken to be the terminal's .Ic worderase character. .It Ic \&? Displays the legal .Ic set .Pq Ic unset commands. .El .It Ic slc Ar state The .Ic slc command (Set Local Characters) is used to set or change the state of the special characters when the .Dv TELNET LINEMODE option has been enabled. Special characters are characters that get mapped to .Tn TELNET commands sequences (like .Ic ip or .Ic quit ) or line editing characters (like .Ic erase and .Ic kill ) . By default, the local special characters are exported. .Bl -tag -width Fl .It Ic check Verify the current settings for the current special characters. The remote side is requested to send all the current special character settings, and if there are any discrepancies with the local side, the local side will switch to the remote value. .It Ic export Switch to the local defaults for the special characters. The local default characters are those of the local terminal at the time when .Nm was started. .It Ic import Switch to the remote defaults for the special characters. The remote default characters are those of the remote system at the time when the .Tn TELNET connection was established. .It Ic \&? Prints out help information for the .Ic slc command. .El .It Ic status Show the current status of .Nm telnet . This includes the peer one is connected to, as well as the current mode. .It Ic toggle Ar arguments ... Toggle (between .Dv TRUE and .Dv FALSE ) various flags that control how .Nm responds to events. These flags may be set explicitly to .Dv TRUE or .Dv FALSE using the .Ic set and .Ic unset commands listed above. More than one argument may be specified. The state of these flags may be interrogated with the .Ic display command. Valid arguments are: .Bl -tag -width Ar .It Ic authdebug Turns on debugging information for the authentication code. .It Ic autoflush If .Ic autoflush and .Ic localchars are both .Dv TRUE , then when the .Ic ao , or .Ic quit characters are recognized (and transformed into .Tn TELNET sequences; see .Ic set above for details), .Nm refuses to display any data on the user's terminal until the remote system acknowledges (via a .Dv TELNET TIMING MARK option) that it has processed those .Tn TELNET sequences. The initial value for this toggle is .Dv TRUE if the terminal user had not done an "stty noflsh", otherwise .Dv FALSE (see .Xr stty 1 ) . .It Ic autodecrypt When the .Dv TELNET ENCRYPT option is negotiated, by default the actual encryption (decryption) of the data stream does not start automatically. The autoencrypt (autodecrypt) command states that encryption of the output (input) stream should be enabled as soon as possible. .Pp Note: Because of export controls, the .Dv TELNET ENCRYPT option is not supported outside the United States and Canada. .It Ic autologin If the remote side supports the .Dv TELNET AUTHENTICATION option .Nm attempts to use it to perform automatic authentication. If the .Dv AUTHENTICATION option is not supported, the user's login name are propagated through the .Dv TELNET ENVIRON option. This command is the same as specifying .Fl a option on the .Ic open command. .It Ic autosynch If .Ic autosynch and .Ic localchars are both .Dv TRUE , then when either the .Ic intr or .Ic quit characters is typed (see .Ic set above for descriptions of the .Ic intr and .Ic quit characters), the resulting .Tn TELNET sequence sent is followed by the .Dv TELNET SYNCH sequence. This procedure .Ic should cause the remote system to begin throwing away all previously typed input until both of the .Tn TELNET sequences have been read and acted upon. The initial value of this toggle is .Dv FALSE . .It Ic binary Enable or disable the .Dv TELNET BINARY option on both input and output. .It Ic inbinary Enable or disable the .Dv TELNET BINARY option on input. .It Ic outbinary Enable or disable the .Dv TELNET BINARY option on output. .It Ic crlf If this is .Dv TRUE , then carriage returns will be sent as .Li . If this is .Dv FALSE , then carriage returns will be send as .Li . The initial value for this toggle is .Dv FALSE . .It Ic crmod Toggle carriage return mode. When this mode is enabled, most carriage return characters received from the remote host will be mapped into a carriage return followed by a line feed. This mode does not affect those characters typed by the user, only those received from the remote host. This mode is not very useful unless the remote host only sends carriage return, but never line feed. The initial value for this toggle is .Dv FALSE . .It Ic debug Toggles socket level debugging (useful only to the .Ic super user ) . The initial value for this toggle is .Dv FALSE . .It Ic encdebug Turns on debugging information for the encryption code. .It Ic localchars If this is .Dv TRUE , then the .Ic flush , .Ic interrupt , .Ic quit , .Ic erase , and .Ic kill characters (see .Ic set above) are recognized locally, and transformed into (hopefully) appropriate .Tn TELNET control sequences (respectively .Ic ao , .Ic ip , .Ic brk , .Ic ec , and .Ic el ; see .Ic send above). The initial value for this toggle is .Dv TRUE in \*(Lqold line by line\*(Rq mode, and .Dv FALSE in \*(Lqcharacter at a time\*(Rq mode. When the .Dv LINEMODE option is enabled, the value of .Ic localchars is ignored, and assumed to always be .Dv TRUE . If .Dv LINEMODE has ever been enabled, then .Ic quit is sent as .Ic abort , and .Ic eof and .Ic suspend are sent as .Ic eof and .Ic susp (see .Ic send above). .It Ic netdata Toggles the display of all network data (in hexadecimal format). The initial value for this toggle is .Dv FALSE . .It Ic options Toggles the display of some internal .Nm protocol processing (having to do with .Tn TELNET options). The initial value for this toggle is .Dv FALSE . .It Ic prettydump When the .Ic netdata toggle is enabled, if .Ic prettydump is enabled the output from the .Ic netdata command will be formatted in a more user readable format. Spaces are put between each character in the output, and the beginning of any .Nm escape sequence is preceded by a '*' to aid in locating them. .It Ic skiprc When the skiprc toggle is .Dv TRUE , .Nm skips the reading of the .Pa \&.telnetrc file in the users home directory when connections are opened. The initial value for this toggle is .Dv FALSE. .It Ic termdata Toggles the display of all terminal data (in hexadecimal format). The initial value for this toggle is .Dv FALSE . .It Ic verbose_encrypt When the .Ic verbose_encrypt toggle is .Dv TRUE , .Nm prints out a message each time encryption is enabled or disabled. The initial value for this toggle is .Dv FALSE. Note: Because of export controls, data encryption is not supported outside of the United States and Canada. .It Ic \&? Displays the legal .Ic toggle commands. .El .It Ic z Suspend .Nm telnet . This command only works when the user is using the .Xr csh 1 . .It Ic \&! Op Ar command Execute a single command in a subshell on the local system. If .Ar command is omitted, then an interactive subshell is invoked. .It Ic \&? Op Ar command Get help. With no arguments, .Nm prints a help summary. If .Ar command is specified, .Nm will print the help information for just that command. .El .Sh ENVIRONMENT .Nm uses at least the .Ev HOME , .Ev SHELL , .Ev DISPLAY , and .Ev TERM environment variables. Other environment variables may be propagated to the other side via the .Dv TELNET ENVIRON option. .Sh SEE ALSO .Xr rlogin 1 , .Xr rsh 1 , .Xr hosts 5 , .Xr nologin 5 , .Xr telnetd 8 .Sh FILES .Bl -tag -width ~/.telnetrc -compact .It Pa ~/.telnetrc user customized telnet startup values .El .Sh HISTORY The .Nm command appeared in .Bx 4.2 . .Sh NOTES .Pp On some remote systems, echo has to be turned off manually when in \*(Lqold line by line\*(Rq mode. .Pp In \*(Lqold line by line\*(Rq mode or .Dv LINEMODE the terminal's .Ic eof character is only recognized (and sent to the remote system) when it is the first character on a line. diff --git a/usr.bin/telnet/telnet.c b/usr.bin/telnet/telnet.c index 5057969c6f24..da8b0e113bf1 100644 --- a/usr.bin/telnet/telnet.c +++ b/usr.bin/telnet/telnet.c @@ -1,2561 +1,2562 @@ /* * Copyright (c) 1988, 1990, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static char sccsid[] = "@(#)telnet.c 8.2 (Berkeley) 12/15/93"; #endif /* not lint */ #include #if defined(unix) #include /* By the way, we need to include curses.h before telnet.h since, * among other things, telnet.h #defines 'DO', which is a variable * declared in curses.h. */ #endif /* defined(unix) */ #include #include #include #include "ring.h" #include "defines.h" #include "externs.h" #include "types.h" #include "general.h" #define strip(x) ((my_want_state_is_wont(TELOPT_BINARY)) ? ((x)&0x7f) : (x)) static unsigned char subbuffer[SUBBUFSIZE], *subpointer, *subend; /* buffer for sub-options */ #define SB_CLEAR() subpointer = subbuffer; #define SB_TERM() { subend = subpointer; SB_CLEAR(); } #define SB_ACCUM(c) if (subpointer < (subbuffer+sizeof subbuffer)) { \ *subpointer++ = (c); \ } #define SB_GET() ((*subpointer++)&0xff) #define SB_PEEK() ((*subpointer)&0xff) #define SB_EOF() (subpointer >= subend) #define SB_LEN() (subend - subpointer) char options[256]; /* The combined options */ char do_dont_resp[256]; char will_wont_resp[256]; int eight = 0, autologin = 0, /* Autologin anyone? */ skiprc = 0, connected, showoptions, In3270, /* Are we in 3270 mode? */ ISend, /* trying to send network data in */ debug = 0, crmod, netdata, /* Print out network data flow */ crlf, /* Should '\r' be mapped to (or )? */ #if defined(TN3270) noasynchtty = 0,/* User specified "-noasynch" on command line */ noasynchnet = 0,/* User specified "-noasynch" on command line */ askedSGA = 0, /* We have talked about suppress go ahead */ #endif /* defined(TN3270) */ telnetport, SYNCHing, /* we are in TELNET SYNCH mode */ flushout, /* flush output */ autoflush = 0, /* flush output when interrupting? */ autosynch, /* send interrupt characters with SYNCH? */ localflow, /* we handle flow control locally */ restartany, /* if flow control enabled, restart on any character */ localchars, /* we recognize interrupt/quit */ donelclchars, /* the user has set "localchars" */ donebinarytoggle, /* the user has put us in binary */ dontlecho, /* do we suppress local echoing right now? */ globalmode, + doaddrlookup = 1, /* do a reverse address lookup? */ clienteof = 0; char *prompt = 0; cc_t escape; cc_t rlogin; #ifdef KLUDGELINEMODE cc_t echoc; #endif /* * Telnet receiver states for fsm */ #define TS_DATA 0 #define TS_IAC 1 #define TS_WILL 2 #define TS_WONT 3 #define TS_DO 4 #define TS_DONT 5 #define TS_CR 6 #define TS_SB 7 /* sub-option collection */ #define TS_SE 8 /* looking for sub-option end */ static int telrcv_state; #ifdef OLD_ENVIRON unsigned char telopt_environ = TELOPT_NEW_ENVIRON; #else # define telopt_environ TELOPT_NEW_ENVIRON #endif jmp_buf toplevel = { 0 }; jmp_buf peerdied; int flushline; int linemode; #ifdef KLUDGELINEMODE int kludgelinemode = 1; #endif /* * The following are some clocks used to decide how to interpret * the relationship between various variables. */ Clocks clocks; #ifdef notdef Modelist modelist[] = { { "telnet command mode", COMMAND_LINE }, { "character-at-a-time mode", 0 }, { "character-at-a-time mode (local echo)", LOCAL_ECHO|LOCAL_CHARS }, { "line-by-line mode (remote echo)", LINE | LOCAL_CHARS }, { "line-by-line mode", LINE | LOCAL_ECHO | LOCAL_CHARS }, { "line-by-line mode (local echoing suppressed)", LINE | LOCAL_CHARS }, { "3270 mode", 0 }, }; #endif /* * Initialize telnet environment. */ void init_telnet() { env_init(); SB_CLEAR(); ClearArray(options); connected = In3270 = ISend = localflow = donebinarytoggle = 0; #if defined(AUTHENTICATION) auth_encrypt_connect(connected); #endif /* defined(AUTHENTICATION) */ restartany = -1; SYNCHing = 0; /* Don't change NetTrace */ escape = CONTROL(']'); rlogin = _POSIX_VDISABLE; #ifdef KLUDGELINEMODE echoc = CONTROL('E'); #endif flushline = 1; telrcv_state = TS_DATA; } #ifdef notdef #include /*VARARGS*/ static void printring(va_alist) va_dcl { va_list ap; char buffer[100]; /* where things go */ char *ptr; char *format; char *string; Ring *ring; int i; va_start(ap); ring = va_arg(ap, Ring *); format = va_arg(ap, char *); ptr = buffer; while ((i = *format++) != 0) { if (i == '%') { i = *format++; switch (i) { case 'c': *ptr++ = va_arg(ap, int); break; case 's': string = va_arg(ap, char *); ring_supply_data(ring, buffer, ptr-buffer); ring_supply_data(ring, string, strlen(string)); ptr = buffer; break; case 0: ExitString("printring: trailing %%.\n", 1); /*NOTREACHED*/ default: ExitString("printring: unknown format character.\n", 1); /*NOTREACHED*/ } } else { *ptr++ = i; } } ring_supply_data(ring, buffer, ptr-buffer); } #endif /* * These routines are in charge of sending option negotiations * to the other side. * * The basic idea is that we send the negotiation if either side * is in disagreement as to what the current state should be. */ void send_do(c, init) register int c, init; { if (init) { if (((do_dont_resp[c] == 0) && my_state_is_do(c)) || my_want_state_is_do(c)) return; set_my_want_state_do(c); do_dont_resp[c]++; } NET2ADD(IAC, DO); NETADD(c); printoption("SENT", DO, c); } void send_dont(c, init) register int c, init; { if (init) { if (((do_dont_resp[c] == 0) && my_state_is_dont(c)) || my_want_state_is_dont(c)) return; set_my_want_state_dont(c); do_dont_resp[c]++; } NET2ADD(IAC, DONT); NETADD(c); printoption("SENT", DONT, c); } void send_will(c, init) register int c, init; { if (init) { if (((will_wont_resp[c] == 0) && my_state_is_will(c)) || my_want_state_is_will(c)) return; set_my_want_state_will(c); will_wont_resp[c]++; } NET2ADD(IAC, WILL); NETADD(c); printoption("SENT", WILL, c); } void send_wont(c, init) register int c, init; { if (init) { if (((will_wont_resp[c] == 0) && my_state_is_wont(c)) || my_want_state_is_wont(c)) return; set_my_want_state_wont(c); will_wont_resp[c]++; } NET2ADD(IAC, WONT); NETADD(c); printoption("SENT", WONT, c); } void willoption(option) int option; { int new_state_ok = 0; if (do_dont_resp[option]) { --do_dont_resp[option]; if (do_dont_resp[option] && my_state_is_do(option)) --do_dont_resp[option]; } if ((do_dont_resp[option] == 0) && my_want_state_is_dont(option)) { switch (option) { case TELOPT_ECHO: # if defined(TN3270) /* * The following is a pain in the rear-end. * Various IBM servers (some versions of Wiscnet, * possibly Fibronics/Spartacus, and who knows who * else) will NOT allow us to send "DO SGA" too early * in the setup proceedings. On the other hand, * 4.2 servers (telnetd) won't set SGA correctly. * So, we are stuck. Empirically (but, based on * a VERY small sample), the IBM servers don't send * out anything about ECHO, so we postpone our sending * "DO SGA" until we see "WILL ECHO" (which 4.2 servers * DO send). */ { if (askedSGA == 0) { askedSGA = 1; if (my_want_state_is_dont(TELOPT_SGA)) send_do(TELOPT_SGA, 1); } } /* Fall through */ case TELOPT_EOR: #endif /* defined(TN3270) */ case TELOPT_BINARY: case TELOPT_SGA: settimer(modenegotiated); /* FALL THROUGH */ case TELOPT_STATUS: #if defined(AUTHENTICATION) case TELOPT_AUTHENTICATION: #endif new_state_ok = 1; break; case TELOPT_TM: if (flushout) flushout = 0; /* * Special case for TM. If we get back a WILL, * pretend we got back a WONT. */ set_my_want_state_dont(option); set_my_state_dont(option); return; /* Never reply to TM will's/wont's */ case TELOPT_LINEMODE: default: break; } if (new_state_ok) { set_my_want_state_do(option); send_do(option, 0); setconnmode(0); /* possibly set new tty mode */ } else { do_dont_resp[option]++; send_dont(option, 0); } } set_my_state_do(option); } void wontoption(option) int option; { if (do_dont_resp[option]) { --do_dont_resp[option]; if (do_dont_resp[option] && my_state_is_dont(option)) --do_dont_resp[option]; } if ((do_dont_resp[option] == 0) && my_want_state_is_do(option)) { switch (option) { #ifdef KLUDGELINEMODE case TELOPT_SGA: if (!kludgelinemode) break; /* FALL THROUGH */ #endif case TELOPT_ECHO: settimer(modenegotiated); break; case TELOPT_TM: if (flushout) flushout = 0; set_my_want_state_dont(option); set_my_state_dont(option); return; /* Never reply to TM will's/wont's */ default: break; } set_my_want_state_dont(option); if (my_state_is_do(option)) send_dont(option, 0); setconnmode(0); /* Set new tty mode */ } else if (option == TELOPT_TM) { /* * Special case for TM. */ if (flushout) flushout = 0; set_my_want_state_dont(option); } set_my_state_dont(option); } static void dooption(option) int option; { int new_state_ok = 0; if (will_wont_resp[option]) { --will_wont_resp[option]; if (will_wont_resp[option] && my_state_is_will(option)) --will_wont_resp[option]; } if (will_wont_resp[option] == 0) { if (my_want_state_is_wont(option)) { switch (option) { case TELOPT_TM: /* * Special case for TM. We send a WILL, but pretend * we sent WONT. */ send_will(option, 0); set_my_want_state_wont(TELOPT_TM); set_my_state_wont(TELOPT_TM); return; # if defined(TN3270) case TELOPT_EOR: /* end of record */ # endif /* defined(TN3270) */ case TELOPT_BINARY: /* binary mode */ case TELOPT_NAWS: /* window size */ case TELOPT_TSPEED: /* terminal speed */ case TELOPT_LFLOW: /* local flow control */ case TELOPT_TTYPE: /* terminal type option */ case TELOPT_SGA: /* no big deal */ new_state_ok = 1; break; case TELOPT_NEW_ENVIRON: /* New environment variable option */ #ifdef OLD_ENVIRON if (my_state_is_will(TELOPT_OLD_ENVIRON)) send_wont(TELOPT_OLD_ENVIRON, 1); /* turn off the old */ goto env_common; case TELOPT_OLD_ENVIRON: /* Old environment variable option */ if (my_state_is_will(TELOPT_NEW_ENVIRON)) break; /* Don't enable if new one is in use! */ env_common: telopt_environ = option; #endif new_state_ok = 1; break; #if defined(AUTHENTICATION) case TELOPT_AUTHENTICATION: if (autologin) new_state_ok = 1; break; #endif case TELOPT_XDISPLOC: /* X Display location */ if (env_getvalue((unsigned char *)"DISPLAY")) new_state_ok = 1; break; case TELOPT_LINEMODE: #ifdef KLUDGELINEMODE kludgelinemode = 0; send_do(TELOPT_SGA, 1); #endif set_my_want_state_will(TELOPT_LINEMODE); send_will(option, 0); set_my_state_will(TELOPT_LINEMODE); slc_init(); return; case TELOPT_ECHO: /* We're never going to echo... */ default: break; } if (new_state_ok) { set_my_want_state_will(option); send_will(option, 0); setconnmode(0); /* Set new tty mode */ } else { will_wont_resp[option]++; send_wont(option, 0); } } else { /* * Handle options that need more things done after the * other side has acknowledged the option. */ switch (option) { case TELOPT_LINEMODE: #ifdef KLUDGELINEMODE kludgelinemode = 0; send_do(TELOPT_SGA, 1); #endif set_my_state_will(option); slc_init(); send_do(TELOPT_SGA, 0); return; } } } set_my_state_will(option); } static void dontoption(option) int option; { if (will_wont_resp[option]) { --will_wont_resp[option]; if (will_wont_resp[option] && my_state_is_wont(option)) --will_wont_resp[option]; } if ((will_wont_resp[option] == 0) && my_want_state_is_will(option)) { switch (option) { case TELOPT_LINEMODE: linemode = 0; /* put us back to the default state */ break; #ifdef OLD_ENVIRON case TELOPT_NEW_ENVIRON: /* * The new environ option wasn't recognized, try * the old one. */ send_will(TELOPT_OLD_ENVIRON, 1); telopt_environ = TELOPT_OLD_ENVIRON; break; #endif } /* we always accept a DONT */ set_my_want_state_wont(option); if (my_state_is_will(option)) send_wont(option, 0); setconnmode(0); /* Set new tty mode */ } set_my_state_wont(option); } /* * Given a buffer returned by tgetent(), this routine will turn * the pipe seperated list of names in the buffer into an array * of pointers to null terminated names. We toss out any bad, * duplicate, or verbose names (names with spaces). */ static char *name_unknown = "UNKNOWN"; static char *unknown[] = { 0, 0 }; char ** mklist(buf, name) char *buf, *name; { register int n; register char c, *cp, **argvp, *cp2, **argv, **avt; if (name) { if (strlen(name) > 40) { name = 0; unknown[0] = name_unknown; } else { unknown[0] = name; upcase(name); } } else unknown[0] = name_unknown; /* * Count up the number of names. */ for (n = 1, cp = buf; *cp && *cp != ':'; cp++) { if (*cp == '|') n++; } /* * Allocate an array to put the name pointers into */ argv = (char **)malloc((n+3)*sizeof(char *)); if (argv == 0) return(unknown); /* * Fill up the array of pointers to names. */ *argv = 0; argvp = argv+1; n = 0; for (cp = cp2 = buf; (c = *cp); cp++) { if (c == '|' || c == ':') { *cp++ = '\0'; /* * Skip entries that have spaces or are over 40 * characters long. If this is our environment * name, then put it up front. Otherwise, as * long as this is not a duplicate name (case * insensitive) add it to the list. */ if (n || (cp - cp2 > 41)) ; else if (name && (strncasecmp(name, cp2, cp-cp2) == 0)) *argv = cp2; else if (is_unique(cp2, argv+1, argvp)) *argvp++ = cp2; if (c == ':') break; /* * Skip multiple delimiters. Reset cp2 to * the beginning of the next name. Reset n, * the flag for names with spaces. */ while ((c = *cp) == '|') cp++; cp2 = cp; n = 0; } /* * Skip entries with spaces or non-ascii values. * Convert lower case letters to upper case. */ if ((c == ' ') || !isascii(c)) n = 1; else if (islower(c)) *cp = toupper(c); } /* * Check for an old V6 2 character name. If the second * name points to the beginning of the buffer, and is * only 2 characters long, move it to the end of the array. */ if ((argv[1] == buf) && (strlen(argv[1]) == 2)) { --argvp; for (avt = &argv[1]; avt < argvp; avt++) *avt = *(avt+1); *argvp++ = buf; } /* * Duplicate last name, for TTYPE option, and null * terminate the array. If we didn't find a match on * our terminal name, put that name at the beginning. */ cp = *(argvp-1); *argvp++ = cp; *argvp = 0; if (*argv == 0) { if (name) *argv = name; else { --argvp; for (avt = argv; avt < argvp; avt++) *avt = *(avt+1); } } if (*argv) return(argv); else return(unknown); } int is_unique(name, as, ae) register char *name, **as, **ae; { register char **ap; register int n; n = strlen(name) + 1; for (ap = as; ap < ae; ap++) if (strncasecmp(*ap, name, n) == 0) return(0); return (1); } #ifdef TERMCAP char termbuf[1024]; /*ARGSUSED*/ int setupterm(tname, fd, errp) char *tname; int fd, *errp; { if (tgetent(termbuf, tname) == 1) { termbuf[1023] = '\0'; if (errp) *errp = 1; return(0); } if (errp) *errp = 0; return(-1); } #else #define termbuf ttytype extern char ttytype[]; #endif int resettermname = 1; char * gettermname() { char *tname; static char **tnamep = 0; static char **next; int err; if (resettermname) { resettermname = 0; if (tnamep && tnamep != unknown) free(tnamep); if ((tname = (char *)env_getvalue((unsigned char *)"TERM")) && (setupterm(tname, 1, &err) == 0)) { tnamep = mklist(termbuf, tname); } else { if (tname && (strlen(tname) <= 40)) { unknown[0] = tname; upcase(tname); } else unknown[0] = name_unknown; tnamep = unknown; } next = tnamep; } if (*next == 0) next = tnamep; return(*next++); } /* * suboption() * * Look at the sub-option buffer, and try to be helpful to the other * side. * * Currently we recognize: * * Terminal type, send request. * Terminal speed (send request). * Local flow control (is request). * Linemode */ static void suboption() { unsigned char subchar; printsub('<', subbuffer, SB_LEN()+2); switch (subchar = SB_GET()) { case TELOPT_TTYPE: if (my_want_state_is_wont(TELOPT_TTYPE)) return; if (SB_EOF() || SB_GET() != TELQUAL_SEND) { return; } else { char *name; unsigned char temp[50]; int len; #if defined(TN3270) if (tn3270_ttype()) { return; } #endif /* defined(TN3270) */ name = gettermname(); len = strlen(name) + 4 + 2; if (len < NETROOM()) { sprintf((char *)temp, "%c%c%c%c%s%c%c", IAC, SB, TELOPT_TTYPE, TELQUAL_IS, name, IAC, SE); ring_supply_data(&netoring, temp, len); printsub('>', &temp[2], len-2); } else { ExitString("No room in buffer for terminal type.\n", 1); /*NOTREACHED*/ } } break; case TELOPT_TSPEED: if (my_want_state_is_wont(TELOPT_TSPEED)) return; if (SB_EOF()) return; if (SB_GET() == TELQUAL_SEND) { long ospeed, ispeed; unsigned char temp[50]; int len; TerminalSpeeds(&ispeed, &ospeed); sprintf((char *)temp, "%c%c%c%c%ld,%ld%c%c", IAC, SB, TELOPT_TSPEED, TELQUAL_IS, ospeed, ispeed, IAC, SE); len = strlen((char *)temp+4) + 4; /* temp[3] is 0 ... */ if (len < NETROOM()) { ring_supply_data(&netoring, temp, len); printsub('>', temp+2, len - 2); } /*@*/ else printf("lm_will: not enough room in buffer\n"); } break; case TELOPT_LFLOW: if (my_want_state_is_wont(TELOPT_LFLOW)) return; if (SB_EOF()) return; switch(SB_GET()) { case LFLOW_RESTART_ANY: restartany = 1; break; case LFLOW_RESTART_XON: restartany = 0; break; case LFLOW_ON: localflow = 1; break; case LFLOW_OFF: localflow = 0; break; default: return; } setcommandmode(); setconnmode(0); break; case TELOPT_LINEMODE: if (my_want_state_is_wont(TELOPT_LINEMODE)) return; if (SB_EOF()) return; switch (SB_GET()) { case WILL: lm_will(subpointer, SB_LEN()); break; case WONT: lm_wont(subpointer, SB_LEN()); break; case DO: lm_do(subpointer, SB_LEN()); break; case DONT: lm_dont(subpointer, SB_LEN()); break; case LM_SLC: slc(subpointer, SB_LEN()); break; case LM_MODE: lm_mode(subpointer, SB_LEN(), 0); break; default: break; } break; #ifdef OLD_ENVIRON case TELOPT_OLD_ENVIRON: #endif case TELOPT_NEW_ENVIRON: if (SB_EOF()) return; switch(SB_PEEK()) { case TELQUAL_IS: case TELQUAL_INFO: if (my_want_state_is_dont(subchar)) return; break; case TELQUAL_SEND: if (my_want_state_is_wont(subchar)) { return; } break; default: return; } env_opt(subpointer, SB_LEN()); break; case TELOPT_XDISPLOC: if (my_want_state_is_wont(TELOPT_XDISPLOC)) return; if (SB_EOF()) return; if (SB_GET() == TELQUAL_SEND) { unsigned char temp[50], *dp; int len; if ((dp = env_getvalue((unsigned char *)"DISPLAY")) == NULL) { /* * Something happened, we no longer have a DISPLAY * variable. So, turn off the option. */ send_wont(TELOPT_XDISPLOC, 1); break; } sprintf((char *)temp, "%c%c%c%c%s%c%c", IAC, SB, TELOPT_XDISPLOC, TELQUAL_IS, dp, IAC, SE); len = strlen((char *)temp+4) + 4; /* temp[3] is 0 ... */ if (len < NETROOM()) { ring_supply_data(&netoring, temp, len); printsub('>', temp+2, len - 2); } /*@*/ else printf("lm_will: not enough room in buffer\n"); } break; #if defined(AUTHENTICATION) case TELOPT_AUTHENTICATION: { if (!autologin) break; if (SB_EOF()) return; switch(SB_GET()) { case TELQUAL_IS: if (my_want_state_is_dont(TELOPT_AUTHENTICATION)) return; auth_is(subpointer, SB_LEN()); break; case TELQUAL_SEND: if (my_want_state_is_wont(TELOPT_AUTHENTICATION)) return; auth_send(subpointer, SB_LEN()); break; case TELQUAL_REPLY: if (my_want_state_is_wont(TELOPT_AUTHENTICATION)) return; auth_reply(subpointer, SB_LEN()); break; case TELQUAL_NAME: if (my_want_state_is_dont(TELOPT_AUTHENTICATION)) return; auth_name(subpointer, SB_LEN()); break; } } break; #endif default: break; } } static unsigned char str_lm[] = { IAC, SB, TELOPT_LINEMODE, 0, 0, IAC, SE }; void lm_will(cmd, len) unsigned char *cmd; int len; { if (len < 1) { /*@*/ printf("lm_will: no command!!!\n"); /* Should not happen... */ return; } switch(cmd[0]) { case LM_FORWARDMASK: /* We shouldn't ever get this... */ default: str_lm[3] = DONT; str_lm[4] = cmd[0]; if (NETROOM() > sizeof(str_lm)) { ring_supply_data(&netoring, str_lm, sizeof(str_lm)); printsub('>', &str_lm[2], sizeof(str_lm)-2); } /*@*/ else printf("lm_will: not enough room in buffer\n"); break; } } void lm_wont(cmd, len) unsigned char *cmd; int len; { if (len < 1) { /*@*/ printf("lm_wont: no command!!!\n"); /* Should not happen... */ return; } switch(cmd[0]) { case LM_FORWARDMASK: /* We shouldn't ever get this... */ default: /* We are always DONT, so don't respond */ return; } } void lm_do(cmd, len) unsigned char *cmd; int len; { if (len < 1) { /*@*/ printf("lm_do: no command!!!\n"); /* Should not happen... */ return; } switch(cmd[0]) { case LM_FORWARDMASK: default: str_lm[3] = WONT; str_lm[4] = cmd[0]; if (NETROOM() > sizeof(str_lm)) { ring_supply_data(&netoring, str_lm, sizeof(str_lm)); printsub('>', &str_lm[2], sizeof(str_lm)-2); } /*@*/ else printf("lm_do: not enough room in buffer\n"); break; } } void lm_dont(cmd, len) unsigned char *cmd; int len; { if (len < 1) { /*@*/ printf("lm_dont: no command!!!\n"); /* Should not happen... */ return; } switch(cmd[0]) { case LM_FORWARDMASK: default: /* we are always WONT, so don't respond */ break; } } static unsigned char str_lm_mode[] = { IAC, SB, TELOPT_LINEMODE, LM_MODE, 0, IAC, SE }; void lm_mode(cmd, len, init) unsigned char *cmd; int len, init; { if (len != 1) return; if ((linemode&MODE_MASK&~MODE_ACK) == *cmd) return; if (*cmd&MODE_ACK) return; linemode = *cmd&(MODE_MASK&~MODE_ACK); str_lm_mode[4] = linemode; if (!init) str_lm_mode[4] |= MODE_ACK; if (NETROOM() > sizeof(str_lm_mode)) { ring_supply_data(&netoring, str_lm_mode, sizeof(str_lm_mode)); printsub('>', &str_lm_mode[2], sizeof(str_lm_mode)-2); } /*@*/ else printf("lm_mode: not enough room in buffer\n"); setconnmode(0); /* set changed mode */ } /* * slc() * Handle special character suboption of LINEMODE. */ struct spc { cc_t val; cc_t *valp; char flags; /* Current flags & level */ char mylevel; /* Maximum level & flags */ } spc_data[NSLC+1]; #define SLC_IMPORT 0 #define SLC_EXPORT 1 #define SLC_RVALUE 2 static int slc_mode = SLC_EXPORT; void slc_init() { register struct spc *spcp; localchars = 1; for (spcp = spc_data; spcp < &spc_data[NSLC+1]; spcp++) { spcp->val = 0; spcp->valp = 0; spcp->flags = spcp->mylevel = SLC_NOSUPPORT; } #define initfunc(func, flags) { \ spcp = &spc_data[func]; \ if (spcp->valp = tcval(func)) { \ spcp->val = *spcp->valp; \ spcp->mylevel = SLC_VARIABLE|flags; \ } else { \ spcp->val = 0; \ spcp->mylevel = SLC_DEFAULT; \ } \ } initfunc(SLC_SYNCH, 0); /* No BRK */ initfunc(SLC_AO, 0); initfunc(SLC_AYT, 0); /* No EOR */ initfunc(SLC_ABORT, SLC_FLUSHIN|SLC_FLUSHOUT); initfunc(SLC_EOF, 0); #ifndef SYSV_TERMIO initfunc(SLC_SUSP, SLC_FLUSHIN); #endif initfunc(SLC_EC, 0); initfunc(SLC_EL, 0); #ifndef SYSV_TERMIO initfunc(SLC_EW, 0); initfunc(SLC_RP, 0); initfunc(SLC_LNEXT, 0); #endif initfunc(SLC_XON, 0); initfunc(SLC_XOFF, 0); #ifdef SYSV_TERMIO spc_data[SLC_XON].mylevel = SLC_CANTCHANGE; spc_data[SLC_XOFF].mylevel = SLC_CANTCHANGE; #endif initfunc(SLC_FORW1, 0); #ifdef USE_TERMIO initfunc(SLC_FORW2, 0); /* No FORW2 */ #endif initfunc(SLC_IP, SLC_FLUSHIN|SLC_FLUSHOUT); #undef initfunc if (slc_mode == SLC_EXPORT) slc_export(); else slc_import(1); } void slcstate() { printf("Special characters are %s values\n", slc_mode == SLC_IMPORT ? "remote default" : slc_mode == SLC_EXPORT ? "local" : "remote"); } void slc_mode_export() { slc_mode = SLC_EXPORT; if (my_state_is_will(TELOPT_LINEMODE)) slc_export(); } void slc_mode_import(def) int def; { slc_mode = def ? SLC_IMPORT : SLC_RVALUE; if (my_state_is_will(TELOPT_LINEMODE)) slc_import(def); } unsigned char slc_import_val[] = { IAC, SB, TELOPT_LINEMODE, LM_SLC, 0, SLC_VARIABLE, 0, IAC, SE }; unsigned char slc_import_def[] = { IAC, SB, TELOPT_LINEMODE, LM_SLC, 0, SLC_DEFAULT, 0, IAC, SE }; void slc_import(def) int def; { if (NETROOM() > sizeof(slc_import_val)) { if (def) { ring_supply_data(&netoring, slc_import_def, sizeof(slc_import_def)); printsub('>', &slc_import_def[2], sizeof(slc_import_def)-2); } else { ring_supply_data(&netoring, slc_import_val, sizeof(slc_import_val)); printsub('>', &slc_import_val[2], sizeof(slc_import_val)-2); } } /*@*/ else printf("slc_import: not enough room\n"); } void slc_export() { register struct spc *spcp; TerminalDefaultChars(); slc_start_reply(); for (spcp = &spc_data[1]; spcp < &spc_data[NSLC+1]; spcp++) { if (spcp->mylevel != SLC_NOSUPPORT) { if (spcp->val == (cc_t)(_POSIX_VDISABLE)) spcp->flags = SLC_NOSUPPORT; else spcp->flags = spcp->mylevel; if (spcp->valp) spcp->val = *spcp->valp; slc_add_reply(spcp - spc_data, spcp->flags, spcp->val); } } slc_end_reply(); (void)slc_update(); setconnmode(1); /* Make sure the character values are set */ } void slc(cp, len) register unsigned char *cp; int len; { register struct spc *spcp; register int func,level; slc_start_reply(); for (; len >= 3; len -=3, cp +=3) { func = cp[SLC_FUNC]; if (func == 0) { /* * Client side: always ignore 0 function. */ continue; } if (func > NSLC) { if ((cp[SLC_FLAGS] & SLC_LEVELBITS) != SLC_NOSUPPORT) slc_add_reply(func, SLC_NOSUPPORT, 0); continue; } spcp = &spc_data[func]; level = cp[SLC_FLAGS]&(SLC_LEVELBITS|SLC_ACK); if ((cp[SLC_VALUE] == (unsigned char)spcp->val) && ((level&SLC_LEVELBITS) == (spcp->flags&SLC_LEVELBITS))) { continue; } if (level == (SLC_DEFAULT|SLC_ACK)) { /* * This is an error condition, the SLC_ACK * bit should never be set for the SLC_DEFAULT * level. Our best guess to recover is to * ignore the SLC_ACK bit. */ cp[SLC_FLAGS] &= ~SLC_ACK; } if (level == ((spcp->flags&SLC_LEVELBITS)|SLC_ACK)) { spcp->val = (cc_t)cp[SLC_VALUE]; spcp->flags = cp[SLC_FLAGS]; /* include SLC_ACK */ continue; } level &= ~SLC_ACK; if (level <= (spcp->mylevel&SLC_LEVELBITS)) { spcp->flags = cp[SLC_FLAGS]|SLC_ACK; spcp->val = (cc_t)cp[SLC_VALUE]; } if (level == SLC_DEFAULT) { if ((spcp->mylevel&SLC_LEVELBITS) != SLC_DEFAULT) spcp->flags = spcp->mylevel; else spcp->flags = SLC_NOSUPPORT; } slc_add_reply(func, spcp->flags, spcp->val); } slc_end_reply(); if (slc_update()) setconnmode(1); /* set the new character values */ } void slc_check() { register struct spc *spcp; slc_start_reply(); for (spcp = &spc_data[1]; spcp < &spc_data[NSLC+1]; spcp++) { if (spcp->valp && spcp->val != *spcp->valp) { spcp->val = *spcp->valp; if (spcp->val == (cc_t)(_POSIX_VDISABLE)) spcp->flags = SLC_NOSUPPORT; else spcp->flags = spcp->mylevel; slc_add_reply(spcp - spc_data, spcp->flags, spcp->val); } } slc_end_reply(); setconnmode(1); } unsigned char slc_reply[128]; unsigned char *slc_replyp; void slc_start_reply() { slc_replyp = slc_reply; *slc_replyp++ = IAC; *slc_replyp++ = SB; *slc_replyp++ = TELOPT_LINEMODE; *slc_replyp++ = LM_SLC; } void slc_add_reply(func, flags, value) unsigned char func; unsigned char flags; cc_t value; { if ((*slc_replyp++ = func) == IAC) *slc_replyp++ = IAC; if ((*slc_replyp++ = flags) == IAC) *slc_replyp++ = IAC; if ((*slc_replyp++ = (unsigned char)value) == IAC) *slc_replyp++ = IAC; } void slc_end_reply() { register int len; *slc_replyp++ = IAC; *slc_replyp++ = SE; len = slc_replyp - slc_reply; if (len <= 6) return; if (NETROOM() > len) { ring_supply_data(&netoring, slc_reply, slc_replyp - slc_reply); printsub('>', &slc_reply[2], slc_replyp - slc_reply - 2); } /*@*/else printf("slc_end_reply: not enough room\n"); } int slc_update() { register struct spc *spcp; int need_update = 0; for (spcp = &spc_data[1]; spcp < &spc_data[NSLC+1]; spcp++) { if (!(spcp->flags&SLC_ACK)) continue; spcp->flags &= ~SLC_ACK; if (spcp->valp && (*spcp->valp != spcp->val)) { *spcp->valp = spcp->val; need_update = 1; } } return(need_update); } #ifdef OLD_ENVIRON # ifdef ENV_HACK /* * Earlier version of telnet/telnetd from the BSD code had * the definitions of VALUE and VAR reversed. To ensure * maximum interoperability, we assume that the server is * an older BSD server, until proven otherwise. The newer * BSD servers should be able to handle either definition, * so it is better to use the wrong values if we don't * know what type of server it is. */ int env_auto = 1; int old_env_var = OLD_ENV_VAR; int old_env_value = OLD_ENV_VALUE; # else # define old_env_var OLD_ENV_VAR # define old_env_value OLD_ENV_VALUE # endif #endif void env_opt(buf, len) register unsigned char *buf; register int len; { register unsigned char *ep = 0, *epc = 0; register int i; switch(buf[0]&0xff) { case TELQUAL_SEND: env_opt_start(); if (len == 1) { env_opt_add(NULL); } else for (i = 1; i < len; i++) { switch (buf[i]&0xff) { #ifdef OLD_ENVIRON case OLD_ENV_VAR: # ifdef ENV_HACK if (telopt_environ == TELOPT_OLD_ENVIRON && env_auto) { /* Server has the same definitions */ old_env_var = OLD_ENV_VAR; old_env_value = OLD_ENV_VALUE; } /* FALL THROUGH */ # endif case OLD_ENV_VALUE: /* * Although OLD_ENV_VALUE is not legal, we will * still recognize it, just in case it is an * old server that has VAR & VALUE mixed up... */ /* FALL THROUGH */ #else case NEW_ENV_VAR: #endif case ENV_USERVAR: if (ep) { *epc = 0; env_opt_add(ep); } ep = epc = &buf[i+1]; break; case ENV_ESC: i++; /*FALL THROUGH*/ default: if (epc) *epc++ = buf[i]; break; } } if (ep) { *epc = 0; env_opt_add(ep); } env_opt_end(1); break; case TELQUAL_IS: case TELQUAL_INFO: /* Ignore for now. We shouldn't get it anyway. */ break; default: break; } } #define OPT_REPLY_SIZE 256 unsigned char *opt_reply; unsigned char *opt_replyp; unsigned char *opt_replyend; void env_opt_start() { if (opt_reply) opt_reply = (unsigned char *)realloc(opt_reply, OPT_REPLY_SIZE); else opt_reply = (unsigned char *)malloc(OPT_REPLY_SIZE); if (opt_reply == NULL) { /*@*/ printf("env_opt_start: malloc()/realloc() failed!!!\n"); opt_reply = opt_replyp = opt_replyend = NULL; return; } opt_replyp = opt_reply; opt_replyend = opt_reply + OPT_REPLY_SIZE; *opt_replyp++ = IAC; *opt_replyp++ = SB; *opt_replyp++ = telopt_environ; *opt_replyp++ = TELQUAL_IS; } void env_opt_start_info() { env_opt_start(); if (opt_replyp) opt_replyp[-1] = TELQUAL_INFO; } void env_opt_add(ep) register unsigned char *ep; { register unsigned char *vp, c; if (opt_reply == NULL) /*XXX*/ return; /*XXX*/ if (ep == NULL || *ep == '\0') { /* Send user defined variables first. */ env_default(1, 0); while (ep = env_default(0, 0)) env_opt_add(ep); /* Now add the list of well know variables. */ env_default(1, 1); while (ep = env_default(0, 1)) env_opt_add(ep); return; } vp = env_getvalue(ep); if (opt_replyp + (vp ? strlen((char *)vp) : 0) + strlen((char *)ep) + 6 > opt_replyend) { register int len; opt_replyend += OPT_REPLY_SIZE; len = opt_replyend - opt_reply; opt_reply = (unsigned char *)realloc(opt_reply, len); if (opt_reply == NULL) { /*@*/ printf("env_opt_add: realloc() failed!!!\n"); opt_reply = opt_replyp = opt_replyend = NULL; return; } opt_replyp = opt_reply + len - (opt_replyend - opt_replyp); opt_replyend = opt_reply + len; } if (opt_welldefined(ep)) #ifdef OLD_ENVIRON if (telopt_environ == TELOPT_OLD_ENVIRON) *opt_replyp++ = old_env_var; else #endif *opt_replyp++ = NEW_ENV_VAR; else *opt_replyp++ = ENV_USERVAR; for (;;) { while (c = *ep++) { switch(c&0xff) { case IAC: *opt_replyp++ = IAC; break; case NEW_ENV_VAR: case NEW_ENV_VALUE: case ENV_ESC: case ENV_USERVAR: *opt_replyp++ = ENV_ESC; break; } *opt_replyp++ = c; } if (ep = vp) { #ifdef OLD_ENVIRON if (telopt_environ == TELOPT_OLD_ENVIRON) *opt_replyp++ = old_env_value; else #endif *opt_replyp++ = NEW_ENV_VALUE; vp = NULL; } else break; } } int opt_welldefined(ep) char *ep; { if ((strcmp(ep, "USER") == 0) || (strcmp(ep, "DISPLAY") == 0) || (strcmp(ep, "PRINTER") == 0) || (strcmp(ep, "SYSTEMTYPE") == 0) || (strcmp(ep, "JOB") == 0) || (strcmp(ep, "ACCT") == 0)) return(1); return(0); } void env_opt_end(emptyok) register int emptyok; { register int len; len = opt_replyp - opt_reply + 2; if (emptyok || len > 6) { *opt_replyp++ = IAC; *opt_replyp++ = SE; if (NETROOM() > len) { ring_supply_data(&netoring, opt_reply, len); printsub('>', &opt_reply[2], len - 2); } /*@*/ else printf("slc_end_reply: not enough room\n"); } if (opt_reply) { free(opt_reply); opt_reply = opt_replyp = opt_replyend = NULL; } } int telrcv() { register int c; register int scc; register unsigned char *sbp; int count; int returnValue = 0; scc = 0; count = 0; while (TTYROOM() > 2) { if (scc == 0) { if (count) { ring_consumed(&netiring, count); returnValue = 1; count = 0; } sbp = netiring.consume; scc = ring_full_consecutive(&netiring); if (scc == 0) { /* No more data coming in */ break; } } c = *sbp++ & 0xff, scc--; count++; switch (telrcv_state) { case TS_CR: telrcv_state = TS_DATA; if (c == '\0') { break; /* Ignore \0 after CR */ } else if ((c == '\n') && my_want_state_is_dont(TELOPT_ECHO) && !crmod) { TTYADD(c); break; } /* Else, fall through */ case TS_DATA: if (c == IAC) { telrcv_state = TS_IAC; break; } # if defined(TN3270) if (In3270) { *Ifrontp++ = c; while (scc > 0) { c = *sbp++ & 0377, scc--; count++; if (c == IAC) { telrcv_state = TS_IAC; break; } *Ifrontp++ = c; } } else # endif /* defined(TN3270) */ /* * The 'crmod' hack (see following) is needed * since we can't * set CRMOD on output only. * Machines like MULTICS like to send \r without * \n; since we must turn off CRMOD to get proper * input, the mapping is done here (sigh). */ if ((c == '\r') && my_want_state_is_dont(TELOPT_BINARY)) { if (scc > 0) { c = *sbp&0xff; if (c == 0) { sbp++, scc--; count++; /* a "true" CR */ TTYADD('\r'); } else if (my_want_state_is_dont(TELOPT_ECHO) && (c == '\n')) { sbp++, scc--; count++; TTYADD('\n'); } else { TTYADD('\r'); if (crmod) { TTYADD('\n'); } } } else { telrcv_state = TS_CR; TTYADD('\r'); if (crmod) { TTYADD('\n'); } } } else { TTYADD(c); } continue; case TS_IAC: process_iac: switch (c) { case WILL: telrcv_state = TS_WILL; continue; case WONT: telrcv_state = TS_WONT; continue; case DO: telrcv_state = TS_DO; continue; case DONT: telrcv_state = TS_DONT; continue; case DM: /* * We may have missed an urgent notification, * so make sure we flush whatever is in the * buffer currently. */ printoption("RCVD", IAC, DM); SYNCHing = 1; (void) ttyflush(1); SYNCHing = stilloob(); settimer(gotDM); break; case SB: SB_CLEAR(); telrcv_state = TS_SB; continue; # if defined(TN3270) case EOR: if (In3270) { if (Ibackp == Ifrontp) { Ibackp = Ifrontp = Ibuf; ISend = 0; /* should have been! */ } else { Ibackp += DataFromNetwork(Ibackp, Ifrontp-Ibackp, 1); ISend = 1; } } printoption("RCVD", IAC, EOR); break; # endif /* defined(TN3270) */ case IAC: # if !defined(TN3270) TTYADD(IAC); # else /* !defined(TN3270) */ if (In3270) { *Ifrontp++ = IAC; } else { TTYADD(IAC); } # endif /* !defined(TN3270) */ break; case NOP: case GA: default: printoption("RCVD", IAC, c); break; } telrcv_state = TS_DATA; continue; case TS_WILL: printoption("RCVD", WILL, c); willoption(c); SetIn3270(); telrcv_state = TS_DATA; continue; case TS_WONT: printoption("RCVD", WONT, c); wontoption(c); SetIn3270(); telrcv_state = TS_DATA; continue; case TS_DO: printoption("RCVD", DO, c); dooption(c); SetIn3270(); if (c == TELOPT_NAWS) { sendnaws(); } else if (c == TELOPT_LFLOW) { localflow = 1; setcommandmode(); setconnmode(0); } telrcv_state = TS_DATA; continue; case TS_DONT: printoption("RCVD", DONT, c); dontoption(c); flushline = 1; setconnmode(0); /* set new tty mode (maybe) */ SetIn3270(); telrcv_state = TS_DATA; continue; case TS_SB: if (c == IAC) { telrcv_state = TS_SE; } else { SB_ACCUM(c); } continue; case TS_SE: if (c != SE) { if (c != IAC) { /* * This is an error. We only expect to get * "IAC IAC" or "IAC SE". Several things may * have happend. An IAC was not doubled, the * IAC SE was left off, or another option got * inserted into the suboption are all possibilities. * If we assume that the IAC was not doubled, * and really the IAC SE was left off, we could * get into an infinate loop here. So, instead, * we terminate the suboption, and process the * partial suboption if we can. */ SB_ACCUM(IAC); SB_ACCUM(c); subpointer -= 2; SB_TERM(); printoption("In SUBOPTION processing, RCVD", IAC, c); suboption(); /* handle sub-option */ SetIn3270(); telrcv_state = TS_IAC; goto process_iac; } SB_ACCUM(c); telrcv_state = TS_SB; } else { SB_ACCUM(IAC); SB_ACCUM(SE); subpointer -= 2; SB_TERM(); suboption(); /* handle sub-option */ SetIn3270(); telrcv_state = TS_DATA; } } } if (count) ring_consumed(&netiring, count); return returnValue||count; } static int bol = 1, local = 0; int rlogin_susp() { if (local) { local = 0; bol = 1; command(0, "z\n", 2); return(1); } return(0); } static int telsnd() { int tcc; int count; int returnValue = 0; unsigned char *tbp; tcc = 0; count = 0; while (NETROOM() > 2) { register int sc; register int c; if (tcc == 0) { if (count) { ring_consumed(&ttyiring, count); returnValue = 1; count = 0; } tbp = ttyiring.consume; tcc = ring_full_consecutive(&ttyiring); if (tcc == 0) { break; } } c = *tbp++ & 0xff, sc = strip(c), tcc--; count++; if (rlogin != _POSIX_VDISABLE) { if (bol) { bol = 0; if (sc == rlogin) { local = 1; continue; } } else if (local) { local = 0; if (sc == '.' || c == termEofChar) { bol = 1; command(0, "close\n", 6); continue; } if (sc == termSuspChar) { bol = 1; command(0, "z\n", 2); continue; } if (sc == escape) { command(0, (char *)tbp, tcc); bol = 1; count += tcc; tcc = 0; flushline = 1; break; } if (sc != rlogin) { ++tcc; --tbp; --count; c = sc = rlogin; } } if ((sc == '\n') || (sc == '\r')) bol = 1; } else if (sc == escape) { /* * Double escape is a pass through of a single escape character. */ if (tcc && strip(*tbp) == escape) { tbp++; tcc--; count++; bol = 0; } else { command(0, (char *)tbp, tcc); bol = 1; count += tcc; tcc = 0; flushline = 1; break; } } else bol = 0; #ifdef KLUDGELINEMODE if (kludgelinemode && (globalmode&MODE_EDIT) && (sc == echoc)) { if (tcc > 0 && strip(*tbp) == echoc) { tcc--; tbp++; count++; } else { dontlecho = !dontlecho; settimer(echotoggle); setconnmode(0); flushline = 1; break; } } #endif if (MODE_LOCAL_CHARS(globalmode)) { if (TerminalSpecialChars(sc) == 0) { bol = 1; break; } } if (my_want_state_is_wont(TELOPT_BINARY)) { switch (c) { case '\n': /* * If we are in CRMOD mode (\r ==> \n) * on our local machine, then probably * a newline (unix) is CRLF (TELNET). */ if (MODE_LOCAL_CHARS(globalmode)) { NETADD('\r'); } NETADD('\n'); bol = flushline = 1; break; case '\r': if (!crlf) { NET2ADD('\r', '\0'); } else { NET2ADD('\r', '\n'); } bol = flushline = 1; break; case IAC: NET2ADD(IAC, IAC); break; default: NETADD(c); break; } } else if (c == IAC) { NET2ADD(IAC, IAC); } else { NETADD(c); } } if (count) ring_consumed(&ttyiring, count); return returnValue||count; /* Non-zero if we did anything */ } /* * Scheduler() * * Try to do something. * * If we do something useful, return 1; else return 0. * */ int Scheduler(block) int block; /* should we block in the select ? */ { /* One wants to be a bit careful about setting returnValue * to one, since a one implies we did some useful work, * and therefore probably won't be called to block next * time (TN3270 mode only). */ int returnValue; int netin, netout, netex, ttyin, ttyout; /* Decide which rings should be processed */ netout = ring_full_count(&netoring) && (flushline || (my_want_state_is_wont(TELOPT_LINEMODE) #ifdef KLUDGELINEMODE && (!kludgelinemode || my_want_state_is_do(TELOPT_SGA)) #endif ) || my_want_state_is_will(TELOPT_BINARY)); ttyout = ring_full_count(&ttyoring); #if defined(TN3270) ttyin = ring_empty_count(&ttyiring) && (clienteof == 0) && (shell_active == 0); #else /* defined(TN3270) */ ttyin = ring_empty_count(&ttyiring) && (clienteof == 0); #endif /* defined(TN3270) */ #if defined(TN3270) netin = ring_empty_count(&netiring); # else /* !defined(TN3270) */ netin = !ISend && ring_empty_count(&netiring); # endif /* !defined(TN3270) */ netex = !SYNCHing; /* If we have seen a signal recently, reset things */ # if defined(TN3270) && defined(unix) if (HaveInput) { HaveInput = 0; (void) signal(SIGIO, inputAvailable); } #endif /* defined(TN3270) && defined(unix) */ /* Call to system code to process rings */ returnValue = process_rings(netin, netout, netex, ttyin, ttyout, !block); /* Now, look at the input rings, looking for work to do. */ if (ring_full_count(&ttyiring)) { # if defined(TN3270) if (In3270) { int c; c = DataFromTerminal(ttyiring.consume, ring_full_consecutive(&ttyiring)); if (c) { returnValue = 1; ring_consumed(&ttyiring, c); } } else { # endif /* defined(TN3270) */ returnValue |= telsnd(); # if defined(TN3270) } # endif /* defined(TN3270) */ } if (ring_full_count(&netiring)) { # if !defined(TN3270) returnValue |= telrcv(); # else /* !defined(TN3270) */ returnValue = Push3270(); # endif /* !defined(TN3270) */ } return returnValue; } /* * Select from tty and network... */ void telnet(user) char *user; { sys_telnet_init(); #if defined(AUTHENTICATION) { static char local_host[256] = { 0 }; if (!local_host[0]) { gethostname(local_host, sizeof(local_host)); local_host[sizeof(local_host)-1] = 0; } auth_encrypt_init(local_host, hostname, "TELNET", 0); auth_encrypt_user(user); } #endif /* defined(AUTHENTICATION) */ # if !defined(TN3270) if (telnetport) { #if defined(AUTHENTICATION) if (autologin) send_will(TELOPT_AUTHENTICATION, 1); #endif send_do(TELOPT_SGA, 1); send_will(TELOPT_TTYPE, 1); send_will(TELOPT_NAWS, 1); send_will(TELOPT_TSPEED, 1); send_will(TELOPT_LFLOW, 1); send_will(TELOPT_LINEMODE, 1); send_will(TELOPT_NEW_ENVIRON, 1); send_do(TELOPT_STATUS, 1); if (env_getvalue((unsigned char *)"DISPLAY")) send_will(TELOPT_XDISPLOC, 1); if (eight) tel_enter_binary(eight); } # endif /* !defined(TN3270) */ # if !defined(TN3270) for (;;) { int schedValue; while ((schedValue = Scheduler(0)) != 0) { if (schedValue == -1) { setcommandmode(); return; } } if (Scheduler(1) == -1) { setcommandmode(); return; } } # else /* !defined(TN3270) */ for (;;) { int schedValue; while (!In3270 && !shell_active) { if (Scheduler(1) == -1) { setcommandmode(); return; } } while ((schedValue = Scheduler(0)) != 0) { if (schedValue == -1) { setcommandmode(); return; } } /* If there is data waiting to go out to terminal, don't * schedule any more data for the terminal. */ if (ring_full_count(&ttyoring)) { schedValue = 1; } else { if (shell_active) { if (shell_continue() == 0) { ConnectScreen(); } } else if (In3270) { schedValue = DoTerminalOutput(); } } if (schedValue && (shell_active == 0)) { if (Scheduler(1) == -1) { setcommandmode(); return; } } } # endif /* !defined(TN3270) */ } #if 0 /* XXX - this not being in is a bug */ /* * nextitem() * * Return the address of the next "item" in the TELNET data * stream. This will be the address of the next character if * the current address is a user data character, or it will * be the address of the character following the TELNET command * if the current address is a TELNET IAC ("I Am a Command") * character. */ static char * nextitem(current) char *current; { if ((*current&0xff) != IAC) { return current+1; } switch (*(current+1)&0xff) { case DO: case DONT: case WILL: case WONT: return current+3; case SB: /* loop forever looking for the SE */ { register char *look = current+2; for (;;) { if ((*look++&0xff) == IAC) { if ((*look++&0xff) == SE) { return look; } } } } default: return current+2; } } #endif /* 0 */ /* * netclear() * * We are about to do a TELNET SYNCH operation. Clear * the path to the network. * * Things are a bit tricky since we may have sent the first * byte or so of a previous TELNET command into the network. * So, we have to scan the network buffer from the beginning * until we are up to where we want to be. * * A side effect of what we do, just to keep things * simple, is to clear the urgent data pointer. The principal * caller should be setting the urgent data pointer AFTER calling * us in any case. */ static void netclear() { #if 0 /* XXX */ register char *thisitem, *next; char *good; #define wewant(p) ((nfrontp > p) && ((*p&0xff) == IAC) && \ ((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL)) thisitem = netobuf; while ((next = nextitem(thisitem)) <= netobuf.send) { thisitem = next; } /* Now, thisitem is first before/at boundary. */ good = netobuf; /* where the good bytes go */ while (netoring.add > thisitem) { if (wewant(thisitem)) { int length; next = thisitem; do { next = nextitem(next); } while (wewant(next) && (nfrontp > next)); length = next-thisitem; memcpy(good, thisitem, length); good += length; thisitem = next; } else { thisitem = nextitem(thisitem); } } #endif /* 0 */ } /* * These routines add various telnet commands to the data stream. */ static void doflush() { NET2ADD(IAC, DO); NETADD(TELOPT_TM); flushline = 1; flushout = 1; (void) ttyflush(1); /* Flush/drop output */ /* do printoption AFTER flush, otherwise the output gets tossed... */ printoption("SENT", DO, TELOPT_TM); } void xmitAO() { NET2ADD(IAC, AO); printoption("SENT", IAC, AO); if (autoflush) { doflush(); } } void xmitEL() { NET2ADD(IAC, EL); printoption("SENT", IAC, EL); } void xmitEC() { NET2ADD(IAC, EC); printoption("SENT", IAC, EC); } int dosynch() { netclear(); /* clear the path to the network */ NETADD(IAC); setneturg(); NETADD(DM); printoption("SENT", IAC, DM); return 1; } int want_status_response = 0; int get_status() { unsigned char tmp[16]; register unsigned char *cp; if (my_want_state_is_dont(TELOPT_STATUS)) { printf("Remote side does not support STATUS option\n"); return 0; } cp = tmp; *cp++ = IAC; *cp++ = SB; *cp++ = TELOPT_STATUS; *cp++ = TELQUAL_SEND; *cp++ = IAC; *cp++ = SE; if (NETROOM() >= cp - tmp) { ring_supply_data(&netoring, tmp, cp-tmp); printsub('>', tmp+2, cp - tmp - 2); } ++want_status_response; return 1; } void intp() { NET2ADD(IAC, IP); printoption("SENT", IAC, IP); flushline = 1; if (autoflush) { doflush(); } if (autosynch) { dosynch(); } } void sendbrk() { NET2ADD(IAC, BREAK); printoption("SENT", IAC, BREAK); flushline = 1; if (autoflush) { doflush(); } if (autosynch) { dosynch(); } } void sendabort() { NET2ADD(IAC, ABORT); printoption("SENT", IAC, ABORT); flushline = 1; if (autoflush) { doflush(); } if (autosynch) { dosynch(); } } void sendsusp() { NET2ADD(IAC, SUSP); printoption("SENT", IAC, SUSP); flushline = 1; if (autoflush) { doflush(); } if (autosynch) { dosynch(); } } void sendeof() { NET2ADD(IAC, xEOF); printoption("SENT", IAC, xEOF); } void sendayt() { NET2ADD(IAC, AYT); printoption("SENT", IAC, AYT); } /* * Send a window size update to the remote system. */ void sendnaws() { long rows, cols; unsigned char tmp[16]; register unsigned char *cp; if (my_state_is_wont(TELOPT_NAWS)) return; #define PUTSHORT(cp, x) { if ((*cp++ = ((x)>>8)&0xff) == IAC) *cp++ = IAC; \ if ((*cp++ = ((x))&0xff) == IAC) *cp++ = IAC; } if (TerminalWindowSize(&rows, &cols) == 0) { /* Failed */ return; } cp = tmp; *cp++ = IAC; *cp++ = SB; *cp++ = TELOPT_NAWS; PUTSHORT(cp, cols); PUTSHORT(cp, rows); *cp++ = IAC; *cp++ = SE; if (NETROOM() >= cp - tmp) { ring_supply_data(&netoring, tmp, cp-tmp); printsub('>', tmp+2, cp - tmp - 2); } } void tel_enter_binary(rw) int rw; { if (rw&1) send_do(TELOPT_BINARY, 1); if (rw&2) send_will(TELOPT_BINARY, 1); } void tel_leave_binary(rw) int rw; { if (rw&1) send_dont(TELOPT_BINARY, 1); if (rw&2) send_wont(TELOPT_BINARY, 1); }