📄 telnet.c
字号:
/*
* 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.4 (Berkeley) 5/30/95";
#endif /* not lint */
#include <sys/types.h>
#if defined(unix)
#include <signal.h>
/* 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 <arpa/telnet.h>
#include <ctype.h>
#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 <CR><LF> (or <CR><NUL>)? */
#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;
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) || defined(ENCRYPTION)
auth_encrypt_connect(connected);
#endif /* defined(AUTHENTICATION) || defined(ENCRYPTION) */
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.h>
/*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
#ifdef ENCRYPTION
case TELOPT_ENCRYPT:
#endif /* ENCRYPTION */
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);
#ifdef ENCRYPTION
if (option == TELOPT_ENCRYPT)
encrypt_send_support();
#endif /* ENCRYPTION */
}
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 */
#ifdef ENCRYPTION
case TELOPT_ENCRYPT: /* encryption variable option */
#endif /* ENCRYPTION */
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 ((int)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.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -