upstream commit

refactor channels.c

Move static state to a "struct ssh_channels" that is allocated at
runtime and tracked as a member of struct ssh.

Explicitly pass "struct ssh" to all channels functions.

Replace use of the legacy packet APIs in channels.c.

Rework sshd_config PermitOpen handling: previously the configuration
parser would call directly into the channels layer. After the refactor
this is not possible, as the channels structures are allocated at
connection time and aren't available when the configuration is parsed.
The server config parser now tracks PermitOpen itself and explicitly
configures the channels code later.

ok markus@

Upstream-ID: 11828f161656b965cc306576422613614bea2d8f
diff --git a/channels.c b/channels.c
index d9e81b5..935625c 100644
--- a/channels.c
+++ b/channels.c
@@ -55,26 +55,27 @@
 
 #include <errno.h>
 #include <fcntl.h>
+#include <limits.h>
 #include <netdb.h>
+#include <stdarg.h>
 #ifdef HAVE_STDINT_H
-#include <stdint.h>
+ #include <stdint.h>
 #endif
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <termios.h>
 #include <unistd.h>
-#include <stdarg.h>
 
 #include "openbsd-compat/sys-queue.h"
 #include "xmalloc.h"
 #include "ssh.h"
 #include "ssh2.h"
 #include "ssherr.h"
+#include "sshbuf.h"
 #include "packet.h"
 #include "log.h"
 #include "misc.h"
-#include "buffer.h"
 #include "channels.h"
 #include "compat.h"
 #include "canohost.h"
@@ -82,28 +83,19 @@
 #include "authfd.h"
 #include "pathnames.h"
 
-/* -- channel core */
-
-/*
- * Pointer to an array containing all allocated channels.  The array is
- * dynamically extended as needed.
- */
-static Channel **channels = NULL;
-
-/*
- * Size of the channel array.  All slots of the array must always be
- * initialized (at least the type field); unused slots set to NULL
- */
-static u_int channels_alloc = 0;
-
-/*
- * Maximum file descriptor value used in any of the channels.  This is
- * updated in channel_new.
- */
-static int channel_max_fd = 0;
-
+/* -- agent forwarding */
+#define	NUM_SOCKS	10
 
 /* -- tcp forwarding */
+/* special-case port number meaning allow any port */
+#define FWD_PERMIT_ANY_PORT	0
+
+/* special-case wildcard meaning allow any host */
+#define FWD_PERMIT_ANY_HOST	"*"
+
+/* -- X11 forwarding */
+/* Maximum number of fake X11 displays to try. */
+#define MAX_DISPLAYS  1000
 
 /*
  * Data structure for storing which hosts are permitted for forward requests.
@@ -123,100 +115,150 @@
 	Channel *downstream;		/* Downstream mux*/
 } ForwardPermission;
 
-/* List of all permitted host/port pairs to connect by the user. */
-static ForwardPermission *permitted_opens = NULL;
+typedef void chan_fn(struct ssh *, Channel *c,
+    fd_set *readset, fd_set *writeset);
 
-/* List of all permitted host/port pairs to connect by the admin. */
-static ForwardPermission *permitted_adm_opens = NULL;
+/* Master structure for channels state */
+struct ssh_channels {
+	/*
+	 * Pointer to an array containing all allocated channels.  The array
+	 * is dynamically extended as needed.
+	 */
+	Channel **channels;
 
-/* Number of permitted host/port pairs in the array permitted by the user. */
-static int num_permitted_opens = 0;
+	/*
+	 * Size of the channel array.  All slots of the array must always be
+	 * initialized (at least the type field); unused slots set to NULL
+	 */
+	u_int channels_alloc;
 
-/* Number of permitted host/port pair in the array permitted by the admin. */
-static int num_adm_permitted_opens = 0;
+	/*
+	 * Maximum file descriptor value used in any of the channels.  This is
+	 * updated in channel_new.
+	 */
+	int channel_max_fd;
 
-/* special-case port number meaning allow any port */
-#define FWD_PERMIT_ANY_PORT	0
+	/*
+	 * 'channel_pre*' are called just before select() to add any bits
+	 * relevant to channels in the select bitmasks.
+	 *
+	 * 'channel_post*': perform any appropriate operations for
+	 * channels which have events pending.
+	 */
+	chan_fn **channel_pre;
+	chan_fn **channel_post;
 
-/* special-case wildcard meaning allow any host */
-#define FWD_PERMIT_ANY_HOST	"*"
+	/* -- tcp forwarding */
 
-/*
- * If this is true, all opens are permitted.  This is the case on the server
- * on which we have to trust the client anyway, and the user could do
- * anything after logging in anyway.
- */
-static int all_opens_permitted = 0;
+	/* List of all permitted host/port pairs to connect by the user. */
+	ForwardPermission *permitted_opens;
 
+	/* List of all permitted host/port pairs to connect by the admin. */
+	ForwardPermission *permitted_adm_opens;
 
-/* -- X11 forwarding */
+	/*
+	 * Number of permitted host/port pairs in the array permitted by
+	 * the user.
+	 */
+	u_int num_permitted_opens;
 
-/* Maximum number of fake X11 displays to try. */
-#define MAX_DISPLAYS  1000
+	/*
+	 * Number of permitted host/port pair in the array permitted by
+	 * the admin.
+	 */
+	u_int num_adm_permitted_opens;
 
-/* Saved X11 local (client) display. */
-static char *x11_saved_display = NULL;
+	/*
+	 * If this is true, all opens are permitted.  This is the case on
+	 * the server on which we have to trust the client anyway, and the
+	 * user could do anything after logging in anyway.
+	 */
+	int all_opens_permitted;
 
-/* Saved X11 authentication protocol name. */
-static char *x11_saved_proto = NULL;
+	/* -- X11 forwarding */
 
-/* Saved X11 authentication data.  This is the real data. */
-static char *x11_saved_data = NULL;
-static u_int x11_saved_data_len = 0;
+	/* Saved X11 local (client) display. */
+	char *x11_saved_display;
 
-/* Deadline after which all X11 connections are refused */
-static u_int x11_refuse_time;
+	/* Saved X11 authentication protocol name. */
+	char *x11_saved_proto;
 
-/*
- * Fake X11 authentication data.  This is what the server will be sending us;
- * we should replace any occurrences of this by the real data.
- */
-static u_char *x11_fake_data = NULL;
-static u_int x11_fake_data_len;
+	/* Saved X11 authentication data.  This is the real data. */
+	char *x11_saved_data;
+	u_int x11_saved_data_len;
 
+	/* Deadline after which all X11 connections are refused */
+	u_int x11_refuse_time;
 
-/* -- agent forwarding */
+	/*
+	 * Fake X11 authentication data.  This is what the server will be
+	 * sending us; we should replace any occurrences of this by the
+	 * real data.
+	 */
+	u_char *x11_fake_data;
+	u_int x11_fake_data_len;
 
-#define	NUM_SOCKS	10
-
-/* AF_UNSPEC or AF_INET or AF_INET6 */
-static int IPv4or6 = AF_UNSPEC;
+	/* AF_UNSPEC or AF_INET or AF_INET6 */
+	int IPv4or6;
+};
 
 /* helper */
-static void port_open_helper(Channel *c, char *rtype);
+static void port_open_helper(struct ssh *ssh, Channel *c, char *rtype);
 static const char *channel_rfwd_bind_host(const char *listen_host);
 
 /* non-blocking connect helpers */
 static int connect_next(struct channel_connect *);
 static void channel_connect_ctx_free(struct channel_connect *);
 
+/* Setup helper */
+static void channel_handler_init(struct ssh_channels *sc);
+
 /* -- channel core */
 
+void
+channel_init_channels(struct ssh *ssh)
+{
+	struct ssh_channels *sc;
+
+	if ((sc = calloc(1, sizeof(*sc))) == NULL ||
+	    (sc->channel_pre = calloc(SSH_CHANNEL_MAX_TYPE,
+	    sizeof(*sc->channel_pre))) == NULL ||
+	    (sc->channel_post = calloc(SSH_CHANNEL_MAX_TYPE,
+	    sizeof(*sc->channel_post))) == NULL)
+		fatal("%s: allocation failed", __func__);
+	sc->channels_alloc = 10;
+	sc->channels = xcalloc(sc->channels_alloc, sizeof(*sc->channels));
+	sc->IPv4or6 = AF_UNSPEC;
+	channel_handler_init(sc);
+
+	ssh->chanctxt = sc;
+}
+
 Channel *
