📄 ssh.c
字号:
enum { /* channel types */
CHAN_MAINSESSION,
CHAN_X11,
CHAN_AGENT,
CHAN_SOCKDATA,
CHAN_SOCKDATA_DORMANT /* one the remote hasn't confirmed */
};
/*
* 2-3-4 tree storing channels.
*/
struct ssh_channel {
Ssh ssh; /* pointer back to main context */
unsigned remoteid, localid;
int type;
/* True if we opened this channel but server hasn't confirmed. */
int halfopen;
/*
* In SSH1, this value contains four bits:
*
* 1 We have sent SSH1_MSG_CHANNEL_CLOSE.
* 2 We have sent SSH1_MSG_CHANNEL_CLOSE_CONFIRMATION.
* 4 We have received SSH1_MSG_CHANNEL_CLOSE.
* 8 We have received SSH1_MSG_CHANNEL_CLOSE_CONFIRMATION.
*
* A channel is completely finished with when all four bits are set.
*/
int closes;
union {
struct ssh1_data_channel {
int throttling;
} v1;
struct ssh2_data_channel {
bufchain outbuffer;
unsigned remwindow, remmaxpkt;
unsigned locwindow;
} v2;
} v;
union {
struct ssh_agent_channel {
unsigned char *message;
unsigned char msglen[4];
unsigned lensofar, totallen;
} a;
struct ssh_x11_channel {
Socket s;
} x11;
struct ssh_pfd_channel {
Socket s;
} pfd;
} u;
};
/*
* 2-3-4 tree storing remote->local port forwardings. SSH 1 and SSH
* 2 use this structure in different ways, reflecting SSH 2's
* altogether saner approach to port forwarding.
*
* In SSH 1, you arrange a remote forwarding by sending the server
* the remote port number, and the local destination host:port.
* When a connection comes in, the server sends you back that
* host:port pair, and you connect to it. This is a ready-made
* security hole if you're not on the ball: a malicious server
* could send you back _any_ host:port pair, so if you trustingly
* connect to the address it gives you then you've just opened the
* entire inside of your corporate network just by connecting
* through it to a dodgy SSH server. Hence, we must store a list of
* host:port pairs we _are_ trying to forward to, and reject a
* connection request from the server if it's not in the list.
*
* In SSH 2, each side of the connection minds its own business and
* doesn't send unnecessary information to the other. You arrange a
* remote forwarding by sending the server just the remote port
* number. When a connection comes in, the server tells you which
* of its ports was connected to; and _you_ have to remember what
* local host:port pair went with that port number.
*
* Hence, in SSH 1 this structure is indexed by destination
* host:port pair, whereas in SSH 2 it is indexed by source port.
*/
struct ssh_portfwd; /* forward declaration */
struct ssh_rportfwd {
unsigned sport, dport;
char dhost[256];
char *sportdesc;
struct ssh_portfwd *pfrec;
};
#define free_rportfwd(pf) ( \
((pf) ? (sfree((pf)->sportdesc)) : (void)0 ), sfree(pf) )
/*
* Separately to the rportfwd tree (which is for looking up port
* open requests from the server), a tree of _these_ structures is
* used to keep track of all the currently open port forwardings,
* so that we can reconfigure in mid-session if the user requests
* it.
*/
struct ssh_portfwd {
enum { DESTROY, KEEP, CREATE } status;
int type;
unsigned sport, dport;
char *saddr, *daddr;
char *sserv, *dserv;
struct ssh_rportfwd *remote;
int addressfamily;
void *local;
};
#define free_portfwd(pf) ( \
((pf) ? (sfree((pf)->saddr), sfree((pf)->daddr), \
sfree((pf)->sserv), sfree((pf)->dserv)) : (void)0 ), sfree(pf) )
struct Packet {
long length;
long forcepad; /* Force padding to at least this length */
int type;
unsigned long sequence;
unsigned char *data;
unsigned char *body;
long savedpos;
long maxlen;
long encrypted_len; /* for SSH2 total-size counting */
/*
* State associated with packet logging
*/
int logmode;
int nblanks;
struct logblank_t *blanks;
};
static void ssh1_protocol(Ssh ssh, void *vin, int inlen,
struct Packet *pktin);
static void ssh2_protocol(Ssh ssh, void *vin, int inlen,
struct Packet *pktin);
static void ssh1_protocol_setup(Ssh ssh);
static void ssh2_protocol_setup(Ssh ssh);
static void ssh_size(void *handle, int width, int height);
static void ssh_special(void *handle, Telnet_Special);
static int ssh2_try_send(struct ssh_channel *c);
static void ssh2_add_channel_data(struct ssh_channel *c, char *buf, int len);
static void ssh_throttle_all(Ssh ssh, int enable, int bufsize);
static void ssh2_set_window(struct ssh_channel *c, unsigned newwin);
static int ssh_sendbuffer(void *handle);
static int ssh_do_close(Ssh ssh, int notify_exit);
static unsigned long ssh_pkt_getuint32(struct Packet *pkt);
static int ssh2_pkt_getbool(struct Packet *pkt);
static void ssh_pkt_getstring(struct Packet *pkt, char **p, int *length);
static void ssh2_timer(void *ctx, long now);
static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
struct Packet *pktin);
struct rdpkt1_state_tag {
long len, pad, biglen, to_read;
unsigned long realcrc, gotcrc;
unsigned char *p;
int i;
int chunk;
struct Packet *pktin;
};
struct rdpkt2_state_tag {
long len, pad, payload, packetlen, maclen;
int i;
int cipherblk;
unsigned long incoming_sequence;
struct Packet *pktin;
};
typedef void (*handler_fn_t)(Ssh ssh, struct Packet *pktin);
typedef void (*chandler_fn_t)(Ssh ssh, struct Packet *pktin, void *ctx);
struct queued_handler;
struct queued_handler {
int msg1, msg2;
chandler_fn_t handler;
void *ctx;
struct queued_handler *next;
};
struct ssh_tag {
const struct plug_function_table *fn;
/* the above field _must_ be first in the structure */
SHA_State exhash, exhashbase;
Socket s;
void *ldisc;
void *logctx;
unsigned char session_key[32];
int v1_compressing;
int v1_remote_protoflags;
int v1_local_protoflags;
int agentfwd_enabled;
int X11_fwd_enabled;
int remote_bugs;
const struct ssh_cipher *cipher;
void *v1_cipher_ctx;
void *crcda_ctx;
const struct ssh2_cipher *cscipher, *sccipher;
void *cs_cipher_ctx, *sc_cipher_ctx;
const struct ssh_mac *csmac, *scmac;
void *cs_mac_ctx, *sc_mac_ctx;
const struct ssh_compress *cscomp, *sccomp;
void *cs_comp_ctx, *sc_comp_ctx;
const struct ssh_kex *kex;
const struct ssh_signkey *hostkey;
unsigned char v2_session_id[20];
void *kex_ctx;
char *savedhost;
int savedport;
int send_ok;
int echoing, editing;
void *frontend;
int ospeed, ispeed; /* temporaries */
int term_width, term_height;
tree234 *channels; /* indexed by local id */
struct ssh_channel *mainchan; /* primary session channel */
int exitcode;
int close_expected;
tree234 *rportfwds, *portfwds;
enum {
SSH_STATE_PREPACKET,
SSH_STATE_BEFORE_SIZE,
SSH_STATE_INTERMED,
SSH_STATE_SESSION,
SSH_STATE_CLOSED
} state;
int size_needed, eof_needed;
struct Packet **queue;
int queuelen, queuesize;
int queueing;
unsigned char *deferred_send_data;
int deferred_len, deferred_size;
/*
* Gross hack: pscp will try to start SFTP but fall back to
* scp1 if that fails. This variable is the means by which
* scp.c can reach into the SSH code and find out which one it
* got.
*/
int fallback_cmd;
/*
* Used for username and password input.
*/
char *userpass_input_buffer;
int userpass_input_buflen;
int userpass_input_bufpos;
int userpass_input_echo;
int pkt_ctx;
void *x11auth;
int version;
int v1_throttle_count;
int overall_bufsize;
int throttled_all;
int v1_stdout_throttling;
unsigned long v2_outgoing_sequence;
int ssh1_rdpkt_crstate;
int ssh2_rdpkt_crstate;
int do_ssh_init_crstate;
int ssh_gotdata_crstate;
int do_ssh1_login_crstate;
int do_ssh1_connection_crstate;
int do_ssh2_transport_crstate;
int do_ssh2_authconn_crstate;
void *do_ssh_init_state;
void *do_ssh1_login_state;
void *do_ssh2_transport_state;
void *do_ssh2_authconn_state;
struct rdpkt1_state_tag rdpkt1_state;
struct rdpkt2_state_tag rdpkt2_state;
/* ssh1 and ssh2 use this for different things, but both use it */
int protocol_initial_phase_done;
void (*protocol) (Ssh ssh, void *vin, int inlen,
struct Packet *pkt);
struct Packet *(*s_rdpkt) (Ssh ssh, unsigned char **data, int *datalen);
/*
* We maintain a full _copy_ of a Config structure here, not
* merely a pointer to it. That way, when we're passed a new
* one for reconfiguration, we can check the differences and
* potentially reconfigure port forwardings etc in mid-session.
*/
Config cfg;
/*
* Used to transfer data back from async callbacks.
*/
void *agent_response;
int agent_response_len;
int user_response;
/*
* The SSH connection can be set as `frozen', meaning we are
* not currently accepting incoming data from the network. This
* is slightly more serious than setting the _socket_ as
* frozen, because we may already have had data passed to us
* from the network which we need to delay processing until
* after the freeze is lifted, so we also need a bufchain to
* store that data.
*/
int frozen;
bufchain queued_incoming_data;
/*
* Dispatch table for packet types that we may have to deal
* with at any time.
*/
handler_fn_t packet_dispatch[256];
/*
* Queues of one-off handler functions for success/failure
* indications from a request.
*/
struct queued_handler *qhead, *qtail;
/*
* This module deals with sending keepalives.
*/
Pinger pinger;
/*
* Track incoming and outgoing data sizes and time, for
* size-based rekeys.
*/
unsigned long incoming_data_size, outgoing_data_size, deferred_data_size;
unsigned long max_data_size;
int kex_in_progress;
long next_rekey, last_rekey;
char *deferred_rekey_reason; /* points to STATIC string; don't free */
};
/* Not used in FileZilla
#define logevent(s) logevent(ssh->frontend, s)
*/
/* logevent, only printf-formatted. */
static void logeventf(Ssh ssh, const char *fmt, ...)
{
va_list ap;
char *buf;
va_start(ap, fmt);
buf = dupvprintf(fmt, ap);
va_end(ap);
logevent(buf);
sfree(buf);
}
#define bombout(msg) \
do { \
char *text = dupprintf msg; \
ssh_do_close(ssh, FALSE); \
logevent(text); \
connection_fatal(ssh->frontend, "%s", text); \
sfree(text); \
} while (0)
/* Functions to leave bits out of the SSH packet log file. */
static void dont_log_password(Ssh ssh, struct Packet *pkt, int blanktype)
{
if (ssh->cfg.logomitpass)
pkt->logmode = blanktype;
}
static void dont_log_data(Ssh ssh, struct Packet *pkt, int blanktype)
{
if (ssh->cfg.logomitdata)
pkt->logmode = blanktype;
}
static void end_log_omission(Ssh ssh, struct Packet *pkt)
{
pkt->logmode = PKTLOG_EMIT;
}
static int ssh_channelcmp(void *av, void *bv)
{
struct ssh_channel *a = (struct ssh_channel *) av;
struct ssh_channel *b = (struct ssh_channel *) bv;
if (a->localid < b->localid)
return -1;
if (a->localid > b->localid)
return +1;
return 0;
}
static int ssh_channelfind(void *av, void *bv)
{
unsigned *a = (unsigned *) av;
struct ssh_channel *b = (struct ssh_channel *) bv;
if (*a < b->localid)
return -1;
if (*a > b->localid)
return +1;
return 0;
}
static int ssh_rportcmp_ssh1(void *av, void *bv)
{
struct ssh_rportfwd *a = (struct ssh_rportfwd *) av;
struct ssh_rportfwd *b = (struct ssh_rportfwd *) bv;
int i;
if ( (i = strcmp(a->dhost, b->dhost)) != 0)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -