diff --git a/putty.h b/putty.h index cfdc8154..c854d944 100644 --- a/putty.h +++ b/putty.h @@ -445,6 +445,9 @@ struct backend_tag { */ void (*unthrottle) (void *handle, int); int (*cfg_info) (void *handle); + /* Only implemented in the SSH protocol: check whether a + * connection-sharing upstream exists for a given configuration. */ + int (*test_for_upstream)(const char *host, int port, Conf *conf); const char *name; int protocol; int default_port; diff --git a/raw.c b/raw.c index b083d09d..6262ed89 100644 --- a/raw.c +++ b/raw.c @@ -339,6 +339,7 @@ Backend raw_backend = { raw_provide_logctx, raw_unthrottle, raw_cfg_info, + NULL /* test_for_upstream */, "raw", PROT_RAW, 0 diff --git a/rlogin.c b/rlogin.c index 10cda463..d73f7f9f 100644 --- a/rlogin.c +++ b/rlogin.c @@ -425,6 +425,7 @@ Backend rlogin_backend = { rlogin_provide_logctx, rlogin_unthrottle, rlogin_cfg_info, + NULL /* test_for_upstream */, "rlogin", PROT_RLOGIN, 513 diff --git a/ssh.c b/ssh.c index c9394236..f3ce6fe0 100644 --- a/ssh.c +++ b/ssh.c @@ -3586,6 +3586,19 @@ static void ssh_hostport_setup(const char *host, int port, Conf *conf, } } +static int ssh_test_for_upstream(const char *host, int port, Conf *conf) +{ + char *savedhost; + int savedport; + int ret; + + ssh_hostport_setup(host, port, conf, &savedhost, &savedport, NULL); + ret = ssh_share_test_for_upstream(savedhost, savedport, conf); + sfree(savedhost); + + return ret; +} + /* * Connect to specified host and port. * Returns an error message, or NULL on success. @@ -11645,6 +11658,7 @@ Backend ssh_backend = { ssh_provide_logctx, ssh_unthrottle, ssh_cfg_info, + ssh_test_for_upstream, "ssh", PROT_SSH, 22 diff --git a/ssh.h b/ssh.h index e8f1adad..75aad70b 100644 --- a/ssh.h +++ b/ssh.h @@ -24,6 +24,7 @@ void sshfwd_x11_is_local(struct ssh_channel *c); extern Socket ssh_connection_sharing_init(const char *host, int port, Conf *conf, Ssh ssh, void **state); +int ssh_share_test_for_upstream(const char *host, int port, Conf *conf); void share_got_pkt_from_server(void *ctx, int type, unsigned char *pkt, int pktlen); void share_activate(void *state, const char *server_verstring); diff --git a/sshshare.c b/sshshare.c index dcf805d6..62220039 100644 --- a/sshshare.c +++ b/sshshare.c @@ -2027,6 +2027,55 @@ char *ssh_share_sockname(const char *host, int port, Conf *conf) return sockname; } +static void nullplug_socket_log(Plug plug, int type, SockAddr addr, int port, + const char *error_msg, int error_code) {} +static int nullplug_closing(Plug plug, const char *error_msg, int error_code, + int calling_back) { return 0; } +static int nullplug_receive(Plug plug, int urgent, char *data, + int len) { return 0; } +static void nullplug_sent(Plug plug, int bufsize) {} + +int ssh_share_test_for_upstream(const char *host, int port, Conf *conf) +{ + static const struct plug_function_table fn_table = { + nullplug_socket_log, + nullplug_closing, + nullplug_receive, + nullplug_sent, + NULL + }; + struct nullplug { + const struct plug_function_table *fn; + } np; + + char *sockname, *logtext, *ds_err, *us_err; + int result; + Socket sock; + + np.fn = &fn_table; + + sockname = ssh_share_sockname(host, port, conf); + + sock = NULL; + logtext = ds_err = us_err = NULL; + result = platform_ssh_share(sockname, conf, (Plug)&np, (Plug)NULL, &sock, + &logtext, &ds_err, &us_err, FALSE, TRUE); + + sfree(logtext); + sfree(ds_err); + sfree(us_err); + sfree(sockname); + + if (result == SHARE_NONE) { + assert(sock == NULL); + return FALSE; + } else { + assert(result == SHARE_DOWNSTREAM); + sk_close(sock); + return TRUE; + } +} + /* * Init function for connection sharing. We either open a listening * socket and become an upstream, or connect to an existing one and diff --git a/telnet.c b/telnet.c index ca166525..0de8b016 100644 --- a/telnet.c +++ b/telnet.c @@ -1129,6 +1129,7 @@ Backend telnet_backend = { telnet_provide_logctx, telnet_unthrottle, telnet_cfg_info, + NULL /* test_for_upstream */, "telnet", PROT_TELNET, 23 diff --git a/testback.c b/testback.c index bf3047ef..752ec4ca 100644 --- a/testback.c +++ b/testback.c @@ -58,14 +58,14 @@ Backend null_backend = { null_init, null_free, null_reconfig, null_send, null_sendbuffer, null_size, null_special, null_get_specials, null_connected, null_exitcode, null_sendok, null_ldisc, null_provide_ldisc, null_provide_logctx, null_unthrottle, - null_cfg_info, "null", -1, 0 + null_cfg_info, NULL /* test_for_upstream */, "null", -1, 0 }; Backend loop_backend = { loop_init, loop_free, null_reconfig, loop_send, null_sendbuffer, null_size, null_special, null_get_specials, null_connected, null_exitcode, null_sendok, null_ldisc, null_provide_ldisc, null_provide_logctx, null_unthrottle, - null_cfg_info, "loop", -1, 0 + null_cfg_info, NULL /* test_for_upstream */, "loop", -1, 0 }; struct loop_state { diff --git a/unix/uxplink.c b/unix/uxplink.c index 40b12661..ad122e9d 100644 --- a/unix/uxplink.c +++ b/unix/uxplink.c @@ -607,6 +607,7 @@ int main(int argc, char **argv) int errors; int use_subsystem = 0; int got_host = FALSE; + int just_test_share_exists = FALSE; unsigned long now; struct winsize size; @@ -685,6 +686,8 @@ int main(int argc, char **argv) --argc; provide_xrm_string(*++argv); } + } else if (!strcmp(p, "-shareexists")) { + just_test_share_exists = TRUE; } else { fprintf(stderr, "plink: unknown option \"%s\"\n", p); errors = 1; @@ -959,6 +962,19 @@ int main(int argc, char **argv) !conf_get_str_nthstrkey(conf, CONF_portfwd, 0)) conf_set_int(conf, CONF_ssh_simple, TRUE); + if (just_test_share_exists) { + if (!back->test_for_upstream) { + fprintf(stderr, "Connection sharing not supported for connection " + "type '%s'\n", back->name); + return 1; + } + if (back->test_for_upstream(conf_get_str(conf, CONF_host), + conf_get_int(conf, CONF_port), conf)) + return 0; + else + return 1; + } + /* * Start up the connection. */ diff --git a/unix/uxpty.c b/unix/uxpty.c index 0b7c6309..79a60f3c 100644 --- a/unix/uxpty.c +++ b/unix/uxpty.c @@ -1218,6 +1218,7 @@ Backend pty_backend = { pty_provide_logctx, pty_unthrottle, pty_cfg_info, + NULL /* test_for_upstream */, "pty", -1, 0 diff --git a/unix/uxser.c b/unix/uxser.c index 691e527e..41beaf0e 100644 --- a/unix/uxser.c +++ b/unix/uxser.c @@ -591,6 +591,7 @@ Backend serial_backend = { serial_provide_logctx, serial_unthrottle, serial_cfg_info, + NULL /* test_for_upstream */, "serial", PROT_SERIAL, 0 diff --git a/windows/winplink.c b/windows/winplink.c index 43f53bb0..8ffa4651 100644 --- a/windows/winplink.c +++ b/windows/winplink.c @@ -303,6 +303,7 @@ int main(int argc, char **argv) int errors; int got_host = FALSE; int use_subsystem = 0; + int just_test_share_exists = FALSE; unsigned long now, next, then; sklist = NULL; @@ -364,6 +365,8 @@ int main(int argc, char **argv) } else if (!strcmp(p, "-pgpfp")) { pgp_fingerprints(); exit(1); + } else if (!strcmp(p, "-shareexists")) { + just_test_share_exists = TRUE; } else { fprintf(stderr, "plink: unknown option \"%s\"\n", p); errors = 1; @@ -596,6 +599,19 @@ int main(int argc, char **argv) logctx = log_init(NULL, conf); console_provide_logctx(logctx); + if (just_test_share_exists) { + if (!back->test_for_upstream) { + fprintf(stderr, "Connection sharing not supported for connection " + "type '%s'\n", back->name); + return 1; + } + if (back->test_for_upstream(conf_get_str(conf, CONF_host), + conf_get_int(conf, CONF_port), conf)) + return 0; + else + return 1; + } + /* * Start up the connection. */ diff --git a/windows/winser.c b/windows/winser.c index 3f1326db..de9e61b4 100644 --- a/windows/winser.c +++ b/windows/winser.c @@ -453,6 +453,7 @@ Backend serial_backend = { serial_provide_logctx, serial_unthrottle, serial_cfg_info, + NULL /* test_for_upstream */, "serial", PROT_SERIAL, 0