- djm@cvs.openbsd.org 2011/09/09 22:46:44
     [channels.c channels.h clientloop.h mux.c ssh.c]
     support for cancelling local and remote port forwards via the multiplex
     socket. Use ssh -O cancel -L xx:xx:xx -R yy:yy:yy user@host" to request
     the cancellation of the specified forwardings; ok markus@
diff --git a/mux.c b/mux.c
index add0e26..6b63d81 100644
--- a/mux.c
+++ b/mux.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: mux.c,v 1.29 2011/06/22 22:08:42 djm Exp $ */
+/* $OpenBSD: mux.c,v 1.30 2011/09/09 22:46:44 djm Exp $ */
 /*
  * Copyright (c) 2002-2008 Damien Miller <djm@openbsd.org>
  *
@@ -777,10 +777,11 @@
 static int
 process_mux_close_fwd(u_int rid, Channel *c, Buffer *m, Buffer *r)
 {
-	Forward fwd;
+	Forward fwd, *found_fwd;
 	char *fwd_desc = NULL;
+	const char *error_reason = NULL;
 	u_int ftype;
-	int ret = 0;
+	int i, ret = 0;
 
 	fwd.listen_host = fwd.connect_host = NULL;
 	if (buffer_get_int_ret(&ftype, m) != 0 ||
@@ -802,14 +803,66 @@
 		fwd.connect_host = NULL;
 	}
 
-	debug2("%s: channel %d: request %s", __func__, c->self,
+	debug2("%s: channel %d: request cancel %s", __func__, c->self,
 	    (fwd_desc = format_forward(ftype, &fwd)));
 
-	/* XXX implement this */
-	buffer_put_int(r, MUX_S_FAILURE);
-	buffer_put_int(r, rid);
-	buffer_put_cstring(r, "unimplemented");
+	/* make sure this has been requested */
+	found_fwd = NULL;
+	switch (ftype) {
+	case MUX_FWD_LOCAL:
+	case MUX_FWD_DYNAMIC:
+		for (i = 0; i < options.num_local_forwards; i++) {
+			if (compare_forward(&fwd,
+			    options.local_forwards + i)) {
+				found_fwd = options.local_forwards + i;
+				break;
+			}
+		}
+		break;
+	case MUX_FWD_REMOTE:
+		for (i = 0; i < options.num_remote_forwards; i++) {
+			if (compare_forward(&fwd,
+			    options.remote_forwards + i)) {
+				found_fwd = options.remote_forwards + i;
+				break;
+			}
+		}
+		break;
+	}
 
+	if (found_fwd == NULL)
+		error_reason = "port not forwarded";
+	else if (ftype == MUX_FWD_REMOTE) {
+		/*
+		 * This shouldn't fail unless we confused the host/port
+		 * between options.remote_forwards and permitted_opens.
+		 */
+		if (channel_request_rforward_cancel(fwd.listen_host,
+		    fwd.listen_port) == -1)
+			error_reason = "port not in permitted opens";
+	} else {	/* local and dynamic forwards */
+		/* Ditto */
+		if (channel_cancel_lport_listener(fwd.listen_host,
+		    fwd.listen_port, fwd.connect_port,
+		    options.gateway_ports) == -1)
+			error_reason = "port not found";
+	}
+
+	if (error_reason == NULL) {
+		buffer_put_int(r, MUX_S_OK);
+		buffer_put_int(r, rid);
+
+		if (found_fwd->listen_host != NULL)
+			xfree(found_fwd->listen_host);
+		if (found_fwd->connect_host != NULL)
+			xfree(found_fwd->connect_host);
+		found_fwd->listen_host = found_fwd->connect_host = NULL;
+		found_fwd->listen_port = found_fwd->connect_port = 0;
+	} else {
+		buffer_put_int(r, MUX_S_FAILURE);
+		buffer_put_int(r, rid);
+		buffer_put_cstring(r, error_reason);
+	}
  out:
 	if (fwd_desc != NULL)
 		xfree(fwd_desc);
