зеркало из https://github.com/github/putty.git
Replace enum+union of local channel types with a vtable.
There's now an interface called 'Channel', which handles the local side of an SSH connection-layer channel, in terms of knowing where to send incoming channel data to, whether to close the channel, etc. Channel and the previous 'struct ssh_channel' mutually refer. The latter contains all the SSH-specific parts, and as much of the common logic as possible: in particular, Channel doesn't have to know anything about SSH packet formats, or which SSH protocol version is in use, or deal with all the fiddly stuff about window sizes - with the exception that x11fwd.c's implementation of it does have to be able to ask for a small fixed initial window size for the bodgy system that distinguishes upstream from downstream X forwardings. I've taken the opportunity to move the code implementing the detailed behaviour of agent forwarding out of ssh.c, now that all of it is on the far side of a uniform interface. (This also means that if I later implement agent forwarding directly to a Unix socket as an alternative, it'll be a matter of changing just the one call to agentf_new() that makes the Channel to plug into a forwarding.)
This commit is contained in:
Родитель
8dfb2a1186
Коммит
6a8b9d3813
2
Recipe
2
Recipe
|
@ -254,7 +254,7 @@ SSH = ssh ssh1bpp ssh2bpp ssh2bpp-bare ssh1censor ssh2censor
|
|||
+ sshcrc sshdes sshmd5 sshrsa sshrand sshsha sshblowf
|
||||
+ sshdh sshcrcda sshpubk sshzlib sshdss x11fwd portfwd
|
||||
+ sshaes sshccp sshsh256 sshsh512 sshbn wildcard pinger ssharcf
|
||||
+ sshgssc pgssapi sshshare sshecc aqsync marshal nullplug
|
||||
+ sshgssc pgssapi sshshare sshecc aqsync marshal nullplug agentf
|
||||
WINSSH = SSH winnoise wincapi winpgntc wingss winshare winnps winnpc
|
||||
+ winhsock errsock
|
||||
UXSSH = SSH uxnoise uxagentc uxgss uxshare
|
||||
|
|
|
@ -0,0 +1,235 @@
|
|||
/*
|
||||
* SSH agent forwarding.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "putty.h"
|
||||
#include "ssh.h"
|
||||
#include "pageant.h"
|
||||
#include "sshchan.h"
|
||||
|
||||
typedef struct agentf {
|
||||
struct ssh_channel *c;
|
||||
bufchain inbuffer;
|
||||
agent_pending_query *pending;
|
||||
int input_wanted;
|
||||
int rcvd_eof;
|
||||
|
||||
Channel chan;
|
||||
} agentf;
|
||||
|
||||
static void agentf_got_response(agentf *af, void *reply, int replylen)
|
||||
{
|
||||
af->pending = NULL;
|
||||
|
||||
if (!reply) {
|
||||
/* The real agent didn't send any kind of reply at all for
|
||||
* some reason, so fake an SSH_AGENT_FAILURE. */
|
||||
reply = "\0\0\0\1\5";
|
||||
replylen = 5;
|
||||
}
|
||||
|
||||
sshfwd_write(af->c, reply, replylen);
|
||||
}
|
||||
|
||||
static void agentf_callback(void *vctx, void *reply, int replylen);
|
||||
|
||||
static void agentf_try_forward(agentf *af)
|
||||
{
|
||||
unsigned datalen, length;
|
||||
strbuf *message;
|
||||
unsigned char msglen[4];
|
||||
void *reply;
|
||||
int replylen;
|
||||
|
||||
/*
|
||||
* Don't try to parallelise agent requests. Wait for each one to
|
||||
* return before attempting the next.
|
||||
*/
|
||||
if (af->pending)
|
||||
return;
|
||||
|
||||
/*
|
||||
* If the outgoing side of the channel connection is currently
|
||||
* throttled, don't submit any new forwarded requests to the real
|
||||
* agent. This causes the input side of the agent forwarding not
|
||||
* to be emptied, exerting the required back-pressure on the
|
||||
* remote client, and encouraging it to read our responses before
|
||||
* sending too many more requests.
|
||||
*/
|
||||
if (!af->input_wanted)
|
||||
return;
|
||||
|
||||
while (1) {
|
||||
/*
|
||||
* Try to extract a complete message from the input buffer.
|
||||
*/
|
||||
datalen = bufchain_size(&af->inbuffer);
|
||||
if (datalen < 4)
|
||||
break; /* not even a length field available yet */
|
||||
|
||||
bufchain_fetch(&af->inbuffer, msglen, 4);
|
||||
length = GET_32BIT(msglen);
|
||||
|
||||
if (length > AGENT_MAX_MSGLEN-4) {
|
||||
/*
|
||||
* If the remote has sent a message that's just _too_
|
||||
* long, we should reject it in advance of seeing the rest
|
||||
* of the incoming message, and also close the connection
|
||||
* for good measure (which avoids us having to faff about
|
||||
* with carefully ignoring just the right number of bytes
|
||||
* from the overlong message).
|
||||
*/
|
||||
agentf_got_response(af, NULL, 0);
|
||||
sshfwd_write_eof(af->c);
|
||||
return;
|
||||
}
|
||||
|
||||
if (length > datalen - 4)
|
||||
break; /* a whole message is not yet available */
|
||||
|
||||
bufchain_consume(&af->inbuffer, 4);
|
||||
|
||||
message = strbuf_new_for_agent_query();
|
||||
bufchain_fetch_consume(
|
||||
&af->inbuffer, strbuf_append(message, length), length);
|
||||
af->pending = agent_query(
|
||||
message, &reply, &replylen, agentf_callback, af);
|
||||
strbuf_free(message);
|
||||
|
||||
if (af->pending)
|
||||
return; /* agent_query promised to reply in due course */
|
||||
|
||||
/*
|
||||
* If the agent gave us an answer immediately, pass it
|
||||
* straight on and go round this loop again.
|
||||
*/
|
||||
agentf_got_response(af, reply, replylen);
|
||||
sfree(reply);
|
||||
}
|
||||
|
||||
/*
|
||||
* If we get here (i.e. we left the above while loop via 'break'
|
||||
* rather than 'return'), that means we've determined that the
|
||||
* input buffer for the agent forwarding connection doesn't
|
||||
* contain a complete request.
|
||||
*
|
||||
* So if there's potentially more data to come, we can return now,
|
||||
* and wait for the remote client to send it. But if the remote
|
||||
* has sent EOF, it would be a mistake to do that, because we'd be
|
||||
* waiting a long time. So this is the moment to check for EOF,
|
||||
* and respond appropriately.
|
||||
*/
|
||||
if (af->rcvd_eof)
|
||||
sshfwd_write_eof(af->c);
|
||||
}
|
||||
|
||||
static void agentf_callback(void *vctx, void *reply, int replylen)
|
||||
{
|
||||
agentf *af = (agentf *)vctx;
|
||||
|
||||
agentf_got_response(af, reply, replylen);
|
||||
sfree(reply);
|
||||
|
||||
/*
|
||||
* Now try to extract and send further messages from the channel's
|
||||
* input-side buffer.
|
||||
*/
|
||||
agentf_try_forward(af);
|
||||
}
|
||||
|
||||
static void agentf_free(Channel *chan);
|
||||
static int agentf_send(Channel *chan, int is_stderr, const void *, int);
|
||||
static void agentf_send_eof(Channel *chan);
|
||||
static char *agentf_log_close_msg(Channel *chan);
|
||||
static void agentf_set_input_wanted(Channel *chan, int wanted);
|
||||
|
||||
static const struct ChannelVtable agentf_channelvt = {
|
||||
agentf_free,
|
||||
chan_remotely_opened_confirmation,
|
||||
chan_remotely_opened_failure,
|
||||
agentf_send,
|
||||
agentf_send_eof,
|
||||
agentf_set_input_wanted,
|
||||
agentf_log_close_msg,
|
||||
chan_no_eager_close,
|
||||
};
|
||||
|
||||
Channel *agentf_new(struct ssh_channel *c)
|
||||
{
|
||||
agentf *af = snew(agentf);
|
||||
af->c = c;
|
||||
af->chan.vt = &agentf_channelvt;
|
||||
af->chan.initial_fixed_window_size = 0;
|
||||
af->rcvd_eof = TRUE;
|
||||
bufchain_init(&af->inbuffer);
|
||||
af->pending = NULL;
|
||||
af->input_wanted = TRUE;
|
||||
return &af->chan;
|
||||
}
|
||||
|
||||
static void agentf_free(Channel *chan)
|
||||
{
|
||||
assert(chan->vt == &agentf_channelvt);
|
||||
agentf *af = FROMFIELD(chan, agentf, chan);
|
||||
|
||||
if (af->pending)
|
||||
agent_cancel_query(af->pending);
|
||||
bufchain_clear(&af->inbuffer);
|
||||
sfree(af);
|
||||
}
|
||||
|
||||
static int agentf_send(Channel *chan, int is_stderr,
|
||||
const void *data, int length)
|
||||
{
|
||||
assert(chan->vt == &agentf_channelvt);
|
||||
agentf *af = FROMFIELD(chan, agentf, chan);
|
||||
bufchain_add(&af->inbuffer, data, length);
|
||||
agentf_try_forward(af);
|
||||
|
||||
/*
|
||||
* We exert back-pressure on an agent forwarding client if and
|
||||
* only if we're waiting for the response to an asynchronous agent
|
||||
* request. This prevents the client running out of window while
|
||||
* receiving the _first_ message, but means that if any message
|
||||
* takes time to process, the client will be discouraged from
|
||||
* sending an endless stream of further ones after it.
|
||||
*/
|
||||
return (af->pending ? bufchain_size(&af->inbuffer) : 0);
|
||||
}
|
||||
|
||||
static void agentf_send_eof(Channel *chan)
|
||||
{
|
||||
assert(chan->vt == &agentf_channelvt);
|
||||
agentf *af = FROMFIELD(chan, agentf, chan);
|
||||
|
||||
af->rcvd_eof = TRUE;
|
||||
|
||||
/* Call try_forward, which will respond to the EOF now if
|
||||
* appropriate, or wait until the queue of outstanding requests is
|
||||
* dealt with if not. */
|
||||
agentf_try_forward(af);
|
||||
}
|
||||
|
||||
static char *agentf_log_close_msg(Channel *chan)
|
||||
{
|
||||
return dupstr("Agent-forwarding connection closed");
|
||||
}
|
||||
|
||||
static void agentf_set_input_wanted(Channel *chan, int wanted)
|
||||
{
|
||||
assert(chan->vt == &agentf_channelvt);
|
||||
agentf *af = FROMFIELD(chan, agentf, chan);
|
||||
|
||||
af->input_wanted = wanted;
|
||||
|
||||
/* Agent forwarding channels are buffer-managed by not asking the
|
||||
* agent questions if the SSH channel isn't accepting input. So if
|
||||
* it's started again, we should ask a question if we have one
|
||||
* pending.. */
|
||||
if (wanted)
|
||||
agentf_try_forward(af);
|
||||
}
|
2
defs.h
2
defs.h
|
@ -53,6 +53,8 @@ typedef struct Frontend Frontend;
|
|||
|
||||
typedef struct ssh_tag *Ssh;
|
||||
|
||||
typedef struct Channel Channel;
|
||||
|
||||
/* Note indirection: for historical reasons (it used to be closer to
|
||||
* the OS socket type), the type that most code uses for a socket is
|
||||
* 'Socket', not 'Socket *'. So an implementation of Socket or Plug
|
||||
|
|
143
portfwd.c
143
portfwd.c
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include "putty.h"
|
||||
#include "ssh.h"
|
||||
#include "sshchan.h"
|
||||
|
||||
/*
|
||||
* Enumeration of values that live in the 'socks_state' field of
|
||||
|
@ -21,12 +22,12 @@ typedef enum {
|
|||
SOCKS_5_CONNECT /* expect a SOCKS 5 connection message */
|
||||
} SocksState;
|
||||
|
||||
struct PortForwarding {
|
||||
typedef struct PortForwarding {
|
||||
struct ssh_channel *c; /* channel structure held by ssh.c */
|
||||
Ssh ssh; /* instance of SSH backend itself */
|
||||
/* Note that ssh need not be filled in if c is non-NULL */
|
||||
Socket s;
|
||||
int throttled, throttle_override;
|
||||
int input_wanted;
|
||||
int ready;
|
||||
SocksState socks_state;
|
||||
/*
|
||||
|
@ -44,7 +45,8 @@ struct PortForwarding {
|
|||
size_t socksbuf_consumed;
|
||||
|
||||
const Plug_vtable *plugvt;
|
||||
};
|
||||
Channel chan;
|
||||
} PortForwarding;
|
||||
|
||||
struct PortListener {
|
||||
Ssh ssh; /* instance of SSH backend itself */
|
||||
|
@ -105,6 +107,8 @@ static void pfl_log(Plug plug, int type, SockAddr addr, int port,
|
|||
/* we have to dump these since we have no interface to logging.c */
|
||||
}
|
||||
|
||||
static void pfd_close(struct PortForwarding *pf);
|
||||
|
||||
static void pfd_closing(Plug plug, const char *error_msg, int error_code,
|
||||
int calling_back)
|
||||
{
|
||||
|
@ -142,10 +146,12 @@ static void pfl_closing(Plug plug, const char *error_msg, int error_code,
|
|||
pfl_terminate(pl);
|
||||
}
|
||||
|
||||
static void wrap_send_port_open(void *channel, const char *hostname, int port,
|
||||
Socket s)
|
||||
static struct ssh_channel *wrap_send_port_open(
|
||||
Ssh ssh, const char *hostname, int port, Socket s, Channel *chan)
|
||||
{
|
||||
char *peerinfo, *description;
|
||||
struct ssh_channel *toret;
|
||||
|
||||
peerinfo = sk_peer_info(s);
|
||||
if (peerinfo) {
|
||||
description = dupprintf("forwarding from %s", peerinfo);
|
||||
|
@ -153,8 +159,11 @@ static void wrap_send_port_open(void *channel, const char *hostname, int port,
|
|||
} else {
|
||||
description = dupstr("forwarding");
|
||||
}
|
||||
ssh_send_port_open(channel, hostname, port, description);
|
||||
|
||||
toret = ssh_send_port_open(ssh, hostname, port, description, chan);
|
||||
|
||||
sfree(description);
|
||||
return toret;
|
||||
}
|
||||
|
||||
static char *ipv4_to_string(unsigned ipv4)
|
||||
|
@ -396,21 +405,11 @@ static void pfd_receive(Plug plug, int urgent, char *data, int len)
|
|||
*/
|
||||
sk_set_frozen(pf->s, 1);
|
||||
|
||||
pf->c = new_sock_channel(pf->ssh, pf);
|
||||
if (pf->c == NULL) {
|
||||
pfd_close(pf);
|
||||
return;
|
||||
} else {
|
||||
/* asks to forward to the specified host/port for this */
|
||||
wrap_send_port_open(pf->c, pf->hostname, pf->port, pf->s);
|
||||
}
|
||||
}
|
||||
if (pf->ready) {
|
||||
if (sshfwd_write(pf->c, data, len) > 0) {
|
||||
pf->throttled = 1;
|
||||
sk_set_frozen(pf->s, 1);
|
||||
}
|
||||
pf->c = wrap_send_port_open(pf->ssh, pf->hostname, pf->port, pf->s,
|
||||
&pf->chan);
|
||||
}
|
||||
if (pf->ready)
|
||||
sshfwd_write(pf->c, data, len);
|
||||
}
|
||||
|
||||
static void pfd_sent(Plug plug, int bufsize)
|
||||
|
@ -429,6 +428,25 @@ static const Plug_vtable PortForwarding_plugvt = {
|
|||
NULL
|
||||
};
|
||||
|
||||
static void pfd_chan_free(Channel *chan);
|
||||
static void pfd_open_confirmation(Channel *chan);
|
||||
static void pfd_open_failure(Channel *chan, const char *errtext);
|
||||
static int pfd_send(Channel *chan, int is_stderr, const void *data, int len);
|
||||
static void pfd_send_eof(Channel *chan);
|
||||
static void pfd_set_input_wanted(Channel *chan, int wanted);
|
||||
static char *pfd_log_close_msg(Channel *chan);
|
||||
|
||||
static const struct ChannelVtable PortForwarding_channelvt = {
|
||||
pfd_chan_free,
|
||||
pfd_open_confirmation,
|
||||
pfd_open_failure,
|
||||
pfd_send,
|
||||
pfd_send_eof,
|
||||
pfd_set_input_wanted,
|
||||
pfd_log_close_msg,
|
||||
chan_no_eager_close,
|
||||
};
|
||||
|
||||
/*
|
||||
* Called when receiving a PORT OPEN from the server to make a
|
||||
* connection to a destination host.
|
||||
|
@ -436,8 +454,8 @@ static const Plug_vtable PortForwarding_plugvt = {
|
|||
* On success, returns NULL and fills in *pf_ret. On error, returns a
|
||||
* dynamically allocated error message string.
|
||||
*/
|
||||
char *pfd_connect(struct PortForwarding **pf_ret, char *hostname,int port,
|
||||
void *c, Conf *conf, int addressfamily)
|
||||
char *pfd_connect(Channel **chan_ret, char *hostname,int port,
|
||||
struct ssh_channel *c, Conf *conf, int addressfamily)
|
||||
{
|
||||
SockAddr addr;
|
||||
const char *err;
|
||||
|
@ -459,9 +477,12 @@ char *pfd_connect(struct PortForwarding **pf_ret, char *hostname,int port,
|
|||
/*
|
||||
* Open socket.
|
||||
*/
|
||||
pf = *pf_ret = new_portfwd_state();
|
||||
pf = new_portfwd_state();
|
||||
*chan_ret = &pf->chan;
|
||||
pf->plugvt = &PortForwarding_plugvt;
|
||||
pf->throttled = pf->throttle_override = 0;
|
||||
pf->chan.initial_fixed_window_size = 0;
|
||||
pf->chan.vt = &PortForwarding_channelvt;
|
||||
pf->input_wanted = TRUE;
|
||||
pf->ready = 1;
|
||||
pf->c = c;
|
||||
pf->ssh = NULL; /* we shouldn't need this */
|
||||
|
@ -474,7 +495,7 @@ char *pfd_connect(struct PortForwarding **pf_ret, char *hostname,int port,
|
|||
char *err_ret = dupstr(err);
|
||||
sk_close(pf->s);
|
||||
free_portfwd_state(pf);
|
||||
*pf_ret = NULL;
|
||||
*chan_ret = NULL;
|
||||
return err_ret;
|
||||
}
|
||||
|
||||
|
@ -495,6 +516,9 @@ static int pfl_accepting(Plug p, accept_fn_t constructor, accept_ctx_t ctx)
|
|||
pl = FROMFIELD(p, struct PortListener, plugvt);
|
||||
pf = new_portfwd_state();
|
||||
pf->plugvt = &PortForwarding_plugvt;
|
||||
pf->chan.initial_fixed_window_size = 0;
|
||||
pf->chan.vt = &PortForwarding_channelvt;
|
||||
pf->input_wanted = TRUE;
|
||||
|
||||
pf->c = NULL;
|
||||
pf->ssh = pl->ssh;
|
||||
|
@ -505,7 +529,7 @@ static int pfl_accepting(Plug p, accept_fn_t constructor, accept_ctx_t ctx)
|
|||
return err != NULL;
|
||||
}
|
||||
|
||||
pf->throttled = pf->throttle_override = 0;
|
||||
pf->input_wanted = TRUE;
|
||||
pf->ready = 0;
|
||||
|
||||
if (pl->is_dynamic) {
|
||||
|
@ -518,15 +542,8 @@ static int pfl_accepting(Plug p, accept_fn_t constructor, accept_ctx_t ctx)
|
|||
pf->socks_state = SOCKS_NONE;
|
||||
pf->hostname = dupstr(pl->hostname);
|
||||
pf->port = pl->port;
|
||||
pf->c = new_sock_channel(pl->ssh, pf);
|
||||
|
||||
if (pf->c == NULL) {
|
||||
free_portfwd_state(pf);
|
||||
return 1;
|
||||
} else {
|
||||
/* asks to forward to the specified host/port for this */
|
||||
wrap_send_port_open(pf->c, pf->hostname, pf->port, s);
|
||||
}
|
||||
pf->c = wrap_send_port_open(pl->ssh, pf->hostname, pf->port,
|
||||
s, &pf->chan);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -580,7 +597,12 @@ char *pfl_listen(char *desthost, int destport, char *srcaddr,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
void pfd_close(struct PortForwarding *pf)
|
||||
static char *pfd_log_close_msg(Channel *chan)
|
||||
{
|
||||
return dupstr("Forwarded port closed");
|
||||
}
|
||||
|
||||
static void pfd_close(struct PortForwarding *pf)
|
||||
{
|
||||
if (!pf)
|
||||
return;
|
||||
|
@ -601,43 +623,42 @@ void pfl_terminate(struct PortListener *pl)
|
|||
free_portlistener_state(pl);
|
||||
}
|
||||
|
||||
void pfd_unthrottle(struct PortForwarding *pf)
|
||||
static void pfd_set_input_wanted(Channel *chan, int wanted)
|
||||
{
|
||||
if (!pf)
|
||||
return;
|
||||
|
||||
pf->throttled = 0;
|
||||
sk_set_frozen(pf->s, pf->throttled || pf->throttle_override);
|
||||
assert(chan->vt == &PortForwarding_channelvt);
|
||||
PortForwarding *pf = FROMFIELD(chan, PortForwarding, chan);
|
||||
pf->input_wanted = wanted;
|
||||
sk_set_frozen(pf->s, !pf->input_wanted);
|
||||
}
|
||||
|
||||
void pfd_override_throttle(struct PortForwarding *pf, int enable)
|
||||
static void pfd_chan_free(Channel *chan)
|
||||
{
|
||||
if (!pf)
|
||||
return;
|
||||
|
||||
pf->throttle_override = enable;
|
||||
sk_set_frozen(pf->s, pf->throttled || pf->throttle_override);
|
||||
assert(chan->vt == &PortForwarding_channelvt);
|
||||
PortForwarding *pf = FROMFIELD(chan, PortForwarding, chan);
|
||||
pfd_close(pf);
|
||||
}
|
||||
|
||||
/*
|
||||
* Called to send data down the raw connection.
|
||||
*/
|
||||
int pfd_send(struct PortForwarding *pf, const void *data, int len)
|
||||
static int pfd_send(Channel *chan, int is_stderr, const void *data, int len)
|
||||
{
|
||||
if (pf == NULL)
|
||||
return 0;
|
||||
assert(chan->vt == &PortForwarding_channelvt);
|
||||
PortForwarding *pf = FROMFIELD(chan, PortForwarding, chan);
|
||||
return sk_write(pf->s, data, len);
|
||||
}
|
||||
|
||||
void pfd_send_eof(struct PortForwarding *pf)
|
||||
static void pfd_send_eof(Channel *chan)
|
||||
{
|
||||
assert(chan->vt == &PortForwarding_channelvt);
|
||||
PortForwarding *pf = FROMFIELD(chan, PortForwarding, chan);
|
||||
sk_write_eof(pf->s);
|
||||
}
|
||||
|
||||
void pfd_confirm(struct PortForwarding *pf)
|
||||
static void pfd_open_confirmation(Channel *chan)
|
||||
{
|
||||
if (pf == NULL)
|
||||
return;
|
||||
assert(chan->vt == &PortForwarding_channelvt);
|
||||
PortForwarding *pf = FROMFIELD(chan, PortForwarding, chan);
|
||||
|
||||
pf->ready = 1;
|
||||
sk_set_frozen(pf->s, 0);
|
||||
|
@ -649,3 +670,15 @@ void pfd_confirm(struct PortForwarding *pf)
|
|||
pf->socksbuf = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void pfd_open_failure(Channel *chan, const char *errtext)
|
||||
{
|
||||
assert(chan->vt == &PortForwarding_channelvt);
|
||||
PortForwarding *pf = FROMFIELD(chan, PortForwarding, chan);
|
||||
|
||||
char *msg = dupprintf(
|
||||
"Forwarded connection refused by server%s%s",
|
||||
errtext ? ": " : "", errtext ? errtext : "");
|
||||
logevent(ssh_get_frontend(pf->ssh), msg);
|
||||
sfree(msg);
|
||||
}
|
||||
|
|
783
ssh.c
783
ssh.c
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
31
ssh.h
31
ssh.h
|
@ -14,12 +14,12 @@ extern void sshfwd_write_eof(struct ssh_channel *c);
|
|||
extern void sshfwd_unclean_close(struct ssh_channel *c, const char *err);
|
||||
extern void sshfwd_unthrottle(struct ssh_channel *c, int bufsize);
|
||||
Conf *sshfwd_get_conf(struct ssh_channel *c);
|
||||
void sshfwd_window_override_removed(struct ssh_channel *c);
|
||||
void sshfwd_x11_sharing_handover(struct ssh_channel *c,
|
||||
void *share_cs, void *share_chan,
|
||||
const char *peer_addr, int peer_port,
|
||||
int endian, int protomajor, int protominor,
|
||||
const void *initial_data, int initial_len);
|
||||
void sshfwd_x11_is_local(struct ssh_channel *c);
|
||||
|
||||
/*
|
||||
* Buffer management constants. There are several of these for
|
||||
|
@ -185,6 +185,8 @@ void share_setup_x11_channel(void *csv, void *chanv,
|
|||
int protomajor, int protominor,
|
||||
const void *initial_data, int initial_len);
|
||||
|
||||
Frontend *ssh_get_frontend(Ssh ssh);
|
||||
|
||||
/*
|
||||
* Useful thing.
|
||||
*/
|
||||
|
@ -665,19 +667,12 @@ void logevent(Frontend *, const char *);
|
|||
struct PortForwarding;
|
||||
|
||||
/* Allocate and register a new channel for port forwarding */
|
||||
void *new_sock_channel(Ssh ssh, struct PortForwarding *pf);
|
||||
void ssh_send_port_open(void *channel, const char *hostname, int port,
|
||||
const char *org);
|
||||
struct ssh_channel *ssh_send_port_open(Ssh ssh, const char *hostname, int port,
|
||||
const char *org, Channel *chan);
|
||||
|
||||
/* Exports from portfwd.c */
|
||||
extern char *pfd_connect(struct PortForwarding **pf, char *hostname, int port,
|
||||
void *c, Conf *conf, int addressfamily);
|
||||
extern void pfd_close(struct PortForwarding *);
|
||||
extern int pfd_send(struct PortForwarding *, const void *data, int len);
|
||||
extern void pfd_send_eof(struct PortForwarding *);
|
||||
extern void pfd_confirm(struct PortForwarding *);
|
||||
extern void pfd_unthrottle(struct PortForwarding *);
|
||||
extern void pfd_override_throttle(struct PortForwarding *, int enable);
|
||||
extern char *pfd_connect(Channel **chan_ret, char *hostname, int port,
|
||||
struct ssh_channel *c, Conf *conf, int addressfamily);
|
||||
struct PortListener;
|
||||
/* desthost == NULL indicates dynamic (SOCKS) port forwarding */
|
||||
extern char *pfl_listen(char *desthost, int destport, char *srcaddr,
|
||||
|
@ -750,13 +745,9 @@ extern struct X11Display *x11_setup_display(const char *display, Conf *);
|
|||
void x11_free_display(struct X11Display *disp);
|
||||
struct X11FakeAuth *x11_invent_fake_auth(tree234 *t, int authtype);
|
||||
void x11_free_fake_auth(struct X11FakeAuth *auth);
|
||||
struct X11Connection; /* opaque outside x11fwd.c */
|
||||
struct X11Connection *x11_init(tree234 *authtree, void *, const char *, int);
|
||||
extern void x11_close(struct X11Connection *);
|
||||
extern int x11_send(struct X11Connection *, const void *, int);
|
||||
extern void x11_send_eof(struct X11Connection *s);
|
||||
extern void x11_unthrottle(struct X11Connection *s);
|
||||
extern void x11_override_throttle(struct X11Connection *s, int enable);
|
||||
Channel *x11_new_channel(tree234 *authtree, struct ssh_channel *c,
|
||||
const char *peeraddr, int peerport,
|
||||
int connection_sharing_possible);
|
||||
char *x11_display(const char *display);
|
||||
/* Platform-dependent X11 functions */
|
||||
extern void platform_get_x11_auth(struct X11Display *display, Conf *);
|
||||
|
@ -786,6 +777,8 @@ void x11_get_auth_from_authfile(struct X11Display *display,
|
|||
int x11_identify_auth_proto(ptrlen protoname);
|
||||
void *x11_dehexify(ptrlen hex, int *outlen);
|
||||
|
||||
Channel *agentf_new(struct ssh_channel *c);
|
||||
|
||||
Bignum copybn(Bignum b);
|
||||
Bignum bn_power_2(int n);
|
||||
void bn_restore_invariant(Bignum b);
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Abstraction of the various ways to handle the local end of an SSH
|
||||
* connection-layer channel.
|
||||
*/
|
||||
|
||||
#ifndef PUTTY_SSHCHAN_H
|
||||
#define PUTTY_SSHCHAN_H
|
||||
|
||||
struct ChannelVtable {
|
||||
void (*free)(Channel *);
|
||||
|
||||
/* Called for channel types that were created at the same time as
|
||||
* we sent an outgoing CHANNEL_OPEN, when the confirmation comes
|
||||
* back from the server indicating that the channel has been
|
||||
* opened, or the failure message indicating that it hasn't,
|
||||
* respectively. In the latter case, this must _not_ free the
|
||||
* Channel structure - the client will call the free method
|
||||
* separately. But it might do logging or other local cleanup. */
|
||||
void (*open_confirmation)(Channel *);
|
||||
void (*open_failed)(Channel *, const char *error_text);
|
||||
|
||||
int (*send)(Channel *, int is_stderr, const void *buf, int len);
|
||||
void (*send_eof)(Channel *);
|
||||
void (*set_input_wanted)(Channel *, int wanted);
|
||||
|
||||
char *(*log_close_msg)(Channel *);
|
||||
|
||||
int (*want_close)(Channel *, int sent_local_eof, int rcvd_remote_eof);
|
||||
};
|
||||
|
||||
struct Channel {
|
||||
const struct ChannelVtable *vt;
|
||||
unsigned initial_fixed_window_size;
|
||||
};
|
||||
|
||||
#define chan_free(ch) ((ch)->vt->free(ch))
|
||||
#define chan_open_confirmation(ch) ((ch)->vt->open_confirmation(ch))
|
||||
#define chan_open_failed(ch, err) ((ch)->vt->open_failed(ch, err))
|
||||
#define chan_send(ch, err, buf, len) ((ch)->vt->send(ch, err, buf, len))
|
||||
#define chan_send_eof(ch) ((ch)->vt->send_eof(ch))
|
||||
#define chan_set_input_wanted(ch, wanted) \
|
||||
((ch)->vt->set_input_wanted(ch, wanted))
|
||||
#define chan_log_close_msg(ch) ((ch)->vt->send_eof(ch))
|
||||
#define chan_want_close(ch, leof, reof) ((ch)->vt->want_close(ch, leof, reof))
|
||||
|
||||
/*
|
||||
* Reusable methods you can put in vtables to give default handling of
|
||||
* some of those functions.
|
||||
*/
|
||||
|
||||
/* open_confirmation / open_failed for any channel it doesn't apply to */
|
||||
void chan_remotely_opened_confirmation(Channel *chan);
|
||||
void chan_remotely_opened_failure(Channel *chan, const char *errtext);
|
||||
|
||||
/* want_close for any channel that wants the default behaviour of not
|
||||
* closing until both directions have had an EOF */
|
||||
int chan_no_eager_close(Channel *, int, int);
|
||||
|
||||
#endif /* PUTTY_SSHCHAN_H */
|
|
@ -161,13 +161,17 @@ int sshfwd_write(struct ssh_channel *c, const void *data, int len)
|
|||
void sshfwd_write_eof(struct ssh_channel *c) { }
|
||||
void sshfwd_unclean_close(struct ssh_channel *c, const char *err) { }
|
||||
void sshfwd_unthrottle(struct ssh_channel *c, int bufsize) {}
|
||||
void sshfwd_window_override_removed(struct ssh_channel *c) { }
|
||||
void chan_remotely_opened_confirmation(Channel *chan) { }
|
||||
void chan_remotely_opened_failure(Channel *chan, const char *err) { }
|
||||
int chan_no_eager_close(Channel *chan, int s, int r) { return FALSE; }
|
||||
|
||||
Conf *sshfwd_get_conf(struct ssh_channel *c) { return NULL; }
|
||||
void sshfwd_x11_sharing_handover(struct ssh_channel *c,
|
||||
void *share_cs, void *share_chan,
|
||||
const char *peer_addr, int peer_port,
|
||||
int endian, int protomajor, int protominor,
|
||||
const void *initial_data, int initial_len) {}
|
||||
void sshfwd_x11_is_local(struct ssh_channel *c) {}
|
||||
|
||||
/*
|
||||
* These functions are part of the plug for our connection to the X
|
||||
|
|
93
x11fwd.c
93
x11fwd.c
|
@ -9,6 +9,7 @@
|
|||
|
||||
#include "putty.h"
|
||||
#include "ssh.h"
|
||||
#include "sshchan.h"
|
||||
#include "tree234.h"
|
||||
|
||||
#define GET_16BIT(endian, cp) \
|
||||
|
@ -26,7 +27,7 @@ struct XDMSeen {
|
|||
unsigned char clientid[6];
|
||||
};
|
||||
|
||||
struct X11Connection {
|
||||
typedef struct X11Connection {
|
||||
unsigned char firstpkt[12]; /* first X data packet */
|
||||
tree234 *authtree;
|
||||
struct X11Display *disp;
|
||||
|
@ -34,7 +35,7 @@ struct X11Connection {
|
|||
unsigned char *auth_data;
|
||||
int data_read, auth_plen, auth_psize, auth_dlen, auth_dsize;
|
||||
int verified;
|
||||
int throttled, throttle_override;
|
||||
int input_wanted;
|
||||
int no_data_sent_to_x_client;
|
||||
char *peer_addr;
|
||||
int peer_port;
|
||||
|
@ -42,7 +43,8 @@ struct X11Connection {
|
|||
Socket s;
|
||||
|
||||
const Plug_vtable *plugvt;
|
||||
};
|
||||
Channel chan;
|
||||
} X11Connection;
|
||||
|
||||
static int xdmseen_cmp(void *a, void *b)
|
||||
{
|
||||
|
@ -666,11 +668,8 @@ static void x11_receive(Plug plug, int urgent, char *data, int len)
|
|||
struct X11Connection *xconn = FROMFIELD(
|
||||
plug, struct X11Connection, plugvt);
|
||||
|
||||
if (sshfwd_write(xconn->c, data, len) > 0) {
|
||||
xconn->throttled = 1;
|
||||
xconn->no_data_sent_to_x_client = FALSE;
|
||||
sk_set_frozen(xconn->s, 1);
|
||||
}
|
||||
xconn->no_data_sent_to_x_client = FALSE;
|
||||
sshfwd_write(xconn->c, data, len);
|
||||
}
|
||||
|
||||
static void x11_sent(Plug plug, int bufsize)
|
||||
|
@ -707,12 +706,30 @@ static const Plug_vtable X11Connection_plugvt = {
|
|||
NULL
|
||||
};
|
||||
|
||||
static void x11_chan_free(Channel *chan);
|
||||
static int x11_send(Channel *chan, int is_stderr, const void *vdata, int len);
|
||||
static void x11_send_eof(Channel *chan);
|
||||
static void x11_set_input_wanted(Channel *chan, int wanted);
|
||||
static char *x11_log_close_msg(Channel *chan);
|
||||
|
||||
static const struct ChannelVtable X11Connection_channelvt = {
|
||||
x11_chan_free,
|
||||
chan_remotely_opened_confirmation,
|
||||
chan_remotely_opened_failure,
|
||||
x11_send,
|
||||
x11_send_eof,
|
||||
x11_set_input_wanted,
|
||||
x11_log_close_msg,
|
||||
chan_no_eager_close,
|
||||
};
|
||||
|
||||
/*
|
||||
* Called to set up the X11Connection structure, though this does not
|
||||
* yet connect to an actual server.
|
||||
*/
|
||||
struct X11Connection *x11_init(tree234 *authtree, void *c,
|
||||
const char *peeraddr, int peerport)
|
||||
Channel *x11_new_channel(tree234 *authtree, struct ssh_channel *c,
|
||||
const char *peeraddr, int peerport,
|
||||
int connection_sharing_possible)
|
||||
{
|
||||
struct X11Connection *xconn;
|
||||
|
||||
|
@ -721,11 +738,14 @@ struct X11Connection *x11_init(tree234 *authtree, void *c,
|
|||
*/
|
||||
xconn = snew(struct X11Connection);
|
||||
xconn->plugvt = &X11Connection_plugvt;
|
||||
xconn->chan.vt = &X11Connection_channelvt;
|
||||
xconn->chan.initial_fixed_window_size =
|
||||
(connection_sharing_possible ? 128 : 0);
|
||||
xconn->auth_protocol = NULL;
|
||||
xconn->authtree = authtree;
|
||||
xconn->verified = 0;
|
||||
xconn->data_read = 0;
|
||||
xconn->throttled = xconn->throttle_override = 0;
|
||||
xconn->input_wanted = TRUE;
|
||||
xconn->no_data_sent_to_x_client = TRUE;
|
||||
xconn->c = c;
|
||||
|
||||
|
@ -746,13 +766,13 @@ struct X11Connection *x11_init(tree234 *authtree, void *c,
|
|||
xconn->peer_addr = peeraddr ? dupstr(peeraddr) : NULL;
|
||||
xconn->peer_port = peerport;
|
||||
|
||||
return xconn;
|
||||
return &xconn->chan;
|
||||
}
|
||||
|
||||
void x11_close(struct X11Connection *xconn)
|
||||
static void x11_chan_free(Channel *chan)
|
||||
{
|
||||
if (!xconn)
|
||||
return;
|
||||
assert(chan->vt == &X11Connection_channelvt);
|
||||
X11Connection *xconn = FROMFIELD(chan, X11Connection, chan);
|
||||
|
||||
if (xconn->auth_protocol) {
|
||||
sfree(xconn->auth_protocol);
|
||||
|
@ -766,24 +786,14 @@ void x11_close(struct X11Connection *xconn)
|
|||
sfree(xconn);
|
||||
}
|
||||
|
||||
void x11_unthrottle(struct X11Connection *xconn)
|
||||
static void x11_set_input_wanted(Channel *chan, int wanted)
|
||||
{
|
||||
if (!xconn)
|
||||
return;
|
||||
assert(chan->vt == &X11Connection_channelvt);
|
||||
X11Connection *xconn = FROMFIELD(chan, X11Connection, chan);
|
||||
|
||||
xconn->throttled = 0;
|
||||
xconn->input_wanted = wanted;
|
||||
if (xconn->s)
|
||||
sk_set_frozen(xconn->s, xconn->throttled || xconn->throttle_override);
|
||||
}
|
||||
|
||||
void x11_override_throttle(struct X11Connection *xconn, int enable)
|
||||
{
|
||||
if (!xconn)
|
||||
return;
|
||||
|
||||
xconn->throttle_override = enable;
|
||||
if (xconn->s)
|
||||
sk_set_frozen(xconn->s, xconn->throttled || xconn->throttle_override);
|
||||
sk_set_frozen(xconn->s, !xconn->input_wanted);
|
||||
}
|
||||
|
||||
static void x11_send_init_error(struct X11Connection *xconn,
|
||||
|
@ -831,13 +841,12 @@ static int x11_parse_ip(const char *addr_string, unsigned long *ip)
|
|||
/*
|
||||
* Called to send data down the raw connection.
|
||||
*/
|
||||
int x11_send(struct X11Connection *xconn, const void *vdata, int len)
|
||||
static int x11_send(Channel *chan, int is_stderr, const void *vdata, int len)
|
||||
{
|
||||
assert(chan->vt == &X11Connection_channelvt);
|
||||
X11Connection *xconn = FROMFIELD(chan, X11Connection, chan);
|
||||
const char *data = (const char *)vdata;
|
||||
|
||||
if (!xconn)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Read the first packet.
|
||||
*/
|
||||
|
@ -914,7 +923,8 @@ int x11_send(struct X11Connection *xconn, const void *vdata, int len)
|
|||
/*
|
||||
* If this auth points to a connection-sharing downstream
|
||||
* rather than an X display we know how to connect to
|
||||
* directly, pass it off to the sharing module now.
|
||||
* directly, pass it off to the sharing module now. (This will
|
||||
* have the side effect of freeing xconn.)
|
||||
*/
|
||||
if (auth_matched->share_cs) {
|
||||
sshfwd_x11_sharing_handover(xconn->c, auth_matched->share_cs,
|
||||
|
@ -929,7 +939,8 @@ int x11_send(struct X11Connection *xconn, const void *vdata, int len)
|
|||
* Now we know we're going to accept the connection, and what
|
||||
* X display to connect to. Actually connect to it.
|
||||
*/
|
||||
sshfwd_x11_is_local(xconn->c);
|
||||
xconn->chan.initial_fixed_window_size = 0;
|
||||
sshfwd_window_override_removed(xconn->c);
|
||||
xconn->disp = auth_matched->disp;
|
||||
xconn->s = new_connection(sk_addr_dup(xconn->disp->addr),
|
||||
xconn->disp->realhost, xconn->disp->port,
|
||||
|
@ -984,8 +995,11 @@ int x11_send(struct X11Connection *xconn, const void *vdata, int len)
|
|||
return sk_write(xconn->s, data, len);
|
||||
}
|
||||
|
||||
void x11_send_eof(struct X11Connection *xconn)
|
||||
static void x11_send_eof(Channel *chan)
|
||||
{
|
||||
assert(chan->vt == &X11Connection_channelvt);
|
||||
X11Connection *xconn = FROMFIELD(chan, X11Connection, chan);
|
||||
|
||||
if (xconn->s) {
|
||||
sk_write_eof(xconn->s);
|
||||
} else {
|
||||
|
@ -1000,6 +1014,11 @@ void x11_send_eof(struct X11Connection *xconn)
|
|||
}
|
||||
}
|
||||
|
||||
static char *x11_log_close_msg(Channel *chan)
|
||||
{
|
||||
return dupstr("Forwarded X11 connection terminated");
|
||||
}
|
||||
|
||||
/*
|
||||
* Utility functions used by connection sharing to convert textual
|
||||
* representations of an X11 auth protocol name + hex cookie into our
|
||||
|
|
Загрузка…
Ссылка в новой задаче