diff --git a/network.h b/network.h index 099895bc..0b600f63 100644 --- a/network.h +++ b/network.h @@ -129,6 +129,26 @@ struct PlugVtable { * want the connection for some reason, or 0 on success. */ int (*accepting)(Plug *p, accept_fn_t constructor, accept_ctx_t ctx); + + /* + * Returns a user-facing description of the nature of the network + * connection being made. Used in interactive proxy authentication + * to announce which connection attempt is now in control of the + * Seat. + * + * The idea is not just to be written in natural language, but to + * connect with the user's idea of _why_ they think some + * connection is being made. For example, instead of saying 'TCP + * connection to 123.45.67.89 port 22', you might say 'SSH + * connection to [logical host name for SSH host key purposes]'. + * + * This function pointer may be NULL, or may exist but return + * NULL, in which case no user-facing description is available. + * + * If a non-NULL string is returned, it must be freed by the + * caller. + */ + char *(*description)(Plug *p); }; /* Proxy indirection layer. @@ -236,6 +256,8 @@ static inline void plug_sent (Plug *p, size_t bufsize) { p->vt->sent(p, bufsize); } static inline int plug_accepting(Plug *p, accept_fn_t cons, accept_ctx_t ctx) { return p->vt->accepting(p, cons, ctx); } +static inline char *plug_description(Plug *p) +{ return p->vt->description ? p->vt->description(p) : NULL; } /* * Special error values are returned from sk_namelookup and sk_new diff --git a/otherbackends/raw.c b/otherbackends/raw.c index d6693244..57130dc6 100644 --- a/otherbackends/raw.c +++ b/otherbackends/raw.c @@ -19,6 +19,7 @@ struct Raw { LogContext *logctx; Ldisc *ldisc; bool sent_console_eof, sent_socket_eof, socket_connected; + char *description; Conf *conf; @@ -115,11 +116,24 @@ static void raw_sent(Plug *plug, size_t bufsize) seat_sent(raw->seat, raw->bufsize); } +static char *raw_plug_description(Plug *plug) +{ + Raw *raw = container_of(plug, Raw, plug); + return dupstr(raw->description); +} + +static char *raw_backend_description(Backend *backend) +{ + Raw *raw = container_of(backend, Raw, backend); + return dupstr(raw->description); +} + static const PlugVtable Raw_plugvt = { .log = raw_log, .closing = raw_closing, .receive = raw_receive, .sent = raw_sent, + .description = raw_plug_description, }; /* @@ -151,6 +165,7 @@ static char *raw_init(const BackendVtable *vt, Seat *seat, raw->bufsize = 0; raw->socket_connected = false; raw->conf = conf_copy(conf); + raw->description = default_description(vt, host, port); raw->seat = seat; raw->logctx = logctx; @@ -205,6 +220,7 @@ static void raw_free(Backend *be) if (raw->s) sk_close(raw->s); conf_free(raw->conf); + sfree(raw->description); sfree(raw); } @@ -337,6 +353,7 @@ const BackendVtable raw_backend = { .provide_ldisc = raw_provide_ldisc, .unthrottle = raw_unthrottle, .cfg_info = raw_cfg_info, + .description = raw_backend_description, .id = "raw", .displayname_tc = "Raw", .displayname_lc = "raw", diff --git a/otherbackends/rlogin.c b/otherbackends/rlogin.c index 44a3f625..4e3abffe 100644 --- a/otherbackends/rlogin.c +++ b/otherbackends/rlogin.c @@ -23,6 +23,7 @@ struct Rlogin { Seat *seat; LogContext *logctx; Ldisc *ldisc; + char *description; Conf *conf; @@ -192,11 +193,24 @@ static void rlogin_startup(Rlogin *rlogin, int prompt_result, ldisc_check_sendok(rlogin->ldisc); } +static char *rlogin_plug_description(Plug *plug) +{ + Rlogin *rlogin = container_of(plug, Rlogin, plug); + return dupstr(rlogin->description); +} + +static char *rlogin_backend_description(Backend *backend) +{ + Rlogin *rlogin = container_of(backend, Rlogin, backend); + return dupstr(rlogin->description); +} + static const PlugVtable Rlogin_plugvt = { .log = rlogin_log, .closing = rlogin_closing, .receive = rlogin_receive, .sent = rlogin_sent, + .description = rlogin_plug_description, }; /* @@ -232,6 +246,7 @@ static char *rlogin_init(const BackendVtable *vt, Seat *seat, rlogin->cansize = false; rlogin->prompt = NULL; rlogin->conf = conf_copy(conf); + rlogin->description = default_description(vt, host, port); *backend_handle = &rlogin->backend; addressfamily = conf_get_int(conf, CONF_addressfamily); @@ -283,6 +298,7 @@ static void rlogin_free(Backend *be) if (rlogin->s) sk_close(rlogin->s); conf_free(rlogin->conf); + sfree(rlogin->description); sfree(rlogin); } @@ -444,6 +460,7 @@ const BackendVtable rlogin_backend = { .provide_ldisc = rlogin_provide_ldisc, .unthrottle = rlogin_unthrottle, .cfg_info = rlogin_cfg_info, + .description = rlogin_backend_description, .id = "rlogin", .displayname_tc = "Rlogin", .displayname_lc = "Rlogin", /* proper name, so capitalise it anyway */ diff --git a/otherbackends/supdup.c b/otherbackends/supdup.c index 2ec8e0f9..278e689e 100644 --- a/otherbackends/supdup.c +++ b/otherbackends/supdup.c @@ -73,6 +73,7 @@ struct supdup_tag LogContext *logctx; Ldisc *ldisc; int term_width, term_height; + char *description; long long ttyopt; long tcmxv; @@ -641,6 +642,18 @@ static void supdup_send_config(Supdup *supdup) supdup_send_36bits(supdup, TTYROL); // scroll amount } +static char *supdup_plug_description(Plug *plug) +{ + Supdup *supdup = container_of(plug, Supdup, plug); + return dupstr(supdup->description); +} + +static char *supdup_backend_description(Backend *backend) +{ + Supdup *supdup = container_of(backend, Supdup, backend); + return dupstr(supdup->description); +} + /* * Called to set up the Supdup connection. * @@ -660,6 +673,7 @@ static char *supdup_init(const BackendVtable *x, Seat *seat, .closing = supdup_closing, .receive = supdup_receive, .sent = supdup_sent, + .description = supdup_plug_description, }; SockAddr *addr; const char *err; @@ -681,6 +695,7 @@ static char *supdup_init(const BackendVtable *x, Seat *seat, supdup->term_height = conf_get_int(supdup->conf, CONF_height); supdup->pinger = NULL; supdup->sent_location = false; + supdup->description = default_description(supdup->backend.vt, host, port); *backend_handle = &supdup->backend; switch (conf_get_int(supdup->conf, CONF_supdup_ascii_set)) { @@ -799,6 +814,7 @@ static void supdup_free(Backend *be) if (supdup->pinger) pinger_free(supdup->pinger); conf_free(supdup->conf); + sfree(supdup->description); sfree(supdup); } @@ -935,6 +951,7 @@ const BackendVtable supdup_backend = { .provide_ldisc = supdup_provide_ldisc, .unthrottle = supdup_unthrottle, .cfg_info = supdup_cfg_info, + .description = supdup_backend_description, .id = "supdup", .displayname_tc = "SUPDUP", .displayname_lc = "SUPDUP", /* proper name, so capitalise it anyway */ diff --git a/otherbackends/telnet.c b/otherbackends/telnet.c index 048bb2d6..0011cf0d 100644 --- a/otherbackends/telnet.c +++ b/otherbackends/telnet.c @@ -178,6 +178,7 @@ struct Telnet { LogContext *logctx; Ldisc *ldisc; int term_width, term_height; + char *description; int opt_states[NUM_OPTS]; @@ -680,11 +681,24 @@ static void telnet_sent(Plug *plug, size_t bufsize) seat_sent(telnet->seat, telnet->bufsize); } +static char *telnet_plug_description(Plug *plug) +{ + Telnet *telnet = container_of(plug, Telnet, plug); + return dupstr(telnet->description); +} + +static char *telnet_backend_description(Backend *backend) +{ + Telnet *telnet = container_of(backend, Telnet, backend); + return dupstr(telnet->description); +} + static const PlugVtable Telnet_plugvt = { .log = telnet_log, .closing = telnet_closing, .receive = telnet_receive, .sent = telnet_sent, + .description = telnet_plug_description, }; /* @@ -724,6 +738,7 @@ static char *telnet_init(const BackendVtable *vt, Seat *seat, telnet->state = TOP_LEVEL; telnet->ldisc = NULL; telnet->pinger = NULL; + telnet->description = default_description(vt, host, port); *backend_handle = &telnet->backend; /* @@ -813,6 +828,7 @@ static void telnet_free(Backend *be) if (telnet->pinger) pinger_free(telnet->pinger); conf_free(telnet->conf); + sfree(telnet->description); sfree(telnet); } /* @@ -1081,6 +1097,7 @@ const BackendVtable telnet_backend = { .provide_ldisc = telnet_provide_ldisc, .unthrottle = telnet_unthrottle, .cfg_info = telnet_cfg_info, + .description = telnet_backend_description, .id = "telnet", .displayname_tc = "Telnet", .displayname_lc = "Telnet", /* proper name, so capitalise it anyway */ diff --git a/putty.h b/putty.h index a2136a05..82ee83a8 100644 --- a/putty.h +++ b/putty.h @@ -680,6 +680,28 @@ struct BackendVtable { * connections that would be lost if this one were terminated. */ char *(*close_warn_text)(Backend *be); + /* + * Returns a user-facing description of the nature of the network + * connection being made. Used in interactive proxy authentication + * to announce which connection attempt is now in control of the + * Seat. + * + * The idea is not just to be written in natural language, but to + * connect with the user's idea of _why_ they think some + * connection is being made. For example, instead of saying 'TCP + * connection to 123.45.67.89 port 22', you might say 'SSH + * connection to [logical host name for SSH host key purposes]'. + * + * This function pointer may be NULL, or may exist but return + * NULL, in which case no user-facing description is available. + * (Backends which are never proxied, such as pty and ConPTY, need + * not bother to fill this in.) + * + * If a non-NULL string is returned, it must be freed by the + * caller. + */ + char *(*description)(Backend *be); + /* 'id' is a machine-readable name for the backend, used in * saved-session storage. 'displayname_tc' and 'displayname_lc' * are human-readable names, one in title-case for config boxes, @@ -728,6 +750,11 @@ static inline void backend_unthrottle(Backend *be, size_t bufsize) { be->vt->unthrottle(be, bufsize); } static inline int backend_cfg_info(Backend *be) { return be->vt->cfg_info(be); } +static inline char *backend_description(Backend *be) +{ return be->vt->description ? be->vt->description(be) : NULL; } + +char *default_description(const BackendVtable *backvt, + const char *host, int port); extern const struct BackendVtable *const backends[]; /* diff --git a/ssh/ssh.c b/ssh/ssh.c index 103f5795..c9ce2b0b 100644 --- a/ssh/ssh.c +++ b/ssh/ssh.c @@ -57,6 +57,7 @@ struct Ssh { char *savedhost; int savedport; char *fullhostname; + char *description; bool fallback_cmd; int exitcode; @@ -717,11 +718,24 @@ static char *ssh_close_warn_text(Backend *be) return msg; } +static char *ssh_plug_description(Plug *plug) +{ + Ssh *ssh = container_of(plug, Ssh, plug); + return dupstr(ssh->description); +} + +static char *ssh_backend_description(Backend *backend) +{ + Ssh *ssh = container_of(backend, Ssh, backend); + return dupstr(ssh->description); +} + static const PlugVtable Ssh_plugvt = { .log = ssh_socket_log, .closing = ssh_closing, .receive = ssh_receive, .sent = ssh_sent, + .description = ssh_plug_description, }; /* @@ -731,17 +745,13 @@ static const PlugVtable Ssh_plugvt = { * freed by the caller. */ static char *connect_to_host( - Ssh *ssh, const char *host, int port, char **realhost, + Ssh *ssh, const char *host, int port, char *loghost, char **realhost, bool nodelay, bool keepalive) { SockAddr *addr; const char *err; - char *loghost; int addressfamily, sshprot; - ssh_hostport_setup(host, port, ssh->conf, - &ssh->savedhost, &ssh->savedport, &loghost); - ssh->plug.vt = &Ssh_plugvt; /* @@ -938,11 +948,17 @@ static char *ssh_init(const BackendVtable *vt, Seat *seat, ssh->cl_dummy.vt = &dummy_connlayer_vtable; ssh->cl_dummy.logctx = ssh->logctx = logctx; + char *loghost; + + ssh_hostport_setup(host, port, ssh->conf, + &ssh->savedhost, &ssh->savedport, &loghost); + ssh->description = default_description(vt, ssh->savedhost, ssh->savedport); + random_ref(); /* do this now - may be needed by sharing setup code */ ssh->need_random_unref = true; char *conn_err = connect_to_host( - ssh, host, port, realhost, nodelay, keepalive); + ssh, host, port, loghost, realhost, nodelay, keepalive); if (conn_err) { /* Call random_unref now instead of waiting until the caller * frees this useless Ssh object, in case the caller is @@ -985,6 +1001,7 @@ static void ssh_free(Backend *be) #endif sfree(ssh->deferred_abort_message); + sfree(ssh->description); delete_callbacks_for_context(ssh); /* likely to catch ic_out_raw */ @@ -1247,6 +1264,7 @@ const BackendVtable ssh_backend = { .cfg_info = ssh_cfg_info, .test_for_upstream = ssh_test_for_upstream, .close_warn_text = ssh_close_warn_text, + .description = ssh_backend_description, .id = "ssh", .displayname_tc = "SSH", .displayname_lc = "SSH", /* proper name, so capitalise it anyway */ @@ -1273,6 +1291,7 @@ const BackendVtable sshconn_backend = { .cfg_info = ssh_cfg_info, .test_for_upstream = ssh_test_for_upstream, .close_warn_text = ssh_close_warn_text, + .description = ssh_backend_description, .id = "ssh-connection", .displayname_tc = "Bare ssh-connection", .displayname_lc = "bare ssh-connection", diff --git a/utils/CMakeLists.txt b/utils/CMakeLists.txt index 6376c0c9..97557eb6 100644 --- a/utils/CMakeLists.txt +++ b/utils/CMakeLists.txt @@ -11,6 +11,7 @@ add_sources_from_current_dir(utils conf_launchable.c ctrlparse.c debug.c + default_description.c dupcat.c dupprintf.c dupstr.c diff --git a/utils/default_description.c b/utils/default_description.c new file mode 100644 index 00000000..e0695ee6 --- /dev/null +++ b/utils/default_description.c @@ -0,0 +1,22 @@ +/* + * Construct a description string for a backend to use as + * backend_description(), or a plug as plug_description(). + * + * For some backends this will be overridden: e.g. SSH prefers to + * think in terms of _logical_ host names (i.e. the one associated + * with the host key) rather than the physical details of where you're + * connecting to. But this default is good for simpler backends. + */ + +#include "putty.h" + +char *default_description(const BackendVtable *backvt, + const char *host, int port) +{ + const char *be_name = backvt->displayname_lc; + + if (backvt->default_port && port == backvt->default_port) + return dupprintf("%s connection to %s", be_name, host); + else + return dupprintf("%s connection to %s port %d", be_name, host, port); +}