- djm@cvs.openbsd.org 2004/05/21 11:33:11
     [channels.c channels.h clientloop.c serverloop.c ssh.1]
     bz #756: add support for the cancel-tcpip-forward request for the server and
     the client (through the ~C commandline). reported by z3p AT twistedmatrix.com;
     ok markus@
diff --git a/ChangeLog b/ChangeLog
index 91c9ce5..0e8f4a5 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -10,6 +10,11 @@
    - markus@cvs.openbsd.org 2004/05/21 08:43:03
      [kex.h moduli.c tildexpand.c]
      add prototypes for -Wall; ok djm
+   - djm@cvs.openbsd.org 2004/05/21 11:33:11
+     [channels.c channels.h clientloop.c serverloop.c ssh.1]
+     bz #756: add support for the cancel-tcpip-forward request for the server and
+     the client (through the ~C commandline). reported by z3p AT twistedmatrix.com;
+     ok markus@
 
 20040523
  - (djm) [sshd_config] Explain consequences of UsePAM=yes a little better in 
@@ -1139,4 +1144,4 @@
    - (djm) Trim deprecated options from INSTALL. Mention UsePAM
    - (djm) Fix quote handling in sftp; Patch from admorten AT umich.edu
 
-$Id: ChangeLog,v 1.3361 2004/05/24 00:14:24 dtucker Exp $
+$Id: ChangeLog,v 1.3362 2004/05/24 00:18:05 dtucker Exp $
diff --git a/channels.c b/channels.c
index 55dc673..2b1ce0e 100644
--- a/channels.c
+++ b/channels.c
@@ -39,7 +39,7 @@
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: channels.c,v 1.201 2004/05/11 19:01:43 deraadt Exp $");
+RCSID("$OpenBSD: channels.c,v 1.202 2004/05/21 11:33:11 djm Exp $");
 
 #include "ssh.h"
 #include "ssh1.h"
@@ -2228,6 +2228,26 @@
 	return success;
 }
 
+int
+channel_cancel_rport_listener(const char *host, u_short port)
+{
+	int i, found = 0;
+
+	for(i = 0; i < channels_alloc; i++) {
+		Channel *c = channels[i];
+
+		if (c != NULL && c->type == SSH_CHANNEL_RPORT_LISTENER &&
+		    strncmp(c->path, host, sizeof(c->path)) == 0 &&
+	    	    c->listening_port == port) {
+			debug2("%s: close clannel %d", __func__, i);
+			channel_free(c);
+			found = 1;
+		}
+	}
+
+	return (found);
+}
+
 /* protocol local port fwd, used by ssh (and sshd in v1) */
 int
 channel_setup_local_fwd_listener(u_short listen_port,
@@ -2305,6 +2325,42 @@
 }
 
 /*
+ * Request cancellation of remote forwarding of connection host:port from 
+ * local side.
+ */
+
+void
+channel_request_rforward_cancel(u_short port)
+{
+	int i;
+	const char *address_to_bind = "0.0.0.0";
+
+	if (!compat20)
+		return;
+
+	for (i = 0; i < num_permitted_opens; i++) {
+		if (permitted_opens[i].host_to_connect != NULL && 
+		    permitted_opens[i].listen_port == port)
+			break;
+	}
+	if (i >= num_permitted_opens) {
+		debug("%s: requested forward not found", __func__);
+		return;
+	}
+	packet_start(SSH2_MSG_GLOBAL_REQUEST);
+	packet_put_cstring("cancel-tcpip-forward");
+	packet_put_char(0);
+	packet_put_cstring(address_to_bind);
+	packet_put_int(port);
+	packet_send();
+
+	permitted_opens[i].listen_port = 0;
+	permitted_opens[i].port_to_connect = 0;
+	free(permitted_opens[i].host_to_connect);
+	permitted_opens[i].host_to_connect = NULL;
+}
+
+/*
  * This is called after receiving CHANNEL_FORWARDING_REQUEST.  This initates
  * listening for the port, and sends back a success reply (or disconnect
  * message if there was an error).  This never returns if there was an error.
@@ -2373,7 +2429,8 @@
 	int i;
 
 	for (i = 0; i < num_permitted_opens; i++)
-		xfree(permitted_opens[i].host_to_connect);
+		if (permitted_opens[i].host_to_connect != NULL)
+			xfree(permitted_opens[i].host_to_connect);
 	num_permitted_opens = 0;
 
 }
@@ -2441,7 +2498,8 @@
 	int i;
 
 	for (i = 0; i < num_permitted_opens; i++)
-		if (permitted_opens[i].listen_port == listen_port)
+		if (permitted_opens[i].host_to_connect != NULL &&
+		    permitted_opens[i].listen_port == listen_port)
 			return connect_to(
 			    permitted_opens[i].host_to_connect,
 			    permitted_opens[i].port_to_connect);
@@ -2459,7 +2517,8 @@
 	permit = all_opens_permitted;
 	if (!permit) {
 		for (i = 0; i < num_permitted_opens; i++)
-			if (permitted_opens[i].port_to_connect == port &&
+			if (permitted_opens[i].host_to_connect != NULL &&
+			    permitted_opens[i].port_to_connect == port &&
 			    strcmp(permitted_opens[i].host_to_connect, host) == 0)
 				permit = 1;
 
diff --git a/channels.h b/channels.h
index 7d98147..0a49c55 100644
--- a/channels.h
+++ b/channels.h
@@ -1,4 +1,4 @@
-/*	$OpenBSD: channels.h,v 1.71 2003/09/23 20:41:11 markus Exp $	*/
+/*	$OpenBSD: channels.h,v 1.72 2004/05/21 11:33:11 djm Exp $	*/
 
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -200,8 +200,10 @@
 int	 channel_connect_to(const char *, u_short);
 int	 channel_connect_by_listen_address(u_short);
 void	 channel_request_remote_forwarding(u_short, const char *, u_short);