@@ -1537,18 +1590,19 @@
 }
 
 static int
-mux_client_request_forward(int fd, u_int ftype, Forward *fwd)
+mux_client_forward(int fd, int cancel_flag, u_int ftype, Forward *fwd)
 {
 	Buffer m;
 	char *e, *fwd_desc;
 	u_int type, rid;
 
 	fwd_desc = format_forward(ftype, fwd);
-	debug("Requesting %s", fwd_desc);
+	debug("Requesting %s %s",
+	    cancel_flag ? "cancellation of" : "forwarding of", fwd_desc);
 	xfree(fwd_desc);
 
 	buffer_init(&m);
-	buffer_put_int(&m, MUX_C_OPEN_FWD);
+	buffer_put_int(&m, cancel_flag ? MUX_C_CLOSE_FWD : MUX_C_OPEN_FWD);
 	buffer_put_int(&m, muxclient_request_id);
 	buffer_put_int(&m, ftype);
 	buffer_put_cstring(&m,
@@ -1577,6 +1631,8 @@
 	case MUX_S_OK:
 		break;
 	case MUX_S_REMOTE_PORT:
+		if (cancel_flag)
+			fatal("%s: got MUX_S_REMOTE_PORT for cancel", __func__);
 		fwd->allocated_port = buffer_get_int(&m);
 		logit("Allocated port %u for remote forward to %s:%d",
 		    fwd->allocated_port,
@@ -1606,27 +1662,28 @@
 }
 
 static int
-mux_client_request_forwards(int fd)
+mux_client_forwards(int fd, int cancel_flag)
 {
-	int i;
+	int i, ret = 0;
 
-	debug3("%s: requesting forwardings: %d local, %d remote", __func__,
+	debug3("%s: %s forwardings: %d local, %d remote", __func__,
+	    cancel_flag ? "cancel" : "request",
 	    options.num_local_forwards, options.num_remote_forwards);
 
 	/* XXX ExitOnForwardingFailure */
 	for (i = 0; i < options.num_local_forwards; i++) {
-		if (mux_client_request_forward(fd,
+		if (mux_client_forward(fd, cancel_flag,
 		    options.local_forwards[i].connect_port == 0 ?
 		    MUX_FWD_DYNAMIC : MUX_FWD_LOCAL,
 		    options.local_forwards + i) != 0)
-			return -1;
+			ret = -1;
 	}
 	for (i = 0; i < options.num_remote_forwards; i++) {
-		if (mux_client_request_forward(fd, MUX_FWD_REMOTE,
+		if (mux_client_forward(fd, cancel_flag, MUX_FWD_REMOTE,
 		    options.remote_forwards + i) != 0)
-			return -1;
+			ret = -1;
 	}
-	return 0;
+	return ret;
 }
 
 static int
@@ -2014,11 +2071,11 @@
 		fprintf(stderr, "Exit request sent.\r\n");
 		exit(0);
 	case SSHMUX_COMMAND_FORWARD:
-		if (mux_client_request_forwards(sock) != 0)
+		if (mux_client_forwards(sock, 0) != 0)
 			fatal("%s: master forward request failed", __func__);
 		exit(0);
 	case SSHMUX_COMMAND_OPEN:
-		if (mux_client_request_forwards(sock) != 0) {
+		if (mux_client_forwards(sock, 0) != 0) {
 			error("%s: master forward request failed", __func__);
 			return;
 		}
@@ -2031,6 +2088,11 @@
 		mux_client_request_stop_listening(sock);
 		fprintf(stderr, "Stop listening request sent.\r\n");
 		exit(0);
+	case SSHMUX_COMMAND_CANCEL_FWD:
+		if (mux_client_forwards(sock, 1) != 0)
+			error("%s: master cancel forward request failed",
+			    __func__);
+		exit(0);
 	default:
 		fatal("unrecognised muxclient_command %d", muxclient_command);
 	}