-channel_by_id(int id)
+channel_by_id(struct ssh *ssh, int id)
 {
 	Channel *c;
 
-	if (id < 0 || (u_int)id >= channels_alloc) {
-		logit("channel_by_id: %d: bad id", id);
+	if (id < 0 || (u_int)id >= ssh->chanctxt->channels_alloc) {
+		logit("%s: %d: bad id", __func__, id);
 		return NULL;
 	}
-	c = channels[id];
+	c = ssh->chanctxt->channels[id];
 	if (c == NULL) {
-		logit("channel_by_id: %d: bad id: channel free", id);
+		logit("%s: %d: bad id: channel free", __func__, id);
 		return NULL;
 	}
 	return c;
 }
 
 Channel *
-channel_by_remote_id(int remote_id)
+channel_by_remote_id(struct ssh *ssh, int remote_id)
 {
 	Channel *c;
 	u_int i;
 
-	for (i = 0; i < channels_alloc; i++) {
-		c = channels[i];
+	for (i = 0; i < ssh->chanctxt->channels_alloc; i++) {
+		c = ssh->chanctxt->channels[i];
 		if (c != NULL && c->remote_id == remote_id)
 			return c;
 	}
@@ -228,12 +270,12 @@
  * Private channels, like listening sockets, may not receive messages.
  */
 Channel *
-channel_lookup(int id)
+channel_lookup(struct ssh *ssh, int id)
 {
 	Channel *c;
 
-	if ((c = channel_by_id(id)) == NULL)
-		return (NULL);
+	if ((c = channel_by_id(ssh, id)) == NULL)
+		return NULL;
 
 	switch (c->type) {
 	case SSH_CHANNEL_X11_OPEN:
@@ -244,10 +286,10 @@
 	case SSH_CHANNEL_OPEN:
 	case SSH_CHANNEL_ABANDONED:
 	case SSH_CHANNEL_MUX_PROXY:
-		return (c);
+		return c;
 	}
 	logit("Non-public channel %d, type %d.", id, c->type);
-	return (NULL);
+	return NULL;
 }
 
 /*
@@ -255,13 +297,15 @@
  * when the channel consumer/producer is ready, e.g. shell exec'd
  */
 static void
-channel_register_fds(Channel *c, int rfd, int wfd, int efd,
+channel_register_fds(struct ssh *ssh, Channel *c, int rfd, int wfd, int efd,
     int extusage, int nonblock, int is_tty)
 {
+	struct ssh_channels *sc = ssh->chanctxt;
+
 	/* Update the maximum file descriptor value. */
-	channel_max_fd = MAXIMUM(channel_max_fd, rfd);
-	channel_max_fd = MAXIMUM(channel_max_fd, wfd);
-	channel_max_fd = MAXIMUM(channel_max_fd, efd);
+	sc->channel_max_fd = MAXIMUM(sc->channel_max_fd, rfd);
+	sc->channel_max_fd = MAXIMUM(sc->channel_max_fd, wfd);
+	sc->channel_max_fd = MAXIMUM(sc->channel_max_fd, efd);
 
 	if (rfd != -1)
 		fcntl(rfd, F_SETFD, FD_CLOEXEC);
@@ -299,190 +343,221 @@
  * remote_name to be freed.
  */
 Channel *
-channel_new(char *ctype, int type, int rfd, int wfd, int efd,
+channel_new(struct ssh *ssh, char *ctype, int type, int rfd, int wfd, int efd,
     u_int window, u_int maxpack, int extusage, char *remote_name, int nonblock)
 {
-	int found;
-	u_int i;
+	struct ssh_channels *sc = ssh->chanctxt;
+	u_int i, found;
 	Channel *c;
 
-	/* Do initial allocation if this is the first call. */
-	if (channels_alloc == 0) {
-		channels_alloc = 10;
-		channels = xcalloc(channels_alloc, sizeof(Channel *));
-		for (i = 0; i < channels_alloc; i++)
-			channels[i] = NULL;
-	}
 	/* Try to find a free slot where to put the new channel. */
-	for (found = -1, i = 0; i < channels_alloc; i++)
-		if (channels[i] == NULL) {
+	for (i = 0; i < sc->channels_alloc; i++) {
+		if (sc->channels[i] == NULL) {
 			/* Found a free slot. */
-			found = (int)i;
+			found = i;
 			break;
 		}
-	if (found < 0) {
-		/* There are no free slots.  Take last+1 slot and expand the array.  */
-		found = channels_alloc;
-		if (channels_alloc > 10000)
-			fatal("channel_new: internal error: channels_alloc %d "
-			    "too big.", channels_alloc);
-		channels = xreallocarray(channels, channels_alloc + 10,
-		    sizeof(Channel *));
-		channels_alloc += 10;
-		debug2("channel: expanding %d", channels_alloc);
-		for (i = found; i < channels_alloc; i++)
-			channels[i] = NULL;
+	}
+	if (i >= sc->channels_alloc) {
+		/*
+		 * There are no free slots. Take last+1 slot and expand
+		 * the array.
+		 */
+		found = sc->channels_alloc;
+		if (sc->channels_alloc > CHANNELS_MAX_CHANNELS)
+			fatal("%s: internal error: channels_alloc %d too big",
+			    __func__, sc->channels_alloc);
+		sc->channels = xrecallocarray(sc->channels, sc->channels_alloc,
+		    sc->channels_alloc + 10, sizeof(*sc->channels));
+		sc->channels_alloc += 10;
+		debug2("channel: expanding %d", sc->channels_alloc);
 	}
 	/* Initialize and return new channel. */
-	c = channels[found] = xcalloc(1, sizeof(Channel));
-	buffer_init(&c->input);
-	buffer_init(&c->output);
-	buffer_init(&c->extended);
-	c->path = NULL;
-	c->listening_addr = NULL;
-	c->listening_port = 0;
+	c = sc->channels[found] = xcalloc(1, sizeof(Channel));
+	if ((c->input = sshbuf_new()) == NULL ||
+	    (c->output = sshbuf_new()) == NULL ||
+	    (c->extended = sshbuf_new()) == NULL)
+		fatal("%s: sshbuf_new failed", __func__);
 	c->ostate = CHAN_OUTPUT_OPEN;
 	c->istate = CHAN_INPUT_OPEN;
-	c->flags = 0;
-	channel_register_fds(c, rfd, wfd, efd, extusage, nonblock, 0);
-	c->notbefore = 0;
+	channel_register_fds(ssh, c, rfd, wfd, efd, extusage, nonblock, 0);
 	c->self = found;
 	c->type = type;
 	c->ctype = ctype;
 	c->local_window = window;
 	c->local_window_max = window;
-	c->local_consumed = 0;
 	c->local_maxpacket = maxpack;
 	c->remote_id = -1;
 	c->remote_name = xstrdup(remote_name);
-	c->remote_window = 0;
-	c->remote_maxpacket = 0;
-	c->force_drain = 0;
-	c->single_connection = 0;
-	c->detach_user = NULL;
-	c->detach_close = 0;
-	c->open_confirm = NULL;
-	c->open_confirm_ctx = NULL;
-	c->input_filter = NULL;
-	c->output_filter = NULL;
-	c->filter_ctx = NULL;
-	c->filter_cleanup = NULL;
 	c->ctl_chan = -1;
-	c->mux_rcb = NULL;
-	c->mux_ctx = NULL;
-	c->mux_pause = 0;
 	c->delayed = 1;		/* prevent call to channel_post handler */
 	TAILQ_INIT(&c->status_confirms);
 	debug("channel %d: new [%s]", found, remote_name);
 	return c;
 }
 
-static int
-channel_find_maxfd(void)
+static void
+channel_find_maxfd(struct ssh_channels *sc)
 {
 	u_int i;
 	int max = 0;
 	Channel *c;
 
-	for (i = 0; i < channels_alloc; i++) {
-		c = channels[i];
+	for (i = 0; i < sc->channels_alloc; i++) {
+		c = sc->channels[i];
 		if (c != NULL) {
 			max = MAXIMUM(max, c->rfd);
 			max = MAXIMUM(max, c->wfd);
 			max = MAXIMUM(max, c->efd);
 		}
 	}
-	return max;
+	sc->channel_max_fd = max;
 }
 
 int
-channel_close_fd(int *fdp)
+channel_close_fd(struct ssh *ssh, int *fdp)
 {
+	struct ssh_channels *sc = ssh->chanctxt;
 	int ret = 0, fd = *fdp;
 
 	if (fd != -1) {
 		ret = close(fd);
 		*fdp = -1;
-		if (fd == channel_max_fd)
-			channel_max_fd = channel_find_maxfd();
+		if (fd == sc->channel_max_fd)
+			channel_find_maxfd(sc);
 	}
 	return ret;
 }
 
 /* Close all channel fd/socket. */
 static void
-channel_close_fds(Channel *c)
+channel_close_fds(struct ssh *ssh, Channel *c)
 {
-	channel_close_fd(&c->sock);
-	channel_close_fd(&c->rfd);
-	channel_close_fd(&c->wfd);
-	channel_close_fd(&c->efd);
+	channel_close_fd(ssh, &c->sock);
+	channel_close_fd(ssh, &c->rfd);
+	channel_close_fd(ssh, &c->wfd);
+	channel_close_fd(ssh, &c->efd);
+}
+
+static void
+fwd_perm_clear(ForwardPermission *fp)
+{
+	free(fp->host_to_connect);
+	free(fp->listen_host);
+	free(fp->listen_path);
+	bzero(fp, sizeof(*fp));
+}
+
+enum { FWDPERM_USER, FWDPERM_ADMIN };
+
+static int
+fwd_perm_list_add(struct ssh *ssh, int which,
+    const char *host_to_connect, int port_to_connect,
+    const char *listen_host, const char *listen_path, int listen_port,
+    Channel *downstream)
+{
+	ForwardPermission **fpl;
+	u_int n, *nfpl;
+
+	switch (which) {
+	case FWDPERM_USER:
+		fpl = &ssh->chanctxt->permitted_opens;
+		nfpl = &ssh->chanctxt->num_permitted_opens;
+		break;
+	case FWDPERM_ADMIN:
+		fpl = &ssh->chanctxt->permitted_adm_opens;
+		nfpl = &ssh->chanctxt->num_adm_permitted_opens;
+		break;
+	default:
+		fatal("%s: invalid list %d", __func__, which);
+	}
+
+	if (*nfpl >= INT_MAX)
+		fatal("%s: overflow", __func__);
+
+	*fpl = xrecallocarray(*fpl, *nfpl, *nfpl + 1, sizeof(**fpl));
+	n = (*nfpl)++;
+#define MAYBE_DUP(s) ((s == NULL) ? NULL : xstrdup(s))
+	(*fpl)[n].host_to_connect = MAYBE_DUP(host_to_connect);
+	(*fpl)[n].port_to_connect = port_to_connect;
+	(*fpl)[n].listen_host = MAYBE_DUP(listen_host);
+	(*fpl)[n].listen_path = MAYBE_DUP(listen_path);
+	(*fpl)[n].listen_port = listen_port;
+	(*fpl)[n].downstream = downstream;
+#undef MAYBE_DUP
+	return (int)n;
+}
+
+static void
+mux_remove_remote_forwardings(struct ssh *ssh, Channel *c)
+{
+	struct ssh_channels *sc = ssh->chanctxt;
+	ForwardPermission *fp;
+	int r;
+	u_int i;
+
+	for (i = 0; i < sc->num_permitted_opens; i++) {
+		fp = &sc->permitted_opens[i];
+		if (fp->downstream != c)
+			continue;
+
+		/* cancel on the server, since mux client is gone */
+		debug("channel %d: cleanup remote forward for %s:%u",
+		    c->self, fp->listen_host, fp->listen_port);
+		if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 ||
+		    (r = sshpkt_put_cstring(ssh,
+		    "cancel-tcpip-forward")) != 0 ||
+		    (r = sshpkt_put_u8(ssh, 0)) != 0 ||
+		    (r = sshpkt_put_cstring(ssh,
+		    channel_rfwd_bind_host(fp->listen_host))) != 0 ||
+		    (r = sshpkt_put_u32(ssh, fp->listen_port)) != 0 ||
+		    (r = sshpkt_send(ssh)) != 0) {
+			fatal("%s: channel %i: %s", __func__,
+			    c->self, ssh_err(r));
+		}
+		fwd_perm_clear(fp); /* unregister */
+	}
 }
 
 /* Free the channel and close its fd/socket. */
 void
-channel_free(Channel *c)
+channel_free(struct ssh *ssh, Channel *c)
 {
+	struct ssh_channels *sc = ssh->chanctxt;
 	char *s;
 	u_int i, n;
 	Channel *other;
 	struct channel_confirm *cc;
 
-	for (n = 0, i = 0; i < channels_alloc; i++) {
-		if ((other = channels[i]) != NULL) {
-			n++;
-
-			/* detach from mux client and prepare for closing */
-			if (c->type == SSH_CHANNEL_MUX_CLIENT &&
-			    other->type == SSH_CHANNEL_MUX_PROXY &&
-			    other->mux_ctx == c) {
-				other->mux_ctx = NULL;
-				other->type = SSH_CHANNEL_OPEN;
-				other->istate = CHAN_INPUT_CLOSED;
-				other->ostate = CHAN_OUTPUT_CLOSED;
-			}
+	for (n = 0, i = 0; i < sc->channels_alloc; i++) {
+		if ((other = sc->channels[i]) == NULL)
+			continue;
+		n++;
+		/* detach from mux client and prepare for closing */
+		if (c->type == SSH_CHANNEL_MUX_CLIENT &&
+		    other->type == SSH_CHANNEL_MUX_PROXY &&
+		    other->mux_ctx == c) {
+			other->mux_ctx = NULL;
+			other->type = SSH_CHANNEL_OPEN;
+			other->istate = CHAN_INPUT_CLOSED;
+			other->ostate = CHAN_OUTPUT_CLOSED;
 		}
 	}
 	debug("channel %d: free: %s, nchannels %u", c->self,
 	    c->remote_name ? c->remote_name : "???", n);
 
-	/* XXX more MUX cleanup: remove remote forwardings */
-	if (c->type == SSH_CHANNEL_MUX_CLIENT) {
-		for (i = 0; i < (u_int)num_permitted_opens; i++) {
-			if (permitted_opens[i].downstream != c)
-				continue;
-			/* cancel on the server, since mux client is gone */
-			debug("channel %d: cleanup remote forward for %s:%u",
-			    c->self,
-			    permitted_opens[i].listen_host,
-			    permitted_opens[i].listen_port);
-			packet_start(SSH2_MSG_GLOBAL_REQUEST);
-			packet_put_cstring("cancel-tcpip-forward");
-			packet_put_char(0);
-			packet_put_cstring(channel_rfwd_bind_host(
-			    permitted_opens[i].listen_host));
-			packet_put_int(permitted_opens[i].listen_port);
-			packet_send();
-			/* unregister */
-			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;
-			free(permitted_opens[i].listen_host);
-			permitted_opens[i].listen_host = NULL;
-			permitted_opens[i].listen_path = NULL;
-			permitted_opens[i].downstream = NULL;
-		}
-	}
+	if (c->type == SSH_CHANNEL_MUX_CLIENT)
+		mux_remove_remote_forwardings(ssh, c);
 
-	s = channel_open_message();
+	s = channel_open_message(ssh);
 	debug3("channel %d: status: %s", c->self, s);
 	free(s);
 
-	channel_close_fds(c);
-	buffer_free(&c->input);
-	buffer_free(&c->output);
-	buffer_free(&c->extended);
+	channel_close_fds(ssh, c);
+	sshbuf_free(c->input);
+	sshbuf_free(c->output);
+	sshbuf_free(c->extended);
+	c->input = c->output = c->extended = NULL;
 	free(c->remote_name);
 	c->remote_name = NULL;
 	free(c->path);
@@ -491,25 +566,26 @@
 	c->listening_addr = NULL;
 	while ((cc = TAILQ_FIRST(&c->status_confirms)) != NULL) {
 		if (cc->abandon_cb != NULL)
-			cc->abandon_cb(c, cc->ctx);
+			cc->abandon_cb(ssh, c, cc->ctx);
 		TAILQ_REMOVE(&c->status_confirms, cc, entry);
 		explicit_bzero(cc, sizeof(*cc));
 		free(cc);
 	}
 	if (c->filter_cleanup != NULL && c->filter_ctx != NULL)
-		c->filter_cleanup(c->self, c->filter_ctx);
-	channels[c->self] = NULL;
+		c->filter_cleanup(ssh, c->self, c->filter_ctx);
+	sc->channels[c->self] = NULL;
+	bzero(c, sizeof(*c));
 	free(c);
 }
 
 void
-channel_free_all(void)
+channel_free_all(struct ssh *ssh)
 {
 	u_int i;
 
-	for (i = 0; i < channels_alloc; i++)
-		if (channels[i] != NULL)
-			channel_free(channels[i]);
+	for (i = 0; i < ssh->chanctxt->channels_alloc; i++)
+		if (ssh->chanctxt->channels[i] != NULL)
+			channel_free(ssh, ssh->chanctxt->channels[i]);
 }
 
 /*
@@ -517,26 +593,26 @@
  * descriptors after a fork.
  */
 void
-channel_close_all(void)
+channel_close_all(struct ssh *ssh)
 {
 	u_int i;
 
-	for (i = 0; i < channels_alloc; i++)
-		if (channels[i] != NULL)
-			channel_close_fds(channels[i]);
+	for (i = 0; i < ssh->chanctxt->channels_alloc; i++)
+		if (ssh->chanctxt->channels[i] != NULL)
+			channel_close_fds(ssh, ssh->chanctxt->channels[i]);
 }
 
 /*
  * Stop listening to channels.
  */
 void
-channel_stop_listening(void)
+channel_stop_listening(struct ssh *ssh)
 {
 	u_int i;
 	Channel *c;
 
-	for (i = 0; i < channels_alloc; i++) {
-		c = channels[i];
+	for (i = 0; i < ssh->chanctxt->channels_alloc; i++) {
+		c = ssh->chanctxt->channels[i];
 		if (c != NULL) {
 			switch (c->type) {
 			case SSH_CHANNEL_AUTH_SOCKET:
@@ -545,8 +621,8 @@
 			case SSH_CHANNEL_X11_LISTENER:
 			case SSH_CHANNEL_UNIX_LISTENER:
 			case SSH_CHANNEL_RUNIX_LISTENER:
-				channel_close_fd(&c->sock);
-				channel_free(c);
+				channel_close_fd(ssh, &c->sock);
+				channel_free(ssh, c);
 				break;
 			}
 		}
@@ -558,20 +634,20 @@
  * more channel is overfull.
  */
 int
-channel_not_very_much_buffered_data(void)
+channel_not_very_much_buffered_data(struct ssh *ssh)
 {
 	u_int i;
+	u_int maxsize = ssh_packet_get_maxsize(ssh);
 	Channel *c;
 
-	for (i = 0; i < channels_alloc; i++) {
-		c = channels[i];
-		if (c != NULL && c->type == SSH_CHANNEL_OPEN) {
-			if (buffer_len(&c->output) > packet_get_maxsize()) {
-				debug2("channel %d: big output buffer %u > %u",
-				    c->self, buffer_len(&c->output),
-				    packet_get_maxsize());
-				return 0;
-			}
+	for (i = 0; i < ssh->chanctxt->channels_alloc; i++) {
+		c = ssh->chanctxt->channels[i];
+		if (c == NULL || c->type != SSH_CHANNEL_OPEN)
+			continue;
+		if (sshbuf_len(c->output) > maxsize) {
+			debug2("channel %d: big output buffer %zu > %u",
+			    c->self, sshbuf_len(c->output), maxsize);
+			return 0;
 		}
 	}
 	return 1;
@@ -579,13 +655,13 @@
 
 /* Returns true if any channel is still open. */
 int
-channel_still_open(void)
+channel_still_open(struct ssh *ssh)
 {
 	u_int i;
 	Channel *c;
 
-	for (i = 0; i < channels_alloc; i++) {
-		c = channels[i];
+	for (i = 0; i < ssh->chanctxt->channels_alloc; i++) {
+		c = ssh->chanctxt->channels[i];
 		if (c == NULL)
 			continue;
 		switch (c->type) {
@@ -620,13 +696,13 @@
 
 /* Returns the id of an open channel suitable for keepaliving */
 int
-channel_find_open(void)
+channel_find_open(struct ssh *ssh)
 {
 	u_int i;
 	Channel *c;
 
-	for (i = 0; i < channels_alloc; i++) {
-		c = channels[i];
+	for (i = 0; i < ssh->chanctxt->channels_alloc; i++) {
+		c = ssh->chanctxt->channels[i];
 		if (c == NULL || c->remote_id < 0)
 			continue;
 		switch (c->type) {
@@ -664,18 +740,21 @@
  * newlines.
  */
 char *
-channel_open_message(void)
+channel_open_message(struct ssh *ssh)
 {
-	Buffer buffer;
+	struct sshbuf *buf;
 	Channel *c;
-	char buf[1024], *cp;
 	u_int i;
+	int r;
+	char *ret;
 
-	buffer_init(&buffer);
-	snprintf(buf, sizeof buf, "The following connections are open:\r\n");
-	buffer_append(&buffer, buf, strlen(buf));
-	for (i = 0; i < channels_alloc; i++) {
-		c = channels[i];
+	if ((buf = sshbuf_new()) == NULL)
+		fatal("%s: sshbuf_new", __func__);
+	if ((r = sshbuf_putf(buf,
+	    "The following connections are open:\r\n")) != 0)
+		fatal("%s: sshbuf_putf: %s", __func__, ssh_err(r));
+	for (i = 0; i < ssh->chanctxt->channels_alloc; i++) {
+		c = ssh->chanctxt->channels[i];
 		if (c == NULL)
 			continue;
 		switch (c->type) {
@@ -698,69 +777,85 @@
 		case SSH_CHANNEL_X11_OPEN:
 		case SSH_CHANNEL_MUX_PROXY:
 		case SSH_CHANNEL_MUX_CLIENT:
-			snprintf(buf, sizeof buf,
-			    "  #%d %.300s (t%d r%d i%u/%d o%u/%d fd %d/%d cc %d)\r\n",
+			if ((r = sshbuf_putf(buf, "  #%d %.300s "
+			    "(t%d r%d i%u/%zu o%u/%zu fd %d/%d cc %d)\r\n",
 			    c->self, c->remote_name,
 			    c->type, c->remote_id,
-			    c->istate, buffer_len(&c->input),
-			    c->ostate, buffer_len(&c->output),
-			    c->rfd, c->wfd, c->ctl_chan);
-			buffer_append(&buffer, buf, strlen(buf));
+			    c->istate, sshbuf_len(c->input),
+			    c->ostate, sshbuf_len(c->output),
+			    c->rfd, c->wfd, c->ctl_chan)) != 0)
+				fatal("%s: sshbuf_putf: %s",
+				    __func__, ssh_err(r));
 			continue;
 		default:
-			fatal("channel_open_message: bad channel type %d", c->type);
+			fatal("%s: bad channel type %d", __func__, c->type);
 			/* NOTREACHED */
 		}
 	}
-	buffer_append(&buffer, "\0", 1);
-	cp = xstrdup((char *)buffer_ptr(&buffer));
-	buffer_free(&buffer);
-	return cp;
+	if ((ret = sshbuf_dup_string(buf)) == NULL)
+		fatal("%s: sshbuf_dup_string", __func__);
+	sshbuf_free(buf);
+	return ret;
+}
+
+static void
+open_preamble(struct ssh *ssh, const char *where, Channel *c, const char *type)
+{
+	int r;
+
+	if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_OPEN)) != 0 ||
+	    (r = sshpkt_put_cstring(ssh, type)) != 0 ||
+	    (r = sshpkt_put_u32(ssh, c->self)) != 0 ||
+	    (r = sshpkt_put_u32(ssh, c->local_window)) != 0 ||
+	    (r = sshpkt_put_u32(ssh, c->local_maxpacket)) != 0) {
+		fatal("%s: channel %i: open: %s", where, c->self, ssh_err(r));
+	}
 }
 
 void
-channel_send_open(int id)
+channel_send_open(struct ssh *ssh, int id)
 {
-	Channel *c = channel_lookup(id);
+	Channel *c = channel_lookup(ssh, id);
+	int r;
 
 	if (c == NULL) {
 		logit("channel_send_open: %d: bad id", id);
 		return;
 	}
 	debug2("channel %d: send open", id);
-	packet_start(SSH2_MSG_CHANNEL_OPEN);
-	packet_put_cstring(c->ctype);
-	packet_put_int(c->self);
-	packet_put_int(c->local_window);
-	packet_put_int(c->local_maxpacket);
-	packet_send();
+	open_preamble(ssh, __func__, c, c->ctype);
+	if ((r = sshpkt_send(ssh)) != 0)
+		fatal("%s: channel %i: %s", __func__, c->self, ssh_err(r));
 }
 
 void
-channel_request_start(int id, char *service, int wantconfirm)
+channel_request_start(struct ssh *ssh, int id, char *service, int wantconfirm)
 {
-	Channel *c = channel_lookup(id);
+	Channel *c = channel_lookup(ssh, id);
+	int r;
 
 	if (c == NULL) {
-		logit("channel_request_start: %d: unknown channel id", id);
+		logit("%s: %d: unknown channel id", __func__, id);
 		return;
 	}
 	debug2("channel %d: request %s confirm %d", id, service, wantconfirm);
-	packet_start(SSH2_MSG_CHANNEL_REQUEST);
-	packet_put_int(c->remote_id);
-	packet_put_cstring(service);
-	packet_put_char(wantconfirm);
+	if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_REQUEST)) != 0 ||
+	    (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
+	    (r = sshpkt_put_cstring(ssh, service)) != 0 ||
+	    (r = sshpkt_put_u8(ssh, wantconfirm)) != 0) {
+		fatal("%s: channel %i: %s", __func__, c->self, ssh_err(r));
+	}
 }
 
 void
-channel_register_status_confirm(int id, channel_confirm_cb *cb,
-    channel_confirm_abandon_cb *abandon_cb, void *ctx)
+channel_register_status_confirm(struct ssh *ssh, int id,
+    channel_confirm_cb *cb, channel_confirm_abandon_cb *abandon_cb, void *ctx)
 {
 	struct channel_confirm *cc;
 	Channel *c;
 
-	if ((c = channel_lookup(id)) == NULL)
-		fatal("channel_register_expect: %d: bad id", id);
+	if ((c = channel_lookup(ssh, id)) == NULL)
+		fatal("%s: %d: bad id", __func__, id);
 
 	cc = xcalloc(1, sizeof(*cc));
 	cc->cb = cb;
@@ -770,12 +865,13 @@
 }
 
 void
-channel_register_open_confirm(int id, channel_open_fn *fn, void *ctx)
+channel_register_open_confirm(struct ssh *ssh, int id,
+    channel_open_fn *fn, void *ctx)
 {
-	Channel *c = channel_lookup(id);
+	Channel *c = channel_lookup(ssh, id);
 
 	if (c == NULL) {
-		logit("channel_register_open_confirm: %d: bad id", id);
+		logit("%s: %d: bad id", __func__, id);
 		return;
 	}
 	c->open_confirm = fn;
@@ -783,12 +879,13 @@
 }
 
 void
-channel_register_cleanup(int id, channel_callback_fn *fn, int do_close)
+channel_register_cleanup(struct ssh *ssh, int id,
+    channel_callback_fn *fn, int do_close)
 {
-	Channel *c = channel_by_id(id);
+	Channel *c = channel_by_id(ssh, id);
 
 	if (c == NULL) {
-		logit("channel_register_cleanup: %d: bad id", id);
+		logit("%s: %d: bad id", __func__, id);
 		return;
 	}
 	c->detach_user = fn;
@@ -796,12 +893,12 @@
 }
 
 void
-channel_cancel_cleanup(int id)
+channel_cancel_cleanup(struct ssh *ssh, int id)
 {
-	Channel *c = channel_by_id(id);
+	Channel *c = channel_by_id(ssh, id);
 
 	if (c == NULL) {
-		logit("channel_cancel_cleanup: %d: bad id", id);
+		logit("%s: %d: bad id", __func__, id);
 		return;
 	}
 	c->detach_user = NULL;
@@ -809,13 +906,13 @@
 }
 
 void
-channel_register_filter(int id, channel_infilter_fn *ifn,
+channel_register_filter(struct ssh *ssh, int id, channel_infilter_fn *ifn,
     channel_outfilter_fn *ofn, channel_filter_cleanup_fn *cfn, void *ctx)
 {
-	Channel *c = channel_lookup(id);
+	Channel *c = channel_lookup(ssh, id);
 
 	if (c == NULL) {
-		logit("channel_register_filter: %d: bad id", id);
+		logit("%s: %d: bad id", __func__, id);
 		return;
 	}
 	c->input_filter = ifn;
@@ -825,79 +922,72 @@
 }
 
 void
-channel_set_fds(int id, int rfd, int wfd, int efd,
+channel_set_fds(struct ssh *ssh, int id, int rfd, int wfd, int efd,
     int extusage, int nonblock, int is_tty, u_int window_max)
 {
-	Channel *c = channel_lookup(id);
+	Channel *c = channel_lookup(ssh, id);
+	int r;
 
 	if (c == NULL || c->type != SSH_CHANNEL_LARVAL)
 		fatal("channel_activate for non-larval channel %d.", id);
-	channel_register_fds(c, rfd, wfd, efd, extusage, nonblock, is_tty);
+	channel_register_fds(ssh, c, rfd, wfd, efd, extusage, nonblock, is_tty);
 	c->type = SSH_CHANNEL_OPEN;
 	c->local_window = c->local_window_max = window_max;
-	packet_start(SSH2_MSG_CHANNEL_WINDOW_ADJUST);
-	packet_put_int(c->remote_id);
-	packet_put_int(c->local_window);
-	packet_send();
+
+	if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_WINDOW_ADJUST)) != 0 ||
+	    (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
+	    (r = sshpkt_put_u32(ssh, c->local_window)) != 0 ||
+	    (r = sshpkt_send(ssh)) != 0)
+		fatal("%s: channel %i: %s", __func__, c->self, ssh_err(r));
 }
 
-/*
- * 'channel_pre*' are called just before select() to add any bits relevant to
- * channels in the select bitmasks.
- */
-/*
- * 'channel_post*': perform any appropriate operations for channels which
- * have events pending.
- */
-typedef void chan_fn(Channel *c, fd_set *readset, fd_set *writeset);
-chan_fn *channel_pre[SSH_CHANNEL_MAX_TYPE];
-chan_fn *channel_post[SSH_CHANNEL_MAX_TYPE];
-
-/* ARGSUSED */
 static void
-channel_pre_listener(Channel *c, fd_set *readset, fd_set *writeset)
+channel_pre_listener(struct ssh *ssh, Channel *c,
+    fd_set *readset, fd_set *writeset)
 {
 	FD_SET(c->sock, readset);
 }
 
-/* ARGSUSED */
 static void
-channel_pre_connecting(Channel *c, fd_set *readset, fd_set *writeset)
+channel_pre_connecting(struct ssh *ssh, Channel *c,
+    fd_set *readset, fd_set *writeset)
 {
 	debug3("channel %d: waiting for connection", c->self);
 	FD_SET(c->sock, writeset);
 }
 
 static void
-channel_pre_open(Channel *c, fd_set *readset, fd_set *writeset)
+channel_pre_open(struct ssh *ssh, Channel *c,
+    fd_set *readset, fd_set *writeset)
 {
 	if (c->istate == CHAN_INPUT_OPEN &&
 	    c->remote_window > 0 &&
-	    buffer_len(&c->input) < c->remote_window &&
-	    buffer_check_alloc(&c->input, CHAN_RBUF))
+	    sshbuf_len(c->input) < c->remote_window &&
+	    sshbuf_check_reserve(c->input, CHAN_RBUF) == 0)
 		FD_SET(c->rfd, readset);
 	if (c->ostate == CHAN_OUTPUT_OPEN ||
 	    c->ostate == CHAN_OUTPUT_WAIT_DRAIN) {
-		if (buffer_len(&c->output) > 0) {
+		if (sshbuf_len(c->output) > 0) {
 			FD_SET(c->wfd, writeset);
 		} else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN) {
 			if (CHANNEL_EFD_OUTPUT_ACTIVE(c))
-				debug2("channel %d: obuf_empty delayed efd %d/(%d)",
-				    c->self, c->efd, buffer_len(&c->extended));
+				debug2("channel %d: "
+				    "obuf_empty delayed efd %d/(%zu)", c->self,
+				    c->efd, sshbuf_len(c->extended));
 			else
-				chan_obuf_empty(c);
+				chan_obuf_empty(ssh, c);
 		}
 	}
 	/** XXX check close conditions, too */
 	if (c->efd != -1 && !(c->istate == CHAN_INPUT_CLOSED &&
 	    c->ostate == CHAN_OUTPUT_CLOSED)) {
 		if (c->extended_usage == CHAN_EXTENDED_WRITE &&
-		    buffer_len(&c->extended) > 0)
+		    sshbuf_len(c->extended) > 0)
 			FD_SET(c->efd, writeset);
 		else if (c->efd != -1 && !(c->flags & CHAN_EOF_SENT) &&
 		    (c->extended_usage == CHAN_EXTENDED_READ ||
 		    c->extended_usage == CHAN_EXTENDED_IGNORE) &&
-		    buffer_len(&c->extended) < c->remote_window)
+		    sshbuf_len(c->extended) < c->remote_window)
 			FD_SET(c->efd, readset);
 	}
 	/* XXX: What about efd? races? */
@@ -913,24 +1003,26 @@
  * Returns: 0 = need more data, -1 = wrong cookie, 1 = ok
  */
 static int
-x11_open_helper(Buffer *b)
+x11_open_helper(struct ssh *ssh, struct sshbuf *b)
 {
+	struct ssh_channels *sc = ssh->chanctxt;
 	u_char *ucp;
 	u_int proto_len, data_len;
 
 	/* Is this being called after the refusal deadline? */
-	if (x11_refuse_time != 0 && (u_int)monotime() >= x11_refuse_time) {
+	if (sc->x11_refuse_time != 0 &&
+	    (u_int)monotime() >= sc->x11_refuse_time) {
 		verbose("Rejected X11 connection after ForwardX11Timeout "
 		    "expired");
 		return -1;
 	}
 
 	/* Check if the fixed size part of the packet is in buffer. */
-	if (buffer_len(b) < 12)
+	if (sshbuf_len(b) < 12)
 		return 0;
 
 	/* Parse the lengths of variable-length fields. */
-	ucp = buffer_ptr(b);
+	ucp = sshbuf_mutable_ptr(b);
 	if (ucp[0] == 0x42) {	/* Byte order MSB first. */
 		proto_len = 256 * ucp[6] + ucp[7];
 		data_len = 256 * ucp[8] + ucp[9];
@@ -944,27 +1036,27 @@
 	}
 
 	/* Check if the whole packet is in buffer. */
-	if (buffer_len(b) <
+	if (sshbuf_len(b) <
 	    12 + ((proto_len + 3) & ~3) + ((data_len + 3) & ~3))
 		return 0;
 
 	/* Check if authentication protocol matches. */
-	if (proto_len != strlen(x11_saved_proto) ||
-	    memcmp(ucp + 12, x11_saved_proto, proto_len) != 0) {
+	if (proto_len != strlen(sc->x11_saved_proto) ||
+	    memcmp(ucp + 12, sc->x11_saved_proto, proto_len) != 0) {
 		debug2("X11 connection uses different authentication protocol.");
 		return -1;
 	}
 	/* Check if authentication data matches our fake data. */
-	if (data_len != x11_fake_data_len ||
+	if (data_len != sc->x11_fake_data_len ||
 	    timingsafe_bcmp(ucp + 12 + ((proto_len + 3) & ~3),
-		x11_fake_data, x11_fake_data_len) != 0) {
+		sc->x11_fake_data, sc->x11_fake_data_len) != 0) {
 		debug2("X11 auth data does not match fake data.");
 		return -1;
 	}
 	/* Check fake data length */
-	if (x11_fake_data_len != x11_saved_data_len) {
+	if (sc->x11_fake_data_len != sc->x11_saved_data_len) {
 		error("X11 fake_data_len %d != saved_data_len %d",
-		    x11_fake_data_len, x11_saved_data_len);
+		    sc->x11_fake_data_len, sc->x11_saved_data_len);
 		return -1;
 	}
 	/*
@@ -973,60 +1065,64 @@
 	 * data.
 	 */
 	memcpy(ucp + 12 + ((proto_len + 3) & ~3),
-	    x11_saved_data, x11_saved_data_len);
+	    sc->x11_saved_data, sc->x11_saved_data_len);
 	return 1;
 }
 
 static void
-channel_pre_x11_open(Channel *c, fd_set *readset, fd_set *writeset)
+channel_pre_x11_open(struct ssh *ssh, Channel *c,
+    fd_set *readset, fd_set *writeset)
 {
-	int ret = x11_open_helper(&c->output);
+	int ret = x11_open_helper(ssh, c->output);
 
 	/* c->force_drain = 1; */
 
 	if (ret == 1) {
 		c->type = SSH_CHANNEL_OPEN;
-		channel_pre_open(c, readset, writeset);
+		channel_pre_open(ssh, c, readset, writeset);
 	} else if (ret == -1) {
 		logit("X11 connection rejected because of wrong authentication.");
-		debug2("X11 rejected %d i%d/o%d", c->self, c->istate, c->ostate);
-		chan_read_failed(c);
-		buffer_clear(&c->input);
-		chan_ibuf_empty(c);
-		buffer_clear(&c->output);
-		chan_write_failed(c);
+		debug2("X11 rejected %d i%d/o%d",
+		    c->self, c->istate, c->ostate);
+		chan_read_failed(ssh, c);
+		sshbuf_reset(c->input);
+		chan_ibuf_empty(ssh, c);
+		sshbuf_reset(c->output);
+		chan_write_failed(ssh, c);
 		debug2("X11 closed %d i%d/o%d", c->self, c->istate, c->ostate);
 	}
 }
 
 static void
-channel_pre_mux_client(Channel *c, fd_set *readset, fd_set *writeset)
+channel_pre_mux_client(struct ssh *ssh,
+    Channel *c, fd_set *readset, fd_set *writeset)
 {
 	if (c->istate == CHAN_INPUT_OPEN && !c->mux_pause &&
-	    buffer_check_alloc(&c->input, CHAN_RBUF))
+	    sshbuf_check_reserve(c->input, CHAN_RBUF) == 0)
 		FD_SET(c->rfd, readset);
 	if (c->istate == CHAN_INPUT_WAIT_DRAIN) {
 		/* clear buffer immediately (discard any partial packet) */
-		buffer_clear(&c->input);
-		chan_ibuf_empty(c);
+		sshbuf_reset(c->input);
+		chan_ibuf_empty(ssh, c);
 		/* Start output drain. XXX just kill chan? */
-		chan_rcvd_oclose(c);
+		chan_rcvd_oclose(ssh, c);
 	}
 	if (c->ostate == CHAN_OUTPUT_OPEN ||
 	    c->ostate == CHAN_OUTPUT_WAIT_DRAIN) {
-		if (buffer_len(&c->output) > 0)
+		if (sshbuf_len(c->output) > 0)
 			FD_SET(c->wfd, writeset);
 		else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN)
-			chan_obuf_empty(c);
+			chan_obuf_empty(ssh, c);
 	}
 }
 
 /* try to decode a socks4 header */
-/* ARGSUSED */
 static int
-channel_decode_socks4(Channel *c, fd_set *readset, fd_set *writeset)
+channel_decode_socks4(struct ssh *ssh, Channel *c,
+    fd_set *readset, fd_set *writeset)
 {
-	char *p, *host;
+	const u_char *p;
+	char *host;
 	u_int len, have, i, found, need;
 	char username[256];
 	struct {
@@ -1035,14 +1131,15 @@
 		u_int16_t dest_port;
 		struct in_addr dest_addr;
 	} s4_req, s4_rsp;
+	int r;
 
 	debug2("channel %d: decode socks4", c->self);
 
-	have = buffer_len(&c->input);
+	have = sshbuf_len(c->input);
 	len = sizeof(s4_req);
 	if (have < len)
 		return 0;
-	p = (char *)buffer_ptr(&c->input);
+	p = sshbuf_ptr(c->input);
 
 	need = 1;
 	/* SOCKS4A uses an invalid IP address 0.0.0.x */
@@ -1067,12 +1164,15 @@
 	}
 	if (found < need)
 		return 0;
-	buffer_get(&c->input, (char *)&s4_req.version, 1);
-	buffer_get(&c->input, (char *)&s4_req.command, 1);
-	buffer_get(&c->input, (char *)&s4_req.dest_port, 2);
-	buffer_get(&c->input, (char *)&s4_req.dest_addr, 4);
-	have = buffer_len(&c->input);
-	p = (char *)buffer_ptr(&c->input);
+	if ((r = sshbuf_get(c->input, &s4_req.version, 1)) != 0 ||
+	    (r = sshbuf_get(c->input, &s4_req.command, 1)) != 0 ||
+	    (r = sshbuf_get(c->input, &s4_req.dest_port, 2)) != 0 ||
+	    (r = sshbuf_get(c->input, &s4_req.dest_addr, 4)) != 0) {
+		debug("channels %d: decode socks4: %s", c->self, ssh_err(r));
+		return -1;
+	}
+	have = sshbuf_len(c->input);
+	p = sshbuf_ptr(c->input);
 	if (memchr(p, '\0', have) == NULL) {
 		error("channel %d: decode socks4: user not nul terminated",
 		    c->self);
@@ -1080,21 +1180,20 @@
 	}
 	len = strlen(p);
 	debug2("channel %d: decode socks4: user %s/%d", c->self, p, len);
-	len++;					/* trailing '\0' */
-	if (len > have)
-		fatal("channel %d: decode socks4: len %d > have %d",
-		    c->self, len, have);
+	len++; /* trailing '\0' */
 	strlcpy(username, p, sizeof(username));
-	buffer_consume(&c->input, len);
-
+	if ((r = sshbuf_consume(c->input, len)) != 0) {
+		fatal("%s: channel %d: consume: %s", __func__,
+		    c->self, ssh_err(r));
+	}
 	free(c->path);
 	c->path = NULL;
 	if (need == 1) {			/* SOCKS4: one string */
 		host = inet_ntoa(s4_req.dest_addr);
 		c->path = xstrdup(host);
 	} else {				/* SOCKS4A: two strings */
-		have = buffer_len(&c->input);
-		p = (char *)buffer_ptr(&c->input);
+		have = sshbuf_len(c->input);
+		p = sshbuf_ptr(c->input);
 		if (memchr(p, '\0', have) == NULL) {
 			error("channel %d: decode socks4a: host not nul "
 			    "terminated", c->self);
@@ -1110,7 +1209,10 @@
 			return -1;
 		}
 		c->path = xstrdup(p);
-		buffer_consume(&c->input, len);
+		if ((r = sshbuf_consume(c->input, len)) != 0) {
+			fatal("%s: channel %d: consume: %s", __func__,
+			    c->self, ssh_err(r));
+		}
 	}
 	c->host_port = ntohs(s4_req.dest_port);
 
@@ -1126,7 +1228,10 @@
 	s4_rsp.command = 90;			/* cd: req granted */
 	s4_rsp.dest_port = 0;			/* ignored */
 	s4_rsp.dest_addr.s_addr = INADDR_ANY;	/* ignored */
-	buffer_append(&c->output, &s4_rsp, sizeof(s4_rsp));
+	if ((r = sshbuf_put(c->output, &s4_rsp, sizeof(s4_rsp))) != 0) {
+		fatal("%s: channel %d: append reply: %s", __func__,
+		    c->self, ssh_err(r));
+	}
 	return 1;
 }
 
@@ -1139,10 +1244,11 @@
 #define SSH_SOCKS5_CONNECT	0x01
 #define SSH_SOCKS5_SUCCESS	0x00
 
-/* ARGSUSED */
 static int
-channel_decode_socks5(Channel *c, fd_set *readset, fd_set *writeset)
+channel_decode_socks5(struct ssh *ssh, Channel *c,
+    fd_set *readset, fd_set *writeset)
 {
+	/* XXX use get/put_u8 instead of trusting struct padding */
 	struct {
 		u_int8_t version;
 		u_int8_t command;
@@ -1151,14 +1257,15 @@
 	} s5_req, s5_rsp;
 	u_int16_t dest_port;
 	char dest_addr[255+1], ntop[INET6_ADDRSTRLEN];
-	u_char *p;
+	const u_char *p;
 	u_int have, need, i, found, nmethods, addrlen, af;
+	int r;
 
 	debug2("channel %d: decode socks5", c->self);
-	p = buffer_ptr(&c->input);
+	p = sshbuf_ptr(c->input);
 	if (p[0] != 0x05)
 		return -1;
-	have = buffer_len(&c->input);
+	have = sshbuf_len(c->input);
 	if (!(c->flags & SSH_SOCKS5_AUTHDONE)) {
 		/* format: ver | nmethods | methods */
 		if (have < 2)
@@ -1178,9 +1285,16 @@
 			    c->self);
 			return -1;
 		}
-		buffer_consume(&c->input, nmethods + 2);
-		buffer_put_char(&c->output, 0x05);		/* version */
-		buffer_put_char(&c->output, SSH_SOCKS5_NOAUTH);	/* method */
+		if ((r = sshbuf_consume(c->input, nmethods + 2)) != 0) {
+			fatal("%s: channel %d: consume: %s", __func__,
+			    c->self, ssh_err(r));
+		}
+		/* version, method */
+		if ((r = sshbuf_put_u8(c->output, 0x05)) != 0 ||
+		    (r = sshbuf_put_u8(c->output, SSH_SOCKS5_NOAUTH)) != 0) {
+			fatal("%s: channel %d: append reply: %s", __func__,
+			    c->self, ssh_err(r));
+		}
 		FD_SET(c->sock, writeset);
 		c->flags |= SSH_SOCKS5_AUTHDONE;
 		debug2("channel %d: socks5 auth done", c->self);
@@ -1218,11 +1332,22 @@
 		need++;
 	if (have < need)
 		return 0;
-	buffer_consume(&c->input, sizeof(s5_req));
-	if (s5_req.atyp == SSH_SOCKS5_DOMAIN)
-		buffer_consume(&c->input, 1);    /* host string length */
-	buffer_get(&c->input, &dest_addr, addrlen);
-	buffer_get(&c->input, (char *)&dest_port, 2);
+	if ((r = sshbuf_consume(c->input, sizeof(s5_req))) != 0) {
+		fatal("%s: channel %d: consume: %s", __func__,
+		    c->self, ssh_err(r));
+	}
+	if (s5_req.atyp == SSH_SOCKS5_DOMAIN) {
+		/* host string length */
+		if ((r = sshbuf_consume(c->input, 1)) != 0) {
+			fatal("%s: channel %d: consume: %s", __func__,
+			    c->self, ssh_err(r));
+		}
+	}
+	if ((r = sshbuf_get(c->input, &dest_addr, addrlen)) != 0 ||
+	    (r = sshbuf_get(c->input, &dest_port, 2)) != 0) {
+		debug("channel %d: parse addr/port: %s", c->self, ssh_err(r));
+		return -1;
+	}
 	dest_addr[addrlen] = '\0';
 	free(c->path);
 	c->path = NULL;
@@ -1249,22 +1374,23 @@
 	s5_rsp.atyp = SSH_SOCKS5_IPV4;
 	dest_port = 0;				/* ignored */
 
-	buffer_append(&c->output, &s5_rsp, sizeof(s5_rsp));
-	buffer_put_int(&c->output, ntohl(INADDR_ANY)); /* bind address */
-	buffer_append(&c->output, &dest_port, sizeof(dest_port));
+	if ((r = sshbuf_put(c->output, &s5_rsp, sizeof(s5_rsp))) != 0 ||
+	    (r = sshbuf_put_u32(c->output, ntohl(INADDR_ANY))) != 0 ||
+	    (r = sshbuf_put(c->output, &dest_port, sizeof(dest_port))) != 0)
+		fatal("%s: channel %d: append reply: %s", __func__,
+		    c->self, ssh_err(r));
 	return 1;
 }
 
 Channel *
-channel_connect_stdio_fwd(const char *host_to_connect, u_short port_to_connect,
-    int in, int out)
+channel_connect_stdio_fwd(struct ssh *ssh,
+    const char *host_to_connect, u_short port_to_connect, int in, int out)
 {
 	Channel *c;
 
-	debug("channel_connect_stdio_fwd %s:%d", host_to_connect,
-	    port_to_connect);
+	debug("%s %s:%d", __func__, host_to_connect, port_to_connect);
 
-	c = channel_new("stdio-forward", SSH_CHANNEL_OPENING, in, out,
+	c = channel_new(ssh, "stdio-forward", SSH_CHANNEL_OPENING, in, out,
 	    -1, CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT,
 	    0, "stdio-forward", /*nonblock*/0);
 
@@ -1273,23 +1399,24 @@
 	c->listening_port = 0;
 	c->force_drain = 1;
 
-	channel_register_fds(c, in, out, -1, 0, 1, 0);
-	port_open_helper(c, "direct-tcpip");
+	channel_register_fds(ssh, c, in, out, -1, 0, 1, 0);
+	port_open_helper(ssh, c, "direct-tcpip");
 
 	return c;
 }
 
 /* dynamic port forwarding */
 static void
-channel_pre_dynamic(Channel *c, fd_set *readset, fd_set *writeset)
+channel_pre_dynamic(struct ssh *ssh, Channel *c,
+    fd_set *readset, fd_set *writeset)
 {
-	u_char *p;
+	const u_char *p;
 	u_int have;
 	int ret;
 
-	have = buffer_len(&c->input);
+	have = sshbuf_len(c->input);
 	debug2("channel %d: pre_dynamic: have %d", c->self, have);
-	/* buffer_dump(&c->input); */
+	/* sshbuf_dump(c->input, stderr); */
 	/* check if the fixed size part of the packet is in buffer. */
 	if (have < 3) {
 		/* need more */
@@ -1297,20 +1424,21 @@
 		return;
 	}
 	/* try to guess the protocol */
-	p = buffer_ptr(&c->input);
+	p = sshbuf_ptr(c->input);
+	/* XXX sshbuf_peek_u8? */
 	switch (p[0]) {
 	case 0x04:
-		ret = channel_decode_socks4(c, readset, writeset);
+		ret = channel_decode_socks4(ssh, c, readset, writeset);
 		break;
 	case 0x05:
-		ret = channel_decode_socks5(c, readset, writeset);
+		ret = channel_decode_socks5(ssh, c, readset, writeset);
 		break;
 	default:
 		ret = -1;
 		break;
 	}
 	if (ret < 0) {
-		chan_mark_dead(c);
+		chan_mark_dead(ssh, c);
 	} else if (ret == 0) {
 		debug2("channel %d: pre_dynamic: need more", c->self);
 		/* need more */
@@ -1318,75 +1446,75 @@
 	} else {
 		/* switch to the next state */
 		c->type = SSH_CHANNEL_OPENING;
-		port_open_helper(c, "direct-tcpip");
+		port_open_helper(ssh, c, "direct-tcpip");
 	}
 }
 
 /* This is our fake X11 server socket. */
-/* ARGSUSED */
 static void
-channel_post_x11_listener(Channel *c, fd_set *readset, fd_set *writeset)
+channel_post_x11_listener(struct ssh *ssh, Channel *c,
+    fd_set *readset, fd_set *writeset)
 {
 	Channel *nc;
 	struct sockaddr_storage addr;
-	int newsock, oerrno;
+	int r, newsock, oerrno, remote_port;
 	socklen_t addrlen;
 	char buf[16384], *remote_ipaddr;
-	int remote_port;
 
-	if (FD_ISSET(c->sock, readset)) {
-		debug("X11 connection requested.");
-		addrlen = sizeof(addr);
-		newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen);
-		if (c->single_connection) {
-			oerrno = errno;
-			debug2("single_connection: closing X11 listener.");
-			channel_close_fd(&c->sock);
-			chan_mark_dead(c);
-			errno = oerrno;
-		}
-		if (newsock < 0) {
-			if (errno != EINTR && errno != EWOULDBLOCK &&
-			    errno != ECONNABORTED)
-				error("accept: %.100s", strerror(errno));
-			if (errno == EMFILE || errno == ENFILE)
-				c->notbefore = monotime() + 1;
-			return;
-		}
-		set_nodelay(newsock);
-		remote_ipaddr = get_peer_ipaddr(newsock);
-		remote_port = get_peer_port(newsock);
-		snprintf(buf, sizeof buf, "X11 connection from %.200s port %d",
-		    remote_ipaddr, remote_port);
+	if (!FD_ISSET(c->sock, readset))
+		return;
 
-		nc = channel_new("accepted x11 socket",
-		    SSH_CHANNEL_OPENING, newsock, newsock, -1,
-		    c->local_window_max, c->local_maxpacket, 0, buf, 1);
-		packet_start(SSH2_MSG_CHANNEL_OPEN);
-		packet_put_cstring("x11");
-		packet_put_int(nc->self);
-		packet_put_int(nc->local_window_max);
-		packet_put_int(nc->local_maxpacket);
-		/* originator ipaddr and port */
-		packet_put_cstring(remote_ipaddr);
-		if (datafellows & SSH_BUG_X11FWD) {
-			debug2("ssh2 x11 bug compat mode");
-		} else {
-			packet_put_int(remote_port);
-		}
-		packet_send();
-		free(remote_ipaddr);
+	debug("X11 connection requested.");
+	addrlen = sizeof(addr);
+	newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen);
+	if (c->single_connection) {
+		oerrno = errno;
+		debug2("single_connection: closing X11 listener.");
+		channel_close_fd(ssh, &c->sock);
+		chan_mark_dead(ssh, c);
+		errno = oerrno;
 	}
+	if (newsock < 0) {
+		if (errno != EINTR && errno != EWOULDBLOCK &&
+		    errno != ECONNABORTED)
+			error("accept: %.100s", strerror(errno));
+		if (errno == EMFILE || errno == ENFILE)
+			c->notbefore = monotime() + 1;
+		return;
+	}
+	set_nodelay(newsock);
+	remote_ipaddr = get_peer_ipaddr(newsock);
+	remote_port = get_peer_port(newsock);
+	snprintf(buf, sizeof buf, "X11 connection from %.200s port %d",
+	    remote_ipaddr, remote_port);
+
+	nc = channel_new(ssh, "accepted x11 socket",
+	    SSH_CHANNEL_OPENING, newsock, newsock, -1,
+	    c->local_window_max, c->local_maxpacket, 0, buf, 1);
+	open_preamble(ssh, __func__, nc, "x11");
+	if ((r = sshpkt_put_cstring(ssh, remote_ipaddr)) != 0) {
+		fatal("%s: channel %i: reply %s", __func__,
+		    c->self, ssh_err(r));
+	}
+	if ((datafellows & SSH_BUG_X11FWD) != 0)
+		debug2("channel %d: ssh2 x11 bug compat mode", nc->self);
+	else if ((r = sshpkt_put_u32(ssh, remote_port)) != 0) {
+		fatal("%s: channel %i: reply %s", __func__,
+		    c->self, ssh_err(r));
+	}
+	if ((r = sshpkt_send(ssh)) != 0)
+		fatal("%s: channel %i: send %s", __func__, c->self, ssh_err(r));
+	free(remote_ipaddr);
 }
 
 static void
-port_open_helper(Channel *c, char *rtype)
+port_open_helper(struct ssh *ssh, Channel *c, char *rtype)
 {
-	char buf[1024];
 	char *local_ipaddr = get_local_ipaddr(c->sock);
 	int local_port = c->sock == -1 ? 65536 : get_local_port(c->sock);
 	char *remote_ipaddr = get_peer_ipaddr(c->sock);
 	int remote_port = get_peer_port(c->sock);
+	int r;
 
 	if (remote_port == -1) {
 		/* Fake addr/port to appease peers that validate it (Tectia) */
@@ -1395,44 +1523,57 @@
 		remote_port = 65535;
 	}
 
-	snprintf(buf, sizeof buf,
+	free(c->remote_name);
+	xasprintf(&c->remote_name,
 	    "%s: listening port %d for %.100s port %d, "
 	    "connect from %.200s port %d to %.100s port %d",
 	    rtype, c->listening_port, c->path, c->host_port,
 	    remote_ipaddr, remote_port, local_ipaddr, local_port);
 
-	free(c->remote_name);
-	c->remote_name = xstrdup(buf);
-
-	packet_start(SSH2_MSG_CHANNEL_OPEN);
-	packet_put_cstring(rtype);
-	packet_put_int(c->self);
-	packet_put_int(c->local_window_max);
-	packet_put_int(c->local_maxpacket);
+	open_preamble(ssh, __func__, c, rtype);
 	if (strcmp(rtype, "direct-tcpip") == 0) {
 		/* target host, port */
-		packet_put_cstring(c->path);
-		packet_put_int(c->host_port);
+		if ((r = sshpkt_put_cstring(ssh, c->path)) != 0 ||
+		    (r = sshpkt_put_u32(ssh, c->host_port)) != 0) {
+			fatal("%s: channel %i: reply %s", __func__,
+			    c->self, ssh_err(r));
+		}
 	} else if (strcmp(rtype, "direct-streamlocal@openssh.com") == 0) {
 		/* target path */
-		packet_put_cstring(c->path);
+		if ((r = sshpkt_put_cstring(ssh, c->path)) != 0) {
+			fatal("%s: channel %i: reply %s", __func__,
+			    c->self, ssh_err(r));
+		}
 	} else if (strcmp(rtype, "forwarded-streamlocal@openssh.com") == 0) {
 		/* listen path */
-		packet_put_cstring(c->path);
+		if ((r = sshpkt_put_cstring(ssh, c->path)) != 0) {
+			fatal("%s: channel %i: reply %s", __func__,
+			    c->self, ssh_err(r));
+		}
 	} else {
 		/* listen address, port */
-		packet_put_cstring(c->path);
-		packet_put_int(local_port);
+		if ((r = sshpkt_put_cstring(ssh, c->path)) != 0 ||
+		    (r = sshpkt_put_u32(ssh, local_port)) != 0) {
+			fatal("%s: channel %i: reply %s", __func__,
+			    c->self, ssh_err(r));
+		}
 	}
 	if (strcmp(rtype, "forwarded-streamlocal@openssh.com") == 0) {
 		/* reserved for future owner/mode info */
-		packet_put_cstring("");
+		if ((r = sshpkt_put_cstring(ssh, "")) != 0) {
+			fatal("%s: channel %i: reply %s", __func__,
+			    c->self, ssh_err(r));
+		}
 	} else {
 		/* originator host and port */
-		packet_put_cstring(remote_ipaddr);
-		packet_put_int((u_int)remote_port);
+		if ((r = sshpkt_put_cstring(ssh, remote_ipaddr)) != 0 ||
+		    (r = sshpkt_put_u32(ssh, (u_int)remote_port)) != 0) {
+			fatal("%s: channel %i: reply %s", __func__,
+			    c->self, ssh_err(r));
+		}
 	}
-	packet_send();
+	if ((r = sshpkt_send(ssh)) != 0)
+		fatal("%s: channel %i: send %s", __func__, c->self, ssh_err(r));
 	free(remote_ipaddr);
 	free(local_ipaddr);
 }
@@ -1451,17 +1592,17 @@
 }
 
 void
-channel_set_x11_refuse_time(u_int refuse_time)
+channel_set_x11_refuse_time(struct ssh *ssh, u_int refuse_time)
 {
-	x11_refuse_time = refuse_time;
+	ssh->chanctxt->x11_refuse_time = refuse_time;
 }
 
 /*
  * This socket is listening for connections to a forwarded TCP/IP port.
  */
-/* ARGSUSED */
 static void
-channel_post_port_listener(Channel *c, fd_set *readset, fd_set *writeset)
+channel_post_port_listener(struct ssh *ssh, Channel *c,
+    fd_set *readset, fd_set *writeset)
 {
 	Channel *nc;
 	struct sockaddr_storage addr;
@@ -1469,336 +1610,387 @@
 	socklen_t addrlen;
 	char *rtype;
 
-	if (FD_ISSET(c->sock, readset)) {
-		debug("Connection to port %d forwarding "
-		    "to %.100s port %d requested.",
-		    c->listening_port, c->path, c->host_port);
+	if (!FD_ISSET(c->sock, readset))
+		return;
 
-		if (c->type == SSH_CHANNEL_RPORT_LISTENER) {
-			nextstate = SSH_CHANNEL_OPENING;
-			rtype = "forwarded-tcpip";
-		} else if (c->type == SSH_CHANNEL_RUNIX_LISTENER) {
-			nextstate = SSH_CHANNEL_OPENING;
-			rtype = "forwarded-streamlocal@openssh.com";
-		} else if (c->host_port == PORT_STREAMLOCAL) {
-			nextstate = SSH_CHANNEL_OPENING;
-			rtype = "direct-streamlocal@openssh.com";
-		} else if (c->host_port == 0) {
-			nextstate = SSH_CHANNEL_DYNAMIC;
-			rtype = "dynamic-tcpip";
-		} else {
-			nextstate = SSH_CHANNEL_OPENING;
-			rtype = "direct-tcpip";
-		}
+	debug("Connection to port %d forwarding to %.100s port %d requested.",
+	    c->listening_port, c->path, c->host_port);
 
-		addrlen = sizeof(addr);
-		newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen);
-		if (newsock < 0) {
-			if (errno != EINTR && errno != EWOULDBLOCK &&
-			    errno != ECONNABORTED)
-				error("accept: %.100s", strerror(errno));
-			if (errno == EMFILE || errno == ENFILE)
-				c->notbefore = monotime() + 1;
-			return;
-		}
-		if (c->host_port != PORT_STREAMLOCAL)
-			set_nodelay(newsock);
-		nc = channel_new(rtype, nextstate, newsock, newsock, -1,
-		    c->local_window_max, c->local_maxpacket, 0, rtype, 1);
-		nc->listening_port = c->listening_port;
-		nc->host_port = c->host_port;
-		if (c->path != NULL)
-			nc->path = xstrdup(c->path);
-
-		if (nextstate != SSH_CHANNEL_DYNAMIC)
-			port_open_helper(nc, rtype);
+	if (c->type == SSH_CHANNEL_RPORT_LISTENER) {
+		nextstate = SSH_CHANNEL_OPENING;
+		rtype = "forwarded-tcpip";
+	} else if (c->type == SSH_CHANNEL_RUNIX_LISTENER) {
+		nextstate = SSH_CHANNEL_OPENING;
+		rtype = "forwarded-streamlocal@openssh.com";
+	} else if (c->host_port == PORT_STREAMLOCAL) {
+		nextstate = SSH_CHANNEL_OPENING;
+		rtype = "direct-streamlocal@openssh.com";
+	} else if (c->host_port == 0) {
+		nextstate = SSH_CHANNEL_DYNAMIC;
+		rtype = "dynamic-tcpip";
+	} else {
+		nextstate = SSH_CHANNEL_OPENING;
+		rtype = "direct-tcpip";
 	}
+
+	addrlen = sizeof(addr);
+	newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen);
+	if (newsock < 0) {
+		if (errno != EINTR && errno != EWOULDBLOCK &&
+		    errno != ECONNABORTED)
+			error("accept: %.100s", strerror(errno));
+		if (errno == EMFILE || errno == ENFILE)
+			c->notbefore = monotime() + 1;
+		return;
+	}
+	if (c->host_port != PORT_STREAMLOCAL)
+		set_nodelay(newsock);
+	nc = channel_new(ssh, rtype, nextstate, newsock, newsock, -1,
+	    c->local_window_max, c->local_maxpacket, 0, rtype, 1);
+	nc->listening_port = c->listening_port;
+	nc->host_port = c->host_port;
+	if (c->path != NULL)
+		nc->path = xstrdup(c->path);
+
+	if (nextstate != SSH_CHANNEL_DYNAMIC)
+		port_open_helper(ssh, nc, rtype);
 }
 
 /*
  * This is the authentication agent socket listening for connections from
  * clients.
  */
-/* ARGSUSED */
 static void
-channel_post_auth_listener(Channel *c, fd_set *readset, fd_set *writeset)
+channel_post_auth_listener(struct ssh *ssh, Channel *c,
+    fd_set *readset, fd_set *writeset)
 {
 	Channel *nc;
-	int newsock;
+	int r, newsock;
 	struct sockaddr_storage addr;
 	socklen_t addrlen;
 
-	if (FD_ISSET(c->sock, readset)) {
-		addrlen = sizeof(addr);
-		newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen);
-		if (newsock < 0) {
-			error("accept from auth socket: %.100s",
-			    strerror(errno));
-			if (errno == EMFILE || errno == ENFILE)
-				c->notbefore = monotime() + 1;
-			return;
-		}
-		nc = channel_new("accepted auth socket",
-		    SSH_CHANNEL_OPENING, newsock, newsock, -1,
-		    c->local_window_max, c->local_maxpacket,
-		    0, "accepted auth socket", 1);
-		packet_start(SSH2_MSG_CHANNEL_OPEN);
-		packet_put_cstring("auth-agent@openssh.com");
-		packet_put_int(nc->self);
-		packet_put_int(c->local_window_max);
-		packet_put_int(c->local_maxpacket);
-		packet_send();
+	if (!FD_ISSET(c->sock, readset))
+		return;
+
+	addrlen = sizeof(addr);
+	newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen);
+	if (newsock < 0) {
+		error("accept from auth socket: %.100s", strerror(errno));
+		if (errno == EMFILE || errno == ENFILE)
+			c->notbefore = monotime() + 1;
+		return;
 	}
+	nc = channel_new(ssh, "accepted auth socket",
+	    SSH_CHANNEL_OPENING, newsock, newsock, -1,
+	    c->local_window_max, c->local_maxpacket,
+	    0, "accepted auth socket", 1);
+	open_preamble(ssh, __func__, nc, "auth-agent@openssh.com");
+	if ((r = sshpkt_send(ssh)) != 0)
+		fatal("%s: channel %i: %s", __func__, c->self, ssh_err(r));
 }
 
-/* ARGSUSED */
 static void
-channel_post_connecting(Channel *c, fd_set *readset, fd_set *writeset)
+channel_post_connecting(struct ssh *ssh, Channel *c,
+    fd_set *readset, fd_set *writeset)
 {
-	int err = 0, sock;
+	int err = 0, sock, r;
 	socklen_t sz = sizeof(err);
 
-	if (FD_ISSET(c->sock, writeset)) {
-		if (getsockopt(c->sock, SOL_SOCKET, SO_ERROR, &err, &sz) < 0) {
-			err = errno;
-			error("getsockopt SO_ERROR failed");
-		}
-		if (err == 0) {
-			debug("channel %d: connected to %s port %d",
-			    c->self, c->connect_ctx.host, c->connect_ctx.port);
-			channel_connect_ctx_free(&c->connect_ctx);
-			c->type = SSH_CHANNEL_OPEN;
-			packet_start(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION);
-			packet_put_int(c->remote_id);
-			packet_put_int(c->self);
-			packet_put_int(c->local_window);
-			packet_put_int(c->local_maxpacket);
-		} else {
-			debug("channel %d: connection failed: %s",
-			    c->self, strerror(err));
-			/* Try next address, if any */
-			if ((sock = connect_next(&c->connect_ctx)) > 0) {
-				close(c->sock);
-				c->sock = c->rfd = c->wfd = sock;
-				channel_max_fd = channel_find_maxfd();
-				return;
-			}
-			/* Exhausted all addresses */
-			error("connect_to %.100s port %d: failed.",
-			    c->connect_ctx.host, c->connect_ctx.port);
-			channel_connect_ctx_free(&c->connect_ctx);
-			packet_start(SSH2_MSG_CHANNEL_OPEN_FAILURE);
-			packet_put_int(c->remote_id);
-			packet_put_int(SSH2_OPEN_CONNECT_FAILED);
-			if (!(datafellows & SSH_BUG_OPENFAILURE)) {
-				packet_put_cstring(strerror(err));
-				packet_put_cstring("");
-			}
-			chan_mark_dead(c);
-		}
-		packet_send();
+	if (!FD_ISSET(c->sock, writeset))
+		return;
+
+	if (getsockopt(c->sock, SOL_SOCKET, SO_ERROR, &err, &sz) < 0) {
+		err = errno;
+		error("getsockopt SO_ERROR failed");
 	}
+	if (err == 0) {
+		debug("channel %d: connected to %s port %d",
+		    c->self, c->connect_ctx.host, c->connect_ctx.port);
+		channel_connect_ctx_free(&c->connect_ctx);
+		c->type = SSH_CHANNEL_OPEN;
+		if ((r = sshpkt_start(ssh,
+		    SSH2_MSG_CHANNEL_OPEN_CONFIRMATION)) != 0 ||
+		    (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
+		    (r = sshpkt_put_u32(ssh, c->self)) != 0 ||
+		    (r = sshpkt_put_u32(ssh, c->local_window)) != 0 ||
+		    (r = sshpkt_put_u32(ssh, c->local_maxpacket)) != 0) {
+			fatal("%s: channel %i: confirm: %s", __func__,
+			    c->self, ssh_err(r));
+		}
+	} else {
+		debug("channel %d: connection failed: %s",
+		    c->self, strerror(err));
+		/* Try next address, if any */
+		if ((sock = connect_next(&c->connect_ctx)) > 0) {
+			close(c->sock);
+			c->sock = c->rfd = c->wfd = sock;
+			channel_find_maxfd(ssh->chanctxt);
+			return;
+		}
+		/* Exhausted all addresses */
+		error("connect_to %.100s port %d: failed.",
+		    c->connect_ctx.host, c->connect_ctx.port);
+		channel_connect_ctx_free(&c->connect_ctx);
+		if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_OPEN_FAILURE)) != 0 ||
+		    (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
+		    (r = sshpkt_put_u32(ssh, SSH2_OPEN_CONNECT_FAILED)) != 0) {
+			fatal("%s: channel %i: failure: %s", __func__,
+			    c->self, ssh_err(r));
+		}
+		if ((datafellows & SSH_BUG_OPENFAILURE) == 0 &&
+		    ((r = sshpkt_put_cstring(ssh, strerror(err))) != 0 ||
+		    (r = sshpkt_put_cstring(ssh, "")) != 0)) {
+			fatal("%s: channel %i: failure: %s", __func__,
+			    c->self, ssh_err(r));
+		}
+		chan_mark_dead(ssh, c);
+	}
+	if ((r = sshpkt_send(ssh)) != 0)
+		fatal("%s: channel %i: %s", __func__, c->self, ssh_err(r));
 }
 
-/* ARGSUSED */
 static int
-channel_handle_rfd(Channel *c, fd_set *readset, fd_set *writeset)
+channel_handle_rfd(struct ssh *ssh, Channel *c,
+    fd_set *readset, fd_set *writeset)
 {
 	char buf[CHAN_RBUF];
-	int len, force;
+	ssize_t len;
+	int r, force;
 
 	force = c->isatty && c->detach_close && c->istate != CHAN_INPUT_CLOSED;
-	if (c->rfd != -1 && (force || FD_ISSET(c->rfd, readset))) {
-		errno = 0;
-		len = read(c->rfd, buf, sizeof(buf));
-		if (len < 0 && (errno == EINTR ||
-		    ((errno == EAGAIN || errno == EWOULDBLOCK) && !force)))
-			return 1;
+
+	if (c->rfd == -1 || (!force && !FD_ISSET(c->rfd, readset)))
+		return 1;
+
+	errno = 0;
+	len = read(c->rfd, buf, sizeof(buf));
+	if (len < 0 && (errno == EINTR ||
+	    ((errno == EAGAIN || errno == EWOULDBLOCK) && !force)))
+		return 1;
 #ifndef PTY_ZEROREAD
-		if (len <= 0) {
+ 	if (len <= 0) {
 #else
-		if ((!c->isatty && len <= 0) ||
-		    (c->isatty && (len < 0 || (len == 0 && errno != 0)))) {
+	if ((!c->isatty && len <= 0) ||
+	    (c->isatty && (len < 0 || (len == 0 && errno != 0)))) {
 #endif
-			debug2("channel %d: read<=0 rfd %d len %d",
-			    c->self, c->rfd, len);
-			if (c->type != SSH_CHANNEL_OPEN) {
-				debug2("channel %d: not open", c->self);
-				chan_mark_dead(c);
-				return -1;
-			} else {
-				chan_read_failed(c);
-			}
+		debug2("channel %d: read<=0 rfd %d len %zd",
+		    c->self, c->rfd, len);
+		if (c->type != SSH_CHANNEL_OPEN) {
+			debug2("channel %d: not open", c->self);
+			chan_mark_dead(ssh, c);
 			return -1;
-		}
-		if (c->input_filter != NULL) {
-			if (c->input_filter(c, buf, len) == -1) {
-				debug2("channel %d: filter stops", c->self);
-				chan_read_failed(c);
-			}
-		} else if (c->datagram) {
-			buffer_put_string(&c->input, buf, len);
 		} else {
-			buffer_append(&c->input, buf, len);
+			chan_read_failed(ssh, c);
 		}
+		return -1;
+	}
+	if (c->input_filter != NULL) {
+		if (c->input_filter(ssh, c, buf, len) == -1) {
+			debug2("channel %d: filter stops", c->self);
+			chan_read_failed(ssh, c);
+		}
+	} else if (c->datagram) {
+		if ((r = sshbuf_put_string(c->input, buf, len)) != 0)
+			fatal("%s: channel %d: put datagram: %s", __func__,
+			    c->self, ssh_err(r));
+	} else if ((r = sshbuf_put(c->input, buf, len)) != 0) {
+		fatal("%s: channel %d: put data: %s", __func__,
+		    c->self, ssh_err(r));
 	}
 	return 1;
 }
 
-/* ARGSUSED */
 static int
-channel_handle_wfd(Channel *c, fd_set *readset, fd_set *writeset)
+channel_handle_wfd(struct ssh *ssh, Channel *c,
+   fd_set *readset, fd_set *writeset)
 {
 	struct termios tio;
-	u_char *data = NULL, *buf;
-	u_int dlen, olen = 0;
-	int len;
+	u_char *data = NULL, *buf; /* XXX const; need filter API change */
+	size_t dlen, olen = 0;
+	int r, len;
+
+	if (c->wfd == -1 || !FD_ISSET(c->wfd, writeset) ||
+	    sshbuf_len(c->output) == 0)
+		return 1;
 
 	/* Send buffered output data to the socket. */
-	if (c->wfd != -1 &&
-	    FD_ISSET(c->wfd, writeset) &&
-	    buffer_len(&c->output) > 0) {
-		olen = buffer_len(&c->output);
-		if (c->output_filter != NULL) {
-			if ((buf = c->output_filter(c, &data, &dlen)) == NULL) {
-				debug2("channel %d: filter stops", c->self);
-				if (c->type != SSH_CHANNEL_OPEN)
-					chan_mark_dead(c);
-				else
-					chan_write_failed(c);
-				return -1;
-			}
-		} else if (c->datagram) {
-			buf = data = buffer_get_string(&c->output, &dlen);
-		} else {
-			buf = data = buffer_ptr(&c->output);
-			dlen = buffer_len(&c->output);
-		}
-
-		if (c->datagram) {
-			/* ignore truncated writes, datagrams might get lost */
-			len = write(c->wfd, buf, dlen);
-			free(data);
-			if (len < 0 && (errno == EINTR || errno == EAGAIN ||
-			    errno == EWOULDBLOCK))
-				return 1;
-			if (len <= 0) {
-				if (c->type != SSH_CHANNEL_OPEN)
-					chan_mark_dead(c);
-				else
-					chan_write_failed(c);
-				return -1;
-			}
-			goto out;
-		}
-#ifdef _AIX
-		/* XXX: Later AIX versions can't push as much data to tty */
-		if (c->wfd_isatty)
-			dlen = MIN(dlen, 8*1024);
-#endif
-
-		len = write(c->wfd, buf, dlen);
-		if (len < 0 &&
-		    (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK))
-			return 1;
-		if (len <= 0) {
-			if (c->type != SSH_CHANNEL_OPEN) {
-				debug2("channel %d: not open", c->self);
-				chan_mark_dead(c);
-				return -1;
-			} else {
-				chan_write_failed(c);
-			}
+	olen = sshbuf_len(c->output);
+	if (c->output_filter != NULL) {
+		if ((buf = c->output_filter(ssh, c, &data, &dlen)) == NULL) {
+			debug2("channel %d: filter stops", c->self);
+			if (c->type != SSH_CHANNEL_OPEN)
+				chan_mark_dead(ssh, c);
+			else
+				chan_write_failed(ssh, c);
 			return -1;
 		}
-#ifndef BROKEN_TCGETATTR_ICANON
-		if (c->isatty && dlen >= 1 && buf[0] != '\r') {
-			if (tcgetattr(c->wfd, &tio) == 0 &&
-			    !(tio.c_lflag & ECHO) && (tio.c_lflag & ICANON)) {
-				/*
-				 * Simulate echo to reduce the impact of
-				 * traffic analysis. We need to match the
-				 * size of a SSH2_MSG_CHANNEL_DATA message
-				 * (4 byte channel id + buf)
-				 */
-				packet_send_ignore(4 + len);
-				packet_send();
-			}
-		}
+	} else if (c->datagram) {
+		if ((r = sshbuf_get_string(c->output, &data, &dlen)) != 0)
+			fatal("%s: channel %d: get datagram: %s", __func__,
+			    c->self, ssh_err(r));
+	} else {
+		buf = data = sshbuf_mutable_ptr(c->output);
+		dlen = sshbuf_len(c->output);
+	}
+
+	if (c->datagram) {
+		/* ignore truncated writes, datagrams might get lost */
+		len = write(c->wfd, data, dlen);
+		free(data);
+		if (len < 0 && (errno == EINTR || errno == EAGAIN ||
+		    errno == EWOULDBLOCK))
+			return 1;
+		if (len <= 0)
+			goto write_fail;
+		goto out;
+	}
+
+#ifdef _AIX
+	/* XXX: Later AIX versions can't push as much data to tty */
+	if (c->wfd_isatty)
+		dlen = MIN(dlen, 8*1024);
 #endif
-		buffer_consume(&c->output, len);
+
+	len = write(c->wfd, buf, dlen);
+	if (len < 0 &&
+	    (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK))
+		return 1;
+	if (len <= 0) {
+ write_fail:
+		if (c->type != SSH_CHANNEL_OPEN) {
+			debug2("channel %d: not open", c->self);
+			chan_mark_dead(ssh, c);
+			return -1;
+		} else {
+			chan_write_failed(ssh, c);
+		}
+		return -1;
+	}
+#ifndef BROKEN_TCGETATTR_ICANON
+	if (c->isatty && dlen >= 1 && buf[0] != '\r') {
+		if (tcgetattr(c->wfd, &tio) == 0 &&
+		    !(tio.c_lflag & ECHO) && (tio.c_lflag & ICANON)) {
+			/*
+			 * Simulate echo to reduce the impact of
+			 * traffic analysis. We need to match the
+			 * size of a SSH2_MSG_CHANNEL_DATA message
+			 * (4 byte channel id + buf)
+			 */
+			if ((r = sshpkt_msg_ignore(ssh, 4+len)) != 0 ||
+			    (r = sshpkt_send(ssh)) != 0)
+				fatal("%s: channel %d: ignore: %s",
+				    __func__, c->self, ssh_err(r));
+		}
+	}
+#endif /* BROKEN_TCGETATTR_ICANON */
+	if ((r = sshbuf_consume(c->output, len)) != 0) {
+		fatal("%s: channel %d: consume: %s",
+		    __func__, c->self, ssh_err(r));
 	}
  out:
-	if (olen > 0)
-		c->local_consumed += olen - buffer_len(&c->output);
+	c->local_consumed += olen - sshbuf_len(c->output);
+
 	return 1;
 }
 
 static int
-channel_handle_efd(Channel *c, fd_set *readset, fd_set *writeset)
+channel_handle_efd_write(struct ssh *ssh, Channel *c,
+    fd_set *readset, fd_set *writeset)
+{
+	int r;
+	ssize_t len;
+
+	if (!FD_ISSET(c->efd, writeset) || sshbuf_len(c->extended) == 0)
+		return 1;
+
+	len = write(c->efd, sshbuf_ptr(c->extended),
+	    sshbuf_len(c->extended));
+	debug2("channel %d: written %zd to efd %d", c->self, len, c->efd);
+	if (len < 0 && (errno == EINTR || errno == EAGAIN ||
+	    errno == EWOULDBLOCK))
+		return 1;
+	if (len <= 0) {
+		debug2("channel %d: closing write-efd %d", c->self, c->efd);
+		channel_close_fd(ssh, &c->efd);
+	} else {
+		if ((r = sshbuf_consume(c->extended, len)) != 0) {
+			fatal("%s: channel %d: consume: %s",
+			    __func__, c->self, ssh_err(r));
+		}
+		c->local_consumed += len;
+	}
+	return 1;
+}
+
+static int
+channel_handle_efd_read(struct ssh *ssh, Channel *c,
+    fd_set *readset, fd_set *writeset)
 {
 	char buf[CHAN_RBUF];
-	int len;
+	int r;
+	ssize_t len;
 
-/** XXX handle drain efd, too */
-	if (c->efd != -1) {
-		if (c->extended_usage == CHAN_EXTENDED_WRITE &&
-		    FD_ISSET(c->efd, writeset) &&
-		    buffer_len(&c->extended) > 0) {
-			len = write(c->efd, buffer_ptr(&c->extended),
-			    buffer_len(&c->extended));
-			debug2("channel %d: written %d to efd %d",
-			    c->self, len, c->efd);
-			if (len < 0 && (errno == EINTR || errno == EAGAIN ||
-			    errno == EWOULDBLOCK))
-				return 1;
-			if (len <= 0) {
-				debug2("channel %d: closing write-efd %d",
-				    c->self, c->efd);
-				channel_close_fd(&c->efd);
-			} else {
-				buffer_consume(&c->extended, len);
-				c->local_consumed += len;
-			}
-		} else if (c->efd != -1 &&
-		    (c->extended_usage == CHAN_EXTENDED_READ ||
-		    c->extended_usage == CHAN_EXTENDED_IGNORE) &&
-		    (c->detach_close || FD_ISSET(c->efd, readset))) {
-			len = read(c->efd, buf, sizeof(buf));
-			debug2("channel %d: read %d from efd %d",
-			    c->self, len, c->efd);
-			if (len < 0 && (errno == EINTR || ((errno == EAGAIN ||
-			    errno == EWOULDBLOCK) && !c->detach_close)))
-				return 1;
-			if (len <= 0) {
-				debug2("channel %d: closing read-efd %d",
-				    c->self, c->efd);
-				channel_close_fd(&c->efd);
-			} else {
-				if (c->extended_usage == CHAN_EXTENDED_IGNORE) {
-					debug3("channel %d: discard efd",
-					    c->self);
-				} else
-					buffer_append(&c->extended, buf, len);
-			}
+	if (!c->detach_close && !FD_ISSET(c->efd, readset))
+		return 1;
+
+	len = read(c->efd, buf, sizeof(buf));
+	debug2("channel %d: read %zd from efd %d", c->self, len, c->efd);
+	if (len < 0 && (errno == EINTR || ((errno == EAGAIN ||
+	    errno == EWOULDBLOCK) && !c->detach_close)))
+		return 1;
+	if (len <= 0) {
+		debug2("channel %d: closing read-efd %d",
+		    c->self, c->efd);
+		channel_close_fd(ssh, &c->efd);
+	} else {
+		if (c->extended_usage == CHAN_EXTENDED_IGNORE) {
+			debug3("channel %d: discard efd",
+			    c->self);
+		} else if ((r = sshbuf_put(c->extended, buf, len)) != 0) {
+			fatal("%s: channel %d: append: %s",
+			    __func__, c->self, ssh_err(r));
 		}
 	}
 	return 1;
 }
 
 static int
-channel_check_window(Channel *c)
+channel_handle_efd(struct ssh *ssh, Channel *c,
+    fd_set *readset, fd_set *writeset)
 {
+	if (c->efd == -1)
+		return 1;
+
+	/** XXX handle drain efd, too */
+
+	if (c->extended_usage == CHAN_EXTENDED_WRITE)
+		return channel_handle_efd_write(ssh, c, readset, writeset);
+	else if (c->extended_usage == CHAN_EXTENDED_READ ||
+	    c->extended_usage == CHAN_EXTENDED_IGNORE)
+		return channel_handle_efd_read(ssh, c, readset, writeset);
+
+	return 1;
+}
+
+static int
+channel_check_window(struct ssh *ssh, Channel *c)
+{
+	int r;
+
 	if (c->type == SSH_CHANNEL_OPEN &&
 	    !(c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD)) &&
 	    ((c->local_window_max - c->local_window >
 	    c->local_maxpacket*3) ||
 	    c->local_window < c->local_window_max/2) &&
 	    c->local_consumed > 0) {
-		packet_start(SSH2_MSG_CHANNEL_WINDOW_ADJUST);
-		packet_put_int(c->remote_id);
-		packet_put_int(c->local_consumed);
-		packet_send();
+		if ((r = sshpkt_start(ssh,
+		    SSH2_MSG_CHANNEL_WINDOW_ADJUST)) != 0 ||
+		    (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
+		    (r = sshpkt_put_u32(ssh, c->local_consumed)) != 0 ||
+		    (r = sshpkt_send(ssh)) != 0) {
+			fatal("%s: channel %i: %s", __func__,
+			    c->self, ssh_err(r));
+		}
 		debug2("channel %d: window %d sent adjust %d",
 		    c->self, c->local_window,
 		    c->local_consumed);
@@ -1809,85 +2001,112 @@
 }
 
 static void
-channel_post_open(Channel *c, fd_set *readset, fd_set *writeset)
+channel_post_open(struct ssh *ssh, Channel *c,
+    fd_set *readset, fd_set *writeset)
 {
-	channel_handle_rfd(c, readset, writeset);
-	channel_handle_wfd(c, readset, writeset);
-	channel_handle_efd(c, readset, writeset);
-	channel_check_window(c);
+	channel_handle_rfd(ssh, c, readset, writeset);
+	channel_handle_wfd(ssh, c, readset, writeset);
+	channel_handle_efd(ssh, c, readset, writeset);
+	channel_check_window(ssh, c);
 }
 
 static u_int
-read_mux(Channel *c, u_int need)
+read_mux(struct ssh *ssh, Channel *c, u_int need)
 {
 	char buf[CHAN_RBUF];
-	int len;
+	ssize_t len;
 	u_int rlen;
+	int r;
 
-	if (buffer_len(&c->input) < need) {
-		rlen = need - buffer_len(&c->input);
+	if (sshbuf_len(c->input) < need) {
+		rlen = need - sshbuf_len(c->input);
 		len = read(c->rfd, buf, MINIMUM(rlen, CHAN_RBUF));
 		if (len < 0 && (errno == EINTR || errno == EAGAIN))
-			return buffer_len(&c->input);
+			return sshbuf_len(c->input);
 		if (len <= 0) {
-			debug2("channel %d: ctl read<=0 rfd %d len %d",
+			debug2("channel %d: ctl read<=0 rfd %d len %zd",
 			    c->self, c->rfd, len);
-			chan_read_failed(c);
+			chan_read_failed(ssh, c);
 			return 0;
-		} else
-			buffer_append(&c->input, buf, len);
+		} else if ((r = sshbuf_put(c->input, buf, len)) != 0) {
+			fatal("%s: channel %d: append: %s",
+			    __func__, c->self, ssh_err(r));
+		}
 	}
-	return buffer_len(&c->input);
+	return sshbuf_len(c->input);
 }
 
 static void
-channel_post_mux_client(Channel *c, fd_set *readset, fd_set *writeset)
+channel_post_mux_client_read(struct ssh *ssh, Channel *c,
+    fd_set *readset, fd_set *writeset)
 {
 	u_int need;
-	ssize_t len;
 
-	if (c->rfd != -1 && !c->mux_pause && FD_ISSET(c->rfd, readset) &&
-	    (c->istate == CHAN_INPUT_OPEN ||
-	    c->istate == CHAN_INPUT_WAIT_DRAIN)) {
-		/*
-		 * Don't not read past the precise end of packets to
-		 * avoid disrupting fd passing.
-		 */
-		if (read_mux(c, 4) < 4) /* read header */
-			return;
-		need = get_u32(buffer_ptr(&c->input));
+	if (c->rfd == -1 || !FD_ISSET(c->rfd, readset))
+		return;
+	if (c->istate != CHAN_INPUT_OPEN && c->istate != CHAN_INPUT_WAIT_DRAIN)
+		return;
+	if (c->mux_pause)
+		return;
+
+	/*
+	 * Don't not read past the precise end of packets to
+	 * avoid disrupting fd passing.
+	 */
+	if (read_mux(ssh, c, 4) < 4) /* read header */
+		return;
+	/* XXX sshbuf_peek_u32 */
+	need = PEEK_U32(sshbuf_ptr(c->input));
 #define CHANNEL_MUX_MAX_PACKET	(256 * 1024)
-		if (need > CHANNEL_MUX_MAX_PACKET) {
-			debug2("channel %d: packet too big %u > %u",
-			    c->self, CHANNEL_MUX_MAX_PACKET, need);
-			chan_rcvd_oclose(c);
-			return;
-		}
-		if (read_mux(c, need + 4) < need + 4) /* read body */
-			return;
-		if (c->mux_rcb(c) != 0) {
-			debug("channel %d: mux_rcb failed", c->self);
-			chan_mark_dead(c);
-			return;
-		}
+	if (need > CHANNEL_MUX_MAX_PACKET) {
+		debug2("channel %d: packet too big %u > %u",
+		    c->self, CHANNEL_MUX_MAX_PACKET, need);
+		chan_rcvd_oclose(ssh, c);
+		return;
 	}
-
-	if (c->wfd != -1 && FD_ISSET(c->wfd, writeset) &&
-	    buffer_len(&c->output) > 0) {
-		len = write(c->wfd, buffer_ptr(&c->output),
-		    buffer_len(&c->output));
-		if (len < 0 && (errno == EINTR || errno == EAGAIN))
-			return;
-		if (len <= 0) {
-			chan_mark_dead(c);
-			return;
-		}
-		buffer_consume(&c->output, len);
+	if (read_mux(ssh, c, need + 4) < need + 4) /* read body */
+		return;
+	if (c->mux_rcb(ssh, c) != 0) {
+		debug("channel %d: mux_rcb failed", c->self);
+		chan_mark_dead(ssh, c);
+		return;
 	}
 }
 
 static void
-channel_post_mux_listener(Channel *c, fd_set *readset, fd_set *writeset)
+channel_post_mux_client_write(struct ssh *ssh, Channel *c,
+    fd_set *readset, fd_set *writeset)
+{
+	ssize_t len;
+	int r;
+
+	if (c->wfd == -1 || !FD_ISSET(c->wfd, writeset) ||
+	    sshbuf_len(c->output) == 0)
+		return;
+
+	len = write(c->wfd, sshbuf_ptr(c->output), sshbuf_len(c->output));
+	if (len < 0 && (errno == EINTR || errno == EAGAIN))
+		return;
+	if (len <= 0) {
+		chan_mark_dead(ssh, c);
+		return;
+	}
+	if ((r = sshbuf_consume(c->output, len)) != 0)
+		fatal("%s: channel %d: consume: %s", __func__,
+		    c->self, ssh_err(r));
+}
+
+static void
+channel_post_mux_client(struct ssh *ssh, Channel *c,
+    fd_set *readset, fd_set *writeset)
+{
+	channel_post_mux_client_read(ssh, c, readset, writeset);
+	channel_post_mux_client_write(ssh, c, readset, writeset);
+}
+
+static void
+channel_post_mux_listener(struct ssh *ssh, Channel *c,
+    fd_set *readset, fd_set *writeset)
 {
 	Channel *nc;
 	struct sockaddr_storage addr;
@@ -1926,97 +2145,98 @@
 		close(newsock);
 		return;
 	}
-	nc = channel_new("multiplex client", SSH_CHANNEL_MUX_CLIENT,
+	nc = channel_new(ssh, "multiplex client", SSH_CHANNEL_MUX_CLIENT,
 	    newsock, newsock, -1, c->local_window_max,
 	    c->local_maxpacket, 0, "mux-control", 1);
 	nc->mux_rcb = c->mux_rcb;
-	debug3("%s: new mux channel %d fd %d", __func__,
-	    nc->self, nc->sock);
+	debug3("%s: new mux channel %d fd %d", __func__, nc->self, nc->sock);
 	/* establish state */
-	nc->mux_rcb(nc);
+	nc->mux_rcb(ssh, nc);
 	/* mux state transitions must not elicit protocol messages */
 	nc->flags |= CHAN_LOCAL;
 }
 
 static void
-channel_handler_init(void)
+channel_handler_init(struct ssh_channels *sc)
 {
-	int i;
+	chan_fn **pre, **post;
 
-	for (i = 0; i < SSH_CHANNEL_MAX_TYPE; i++) {
-		channel_pre[i] = NULL;
-		channel_post[i] = NULL;
-	}
-	channel_pre[SSH_CHANNEL_OPEN] =			&channel_pre_open;
-	channel_pre[SSH_CHANNEL_X11_OPEN] =		&channel_pre_x11_open;
-	channel_pre[SSH_CHANNEL_PORT_LISTENER] =	&channel_pre_listener;
-	channel_pre[SSH_CHANNEL_RPORT_LISTENER] =	&channel_pre_listener;
-	channel_pre[SSH_CHANNEL_UNIX_LISTENER] =	&channel_pre_listener;
-	channel_pre[SSH_CHANNEL_RUNIX_LISTENER] =	&channel_pre_listener;
-	channel_pre[SSH_CHANNEL_X11_LISTENER] =		&channel_pre_listener;
-	channel_pre[SSH_CHANNEL_AUTH_SOCKET] =		&channel_pre_listener;
-	channel_pre[SSH_CHANNEL_CONNECTING] =		&channel_pre_connecting;
-	channel_pre[SSH_CHANNEL_DYNAMIC] =		&channel_pre_dynamic;
-	channel_pre[SSH_CHANNEL_MUX_LISTENER] =		&channel_pre_listener;
-	channel_pre[SSH_CHANNEL_MUX_CLIENT] =		&channel_pre_mux_client;
+	if ((pre = calloc(SSH_CHANNEL_MAX_TYPE, sizeof(*pre))) == NULL ||
+	   (post = calloc(SSH_CHANNEL_MAX_TYPE, sizeof(*post))) == NULL)
+		fatal("%s: allocation failed", __func__);
 
-	channel_post[SSH_CHANNEL_OPEN] =		&channel_post_open;
-	channel_post[SSH_CHANNEL_PORT_LISTENER] =	&channel_post_port_listener;
-	channel_post[SSH_CHANNEL_RPORT_LISTENER] =	&channel_post_port_listener;
-	channel_post[SSH_CHANNEL_UNIX_LISTENER] =	&channel_post_port_listener;
-	channel_post[SSH_CHANNEL_RUNIX_LISTENER] =	&channel_post_port_listener;
-	channel_post[SSH_CHANNEL_X11_LISTENER] =	&channel_post_x11_listener;
-	channel_post[SSH_CHANNEL_AUTH_SOCKET] =		&channel_post_auth_listener;
-	channel_post[SSH_CHANNEL_CONNECTING] =		&channel_post_connecting;
-	channel_post[SSH_CHANNEL_DYNAMIC] =		&channel_post_open;
-	channel_post[SSH_CHANNEL_MUX_LISTENER] =	&channel_post_mux_listener;
-	channel_post[SSH_CHANNEL_MUX_CLIENT] =		&channel_post_mux_client;
+	pre[SSH_CHANNEL_OPEN] =			&channel_pre_open;
+	pre[SSH_CHANNEL_X11_OPEN] =		&channel_pre_x11_open;
+	pre[SSH_CHANNEL_PORT_LISTENER] =	&channel_pre_listener;
+	pre[SSH_CHANNEL_RPORT_LISTENER] =	&channel_pre_listener;
+	pre[SSH_CHANNEL_UNIX_LISTENER] =	&channel_pre_listener;
+	pre[SSH_CHANNEL_RUNIX_LISTENER] =	&channel_pre_listener;
+	pre[SSH_CHANNEL_X11_LISTENER] =		&channel_pre_listener;
+	pre[SSH_CHANNEL_AUTH_SOCKET] =		&channel_pre_listener;
+	pre[SSH_CHANNEL_CONNECTING] =		&channel_pre_connecting;
+	pre[SSH_CHANNEL_DYNAMIC] =		&channel_pre_dynamic;
+	pre[SSH_CHANNEL_MUX_LISTENER] =		&channel_pre_listener;
+	pre[SSH_CHANNEL_MUX_CLIENT] =		&channel_pre_mux_client;
+
+	post[SSH_CHANNEL_OPEN] =		&channel_post_open;
+	post[SSH_CHANNEL_PORT_LISTENER] =	&channel_post_port_listener;
+	post[SSH_CHANNEL_RPORT_LISTENER] =	&channel_post_port_listener;
+	post[SSH_CHANNEL_UNIX_LISTENER] =	&channel_post_port_listener;
+	post[SSH_CHANNEL_RUNIX_LISTENER] =	&channel_post_port_listener;
+	post[SSH_CHANNEL_X11_LISTENER] =	&channel_post_x11_listener;
+	post[SSH_CHANNEL_AUTH_SOCKET] =		&channel_post_auth_listener;
+	post[SSH_CHANNEL_CONNECTING] =		&channel_post_connecting;
+	post[SSH_CHANNEL_DYNAMIC] =		&channel_post_open;
+	post[SSH_CHANNEL_MUX_LISTENER] =	&channel_post_mux_listener;
+	post[SSH_CHANNEL_MUX_CLIENT] =		&channel_post_mux_client;
+
+	sc->channel_pre = pre;
+	sc->channel_post = post;
 }
 
 /* gc dead channels */
 static void
-channel_garbage_collect(Channel *c)
+channel_garbage_collect(struct ssh *ssh, Channel *c)
 {
 	if (c == NULL)
 		return;
 	if (c->detach_user != NULL) {
-		if (!chan_is_dead(c, c->detach_close))
+		if (!chan_is_dead(ssh, c, c->detach_close))
 			return;
 		debug2("channel %d: gc: notify user", c->self);
-		c->detach_user(c->self, NULL);
+		c->detach_user(ssh, c->self, NULL);
 		/* if we still have a callback */
 		if (c->detach_user != NULL)
 			return;
 		debug2("channel %d: gc: user detached", c->self);
 	}
-	if (!chan_is_dead(c, 1))
+	if (!chan_is_dead(ssh, c, 1))
 		return;
 	debug2("channel %d: garbage collecting", c->self);
-	channel_free(c);
+	channel_free(ssh, c);
 }
 
+enum channel_table { CHAN_PRE, CHAN_POST };
+
 static void
-channel_handler(struct ssh *ssh, chan_fn *ftab[],
+channel_handler(struct ssh *ssh, int table,
     fd_set *readset, fd_set *writeset, time_t *unpause_secs)
 {
-	static int did_init = 0;
+	struct ssh_channels *sc = ssh->chanctxt;
+	chan_fn **ftab = table == CHAN_PRE ? sc->channel_pre : sc->channel_post;
 	u_int i, oalloc;
 	Channel *c;
 	time_t now;
 
-	if (!did_init) {
-		channel_handler_init();
-		did_init = 1;
-	}
 	now = monotime();
 	if (unpause_secs != NULL)
 		*unpause_secs = 0;
-	for (i = 0, oalloc = channels_alloc; i < oalloc; i++) {
-		c = channels[i];
+	for (i = 0, oalloc = sc->channels_alloc; i < oalloc; i++) {
+		c = sc->channels[i];
 		if (c == NULL)
 			continue;
 		if (c->delayed) {
-			if (ftab == channel_pre)
+			if (table == CHAN_PRE)
 				c->delayed = 0;
 			else
 				continue;
@@ -2026,7 +2246,7 @@
 			 * Run handlers that are not paused.
 			 */
 			if (c->notbefore <= now)
-				(*ftab[c->type])(c, readset, writeset);
+				(*ftab[c->type])(ssh, c, readset, writeset);
 			else if (unpause_secs != NULL) {
 				/*
 				 * Collect the time that the earliest
@@ -2040,7 +2260,7 @@
 					*unpause_secs = c->notbefore - now;
 			}
 		}
-		channel_garbage_collect(c);
+		channel_garbage_collect(ssh, c);
 	}
 	if (unpause_secs != NULL && *unpause_secs != 0)
 		debug3("%s: first channel unpauses in %d seconds",
@@ -2057,7 +2277,7 @@
 {
 	u_int n, sz, nfdset;
 
-	n = MAXIMUM(*maxfdp, channel_max_fd);
+	n = MAXIMUM(*maxfdp, ssh->chanctxt->channel_max_fd);
 
 	nfdset = howmany(n+1, NFDBITS);
 	/* Explicitly test here, because xrealloc isn't always called */
@@ -2076,7 +2296,7 @@
 	memset(*writesetp, 0, sz);
 
 	if (!ssh_packet_is_rekeying(ssh))
-		channel_handler(ssh, channel_pre, *readsetp, *writesetp,
+		channel_handler(ssh, CHAN_PRE, *readsetp, *writesetp,
 		    minwait_secs);
 }
 
@@ -2087,19 +2307,128 @@
 void
 channel_after_select(struct ssh *ssh, fd_set *readset, fd_set *writeset)
 {
-	channel_handler(ssh, channel_post, readset, writeset, NULL);
+	channel_handler(ssh, CHAN_POST, readset, writeset, NULL);
 }
 
+/*
+ * Enqueue data for channels with open or draining c->input.
+ */
+static void
+channel_output_poll_input_open(struct ssh *ssh, Channel *c)
+{
+	size_t len, dlen;
+	int r;
+
+	if ((len = sshbuf_len(c->input)) == 0) {
+		if (c->istate == CHAN_INPUT_WAIT_DRAIN) {
+			/*
+			 * input-buffer is empty and read-socket shutdown:
+			 * tell peer, that we will not send more data:
+			 * send IEOF.
+			 * hack for extended data: delay EOF if EFD still
+			 * in use.
+			 */
+			if (CHANNEL_EFD_INPUT_ACTIVE(c))
+				debug2("channel %d: "
+				    "ibuf_empty delayed efd %d/(%zu)",
+				    c->self, c->efd, sshbuf_len(c->extended));
+			else
+				chan_ibuf_empty(ssh, c);
+		}
+		return;
+	}
+
+	if (c->datagram) {
+		/* Check datagram will fit; drop if not */
+		if ((r = sshbuf_peek_string_direct(c->input, NULL, &dlen)) != 0)
+			fatal("%s: channel %d: peek datagram: %s", __func__,
+			    c->self, ssh_err(r));
+		/*
+		 * XXX this does tail-drop on the datagram queue which is
+		 * usually suboptimal compared to head-drop. Better to have
+		 * backpressure at read time? (i.e. read + discard)
+		 */
+		if (dlen > c->remote_window || dlen > c->remote_maxpacket) {
+			debug("channel %d: datagram too big", c->self);
+			return;
+		}
+		/* Enqueue it */
+		if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_DATA)) != 0 ||
+		    (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
+		    (r = sshpkt_put_stringb(ssh, c->input)) != 0 ||
+		    (r = sshpkt_send(ssh)) != 0) {
+			fatal("%s: channel %i: datagram: %s", __func__,
+			    c->self, ssh_err(r));
+		}
+		c->remote_window -= dlen;
+		return;
+	}
+
+	/* Enqueue packet for buffered data. */
+	if (len > c->remote_window)
+		len = c->remote_window;
+	if (len > c->remote_maxpacket)
+		len = c->remote_maxpacket;
+	if (len == 0)
+		return;
+	if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_DATA)) != 0 ||
+	    (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
+	    (r = sshpkt_put_string(ssh, sshbuf_ptr(c->input), len)) != 0 ||
+	    (r = sshpkt_send(ssh)) != 0) {
+		fatal("%s: channel %i: data: %s", __func__,
+		    c->self, ssh_err(r));
+	}
+	if ((r = sshbuf_consume(c->input, len)) != 0)
+		fatal("%s: channel %i: consume: %s", __func__,
+		    c->self, ssh_err(r));
+	c->remote_window -= len;
+}
+
+/*
+ * Enqueue data for channels with open c->extended in read mode.
+ */
+static void
+channel_output_poll_extended_read(struct ssh *ssh, Channel *c)
+{
+	size_t len;
+	int r;
+
+	if ((len = sshbuf_len(c->extended)) == 0)
+		return;
+
+	debug2("channel %d: rwin %u elen %zu euse %d", c->self,
+	    c->remote_window, sshbuf_len(c->extended), c->extended_usage);
+	if (len > c->remote_window)
+		len = c->remote_window;
+	if (len > c->remote_maxpacket)
+		len = c->remote_maxpacket;
+	if (len == 0)
+		return;
+	if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_EXTENDED_DATA)) != 0 ||
+	    (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
+	    (r = sshpkt_put_u32(ssh, SSH2_EXTENDED_DATA_STDERR)) != 0 ||
+	    (r = sshpkt_put_string(ssh, sshbuf_ptr(c->extended), len)) != 0 ||
+	    (r = sshpkt_send(ssh)) != 0) {
+		fatal("%s: channel %i: data: %s", __func__,
+		    c->self, ssh_err(r));
+	}
+	if ((r = sshbuf_consume(c->extended, len)) != 0)
+		fatal("%s: channel %i: consume: %s", __func__,
+		    c->self, ssh_err(r));
+	c->remote_window -= len;
+	debug2("channel %d: sent ext data %zu", c->self, len);
+}
 
 /* If there is data to send to the connection, enqueue some of it now. */
 void
-channel_output_poll(void)
+channel_output_poll(struct ssh *ssh)
 {
+	struct ssh_channels *sc = ssh->chanctxt;
 	Channel *c;
-	u_int i, len;
+	u_int i;
 
-	for (i = 0; i < channels_alloc; i++) {
-		c = channels[i];
+	for (i = 0; i < sc->channels_alloc; i++) {
+		c = sc->channels[i];
 		if (c == NULL)
 			continue;
 
@@ -2111,87 +2440,19 @@
 			continue;
 		if ((c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD))) {
 			/* XXX is this true? */
-			debug3("channel %d: will not send data after close", c->self);
+			debug3("channel %d: will not send data after close",
+			    c->self);
 			continue;
 		}
 
 		/* Get the amount of buffered data for this channel. */
-		if ((c->istate == CHAN_INPUT_OPEN ||
-		    c->istate == CHAN_INPUT_WAIT_DRAIN) &&
-		    (len = buffer_len(&c->input)) > 0) {
-			if (c->datagram) {
-				if (len > 0) {
-					u_char *data;
-					u_int dlen;
-
-					data = buffer_get_string(&c->input,
-					    &dlen);
-					if (dlen > c->remote_window ||
-					    dlen > c->remote_maxpacket) {
-						debug("channel %d: datagram "
-						    "too big for channel",
-						    c->self);
-						free(data);
-						continue;
-					}
-					packet_start(SSH2_MSG_CHANNEL_DATA);
-					packet_put_int(c->remote_id);
-					packet_put_string(data, dlen);
-					packet_send();
-					c->remote_window -= dlen;
-					free(data);
-				}
-				continue;
-			}
-			/*
-			 * Send some data for the other side over the secure
-			 * connection.
-			 */
-			if (len > c->remote_window)
-				len = c->remote_window;
-			if (len > c->remote_maxpacket)
-				len = c->remote_maxpacket;
-			if (len > 0) {
-				packet_start(SSH2_MSG_CHANNEL_DATA);
-				packet_put_int(c->remote_id);
-				packet_put_string(buffer_ptr(&c->input), len);
-				packet_send();
-				buffer_consume(&c->input, len);
-				c->remote_window -= len;
-			}
-		} else if (c->istate == CHAN_INPUT_WAIT_DRAIN) {
-			/*
-			 * input-buffer is empty and read-socket shutdown:
-			 * tell peer, that we will not send more data: send IEOF.
-			 * hack for extended data: delay EOF if EFD still in use.
-			 */
-			if (CHANNEL_EFD_INPUT_ACTIVE(c))
-				debug2("channel %d: ibuf_empty delayed efd %d/(%d)",
-				    c->self, c->efd, buffer_len(&c->extended));
-			else
-				chan_ibuf_empty(c);
-		}
+		if (c->istate == CHAN_INPUT_OPEN ||
+		    c->istate == CHAN_INPUT_WAIT_DRAIN)
+			channel_output_poll_input_open(ssh, c);
 		/* Send extended data, i.e. stderr */
 		if (!(c->flags & CHAN_EOF_SENT) &&
-		    c->remote_window > 0 &&
-		    (len = buffer_len(&c->extended)) > 0 &&
-		    c->extended_usage == CHAN_EXTENDED_READ) {
-			debug2("channel %d: rwin %u elen %u euse %d",
-			    c->self, c->remote_window, buffer_len(&c->extended),
-			    c->extended_usage);
-			if (len > c->remote_window)
-				len = c->remote_window;
-			if (len > c->remote_maxpacket)
-				len = c->remote_maxpacket;
-			packet_start(SSH2_MSG_CHANNEL_EXTENDED_DATA);
-			packet_put_int(c->remote_id);
-			packet_put_int(SSH2_EXTENDED_DATA_STDERR);
-			packet_put_string(buffer_ptr(&c->extended), len);
-			packet_send();
-			buffer_consume(&c->extended, len);
-			c->remote_window -= len;
-			debug2("channel %d: sent ext data %d", c->self, len);
-		}
+		    c->extended_usage == CHAN_EXTENDED_READ)
+			channel_output_poll_extended_read(ssh, c);
 	}
 }
 
@@ -2236,10 +2497,9 @@
  * on channel creation.
  */
 int
-channel_proxy_downstream(Channel *downstream)
+channel_proxy_downstream(struct ssh *ssh, Channel *downstream)
 {
 	Channel *c = NULL;
-	struct ssh *ssh = active_state;
 	struct sshbuf *original = NULL, *modified = NULL;
 	const u_char *cp;
 	char *ctype = NULL, *listen_host = NULL;
@@ -2248,8 +2508,8 @@
 	int ret = -1, r, idx;
 	u_int id, remote_id, listen_port;
 
-	/* sshbuf_dump(&downstream->input, stderr); */
-	if ((r = sshbuf_get_string_direct(&downstream->input, &cp, &have))
+	/* sshbuf_dump(downstream->input, stderr); */
+	if ((r = sshbuf_get_string_direct(downstream->input, &cp, &have))
 	    != 0) {
 		error("%s: malformed message: %s", __func__, ssh_err(r));
 		return -1;
@@ -2278,7 +2538,7 @@
 			error("%s: parse error %s", __func__, ssh_err(r));
 			goto out;
 		}
-		c = channel_new("mux proxy", SSH_CHANNEL_MUX_PROXY,
+		c = channel_new(ssh, "mux proxy", SSH_CHANNEL_MUX_PROXY,
 		   -1, -1, -1, 0, 0, 0, ctype, 1);
 		c->mux_ctx = downstream;	/* point to mux client */
 		c->mux_downstream_id = id;	/* original downstream id */
@@ -2286,7 +2546,7 @@
 		    (r = sshbuf_put_u32(modified, c->self)) != 0 ||
 		    (r = sshbuf_putb(modified, original)) != 0) {
 			error("%s: compose error %s", __func__, ssh_err(r));
-			channel_free(c);
+			channel_free(ssh, c);
 			goto out;
 		}
 		break;
@@ -2305,7 +2565,7 @@
 			error("%s: parse error %s", __func__, ssh_err(r));
 			goto out;
 		}
-		c = channel_new("mux proxy", SSH_CHANNEL_MUX_PROXY,
+		c = channel_new(ssh, "mux proxy", SSH_CHANNEL_MUX_PROXY,
 		   -1, -1, -1, 0, 0, 0, "mux-down-connect", 1);
 		c->mux_ctx = downstream;	/* point to mux client */
 		c->mux_downstream_id = id;
@@ -2314,7 +2574,7 @@
 		    (r = sshbuf_put_u32(modified, c->self)) != 0 ||
 		    (r = sshbuf_putb(modified, original)) != 0) {
 			error("%s: compose error %s", __func__, ssh_err(r));
-			channel_free(c);
+			channel_free(ssh, c);
 			goto out;
 		}
 		break;
@@ -2343,23 +2603,17 @@
 			goto out;
 		}
 		/* Record that connection to this host/port is permitted. */
-		permitted_opens = xreallocarray(permitted_opens,
-		    num_permitted_opens + 1, sizeof(*permitted_opens));
-		idx = num_permitted_opens++;
-		permitted_opens[idx].host_to_connect = xstrdup("<mux>");
-		permitted_opens[idx].port_to_connect = -1;
-		permitted_opens[idx].listen_host = listen_host;
-		permitted_opens[idx].listen_port = (int)listen_port;
-		permitted_opens[idx].downstream = downstream;
+		idx = fwd_perm_list_add(ssh, FWDPERM_USER, "<mux>", -1,
+		    listen_host, NULL, (int)listen_port, downstream);
 		listen_host = NULL;
 		break;
 	case SSH2_MSG_CHANNEL_CLOSE:
 		if (have < 4)
 			break;
 		remote_id = PEEK_U32(cp);
-		if ((c = channel_by_remote_id(remote_id)) != NULL) {
+		if ((c = channel_by_remote_id(ssh, remote_id)) != NULL) {
 			if (c->flags & CHAN_CLOSE_RCVD)
-				channel_free(c);
+				channel_free(ssh, c);
 			else
 				c->flags |= CHAN_CLOSE_SENT;
 		}
@@ -2446,7 +2700,7 @@
 	    (r = sshbuf_put_u8(b, type)) != 0 ||
 	    (r = sshbuf_put_u32(b, c->mux_downstream_id)) != 0 ||
 	    (r = sshbuf_put(b, cp, len)) != 0 ||
-	    (r = sshbuf_put_stringb(&downstream->output, b)) != 0) {
+	    (r = sshbuf_put_stringb(downstream->output, b)) != 0) {
 		error("%s: compose for muxclient %s", __func__, ssh_err(r));
 		goto out;
 	}
@@ -2464,7 +2718,7 @@
 		break;
 	case SSH2_MSG_CHANNEL_CLOSE:
 		if (c->flags & CHAN_CLOSE_SENT)
-			channel_free(c);
+			channel_free(ssh, c);
 		else
 			c->flags |= CHAN_CLOSE_RCVD;
 		break;
@@ -2475,20 +2729,46 @@
 
 /* -- protocol input */
 
-/* ARGSUSED */
+/* Parse a channel ID from the current packet */
+static int
+channel_parse_id(struct ssh *ssh, const char *where, const char *what)
+{
+	u_int32_t id;
+	int r;
+
+	if ((r = sshpkt_get_u32(ssh, &id)) != 0) {
+		error("%s: parse id: %s", where, ssh_err(r));
+		ssh_packet_disconnect(ssh, "Invalid %s message", what);
+	}
+	if (id > INT_MAX) {
+		error("%s: bad channel id %u: %s", where, id, ssh_err(r));
+		ssh_packet_disconnect(ssh, "Invalid %s channel id", what);
+	}
+	return (int)id;
+}
+
+/* Lookup a channel from an ID in the current packet */
+static Channel *
+channel_from_packet_id(struct ssh *ssh, const char *where, const char *what)
+{
+	int id = channel_parse_id(ssh, where, what);
+	Channel *c;
+
+	if ((c = channel_lookup(ssh, id)) == NULL) {
+		ssh_packet_disconnect(ssh,
+		    "%s packet referred to nonexistent channel %d", what, id);
+	}
+	return c;
+}
+
 int
 channel_input_data(int type, u_int32_t seq, struct ssh *ssh)
 {
-	int id;
 	const u_char *data;
-	u_int data_len, win_len;
-	Channel *c;
+	size_t data_len, win_len;
+	Channel *c = channel_from_packet_id(ssh, __func__, "data");
+	int r;
 
-	/* Get the channel number and verify it. */
-	id = packet_get_int();
-	c = channel_lookup(id);
-	if (c == NULL)
-		packet_disconnect("Received data for nonexistent channel %d.", id);
 	if (channel_proxy_upstream(c, type, seq, ssh))
 		return 0;
 
@@ -2498,17 +2778,19 @@
 		return 0;
 
 	/* Get the data. */
-	data = packet_get_string_ptr(&data_len);
+	if ((r = sshpkt_get_string_direct(ssh, &data, &data_len)) != 0)
+		fatal("%s: channel %d: get data: %s", __func__,
+		    c->self, ssh_err(r));
+	ssh_packet_check_eom(ssh);
+
 	win_len = data_len;
 	if (c->datagram)
 		win_len += 4;  /* string length header */
 
 	/*
-	 * Ignore data for protocol > 1.3 if output end is no longer open.
-	 * For protocol 2 the sending side is reducing its window as it sends
-	 * data, so we must 'fake' consumption of the data in order to ensure
-	 * that window updates are sent back.  Otherwise the connection might
-	 * deadlock.
+	 * The sending side reduces its window as it sends data, so we
+	 * must 'fake' consumption of the data in order to ensure that window
+	 * updates are sent back. Otherwise the connection might deadlock.
 	 */
 	if (c->ostate != CHAN_OUTPUT_OPEN) {
 		c->local_window -= win_len;
@@ -2517,149 +2799,148 @@
 	}
 
 	if (win_len > c->local_maxpacket) {
-		logit("channel %d: rcvd big packet %d, maxpack %d",
+		logit("channel %d: rcvd big packet %zu, maxpack %u",
 		    c->self, win_len, c->local_maxpacket);
+		return 0;
 	}
 	if (win_len > c->local_window) {
-		logit("channel %d: rcvd too much data %d, win %d",
+		logit("channel %d: rcvd too much data %zu, win %u",
 		    c->self, win_len, c->local_window);
 		return 0;
 	}
 	c->local_window -= win_len;
 
-	if (c->datagram)
-		buffer_put_string(&c->output, data, data_len);
-	else
-		buffer_append(&c->output, data, data_len);
-	packet_check_eom();
+	if (c->datagram) {
+		if ((r = sshbuf_put_string(c->output, data, data_len)) != 0)
+			fatal("%s: channel %d: append datagram: %s",
+			    __func__, c->self, ssh_err(r));
+	} else if ((r = sshbuf_put(c->output, data, data_len)) != 0)
+		fatal("%s: channel %d: append data: %s",
+		    __func__, c->self, ssh_err(r));
+
 	return 0;
 }
 
-/* ARGSUSED */
 int
 channel_input_extended_data(int type, u_int32_t seq, struct ssh *ssh)
 {
-	int id;
-	char *data;
-	u_int data_len, tcode;
-	Channel *c;
+	const u_char *data;
+	size_t data_len;
+	u_int32_t tcode;
+	Channel *c = channel_from_packet_id(ssh, __func__, "extended data");
+	int r;
 
-	/* Get the channel number and verify it. */
-	id = packet_get_int();
-	c = channel_lookup(id);
-
-	if (c == NULL)
-		packet_disconnect("Received extended_data for bad channel %d.", id);
 	if (channel_proxy_upstream(c, type, seq, ssh))
 		return 0;
 	if (c->type != SSH_CHANNEL_OPEN) {
-		logit("channel %d: ext data for non open", id);
+		logit("channel %d: ext data for non open", c->self);
 		return 0;
 	}
 	if (c->flags & CHAN_EOF_RCVD) {
 		if (datafellows & SSH_BUG_EXTEOF)
-			debug("channel %d: accepting ext data after eof", id);
+			debug("channel %d: accepting ext data after eof",
+			    c->self);
 		else
-			packet_disconnect("Received extended_data after EOF "
-			    "on channel %d.", id);
+			ssh_packet_disconnect(ssh, "Received extended_data "
+			    "after EOF on channel %d.", c->self);
 	}
-	tcode = packet_get_int();
+
+	if ((r = sshpkt_get_u32(ssh, &tcode)) != 0) {
+		error("%s: parse tcode: %s", __func__, ssh_err(r));
+		ssh_packet_disconnect(ssh, "Invalid extended_data message");
+	}
 	if (c->efd == -1 ||
 	    c->extended_usage != CHAN_EXTENDED_WRITE ||
 	    tcode != SSH2_EXTENDED_DATA_STDERR) {
 		logit("channel %d: bad ext data", c->self);
 		return 0;
 	}
-	data = packet_get_string(&data_len);
-	packet_check_eom();
+	if ((r = sshpkt_get_string_direct(ssh, &data, &data_len)) != 0) {
+		error("%s: parse data: %s", __func__, ssh_err(r));
+		ssh_packet_disconnect(ssh, "Invalid extended_data message");
+	}
+	ssh_packet_check_eom(ssh);
+
 	if (data_len > c->local_window) {
-		logit("channel %d: rcvd too much extended_data %d, win %d",
+		logit("channel %d: rcvd too much extended_data %zu, win %u",
 		    c->self, data_len, c->local_window);
-		free(data);
 		return 0;
 	}
-	debug2("channel %d: rcvd ext data %d", c->self, data_len);
+	debug2("channel %d: rcvd ext data %zu", c->self, data_len);
+	/* XXX sshpkt_getb? */
+	if ((r = sshbuf_put(c->extended, data, data_len)) != 0)
+		error("%s: append: %s", __func__, ssh_err(r));
 	c->local_window -= data_len;
-	buffer_append(&c->extended, data, data_len);
-	free(data);
 	return 0;
 }
 
-/* ARGSUSED */
 int
 channel_input_ieof(int type, u_int32_t seq, struct ssh *ssh)
 {
-	int id;
-	Channel *c;
+	Channel *c = channel_from_packet_id(ssh, __func__, "ieof");
 
-	id = packet_get_int();
-	packet_check_eom();
-	c = channel_lookup(id);
-	if (c == NULL)
-		packet_disconnect("Received ieof for nonexistent channel %d.", id);
+	ssh_packet_check_eom(ssh);
+
 	if (channel_proxy_upstream(c, type, seq, ssh))
 		return 0;
-	chan_rcvd_ieof(c);
+	chan_rcvd_ieof(ssh, c);
 
 	/* XXX force input close */
 	if (c->force_drain && c->istate == CHAN_INPUT_OPEN) {
 		debug("channel %d: FORCE input drain", c->self);
 		c->istate = CHAN_INPUT_WAIT_DRAIN;
-		if (buffer_len(&c->input) == 0)
-			chan_ibuf_empty(c);
+		if (sshbuf_len(c->input) == 0)
+			chan_ibuf_empty(ssh, c);
 	}
 	return 0;
 }
 
-/* ARGSUSED */
 int
 channel_input_oclose(int type, u_int32_t seq, struct ssh *ssh)
 {
-	int id = packet_get_int();
-	Channel *c = channel_lookup(id);
+	Channel *c = channel_from_packet_id(ssh, __func__, "oclose");
 
-	if (c == NULL)
-		packet_disconnect("Received oclose for nonexistent channel %d.", id);
 	if (channel_proxy_upstream(c, type, seq, ssh))
 		return 0;
-	packet_check_eom();
-	chan_rcvd_oclose(c);
+	ssh_packet_check_eom(ssh);
+	chan_rcvd_oclose(ssh, c);
 	return 0;
 }
 
-/* ARGSUSED */
 int
 channel_input_open_confirmation(int type, u_int32_t seq, struct ssh *ssh)
 {
-	int id, remote_id;
-	Channel *c;
+	Channel *c = channel_from_packet_id(ssh, __func__, "open confirmation");
+	u_int32_t remote_window, remote_maxpacket;
+	int r;
 
-	id = packet_get_int();
-	c = channel_lookup(id);
-
-	if (c==NULL)
-		packet_disconnect("Received open confirmation for "
-		    "unknown channel %d.", id);
 	if (channel_proxy_upstream(c, type, seq, ssh))
 		return 0;
 	if (c->type != SSH_CHANNEL_OPENING)
 		packet_disconnect("Received open confirmation for "
-		    "non-opening channel %d.", id);
-	remote_id = packet_get_int();
-	/* Record the remote channel number and mark that the channel is now open. */
-	c->remote_id = remote_id;
-	c->type = SSH_CHANNEL_OPEN;
+		    "non-opening channel %d.", c->self);
+	/*
+	 * Record the remote channel number and mark that the channel
+	 * is now open.
+	 */
+	c->remote_id = channel_parse_id(ssh, __func__, "open confirmation");
+	if ((r = sshpkt_get_u32(ssh, &remote_window)) != 0 ||
+	    (r = sshpkt_get_u32(ssh, &remote_maxpacket)) != 0) {
+		error("%s: window/maxpacket: %s", __func__, ssh_err(r));
+		packet_disconnect("Invalid open confirmation message");
+	}
+	ssh_packet_check_eom(ssh);
 
-	c->remote_window = packet_get_int();
-	c->remote_maxpacket = packet_get_int();
+	c->remote_window = remote_window;
+	c->remote_maxpacket = remote_maxpacket;
+	c->type = SSH_CHANNEL_OPEN;
 	if (c->open_confirm) {
-		debug2("callback start");
-		c->open_confirm(c->self, 1, c->open_confirm_ctx);
-		debug2("callback done");
+		debug2("%s: channel %d: callback start", __func__, c->self);
+		c->open_confirm(ssh, c->self, 1, c->open_confirm_ctx);
+		debug2("%s: channel %d: callback done", __func__, c->self);
 	}
 	debug2("channel %d: open confirm rwindow %u rmax %u", c->self,
 	    c->remote_window, c->remote_maxpacket);
-	packet_check_eom();
 	return 0;
 }
 
@@ -2679,97 +2960,97 @@
 	return "unknown reason";
 }
 
-/* ARGSUSED */
 int
 channel_input_open_failure(int type, u_int32_t seq, struct ssh *ssh)
 {
-	int id, reason;
-	char *msg = NULL, *lang = NULL;
-	Channel *c;
+	Channel *c = channel_from_packet_id(ssh, __func__, "open failure");
+	u_int32_t reason;
+	char *msg = NULL;
+	int r;
 
-	id = packet_get_int();
-	c = channel_lookup(id);
-
-	if (c==NULL)
-		packet_disconnect("Received open failure for "
-		    "unknown channel %d.", id);
 	if (channel_proxy_upstream(c, type, seq, ssh))
 		return 0;
 	if (c->type != SSH_CHANNEL_OPENING)
 		packet_disconnect("Received open failure for "
-		    "non-opening channel %d.", id);
-	reason = packet_get_int();
-	if (!(datafellows & SSH_BUG_OPENFAILURE)) {
-		msg  = packet_get_string(NULL);
-		lang = packet_get_string(NULL);
+		    "non-opening channel %d.", c->self);
+	if ((r = sshpkt_get_u32(ssh, &reason)) != 0) {
+		error("%s: reason: %s", __func__, ssh_err(r));
+		packet_disconnect("Invalid open failure message");
 	}
-	logit("channel %d: open failed: %s%s%s", id,
+	if ((datafellows & SSH_BUG_OPENFAILURE) == 0) {
+		/* skip language */
+		if ((r = sshpkt_get_cstring(ssh, &msg, NULL)) != 0 ||
+		    (r = sshpkt_get_string_direct(ssh, NULL, NULL)) == 0) {
+			error("%s: message/lang: %s", __func__, ssh_err(r));
+			packet_disconnect("Invalid open failure message");
+		}
+	}
+	ssh_packet_check_eom(ssh);
+	logit("channel %d: open failed: %s%s%s", c->self,
 	    reason2txt(reason), msg ? ": ": "", msg ? msg : "");
 	free(msg);
-	free(lang);
 	if (c->open_confirm) {
-		debug2("callback start");
-		c->open_confirm(c->self, 0, c->open_confirm_ctx);
-		debug2("callback done");
+		debug2("%s: channel %d: callback start", __func__, c->self);
+		c->open_confirm(ssh, c->self, 0, c->open_confirm_ctx);
+		debug2("%s: channel %d: callback done", __func__, c->self);
 	}
-	packet_check_eom();
 	/* Schedule the channel for cleanup/deletion. */
-	chan_mark_dead(c);
+	chan_mark_dead(ssh, c);
 	return 0;
 }
 
-/* ARGSUSED */
 int
 channel_input_window_adjust(int type, u_int32_t seq, struct ssh *ssh)
 {
+	int id = channel_parse_id(ssh, __func__, "window adjust");
 	Channel *c;
-	int id;
-	u_int adjust, tmp;
+	u_int32_t adjust;
+	u_int new_rwin;
+	int r;
 
-	/* Get the channel number and verify it. */
-	id = packet_get_int();
-	c = channel_lookup(id);
-
-	if (c == NULL) {
+	if ((c = channel_lookup(ssh, id)) == NULL) {
 		logit("Received window adjust for non-open channel %d.", id);
 		return 0;
-	}
+        }
+
 	if (channel_proxy_upstream(c, type, seq, ssh))
 		return 0;
-	adjust = packet_get_int();
-	packet_check_eom();
-	debug2("channel %d: rcvd adjust %u", id, adjust);
-	if ((tmp = c->remote_window + adjust) < c->remote_window)
+	if ((r = sshpkt_get_u32(ssh, &adjust)) != 0) {
+		error("%s: adjust: %s", __func__, ssh_err(r));
+		packet_disconnect("Invalid window adjust message");
+	}
+	ssh_packet_check_eom(ssh);
+	debug2("channel %d: rcvd adjust %u", c->self, adjust);
+	if ((new_rwin = c->remote_window + adjust) < c->remote_window) {
 		fatal("channel %d: adjust %u overflows remote window %u",
-		    id, adjust, c->remote_window);
-	c->remote_window = tmp;
+		    c->self, adjust, c->remote_window);
+	}
+	c->remote_window = new_rwin;
 	return 0;
 }
 
-/* ARGSUSED */
 int
 channel_input_status_confirm(int type, u_int32_t seq, struct ssh *ssh)
 {
+	int id = channel_parse_id(ssh, __func__, "status confirm");
 	Channel *c;
 	struct channel_confirm *cc;
-	int id;
 
 	/* Reset keepalive timeout */
 	packet_set_alive_timeouts(0);
 
-	id = packet_get_int();
-	debug2("channel_input_status_confirm: type %d id %d", type, id);
+	debug2("%s: type %d id %d", __func__, type, id);
 
-	if ((c = channel_lookup(id)) == NULL) {
-		logit("channel_input_status_confirm: %d: unknown", id);
+	if ((c = channel_lookup(ssh, id)) == NULL) {
+		logit("%s: %d: unknown", __func__, id);
 		return 0;
-	}	
+	}
 	if (channel_proxy_upstream(c, type, seq, ssh))
 		return 0;
-	packet_check_eom();
+	ssh_packet_check_eom(ssh);
 	if ((cc = TAILQ_FIRST(&c->status_confirms)) == NULL)
 		return 0;
-	cc->cb(type, c, cc->ctx);
+	cc->cb(ssh, type, c, cc->ctx);
 	TAILQ_REMOVE(&c->status_confirms, cc, entry);
 	explicit_bzero(cc, sizeof(*cc));
 	free(cc);
@@ -2779,9 +3060,9 @@
 /* -- tcp forwarding */
 
 void
-channel_set_af(int af)
+channel_set_af(struct ssh *ssh, int af)
 {
-	IPv4or6 = af;
+	ssh->chanctxt->IPv4or6 = af;
 }
 
 
@@ -2849,8 +3130,9 @@
 }
 
 static int
-channel_setup_fwd_listener_tcpip(int type, struct Forward *fwd,
-    int *allocated_listen_port, struct ForwardOptions *fwd_opts)
+channel_setup_fwd_listener_tcpip(struct ssh *ssh, int type,
+    struct Forward *fwd, int *allocated_listen_port,
+    struct ForwardOptions *fwd_opts)
 {
 	Channel *c;
 	int sock, r, success = 0, wildcard = 0, is_client;
@@ -2887,7 +3169,7 @@
 	 * set to NULL and hints.ai_flags is not AI_PASSIVE
 	 */
 	memset(&hints, 0, sizeof(hints));
-	hints.ai_family = IPv4or6;
+	hints.ai_family = ssh->chanctxt->IPv4or6;
 	hints.ai_flags = wildcard ? AI_PASSIVE : 0;
 	hints.ai_socktype = SOCK_STREAM;
 	snprintf(strport, sizeof strport, "%d", fwd->listen_port);
@@ -2921,12 +3203,14 @@
 		 * If allocating a port for -R forwards, then use the
 		 * same port for all address families.
 		 */
-		if (type == SSH_CHANNEL_RPORT_LISTENER && fwd->listen_port == 0 &&
-		    allocated_listen_port != NULL && *allocated_listen_port > 0)
+		if (type == SSH_CHANNEL_RPORT_LISTENER &&
+		    fwd->listen_port == 0 && allocated_listen_port != NULL &&
+		    *allocated_listen_port > 0)
 			*lport_p = htons(*allocated_listen_port);
 
 		if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop),
-		    strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV) != 0) {
+		    strport, sizeof(strport),
+		    NI_NUMERICHOST|NI_NUMERICSERV) != 0) {
 			error("%s: getnameinfo failed", __func__);
 			continue;
 		}
@@ -2947,7 +3231,10 @@
 
 		/* Bind the socket to the address. */
 		if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) {
-			/* address can be in use ipv6 address is already bound */
+			/*
+			 * address can be in if use ipv6 address is
+			 * already bound
+			 */
 			if (!ai->ai_next)
 				error("bind: %.100s", strerror(errno));
 			else
@@ -2967,7 +3254,8 @@
 		 * fwd->listen_port == 0 requests a dynamically allocated port -
 		 * record what we got.
 		 */
-		if (type == SSH_CHANNEL_RPORT_LISTENER && fwd->listen_port == 0 &&
+		if (type == SSH_CHANNEL_RPORT_LISTENER &&
+		    fwd->listen_port == 0 &&
 		    allocated_listen_port != NULL &&
 		    *allocated_listen_port == 0) {
 			*allocated_listen_port = get_local_port(sock);
@@ -2976,7 +3264,7 @@
 		}
 
 		/* Allocate a channel number for the socket. */
-		c = channel_new("port listener", type, sock, sock, -1,
+		c = channel_new(ssh, "port listener", type, sock, sock, -1,
 		    CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT,
 		    0, "port listener", 1);
 		c->path = xstrdup(host);
@@ -2997,8 +3285,8 @@
 }
 
 static int
-channel_setup_fwd_listener_streamlocal(int type, struct Forward *fwd,
-    struct ForwardOptions *fwd_opts)
+channel_setup_fwd_listener_streamlocal(struct ssh *ssh, int type,
+    struct Forward *fwd, struct ForwardOptions *fwd_opts)
 {
 	struct sockaddr_un sunaddr;
 	const char *path;
@@ -3060,7 +3348,7 @@
 	debug("Local forwarding listening on path %s.", fwd->listen_path);
 
 	/* Allocate a channel number for the socket. */
-	c = channel_new("unix listener", type, sock, sock, -1,
+	c = channel_new(ssh, "unix listener", type, sock, sock, -1,
 	    CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT,
 	    0, "unix listener", 1);
 	c->path = xstrdup(path);
@@ -3071,66 +3359,71 @@
 }
 
 static int
-channel_cancel_rport_listener_tcpip(const char *host, u_short port)
+channel_cancel_rport_listener_tcpip(struct ssh *ssh,
+    const char *host, u_short port)
 {
 	u_int i;
 	int found = 0;
 
-	for (i = 0; i < channels_alloc; i++) {
-		Channel *c = channels[i];
+	for (i = 0; i < ssh->chanctxt->channels_alloc; i++) {
+		Channel *c = ssh->chanctxt->channels[i];
 		if (c == NULL || c->type != SSH_CHANNEL_RPORT_LISTENER)
 			continue;
 		if (strcmp(c->path, host) == 0 && c->listening_port == port) {
 			debug2("%s: close channel %d", __func__, i);
-			channel_free(c);
+			channel_free(ssh, c);
 			found = 1;
 		}
 	}
 
-	return (found);
+	return found;
 }
 
 static int
-channel_cancel_rport_listener_streamlocal(const char *path)
+channel_cancel_rport_listener_streamlocal(struct ssh *ssh, const char *path)
 {
 	u_int i;
 	int found = 0;
 
-	for (i = 0; i < channels_alloc; i++) {
-		Channel *c = channels[i];
+	for (i = 0; i < ssh->chanctxt->channels_alloc; i++) {
+		Channel *c = ssh->chanctxt->channels[i];
 		if (c == NULL || c->type != SSH_CHANNEL_RUNIX_LISTENER)
 			continue;
 		if (c->path == NULL)
 			continue;
 		if (strcmp(c->path, path) == 0) {
 			debug2("%s: close channel %d", __func__, i);
-			channel_free(c);
+			channel_free(ssh, c);
 			found = 1;
 		}
 	}
 
-	return (found);
+	return found;
 }
 
 int
-channel_cancel_rport_listener(struct Forward *fwd)
+channel_cancel_rport_listener(struct ssh *ssh, struct Forward *fwd)
 {
-	if (fwd->listen_path != NULL)
-		return channel_cancel_rport_listener_streamlocal(fwd->listen_path);
-	else
-		return channel_cancel_rport_listener_tcpip(fwd->listen_host, fwd->listen_port);
+	if (fwd->listen_path != NULL) {
+		return channel_cancel_rport_listener_streamlocal(ssh,
+		    fwd->listen_path);
+	} else {
+		return channel_cancel_rport_listener_tcpip(ssh,
+		    fwd->listen_host, fwd->listen_port);
+	}
 }
 
 static int
-channel_cancel_lport_listener_tcpip(const char *lhost, u_short lport,
-    int cport, struct ForwardOptions *fwd_opts)
+channel_cancel_lport_listener_tcpip(struct ssh *ssh,
+    const char *lhost, u_short lport, int cport,
+    struct ForwardOptions *fwd_opts)
 {
 	u_int i;
 	int found = 0;
 	const char *addr = channel_fwd_bind_addr(lhost, NULL, 1, fwd_opts);
 
-	for (i = 0; i < channels_alloc; i++) {
-		Channel *c = channels[i];
+	for (i = 0; i < ssh->chanctxt->channels_alloc; i++) {
+		Channel *c = ssh->chanctxt->channels[i];
 		if (c == NULL || c->type != SSH_CHANNEL_PORT_LISTENER)
 			continue;
 		if (c->listening_port != lport)
@@ -3148,16 +3441,16 @@
 			continue;
 		if (addr == NULL || strcmp(c->listening_addr, addr) == 0) {
 			debug2("%s: close channel %d", __func__, i);
-			channel_free(c);
+			channel_free(ssh, c);
 			found = 1;
 		}
 	}
 
-	return (found);
+	return found;
 }
 
 static int
-channel_cancel_lport_listener_streamlocal(const char *path)
+channel_cancel_lport_listener_streamlocal(struct ssh *ssh, const char *path)
 {
 	u_int i;
 	int found = 0;
@@ -3167,54 +3460,59 @@
 		return 0;
 	}
 
-	for (i = 0; i < channels_alloc; i++) {
-		Channel *c = channels[i];
+	for (i = 0; i < ssh->chanctxt->channels_alloc; i++) {
+		Channel *c = ssh->chanctxt->channels[i];
 		if (c == NULL || c->type != SSH_CHANNEL_UNIX_LISTENER)
 			continue;
 		if (c->listening_addr == NULL)
 			continue;
 		if (strcmp(c->listening_addr, path) == 0) {
 			debug2("%s: close channel %d", __func__, i);
-			channel_free(c);
+			channel_free(ssh, c);
 			found = 1;
 		}
 	}
 
-	return (found);
+	return found;
 }
 
 int
-channel_cancel_lport_listener(struct Forward *fwd, int cport, struct ForwardOptions *fwd_opts)
+channel_cancel_lport_listener(struct ssh *ssh,
+    struct Forward *fwd, int cport, struct ForwardOptions *fwd_opts)
 {
-	if (fwd->listen_path != NULL)
-		return channel_cancel_lport_listener_streamlocal(fwd->listen_path);
-	else
-		return channel_cancel_lport_listener_tcpip(fwd->listen_host, fwd->listen_port, cport, fwd_opts);
+	if (fwd->listen_path != NULL) {
+		return channel_cancel_lport_listener_streamlocal(ssh,
+		    fwd->listen_path);
+	} else {
+		return channel_cancel_lport_listener_tcpip(ssh,
+		    fwd->listen_host, fwd->listen_port, cport, fwd_opts);
+	}
 }
 
 /* protocol local port fwd, used by ssh */
 int
-channel_setup_local_fwd_listener(struct Forward *fwd, struct ForwardOptions *fwd_opts)
+channel_setup_local_fwd_listener(struct ssh *ssh,
+    struct Forward *fwd, struct ForwardOptions *fwd_opts)
 {
 	if (fwd->listen_path != NULL) {
-		return channel_setup_fwd_listener_streamlocal(
+		return channel_setup_fwd_listener_streamlocal(ssh,
 		    SSH_CHANNEL_UNIX_LISTENER, fwd, fwd_opts);
 	} else {
-		return channel_setup_fwd_listener_tcpip(SSH_CHANNEL_PORT_LISTENER,
-		    fwd, NULL, fwd_opts);
+		return channel_setup_fwd_listener_tcpip(ssh,
+		    SSH_CHANNEL_PORT_LISTENER, fwd, NULL, fwd_opts);
 	}
 }
 
 /* protocol v2 remote port fwd, used by sshd */
 int
-channel_setup_remote_fwd_listener(struct Forward *fwd,
+channel_setup_remote_fwd_listener(struct ssh *ssh, struct Forward *fwd,
     int *allocated_listen_port, struct ForwardOptions *fwd_opts)
 {
 	if (fwd->listen_path != NULL) {
-		return channel_setup_fwd_listener_streamlocal(
+		return channel_setup_fwd_listener_streamlocal(ssh,
 		    SSH_CHANNEL_RUNIX_LISTENER, fwd, fwd_opts);
 	} else {
-		return channel_setup_fwd_listener_tcpip(
+		return channel_setup_fwd_listener_tcpip(ssh,
 		    SSH_CHANNEL_RPORT_LISTENER, fwd, allocated_listen_port,
 		    fwd_opts);
 	}
@@ -3248,56 +3546,61 @@
  * channel_update_permitted_opens().
  */
 int
-channel_request_remote_forwarding(struct Forward *fwd)
+channel_request_remote_forwarding(struct ssh *ssh, struct Forward *fwd)
 {
-	int success = 0, idx = -1;
+	int r, success = 0, idx = -1;
+	char *host_to_connect, *listen_host, *listen_path;
+	int port_to_connect, listen_port;
 
 	/* Send the forward request to the remote side. */
-	packet_start(SSH2_MSG_GLOBAL_REQUEST);
 	if (fwd->listen_path != NULL) {
-	    packet_put_cstring("streamlocal-forward@openssh.com");
-	    packet_put_char(1);		/* boolean: want reply */
-	    packet_put_cstring(fwd->listen_path);
+		if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 ||
+		    (r = sshpkt_put_cstring(ssh,
+		    "streamlocal-forward@openssh.com")) != 0 ||
+		    (r = sshpkt_put_u8(ssh, 1)) != 0 || /* want reply */
+		    (r = sshpkt_put_cstring(ssh, fwd->listen_path)) != 0 ||
+		    (r = sshpkt_send(ssh)) != 0 ||
+		    (r = ssh_packet_write_wait(ssh)) != 0)
+			fatal("%s: request streamlocal: %s",
+			    __func__, ssh_err(r));
 	} else {
-	    packet_put_cstring("tcpip-forward");
-	    packet_put_char(1);		/* boolean: want reply */
-	    packet_put_cstring(channel_rfwd_bind_host(fwd->listen_host));
-	    packet_put_int(fwd->listen_port);
+		if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 ||
+		    (r = sshpkt_put_cstring(ssh, "tcpip-forward")) != 0 ||
+		    (r = sshpkt_put_u8(ssh, 1)) != 0 || /* want reply */
+		    (r = sshpkt_put_cstring(ssh,
+		    channel_rfwd_bind_host(fwd->listen_host))) != 0 ||
+		    (r = sshpkt_put_u32(ssh, fwd->listen_port)) != 0 ||
+		    (r = sshpkt_send(ssh)) != 0 ||
+		    (r = ssh_packet_write_wait(ssh)) != 0)
+			fatal("%s: request tcpip-forward: %s",
+			    __func__, ssh_err(r));
 	}
-	packet_send();
-	packet_write_wait();
 	/* Assume that server accepts the request */
 	success = 1;
 	if (success) {
 		/* Record that connection to this host/port is permitted. */
-		permitted_opens = xreallocarray(permitted_opens,
-		    num_permitted_opens + 1, sizeof(*permitted_opens));
-		idx = num_permitted_opens++;
+		host_to_connect = listen_host = listen_path = NULL;
+		port_to_connect = listen_port = 0;
 		if (fwd->connect_path != NULL) {
-			permitted_opens[idx].host_to_connect =
-			    xstrdup(fwd->connect_path);
-			permitted_opens[idx].port_to_connect =
-			    PORT_STREAMLOCAL;
+			host_to_connect = xstrdup(fwd->connect_path);
+			port_to_connect = PORT_STREAMLOCAL;
 		} else {
-			permitted_opens[idx].host_to_connect =
-			    xstrdup(fwd->connect_host);
-			permitted_opens[idx].port_to_connect =
-			    fwd->connect_port;
+			host_to_connect = xstrdup(fwd->connect_host);
+			port_to_connect = fwd->connect_port;
 		}
 		if (fwd->listen_path != NULL) {
-			permitted_opens[idx].listen_host = NULL;
-			permitted_opens[idx].listen_path =
-			    xstrdup(fwd->listen_path);
-			permitted_opens[idx].listen_port = PORT_STREAMLOCAL;
+			listen_path = xstrdup(fwd->listen_path);
+			listen_port = PORT_STREAMLOCAL;
 		} else {
-			permitted_opens[idx].listen_host =
-			    fwd->listen_host ? xstrdup(fwd->listen_host) : NULL;
-			permitted_opens[idx].listen_path = NULL;
-			permitted_opens[idx].listen_port = fwd->listen_port;
+			if (fwd->listen_host != NULL)
+				listen_host = xstrdup(fwd->listen_host);
+			listen_port = fwd->listen_port;
 		}
-		permitted_opens[idx].downstream = NULL;
+		idx = fwd_perm_list_add(ssh, FWDPERM_USER,
+		    host_to_connect, port_to_connect,
+		    listen_host, listen_path, listen_port, NULL);
 	}
-	return (idx);
+	return idx;
 }
 
 static int
@@ -3362,33 +3665,33 @@
  * local side.
  */
 static int
-channel_request_rforward_cancel_tcpip(const char *host, u_short port)
+channel_request_rforward_cancel_tcpip(struct ssh *ssh,
+    const char *host, u_short port)
 {
-	int i;
+	struct ssh_channels *sc = ssh->chanctxt;
+	int r;
+	u_int i;
+	ForwardPermission *fp;
 
-	for (i = 0; i < num_permitted_opens; i++) {
-		if (open_listen_match_tcpip(&permitted_opens[i], host, port, 0))
+	for (i = 0; i < sc->num_permitted_opens; i++) {
+		fp = &sc->permitted_opens[i];
+		if (open_listen_match_tcpip(fp, host, port, 0))
 			break;
+		fp = NULL;
 	}
-	if (i >= num_permitted_opens) {
+	if (fp == NULL) {
 		debug("%s: requested forward not found", __func__);
 		return -1;
 	}
-	packet_start(SSH2_MSG_GLOBAL_REQUEST);
-	packet_put_cstring("cancel-tcpip-forward");
-	packet_put_char(0);
-	packet_put_cstring(channel_rfwd_bind_host(host));
-	packet_put_int(port);
-	packet_send();
+	if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 ||
+	    (r = sshpkt_put_cstring(ssh, "cancel-tcpip-forward")) != 0 ||
+	    (r = sshpkt_put_u8(ssh, 0)) != 0 || /* want reply */
+	    (r = sshpkt_put_cstring(ssh, channel_rfwd_bind_host(host))) != 0 ||
+	    (r = sshpkt_put_u32(ssh, port)) != 0 ||
+	    (r = sshpkt_send(ssh)) != 0)
+		fatal("%s: send cancel: %s", __func__, ssh_err(r));
 
-	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;
-	free(permitted_opens[i].listen_host);
-	permitted_opens[i].listen_host = NULL;
-	permitted_opens[i].listen_path = NULL;
-	permitted_opens[i].downstream = NULL;
+	fwd_perm_clear(fp); /* unregister */
 
 	return 0;
 }
@@ -3398,32 +3701,32 @@
  * path from local side.
  */
 static int
-channel_request_rforward_cancel_streamlocal(const char *path)
+channel_request_rforward_cancel_streamlocal(struct ssh *ssh, const char *path)
 {
-	int i;
+	struct ssh_channels *sc = ssh->chanctxt;
+	int r;
+	u_int i;
+	ForwardPermission *fp;
 
-	for (i = 0; i < num_permitted_opens; i++) {
-		if (open_listen_match_streamlocal(&permitted_opens[i], path))
+	for (i = 0; i < sc->num_permitted_opens; i++) {
+		fp = &sc->permitted_opens[i];
+		if (open_listen_match_streamlocal(fp, path))
 			break;
+		fp = NULL;
 	}
-	if (i >= num_permitted_opens) {
+	if (fp == NULL) {
 		debug("%s: requested forward not found", __func__);
 		return -1;
 	}
-	packet_start(SSH2_MSG_GLOBAL_REQUEST);
-	packet_put_cstring("cancel-streamlocal-forward@openssh.com");
-	packet_put_char(0);
-	packet_put_cstring(path);
-	packet_send();
+	if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 ||
+	    (r = sshpkt_put_cstring(ssh,
+	    "cancel-streamlocal-forward@openssh.com")) != 0 ||
+	    (r = sshpkt_put_u8(ssh, 0)) != 0 || /* want reply */
+	    (r = sshpkt_put_cstring(ssh, path)) != 0 ||
+	    (r = sshpkt_send(ssh)) != 0)
+		fatal("%s: send cancel: %s", __func__, ssh_err(r));
 
-	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;
-	permitted_opens[i].listen_host = NULL;
-	free(permitted_opens[i].listen_path);
-	permitted_opens[i].listen_path = NULL;
-	permitted_opens[i].downstream = NULL;
+	fwd_perm_clear(fp); /* unregister */
 
 	return 0;
 }
@@ -3432,14 +3735,15 @@
  * Request cancellation of remote forwarding of a connection from local side.
  */
 int
-channel_request_rforward_cancel(struct Forward *fwd)
+channel_request_rforward_cancel(struct ssh *ssh, struct Forward *fwd)
 {
 	if (fwd->listen_path != NULL) {
-		return (channel_request_rforward_cancel_streamlocal(
-		    fwd->listen_path));
+		return channel_request_rforward_cancel_streamlocal(ssh,
+		    fwd->listen_path);
 	} else {
-		return (channel_request_rforward_cancel_tcpip(fwd->listen_host,
-		    fwd->listen_port ? fwd->listen_port : fwd->allocated_port));
+		return channel_request_rforward_cancel_tcpip(ssh,
+		    fwd->listen_host,
+		    fwd->listen_port ? fwd->listen_port : fwd->allocated_port);
 	}
 }
 
@@ -3449,28 +3753,20 @@
  * anyway, and the server has no way to know but to trust the client anyway.
  */
 void
-channel_permit_all_opens(void)
+channel_permit_all_opens(struct ssh *ssh)
 {
-	if (num_permitted_opens == 0)
-		all_opens_permitted = 1;
+	if (ssh->chanctxt->num_permitted_opens == 0)
+		ssh->chanctxt->all_opens_permitted = 1;
 }
 
 void
-channel_add_permitted_opens(char *host, int port)
+channel_add_permitted_opens(struct ssh *ssh, char *host, int port)
 {
+	struct ssh_channels *sc = ssh->chanctxt;
+
 	debug("allow port forwarding to host %s port %d", host, port);
-
-	permitted_opens = xreallocarray(permitted_opens,
-	    num_permitted_opens + 1, sizeof(*permitted_opens));
-	permitted_opens[num_permitted_opens].host_to_connect = xstrdup(host);
-	permitted_opens[num_permitted_opens].port_to_connect = port;
-	permitted_opens[num_permitted_opens].listen_host = NULL;
-	permitted_opens[num_permitted_opens].listen_path = NULL;
-	permitted_opens[num_permitted_opens].listen_port = 0;
-	permitted_opens[num_permitted_opens].downstream = NULL;
-	num_permitted_opens++;
-
-	all_opens_permitted = 0;
+	fwd_perm_list_add(ssh, FWDPERM_USER, host, port, NULL, NULL, 0, NULL);
+	sc->all_opens_permitted = 0;
 }
 
 /*
@@ -3479,105 +3775,61 @@
  * passed then they entry will be invalidated.
  */
 void
-channel_update_permitted_opens(int idx, int newport)
+channel_update_permitted_opens(struct ssh *ssh, int idx, int newport)
 {
-	if (idx < 0 || idx >= num_permitted_opens) {
-		debug("channel_update_permitted_opens: index out of range:"
-		    " %d num_permitted_opens %d", idx, num_permitted_opens);
+	struct ssh_channels *sc = ssh->chanctxt;
+
+	if (idx < 0 || (u_int)idx >= sc->num_permitted_opens) {
+		debug("%s: index out of range: %d num_permitted_opens %d",
+		    __func__, idx, sc->num_permitted_opens);
 		return;
 	}
 	debug("%s allowed port %d for forwarding to host %s port %d",
 	    newport > 0 ? "Updating" : "Removing",
 	    newport,
-	    permitted_opens[idx].host_to_connect,
-	    permitted_opens[idx].port_to_connect);
-	if (newport >= 0)  {
-		permitted_opens[idx].listen_port = 
+	    sc->permitted_opens[idx].host_to_connect,
+	    sc->permitted_opens[idx].port_to_connect);
+	if (newport <= 0)
+		fwd_perm_clear(&sc->permitted_opens[idx]);
+	else {
+		sc->permitted_opens[idx].listen_port =
 		    (datafellows & SSH_BUG_DYNAMIC_RPORT) ? 0 : newport;
-	} else {
-		permitted_opens[idx].listen_port = 0;
-		permitted_opens[idx].port_to_connect = 0;
-		free(permitted_opens[idx].host_to_connect);
-		permitted_opens[idx].host_to_connect = NULL;
-		free(permitted_opens[idx].listen_host);
-		permitted_opens[idx].listen_host = NULL;
-		free(permitted_opens[idx].listen_path);
-		permitted_opens[idx].listen_path = NULL;
 	}
 }
 
 int
-channel_add_adm_permitted_opens(char *host, int port)
+channel_add_adm_permitted_opens(struct ssh *ssh, char *host, int port)
 {
 	debug("config allows port forwarding to host %s port %d", host, port);
-
-	permitted_adm_opens = xreallocarray(permitted_adm_opens,
-	    num_adm_permitted_opens + 1, sizeof(*permitted_adm_opens));
-	permitted_adm_opens[num_adm_permitted_opens].host_to_connect
-	     = xstrdup(host);
-	permitted_adm_opens[num_adm_permitted_opens].port_to_connect = port;
-	permitted_adm_opens[num_adm_permitted_opens].listen_host = NULL;
-	permitted_adm_opens[num_adm_permitted_opens].listen_path = NULL;
-	permitted_adm_opens[num_adm_permitted_opens].listen_port = 0;
-	return ++num_adm_permitted_opens;
+	return fwd_perm_list_add(ssh, FWDPERM_ADMIN, host, port,
+	    NULL, NULL, 0, NULL);
 }
 
 void
-channel_disable_adm_local_opens(void)
+channel_disable_adm_local_opens(struct ssh *ssh)
 {
-	channel_clear_adm_permitted_opens();
-	permitted_adm_opens = xcalloc(sizeof(*permitted_adm_opens), 1);
-	permitted_adm_opens[num_adm_permitted_opens].host_to_connect = NULL;
-	num_adm_permitted_opens = 1;
+	channel_clear_adm_permitted_opens(ssh);
+	fwd_perm_list_add(ssh, FWDPERM_ADMIN, NULL, 0, NULL, NULL, 0, NULL);
 }
 
 void
-channel_clear_permitted_opens(void)
+channel_clear_permitted_opens(struct ssh *ssh)
 {
-	int i;
+	struct ssh_channels *sc = ssh->chanctxt;
 
-	for (i = 0; i < num_permitted_opens; i++) {
-		free(permitted_opens[i].host_to_connect);
-		free(permitted_opens[i].listen_host);
-		free(permitted_opens[i].listen_path);
-	}
-	free(permitted_opens);
-	permitted_opens = NULL;
-	num_permitted_opens = 0;
+	sc->permitted_opens = xrecallocarray(sc->permitted_opens,
+	    sc->num_permitted_opens, 0, sizeof(*sc->permitted_opens));
+	sc->num_permitted_opens = 0;
 }
 
 void
-channel_clear_adm_permitted_opens(void)
+channel_clear_adm_permitted_opens(struct ssh *ssh)
 {
-	int i;
+	struct ssh_channels *sc = ssh->chanctxt;
 
-	for (i = 0; i < num_adm_permitted_opens; i++) {
-		free(permitted_adm_opens[i].host_to_connect);
-		free(permitted_adm_opens[i].listen_host);
-		free(permitted_adm_opens[i].listen_path);
-	}
-	free(permitted_adm_opens);
-	permitted_adm_opens = NULL;
-	num_adm_permitted_opens = 0;
-}
-
-void
-channel_print_adm_permitted_opens(void)
-{
-	int i;
-
-	printf("permitopen");
-	if (num_adm_permitted_opens == 0) {
-		printf(" any\n");
-		return;
-	}
-	for (i = 0; i < num_adm_permitted_opens; i++)
-		if (permitted_adm_opens[i].host_to_connect == NULL)
-			printf(" none");
-		else
-			printf(" %s:%d", permitted_adm_opens[i].host_to_connect,
-			    permitted_adm_opens[i].port_to_connect);
-	printf("\n");
+	sc->permitted_adm_opens = xrecallocarray(sc->permitted_adm_opens,
+	    sc->num_adm_permitted_opens, 0, sizeof(*sc->permitted_adm_opens));
+	sc->num_adm_permitted_opens = 0;
 }
 
 /* returns port number, FWD_PERMIT_ANY_PORT or -1 on error */
@@ -3599,7 +3851,8 @@
 {
 	int sock, saved_errno;
 	struct sockaddr_un *sunaddr;
-	char ntop[NI_MAXHOST], strport[MAXIMUM(NI_MAXSERV,sizeof(sunaddr->sun_path))];
+	char ntop[NI_MAXHOST];
+	char strport[MAXIMUM(NI_MAXSERV, sizeof(sunaddr->sun_path))];
 
 	for (; cctx->ai; cctx->ai = cctx->ai->ai_next) {
 		switch (cctx->ai->ai_family) {
@@ -3669,8 +3922,8 @@
  * passing back the failure reason if appropriate.
  */
 static Channel *
-connect_to_reason(const char *name, int port, char *ctype, char *rname,
-     int *reason, const char **errmsg)
+connect_to_reason(struct ssh *ssh, const char *name, int port,
+    char *ctype, char *rname, int *reason, const char **errmsg)
 {
 	struct addrinfo hints;
 	int gaierr;
@@ -3708,7 +3961,7 @@
 		cctx.aitop = ai;
 	} else {
 		memset(&hints, 0, sizeof(hints));
-		hints.ai_family = IPv4or6;
+		hints.ai_family = ssh->chanctxt->IPv4or6;
 		hints.ai_socktype = SOCK_STREAM;
 		snprintf(strport, sizeof strport, "%d", port);
 		if ((gaierr = getaddrinfo(name, strport, &hints, &cctx.aitop))
@@ -3733,7 +3986,7 @@
 		channel_connect_ctx_free(&cctx);
 		return NULL;
 	}
-	c = channel_new(ctype, SSH_CHANNEL_CONNECTING, sock, sock, -1,
+	c = channel_new(ssh, ctype, SSH_CHANNEL_CONNECTING, sock, sock, -1,
 	    CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, rname, 1);
 	c->connect_ctx = cctx;
 	return c;
@@ -3741,9 +3994,10 @@
 
 /* Return CONNECTING channel to remote host:port or local socket path */
 static Channel *
-connect_to(const char *name, int port, char *ctype, char *rname)
+connect_to(struct ssh *ssh, const char *name, int port,
+    char *ctype, char *rname)
 {
-	return connect_to_reason(name, port, ctype, rname, NULL, NULL);
+	return connect_to_reason(ssh, name, port, ctype, rname, NULL, NULL);
 }
 
 /*
@@ -3751,19 +4005,21 @@
  * that needs to deal with this connection.
  */
 Channel *
-channel_connect_by_listen_address(const char *listen_host,
+channel_connect_by_listen_address(struct ssh *ssh, const char *listen_host,
     u_short listen_port, char *ctype, char *rname)
 {
-	int i;
+	struct ssh_channels *sc = ssh->chanctxt;
+	u_int i;
+	ForwardPermission *fp;
 
-	for (i = 0; i < num_permitted_opens; i++) {
-		if (open_listen_match_tcpip(&permitted_opens[i], listen_host,
-		    listen_port, 1)) {
-			if (permitted_opens[i].downstream)
-				return permitted_opens[i].downstream;
-			return connect_to(
-			    permitted_opens[i].host_to_connect,
-			    permitted_opens[i].port_to_connect, ctype, rname);
+	for (i = 0; i < sc->num_permitted_opens; i++) {
+		fp = &sc->permitted_opens[i];
+		if (open_listen_match_tcpip(fp, listen_host, listen_port, 1)) {
+			if (fp->downstream)
+				return fp->downstream;
+			return connect_to(ssh,
+			    fp->host_to_connect, fp->port_to_connect,
+			    ctype, rname);
 		}
 	}
 	error("WARNING: Server requests forwarding for unknown listen_port %d",
@@ -3772,15 +4028,19 @@
 }
 
 Channel *
-channel_connect_by_listen_path(const char *path, char *ctype, char *rname)
+channel_connect_by_listen_path(struct ssh *ssh, const char *path,
+    char *ctype, char *rname)
 {
-	int i;
+	struct ssh_channels *sc = ssh->chanctxt;
+	u_int i;
+	ForwardPermission *fp;
 
-	for (i = 0; i < num_permitted_opens; i++) {
-		if (open_listen_match_streamlocal(&permitted_opens[i], path)) {
-			return connect_to(
-			    permitted_opens[i].host_to_connect,
-			    permitted_opens[i].port_to_connect, ctype, rname);
+	for (i = 0; i < sc->num_permitted_opens; i++) {
+		fp = &sc->permitted_opens[i];
+		if (open_listen_match_streamlocal(fp, path)) {
+			return connect_to(ssh,
+			    fp->host_to_connect, fp->port_to_connect,
+			    ctype, rname);
 		}
 	}
 	error("WARNING: Server requests forwarding for unknown path %.100s",
@@ -3790,27 +4050,33 @@
 
 /* Check if connecting to that port is permitted and connect. */
 Channel *
-channel_connect_to_port(const char *host, u_short port, char *ctype,
-    char *rname, int *reason, const char **errmsg)
+channel_connect_to_port(struct ssh *ssh, const char *host, u_short port,
+    char *ctype, char *rname, int *reason, const char **errmsg)
 {
-	int i, permit, permit_adm = 1;
+	struct ssh_channels *sc = ssh->chanctxt;
+	u_int i, permit, permit_adm = 1;
+	ForwardPermission *fp;
 
-	permit = all_opens_permitted;
+	permit = sc->all_opens_permitted;
 	if (!permit) {
-		for (i = 0; i < num_permitted_opens; i++)
-			if (open_match(&permitted_opens[i], host, port)) {
+		for (i = 0; i < sc->num_permitted_opens; i++) {
+			fp = &sc->permitted_opens[i];
+			if (open_match(fp, host, port)) {
 				permit = 1;
 				break;
 			}
+		}
 	}
 
-	if (num_adm_permitted_opens > 0) {
+	if (sc->num_adm_permitted_opens > 0) {
 		permit_adm = 0;
-		for (i = 0; i < num_adm_permitted_opens; i++)
-			if (open_match(&permitted_adm_opens[i], host, port)) {
+		for (i = 0; i < sc->num_adm_permitted_opens; i++) {
+			fp = &sc->permitted_adm_opens[i];
+			if (open_match(fp, host, port)) {
 				permit_adm = 1;
 				break;
 			}
+		}
 	}
 
 	if (!permit || !permit_adm) {
@@ -3820,31 +4086,38 @@
 			*reason = SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED;
 		return NULL;
 	}
-	return connect_to_reason(host, port, ctype, rname, reason, errmsg);
+	return connect_to_reason(ssh, host, port, ctype, rname, reason, errmsg);
 }
 
 /* Check if connecting to that path is permitted and connect. */
 Channel *
-channel_connect_to_path(const char *path, char *ctype, char *rname)
+channel_connect_to_path(struct ssh *ssh, const char *path,
+    char *ctype, char *rname)
 {
-	int i, permit, permit_adm = 1;
+	struct ssh_channels *sc = ssh->chanctxt;
+	u_int i, permit, permit_adm = 1;
+	ForwardPermission *fp;
 
-	permit = all_opens_permitted;
+	permit = sc->all_opens_permitted;
 	if (!permit) {
-		for (i = 0; i < num_permitted_opens; i++)
-			if (open_match(&permitted_opens[i], path, PORT_STREAMLOCAL)) {
+		for (i = 0; i < sc->num_permitted_opens; i++) {
+			fp = &sc->permitted_opens[i];
+			if (open_match(fp, path, PORT_STREAMLOCAL)) {
 				permit = 1;
 				break;
 			}
+		}
 	}
 
-	if (num_adm_permitted_opens > 0) {
+	if (sc->num_adm_permitted_opens > 0) {
 		permit_adm = 0;
-		for (i = 0; i < num_adm_permitted_opens; i++)
-			if (open_match(&permitted_adm_opens[i], path, PORT_STREAMLOCAL)) {
+		for (i = 0; i < sc->num_adm_permitted_opens; i++) {
+			fp = &sc->permitted_adm_opens[i];
+			if (open_match(fp, path, PORT_STREAMLOCAL)) {
 				permit_adm = 1;
 				break;
 			}
+		}
 	}
 
 	if (!permit || !permit_adm) {
@@ -3852,27 +4125,31 @@
 		    "but the request was denied.", path);
 		return NULL;
 	}
-	return connect_to(path, PORT_STREAMLOCAL, ctype, rname);
+	return connect_to(ssh, path, PORT_STREAMLOCAL, ctype, rname);
 }
 
 void
-channel_send_window_changes(void)
+channel_send_window_changes(struct ssh *ssh)
 {
-	u_int i;
+	struct ssh_channels *sc = ssh->chanctxt;
 	struct winsize ws;
+	int r;
+	u_int i;
 
-	for (i = 0; i < channels_alloc; i++) {
-		if (channels[i] == NULL || !channels[i]->client_tty ||
-		    channels[i]->type != SSH_CHANNEL_OPEN)
+	for (i = 0; i < sc->channels_alloc; i++) {
+		if (sc->channels[i] == NULL || !sc->channels[i]->client_tty ||
+		    sc->channels[i]->type != SSH_CHANNEL_OPEN)
 			continue;
-		if (ioctl(channels[i]->rfd, TIOCGWINSZ, &ws) < 0)
+		if (ioctl(sc->channels[i]->rfd, TIOCGWINSZ, &ws) < 0)
 			continue;
-		channel_request_start(i, "window-change", 0);
-		packet_put_int((u_int)ws.ws_col);
-		packet_put_int((u_int)ws.ws_row);
-		packet_put_int((u_int)ws.ws_xpixel);
-		packet_put_int((u_int)ws.ws_ypixel);
-		packet_send();
+		channel_request_start(ssh, i, "window-change", 0);
+		if ((r = sshpkt_put_u32(ssh, (u_int)ws.ws_col)) != 0 ||
+		    (r = sshpkt_put_u32(ssh, (u_int)ws.ws_row)) != 0 ||
+		    (r = sshpkt_put_u32(ssh, (u_int)ws.ws_xpixel)) != 0 ||
+		    (r = sshpkt_put_u32(ssh, (u_int)ws.ws_ypixel)) != 0 ||
+		    (r = sshpkt_send(ssh)) != 0)
+			fatal("%s: channel %u: send window-change: %s",
+			    __func__, i, ssh_err(r));
 	}
 }
 
@@ -3884,8 +4161,9 @@
  * stored in display_numberp , or -1 if an error occurs.
  */
 int
-x11_create_display_inet(int x11_display_offset, int x11_use_localhost,
-    int single_connection, u_int *display_numberp, int **chanids)
+x11_create_display_inet(struct ssh *ssh, int x11_display_offset,
+    int x11_use_localhost, int single_connection,
+    u_int *display_numberp, int **chanids)
 {
 	Channel *nc = NULL;
 	int display_number, sock;
@@ -3902,16 +4180,18 @@
 	    display_number++) {
 		port = 6000 + display_number;
 		memset(&hints, 0, sizeof(hints));
-		hints.ai_family = IPv4or6;
+		hints.ai_family = ssh->chanctxt->IPv4or6;
 		hints.ai_flags = x11_use_localhost ? 0: AI_PASSIVE;
 		hints.ai_socktype = SOCK_STREAM;
 		snprintf(strport, sizeof strport, "%d", port);
-		if ((gaierr = getaddrinfo(NULL, strport, &hints, &aitop)) != 0) {
+		if ((gaierr = getaddrinfo(NULL, strport,
+		    &hints, &aitop)) != 0) {
 			error("getaddrinfo: %.100s", ssh_gai_strerror(gaierr));
 			return -1;
 		}
 		for (ai = aitop; ai; ai = ai->ai_next) {
-			if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6)
+			if (ai->ai_family != AF_INET &&
+			    ai->ai_family != AF_INET6)
 				continue;
 			sock = socket(ai->ai_family, ai->ai_socktype,
 			    ai->ai_protocol);
@@ -3935,12 +4215,11 @@
 			if (x11_use_localhost)
 				channel_set_reuseaddr(sock);
 			if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) {
-				debug2("bind port %d: %.100s", port, strerror(errno));
+				debug2("%s: bind port %d: %.100s", __func__,
+				    port, strerror(errno));
 				close(sock);
-
-				for (n = 0; n < num_socks; n++) {
+				for (n = 0; n < num_socks; n++)
 					close(socks[n]);
-				}
 				num_socks = 0;
 				break;
 			}
@@ -3970,7 +4249,7 @@
 	*chanids = xcalloc(num_socks + 1, sizeof(**chanids));
 	for (n = 0; n < num_socks; n++) {
 		sock = socks[n];
-		nc = channel_new("x11 listener",
+		nc = channel_new(ssh, "x11 listener",
 		    SSH_CHANNEL_X11_LISTENER, sock, sock, -1,
 		    CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT,
 		    0, "X11 inet listener", 1);
@@ -3981,7 +4260,7 @@
 
 	/* Return the display number for the DISPLAY environment variable. */
 	*display_numberp = display_number;
-	return (0);
+	return 0;
 }
 
 static int
@@ -4039,7 +4318,7 @@
 #endif
 
 int
-x11_connect_display(void)
+x11_connect_display(struct ssh *ssh)
 {
 	u_int display_number;
 	const char *display;
@@ -4084,9 +4363,10 @@
 	if (strncmp(display, "unix:", 5) == 0 ||
 	    display[0] == ':') {
 		/* Connect to the unix domain socket. */
-		if (sscanf(strrchr(display, ':') + 1, "%u", &display_number) != 1) {
-			error("Could not parse display number from DISPLAY: %.100s",
-			    display);
+		if (sscanf(strrchr(display, ':') + 1, "%u",
+		    &display_number) != 1) {
+			error("Could not parse display number from DISPLAY: "
+			    "%.100s", display);
 			return -1;
 		}
 		/* Create a socket. */
@@ -4108,7 +4388,10 @@
 		return -1;
 	}
 	*cp = 0;
-	/* buf now contains the host name.  But first we parse the display number. */
+	/*
+	 * buf now contains the host name.  But first we parse the
+	 * display number.
+	 */
 	if (sscanf(cp + 1, "%u", &display_number) != 1) {
 		error("Could not parse display number from DISPLAY: %.100s",
 		    display);
@@ -4117,7 +4400,7 @@
 
 	/* Look up the host address */
 	memset(&hints, 0, sizeof(hints));
-	hints.ai_family = IPv4or6;
+	hints.ai_family = ssh->chanctxt->IPv4or6;
 	hints.ai_socktype = SOCK_STREAM;
 	snprintf(strport, sizeof strport, "%u", 6000 + display_number);
 	if ((gaierr = getaddrinfo(buf, strport, &hints, &aitop)) != 0) {
@@ -4144,8 +4427,8 @@
 	}
 	freeaddrinfo(aitop);
 	if (!ai) {
-		error("connect %.100s port %u: %.100s", buf, 6000 + display_number,
-		    strerror(errno));
+		error("connect %.100s port %u: %.100s", buf,
+		    6000 + display_number, strerror(errno));
 		return -1;
 	}
 	set_nodelay(sock);
@@ -4158,18 +4441,19 @@
  * This should be called in the client only.
  */
 void
-x11_request_forwarding_with_spoofing(int client_session_id, const char *disp,
-    const char *proto, const char *data, int want_reply)
+x11_request_forwarding_with_spoofing(struct ssh *ssh, int client_session_id,
+    const char *disp, const char *proto, const char *data, int want_reply)
 {
+	struct ssh_channels *sc = ssh->chanctxt;
 	u_int data_len = (u_int) strlen(data) / 2;
 	u_int i, value;
-	char *new_data;
-	int screen_number;
 	const char *cp;
+	char *new_data;
+	int r, screen_number;
 
-	if (x11_saved_display == NULL)
-		x11_saved_display = xstrdup(disp);
-	else if (strcmp(disp, x11_saved_display) != 0) {
+	if (sc->x11_saved_display == NULL)
+		sc->x11_saved_display = xstrdup(disp);
+	else if (strcmp(disp, sc->x11_saved_display) != 0) {
 		error("x11_request_forwarding_with_spoofing: different "
 		    "$DISPLAY already forwarded");
 		return;
@@ -4183,36 +4467,37 @@
 	else
 		screen_number = 0;
 
-	if (x11_saved_proto == NULL) {
+	if (sc->x11_saved_proto == NULL) {
 		/* Save protocol name. */
-		x11_saved_proto = xstrdup(proto);
+		sc->x11_saved_proto = xstrdup(proto);
 
 		/* Extract real authentication data. */
-		x11_saved_data = xmalloc(data_len);
+		sc->x11_saved_data = xmalloc(data_len);
 		for (i = 0; i < data_len; i++) {
 			if (sscanf(data + 2 * i, "%2x", &value) != 1)
 				fatal("x11_request_forwarding: bad "
 				    "authentication data: %.100s", data);
-			x11_saved_data[i] = value;
+			sc->x11_saved_data[i] = value;
 		}
-		x11_saved_data_len = data_len;
+		sc->x11_saved_data_len = data_len;
 
 		/* Generate fake data of the same length. */
-		x11_fake_data = xmalloc(data_len);
-		arc4random_buf(x11_fake_data, data_len);
-		x11_fake_data_len = data_len;
+		sc->x11_fake_data = xmalloc(data_len);
+		arc4random_buf(sc->x11_fake_data, data_len);
+		sc->x11_fake_data_len = data_len;
 	}
 
 	/* Convert the fake data into hex. */
-	new_data = tohex(x11_fake_data, data_len);
+	new_data = tohex(sc->x11_fake_data, data_len);
 
 	/* Send the request packet. */
-	channel_request_start(client_session_id, "x11-req", want_reply);
-	packet_put_char(0);	/* XXX bool single connection */
-	packet_put_cstring(proto);
-	packet_put_cstring(new_data);
-	packet_put_int(screen_number);
-	packet_send();
-	packet_write_wait();
+	channel_request_start(ssh, client_session_id, "x11-req", want_reply);
+	if ((r = sshpkt_put_u8(ssh, 0)) != 0 || /* bool: single connection */
+	    (r = sshpkt_put_cstring(ssh, proto)) != 0 ||
+	    (r = sshpkt_put_cstring(ssh, new_data)) != 0 ||
+	    (r = sshpkt_put_u32(ssh, screen_number)) != 0 ||
+	    (r = sshpkt_send(ssh)) != 0 ||
+	    (r = ssh_packet_write_wait(ssh)) != 0)
+		fatal("%s: send x11-req: %s", __func__, ssh_err(r));
 	free(new_data);
 }