+void	 channel_request_rforward_cancel(u_short port);
 int	 channel_setup_local_fwd_listener(u_short, const char *, u_short, int);
 int	 channel_setup_remote_fwd_listener(const char *, u_short, int);
+int	 channel_cancel_rport_listener(const char *, u_short);
 
 /* x11 forwarding */
 
diff --git a/clientloop.c b/clientloop.c
index 9cbc1b0..ce627e8 100644
--- a/clientloop.c
+++ b/clientloop.c
@@ -59,7 +59,7 @@
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: clientloop.c,v 1.120 2004/05/20 10:58:05 dtucker Exp $");
+RCSID("$OpenBSD: clientloop.c,v 1.121 2004/05/21 11:33:11 djm Exp $");
 
 #include "ssh.h"
 #include "ssh1.h"
@@ -506,6 +506,7 @@
 	char *s, *cmd;
 	u_short fwd_port, fwd_host_port;
 	char buf[1024], sfwd_port[6], sfwd_host_port[6];
+	int delete = 0;
 	int local = 0;
 
 	leave_raw_mode();
@@ -515,44 +516,77 @@
 		goto out;
 	while (*s && isspace(*s))
 		s++;
+	if (*s == '-')
+		s++;	/* Skip cmdline '-', if any */
 	if (*s == '\0')
 		goto out;
