mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-01-08 08:58:00 +00:00
b4e1bca2c3
This is a sweeping change applied across the whole code base by a spot of Emacs Lisp. Now, everywhere I declare a vtable filled with function pointers (and the occasional const data member), all the members of the vtable structure are initialised by name using the '.fieldname = value' syntax introduced in C99. We were already using this syntax for a handful of things in the new key-generation progress report system, so it's not new to the code base as a whole. The advantage is that now, when a vtable only declares a subset of the available fields, I can initialise the rest to NULL or zero just by leaving them out. This is most dramatic in a couple of the outlying vtables in things like psocks (which has a ConnectionLayerVtable containing only one non-NULL method), but less dramatically, it means that the new 'flags' field in BackendVtable can be completely left out of every backend definition except for the SUPDUP one which defines it to a nonzero value. Similarly, the test_for_upstream method only used by SSH doesn't have to be mentioned in the rest of the backends; network Plugs for listening sockets don't have to explicitly null out 'receive' and 'sent', and vice versa for 'accepting', and so on. While I'm at it, I've normalised the declarations so they don't use the unnecessarily verbose 'struct' keyword. Also a handful of them weren't const; now they are.
317 lines
14 KiB
C
317 lines
14 KiB
C
/*
|
|
* Abstraction of the various ways to handle the local end of an SSH
|
|
* connection-layer channel.
|
|
*/
|
|
|
|
#ifndef PUTTY_SSHCHAN_H
|
|
#define PUTTY_SSHCHAN_H
|
|
|
|
typedef struct ChannelVtable ChannelVtable;
|
|
|
|
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);
|
|
|
|
size_t (*send)(Channel *, bool is_stderr, const void *buf, size_t len);
|
|
void (*send_eof)(Channel *);
|
|
void (*set_input_wanted)(Channel *, bool wanted);
|
|
|
|
char *(*log_close_msg)(Channel *);
|
|
|
|
bool (*want_close)(Channel *, bool sent_local_eof, bool rcvd_remote_eof);
|
|
|
|
/* A method for every channel request we know of. All of these
|
|
* return true for success or false for failure. */
|
|
bool (*rcvd_exit_status)(Channel *, int status);
|
|
bool (*rcvd_exit_signal)(
|
|
Channel *chan, ptrlen signame, bool core_dumped, ptrlen msg);
|
|
bool (*rcvd_exit_signal_numeric)(
|
|
Channel *chan, int signum, bool core_dumped, ptrlen msg);
|
|
bool (*run_shell)(Channel *chan);
|
|
bool (*run_command)(Channel *chan, ptrlen command);
|
|
bool (*run_subsystem)(Channel *chan, ptrlen subsys);
|
|
bool (*enable_x11_forwarding)(
|
|
Channel *chan, bool oneshot, ptrlen authproto, ptrlen authdata,
|
|
unsigned screen_number);
|
|
bool (*enable_agent_forwarding)(Channel *chan);
|
|
bool (*allocate_pty)(
|
|
Channel *chan, ptrlen termtype, unsigned width, unsigned height,
|
|
unsigned pixwidth, unsigned pixheight, struct ssh_ttymodes modes);
|
|
bool (*set_env)(Channel *chan, ptrlen var, ptrlen value);
|
|
bool (*send_break)(Channel *chan, unsigned length);
|
|
bool (*send_signal)(Channel *chan, ptrlen signame);
|
|
bool (*change_window_size)(
|
|
Channel *chan, unsigned width, unsigned height,
|
|
unsigned pixwidth, unsigned pixheight);
|
|
|
|
/* A method for signalling success/failure responses to channel
|
|
* requests initiated from the SshChannel vtable with want_reply
|
|
* true. */
|
|
void (*request_response)(Channel *, bool success);
|
|
};
|
|
|
|
struct Channel {
|
|
const struct ChannelVtable *vt;
|
|
unsigned initial_fixed_window_size;
|
|
};
|
|
|
|
static inline void chan_free(Channel *ch)
|
|
{ ch->vt->free(ch); }
|
|
static inline void chan_open_confirmation(Channel *ch)
|
|
{ ch->vt->open_confirmation(ch); }
|
|
static inline void chan_open_failed(Channel *ch, const char *err)
|
|
{ ch->vt->open_failed(ch, err); }
|
|
static inline size_t chan_send(
|
|
Channel *ch, bool err, const void *buf, size_t len)
|
|
{ return ch->vt->send(ch, err, buf, len); }
|
|
static inline void chan_send_eof(Channel *ch)
|
|
{ ch->vt->send_eof(ch); }
|
|
static inline void chan_set_input_wanted(Channel *ch, bool wanted)
|
|
{ ch->vt->set_input_wanted(ch, wanted); }
|
|
static inline char *chan_log_close_msg(Channel *ch)
|
|
{ return ch->vt->log_close_msg(ch); }
|
|
static inline bool chan_want_close(Channel *ch, bool leof, bool reof)
|
|
{ return ch->vt->want_close(ch, leof, reof); }
|
|
static inline bool chan_rcvd_exit_status(Channel *ch, int status)
|
|
{ return ch->vt->rcvd_exit_status(ch, status); }
|
|
static inline bool chan_rcvd_exit_signal(
|
|
Channel *ch, ptrlen sig, bool core, ptrlen msg)
|
|
{ return ch->vt->rcvd_exit_signal(ch, sig, core, msg); }
|
|
static inline bool chan_rcvd_exit_signal_numeric(
|
|
Channel *ch, int sig, bool core, ptrlen msg)
|
|
{ return ch->vt->rcvd_exit_signal_numeric(ch, sig, core, msg); }
|
|
static inline bool chan_run_shell(Channel *ch)
|
|
{ return ch->vt->run_shell(ch); }
|
|
static inline bool chan_run_command(Channel *ch, ptrlen cmd)
|
|
{ return ch->vt->run_command(ch, cmd); }
|
|
static inline bool chan_run_subsystem(Channel *ch, ptrlen subsys)
|
|
{ return ch->vt->run_subsystem(ch, subsys); }
|
|
static inline bool chan_enable_x11_forwarding(
|
|
Channel *ch, bool oneshot, ptrlen ap, ptrlen ad, unsigned scr)
|
|
{ return ch->vt->enable_x11_forwarding(ch, oneshot, ap, ad, scr); }
|
|
static inline bool chan_enable_agent_forwarding(Channel *ch)
|
|
{ return ch->vt->enable_agent_forwarding(ch); }
|
|
static inline bool chan_allocate_pty(
|
|
Channel *ch, ptrlen termtype, unsigned w, unsigned h,
|
|
unsigned pw, unsigned ph, struct ssh_ttymodes modes)
|
|
{ return ch->vt->allocate_pty(ch, termtype, w, h, pw, ph, modes); }
|
|
static inline bool chan_set_env(Channel *ch, ptrlen var, ptrlen value)
|
|
{ return ch->vt->set_env(ch, var, value); }
|
|
static inline bool chan_send_break(Channel *ch, unsigned length)
|
|
{ return ch->vt->send_break(ch, length); }
|
|
static inline bool chan_send_signal(Channel *ch, ptrlen signame)
|
|
{ return ch->vt->send_signal(ch, signame); }
|
|
static inline bool chan_change_window_size(
|
|
Channel *ch, unsigned w, unsigned h, unsigned pw, unsigned ph)
|
|
{ return ch->vt->change_window_size(ch, w, h, pw, ph); }
|
|
static inline void chan_request_response(Channel *ch, bool success)
|
|
{ ch->vt->request_response(ch, success); }
|
|
|
|
/*
|
|
* 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 */
|
|
bool chan_default_want_close(Channel *, bool, bool);
|
|
|
|
/* default implementations that refuse all the channel requests */
|
|
bool chan_no_exit_status(Channel *, int);
|
|
bool chan_no_exit_signal(Channel *, ptrlen, bool, ptrlen);
|
|
bool chan_no_exit_signal_numeric(Channel *, int, bool, ptrlen);
|
|
bool chan_no_run_shell(Channel *chan);
|
|
bool chan_no_run_command(Channel *chan, ptrlen command);
|
|
bool chan_no_run_subsystem(Channel *chan, ptrlen subsys);
|
|
bool chan_no_enable_x11_forwarding(
|
|
Channel *chan, bool oneshot, ptrlen authproto, ptrlen authdata,
|
|
unsigned screen_number);
|
|
bool chan_no_enable_agent_forwarding(Channel *chan);
|
|
bool chan_no_allocate_pty(
|
|
Channel *chan, ptrlen termtype, unsigned width, unsigned height,
|
|
unsigned pixwidth, unsigned pixheight, struct ssh_ttymodes modes);
|
|
bool chan_no_set_env(Channel *chan, ptrlen var, ptrlen value);
|
|
bool chan_no_send_break(Channel *chan, unsigned length);
|
|
bool chan_no_send_signal(Channel *chan, ptrlen signame);
|
|
bool chan_no_change_window_size(
|
|
Channel *chan, unsigned width, unsigned height,
|
|
unsigned pixwidth, unsigned pixheight);
|
|
|
|
/* default implementation that never expects to receive a response */
|
|
void chan_no_request_response(Channel *, bool);
|
|
|
|
/*
|
|
* Constructor for a trivial do-nothing implementation of
|
|
* ChannelVtable. Used for 'zombie' channels, i.e. channels whose
|
|
* proper local source of data has been shut down or otherwise stopped
|
|
* existing, but the SSH side is still there and needs some kind of a
|
|
* Channel implementation to talk to. In particular, the want_close
|
|
* method for this channel always returns 'yes, please close this
|
|
* channel asap', regardless of whether local and/or remote EOF have
|
|
* been sent - indeed, even if _neither_ has.
|
|
*/
|
|
Channel *zombiechan_new(void);
|
|
|
|
/* ----------------------------------------------------------------------
|
|
* This structure is owned by an SSH connection layer, and identifies
|
|
* the connection layer's end of the channel, for the Channel
|
|
* implementation to talk back to.
|
|
*/
|
|
|
|
typedef struct SshChannelVtable SshChannelVtable;
|
|
|
|
struct SshChannelVtable {
|
|
size_t (*write)(SshChannel *c, bool is_stderr, const void *, size_t);
|
|
void (*write_eof)(SshChannel *c);
|
|
void (*initiate_close)(SshChannel *c, const char *err);
|
|
void (*unthrottle)(SshChannel *c, size_t bufsize);
|
|
Conf *(*get_conf)(SshChannel *c);
|
|
void (*window_override_removed)(SshChannel *c);
|
|
void (*x11_sharing_handover)(SshChannel *c,
|
|
ssh_sharing_connstate *share_cs,
|
|
share_channel *share_chan,
|
|
const char *peer_addr, int peer_port,
|
|
int endian, int protomajor, int protominor,
|
|
const void *initial_data, int initial_len);
|
|
|
|
/*
|
|
* All the outgoing channel requests we support. Each one has a
|
|
* want_reply flag, which will cause a callback to
|
|
* chan_request_response when the result is available.
|
|
*
|
|
* The ones that return 'bool' use it to indicate that the SSH
|
|
* protocol in use doesn't support this request at all.
|
|
*
|
|
* (It's also intentional that not all of them have a want_reply
|
|
* flag: the ones that don't are because SSH-1 has no method for
|
|
* signalling success or failure of that request, or because we
|
|
* wouldn't do anything usefully different with the reply in any
|
|
* case.)
|
|
*/
|
|
void (*send_exit_status)(SshChannel *c, int status);
|
|
void (*send_exit_signal)(
|
|
SshChannel *c, ptrlen signame, bool core_dumped, ptrlen msg);
|
|
void (*send_exit_signal_numeric)(
|
|
SshChannel *c, int signum, bool core_dumped, ptrlen msg);
|
|
void (*request_x11_forwarding)(
|
|
SshChannel *c, bool want_reply, const char *authproto,
|
|
const char *authdata, int screen_number, bool oneshot);
|
|
void (*request_agent_forwarding)(
|
|
SshChannel *c, bool want_reply);
|
|
void (*request_pty)(
|
|
SshChannel *c, bool want_reply, Conf *conf, int w, int h);
|
|
bool (*send_env_var)(
|
|
SshChannel *c, bool want_reply, const char *var, const char *value);
|
|
void (*start_shell)(
|
|
SshChannel *c, bool want_reply);
|
|
void (*start_command)(
|
|
SshChannel *c, bool want_reply, const char *command);
|
|
bool (*start_subsystem)(
|
|
SshChannel *c, bool want_reply, const char *subsystem);
|
|
bool (*send_serial_break)(
|
|
SshChannel *c, bool want_reply, int length); /* length=0 for default */
|
|
bool (*send_signal)(
|
|
SshChannel *c, bool want_reply, const char *signame);
|
|
void (*send_terminal_size_change)(
|
|
SshChannel *c, int w, int h);
|
|
void (*hint_channel_is_simple)(SshChannel *c);
|
|
};
|
|
|
|
struct SshChannel {
|
|
const struct SshChannelVtable *vt;
|
|
ConnectionLayer *cl;
|
|
};
|
|
|
|
static inline size_t sshfwd_write_ext(
|
|
SshChannel *c, bool is_stderr, const void *data, size_t len)
|
|
{ return c->vt->write(c, is_stderr, data, len); }
|
|
static inline size_t sshfwd_write(SshChannel *c, const void *data, size_t len)
|
|
{ return sshfwd_write_ext(c, false, data, len); }
|
|
static inline void sshfwd_write_eof(SshChannel *c)
|
|
{ c->vt->write_eof(c); }
|
|
static inline void sshfwd_initiate_close(SshChannel *c, const char *err)
|
|
{ c->vt->initiate_close(c, err); }
|
|
static inline void sshfwd_unthrottle(SshChannel *c, size_t bufsize)
|
|
{ c->vt->unthrottle(c, bufsize); }
|
|
static inline Conf *sshfwd_get_conf(SshChannel *c)
|
|
{ return c->vt->get_conf(c); }
|
|
static inline void sshfwd_window_override_removed(SshChannel *c)
|
|
{ c->vt->window_override_removed(c); }
|
|
static inline void sshfwd_x11_sharing_handover(
|
|
SshChannel *c, ssh_sharing_connstate *cs, share_channel *sch,
|
|
const char *addr, int port, int endian, int maj, int min,
|
|
const void *idata, int ilen)
|
|
{ c->vt->x11_sharing_handover(c, cs, sch, addr, port, endian,
|
|
maj, min, idata, ilen); }
|
|
static inline void sshfwd_send_exit_status(SshChannel *c, int status)
|
|
{ c->vt->send_exit_status(c, status); }
|
|
static inline void sshfwd_send_exit_signal(
|
|
SshChannel *c, ptrlen signame, bool core_dumped, ptrlen msg)
|
|
{ c->vt->send_exit_signal(c, signame, core_dumped, msg); }
|
|
static inline void sshfwd_send_exit_signal_numeric(
|
|
SshChannel *c, int signum, bool core_dumped, ptrlen msg)
|
|
{ c->vt->send_exit_signal_numeric(c, signum, core_dumped, msg); }
|
|
static inline void sshfwd_request_x11_forwarding(
|
|
SshChannel *c, bool want_reply, const char *proto,
|
|
const char *data, int scr, bool once)
|
|
{ c->vt->request_x11_forwarding(c, want_reply, proto, data, scr, once); }
|
|
static inline void sshfwd_request_agent_forwarding(
|
|
SshChannel *c, bool want_reply)
|
|
{ c->vt->request_agent_forwarding(c, want_reply); }
|
|
static inline void sshfwd_request_pty(
|
|
SshChannel *c, bool want_reply, Conf *conf, int w, int h)
|
|
{ c->vt->request_pty(c, want_reply, conf, w, h); }
|
|
static inline bool sshfwd_send_env_var(
|
|
SshChannel *c, bool want_reply, const char *var, const char *value)
|
|
{ return c->vt->send_env_var(c, want_reply, var, value); }
|
|
static inline void sshfwd_start_shell(
|
|
SshChannel *c, bool want_reply)
|
|
{ c->vt->start_shell(c, want_reply); }
|
|
static inline void sshfwd_start_command(
|
|
SshChannel *c, bool want_reply, const char *command)
|
|
{ c->vt->start_command(c, want_reply, command); }
|
|
static inline bool sshfwd_start_subsystem(
|
|
SshChannel *c, bool want_reply, const char *subsystem)
|
|
{ return c->vt->start_subsystem(c, want_reply, subsystem); }
|
|
static inline bool sshfwd_send_serial_break(
|
|
SshChannel *c, bool want_reply, int length)
|
|
{ return c->vt->send_serial_break(c, want_reply, length); }
|
|
static inline bool sshfwd_send_signal(
|
|
SshChannel *c, bool want_reply, const char *signame)
|
|
{ return c->vt->send_signal(c, want_reply, signame); }
|
|
static inline void sshfwd_send_terminal_size_change(
|
|
SshChannel *c, int w, int h)
|
|
{ c->vt->send_terminal_size_change(c, w, h); }
|
|
static inline void sshfwd_hint_channel_is_simple(SshChannel *c)
|
|
{ c->vt->hint_channel_is_simple(c); }
|
|
|
|
/* ----------------------------------------------------------------------
|
|
* The 'main' or primary channel of the SSH connection is special,
|
|
* because it's the one that's connected directly to parts of the
|
|
* frontend such as the terminal and the specials menu. So it exposes
|
|
* a richer API.
|
|
*/
|
|
|
|
mainchan *mainchan_new(
|
|
PacketProtocolLayer *ppl, ConnectionLayer *cl, Conf *conf,
|
|
int term_width, int term_height, bool is_simple, SshChannel **sc_out);
|
|
void mainchan_get_specials(
|
|
mainchan *mc, add_special_fn_t add_special, void *ctx);
|
|
void mainchan_special_cmd(mainchan *mc, SessionSpecialCode code, int arg);
|
|
void mainchan_terminal_size(mainchan *mc, int width, int height);
|
|
|
|
#endif /* PUTTY_SSHCHAN_H */
|