📄 telnet.c
字号:
/*
* Telnet backend.
*/
#include <stdio.h>
#include <stdlib.h>
#include "putty.h"
#ifndef FALSE
#define FALSE 0
#endif
#ifndef TRUE
#define TRUE 1
#endif
#define IAC 255 /* interpret as command: */
#define DONT 254 /* you are not to use option */
#define DO 253 /* please, you use option */
#define WONT 252 /* I won't use option */
#define WILL 251 /* I will use option */
#define SB 250 /* interpret as subnegotiation */
#define SE 240 /* end sub negotiation */
#define GA 249 /* you may reverse the line */
#define EL 248 /* erase the current line */
#define EC 247 /* erase the current character */
#define AYT 246 /* are you there */
#define AO 245 /* abort output--but let prog finish */
#define IP 244 /* interrupt process--permanently */
#define BREAK 243 /* break */
#define DM 242 /* data mark--for connect. cleaning */
#define NOP 241 /* nop */
#define EOR 239 /* end of record (transparent mode) */
#define ABORT 238 /* Abort process */
#define SUSP 237 /* Suspend process */
#define xEOF 236 /* End of file: EOF is already used... */
#define TELOPT_BINARY 0 /* 8-bit data path */
#define TELOPT_ECHO 1 /* echo */
#define TELOPT_RCP 2 /* prepare to reconnect */
#define TELOPT_SGA 3 /* suppress go ahead */
#define TELOPT_NAMS 4 /* approximate message size */
#define TELOPT_STATUS 5 /* give status */
#define TELOPT_TM 6 /* timing mark */
#define TELOPT_RCTE 7 /* remote controlled transmission and echo */
#define TELOPT_NAOL 8 /* negotiate about output line width */
#define TELOPT_NAOP 9 /* negotiate about output page size */
#define TELOPT_NAOCRD 10 /* negotiate about CR disposition */
#define TELOPT_NAOHTS 11 /* negotiate about horizontal tabstops */
#define TELOPT_NAOHTD 12 /* negotiate about horizontal tab disposition */
#define TELOPT_NAOFFD 13 /* negotiate about formfeed disposition */
#define TELOPT_NAOVTS 14 /* negotiate about vertical tab stops */
#define TELOPT_NAOVTD 15 /* negotiate about vertical tab disposition */
#define TELOPT_NAOLFD 16 /* negotiate about output LF disposition */
#define TELOPT_XASCII 17 /* extended ascic character set */
#define TELOPT_LOGOUT 18 /* force logout */
#define TELOPT_BM 19 /* byte macro */
#define TELOPT_DET 20 /* data entry terminal */
#define TELOPT_SUPDUP 21 /* supdup protocol */
#define TELOPT_SUPDUPOUTPUT 22 /* supdup output */
#define TELOPT_SNDLOC 23 /* send location */
#define TELOPT_TTYPE 24 /* terminal type */
#define TELOPT_EOR 25 /* end or record */
#define TELOPT_TUID 26 /* TACACS user identification */
#define TELOPT_OUTMRK 27 /* output marking */
#define TELOPT_TTYLOC 28 /* terminal location number */
#define TELOPT_3270REGIME 29 /* 3270 regime */
#define TELOPT_X3PAD 30 /* X.3 PAD */
#define TELOPT_NAWS 31 /* window size */
#define TELOPT_TSPEED 32 /* terminal speed */
#define TELOPT_LFLOW 33 /* remote flow control */
#define TELOPT_LINEMODE 34 /* Linemode option */
#define TELOPT_XDISPLOC 35 /* X Display Location */
#define TELOPT_OLD_ENVIRON 36 /* Old - Environment variables */
#define TELOPT_AUTHENTICATION 37 /* Authenticate */
#define TELOPT_ENCRYPT 38 /* Encryption option */
#define TELOPT_NEW_ENVIRON 39 /* New - Environment variables */
#define TELOPT_TN3270E 40 /* TN3270 enhancements */
#define TELOPT_XAUTH 41
#define TELOPT_CHARSET 42 /* Character set */
#define TELOPT_RSP 43 /* Remote serial port */
#define TELOPT_COM_PORT_OPTION 44 /* Com port control */
#define TELOPT_SLE 45 /* Suppress local echo */
#define TELOPT_STARTTLS 46 /* Start TLS */
#define TELOPT_KERMIT 47 /* Automatic Kermit file transfer */
#define TELOPT_SEND_URL 48
#define TELOPT_FORWARD_X 49
#define TELOPT_PRAGMA_LOGON 138
#define TELOPT_SSPI_LOGON 139
#define TELOPT_PRAGMA_HEARTBEAT 140
#define TELOPT_EXOPL 255 /* extended-options-list */
#define TELQUAL_IS 0 /* option is... */
#define TELQUAL_SEND 1 /* send option */
#define TELQUAL_INFO 2 /* ENVIRON: informational version of IS */
#define BSD_VAR 1
#define BSD_VALUE 0
#define RFC_VAR 0
#define RFC_VALUE 1
#define CR 13
#define LF 10
#define NUL 0
#define iswritable(x) \
( (x) != IAC && \
(telnet->opt_states[o_we_bin.index] == ACTIVE || (x) != CR))
static char *telopt(int opt)
{
#define i(x) if (opt == TELOPT_ ## x) return #x;
i(BINARY);
i(ECHO);
i(RCP);
i(SGA);
i(NAMS);
i(STATUS);
i(TM);
i(RCTE);
i(NAOL);
i(NAOP);
i(NAOCRD);
i(NAOHTS);
i(NAOHTD);
i(NAOFFD);
i(NAOVTS);
i(NAOVTD);
i(NAOLFD);
i(XASCII);
i(LOGOUT);
i(BM);
i(DET);
i(SUPDUP);
i(SUPDUPOUTPUT);
i(SNDLOC);
i(TTYPE);
i(EOR);
i(TUID);
i(OUTMRK);
i(TTYLOC);
i(X3PAD);
i(NAWS);
i(TSPEED);
i(LFLOW);
i(LINEMODE);
i(XDISPLOC);
i(OLD_ENVIRON);
i(AUTHENTICATION);
i(ENCRYPT);
i(NEW_ENVIRON);
i(TN3270E);
i(XAUTH);
i(CHARSET);
i(RSP);
i(COM_PORT_OPTION);
i(SLE);
i(STARTTLS);
i(KERMIT);
i(SEND_URL);
i(FORWARD_X);
i(PRAGMA_LOGON);
i(SSPI_LOGON);
i(PRAGMA_HEARTBEAT);
i(EXOPL);
#undef i
return "<unknown>";
}
static void telnet_size(void *handle, int width, int height);
struct Opt {
int send; /* what we initially send */
int nsend; /* -ve send if requested to stop it */
int ack, nak; /* +ve and -ve acknowledgements */
int option; /* the option code */
int index; /* index into telnet->opt_states[] */
enum {
REQUESTED, ACTIVE, INACTIVE, REALLY_INACTIVE
} initial_state;
};
enum {
OPTINDEX_NAWS,
OPTINDEX_TSPEED,
OPTINDEX_TTYPE,
OPTINDEX_OENV,
OPTINDEX_NENV,
OPTINDEX_ECHO,
OPTINDEX_WE_SGA,
OPTINDEX_THEY_SGA,
OPTINDEX_WE_BIN,
OPTINDEX_THEY_BIN,
NUM_OPTS
};
static const struct Opt o_naws =
{ WILL, WONT, DO, DONT, TELOPT_NAWS, OPTINDEX_NAWS, REQUESTED };
static const struct Opt o_tspeed =
{ WILL, WONT, DO, DONT, TELOPT_TSPEED, OPTINDEX_TSPEED, REQUESTED };
static const struct Opt o_ttype =
{ WILL, WONT, DO, DONT, TELOPT_TTYPE, OPTINDEX_TTYPE, REQUESTED };
static const struct Opt o_oenv =
{ WILL, WONT, DO, DONT, TELOPT_OLD_ENVIRON, OPTINDEX_OENV, INACTIVE };
static const struct Opt o_nenv =
{ WILL, WONT, DO, DONT, TELOPT_NEW_ENVIRON, OPTINDEX_NENV, REQUESTED };
static const struct Opt o_echo =
{ DO, DONT, WILL, WONT, TELOPT_ECHO, OPTINDEX_ECHO, REQUESTED };
static const struct Opt o_we_sga =
{ WILL, WONT, DO, DONT, TELOPT_SGA, OPTINDEX_WE_SGA, REQUESTED };
static const struct Opt o_they_sga =
{ DO, DONT, WILL, WONT, TELOPT_SGA, OPTINDEX_THEY_SGA, REQUESTED };
static const struct Opt o_we_bin =
{ WILL, WONT, DO, DONT, TELOPT_BINARY, OPTINDEX_WE_BIN, INACTIVE };
static const struct Opt o_they_bin =
{ DO, DONT, WILL, WONT, TELOPT_BINARY, OPTINDEX_THEY_BIN, INACTIVE };
static const struct Opt *const opts[] = {
&o_naws, &o_tspeed, &o_ttype, &o_oenv, &o_nenv, &o_echo,
&o_we_sga, &o_they_sga, &o_we_bin, &o_they_bin, NULL
};
typedef struct telnet_tag {
const struct plug_function_table *fn;
/* the above field _must_ be first in the structure */
Socket s;
void *frontend;
void *ldisc;
int term_width, term_height;
int opt_states[NUM_OPTS];
int echoing, editing;
int activated;
int bufsize;
int in_synch;
int sb_opt, sb_len;
unsigned char *sb_buf;
int sb_size;
enum {
TOP_LEVEL, SEENIAC, SEENWILL, SEENWONT, SEENDO, SEENDONT,
SEENSB, SUBNEGOT, SUBNEG_IAC, SEENCR
} state;
Config cfg;
Pinger pinger;
} *Telnet;
#define TELNET_MAX_BACKLOG 4096
#define SB_DELTA 1024
static void c_write(Telnet telnet, char *buf, int len)
{
int backlog;
backlog = from_backend(telnet->frontend, 0, buf, len);
sk_set_frozen(telnet->s, backlog > TELNET_MAX_BACKLOG);
}
static void log_option(Telnet telnet, char *sender, int cmd, int option)
{
char *buf;
/*
* The strange-looking "<?""?>" below is there to avoid a
* trigraph - a double question mark followed by > maps to a
* closing brace character!
*/
buf = dupprintf("%s:\t%s %s", sender,
(cmd == WILL ? "WILL" : cmd == WONT ? "WONT" :
cmd == DO ? "DO" : cmd == DONT ? "DONT" : "<?""?>"),
telopt(option));
logevent(telnet->frontend, buf);
sfree(buf);
}
static void send_opt(Telnet telnet, int cmd, int option)
{
unsigned char b[3];
b[0] = IAC;
b[1] = cmd;
b[2] = option;
telnet->bufsize = sk_write(telnet->s, (char *)b, 3);
log_option(telnet, "client", cmd, option);
}
static void deactivate_option(Telnet telnet, const struct Opt *o)
{
if (telnet->opt_states[o->index] == REQUESTED ||
telnet->opt_states[o->index] == ACTIVE)
send_opt(telnet, o->nsend, o->option);
telnet->opt_states[o->index] = REALLY_INACTIVE;
}
/*
* Generate side effects of enabling or disabling an option.
*/
static void option_side_effects(Telnet telnet, const struct Opt *o, int enabled)
{
if (o->option == TELOPT_ECHO && o->send == DO)
telnet->echoing = !enabled;
else if (o->option == TELOPT_SGA && o->send == DO)
telnet->editing = !enabled;
if (telnet->ldisc) /* cause ldisc to notice the change */
ldisc_send(telnet->ldisc, NULL, 0, 0);
/* Ensure we get the minimum options */
if (!telnet->activated) {
if (telnet->opt_states[o_echo.index] == INACTIVE) {
telnet->opt_states[o_echo.index] = REQUESTED;
send_opt(telnet, o_echo.send, o_echo.option);
}
if (telnet->opt_states[o_we_sga.index] == INACTIVE) {
telnet->opt_states[o_we_sga.index] = REQUESTED;
send_opt(telnet, o_we_sga.send, o_we_sga.option);
}
if (telnet->opt_states[o_they_sga.index] == INACTIVE) {
telnet->opt_states[o_they_sga.index] = REQUESTED;
send_opt(telnet, o_they_sga.send, o_they_sga.option);
}
telnet->activated = TRUE;
}
}
static void activate_option(Telnet telnet, const struct Opt *o)
{
if (o->send == WILL && o->option == TELOPT_NAWS)
telnet_size(telnet, telnet->term_width, telnet->term_height);
if (o->send == WILL &&
(o->option == TELOPT_NEW_ENVIRON ||
o->option == TELOPT_OLD_ENVIRON)) {
/*
* We may only have one kind of ENVIRON going at a time.
* This is a hack, but who cares.
*/
deactivate_option(telnet, o->option ==
TELOPT_NEW_ENVIRON ? &o_oenv : &o_nenv);
}
option_side_effects(telnet, o, 1);
}
static void refused_option(Telnet telnet, const struct Opt *o)
{
if (o->send == WILL && o->option == TELOPT_NEW_ENVIRON &&
telnet->opt_states[o_oenv.index] == INACTIVE) {
send_opt(telnet, WILL, TELOPT_OLD_ENVIRON);
telnet->opt_states[o_oenv.index] = REQUESTED;
}
option_side_effects(telnet, o, 0);
}
static void proc_rec_opt(Telnet telnet, int cmd, int option)
{
const struct Opt *const *o;
log_option(telnet, "server", cmd, option);
for (o = opts; *o; o++) {
if ((*o)->option == option && (*o)->ack == cmd) {
switch (telnet->opt_states[(*o)->index]) {
case REQUESTED:
telnet->opt_states[(*o)->index] = ACTIVE;
activate_option(telnet, *o);
break;
case ACTIVE:
break;
case INACTIVE:
telnet->opt_states[(*o)->index] = ACTIVE;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -