📄 ssh.c
字号:
* 2-3-4 tree storing channels. */struct ssh_channel { Ssh ssh; /* pointer back to main context */ unsigned remoteid, localid; int type; /* * 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 stores host:port pairs we intend * to allow connections to, and is indexed by those host:port * pairs. In SSH 2 it stores a mapping from source port to * destination host:port pair, and is indexed by source port. */struct ssh_rportfwd { unsigned sport, dport; char dhost[256];};struct Packet { long length; int type; unsigned char *data; unsigned char *body; long savedpos; long maxlen;};static void ssh1_protocol(Ssh ssh, unsigned char *in, int inlen, int ispkt);static void ssh2_protocol(Ssh ssh, unsigned char *in, int inlen, int ispkt);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 void ssh_do_close(Ssh ssh);static unsigned long ssh_pkt_getuint32(Ssh ssh);static int ssh2_pkt_getbool(Ssh ssh);static void ssh_pkt_getstring(Ssh ssh, char **p, int *length);struct rdpkt1_state_tag { long len, pad, biglen, to_read; unsigned long realcrc, gotcrc; unsigned char *p; int i; int chunk;};struct rdpkt2_state_tag { long len, pad, payload, packetlen, maclen; int i; int cipherblk; unsigned long incoming_sequence;};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; tree234 *rportfwds; 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 pktin; struct Packet pktout; unsigned char *deferred_send_data; int deferred_len, deferred_size; /* * State associated with packet logging */ int pktout_logmode; int pktout_nblanks; struct logblank_t *pktout_blanks; /* * 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; char *portfwd_strptr; int pkt_ctx; void *x11auth; int version; int v1_throttle_count; int overall_bufsize; int throttled_all; int v1_stdout_throttling; int v2_outgoing_sequence; int ssh1_rdpkt_crstate; int ssh2_rdpkt_crstate; int do_ssh_init_crstate; int ssh_gotdata_crstate; int ssh1_protocol_crstate; int do_ssh1_login_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; void (*protocol) (Ssh ssh, unsigned char *in, int inlen, int ispkt); int (*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 agent callbacks. */ void *agent_response; int agent_response_len; /* * Text manipulation buffers for port forwarding. These are kept * here to simplify the state machine code in the protocol * funtions. We can't put these to stack under Symbian OS (too * big), and using local variables for the heap allocated buffers * would be cumbersome since we'd need to free and reallocate them * around every coroutine call. "One of the PuTTY source files * contains possibly the worst piece of C preprocessor hackery * I've ever seen in production code." indeed. */ char *sports; char *dports; char *saddr; char *host; /* * Special code list to be returned from ssh_get_specials(). Placing it * here avoids makiung it writable static data. */ struct telnet_special *specials;};#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); \ 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, int blanktype){ if (ssh->cfg.logomitpass) ssh->pktout_logmode = blanktype;}static void dont_log_data(Ssh ssh, int blanktype){ if (ssh->cfg.logomitdata) ssh->pktout_logmode = blanktype;}static void end_log_omission(Ssh ssh){ ssh->pktout_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) return i < 0 ? -1 : +1; if (a->dport > b->dport) return +1; if (a->dport < b->dport) return -1; return 0;}static int ssh_rportcmp_ssh2(void *av, void *bv){ struct ssh_rportfwd *a = (struct ssh_rportfwd *) av; struct ssh_rportfwd *b = (struct ssh_rportfwd *) bv; if (a->sport > b->sport) return +1; if (a->sport < b->sport) return -1; return 0;}static int alloc_channel_id(Ssh ssh){ const unsigned CHANNEL_NUMBER_OFFSET = 256; unsigned low, high, mid; int tsize; struct ssh_channel *c; /* * First-fit allocation of channel numbers: always pick the * lowest unused one. To do this, binary-search using the * counted B-tree to find the largest channel ID which is in a * contiguous sequence from the beginning. (Precisely * everything in that sequence must have ID equal to its tree * index plus CHANNEL_NUMBER_OFFSET.) */ tsize = count234(ssh->channels); low = UINT_MAX; high = tsize; while (high - low > 1) { mid = (high + low) / 2; c = index234(ssh->channels, mid); if (c->localid == mid + CHANNEL_NUMBER_OFFSET) low = mid; /* this one is fine */ else high = mid; /* this one is past it */ } /* * Now low points to either -1, or the tree index of the * largest ID in the initial sequence. */ { unsigned i = low + 1 + CHANNEL_NUMBER_OFFSET; assert(NULL == find234(ssh->channels, &i, ssh_channelfind)); } return low + 1 + CHANNEL_NUMBER_OFFSET;}static void c_write(Ssh ssh, const char *buf, int len){ if ((statics()->flags & FLAG_STDERR)) { int i; for (i = 0; i < len; i++) if (buf[i] != '\r') fputc(buf[i], stderr); return; } from_backend(ssh->frontend, 1, buf, len);}static void c_write_untrusted(Ssh ssh, const char *buf, int len){ int i; for (i = 0; i < len; i++) { if (buf[i] == '\n') c_write(ssh, "\r\n", 2); else if ((buf[i] & 0x60) || (buf[i] == '\r')) c_write(ssh, buf + i, 1); }}static void c_write_str(Ssh ssh, const char *buf){ c_write(ssh, buf, strlen(buf));}/* * Collect incoming data in the incoming packet buffer. * Decipher and verify the packet when it is completely read. * Drop SSH1_MSG_DEBUG and SSH1_MSG_IGNORE packets. * Update the *data and *datalen variables. * Return the additional nr of bytes needed, or 0 when * a complete packet is available. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -