}
/*
- * All the SSH stuff is omitted in PuTTYtel.
+ * All the SSH stuff is omitted in PuTTYtel, or in a reconfig
+ * when we're not doing SSH.
*/
- if (!midsession && backends[3].name != NULL) {
+ if (backends[3].name != NULL && (!midsession || protocol == PROT_SSH)) {
/*
* The Connection/SSH panel.
ctrl_settitle(b, "Connection/SSH",
"Options controlling SSH connections");
- s = ctrl_getset(b, "Connection/SSH", "data",
- "Data to send to the server");
- ctrl_editbox(s, "Remote command:", 'r', 100,
- HELPCTX(ssh_command),
- dlg_stdeditbox_handler, I(offsetof(Config,remote_cmd)),
- I(sizeof(((Config *)0)->remote_cmd)));
-
- s = ctrl_getset(b, "Connection/SSH", "protocol", "Protocol options");
- ctrl_checkbox(s, "Don't allocate a pseudo-terminal", 'p',
- HELPCTX(ssh_nopty),
- dlg_stdcheckbox_handler,
- I(offsetof(Config,nopty)));
- ctrl_checkbox(s, "Don't start a shell or command at all", 'n',
- HELPCTX(ssh_noshell),
- dlg_stdcheckbox_handler,
- I(offsetof(Config,ssh_no_shell)));
- ctrl_checkbox(s, "Enable compression", 'e',
- HELPCTX(ssh_compress),
- dlg_stdcheckbox_handler,
- I(offsetof(Config,compression)));
- ctrl_radiobuttons(s, "Preferred SSH protocol version:", NO_SHORTCUT, 4,
- HELPCTX(ssh_protocol),
- dlg_stdradiobutton_handler,
- I(offsetof(Config, sshprot)),
- "1 only", 'l', I(0),
- "1", '1', I(1),
- "2", '2', I(2),
- "2 only", 'y', I(3), NULL);
-
- s = ctrl_getset(b, "Connection/SSH", "encryption", "Encryption options");
- c = ctrl_draglist(s, "Encryption cipher selection policy:", 's',
+ if (midsession) {
+ s = ctrl_getset(b, "Connection/SSH", "disclaimer", NULL);
+ ctrl_text(s, "Nothing on this panel may be reconfigured in mid-"
+ "session; it is only here so that sub-panels of it can "
+ "exist without looking strange.", HELPCTX(no_help));
+ }
+
+ if (!midsession) {
+
+ s = ctrl_getset(b, "Connection/SSH", "data",
+ "Data to send to the server");
+ ctrl_editbox(s, "Remote command:", 'r', 100,
+ HELPCTX(ssh_command),
+ dlg_stdeditbox_handler, I(offsetof(Config,remote_cmd)),
+ I(sizeof(((Config *)0)->remote_cmd)));
+
+ s = ctrl_getset(b, "Connection/SSH", "protocol", "Protocol options");
+ ctrl_checkbox(s, "Don't allocate a pseudo-terminal", 'p',
+ HELPCTX(ssh_nopty),
+ dlg_stdcheckbox_handler,
+ I(offsetof(Config,nopty)));
+ ctrl_checkbox(s, "Don't start a shell or command at all", 'n',
+ HELPCTX(ssh_noshell),
+ dlg_stdcheckbox_handler,
+ I(offsetof(Config,ssh_no_shell)));
+ ctrl_checkbox(s, "Enable compression", 'e',
+ HELPCTX(ssh_compress),
+ dlg_stdcheckbox_handler,
+ I(offsetof(Config,compression)));
+ ctrl_radiobuttons(s, "Preferred SSH protocol version:", NO_SHORTCUT, 4,
+ HELPCTX(ssh_protocol),
+ dlg_stdradiobutton_handler,
+ I(offsetof(Config, sshprot)),
+ "1 only", 'l', I(0),
+ "1", '1', I(1),
+ "2", '2', I(2),
+ "2 only", 'y', I(3), NULL);
+
+ s = ctrl_getset(b, "Connection/SSH", "encryption", "Encryption options");
+ c = ctrl_draglist(s, "Encryption cipher selection policy:", 's',
+ HELPCTX(ssh_ciphers),
+ cipherlist_handler, P(NULL));
+ c->listbox.height = 6;
+
+ ctrl_checkbox(s, "Enable legacy use of single-DES in SSH 2", 'i',
HELPCTX(ssh_ciphers),
- cipherlist_handler, P(NULL));
- c->listbox.height = 6;
-
- ctrl_checkbox(s, "Enable legacy use of single-DES in SSH 2", 'i',
- HELPCTX(ssh_ciphers),
- dlg_stdcheckbox_handler,
- I(offsetof(Config,ssh2_des_cbc)));
+ dlg_stdcheckbox_handler,
+ I(offsetof(Config,ssh2_des_cbc)));
- /*
- * The Connection/SSH/Kex panel.
- */
- ctrl_settitle(b, "Connection/SSH/Kex",
- "Options controlling SSH key exchange");
-
- s = ctrl_getset(b, "Connection/SSH/Kex", "main",
- "Key exchange algorithm options");
- c = ctrl_draglist(s, "Algorithm selection policy", 's',
- HELPCTX(ssh_kexlist),
- kexlist_handler, P(NULL));
- c->listbox.height = 5;
-
- s = ctrl_getset(b, "Connection/SSH/Kex", "repeat",
- "Options controlling key re-exchange");
-
- /* FIXME: these could usefully be configured mid-session in SSH-2.
- * (So could cipher/compression/kex, now we have rekey.) */
- ctrl_editbox(s, "Max minutes before rekey (0 for no limit)", 't', 20,
- HELPCTX(ssh_kex_repeat),
- dlg_stdeditbox_handler,
- I(offsetof(Config,ssh_rekey_time)),
- I(-1));
- ctrl_editbox(s, "Max data before rekey (0 for no limit)", 'd', 20,
- HELPCTX(ssh_kex_repeat),
- dlg_stdeditbox_handler,
- I(offsetof(Config,ssh_rekey_data)),
- I(16));
- ctrl_text(s, "(Use 1M for 1 megabyte, 1G for 1 gigabyte etc)",
- HELPCTX(ssh_kex_repeat));
+ /*
+ * The Connection/SSH/Kex panel.
+ */
+ ctrl_settitle(b, "Connection/SSH/Kex",
+ "Options controlling SSH key exchange");
+
+ s = ctrl_getset(b, "Connection/SSH/Kex", "main",
+ "Key exchange algorithm options");
+ c = ctrl_draglist(s, "Algorithm selection policy", 's',
+ HELPCTX(ssh_kexlist),
+ kexlist_handler, P(NULL));
+ c->listbox.height = 5;
+
+ s = ctrl_getset(b, "Connection/SSH/Kex", "repeat",
+ "Options controlling key re-exchange");
+
+ /* FIXME: these could usefully be configured mid-session in SSH-2.
+ * (So could cipher/compression/kex, now we have rekey.) */
+ ctrl_editbox(s, "Max minutes before rekey (0 for no limit)", 't', 20,
+ HELPCTX(ssh_kex_repeat),
+ dlg_stdeditbox_handler,
+ I(offsetof(Config,ssh_rekey_time)),
+ I(-1));
+ ctrl_editbox(s, "Max data before rekey (0 for no limit)", 'd', 20,
+ HELPCTX(ssh_kex_repeat),
+ dlg_stdeditbox_handler,
+ I(offsetof(Config,ssh_rekey_data)),
+ I(16));
+ ctrl_text(s, "(Use 1M for 1 megabyte, 1G for 1 gigabyte etc)",
+ HELPCTX(ssh_kex_repeat));
- /*
- * The Connection/SSH/Auth panel.
- */
- ctrl_settitle(b, "Connection/SSH/Auth",
- "Options controlling SSH authentication");
+ /*
+ * The Connection/SSH/Auth panel.
+ */
+ ctrl_settitle(b, "Connection/SSH/Auth",
+ "Options controlling SSH authentication");
- s = ctrl_getset(b, "Connection/SSH/Auth", "methods",
- "Authentication methods");
- ctrl_checkbox(s, "Attempt TIS or CryptoCard auth (SSH1)", 'm',
- HELPCTX(ssh_auth_tis),
- dlg_stdcheckbox_handler,
- I(offsetof(Config,try_tis_auth)));
- ctrl_checkbox(s, "Attempt \"keyboard-interactive\" auth (SSH2)",
- 'i', HELPCTX(ssh_auth_ki),
- dlg_stdcheckbox_handler,
- I(offsetof(Config,try_ki_auth)));
-
- s = ctrl_getset(b, "Connection/SSH/Auth", "params",
- "Authentication parameters");
- ctrl_checkbox(s, "Allow agent forwarding", 'f',
- HELPCTX(ssh_auth_agentfwd),
- dlg_stdcheckbox_handler, I(offsetof(Config,agentfwd)));
- ctrl_checkbox(s, "Allow attempted changes of username in SSH2", 'u',
- HELPCTX(ssh_auth_changeuser),
- dlg_stdcheckbox_handler,
- I(offsetof(Config,change_username)));
- ctrl_filesel(s, "Private key file for authentication:", 'k',
- FILTER_KEY_FILES, FALSE, "Select private key file",
- HELPCTX(ssh_auth_privkey),
- dlg_stdfilesel_handler, I(offsetof(Config, keyfile)));
+ s = ctrl_getset(b, "Connection/SSH/Auth", "methods",
+ "Authentication methods");
+ ctrl_checkbox(s, "Attempt TIS or CryptoCard auth (SSH1)", 'm',
+ HELPCTX(ssh_auth_tis),
+ dlg_stdcheckbox_handler,
+ I(offsetof(Config,try_tis_auth)));
+ ctrl_checkbox(s, "Attempt \"keyboard-interactive\" auth (SSH2)",
+ 'i', HELPCTX(ssh_auth_ki),
+ dlg_stdcheckbox_handler,
+ I(offsetof(Config,try_ki_auth)));
+
+ s = ctrl_getset(b, "Connection/SSH/Auth", "params",
+ "Authentication parameters");
+ ctrl_checkbox(s, "Allow agent forwarding", 'f',
+ HELPCTX(ssh_auth_agentfwd),
+ dlg_stdcheckbox_handler, I(offsetof(Config,agentfwd)));
+ ctrl_checkbox(s, "Allow attempted changes of username in SSH2", 'u',
+ HELPCTX(ssh_auth_changeuser),
+ dlg_stdcheckbox_handler,
+ I(offsetof(Config,change_username)));
+ ctrl_filesel(s, "Private key file for authentication:", 'k',
+ FILTER_KEY_FILES, FALSE, "Select private key file",
+ HELPCTX(ssh_auth_privkey),
+ dlg_stdfilesel_handler, I(offsetof(Config, keyfile)));
+ }
/*
- * The Connection/SSH/Tunnels panel.
+ * The Connection/SSH/Tunnels panel. Some of this _is_
+ * still available in mid-session.
*/
ctrl_settitle(b, "Connection/SSH/Tunnels",
"Options controlling SSH tunnelling");
- s = ctrl_getset(b, "Connection/SSH/Tunnels", "x11", "X11 forwarding");
- ctrl_checkbox(s, "Enable X11 forwarding", 'e',
- HELPCTX(ssh_tunnels_x11),
- dlg_stdcheckbox_handler,I(offsetof(Config,x11_forward)));
- ctrl_editbox(s, "X display location", 'x', 50,
- HELPCTX(ssh_tunnels_x11),
- dlg_stdeditbox_handler, I(offsetof(Config,x11_display)),
- I(sizeof(((Config *)0)->x11_display)));
- ctrl_radiobuttons(s, "Remote X11 authentication protocol", 'u', 2,
- HELPCTX(ssh_tunnels_x11auth),
- dlg_stdradiobutton_handler,
- I(offsetof(Config, x11_auth)),
- "MIT-Magic-Cookie-1", I(X11_MIT),
- "XDM-Authorization-1", I(X11_XDM), NULL);
+ if (!midsession) {
+ s = ctrl_getset(b, "Connection/SSH/Tunnels", "x11", "X11 forwarding");
+ ctrl_checkbox(s, "Enable X11 forwarding", 'e',
+ HELPCTX(ssh_tunnels_x11),
+ dlg_stdcheckbox_handler,I(offsetof(Config,x11_forward)));
+ ctrl_editbox(s, "X display location", 'x', 50,
+ HELPCTX(ssh_tunnels_x11),
+ dlg_stdeditbox_handler, I(offsetof(Config,x11_display)),
+ I(sizeof(((Config *)0)->x11_display)));
+ ctrl_radiobuttons(s, "Remote X11 authentication protocol", 'u', 2,
+ HELPCTX(ssh_tunnels_x11auth),
+ dlg_stdradiobutton_handler,
+ I(offsetof(Config, x11_auth)),
+ "MIT-Magic-Cookie-1", I(X11_MIT),
+ "XDM-Authorization-1", I(X11_XDM), NULL);
+ }
s = ctrl_getset(b, "Connection/SSH/Tunnels", "portfwd",
"Port forwarding");
ctrl_tabdelay(s, pfd->addbutton);
ctrl_columns(s, 1, 100);
- /*
- * The Connection/SSH/Bugs panel.
- */
- ctrl_settitle(b, "Connection/SSH/Bugs",
- "Workarounds for SSH server bugs");
-
- s = ctrl_getset(b, "Connection/SSH/Bugs", "main",
- "Detection of known bugs in SSH servers");
- ctrl_droplist(s, "Chokes on SSH1 ignore messages", 'i', 20,
- HELPCTX(ssh_bugs_ignore1),
- sshbug_handler, I(offsetof(Config,sshbug_ignore1)));
- ctrl_droplist(s, "Refuses all SSH1 password camouflage", 's', 20,
- HELPCTX(ssh_bugs_plainpw1),
- sshbug_handler, I(offsetof(Config,sshbug_plainpw1)));
- ctrl_droplist(s, "Chokes on SSH1 RSA authentication", 'r', 20,
- HELPCTX(ssh_bugs_rsa1),
- sshbug_handler, I(offsetof(Config,sshbug_rsa1)));
- ctrl_droplist(s, "Miscomputes SSH2 HMAC keys", 'm', 20,
- HELPCTX(ssh_bugs_hmac2),
- sshbug_handler, I(offsetof(Config,sshbug_hmac2)));
- ctrl_droplist(s, "Miscomputes SSH2 encryption keys", 'e', 20,
- HELPCTX(ssh_bugs_derivekey2),
- sshbug_handler, I(offsetof(Config,sshbug_derivekey2)));
- ctrl_droplist(s, "Requires padding on SSH2 RSA signatures", 'p', 20,
- HELPCTX(ssh_bugs_rsapad2),
- sshbug_handler, I(offsetof(Config,sshbug_rsapad2)));
- ctrl_droplist(s, "Misuses the session ID in PK auth", 'n', 20,
- HELPCTX(ssh_bugs_pksessid2),
- sshbug_handler, I(offsetof(Config,sshbug_pksessid2)));
+ if (!midsession) {
+ /*
+ * The Connection/SSH/Bugs panel.
+ */
+ ctrl_settitle(b, "Connection/SSH/Bugs",
+ "Workarounds for SSH server bugs");
+
+ s = ctrl_getset(b, "Connection/SSH/Bugs", "main",
+ "Detection of known bugs in SSH servers");
+ ctrl_droplist(s, "Chokes on SSH1 ignore messages", 'i', 20,
+ HELPCTX(ssh_bugs_ignore1),
+ sshbug_handler, I(offsetof(Config,sshbug_ignore1)));
+ ctrl_droplist(s, "Refuses all SSH1 password camouflage", 's', 20,
+ HELPCTX(ssh_bugs_plainpw1),
+ sshbug_handler, I(offsetof(Config,sshbug_plainpw1)));
+ ctrl_droplist(s, "Chokes on SSH1 RSA authentication", 'r', 20,
+ HELPCTX(ssh_bugs_rsa1),
+ sshbug_handler, I(offsetof(Config,sshbug_rsa1)));
+ ctrl_droplist(s, "Miscomputes SSH2 HMAC keys", 'm', 20,
+ HELPCTX(ssh_bugs_hmac2),
+ sshbug_handler, I(offsetof(Config,sshbug_hmac2)));
+ ctrl_droplist(s, "Miscomputes SSH2 encryption keys", 'e', 20,
+ HELPCTX(ssh_bugs_derivekey2),
+ sshbug_handler, I(offsetof(Config,sshbug_derivekey2)));
+ ctrl_droplist(s, "Requires padding on SSH2 RSA signatures", 'p', 20,
+ HELPCTX(ssh_bugs_rsapad2),
+ sshbug_handler, I(offsetof(Config,sshbug_rsapad2)));
+ ctrl_droplist(s, "Misuses the session ID in PK auth", 'n', 20,
+ HELPCTX(ssh_bugs_pksessid2),
+ sshbug_handler, I(offsetof(Config,sshbug_pksessid2)));
+ }
}
}
* Hence, in SSH 1 this structure is indexed by destination
* host:port pair, whereas in SSH 2 it is indexed by source port.
*/
+struct ssh_portfwd; /* forward declaration */
+
struct ssh_rportfwd {
unsigned sport, dport;
char dhost[256];
char *sportdesc;
+ struct ssh_portfwd *pfrec;
};
+#define free_rportfwd(pf) ( \
+ ((pf) ? (sfree((pf)->sportdesc)) : (void)0 ), sfree(pf) )
+
+/*
+ * Separately to the rportfwd tree (which is for looking up port
+ * open requests from the server), a tree of _these_ structures is
+ * used to keep track of all the currently open port forwardings,
+ * so that we can reconfigure in mid-session if the user requests
+ * it.
+ */
+struct ssh_portfwd {
+ int keep;
+ int type;
+ unsigned sport, dport;
+ char *saddr, *daddr;
+ struct ssh_rportfwd *remote;
+ void *local;
+};
+#define free_portfwd(pf) ( \
+ ((pf) ? (sfree((pf)->saddr), sfree((pf)->daddr)) : (void)0 ), sfree(pf) )
struct Packet {
long length;
struct ssh_channel *mainchan; /* primary session channel */
int exitcode;
- tree234 *rportfwds;
+ tree234 *rportfwds, *portfwds;
enum {
SSH_STATE_PREPACKET,
return 0;
}
+/*
+ * Special form of strcmp which can cope with NULL inputs. NULL is
+ * defined to sort before even the empty string.
+ */
+static int nullstrcmp(const char *a, const char *b)
+{
+ if (a == NULL && b == NULL)
+ return 0;
+ if (a == NULL)
+ return -1;
+ if (b == NULL)
+ return +1;
+ return strcmp(a, b);
+}
+
+static int ssh_portcmp(void *av, void *bv)
+{
+ struct ssh_portfwd *a = (struct ssh_portfwd *) av;
+ struct ssh_portfwd *b = (struct ssh_portfwd *) bv;
+ int i;
+ if (a->type > b->type)
+ return +1;
+ if (a->type < b->type)
+ return -1;
+ if ( (i = nullstrcmp(a->saddr, b->saddr)) != 0)
+ return i < 0 ? -1 : +1;
+ if (a->sport > b->sport)
+ return +1;
+ if (a->sport < b->sport)
+ return -1;
+ if (a->type != 'D') {
+ if ( (i = nullstrcmp(a->daddr, b->daddr)) != 0)
+ return i < 0 ? -1 : +1;
+ if (a->dport > b->dport)
+ return +1;
+ if (a->dport < b->dport)
+ return -1;
+ }
+ return 0;
+}
+
static int alloc_channel_id(Ssh ssh)
{
const unsigned CHANNEL_NUMBER_OFFSET = 256;
rpf = del234(ssh->rportfwds, pf);
assert(rpf == pf);
- sfree(pf->sportdesc);
- sfree(pf);
+ free_rportfwd(pf);
}
}
portfwd_strptr = cfg->portfwd;
+ if (!ssh->portfwds) {
+ ssh->portfwds = newtree234(ssh_portcmp);
+ } else {
+ /*
+ * Go through the existing port forwardings and tag them
+ * with keep==FALSE. Any that we want to keep will be
+ * re-enabled as we go through the configuration and find
+ * out which bits are the same as they were before.
+ */
+ struct ssh_portfwd *epf;
+ int i;
+ for (i = 0; (epf = index234(ssh->portfwds, i)) != NULL; i++)
+ epf->keep = FALSE;
+ }
+
while (*portfwd_strptr) {
type = *portfwd_strptr++;
saddr[0] = '\0';
}
if (sport && dport) {
/* Set up a description of the source port. */
- static char *sportdesc;
+ struct ssh_portfwd *pfrec, *epfrec;
+ char *sportdesc;
sportdesc = dupprintf("%.*s%.*s%.*s%.*s%d%.*s",
(int)(*saddr?strlen(saddr):0), *saddr?saddr:NULL,
(int)(*saddr?1:0), ":",
(int)(sserv ? strlen(sports) : 0), sports,
sserv, "(", sport, sserv, ")");
- if (type == 'L') {
+
+ pfrec = snew(struct ssh_portfwd);
+ pfrec->type = type;
+ pfrec->saddr = *saddr ? dupstr(saddr) : NULL;
+ pfrec->sport = sport;
+ pfrec->daddr = dupstr(host);
+ pfrec->dport = dport;
+ pfrec->local = NULL;
+ pfrec->remote = NULL;
+
+ epfrec = add234(ssh->portfwds, pfrec);
+ if (epfrec != pfrec) {
+ /*
+ * We already have a port forwarding with precisely
+ * these parameters. Hence, no need to do anything;
+ * simply tag the existing one as `keep'.
+ */
+ epfrec->keep = TRUE;
+ free_portfwd(pfrec);
+ } else if (type == 'L') {
/* Verbose description of the destination port */
char *dportdesc = dupprintf("%s:%.*s%.*s%d%.*s",
host,
dserv, "(", dport, dserv, ")");
const char *err = pfd_addforward(host, dport,
*saddr ? saddr : NULL,
- sport, ssh, &ssh->cfg);
+ sport, ssh, &ssh->cfg,
+ &pfrec->local);
if (err) {
logeventf(ssh, "Local port %s forward to %s"
" failed: %s", sportdesc, dportdesc, err);
} else if (type == 'D') {
const char *err = pfd_addforward(NULL, -1,
*saddr ? saddr : NULL,
- sport, ssh, &ssh->cfg);
+ sport, ssh, &ssh->cfg,
+ &pfrec->local);
if (err) {
logeventf(ssh, "Local port %s SOCKS dynamic forward"
" setup failed: %s", sportdesc, err);
pf->sportdesc = sportdesc;
sportdesc = NULL;
+ pfrec->remote = pf;
+ pf->pfrec = pfrec;
if (ssh->version == 1) {
send_packet(ssh, SSH1_CMSG_PORT_FORWARD_REQUEST,
sfree(sportdesc);
}
}
+
+ /*
+ * Now go through and destroy any port forwardings which were
+ * not re-enabled.
+ */
+ {
+ struct ssh_portfwd *epf;
+ int i;
+ for (i = 0; (epf = index234(ssh->portfwds, i)) != NULL; i++)
+ if (!epf->keep) {
+ char *message;
+
+ message = dupprintf("%s port forwarding from %s%s%d",
+ epf->type == 'L' ? "local" :
+ epf->type == 'R' ? "remote" : "dynamic",
+ epf->saddr ? epf->saddr : "",
+ epf->saddr ? ":" : "",
+ epf->sport);
+
+ if (epf->type != 'D') {
+ char *msg2 = dupprintf("%s to %s:%d", message,
+ epf->daddr, epf->dport);
+ sfree(message);
+ message = msg2;
+ }
+
+ logeventf(ssh, "Cancelling %s", message);
+ sfree(message);
+
+ if (epf->remote) {
+ struct ssh_rportfwd *rpf = epf->remote;
+ struct Packet *pktout;
+
+ /*
+ * Cancel the port forwarding at the server
+ * end.
+ */
+ if (ssh->version == 1) {
+ /*
+ * We cannot cancel listening ports on the
+ * server side in SSH1! There's no message
+ * to support it. Instead, we simply remove
+ * the rportfwd record from the local end
+ * so that any connections the server tries
+ * to make on it are rejected.
+ */
+ } else {
+ pktout = ssh2_pkt_init(SSH2_MSG_GLOBAL_REQUEST);
+ ssh2_pkt_addstring(pktout, "cancel-tcpip-forward");
+ ssh2_pkt_addbool(pktout, 0);/* _don't_ want reply */
+ if (epf->saddr) {
+ ssh2_pkt_addstring(pktout, epf->saddr);
+ } else if (ssh->cfg.rport_acceptall) {
+ ssh2_pkt_addstring(pktout, "0.0.0.0");
+ } else {
+ ssh2_pkt_addstring(pktout, "127.0.0.1");
+ }
+ ssh2_pkt_adduint32(pktout, epf->sport);
+ ssh2_pkt_send(ssh, pktout);
+ }
+
+ del234(ssh->rportfwds, rpf);
+ free_rportfwd(rpf);
+ } else if (epf->local) {
+ pfd_terminate(epf->local);
+ }
+
+ delpos234(ssh->portfwds, i);
+ free_portfwd(epf);
+ i--; /* so we don't skip one in the list */
+ }
+ }
}
static void ssh1_smsg_stdout_stderr_data(Ssh ssh, struct Packet *pktin)
/*
* Reconfigure the SSH backend.
- *
- * Currently, this function does nothing very useful. In future,
- * however, we could do some handy things with it. For example, we
- * could make the port forwarding configurer active in the Change
- * Settings box, and this routine could close down existing
- * forwardings and open up new ones in response to changes.
*/
static void ssh_reconfig(void *handle, Config *cfg)
{
Ssh ssh = (Ssh) handle;
pinger_reconfig(ssh->pinger, &ssh->cfg, cfg);
+ ssh_setup_portfwd(ssh, cfg);
ssh->cfg = *cfg; /* STRUCTURE COPY */
}