-	if (strlen(s) < 2 || s[0] != '-' || !(s[1] == 'L' || s[1] == 'R')) {
+
+	if (*s == '?') {
+		logit("Commands:");
+		logit("      -Lport:host:hostport    Request local forward");
+		logit("      -Rport:host:hostport    Request remote forward");
+		logit("      -KRhostport             Cancel remote forward");
+		goto out;
+	}
+
+	if (*s == 'K') {
+		delete = 1;
+		s++;
+	}
+	if (*s != 'L' && *s != 'R') {
 		logit("Invalid command.");
 		goto out;
 	}
-	if (s[1] == 'L')
+	if (*s == 'L')
 		local = 1;
-	if (!local && !compat20) {
+	if (local && delete) {
+		logit("Not supported.");
+		goto out;
+	}
+	if ((!local || delete) && !compat20) {
 		logit("Not supported for SSH protocol version 1.");
 		goto out;
 	}
-	s += 2;
+
+	s++;
 	while (*s && isspace(*s))
 		s++;
 
-	if (sscanf(s, "%5[0-9]:%255[^:]:%5[0-9]",
-	    sfwd_port, buf, sfwd_host_port) != 3 &&
-	    sscanf(s, "%5[0-9]/%255[^/]/%5[0-9]",
-	    sfwd_port, buf, sfwd_host_port) != 3) {
-		logit("Bad forwarding specification.");
-		goto out;
-	}
-	if ((fwd_port = a2port(sfwd_port)) == 0 ||
-	    (fwd_host_port = a2port(sfwd_host_port)) == 0) {
-		logit("Bad forwarding port(s).");
-		goto out;
-	}
-	if (local) {
-		if (channel_setup_local_fwd_listener(fwd_port, buf,
-		    fwd_host_port, options.gateway_ports) < 0) {
-			logit("Port forwarding failed.");
+	if (delete) {
+		if (sscanf(s, "%5[0-9]", sfwd_host_port) != 1) {
+			logit("Bad forwarding specification.");
 			goto out;
 		}
-	} else
-		channel_request_remote_forwarding(fwd_port, buf,
-		    fwd_host_port);
-	logit("Forwarding port.");
+		if ((fwd_host_port = a2port(sfwd_host_port)) == 0) {
+			logit("Bad forwarding port(s).");
+			goto out;
+		}
+		channel_request_rforward_cancel(fwd_host_port);
+	} else {
+		if (sscanf(s, "%5[0-9]:%255[^:]:%5[0-9]",
+		    sfwd_port, buf, sfwd_host_port) != 3 &&
+		    sscanf(s, "%5[0-9]/%255[^/]/%5[0-9]",
+		    sfwd_port, buf, sfwd_host_port) != 3) {
+			logit("Bad forwarding specification.");
+			goto out;
+		}
+		if ((fwd_port = a2port(sfwd_port)) == 0 ||
+		    (fwd_host_port = a2port(sfwd_host_port)) == 0) {
+			logit("Bad forwarding port(s).");
+			goto out;
+		}
+		if (local) {
+			if (channel_setup_local_fwd_listener(fwd_port, buf,
+			    fwd_host_port, options.gateway_ports) < 0) {
+				logit("Port forwarding failed.");
+				goto out;
+			}
+		} else
+			channel_request_remote_forwarding(fwd_port, buf,
+			    fwd_host_port);
+		logit("Forwarding port.");
+	}
+
 out:
 	signal(SIGINT, handler);
 	enter_raw_mode();
diff --git a/serverloop.c b/serverloop.c
index a777a04..8d2642d 100644
--- a/serverloop.c
+++ b/serverloop.c
@@ -35,7 +35,7 @@
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: serverloop.c,v 1.115 2004/01/19 21:25:15 markus Exp $");
+RCSID("$OpenBSD: serverloop.c,v 1.116 2004/05/21 11:33:11 djm Exp $");
 
 #include "xmalloc.h"
 #include "packet.h"
@@ -991,6 +991,17 @@
 			    listen_address, listen_port, options.gateway_ports);
 		}
 		xfree(listen_address);
+	} else if (strcmp(rtype, "cancel-tcpip-forward") == 0) {
+		char *cancel_address;
+		u_short cancel_port;
+
+		cancel_address = packet_get_string(NULL);
+		cancel_port = (u_short)packet_get_int();
+		debug("%s: cancel-tcpip-forward addr %s port %d", __func__,
+		    cancel_address, cancel_port);
+
+		success = channel_cancel_rport_listener(cancel_address,
+		    cancel_port);
 	}
 	if (want_reply) {
 		packet_start(success ?
diff --git a/ssh.1 b/ssh.1
index b7b1264..7da143b 100644
--- a/ssh.1
+++ b/ssh.1
@@ -34,7 +34,7 @@
 .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 .\"
-.\" $OpenBSD: ssh.1,v 1.185 2004/05/02 11:57:52 dtucker Exp $
+.\" $OpenBSD: ssh.1,v 1.186 2004/05/21 11:33:11 djm Exp $
 .Dd September 25, 1999
 .Dt SSH 1
 .Os
@@ -302,11 +302,18 @@
 Send a BREAK to the remote system
 (only useful for SSH protocol version 2 and if the peer supports it).
 .It Cm ~C
-Open command line (only useful for adding port forwardings using the
+Open command line.
+Currently this allows the addition of port forwardings using the
 .Fl L
 and
 .Fl R
-options).
+options (see below).
+It also allows the cancellation of existing remote port-forwardings 
+using
+.Fl KR Ar hostport .
+Basic help is available, using the 
+.Fl ?
+option.
 .It Cm ~R
 Request rekeying of the connection
 (only useful for SSH protocol version 2 and if the peer supports it).