xref: /freebsd/crypto/openssh/channels.c (revision 9fce8d4140370ca8bc03d6a5668fb64a040c00fc)
1*9fce8d41SEd Maste /* $OpenBSD: channels.c,v 1.416 2022/04/11 22:52:08 djm Exp $ */
2511b41d2SMark Murray /*
3511b41d2SMark Murray  * Author: Tatu Ylonen <ylo@cs.hut.fi>
4511b41d2SMark Murray  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
5511b41d2SMark Murray  *                    All rights reserved
6511b41d2SMark Murray  * This file contains functions for generic socket connection forwarding.
7511b41d2SMark Murray  * There is also code for initiating connection forwarding for X11 connections,
8511b41d2SMark Murray  * arbitrary tcp/ip connections, and the authentication agent connection.
9511b41d2SMark Murray  *
10b66f2d16SKris Kennaway  * As far as I am concerned, the code I have written for this software
11b66f2d16SKris Kennaway  * can be used freely for any purpose.  Any derived versions of this
12b66f2d16SKris Kennaway  * software must be clearly marked as such, and if the derived work is
13b66f2d16SKris Kennaway  * incompatible with the protocol description in the RFC file, it must be
14b66f2d16SKris Kennaway  * called by a name other than "ssh" or "Secure Shell".
15b66f2d16SKris Kennaway  *
16a04a10f8SKris Kennaway  * SSH2 support added by Markus Friedl.
17af12a3e7SDag-Erling Smørgrav  * Copyright (c) 1999, 2000, 2001, 2002 Markus Friedl.  All rights reserved.
18b66f2d16SKris Kennaway  * Copyright (c) 1999 Dug Song.  All rights reserved.
19b66f2d16SKris Kennaway  * Copyright (c) 1999 Theo de Raadt.  All rights reserved.
20b66f2d16SKris Kennaway  *
21b66f2d16SKris Kennaway  * Redistribution and use in source and binary forms, with or without
22b66f2d16SKris Kennaway  * modification, are permitted provided that the following conditions
23b66f2d16SKris Kennaway  * are met:
24b66f2d16SKris Kennaway  * 1. Redistributions of source code must retain the above copyright
25b66f2d16SKris Kennaway  *    notice, this list of conditions and the following disclaimer.
26b66f2d16SKris Kennaway  * 2. Redistributions in binary form must reproduce the above copyright
27b66f2d16SKris Kennaway  *    notice, this list of conditions and the following disclaimer in the
28b66f2d16SKris Kennaway  *    documentation and/or other materials provided with the distribution.
29b66f2d16SKris Kennaway  *
30b66f2d16SKris Kennaway  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
31b66f2d16SKris Kennaway  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
32b66f2d16SKris Kennaway  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
33b66f2d16SKris Kennaway  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
34b66f2d16SKris Kennaway  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
35b66f2d16SKris Kennaway  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
36b66f2d16SKris Kennaway  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
37b66f2d16SKris Kennaway  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
38b66f2d16SKris Kennaway  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
39b66f2d16SKris Kennaway  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40511b41d2SMark Murray  */
41511b41d2SMark Murray 
42511b41d2SMark Murray #include "includes.h"
43a04a10f8SKris Kennaway 
44333ee039SDag-Erling Smørgrav #include <sys/types.h>
45a0ee8cc6SDag-Erling Smørgrav #include <sys/stat.h>
46333ee039SDag-Erling Smørgrav #include <sys/ioctl.h>
47333ee039SDag-Erling Smørgrav #include <sys/un.h>
48333ee039SDag-Erling Smørgrav #include <sys/socket.h>
49333ee039SDag-Erling Smørgrav #ifdef HAVE_SYS_TIME_H
50333ee039SDag-Erling Smørgrav # include <sys/time.h>
51333ee039SDag-Erling Smørgrav #endif
52333ee039SDag-Erling Smørgrav 
53333ee039SDag-Erling Smørgrav #include <netinet/in.h>
54333ee039SDag-Erling Smørgrav #include <arpa/inet.h>
55333ee039SDag-Erling Smørgrav 
56333ee039SDag-Erling Smørgrav #include <errno.h>
57b15c8340SDag-Erling Smørgrav #include <fcntl.h>
584f52dfbbSDag-Erling Smørgrav #include <limits.h>
59333ee039SDag-Erling Smørgrav #include <netdb.h>
601323ec57SEd Maste #ifdef HAVE_POLL_H
611323ec57SEd Maste #include <poll.h>
621323ec57SEd Maste #endif
634f52dfbbSDag-Erling Smørgrav #include <stdarg.h>
64bc5531deSDag-Erling Smørgrav #ifdef HAVE_STDINT_H
65bc5531deSDag-Erling Smørgrav # include <stdint.h>
66bc5531deSDag-Erling Smørgrav #endif
67333ee039SDag-Erling Smørgrav #include <stdio.h>
68333ee039SDag-Erling Smørgrav #include <stdlib.h>
69333ee039SDag-Erling Smørgrav #include <string.h>
70333ee039SDag-Erling Smørgrav #include <termios.h>
71333ee039SDag-Erling Smørgrav #include <unistd.h>
72333ee039SDag-Erling Smørgrav 
73d4af9e69SDag-Erling Smørgrav #include "openbsd-compat/sys-queue.h"
74333ee039SDag-Erling Smørgrav #include "xmalloc.h"
75ca3176e7SBrian Feldman #include "ssh.h"
76ca3176e7SBrian Feldman #include "ssh2.h"
77ca86bcf2SDag-Erling Smørgrav #include "ssherr.h"
784f52dfbbSDag-Erling Smørgrav #include "sshbuf.h"
79ca3176e7SBrian Feldman #include "packet.h"
80ca3176e7SBrian Feldman #include "log.h"
81ca3176e7SBrian Feldman #include "misc.h"
82ca3176e7SBrian Feldman #include "channels.h"
83ca3176e7SBrian Feldman #include "compat.h"
84ca3176e7SBrian Feldman #include "canohost.h"
85190cef3dSDag-Erling Smørgrav #include "sshkey.h"
86b66f2d16SKris Kennaway #include "authfd.h"
87af12a3e7SDag-Erling Smørgrav #include "pathnames.h"
88190cef3dSDag-Erling Smørgrav #include "match.h"
89511b41d2SMark Murray 
901323ec57SEd Maste /* XXX remove once we're satisfied there's no lurking bugs */
911323ec57SEd Maste /* #define DEBUG_CHANNEL_POLL 1 */
921323ec57SEd Maste 
934f52dfbbSDag-Erling Smørgrav /* -- agent forwarding */
944f52dfbbSDag-Erling Smørgrav #define	NUM_SOCKS	10
95511b41d2SMark Murray 
96af12a3e7SDag-Erling Smørgrav /* -- tcp forwarding */
974f52dfbbSDag-Erling Smørgrav /* special-case port number meaning allow any port */
984f52dfbbSDag-Erling Smørgrav #define FWD_PERMIT_ANY_PORT	0
994f52dfbbSDag-Erling Smørgrav 
1004f52dfbbSDag-Erling Smørgrav /* special-case wildcard meaning allow any host */
1014f52dfbbSDag-Erling Smørgrav #define FWD_PERMIT_ANY_HOST	"*"
1024f52dfbbSDag-Erling Smørgrav 
1034f52dfbbSDag-Erling Smørgrav /* -- X11 forwarding */
1044f52dfbbSDag-Erling Smørgrav /* Maximum number of fake X11 displays to try. */
1054f52dfbbSDag-Erling Smørgrav #define MAX_DISPLAYS  1000
106511b41d2SMark Murray 
1071323ec57SEd Maste /* Per-channel callback for pre/post IO actions */
1081323ec57SEd Maste typedef void chan_fn(struct ssh *, Channel *c);
109190cef3dSDag-Erling Smørgrav 
110511b41d2SMark Murray /*
111511b41d2SMark Murray  * Data structure for storing which hosts are permitted for forward requests.
112511b41d2SMark Murray  * The local sides of any remote forwards are stored in this array to prevent
113511b41d2SMark Murray  * a corrupt remote server from accessing arbitrary TCP/IP ports on our local
114511b41d2SMark Murray  * network (which might be behind a firewall).
115511b41d2SMark Murray  */
116a0ee8cc6SDag-Erling Smørgrav /* XXX: streamlocal wants a path instead of host:port */
117a0ee8cc6SDag-Erling Smørgrav /*      Overload host_to_connect; we could just make this match Forward */
118a0ee8cc6SDag-Erling Smørgrav /*	XXX - can we use listen_host instead of listen_path? */
119190cef3dSDag-Erling Smørgrav struct permission {
120a04a10f8SKris Kennaway 	char *host_to_connect;		/* Connect to 'host'. */
121a0ee8cc6SDag-Erling Smørgrav 	int port_to_connect;		/* Connect to 'port'. */
122a0ee8cc6SDag-Erling Smørgrav 	char *listen_host;		/* Remote side should listen address. */
123a0ee8cc6SDag-Erling Smørgrav 	char *listen_path;		/* Remote side should listen path. */
124a0ee8cc6SDag-Erling Smørgrav 	int listen_port;		/* Remote side should listen port. */
125ca86bcf2SDag-Erling Smørgrav 	Channel *downstream;		/* Downstream mux*/
126190cef3dSDag-Erling Smørgrav };
127511b41d2SMark Murray 
128190cef3dSDag-Erling Smørgrav /*
129190cef3dSDag-Erling Smørgrav  * Stores the forwarding permission state for a single direction (local or
130190cef3dSDag-Erling Smørgrav  * remote).
131190cef3dSDag-Erling Smørgrav  */
132190cef3dSDag-Erling Smørgrav struct permission_set {
133190cef3dSDag-Erling Smørgrav 	/*
134190cef3dSDag-Erling Smørgrav 	 * List of all local permitted host/port pairs to allow for the
135190cef3dSDag-Erling Smørgrav 	 * user.
136190cef3dSDag-Erling Smørgrav 	 */
137190cef3dSDag-Erling Smørgrav 	u_int num_permitted_user;
138190cef3dSDag-Erling Smørgrav 	struct permission *permitted_user;
139190cef3dSDag-Erling Smørgrav 
140190cef3dSDag-Erling Smørgrav 	/*
141190cef3dSDag-Erling Smørgrav 	 * List of all permitted host/port pairs to allow for the admin.
142190cef3dSDag-Erling Smørgrav 	 */
143190cef3dSDag-Erling Smørgrav 	u_int num_permitted_admin;
144190cef3dSDag-Erling Smørgrav 	struct permission *permitted_admin;
145190cef3dSDag-Erling Smørgrav 
146190cef3dSDag-Erling Smørgrav 	/*
147190cef3dSDag-Erling Smørgrav 	 * If this is true, all opens/listens are permitted.  This is the
148190cef3dSDag-Erling Smørgrav 	 * case on the server on which we have to trust the client anyway,
149190cef3dSDag-Erling Smørgrav 	 * and the user could do anything after logging in.
150190cef3dSDag-Erling Smørgrav 	 */
151190cef3dSDag-Erling Smørgrav 	int all_permitted;
152190cef3dSDag-Erling Smørgrav };
153af12a3e7SDag-Erling Smørgrav 
1544f52dfbbSDag-Erling Smørgrav /* Master structure for channels state */
1554f52dfbbSDag-Erling Smørgrav struct ssh_channels {
1564f52dfbbSDag-Erling Smørgrav 	/*
1574f52dfbbSDag-Erling Smørgrav 	 * Pointer to an array containing all allocated channels.  The array
1584f52dfbbSDag-Erling Smørgrav 	 * is dynamically extended as needed.
1594f52dfbbSDag-Erling Smørgrav 	 */
1604f52dfbbSDag-Erling Smørgrav 	Channel **channels;
161076ad2f8SDag-Erling Smørgrav 
162511b41d2SMark Murray 	/*
1634f52dfbbSDag-Erling Smørgrav 	 * Size of the channel array.  All slots of the array must always be
1644f52dfbbSDag-Erling Smørgrav 	 * initialized (at least the type field); unused slots set to NULL
165511b41d2SMark Murray 	 */
1664f52dfbbSDag-Erling Smørgrav 	u_int channels_alloc;
167511b41d2SMark Murray 
1684f52dfbbSDag-Erling Smørgrav 	/*
1691323ec57SEd Maste 	 * 'channel_pre*' are called just before IO to add any bits
1701323ec57SEd Maste 	 * relevant to channels in the c->io_want bitmasks.
1714f52dfbbSDag-Erling Smørgrav 	 *
1724f52dfbbSDag-Erling Smørgrav 	 * 'channel_post*': perform any appropriate operations for
1731323ec57SEd Maste 	 * channels which have c->io_ready events pending.
1744f52dfbbSDag-Erling Smørgrav 	 */
1754f52dfbbSDag-Erling Smørgrav 	chan_fn **channel_pre;
1764f52dfbbSDag-Erling Smørgrav 	chan_fn **channel_post;
1774f52dfbbSDag-Erling Smørgrav 
1784f52dfbbSDag-Erling Smørgrav 	/* -- tcp forwarding */
179190cef3dSDag-Erling Smørgrav 	struct permission_set local_perms;
180190cef3dSDag-Erling Smørgrav 	struct permission_set remote_perms;
181af12a3e7SDag-Erling Smørgrav 
182af12a3e7SDag-Erling Smørgrav 	/* -- X11 forwarding */
183af12a3e7SDag-Erling Smørgrav 
184d4ecd108SDag-Erling Smørgrav 	/* Saved X11 local (client) display. */
1854f52dfbbSDag-Erling Smørgrav 	char *x11_saved_display;
186d4ecd108SDag-Erling Smørgrav 
187af12a3e7SDag-Erling Smørgrav 	/* Saved X11 authentication protocol name. */
1884f52dfbbSDag-Erling Smørgrav 	char *x11_saved_proto;
189af12a3e7SDag-Erling Smørgrav 
190af12a3e7SDag-Erling Smørgrav 	/* Saved X11 authentication data.  This is the real data. */
1914f52dfbbSDag-Erling Smørgrav 	char *x11_saved_data;
1924f52dfbbSDag-Erling Smørgrav 	u_int x11_saved_data_len;
193af12a3e7SDag-Erling Smørgrav 
194557f75e5SDag-Erling Smørgrav 	/* Deadline after which all X11 connections are refused */
1954f52dfbbSDag-Erling Smørgrav 	u_int x11_refuse_time;
196557f75e5SDag-Erling Smørgrav 
197af12a3e7SDag-Erling Smørgrav 	/*
1984f52dfbbSDag-Erling Smørgrav 	 * Fake X11 authentication data.  This is what the server will be
1994f52dfbbSDag-Erling Smørgrav 	 * sending us; we should replace any occurrences of this by the
2004f52dfbbSDag-Erling Smørgrav 	 * real data.
201af12a3e7SDag-Erling Smørgrav 	 */
2024f52dfbbSDag-Erling Smørgrav 	u_char *x11_fake_data;
2034f52dfbbSDag-Erling Smørgrav 	u_int x11_fake_data_len;
204af12a3e7SDag-Erling Smørgrav 
205ca3176e7SBrian Feldman 	/* AF_UNSPEC or AF_INET or AF_INET6 */
2064f52dfbbSDag-Erling Smørgrav 	int IPv4or6;
2074f52dfbbSDag-Erling Smørgrav };
208ca3176e7SBrian Feldman 
209af12a3e7SDag-Erling Smørgrav /* helper */
2104f52dfbbSDag-Erling Smørgrav static void port_open_helper(struct ssh *ssh, Channel *c, char *rtype);
211ca86bcf2SDag-Erling Smørgrav static const char *channel_rfwd_bind_host(const char *listen_host);
212ca3176e7SBrian Feldman 
213d4af9e69SDag-Erling Smørgrav /* non-blocking connect helpers */
214d4af9e69SDag-Erling Smørgrav static int connect_next(struct channel_connect *);
215d4af9e69SDag-Erling Smørgrav static void channel_connect_ctx_free(struct channel_connect *);
2164f52dfbbSDag-Erling Smørgrav static Channel *rdynamic_connect_prepare(struct ssh *, char *, char *);
2174f52dfbbSDag-Erling Smørgrav static int rdynamic_connect_finish(struct ssh *, Channel *);
2184f52dfbbSDag-Erling Smørgrav 
2194f52dfbbSDag-Erling Smørgrav /* Setup helper */
2204f52dfbbSDag-Erling Smørgrav static void channel_handler_init(struct ssh_channels *sc);
221d4af9e69SDag-Erling Smørgrav 
222af12a3e7SDag-Erling Smørgrav /* -- channel core */
223a04a10f8SKris Kennaway 
2244f52dfbbSDag-Erling Smørgrav void
2254f52dfbbSDag-Erling Smørgrav channel_init_channels(struct ssh *ssh)
2264f52dfbbSDag-Erling Smørgrav {
2274f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc;
2284f52dfbbSDag-Erling Smørgrav 
22919261079SEd Maste 	if ((sc = calloc(1, sizeof(*sc))) == NULL)
23019261079SEd Maste 		fatal_f("allocation failed");
2314f52dfbbSDag-Erling Smørgrav 	sc->channels_alloc = 10;
2324f52dfbbSDag-Erling Smørgrav 	sc->channels = xcalloc(sc->channels_alloc, sizeof(*sc->channels));
2334f52dfbbSDag-Erling Smørgrav 	sc->IPv4or6 = AF_UNSPEC;
2344f52dfbbSDag-Erling Smørgrav 	channel_handler_init(sc);
2354f52dfbbSDag-Erling Smørgrav 
2364f52dfbbSDag-Erling Smørgrav 	ssh->chanctxt = sc;
2374f52dfbbSDag-Erling Smørgrav }
2384f52dfbbSDag-Erling Smørgrav 
239a04a10f8SKris Kennaway Channel *
2404f52dfbbSDag-Erling Smørgrav channel_by_id(struct ssh *ssh, int id)
241a04a10f8SKris Kennaway {
242a04a10f8SKris Kennaway 	Channel *c;
243af12a3e7SDag-Erling Smørgrav 
2444f52dfbbSDag-Erling Smørgrav 	if (id < 0 || (u_int)id >= ssh->chanctxt->channels_alloc) {
24519261079SEd Maste 		logit_f("%d: bad id", id);
246a04a10f8SKris Kennaway 		return NULL;
247a04a10f8SKris Kennaway 	}
2484f52dfbbSDag-Erling Smørgrav 	c = ssh->chanctxt->channels[id];
249af12a3e7SDag-Erling Smørgrav 	if (c == NULL) {
25019261079SEd Maste 		logit_f("%d: bad id: channel free", id);
251a04a10f8SKris Kennaway 		return NULL;
252a04a10f8SKris Kennaway 	}
253a04a10f8SKris Kennaway 	return c;
254a04a10f8SKris Kennaway }
255a04a10f8SKris Kennaway 
256ca86bcf2SDag-Erling Smørgrav Channel *
2574f52dfbbSDag-Erling Smørgrav channel_by_remote_id(struct ssh *ssh, u_int remote_id)
258ca86bcf2SDag-Erling Smørgrav {
259ca86bcf2SDag-Erling Smørgrav 	Channel *c;
260ca86bcf2SDag-Erling Smørgrav 	u_int i;
261ca86bcf2SDag-Erling Smørgrav 
2624f52dfbbSDag-Erling Smørgrav 	for (i = 0; i < ssh->chanctxt->channels_alloc; i++) {
2634f52dfbbSDag-Erling Smørgrav 		c = ssh->chanctxt->channels[i];
2644f52dfbbSDag-Erling Smørgrav 		if (c != NULL && c->have_remote_id && c->remote_id == remote_id)
265ca86bcf2SDag-Erling Smørgrav 			return c;
266ca86bcf2SDag-Erling Smørgrav 	}
267ca86bcf2SDag-Erling Smørgrav 	return NULL;
268ca86bcf2SDag-Erling Smørgrav }
269ca86bcf2SDag-Erling Smørgrav 
270a04a10f8SKris Kennaway /*
271b74df5b2SDag-Erling Smørgrav  * Returns the channel if it is allowed to receive protocol messages.
272b74df5b2SDag-Erling Smørgrav  * Private channels, like listening sockets, may not receive messages.
273b74df5b2SDag-Erling Smørgrav  */
274b74df5b2SDag-Erling Smørgrav Channel *
2754f52dfbbSDag-Erling Smørgrav channel_lookup(struct ssh *ssh, int id)
276b74df5b2SDag-Erling Smørgrav {
277b74df5b2SDag-Erling Smørgrav 	Channel *c;
278b74df5b2SDag-Erling Smørgrav 
2794f52dfbbSDag-Erling Smørgrav 	if ((c = channel_by_id(ssh, id)) == NULL)
2804f52dfbbSDag-Erling Smørgrav 		return NULL;
281b74df5b2SDag-Erling Smørgrav 
282b74df5b2SDag-Erling Smørgrav 	switch (c->type) {
283b74df5b2SDag-Erling Smørgrav 	case SSH_CHANNEL_X11_OPEN:
284b74df5b2SDag-Erling Smørgrav 	case SSH_CHANNEL_LARVAL:
285b74df5b2SDag-Erling Smørgrav 	case SSH_CHANNEL_CONNECTING:
286b74df5b2SDag-Erling Smørgrav 	case SSH_CHANNEL_DYNAMIC:
2874f52dfbbSDag-Erling Smørgrav 	case SSH_CHANNEL_RDYNAMIC_OPEN:
2884f52dfbbSDag-Erling Smørgrav 	case SSH_CHANNEL_RDYNAMIC_FINISH:
289b74df5b2SDag-Erling Smørgrav 	case SSH_CHANNEL_OPENING:
290b74df5b2SDag-Erling Smørgrav 	case SSH_CHANNEL_OPEN:
291e4a9863fSDag-Erling Smørgrav 	case SSH_CHANNEL_ABANDONED:
292ca86bcf2SDag-Erling Smørgrav 	case SSH_CHANNEL_MUX_PROXY:
2934f52dfbbSDag-Erling Smørgrav 		return c;
294b74df5b2SDag-Erling Smørgrav 	}
295b74df5b2SDag-Erling Smørgrav 	logit("Non-public channel %d, type %d.", id, c->type);
2964f52dfbbSDag-Erling Smørgrav 	return NULL;
297b74df5b2SDag-Erling Smørgrav }
298b74df5b2SDag-Erling Smørgrav 
299b74df5b2SDag-Erling Smørgrav /*
300a04a10f8SKris Kennaway  * Register filedescriptors for a channel, used when allocating a channel or
301a04a10f8SKris Kennaway  * when the channel consumer/producer is ready, e.g. shell exec'd
302a04a10f8SKris Kennaway  */
303af12a3e7SDag-Erling Smørgrav static void
3044f52dfbbSDag-Erling Smørgrav channel_register_fds(struct ssh *ssh, Channel *c, int rfd, int wfd, int efd,
305d4af9e69SDag-Erling Smørgrav     int extusage, int nonblock, int is_tty)
306a04a10f8SKris Kennaway {
307b15c8340SDag-Erling Smørgrav 	if (rfd != -1)
308b15c8340SDag-Erling Smørgrav 		fcntl(rfd, F_SETFD, FD_CLOEXEC);
309b15c8340SDag-Erling Smørgrav 	if (wfd != -1 && wfd != rfd)
310b15c8340SDag-Erling Smørgrav 		fcntl(wfd, F_SETFD, FD_CLOEXEC);
311b15c8340SDag-Erling Smørgrav 	if (efd != -1 && efd != rfd && efd != wfd)
312b15c8340SDag-Erling Smørgrav 		fcntl(efd, F_SETFD, FD_CLOEXEC);
313a04a10f8SKris Kennaway 
314a04a10f8SKris Kennaway 	c->rfd = rfd;
315a04a10f8SKris Kennaway 	c->wfd = wfd;
316a04a10f8SKris Kennaway 	c->sock = (rfd == wfd) ? rfd : -1;
317a04a10f8SKris Kennaway 	c->efd = efd;
318a04a10f8SKris Kennaway 	c->extended_usage = extusage;
3195b9b2fafSBrian Feldman 
320d4af9e69SDag-Erling Smørgrav 	if ((c->isatty = is_tty) != 0)
321221552e4SDag-Erling Smørgrav 		debug2("channel %d: rfd %d isatty", c->self, c->rfd);
322e4a9863fSDag-Erling Smørgrav #ifdef _AIX
323e4a9863fSDag-Erling Smørgrav 	/* XXX: Later AIX versions can't push as much data to tty */
324d4af9e69SDag-Erling Smørgrav 	c->wfd_isatty = is_tty || isatty(c->wfd);
325e4a9863fSDag-Erling Smørgrav #endif
326e0fbb1d2SBrian Feldman 
3275b9b2fafSBrian Feldman 	/* enable nonblocking mode */
32819261079SEd Maste 	c->restore_block = 0;
32919261079SEd Maste 	if (nonblock == CHANNEL_NONBLOCK_STDIO) {
33019261079SEd Maste 		/*
33119261079SEd Maste 		 * Special handling for stdio file descriptors: do not set
33219261079SEd Maste 		 * non-blocking mode if they are TTYs. Otherwise prepare to
33319261079SEd Maste 		 * restore their blocking state on exit to avoid interfering
33419261079SEd Maste 		 * with other programs that follow.
33519261079SEd Maste 		 */
33619261079SEd Maste 		if (rfd != -1 && !isatty(rfd) && fcntl(rfd, F_GETFL) == 0) {
33719261079SEd Maste 			c->restore_block |= CHANNEL_RESTORE_RFD;
33819261079SEd Maste 			set_nonblock(rfd);
33919261079SEd Maste 		}
34019261079SEd Maste 		if (wfd != -1 && !isatty(wfd) && fcntl(wfd, F_GETFL) == 0) {
34119261079SEd Maste 			c->restore_block |= CHANNEL_RESTORE_WFD;
34219261079SEd Maste 			set_nonblock(wfd);
34319261079SEd Maste 		}
34419261079SEd Maste 		if (efd != -1 && !isatty(efd) && fcntl(efd, F_GETFL) == 0) {
34519261079SEd Maste 			c->restore_block |= CHANNEL_RESTORE_EFD;
34619261079SEd Maste 			set_nonblock(efd);
34719261079SEd Maste 		}
34819261079SEd Maste 	} else if (nonblock) {
349a04a10f8SKris Kennaway 		if (rfd != -1)
350a04a10f8SKris Kennaway 			set_nonblock(rfd);
351a04a10f8SKris Kennaway 		if (wfd != -1)
352a04a10f8SKris Kennaway 			set_nonblock(wfd);
353a04a10f8SKris Kennaway 		if (efd != -1)
354a04a10f8SKris Kennaway 			set_nonblock(efd);
355a04a10f8SKris Kennaway 	}
3565b9b2fafSBrian Feldman }
357a04a10f8SKris Kennaway 
358511b41d2SMark Murray /*
359511b41d2SMark Murray  * Allocate a new channel object and set its type and socket. This will cause
360511b41d2SMark Murray  * remote_name to be freed.
361511b41d2SMark Murray  */
362af12a3e7SDag-Erling Smørgrav Channel *
3634f52dfbbSDag-Erling Smørgrav channel_new(struct ssh *ssh, char *ctype, int type, int rfd, int wfd, int efd,
364a82e551fSDag-Erling Smørgrav     u_int window, u_int maxpack, int extusage, char *remote_name, int nonblock)
365511b41d2SMark Murray {
3664f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
3674f52dfbbSDag-Erling Smørgrav 	u_int i, found;
368511b41d2SMark Murray 	Channel *c;
36919261079SEd Maste 	int r;
370511b41d2SMark Murray 
371511b41d2SMark Murray 	/* Try to find a free slot where to put the new channel. */
3724f52dfbbSDag-Erling Smørgrav 	for (i = 0; i < sc->channels_alloc; i++) {
3734f52dfbbSDag-Erling Smørgrav 		if (sc->channels[i] == NULL) {
374511b41d2SMark Murray 			/* Found a free slot. */
3754f52dfbbSDag-Erling Smørgrav 			found = i;
376511b41d2SMark Murray 			break;
377511b41d2SMark Murray 		}
3784f52dfbbSDag-Erling Smørgrav 	}
3794f52dfbbSDag-Erling Smørgrav 	if (i >= sc->channels_alloc) {
3804f52dfbbSDag-Erling Smørgrav 		/*
3814f52dfbbSDag-Erling Smørgrav 		 * There are no free slots. Take last+1 slot and expand
3824f52dfbbSDag-Erling Smørgrav 		 * the array.
3834f52dfbbSDag-Erling Smørgrav 		 */
3844f52dfbbSDag-Erling Smørgrav 		found = sc->channels_alloc;
3854f52dfbbSDag-Erling Smørgrav 		if (sc->channels_alloc > CHANNELS_MAX_CHANNELS)
38619261079SEd Maste 			fatal_f("internal error: channels_alloc %d too big",
38719261079SEd Maste 			    sc->channels_alloc);
3884f52dfbbSDag-Erling Smørgrav 		sc->channels = xrecallocarray(sc->channels, sc->channels_alloc,
3894f52dfbbSDag-Erling Smørgrav 		    sc->channels_alloc + 10, sizeof(*sc->channels));
3904f52dfbbSDag-Erling Smørgrav 		sc->channels_alloc += 10;
3914f52dfbbSDag-Erling Smørgrav 		debug2("channel: expanding %d", sc->channels_alloc);
392511b41d2SMark Murray 	}
393af12a3e7SDag-Erling Smørgrav 	/* Initialize and return new channel. */
3944f52dfbbSDag-Erling Smørgrav 	c = sc->channels[found] = xcalloc(1, sizeof(Channel));
3954f52dfbbSDag-Erling Smørgrav 	if ((c->input = sshbuf_new()) == NULL ||
3964f52dfbbSDag-Erling Smørgrav 	    (c->output = sshbuf_new()) == NULL ||
3974f52dfbbSDag-Erling Smørgrav 	    (c->extended = sshbuf_new()) == NULL)
39819261079SEd Maste 		fatal_f("sshbuf_new failed");
39919261079SEd Maste 	if ((r = sshbuf_set_max_size(c->input, CHAN_INPUT_MAX)) != 0)
40019261079SEd Maste 		fatal_fr(r, "sshbuf_set_max_size");
401af12a3e7SDag-Erling Smørgrav 	c->ostate = CHAN_OUTPUT_OPEN;
402af12a3e7SDag-Erling Smørgrav 	c->istate = CHAN_INPUT_OPEN;
4034f52dfbbSDag-Erling Smørgrav 	channel_register_fds(ssh, c, rfd, wfd, efd, extusage, nonblock, 0);
404511b41d2SMark Murray 	c->self = found;
405511b41d2SMark Murray 	c->type = type;
406a04a10f8SKris Kennaway 	c->ctype = ctype;
407a04a10f8SKris Kennaway 	c->local_window = window;
408a04a10f8SKris Kennaway 	c->local_window_max = window;
409a04a10f8SKris Kennaway 	c->local_maxpacket = maxpack;
410221552e4SDag-Erling Smørgrav 	c->remote_name = xstrdup(remote_name);
411b15c8340SDag-Erling Smørgrav 	c->ctl_chan = -1;
412b15c8340SDag-Erling Smørgrav 	c->delayed = 1;		/* prevent call to channel_post handler */
413d4af9e69SDag-Erling Smørgrav 	TAILQ_INIT(&c->status_confirms);
414511b41d2SMark Murray 	debug("channel %d: new [%s]", found, remote_name);
415af12a3e7SDag-Erling Smørgrav 	return c;
416a04a10f8SKris Kennaway }
417511b41d2SMark Murray 
418af12a3e7SDag-Erling Smørgrav int
41919261079SEd Maste channel_close_fd(struct ssh *ssh, Channel *c, int *fdp)
420af12a3e7SDag-Erling Smørgrav {
42119261079SEd Maste 	int ret, fd = *fdp;
422af12a3e7SDag-Erling Smørgrav 
42319261079SEd Maste 	if (fd == -1)
42419261079SEd Maste 		return 0;
42519261079SEd Maste 
42619261079SEd Maste 	if ((*fdp == c->rfd && (c->restore_block & CHANNEL_RESTORE_RFD) != 0) ||
42719261079SEd Maste 	   (*fdp == c->wfd && (c->restore_block & CHANNEL_RESTORE_WFD) != 0) ||
42819261079SEd Maste 	   (*fdp == c->efd && (c->restore_block & CHANNEL_RESTORE_EFD) != 0))
42919261079SEd Maste 		(void)fcntl(*fdp, F_SETFL, 0);	/* restore blocking */
43019261079SEd Maste 
4311323ec57SEd Maste 	if (*fdp == c->rfd) {
4321323ec57SEd Maste 		c->io_want &= ~SSH_CHAN_IO_RFD;
4331323ec57SEd Maste 		c->io_ready &= ~SSH_CHAN_IO_RFD;
4341323ec57SEd Maste 		c->rfd = -1;
43587c1498dSEd Maste 		c->pfds[0] = -1;
4361323ec57SEd Maste 	}
4371323ec57SEd Maste 	if (*fdp == c->wfd) {
4381323ec57SEd Maste 		c->io_want &= ~SSH_CHAN_IO_WFD;
4391323ec57SEd Maste 		c->io_ready &= ~SSH_CHAN_IO_WFD;
4401323ec57SEd Maste 		c->wfd = -1;
44187c1498dSEd Maste 		c->pfds[1] = -1;
4421323ec57SEd Maste 	}
4431323ec57SEd Maste 	if (*fdp == c->efd) {
4441323ec57SEd Maste 		c->io_want &= ~SSH_CHAN_IO_EFD;
4451323ec57SEd Maste 		c->io_ready &= ~SSH_CHAN_IO_EFD;
4461323ec57SEd Maste 		c->efd = -1;
44787c1498dSEd Maste 		c->pfds[2] = -1;
4481323ec57SEd Maste 	}
4491323ec57SEd Maste 	if (*fdp == c->sock) {
4501323ec57SEd Maste 		c->io_want &= ~SSH_CHAN_IO_SOCK;
4511323ec57SEd Maste 		c->io_ready &= ~SSH_CHAN_IO_SOCK;
4521323ec57SEd Maste 		c->sock = -1;
45387c1498dSEd Maste 		c->pfds[3] = -1;
4541323ec57SEd Maste 	}
4551323ec57SEd Maste 
456af12a3e7SDag-Erling Smørgrav 	ret = close(fd);
4571323ec57SEd Maste 	*fdp = -1; /* probably redundant */
458af12a3e7SDag-Erling Smørgrav 	return ret;
459af12a3e7SDag-Erling Smørgrav }
460a04a10f8SKris Kennaway 
461a04a10f8SKris Kennaway /* Close all channel fd/socket. */
462af12a3e7SDag-Erling Smørgrav static void
4634f52dfbbSDag-Erling Smørgrav channel_close_fds(struct ssh *ssh, Channel *c)
464511b41d2SMark Murray {
46547dd1d1bSDag-Erling Smørgrav 	int sock = c->sock, rfd = c->rfd, wfd = c->wfd, efd = c->efd;
46647dd1d1bSDag-Erling Smørgrav 
46719261079SEd Maste 	channel_close_fd(ssh, c, &c->sock);
46847dd1d1bSDag-Erling Smørgrav 	if (rfd != sock)
46919261079SEd Maste 		channel_close_fd(ssh, c, &c->rfd);
47047dd1d1bSDag-Erling Smørgrav 	if (wfd != sock && wfd != rfd)
47119261079SEd Maste 		channel_close_fd(ssh, c, &c->wfd);
47247dd1d1bSDag-Erling Smørgrav 	if (efd != sock && efd != rfd && efd != wfd)
47319261079SEd Maste 		channel_close_fd(ssh, c, &c->efd);
4744f52dfbbSDag-Erling Smørgrav }
4754f52dfbbSDag-Erling Smørgrav 
4764f52dfbbSDag-Erling Smørgrav static void
477190cef3dSDag-Erling Smørgrav fwd_perm_clear(struct permission *perm)
4784f52dfbbSDag-Erling Smørgrav {
479190cef3dSDag-Erling Smørgrav 	free(perm->host_to_connect);
480190cef3dSDag-Erling Smørgrav 	free(perm->listen_host);
481190cef3dSDag-Erling Smørgrav 	free(perm->listen_path);
48219261079SEd Maste 	memset(perm, 0, sizeof(*perm));
4834f52dfbbSDag-Erling Smørgrav }
4844f52dfbbSDag-Erling Smørgrav 
485190cef3dSDag-Erling Smørgrav /* Returns an printable name for the specified forwarding permission list */
486190cef3dSDag-Erling Smørgrav static const char *
487190cef3dSDag-Erling Smørgrav fwd_ident(int who, int where)
488190cef3dSDag-Erling Smørgrav {
489190cef3dSDag-Erling Smørgrav 	if (who == FORWARD_ADM) {
490190cef3dSDag-Erling Smørgrav 		if (where == FORWARD_LOCAL)
491190cef3dSDag-Erling Smørgrav 			return "admin local";
492190cef3dSDag-Erling Smørgrav 		else if (where == FORWARD_REMOTE)
493190cef3dSDag-Erling Smørgrav 			return "admin remote";
494190cef3dSDag-Erling Smørgrav 	} else if (who == FORWARD_USER) {
495190cef3dSDag-Erling Smørgrav 		if (where == FORWARD_LOCAL)
496190cef3dSDag-Erling Smørgrav 			return "user local";
497190cef3dSDag-Erling Smørgrav 		else if (where == FORWARD_REMOTE)
498190cef3dSDag-Erling Smørgrav 			return "user remote";
499190cef3dSDag-Erling Smørgrav 	}
500190cef3dSDag-Erling Smørgrav 	fatal("Unknown forward permission list %d/%d", who, where);
501190cef3dSDag-Erling Smørgrav }
5024f52dfbbSDag-Erling Smørgrav 
503190cef3dSDag-Erling Smørgrav /* Returns the forwarding permission list for the specified direction */
504190cef3dSDag-Erling Smørgrav static struct permission_set *
505190cef3dSDag-Erling Smørgrav permission_set_get(struct ssh *ssh, int where)
506190cef3dSDag-Erling Smørgrav {
507190cef3dSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
508190cef3dSDag-Erling Smørgrav 
509190cef3dSDag-Erling Smørgrav 	switch (where) {
510190cef3dSDag-Erling Smørgrav 	case FORWARD_LOCAL:
511190cef3dSDag-Erling Smørgrav 		return &sc->local_perms;
512190cef3dSDag-Erling Smørgrav 		break;
513190cef3dSDag-Erling Smørgrav 	case FORWARD_REMOTE:
514190cef3dSDag-Erling Smørgrav 		return &sc->remote_perms;
515190cef3dSDag-Erling Smørgrav 		break;
516190cef3dSDag-Erling Smørgrav 	default:
51719261079SEd Maste 		fatal_f("invalid forwarding direction %d", where);
518190cef3dSDag-Erling Smørgrav 	}
519190cef3dSDag-Erling Smørgrav }
520190cef3dSDag-Erling Smørgrav 
52119261079SEd Maste /* Returns pointers to the specified forwarding list and its element count */
522190cef3dSDag-Erling Smørgrav static void
523190cef3dSDag-Erling Smørgrav permission_set_get_array(struct ssh *ssh, int who, int where,
524190cef3dSDag-Erling Smørgrav     struct permission ***permpp, u_int **npermpp)
525190cef3dSDag-Erling Smørgrav {
526190cef3dSDag-Erling Smørgrav 	struct permission_set *pset = permission_set_get(ssh, where);
527190cef3dSDag-Erling Smørgrav 
528190cef3dSDag-Erling Smørgrav 	switch (who) {
529190cef3dSDag-Erling Smørgrav 	case FORWARD_USER:
530190cef3dSDag-Erling Smørgrav 		*permpp = &pset->permitted_user;
531190cef3dSDag-Erling Smørgrav 		*npermpp = &pset->num_permitted_user;
532190cef3dSDag-Erling Smørgrav 		break;
533190cef3dSDag-Erling Smørgrav 	case FORWARD_ADM:
534190cef3dSDag-Erling Smørgrav 		*permpp = &pset->permitted_admin;
535190cef3dSDag-Erling Smørgrav 		*npermpp = &pset->num_permitted_admin;
536190cef3dSDag-Erling Smørgrav 		break;
537190cef3dSDag-Erling Smørgrav 	default:
53819261079SEd Maste 		fatal_f("invalid forwarding client %d", who);
539190cef3dSDag-Erling Smørgrav 	}
540190cef3dSDag-Erling Smørgrav }
541190cef3dSDag-Erling Smørgrav 
5421323ec57SEd Maste /* Adds an entry to the specified forwarding list */
5434f52dfbbSDag-Erling Smørgrav static int
544190cef3dSDag-Erling Smørgrav permission_set_add(struct ssh *ssh, int who, int where,
5454f52dfbbSDag-Erling Smørgrav     const char *host_to_connect, int port_to_connect,
5464f52dfbbSDag-Erling Smørgrav     const char *listen_host, const char *listen_path, int listen_port,
5474f52dfbbSDag-Erling Smørgrav     Channel *downstream)
5484f52dfbbSDag-Erling Smørgrav {
549190cef3dSDag-Erling Smørgrav 	struct permission **permp;
550190cef3dSDag-Erling Smørgrav 	u_int n, *npermp;
5514f52dfbbSDag-Erling Smørgrav 
552190cef3dSDag-Erling Smørgrav 	permission_set_get_array(ssh, who, where, &permp, &npermp);
5534f52dfbbSDag-Erling Smørgrav 
554190cef3dSDag-Erling Smørgrav 	if (*npermp >= INT_MAX)
55519261079SEd Maste 		fatal_f("%s overflow", fwd_ident(who, where));
5564f52dfbbSDag-Erling Smørgrav 
557190cef3dSDag-Erling Smørgrav 	*permp = xrecallocarray(*permp, *npermp, *npermp + 1, sizeof(**permp));
558190cef3dSDag-Erling Smørgrav 	n = (*npermp)++;
5594f52dfbbSDag-Erling Smørgrav #define MAYBE_DUP(s) ((s == NULL) ? NULL : xstrdup(s))
560190cef3dSDag-Erling Smørgrav 	(*permp)[n].host_to_connect = MAYBE_DUP(host_to_connect);
561190cef3dSDag-Erling Smørgrav 	(*permp)[n].port_to_connect = port_to_connect;
562190cef3dSDag-Erling Smørgrav 	(*permp)[n].listen_host = MAYBE_DUP(listen_host);
563190cef3dSDag-Erling Smørgrav 	(*permp)[n].listen_path = MAYBE_DUP(listen_path);
564190cef3dSDag-Erling Smørgrav 	(*permp)[n].listen_port = listen_port;
565190cef3dSDag-Erling Smørgrav 	(*permp)[n].downstream = downstream;
5664f52dfbbSDag-Erling Smørgrav #undef MAYBE_DUP
5674f52dfbbSDag-Erling Smørgrav 	return (int)n;
5684f52dfbbSDag-Erling Smørgrav }
5694f52dfbbSDag-Erling Smørgrav 
5704f52dfbbSDag-Erling Smørgrav static void
5714f52dfbbSDag-Erling Smørgrav mux_remove_remote_forwardings(struct ssh *ssh, Channel *c)
5724f52dfbbSDag-Erling Smørgrav {
5734f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
574190cef3dSDag-Erling Smørgrav 	struct permission_set *pset = &sc->local_perms;
575190cef3dSDag-Erling Smørgrav 	struct permission *perm;
5764f52dfbbSDag-Erling Smørgrav 	int r;
5774f52dfbbSDag-Erling Smørgrav 	u_int i;
5784f52dfbbSDag-Erling Smørgrav 
579190cef3dSDag-Erling Smørgrav 	for (i = 0; i < pset->num_permitted_user; i++) {
580190cef3dSDag-Erling Smørgrav 		perm = &pset->permitted_user[i];
581190cef3dSDag-Erling Smørgrav 		if (perm->downstream != c)
5824f52dfbbSDag-Erling Smørgrav 			continue;
5834f52dfbbSDag-Erling Smørgrav 
5844f52dfbbSDag-Erling Smørgrav 		/* cancel on the server, since mux client is gone */
5854f52dfbbSDag-Erling Smørgrav 		debug("channel %d: cleanup remote forward for %s:%u",
586190cef3dSDag-Erling Smørgrav 		    c->self, perm->listen_host, perm->listen_port);
5874f52dfbbSDag-Erling Smørgrav 		if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 ||
5884f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_cstring(ssh,
5894f52dfbbSDag-Erling Smørgrav 		    "cancel-tcpip-forward")) != 0 ||
5904f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_u8(ssh, 0)) != 0 ||
5914f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_cstring(ssh,
592190cef3dSDag-Erling Smørgrav 		    channel_rfwd_bind_host(perm->listen_host))) != 0 ||
593190cef3dSDag-Erling Smørgrav 		    (r = sshpkt_put_u32(ssh, perm->listen_port)) != 0 ||
5944f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_send(ssh)) != 0) {
59519261079SEd Maste 			fatal_fr(r, "channel %i", c->self);
5964f52dfbbSDag-Erling Smørgrav 		}
597190cef3dSDag-Erling Smørgrav 		fwd_perm_clear(perm); /* unregister */
5984f52dfbbSDag-Erling Smørgrav 	}
599a04a10f8SKris Kennaway }
600511b41d2SMark Murray 
601a04a10f8SKris Kennaway /* Free the channel and close its fd/socket. */
602a04a10f8SKris Kennaway void
6034f52dfbbSDag-Erling Smørgrav channel_free(struct ssh *ssh, Channel *c)
604a04a10f8SKris Kennaway {
6054f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
606af12a3e7SDag-Erling Smørgrav 	char *s;
60721e764dfSDag-Erling Smørgrav 	u_int i, n;
608ca86bcf2SDag-Erling Smørgrav 	Channel *other;
609d4af9e69SDag-Erling Smørgrav 	struct channel_confirm *cc;
610ca3176e7SBrian Feldman 
6114f52dfbbSDag-Erling Smørgrav 	for (n = 0, i = 0; i < sc->channels_alloc; i++) {
6124f52dfbbSDag-Erling Smørgrav 		if ((other = sc->channels[i]) == NULL)
6134f52dfbbSDag-Erling Smørgrav 			continue;
614af12a3e7SDag-Erling Smørgrav 		n++;
615ca86bcf2SDag-Erling Smørgrav 		/* detach from mux client and prepare for closing */
616ca86bcf2SDag-Erling Smørgrav 		if (c->type == SSH_CHANNEL_MUX_CLIENT &&
617ca86bcf2SDag-Erling Smørgrav 		    other->type == SSH_CHANNEL_MUX_PROXY &&
618ca86bcf2SDag-Erling Smørgrav 		    other->mux_ctx == c) {
619ca86bcf2SDag-Erling Smørgrav 			other->mux_ctx = NULL;
620ca86bcf2SDag-Erling Smørgrav 			other->type = SSH_CHANNEL_OPEN;
621ca86bcf2SDag-Erling Smørgrav 			other->istate = CHAN_INPUT_CLOSED;
622ca86bcf2SDag-Erling Smørgrav 			other->ostate = CHAN_OUTPUT_CLOSED;
623ca86bcf2SDag-Erling Smørgrav 		}
624ca86bcf2SDag-Erling Smørgrav 	}
62521e764dfSDag-Erling Smørgrav 	debug("channel %d: free: %s, nchannels %u", c->self,
626af12a3e7SDag-Erling Smørgrav 	    c->remote_name ? c->remote_name : "???", n);
627af12a3e7SDag-Erling Smørgrav 
628e9e8876aSEd Maste 	if (c->type == SSH_CHANNEL_MUX_CLIENT) {
6294f52dfbbSDag-Erling Smørgrav 		mux_remove_remote_forwardings(ssh, c);
630e9e8876aSEd Maste 		free(c->mux_ctx);
631e9e8876aSEd Maste 		c->mux_ctx = NULL;
632e9e8876aSEd Maste 	} else if (c->type == SSH_CHANNEL_MUX_LISTENER) {
63319261079SEd Maste 		free(c->mux_ctx);
63419261079SEd Maste 		c->mux_ctx = NULL;
63519261079SEd Maste 	}
636ca86bcf2SDag-Erling Smørgrav 
637190cef3dSDag-Erling Smørgrav 	if (log_level_get() >= SYSLOG_LEVEL_DEBUG3) {
6384f52dfbbSDag-Erling Smørgrav 		s = channel_open_message(ssh);
639221552e4SDag-Erling Smørgrav 		debug3("channel %d: status: %s", c->self, s);
640e4a9863fSDag-Erling Smørgrav 		free(s);
641190cef3dSDag-Erling Smørgrav 	}
642ca3176e7SBrian Feldman 
6434f52dfbbSDag-Erling Smørgrav 	channel_close_fds(ssh, c);
6444f52dfbbSDag-Erling Smørgrav 	sshbuf_free(c->input);
6454f52dfbbSDag-Erling Smørgrav 	sshbuf_free(c->output);
6464f52dfbbSDag-Erling Smørgrav 	sshbuf_free(c->extended);
6474f52dfbbSDag-Erling Smørgrav 	c->input = c->output = c->extended = NULL;
648e4a9863fSDag-Erling Smørgrav 	free(c->remote_name);
649a04a10f8SKris Kennaway 	c->remote_name = NULL;
650e4a9863fSDag-Erling Smørgrav 	free(c->path);
651cce7d346SDag-Erling Smørgrav 	c->path = NULL;
652e4a9863fSDag-Erling Smørgrav 	free(c->listening_addr);
653462c32cbSDag-Erling Smørgrav 	c->listening_addr = NULL;
654d4af9e69SDag-Erling Smørgrav 	while ((cc = TAILQ_FIRST(&c->status_confirms)) != NULL) {
655d4af9e69SDag-Erling Smørgrav 		if (cc->abandon_cb != NULL)
6564f52dfbbSDag-Erling Smørgrav 			cc->abandon_cb(ssh, c, cc->ctx);
657d4af9e69SDag-Erling Smørgrav 		TAILQ_REMOVE(&c->status_confirms, cc, entry);
65819261079SEd Maste 		freezero(cc, sizeof(*cc));
659d4af9e69SDag-Erling Smørgrav 	}
660d4af9e69SDag-Erling Smørgrav 	if (c->filter_cleanup != NULL && c->filter_ctx != NULL)
6614f52dfbbSDag-Erling Smørgrav 		c->filter_cleanup(ssh, c->self, c->filter_ctx);
6624f52dfbbSDag-Erling Smørgrav 	sc->channels[c->self] = NULL;
66319261079SEd Maste 	freezero(c, sizeof(*c));
664af12a3e7SDag-Erling Smørgrav }
665af12a3e7SDag-Erling Smørgrav 
666af12a3e7SDag-Erling Smørgrav void
6674f52dfbbSDag-Erling Smørgrav channel_free_all(struct ssh *ssh)
668af12a3e7SDag-Erling Smørgrav {
66921e764dfSDag-Erling Smørgrav 	u_int i;
67019261079SEd Maste 	struct ssh_channels *sc = ssh->chanctxt;
671af12a3e7SDag-Erling Smørgrav 
67219261079SEd Maste 	for (i = 0; i < sc->channels_alloc; i++)
67319261079SEd Maste 		if (sc->channels[i] != NULL)
67419261079SEd Maste 			channel_free(ssh, sc->channels[i]);
67519261079SEd Maste 
67619261079SEd Maste 	free(sc->channels);
67719261079SEd Maste 	sc->channels = NULL;
67819261079SEd Maste 	sc->channels_alloc = 0;
67919261079SEd Maste 
68019261079SEd Maste 	free(sc->x11_saved_display);
68119261079SEd Maste 	sc->x11_saved_display = NULL;
68219261079SEd Maste 
68319261079SEd Maste 	free(sc->x11_saved_proto);
68419261079SEd Maste 	sc->x11_saved_proto = NULL;
68519261079SEd Maste 
68619261079SEd Maste 	free(sc->x11_saved_data);
68719261079SEd Maste 	sc->x11_saved_data = NULL;
68819261079SEd Maste 	sc->x11_saved_data_len = 0;
68919261079SEd Maste 
69019261079SEd Maste 	free(sc->x11_fake_data);
69119261079SEd Maste 	sc->x11_fake_data = NULL;
69219261079SEd Maste 	sc->x11_fake_data_len = 0;
693af12a3e7SDag-Erling Smørgrav }
694af12a3e7SDag-Erling Smørgrav 
695af12a3e7SDag-Erling Smørgrav /*
696af12a3e7SDag-Erling Smørgrav  * Closes the sockets/fds of all channels.  This is used to close extra file
697af12a3e7SDag-Erling Smørgrav  * descriptors after a fork.
698af12a3e7SDag-Erling Smørgrav  */
699af12a3e7SDag-Erling Smørgrav void
7004f52dfbbSDag-Erling Smørgrav channel_close_all(struct ssh *ssh)
701af12a3e7SDag-Erling Smørgrav {
70221e764dfSDag-Erling Smørgrav 	u_int i;
703af12a3e7SDag-Erling Smørgrav 
7044f52dfbbSDag-Erling Smørgrav 	for (i = 0; i < ssh->chanctxt->channels_alloc; i++)
7054f52dfbbSDag-Erling Smørgrav 		if (ssh->chanctxt->channels[i] != NULL)
7064f52dfbbSDag-Erling Smørgrav 			channel_close_fds(ssh, ssh->chanctxt->channels[i]);
707af12a3e7SDag-Erling Smørgrav }
708af12a3e7SDag-Erling Smørgrav 
709af12a3e7SDag-Erling Smørgrav /*
710af12a3e7SDag-Erling Smørgrav  * Stop listening to channels.
711af12a3e7SDag-Erling Smørgrav  */
712af12a3e7SDag-Erling Smørgrav void
7134f52dfbbSDag-Erling Smørgrav channel_stop_listening(struct ssh *ssh)
714af12a3e7SDag-Erling Smørgrav {
71521e764dfSDag-Erling Smørgrav 	u_int i;
716af12a3e7SDag-Erling Smørgrav 	Channel *c;
717af12a3e7SDag-Erling Smørgrav 
7184f52dfbbSDag-Erling Smørgrav 	for (i = 0; i < ssh->chanctxt->channels_alloc; i++) {
7194f52dfbbSDag-Erling Smørgrav 		c = ssh->chanctxt->channels[i];
720af12a3e7SDag-Erling Smørgrav 		if (c != NULL) {
721af12a3e7SDag-Erling Smørgrav 			switch (c->type) {
722af12a3e7SDag-Erling Smørgrav 			case SSH_CHANNEL_AUTH_SOCKET:
723af12a3e7SDag-Erling Smørgrav 			case SSH_CHANNEL_PORT_LISTENER:
724af12a3e7SDag-Erling Smørgrav 			case SSH_CHANNEL_RPORT_LISTENER:
725af12a3e7SDag-Erling Smørgrav 			case SSH_CHANNEL_X11_LISTENER:
726a0ee8cc6SDag-Erling Smørgrav 			case SSH_CHANNEL_UNIX_LISTENER:
727a0ee8cc6SDag-Erling Smørgrav 			case SSH_CHANNEL_RUNIX_LISTENER:
72819261079SEd Maste 				channel_close_fd(ssh, c, &c->sock);
7294f52dfbbSDag-Erling Smørgrav 				channel_free(ssh, c);
730af12a3e7SDag-Erling Smørgrav 				break;
731af12a3e7SDag-Erling Smørgrav 			}
732af12a3e7SDag-Erling Smørgrav 		}
733af12a3e7SDag-Erling Smørgrav 	}
734af12a3e7SDag-Erling Smørgrav }
735af12a3e7SDag-Erling Smørgrav 
736af12a3e7SDag-Erling Smørgrav /*
737af12a3e7SDag-Erling Smørgrav  * Returns true if no channel has too much buffered data, and false if one or
738af12a3e7SDag-Erling Smørgrav  * more channel is overfull.
739af12a3e7SDag-Erling Smørgrav  */
740af12a3e7SDag-Erling Smørgrav int
7414f52dfbbSDag-Erling Smørgrav channel_not_very_much_buffered_data(struct ssh *ssh)
742af12a3e7SDag-Erling Smørgrav {
743af12a3e7SDag-Erling Smørgrav 	u_int i;
7444f52dfbbSDag-Erling Smørgrav 	u_int maxsize = ssh_packet_get_maxsize(ssh);
745af12a3e7SDag-Erling Smørgrav 	Channel *c;
746af12a3e7SDag-Erling Smørgrav 
7474f52dfbbSDag-Erling Smørgrav 	for (i = 0; i < ssh->chanctxt->channels_alloc; i++) {
7484f52dfbbSDag-Erling Smørgrav 		c = ssh->chanctxt->channels[i];
7494f52dfbbSDag-Erling Smørgrav 		if (c == NULL || c->type != SSH_CHANNEL_OPEN)
7504f52dfbbSDag-Erling Smørgrav 			continue;
7514f52dfbbSDag-Erling Smørgrav 		if (sshbuf_len(c->output) > maxsize) {
7524f52dfbbSDag-Erling Smørgrav 			debug2("channel %d: big output buffer %zu > %u",
7534f52dfbbSDag-Erling Smørgrav 			    c->self, sshbuf_len(c->output), maxsize);
754af12a3e7SDag-Erling Smørgrav 			return 0;
755af12a3e7SDag-Erling Smørgrav 		}
756af12a3e7SDag-Erling Smørgrav 	}
757af12a3e7SDag-Erling Smørgrav 	return 1;
758af12a3e7SDag-Erling Smørgrav }
759af12a3e7SDag-Erling Smørgrav 
760af12a3e7SDag-Erling Smørgrav /* Returns true if any channel is still open. */
761af12a3e7SDag-Erling Smørgrav int
7624f52dfbbSDag-Erling Smørgrav channel_still_open(struct ssh *ssh)
763af12a3e7SDag-Erling Smørgrav {
76421e764dfSDag-Erling Smørgrav 	u_int i;
765af12a3e7SDag-Erling Smørgrav 	Channel *c;
766af12a3e7SDag-Erling Smørgrav 
7674f52dfbbSDag-Erling Smørgrav 	for (i = 0; i < ssh->chanctxt->channels_alloc; i++) {
7684f52dfbbSDag-Erling Smørgrav 		c = ssh->chanctxt->channels[i];
769af12a3e7SDag-Erling Smørgrav 		if (c == NULL)
770af12a3e7SDag-Erling Smørgrav 			continue;
771af12a3e7SDag-Erling Smørgrav 		switch (c->type) {
772af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_X11_LISTENER:
773af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_PORT_LISTENER:
774af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_RPORT_LISTENER:
775b15c8340SDag-Erling Smørgrav 		case SSH_CHANNEL_MUX_LISTENER:
776af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_CLOSED:
777af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_AUTH_SOCKET:
778af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_DYNAMIC:
7794f52dfbbSDag-Erling Smørgrav 		case SSH_CHANNEL_RDYNAMIC_OPEN:
780af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_CONNECTING:
781af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_ZOMBIE:
782e4a9863fSDag-Erling Smørgrav 		case SSH_CHANNEL_ABANDONED:
783a0ee8cc6SDag-Erling Smørgrav 		case SSH_CHANNEL_UNIX_LISTENER:
784a0ee8cc6SDag-Erling Smørgrav 		case SSH_CHANNEL_RUNIX_LISTENER:
785af12a3e7SDag-Erling Smørgrav 			continue;
786af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_LARVAL:
787af12a3e7SDag-Erling Smørgrav 			continue;
788af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_OPENING:
789af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_OPEN:
7904f52dfbbSDag-Erling Smørgrav 		case SSH_CHANNEL_RDYNAMIC_FINISH:
791af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_X11_OPEN:
792b15c8340SDag-Erling Smørgrav 		case SSH_CHANNEL_MUX_CLIENT:
793ca86bcf2SDag-Erling Smørgrav 		case SSH_CHANNEL_MUX_PROXY:
794af12a3e7SDag-Erling Smørgrav 			return 1;
795af12a3e7SDag-Erling Smørgrav 		default:
79619261079SEd Maste 			fatal_f("bad channel type %d", c->type);
797af12a3e7SDag-Erling Smørgrav 			/* NOTREACHED */
798af12a3e7SDag-Erling Smørgrav 		}
799af12a3e7SDag-Erling Smørgrav 	}
800af12a3e7SDag-Erling Smørgrav 	return 0;
801af12a3e7SDag-Erling Smørgrav }
802af12a3e7SDag-Erling Smørgrav 
803af12a3e7SDag-Erling Smørgrav /* Returns the id of an open channel suitable for keepaliving */
804af12a3e7SDag-Erling Smørgrav int
8054f52dfbbSDag-Erling Smørgrav channel_find_open(struct ssh *ssh)
806af12a3e7SDag-Erling Smørgrav {
80721e764dfSDag-Erling Smørgrav 	u_int i;
808af12a3e7SDag-Erling Smørgrav 	Channel *c;
809af12a3e7SDag-Erling Smørgrav 
8104f52dfbbSDag-Erling Smørgrav 	for (i = 0; i < ssh->chanctxt->channels_alloc; i++) {
8114f52dfbbSDag-Erling Smørgrav 		c = ssh->chanctxt->channels[i];
8124f52dfbbSDag-Erling Smørgrav 		if (c == NULL || !c->have_remote_id)
813af12a3e7SDag-Erling Smørgrav 			continue;
814af12a3e7SDag-Erling Smørgrav 		switch (c->type) {
815af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_CLOSED:
816af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_DYNAMIC:
8174f52dfbbSDag-Erling Smørgrav 		case SSH_CHANNEL_RDYNAMIC_OPEN:
8184f52dfbbSDag-Erling Smørgrav 		case SSH_CHANNEL_RDYNAMIC_FINISH:
819af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_X11_LISTENER:
820af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_PORT_LISTENER:
821af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_RPORT_LISTENER:
822b15c8340SDag-Erling Smørgrav 		case SSH_CHANNEL_MUX_LISTENER:
823b15c8340SDag-Erling Smørgrav 		case SSH_CHANNEL_MUX_CLIENT:
824ca86bcf2SDag-Erling Smørgrav 		case SSH_CHANNEL_MUX_PROXY:
825af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_OPENING:
826af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_CONNECTING:
827af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_ZOMBIE:
828e4a9863fSDag-Erling Smørgrav 		case SSH_CHANNEL_ABANDONED:
829a0ee8cc6SDag-Erling Smørgrav 		case SSH_CHANNEL_UNIX_LISTENER:
830a0ee8cc6SDag-Erling Smørgrav 		case SSH_CHANNEL_RUNIX_LISTENER:
831af12a3e7SDag-Erling Smørgrav 			continue;
832af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_LARVAL:
833af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_AUTH_SOCKET:
834af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_OPEN:
835af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_X11_OPEN:
836af12a3e7SDag-Erling Smørgrav 			return i;
837af12a3e7SDag-Erling Smørgrav 		default:
83819261079SEd Maste 			fatal_f("bad channel type %d", c->type);
839af12a3e7SDag-Erling Smørgrav 			/* NOTREACHED */
840af12a3e7SDag-Erling Smørgrav 		}
841af12a3e7SDag-Erling Smørgrav 	}
842af12a3e7SDag-Erling Smørgrav 	return -1;
843af12a3e7SDag-Erling Smørgrav }
844af12a3e7SDag-Erling Smørgrav 
8452f513db7SEd Maste /* Returns the state of the channel's extended usage flag */
8462f513db7SEd Maste const char *
8472f513db7SEd Maste channel_format_extended_usage(const Channel *c)
8482f513db7SEd Maste {
8492f513db7SEd Maste 	if (c->efd == -1)
8502f513db7SEd Maste 		return "closed";
8512f513db7SEd Maste 
8522f513db7SEd Maste 	switch (c->extended_usage) {
8532f513db7SEd Maste 	case CHAN_EXTENDED_WRITE:
8542f513db7SEd Maste 		return "write";
8552f513db7SEd Maste 	case CHAN_EXTENDED_READ:
8562f513db7SEd Maste 		return "read";
8572f513db7SEd Maste 	case CHAN_EXTENDED_IGNORE:
8582f513db7SEd Maste 		return "ignore";
8592f513db7SEd Maste 	default:
8602f513db7SEd Maste 		return "UNKNOWN";
8612f513db7SEd Maste 	}
8622f513db7SEd Maste }
8632f513db7SEd Maste 
8642f513db7SEd Maste static char *
8652f513db7SEd Maste channel_format_status(const Channel *c)
8662f513db7SEd Maste {
8672f513db7SEd Maste 	char *ret = NULL;
8682f513db7SEd Maste 
8692f513db7SEd Maste 	xasprintf(&ret, "t%d %s%u i%u/%zu o%u/%zu e[%s]/%zu "
8701323ec57SEd Maste 	    "fd %d/%d/%d sock %d cc %d io 0x%02x/0x%02x",
8712f513db7SEd Maste 	    c->type,
8722f513db7SEd Maste 	    c->have_remote_id ? "r" : "nr", c->remote_id,
8732f513db7SEd Maste 	    c->istate, sshbuf_len(c->input),
8742f513db7SEd Maste 	    c->ostate, sshbuf_len(c->output),
8752f513db7SEd Maste 	    channel_format_extended_usage(c), sshbuf_len(c->extended),
8761323ec57SEd Maste 	    c->rfd, c->wfd, c->efd, c->sock, c->ctl_chan,
8771323ec57SEd Maste 	    c->io_want, c->io_ready);
8782f513db7SEd Maste 	return ret;
8792f513db7SEd Maste }
8802f513db7SEd Maste 
881af12a3e7SDag-Erling Smørgrav /*
882af12a3e7SDag-Erling Smørgrav  * Returns a message describing the currently open forwarded connections,
883af12a3e7SDag-Erling Smørgrav  * suitable for sending to the client.  The message contains crlf pairs for
884af12a3e7SDag-Erling Smørgrav  * newlines.
885af12a3e7SDag-Erling Smørgrav  */
886af12a3e7SDag-Erling Smørgrav char *
8874f52dfbbSDag-Erling Smørgrav channel_open_message(struct ssh *ssh)
888af12a3e7SDag-Erling Smørgrav {
8894f52dfbbSDag-Erling Smørgrav 	struct sshbuf *buf;
890af12a3e7SDag-Erling Smørgrav 	Channel *c;
89121e764dfSDag-Erling Smørgrav 	u_int i;
8924f52dfbbSDag-Erling Smørgrav 	int r;
8932f513db7SEd Maste 	char *cp, *ret;
894af12a3e7SDag-Erling Smørgrav 
8954f52dfbbSDag-Erling Smørgrav 	if ((buf = sshbuf_new()) == NULL)
89619261079SEd Maste 		fatal_f("sshbuf_new");
8974f52dfbbSDag-Erling Smørgrav 	if ((r = sshbuf_putf(buf,
8984f52dfbbSDag-Erling Smørgrav 	    "The following connections are open:\r\n")) != 0)
89919261079SEd Maste 		fatal_fr(r, "sshbuf_putf");
9004f52dfbbSDag-Erling Smørgrav 	for (i = 0; i < ssh->chanctxt->channels_alloc; i++) {
9014f52dfbbSDag-Erling Smørgrav 		c = ssh->chanctxt->channels[i];
902af12a3e7SDag-Erling Smørgrav 		if (c == NULL)
903af12a3e7SDag-Erling Smørgrav 			continue;
904af12a3e7SDag-Erling Smørgrav 		switch (c->type) {
905af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_X11_LISTENER:
906af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_PORT_LISTENER:
907af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_RPORT_LISTENER:
908af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_CLOSED:
909af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_AUTH_SOCKET:
910af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_ZOMBIE:
911e4a9863fSDag-Erling Smørgrav 		case SSH_CHANNEL_ABANDONED:
912b15c8340SDag-Erling Smørgrav 		case SSH_CHANNEL_MUX_LISTENER:
913a0ee8cc6SDag-Erling Smørgrav 		case SSH_CHANNEL_UNIX_LISTENER:
914a0ee8cc6SDag-Erling Smørgrav 		case SSH_CHANNEL_RUNIX_LISTENER:
915af12a3e7SDag-Erling Smørgrav 			continue;
916af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_LARVAL:
917af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_OPENING:
918af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_CONNECTING:
919af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_DYNAMIC:
9204f52dfbbSDag-Erling Smørgrav 		case SSH_CHANNEL_RDYNAMIC_OPEN:
9214f52dfbbSDag-Erling Smørgrav 		case SSH_CHANNEL_RDYNAMIC_FINISH:
922af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_OPEN:
923af12a3e7SDag-Erling Smørgrav 		case SSH_CHANNEL_X11_OPEN:
924ca86bcf2SDag-Erling Smørgrav 		case SSH_CHANNEL_MUX_PROXY:
925ca86bcf2SDag-Erling Smørgrav 		case SSH_CHANNEL_MUX_CLIENT:
9262f513db7SEd Maste 			cp = channel_format_status(c);
9272f513db7SEd Maste 			if ((r = sshbuf_putf(buf, "  #%d %.300s (%s)\r\n",
9282f513db7SEd Maste 			    c->self, c->remote_name, cp)) != 0) {
9292f513db7SEd Maste 				free(cp);
93019261079SEd Maste 				fatal_fr(r, "sshbuf_putf");
9312f513db7SEd Maste 			}
9322f513db7SEd Maste 			free(cp);
933af12a3e7SDag-Erling Smørgrav 			continue;
934af12a3e7SDag-Erling Smørgrav 		default:
93519261079SEd Maste 			fatal_f("bad channel type %d", c->type);
936af12a3e7SDag-Erling Smørgrav 			/* NOTREACHED */
937af12a3e7SDag-Erling Smørgrav 		}
938af12a3e7SDag-Erling Smørgrav 	}
9394f52dfbbSDag-Erling Smørgrav 	if ((ret = sshbuf_dup_string(buf)) == NULL)
94019261079SEd Maste 		fatal_f("sshbuf_dup_string");
9414f52dfbbSDag-Erling Smørgrav 	sshbuf_free(buf);
9424f52dfbbSDag-Erling Smørgrav 	return ret;
9434f52dfbbSDag-Erling Smørgrav }
9444f52dfbbSDag-Erling Smørgrav 
9454f52dfbbSDag-Erling Smørgrav static void
9464f52dfbbSDag-Erling Smørgrav open_preamble(struct ssh *ssh, const char *where, Channel *c, const char *type)
9474f52dfbbSDag-Erling Smørgrav {
9484f52dfbbSDag-Erling Smørgrav 	int r;
9494f52dfbbSDag-Erling Smørgrav 
9504f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_OPEN)) != 0 ||
9514f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_cstring(ssh, type)) != 0 ||
9524f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u32(ssh, c->self)) != 0 ||
9534f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u32(ssh, c->local_window)) != 0 ||
9544f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u32(ssh, c->local_maxpacket)) != 0) {
95519261079SEd Maste 		fatal_r(r, "%s: channel %i: open", where, c->self);
9564f52dfbbSDag-Erling Smørgrav 	}
957af12a3e7SDag-Erling Smørgrav }
958af12a3e7SDag-Erling Smørgrav 
959af12a3e7SDag-Erling Smørgrav void
9604f52dfbbSDag-Erling Smørgrav channel_send_open(struct ssh *ssh, int id)
961af12a3e7SDag-Erling Smørgrav {
9624f52dfbbSDag-Erling Smørgrav 	Channel *c = channel_lookup(ssh, id);
9634f52dfbbSDag-Erling Smørgrav 	int r;
964f388f5efSDag-Erling Smørgrav 
965af12a3e7SDag-Erling Smørgrav 	if (c == NULL) {
966221552e4SDag-Erling Smørgrav 		logit("channel_send_open: %d: bad id", id);
967af12a3e7SDag-Erling Smørgrav 		return;
968af12a3e7SDag-Erling Smørgrav 	}
969e73e9afaSDag-Erling Smørgrav 	debug2("channel %d: send open", id);
9704f52dfbbSDag-Erling Smørgrav 	open_preamble(ssh, __func__, c, c->ctype);
9714f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_send(ssh)) != 0)
97219261079SEd Maste 		fatal_fr(r, "channel %i", c->self);
973af12a3e7SDag-Erling Smørgrav }
974af12a3e7SDag-Erling Smørgrav 
975af12a3e7SDag-Erling Smørgrav void
9764f52dfbbSDag-Erling Smørgrav channel_request_start(struct ssh *ssh, int id, char *service, int wantconfirm)
977af12a3e7SDag-Erling Smørgrav {
9784f52dfbbSDag-Erling Smørgrav 	Channel *c = channel_lookup(ssh, id);
9794f52dfbbSDag-Erling Smørgrav 	int r;
980f388f5efSDag-Erling Smørgrav 
981af12a3e7SDag-Erling Smørgrav 	if (c == NULL) {
98219261079SEd Maste 		logit_f("%d: unknown channel id", id);
983af12a3e7SDag-Erling Smørgrav 		return;
984af12a3e7SDag-Erling Smørgrav 	}
9854f52dfbbSDag-Erling Smørgrav 	if (!c->have_remote_id)
98619261079SEd Maste 		fatal_f("channel %d: no remote id", c->self);
9874f52dfbbSDag-Erling Smørgrav 
98821e764dfSDag-Erling Smørgrav 	debug2("channel %d: request %s confirm %d", id, service, wantconfirm);
9894f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_REQUEST)) != 0 ||
9904f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
9914f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_cstring(ssh, service)) != 0 ||
9924f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u8(ssh, wantconfirm)) != 0) {
99319261079SEd Maste 		fatal_fr(r, "channel %i", c->self);
9944f52dfbbSDag-Erling Smørgrav 	}
995af12a3e7SDag-Erling Smørgrav }
996333ee039SDag-Erling Smørgrav 
997af12a3e7SDag-Erling Smørgrav void
9984f52dfbbSDag-Erling Smørgrav channel_register_status_confirm(struct ssh *ssh, int id,
9994f52dfbbSDag-Erling Smørgrav     channel_confirm_cb *cb, channel_confirm_abandon_cb *abandon_cb, void *ctx)
1000d4af9e69SDag-Erling Smørgrav {
1001d4af9e69SDag-Erling Smørgrav 	struct channel_confirm *cc;
1002d4af9e69SDag-Erling Smørgrav 	Channel *c;
1003d4af9e69SDag-Erling Smørgrav 
10044f52dfbbSDag-Erling Smørgrav 	if ((c = channel_lookup(ssh, id)) == NULL)
100519261079SEd Maste 		fatal_f("%d: bad id", id);
1006d4af9e69SDag-Erling Smørgrav 
10070a37d4a3SXin LI 	cc = xcalloc(1, sizeof(*cc));
1008d4af9e69SDag-Erling Smørgrav 	cc->cb = cb;
1009d4af9e69SDag-Erling Smørgrav 	cc->abandon_cb = abandon_cb;
1010d4af9e69SDag-Erling Smørgrav 	cc->ctx = ctx;
1011d4af9e69SDag-Erling Smørgrav 	TAILQ_INSERT_TAIL(&c->status_confirms, cc, entry);
1012d4af9e69SDag-Erling Smørgrav }
1013d4af9e69SDag-Erling Smørgrav 
1014d4af9e69SDag-Erling Smørgrav void
10154f52dfbbSDag-Erling Smørgrav channel_register_open_confirm(struct ssh *ssh, int id,
10164f52dfbbSDag-Erling Smørgrav     channel_open_fn *fn, void *ctx)
1017af12a3e7SDag-Erling Smørgrav {
10184f52dfbbSDag-Erling Smørgrav 	Channel *c = channel_lookup(ssh, id);
1019f388f5efSDag-Erling Smørgrav 
1020af12a3e7SDag-Erling Smørgrav 	if (c == NULL) {
102119261079SEd Maste 		logit_f("%d: bad id", id);
1022af12a3e7SDag-Erling Smørgrav 		return;
1023af12a3e7SDag-Erling Smørgrav 	}
1024d4af9e69SDag-Erling Smørgrav 	c->open_confirm = fn;
1025d4af9e69SDag-Erling Smørgrav 	c->open_confirm_ctx = ctx;
1026af12a3e7SDag-Erling Smørgrav }
1027333ee039SDag-Erling Smørgrav 
1028af12a3e7SDag-Erling Smørgrav void
10294f52dfbbSDag-Erling Smørgrav channel_register_cleanup(struct ssh *ssh, int id,
10304f52dfbbSDag-Erling Smørgrav     channel_callback_fn *fn, int do_close)
1031af12a3e7SDag-Erling Smørgrav {
10324f52dfbbSDag-Erling Smørgrav 	Channel *c = channel_by_id(ssh, id);
1033f388f5efSDag-Erling Smørgrav 
1034af12a3e7SDag-Erling Smørgrav 	if (c == NULL) {
103519261079SEd Maste 		logit_f("%d: bad id", id);
1036af12a3e7SDag-Erling Smørgrav 		return;
1037af12a3e7SDag-Erling Smørgrav 	}
1038af12a3e7SDag-Erling Smørgrav 	c->detach_user = fn;
1039b74df5b2SDag-Erling Smørgrav 	c->detach_close = do_close;
1040af12a3e7SDag-Erling Smørgrav }
1041333ee039SDag-Erling Smørgrav 
1042af12a3e7SDag-Erling Smørgrav void
10434f52dfbbSDag-Erling Smørgrav channel_cancel_cleanup(struct ssh *ssh, int id)
1044af12a3e7SDag-Erling Smørgrav {
10454f52dfbbSDag-Erling Smørgrav 	Channel *c = channel_by_id(ssh, id);
1046f388f5efSDag-Erling Smørgrav 
1047af12a3e7SDag-Erling Smørgrav 	if (c == NULL) {
104819261079SEd Maste 		logit_f("%d: bad id", id);
1049af12a3e7SDag-Erling Smørgrav 		return;
1050af12a3e7SDag-Erling Smørgrav 	}
1051af12a3e7SDag-Erling Smørgrav 	c->detach_user = NULL;
1052b74df5b2SDag-Erling Smørgrav 	c->detach_close = 0;
1053af12a3e7SDag-Erling Smørgrav }
1054333ee039SDag-Erling Smørgrav 
1055af12a3e7SDag-Erling Smørgrav void
10564f52dfbbSDag-Erling Smørgrav channel_register_filter(struct ssh *ssh, int id, channel_infilter_fn *ifn,
1057d4af9e69SDag-Erling Smørgrav     channel_outfilter_fn *ofn, channel_filter_cleanup_fn *cfn, void *ctx)
1058af12a3e7SDag-Erling Smørgrav {
10594f52dfbbSDag-Erling Smørgrav 	Channel *c = channel_lookup(ssh, id);
1060f388f5efSDag-Erling Smørgrav 
1061af12a3e7SDag-Erling Smørgrav 	if (c == NULL) {
106219261079SEd Maste 		logit_f("%d: bad id", id);
1063af12a3e7SDag-Erling Smørgrav 		return;
1064af12a3e7SDag-Erling Smørgrav 	}
1065b74df5b2SDag-Erling Smørgrav 	c->input_filter = ifn;
1066b74df5b2SDag-Erling Smørgrav 	c->output_filter = ofn;
1067d4af9e69SDag-Erling Smørgrav 	c->filter_ctx = ctx;
1068d4af9e69SDag-Erling Smørgrav 	c->filter_cleanup = cfn;
1069af12a3e7SDag-Erling Smørgrav }
1070af12a3e7SDag-Erling Smørgrav 
1071af12a3e7SDag-Erling Smørgrav void
10724f52dfbbSDag-Erling Smørgrav channel_set_fds(struct ssh *ssh, int id, int rfd, int wfd, int efd,
1073d4af9e69SDag-Erling Smørgrav     int extusage, int nonblock, int is_tty, u_int window_max)
1074af12a3e7SDag-Erling Smørgrav {
10754f52dfbbSDag-Erling Smørgrav 	Channel *c = channel_lookup(ssh, id);
10764f52dfbbSDag-Erling Smørgrav 	int r;
1077f388f5efSDag-Erling Smørgrav 
1078af12a3e7SDag-Erling Smørgrav 	if (c == NULL || c->type != SSH_CHANNEL_LARVAL)
1079af12a3e7SDag-Erling Smørgrav 		fatal("channel_activate for non-larval channel %d.", id);
10804f52dfbbSDag-Erling Smørgrav 	if (!c->have_remote_id)
108119261079SEd Maste 		fatal_f("channel %d: no remote id", c->self);
10824f52dfbbSDag-Erling Smørgrav 
10834f52dfbbSDag-Erling Smørgrav 	channel_register_fds(ssh, c, rfd, wfd, efd, extusage, nonblock, is_tty);
1084af12a3e7SDag-Erling Smørgrav 	c->type = SSH_CHANNEL_OPEN;
1085af12a3e7SDag-Erling Smørgrav 	c->local_window = c->local_window_max = window_max;
10864f52dfbbSDag-Erling Smørgrav 
10874f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_WINDOW_ADJUST)) != 0 ||
10884f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
10894f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u32(ssh, c->local_window)) != 0 ||
10904f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_send(ssh)) != 0)
109119261079SEd Maste 		fatal_fr(r, "channel %i", c->self);
1092511b41d2SMark Murray }
1093511b41d2SMark Murray 
1094af12a3e7SDag-Erling Smørgrav static void
10951323ec57SEd Maste channel_pre_listener(struct ssh *ssh, Channel *c)
1096511b41d2SMark Murray {
10971323ec57SEd Maste 	c->io_want = SSH_CHAN_IO_SOCK_R;
1098a04a10f8SKris Kennaway }
1099a04a10f8SKris Kennaway 
1100af12a3e7SDag-Erling Smørgrav static void
11011323ec57SEd Maste channel_pre_connecting(struct ssh *ssh, Channel *c)
1102ca3176e7SBrian Feldman {
1103ca3176e7SBrian Feldman 	debug3("channel %d: waiting for connection", c->self);
11041323ec57SEd Maste 	c->io_want = SSH_CHAN_IO_SOCK_W;
1105ca3176e7SBrian Feldman }
1106ca3176e7SBrian Feldman 
1107af12a3e7SDag-Erling Smørgrav static void
11081323ec57SEd Maste channel_pre_open(struct ssh *ssh, Channel *c)
1109a04a10f8SKris Kennaway {
11101323ec57SEd Maste 	c->io_want = 0;
1111a04a10f8SKris Kennaway 	if (c->istate == CHAN_INPUT_OPEN &&
11124f52dfbbSDag-Erling Smørgrav 	    c->remote_window > 0 &&
11134f52dfbbSDag-Erling Smørgrav 	    sshbuf_len(c->input) < c->remote_window &&
11144f52dfbbSDag-Erling Smørgrav 	    sshbuf_check_reserve(c->input, CHAN_RBUF) == 0)
11151323ec57SEd Maste 		c->io_want |= SSH_CHAN_IO_RFD;
1116a04a10f8SKris Kennaway 	if (c->ostate == CHAN_OUTPUT_OPEN ||
1117a04a10f8SKris Kennaway 	    c->ostate == CHAN_OUTPUT_WAIT_DRAIN) {
11184f52dfbbSDag-Erling Smørgrav 		if (sshbuf_len(c->output) > 0) {
11191323ec57SEd Maste 			c->io_want |= SSH_CHAN_IO_WFD;
1120a04a10f8SKris Kennaway 		} else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN) {
112180628bacSDag-Erling Smørgrav 			if (CHANNEL_EFD_OUTPUT_ACTIVE(c))
11224f52dfbbSDag-Erling Smørgrav 				debug2("channel %d: "
11234f52dfbbSDag-Erling Smørgrav 				    "obuf_empty delayed efd %d/(%zu)", c->self,
11244f52dfbbSDag-Erling Smørgrav 				    c->efd, sshbuf_len(c->extended));
112580628bacSDag-Erling Smørgrav 			else
11264f52dfbbSDag-Erling Smørgrav 				chan_obuf_empty(ssh, c);
1127a04a10f8SKris Kennaway 		}
1128a04a10f8SKris Kennaway 	}
1129a04a10f8SKris Kennaway 	/** XXX check close conditions, too */
11304f52dfbbSDag-Erling Smørgrav 	if (c->efd != -1 && !(c->istate == CHAN_INPUT_CLOSED &&
11314f52dfbbSDag-Erling Smørgrav 	    c->ostate == CHAN_OUTPUT_CLOSED)) {
1132a04a10f8SKris Kennaway 		if (c->extended_usage == CHAN_EXTENDED_WRITE &&
11334f52dfbbSDag-Erling Smørgrav 		    sshbuf_len(c->extended) > 0)
11341323ec57SEd Maste 			c->io_want |= SSH_CHAN_IO_EFD_W;
1135e2f6069cSDag-Erling Smørgrav 		else if (c->efd != -1 && !(c->flags & CHAN_EOF_SENT) &&
1136e2f6069cSDag-Erling Smørgrav 		    (c->extended_usage == CHAN_EXTENDED_READ ||
1137e2f6069cSDag-Erling Smørgrav 		    c->extended_usage == CHAN_EXTENDED_IGNORE) &&
11384f52dfbbSDag-Erling Smørgrav 		    sshbuf_len(c->extended) < c->remote_window)
11391323ec57SEd Maste 			c->io_want |= SSH_CHAN_IO_EFD_R;
1140a04a10f8SKris Kennaway 	}
114121e764dfSDag-Erling Smørgrav 	/* XXX: What about efd? races? */
1142a04a10f8SKris Kennaway }
1143a04a10f8SKris Kennaway 
1144a04a10f8SKris Kennaway /*
1145a04a10f8SKris Kennaway  * This is a special state for X11 authentication spoofing.  An opened X11
1146a04a10f8SKris Kennaway  * connection (when authentication spoofing is being done) remains in this
1147a04a10f8SKris Kennaway  * state until the first packet has been completely read.  The authentication
1148a04a10f8SKris Kennaway  * data in that packet is then substituted by the real data if it matches the
1149a04a10f8SKris Kennaway  * fake data, and the channel is put into normal mode.
1150a04a10f8SKris Kennaway  * XXX All this happens at the client side.
1151af12a3e7SDag-Erling Smørgrav  * Returns: 0 = need more data, -1 = wrong cookie, 1 = ok
1152a04a10f8SKris Kennaway  */
1153af12a3e7SDag-Erling Smørgrav static int
11544f52dfbbSDag-Erling Smørgrav x11_open_helper(struct ssh *ssh, struct sshbuf *b)
1155a04a10f8SKris Kennaway {
11564f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
1157ca3176e7SBrian Feldman 	u_char *ucp;
1158ca3176e7SBrian Feldman 	u_int proto_len, data_len;
1159511b41d2SMark Murray 
1160557f75e5SDag-Erling Smørgrav 	/* Is this being called after the refusal deadline? */
11614f52dfbbSDag-Erling Smørgrav 	if (sc->x11_refuse_time != 0 &&
11624f52dfbbSDag-Erling Smørgrav 	    (u_int)monotime() >= sc->x11_refuse_time) {
1163557f75e5SDag-Erling Smørgrav 		verbose("Rejected X11 connection after ForwardX11Timeout "
1164557f75e5SDag-Erling Smørgrav 		    "expired");
1165557f75e5SDag-Erling Smørgrav 		return -1;
1166557f75e5SDag-Erling Smørgrav 	}
1167557f75e5SDag-Erling Smørgrav 
1168511b41d2SMark Murray 	/* Check if the fixed size part of the packet is in buffer. */
11694f52dfbbSDag-Erling Smørgrav 	if (sshbuf_len(b) < 12)
1170a04a10f8SKris Kennaway 		return 0;
1171511b41d2SMark Murray 
1172511b41d2SMark Murray 	/* Parse the lengths of variable-length fields. */
11734f52dfbbSDag-Erling Smørgrav 	ucp = sshbuf_mutable_ptr(b);
1174511b41d2SMark Murray 	if (ucp[0] == 0x42) {	/* Byte order MSB first. */
1175511b41d2SMark Murray 		proto_len = 256 * ucp[6] + ucp[7];
1176511b41d2SMark Murray 		data_len = 256 * ucp[8] + ucp[9];
1177511b41d2SMark Murray 	} else if (ucp[0] == 0x6c) {	/* Byte order LSB first. */
1178511b41d2SMark Murray 		proto_len = ucp[6] + 256 * ucp[7];
1179511b41d2SMark Murray 		data_len = ucp[8] + 256 * ucp[9];
1180511b41d2SMark Murray 	} else {
1181221552e4SDag-Erling Smørgrav 		debug2("Initial X11 packet contains bad byte order byte: 0x%x",
1182511b41d2SMark Murray 		    ucp[0]);
1183a04a10f8SKris Kennaway 		return -1;
1184511b41d2SMark Murray 	}
1185511b41d2SMark Murray 
1186511b41d2SMark Murray 	/* Check if the whole packet is in buffer. */
11874f52dfbbSDag-Erling Smørgrav 	if (sshbuf_len(b) <
1188511b41d2SMark Murray 	    12 + ((proto_len + 3) & ~3) + ((data_len + 3) & ~3))
1189a04a10f8SKris Kennaway 		return 0;
1190511b41d2SMark Murray 
1191511b41d2SMark Murray 	/* Check if authentication protocol matches. */
11924f52dfbbSDag-Erling Smørgrav 	if (proto_len != strlen(sc->x11_saved_proto) ||
11934f52dfbbSDag-Erling Smørgrav 	    memcmp(ucp + 12, sc->x11_saved_proto, proto_len) != 0) {
1194221552e4SDag-Erling Smørgrav 		debug2("X11 connection uses different authentication protocol.");
1195a04a10f8SKris Kennaway 		return -1;
1196511b41d2SMark Murray 	}
1197511b41d2SMark Murray 	/* Check if authentication data matches our fake data. */
11984f52dfbbSDag-Erling Smørgrav 	if (data_len != sc->x11_fake_data_len ||
1199e2f6069cSDag-Erling Smørgrav 	    timingsafe_bcmp(ucp + 12 + ((proto_len + 3) & ~3),
12004f52dfbbSDag-Erling Smørgrav 		sc->x11_fake_data, sc->x11_fake_data_len) != 0) {
1201221552e4SDag-Erling Smørgrav 		debug2("X11 auth data does not match fake data.");
1202a04a10f8SKris Kennaway 		return -1;
1203511b41d2SMark Murray 	}
1204511b41d2SMark Murray 	/* Check fake data length */
12054f52dfbbSDag-Erling Smørgrav 	if (sc->x11_fake_data_len != sc->x11_saved_data_len) {
1206511b41d2SMark Murray 		error("X11 fake_data_len %d != saved_data_len %d",
12074f52dfbbSDag-Erling Smørgrav 		    sc->x11_fake_data_len, sc->x11_saved_data_len);
1208a04a10f8SKris Kennaway 		return -1;
1209511b41d2SMark Murray 	}
1210511b41d2SMark Murray 	/*
1211511b41d2SMark Murray 	 * Received authentication protocol and data match
1212511b41d2SMark Murray 	 * our fake data. Substitute the fake data with real
1213511b41d2SMark Murray 	 * data.
1214511b41d2SMark Murray 	 */
1215511b41d2SMark Murray 	memcpy(ucp + 12 + ((proto_len + 3) & ~3),
12164f52dfbbSDag-Erling Smørgrav 	    sc->x11_saved_data, sc->x11_saved_data_len);
1217a04a10f8SKris Kennaway 	return 1;
1218a04a10f8SKris Kennaway }
1219511b41d2SMark Murray 
1220af12a3e7SDag-Erling Smørgrav static void
12211323ec57SEd Maste channel_pre_x11_open(struct ssh *ssh, Channel *c)
1222a04a10f8SKris Kennaway {
12234f52dfbbSDag-Erling Smørgrav 	int ret = x11_open_helper(ssh, c->output);
1224af12a3e7SDag-Erling Smørgrav 
1225af12a3e7SDag-Erling Smørgrav 	/* c->force_drain = 1; */
1226af12a3e7SDag-Erling Smørgrav 
1227a04a10f8SKris Kennaway 	if (ret == 1) {
1228a04a10f8SKris Kennaway 		c->type = SSH_CHANNEL_OPEN;
12291323ec57SEd Maste 		channel_pre_open(ssh, c);
1230a04a10f8SKris Kennaway 	} else if (ret == -1) {
1231221552e4SDag-Erling Smørgrav 		logit("X11 connection rejected because of wrong authentication.");
12324f52dfbbSDag-Erling Smørgrav 		debug2("X11 rejected %d i%d/o%d",
12334f52dfbbSDag-Erling Smørgrav 		    c->self, c->istate, c->ostate);
12344f52dfbbSDag-Erling Smørgrav 		chan_read_failed(ssh, c);
12354f52dfbbSDag-Erling Smørgrav 		sshbuf_reset(c->input);
12364f52dfbbSDag-Erling Smørgrav 		chan_ibuf_empty(ssh, c);
12374f52dfbbSDag-Erling Smørgrav 		sshbuf_reset(c->output);
12384f52dfbbSDag-Erling Smørgrav 		chan_write_failed(ssh, c);
1239221552e4SDag-Erling Smørgrav 		debug2("X11 closed %d i%d/o%d", c->self, c->istate, c->ostate);
1240a04a10f8SKris Kennaway 	}
1241a04a10f8SKris Kennaway }
1242a04a10f8SKris Kennaway 
1243b15c8340SDag-Erling Smørgrav static void
12441323ec57SEd Maste channel_pre_mux_client(struct ssh *ssh, Channel *c)
1245b15c8340SDag-Erling Smørgrav {
12461323ec57SEd Maste 	c->io_want = 0;
1247e2f6069cSDag-Erling Smørgrav 	if (c->istate == CHAN_INPUT_OPEN && !c->mux_pause &&
12484f52dfbbSDag-Erling Smørgrav 	    sshbuf_check_reserve(c->input, CHAN_RBUF) == 0)
12491323ec57SEd Maste 		c->io_want |= SSH_CHAN_IO_RFD;
1250b15c8340SDag-Erling Smørgrav 	if (c->istate == CHAN_INPUT_WAIT_DRAIN) {
1251b15c8340SDag-Erling Smørgrav 		/* clear buffer immediately (discard any partial packet) */
12524f52dfbbSDag-Erling Smørgrav 		sshbuf_reset(c->input);
12534f52dfbbSDag-Erling Smørgrav 		chan_ibuf_empty(ssh, c);
1254b15c8340SDag-Erling Smørgrav 		/* Start output drain. XXX just kill chan? */
12554f52dfbbSDag-Erling Smørgrav 		chan_rcvd_oclose(ssh, c);
1256b15c8340SDag-Erling Smørgrav 	}
1257b15c8340SDag-Erling Smørgrav 	if (c->ostate == CHAN_OUTPUT_OPEN ||
1258b15c8340SDag-Erling Smørgrav 	    c->ostate == CHAN_OUTPUT_WAIT_DRAIN) {
12594f52dfbbSDag-Erling Smørgrav 		if (sshbuf_len(c->output) > 0)
12601323ec57SEd Maste 			c->io_want |= SSH_CHAN_IO_WFD;
1261b15c8340SDag-Erling Smørgrav 		else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN)
12624f52dfbbSDag-Erling Smørgrav 			chan_obuf_empty(ssh, c);
1263b15c8340SDag-Erling Smørgrav 	}
1264b15c8340SDag-Erling Smørgrav }
1265b15c8340SDag-Erling Smørgrav 
1266ca3176e7SBrian Feldman /* try to decode a socks4 header */
1267af12a3e7SDag-Erling Smørgrav static int
12684f52dfbbSDag-Erling Smørgrav channel_decode_socks4(Channel *c, struct sshbuf *input, struct sshbuf *output)
1269ca3176e7SBrian Feldman {
12704f52dfbbSDag-Erling Smørgrav 	const u_char *p;
12714f52dfbbSDag-Erling Smørgrav 	char *host;
1272cce7d346SDag-Erling Smørgrav 	u_int len, have, i, found, need;
1273ca3176e7SBrian Feldman 	char username[256];
1274ca3176e7SBrian Feldman 	struct {
1275ca3176e7SBrian Feldman 		u_int8_t version;
1276ca3176e7SBrian Feldman 		u_int8_t command;
1277ca3176e7SBrian Feldman 		u_int16_t dest_port;
1278ca3176e7SBrian Feldman 		struct in_addr dest_addr;
1279ca3176e7SBrian Feldman 	} s4_req, s4_rsp;
12804f52dfbbSDag-Erling Smørgrav 	int r;
1281ca3176e7SBrian Feldman 
1282ca3176e7SBrian Feldman 	debug2("channel %d: decode socks4", c->self);
1283ca3176e7SBrian Feldman 
12844f52dfbbSDag-Erling Smørgrav 	have = sshbuf_len(input);
1285ca3176e7SBrian Feldman 	len = sizeof(s4_req);
1286ca3176e7SBrian Feldman 	if (have < len)
1287ca3176e7SBrian Feldman 		return 0;
12884f52dfbbSDag-Erling Smørgrav 	p = sshbuf_ptr(input);
1289cce7d346SDag-Erling Smørgrav 
1290cce7d346SDag-Erling Smørgrav 	need = 1;
1291cce7d346SDag-Erling Smørgrav 	/* SOCKS4A uses an invalid IP address 0.0.0.x */
1292cce7d346SDag-Erling Smørgrav 	if (p[4] == 0 && p[5] == 0 && p[6] == 0 && p[7] != 0) {
1293cce7d346SDag-Erling Smørgrav 		debug2("channel %d: socks4a request", c->self);
1294cce7d346SDag-Erling Smørgrav 		/* ... and needs an extra string (the hostname) */
1295cce7d346SDag-Erling Smørgrav 		need = 2;
1296cce7d346SDag-Erling Smørgrav 	}
1297cce7d346SDag-Erling Smørgrav 	/* Check for terminating NUL on the string(s) */
1298ca3176e7SBrian Feldman 	for (found = 0, i = len; i < have; i++) {
1299ca3176e7SBrian Feldman 		if (p[i] == '\0') {
1300cce7d346SDag-Erling Smørgrav 			found++;
1301cce7d346SDag-Erling Smørgrav 			if (found == need)
1302ca3176e7SBrian Feldman 				break;
1303ca3176e7SBrian Feldman 		}
1304ca3176e7SBrian Feldman 		if (i > 1024) {
1305ca3176e7SBrian Feldman 			/* the peer is probably sending garbage */
1306ca3176e7SBrian Feldman 			debug("channel %d: decode socks4: too long",
1307ca3176e7SBrian Feldman 			    c->self);
1308ca3176e7SBrian Feldman 			return -1;
1309ca3176e7SBrian Feldman 		}
1310ca3176e7SBrian Feldman 	}
1311cce7d346SDag-Erling Smørgrav 	if (found < need)
1312ca3176e7SBrian Feldman 		return 0;
13134f52dfbbSDag-Erling Smørgrav 	if ((r = sshbuf_get(input, &s4_req.version, 1)) != 0 ||
13144f52dfbbSDag-Erling Smørgrav 	    (r = sshbuf_get(input, &s4_req.command, 1)) != 0 ||
13154f52dfbbSDag-Erling Smørgrav 	    (r = sshbuf_get(input, &s4_req.dest_port, 2)) != 0 ||
13164f52dfbbSDag-Erling Smørgrav 	    (r = sshbuf_get(input, &s4_req.dest_addr, 4)) != 0) {
131719261079SEd Maste 		debug_r(r, "channels %d: decode socks4", c->self);
13184f52dfbbSDag-Erling Smørgrav 		return -1;
13194f52dfbbSDag-Erling Smørgrav 	}
13204f52dfbbSDag-Erling Smørgrav 	have = sshbuf_len(input);
13214f52dfbbSDag-Erling Smørgrav 	p = sshbuf_ptr(input);
13224f52dfbbSDag-Erling Smørgrav 	if (memchr(p, '\0', have) == NULL) {
132319261079SEd Maste 		error("channel %d: decode socks4: unterminated user", c->self);
13244f52dfbbSDag-Erling Smørgrav 		return -1;
13254f52dfbbSDag-Erling Smørgrav 	}
1326ca3176e7SBrian Feldman 	len = strlen(p);
1327ca3176e7SBrian Feldman 	debug2("channel %d: decode socks4: user %s/%d", c->self, p, len);
1328cce7d346SDag-Erling Smørgrav 	len++; /* trailing '\0' */
1329ca3176e7SBrian Feldman 	strlcpy(username, p, sizeof(username));
133019261079SEd Maste 	if ((r = sshbuf_consume(input, len)) != 0)
133119261079SEd Maste 		fatal_fr(r, "channel %d: consume", c->self);
1332e4a9863fSDag-Erling Smørgrav 	free(c->path);
1333cce7d346SDag-Erling Smørgrav 	c->path = NULL;
1334cce7d346SDag-Erling Smørgrav 	if (need == 1) {			/* SOCKS4: one string */
1335ca3176e7SBrian Feldman 		host = inet_ntoa(s4_req.dest_addr);
1336cce7d346SDag-Erling Smørgrav 		c->path = xstrdup(host);
1337cce7d346SDag-Erling Smørgrav 	} else {				/* SOCKS4A: two strings */
13384f52dfbbSDag-Erling Smørgrav 		have = sshbuf_len(input);
13394f52dfbbSDag-Erling Smørgrav 		p = sshbuf_ptr(input);
13404f52dfbbSDag-Erling Smørgrav 		if (memchr(p, '\0', have) == NULL) {
13414f52dfbbSDag-Erling Smørgrav 			error("channel %d: decode socks4a: host not nul "
13424f52dfbbSDag-Erling Smørgrav 			    "terminated", c->self);
13434f52dfbbSDag-Erling Smørgrav 			return -1;
13444f52dfbbSDag-Erling Smørgrav 		}
1345cce7d346SDag-Erling Smørgrav 		len = strlen(p);
1346cce7d346SDag-Erling Smørgrav 		debug2("channel %d: decode socks4a: host %s/%d",
1347cce7d346SDag-Erling Smørgrav 		    c->self, p, len);
1348cce7d346SDag-Erling Smørgrav 		len++;				/* trailing '\0' */
1349cce7d346SDag-Erling Smørgrav 		if (len > NI_MAXHOST) {
1350cce7d346SDag-Erling Smørgrav 			error("channel %d: hostname \"%.100s\" too long",
1351cce7d346SDag-Erling Smørgrav 			    c->self, p);
1352cce7d346SDag-Erling Smørgrav 			return -1;
1353cce7d346SDag-Erling Smørgrav 		}
1354cce7d346SDag-Erling Smørgrav 		c->path = xstrdup(p);
135519261079SEd Maste 		if ((r = sshbuf_consume(input, len)) != 0)
135619261079SEd Maste 			fatal_fr(r, "channel %d: consume", c->self);
1357cce7d346SDag-Erling Smørgrav 	}
1358ca3176e7SBrian Feldman 	c->host_port = ntohs(s4_req.dest_port);
1359ca3176e7SBrian Feldman 
1360221552e4SDag-Erling Smørgrav 	debug2("channel %d: dynamic request: socks4 host %s port %u command %u",
1361cce7d346SDag-Erling Smørgrav 	    c->self, c->path, c->host_port, s4_req.command);
1362ca3176e7SBrian Feldman 
1363ca3176e7SBrian Feldman 	if (s4_req.command != 1) {
1364cce7d346SDag-Erling Smørgrav 		debug("channel %d: cannot handle: %s cn %d",
1365cce7d346SDag-Erling Smørgrav 		    c->self, need == 1 ? "SOCKS4" : "SOCKS4A", s4_req.command);
1366ca3176e7SBrian Feldman 		return -1;
1367ca3176e7SBrian Feldman 	}
1368ca3176e7SBrian Feldman 	s4_rsp.version = 0;			/* vn: 0 for reply */
1369ca3176e7SBrian Feldman 	s4_rsp.command = 90;			/* cd: req granted */
1370ca3176e7SBrian Feldman 	s4_rsp.dest_port = 0;			/* ignored */
1371ca3176e7SBrian Feldman 	s4_rsp.dest_addr.s_addr = INADDR_ANY;	/* ignored */
137219261079SEd Maste 	if ((r = sshbuf_put(output, &s4_rsp, sizeof(s4_rsp))) != 0)
137319261079SEd Maste 		fatal_fr(r, "channel %d: append reply", c->self);
1374ca3176e7SBrian Feldman 	return 1;
1375ca3176e7SBrian Feldman }
1376ca3176e7SBrian Feldman 
1377221552e4SDag-Erling Smørgrav /* try to decode a socks5 header */
1378221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_AUTHDONE	0x1000
1379221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_NOAUTH	0x00
1380221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_IPV4		0x01
1381221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_DOMAIN	0x03
1382221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_IPV6		0x04
1383221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_CONNECT	0x01
1384221552e4SDag-Erling Smørgrav #define SSH_SOCKS5_SUCCESS	0x00
1385221552e4SDag-Erling Smørgrav 
1386221552e4SDag-Erling Smørgrav static int
13874f52dfbbSDag-Erling Smørgrav channel_decode_socks5(Channel *c, struct sshbuf *input, struct sshbuf *output)
1388221552e4SDag-Erling Smørgrav {
13894f52dfbbSDag-Erling Smørgrav 	/* XXX use get/put_u8 instead of trusting struct padding */
1390221552e4SDag-Erling Smørgrav 	struct {
1391221552e4SDag-Erling Smørgrav 		u_int8_t version;
1392221552e4SDag-Erling Smørgrav 		u_int8_t command;
1393221552e4SDag-Erling Smørgrav 		u_int8_t reserved;
1394221552e4SDag-Erling Smørgrav 		u_int8_t atyp;
1395221552e4SDag-Erling Smørgrav 	} s5_req, s5_rsp;
1396221552e4SDag-Erling Smørgrav 	u_int16_t dest_port;
1397e4a9863fSDag-Erling Smørgrav 	char dest_addr[255+1], ntop[INET6_ADDRSTRLEN];
13984f52dfbbSDag-Erling Smørgrav 	const u_char *p;
1399333ee039SDag-Erling Smørgrav 	u_int have, need, i, found, nmethods, addrlen, af;
14004f52dfbbSDag-Erling Smørgrav 	int r;
1401221552e4SDag-Erling Smørgrav 
1402221552e4SDag-Erling Smørgrav 	debug2("channel %d: decode socks5", c->self);
14034f52dfbbSDag-Erling Smørgrav 	p = sshbuf_ptr(input);
1404221552e4SDag-Erling Smørgrav 	if (p[0] != 0x05)
1405221552e4SDag-Erling Smørgrav 		return -1;
14064f52dfbbSDag-Erling Smørgrav 	have = sshbuf_len(input);
1407221552e4SDag-Erling Smørgrav 	if (!(c->flags & SSH_SOCKS5_AUTHDONE)) {
1408221552e4SDag-Erling Smørgrav 		/* format: ver | nmethods | methods */
1409221552e4SDag-Erling Smørgrav 		if (have < 2)
1410221552e4SDag-Erling Smørgrav 			return 0;
1411221552e4SDag-Erling Smørgrav 		nmethods = p[1];
1412221552e4SDag-Erling Smørgrav 		if (have < nmethods + 2)
1413221552e4SDag-Erling Smørgrav 			return 0;
1414221552e4SDag-Erling Smørgrav 		/* look for method: "NO AUTHENTICATION REQUIRED" */
1415221552e4SDag-Erling Smørgrav 		for (found = 0, i = 2; i < nmethods + 2; i++) {
1416221552e4SDag-Erling Smørgrav 			if (p[i] == SSH_SOCKS5_NOAUTH) {
1417221552e4SDag-Erling Smørgrav 				found = 1;
1418221552e4SDag-Erling Smørgrav 				break;
1419221552e4SDag-Erling Smørgrav 			}
1420221552e4SDag-Erling Smørgrav 		}
1421221552e4SDag-Erling Smørgrav 		if (!found) {
1422221552e4SDag-Erling Smørgrav 			debug("channel %d: method SSH_SOCKS5_NOAUTH not found",
1423221552e4SDag-Erling Smørgrav 			    c->self);
1424221552e4SDag-Erling Smørgrav 			return -1;
1425221552e4SDag-Erling Smørgrav 		}
142619261079SEd Maste 		if ((r = sshbuf_consume(input, nmethods + 2)) != 0)
142719261079SEd Maste 			fatal_fr(r, "channel %d: consume", c->self);
14284f52dfbbSDag-Erling Smørgrav 		/* version, method */
14294f52dfbbSDag-Erling Smørgrav 		if ((r = sshbuf_put_u8(output, 0x05)) != 0 ||
143019261079SEd Maste 		    (r = sshbuf_put_u8(output, SSH_SOCKS5_NOAUTH)) != 0)
143119261079SEd Maste 			fatal_fr(r, "channel %d: append reply", c->self);
1432221552e4SDag-Erling Smørgrav 		c->flags |= SSH_SOCKS5_AUTHDONE;
1433221552e4SDag-Erling Smørgrav 		debug2("channel %d: socks5 auth done", c->self);
1434221552e4SDag-Erling Smørgrav 		return 0;				/* need more */
1435221552e4SDag-Erling Smørgrav 	}
1436221552e4SDag-Erling Smørgrav 	debug2("channel %d: socks5 post auth", c->self);
1437221552e4SDag-Erling Smørgrav 	if (have < sizeof(s5_req)+1)
1438221552e4SDag-Erling Smørgrav 		return 0;			/* need more */
1439333ee039SDag-Erling Smørgrav 	memcpy(&s5_req, p, sizeof(s5_req));
1440221552e4SDag-Erling Smørgrav 	if (s5_req.version != 0x05 ||
1441221552e4SDag-Erling Smørgrav 	    s5_req.command != SSH_SOCKS5_CONNECT ||
1442221552e4SDag-Erling Smørgrav 	    s5_req.reserved != 0x00) {
1443221552e4SDag-Erling Smørgrav 		debug2("channel %d: only socks5 connect supported", c->self);
1444221552e4SDag-Erling Smørgrav 		return -1;
1445221552e4SDag-Erling Smørgrav 	}
1446221552e4SDag-Erling Smørgrav 	switch (s5_req.atyp){
1447221552e4SDag-Erling Smørgrav 	case SSH_SOCKS5_IPV4:
1448221552e4SDag-Erling Smørgrav 		addrlen = 4;
1449221552e4SDag-Erling Smørgrav 		af = AF_INET;
1450221552e4SDag-Erling Smørgrav 		break;
1451221552e4SDag-Erling Smørgrav 	case SSH_SOCKS5_DOMAIN:
1452221552e4SDag-Erling Smørgrav 		addrlen = p[sizeof(s5_req)];
1453221552e4SDag-Erling Smørgrav 		af = -1;
1454221552e4SDag-Erling Smørgrav 		break;
1455221552e4SDag-Erling Smørgrav 	case SSH_SOCKS5_IPV6:
1456221552e4SDag-Erling Smørgrav 		addrlen = 16;
1457221552e4SDag-Erling Smørgrav 		af = AF_INET6;
1458221552e4SDag-Erling Smørgrav 		break;
1459221552e4SDag-Erling Smørgrav 	default:
1460221552e4SDag-Erling Smørgrav 		debug2("channel %d: bad socks5 atyp %d", c->self, s5_req.atyp);
1461221552e4SDag-Erling Smørgrav 		return -1;
1462221552e4SDag-Erling Smørgrav 	}
1463333ee039SDag-Erling Smørgrav 	need = sizeof(s5_req) + addrlen + 2;
1464333ee039SDag-Erling Smørgrav 	if (s5_req.atyp == SSH_SOCKS5_DOMAIN)
1465333ee039SDag-Erling Smørgrav 		need++;
1466333ee039SDag-Erling Smørgrav 	if (have < need)
1467221552e4SDag-Erling Smørgrav 		return 0;
146819261079SEd Maste 	if ((r = sshbuf_consume(input, sizeof(s5_req))) != 0)
146919261079SEd Maste 		fatal_fr(r, "channel %d: consume", c->self);
14704f52dfbbSDag-Erling Smørgrav 	if (s5_req.atyp == SSH_SOCKS5_DOMAIN) {
14714f52dfbbSDag-Erling Smørgrav 		/* host string length */
147219261079SEd Maste 		if ((r = sshbuf_consume(input, 1)) != 0)
147319261079SEd Maste 			fatal_fr(r, "channel %d: consume", c->self);
14744f52dfbbSDag-Erling Smørgrav 	}
14754f52dfbbSDag-Erling Smørgrav 	if ((r = sshbuf_get(input, &dest_addr, addrlen)) != 0 ||
14764f52dfbbSDag-Erling Smørgrav 	    (r = sshbuf_get(input, &dest_port, 2)) != 0) {
147719261079SEd Maste 		debug_r(r, "channel %d: parse addr/port", c->self);
14784f52dfbbSDag-Erling Smørgrav 		return -1;
14794f52dfbbSDag-Erling Smørgrav 	}
1480221552e4SDag-Erling Smørgrav 	dest_addr[addrlen] = '\0';
1481e4a9863fSDag-Erling Smørgrav 	free(c->path);
1482cce7d346SDag-Erling Smørgrav 	c->path = NULL;
1483cce7d346SDag-Erling Smørgrav 	if (s5_req.atyp == SSH_SOCKS5_DOMAIN) {
1484cce7d346SDag-Erling Smørgrav 		if (addrlen >= NI_MAXHOST) {
1485cce7d346SDag-Erling Smørgrav 			error("channel %d: dynamic request: socks5 hostname "
1486cce7d346SDag-Erling Smørgrav 			    "\"%.100s\" too long", c->self, dest_addr);
1487221552e4SDag-Erling Smørgrav 			return -1;
1488cce7d346SDag-Erling Smørgrav 		}
1489cce7d346SDag-Erling Smørgrav 		c->path = xstrdup(dest_addr);
1490cce7d346SDag-Erling Smørgrav 	} else {
1491cce7d346SDag-Erling Smørgrav 		if (inet_ntop(af, dest_addr, ntop, sizeof(ntop)) == NULL)
1492cce7d346SDag-Erling Smørgrav 			return -1;
1493cce7d346SDag-Erling Smørgrav 		c->path = xstrdup(ntop);
1494cce7d346SDag-Erling Smørgrav 	}
1495221552e4SDag-Erling Smørgrav 	c->host_port = ntohs(dest_port);
1496221552e4SDag-Erling Smørgrav 
1497221552e4SDag-Erling Smørgrav 	debug2("channel %d: dynamic request: socks5 host %s port %u command %u",
1498221552e4SDag-Erling Smørgrav 	    c->self, c->path, c->host_port, s5_req.command);
1499221552e4SDag-Erling Smørgrav 
1500221552e4SDag-Erling Smørgrav 	s5_rsp.version = 0x05;
1501221552e4SDag-Erling Smørgrav 	s5_rsp.command = SSH_SOCKS5_SUCCESS;
1502221552e4SDag-Erling Smørgrav 	s5_rsp.reserved = 0;			/* ignored */
1503221552e4SDag-Erling Smørgrav 	s5_rsp.atyp = SSH_SOCKS5_IPV4;
1504221552e4SDag-Erling Smørgrav 	dest_port = 0;				/* ignored */
1505221552e4SDag-Erling Smørgrav 
15064f52dfbbSDag-Erling Smørgrav 	if ((r = sshbuf_put(output, &s5_rsp, sizeof(s5_rsp))) != 0 ||
15074f52dfbbSDag-Erling Smørgrav 	    (r = sshbuf_put_u32(output, ntohl(INADDR_ANY))) != 0 ||
15084f52dfbbSDag-Erling Smørgrav 	    (r = sshbuf_put(output, &dest_port, sizeof(dest_port))) != 0)
150919261079SEd Maste 		fatal_fr(r, "channel %d: append reply", c->self);
1510221552e4SDag-Erling Smørgrav 	return 1;
1511221552e4SDag-Erling Smørgrav }
1512221552e4SDag-Erling Smørgrav 
1513b15c8340SDag-Erling Smørgrav Channel *
15144f52dfbbSDag-Erling Smørgrav channel_connect_stdio_fwd(struct ssh *ssh,
151519261079SEd Maste     const char *host_to_connect, u_short port_to_connect,
151619261079SEd Maste     int in, int out, int nonblock)
1517b15c8340SDag-Erling Smørgrav {
1518b15c8340SDag-Erling Smørgrav 	Channel *c;
1519b15c8340SDag-Erling Smørgrav 
152019261079SEd Maste 	debug_f("%s:%d", host_to_connect, port_to_connect);
1521b15c8340SDag-Erling Smørgrav 
15224f52dfbbSDag-Erling Smørgrav 	c = channel_new(ssh, "stdio-forward", SSH_CHANNEL_OPENING, in, out,
1523b15c8340SDag-Erling Smørgrav 	    -1, CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT,
152419261079SEd Maste 	    0, "stdio-forward", nonblock);
1525b15c8340SDag-Erling Smørgrav 
1526b15c8340SDag-Erling Smørgrav 	c->path = xstrdup(host_to_connect);
1527b15c8340SDag-Erling Smørgrav 	c->host_port = port_to_connect;
1528b15c8340SDag-Erling Smørgrav 	c->listening_port = 0;
1529b15c8340SDag-Erling Smørgrav 	c->force_drain = 1;
1530b15c8340SDag-Erling Smørgrav 
15314f52dfbbSDag-Erling Smørgrav 	channel_register_fds(ssh, c, in, out, -1, 0, 1, 0);
15324f52dfbbSDag-Erling Smørgrav 	port_open_helper(ssh, c, "direct-tcpip");
1533b15c8340SDag-Erling Smørgrav 
1534b15c8340SDag-Erling Smørgrav 	return c;
1535b15c8340SDag-Erling Smørgrav }
1536b15c8340SDag-Erling Smørgrav 
1537ca3176e7SBrian Feldman /* dynamic port forwarding */
1538af12a3e7SDag-Erling Smørgrav static void
15391323ec57SEd Maste channel_pre_dynamic(struct ssh *ssh, Channel *c)
1540ca3176e7SBrian Feldman {
15414f52dfbbSDag-Erling Smørgrav 	const u_char *p;
1542d4ecd108SDag-Erling Smørgrav 	u_int have;
1543d4ecd108SDag-Erling Smørgrav 	int ret;
1544ca3176e7SBrian Feldman 
15451323ec57SEd Maste 	c->io_want = 0;
15464f52dfbbSDag-Erling Smørgrav 	have = sshbuf_len(c->input);
1547ca3176e7SBrian Feldman 	debug2("channel %d: pre_dynamic: have %d", c->self, have);
15484f52dfbbSDag-Erling Smørgrav 	/* sshbuf_dump(c->input, stderr); */
1549ca3176e7SBrian Feldman 	/* check if the fixed size part of the packet is in buffer. */
1550221552e4SDag-Erling Smørgrav 	if (have < 3) {
1551ca3176e7SBrian Feldman 		/* need more */
15521323ec57SEd Maste 		c->io_want |= SSH_CHAN_IO_RFD;
1553ca3176e7SBrian Feldman 		return;
1554ca3176e7SBrian Feldman 	}
1555ca3176e7SBrian Feldman 	/* try to guess the protocol */
15564f52dfbbSDag-Erling Smørgrav 	p = sshbuf_ptr(c->input);
15574f52dfbbSDag-Erling Smørgrav 	/* XXX sshbuf_peek_u8? */
1558ca3176e7SBrian Feldman 	switch (p[0]) {
1559ca3176e7SBrian Feldman 	case 0x04:
15604f52dfbbSDag-Erling Smørgrav 		ret = channel_decode_socks4(c, c->input, c->output);
1561ca3176e7SBrian Feldman 		break;
1562221552e4SDag-Erling Smørgrav 	case 0x05:
15634f52dfbbSDag-Erling Smørgrav 		ret = channel_decode_socks5(c, c->input, c->output);
1564221552e4SDag-Erling Smørgrav 		break;
1565ca3176e7SBrian Feldman 	default:
1566ca3176e7SBrian Feldman 		ret = -1;
1567ca3176e7SBrian Feldman 		break;
1568ca3176e7SBrian Feldman 	}
1569ca3176e7SBrian Feldman 	if (ret < 0) {
15704f52dfbbSDag-Erling Smørgrav 		chan_mark_dead(ssh, c);
1571ca3176e7SBrian Feldman 	} else if (ret == 0) {
1572ca3176e7SBrian Feldman 		debug2("channel %d: pre_dynamic: need more", c->self);
1573ca3176e7SBrian Feldman 		/* need more */
15741323ec57SEd Maste 		c->io_want |= SSH_CHAN_IO_RFD;
15754f52dfbbSDag-Erling Smørgrav 		if (sshbuf_len(c->output))
15761323ec57SEd Maste 			c->io_want |= SSH_CHAN_IO_WFD;
1577ca3176e7SBrian Feldman 	} else {
1578ca3176e7SBrian Feldman 		/* switch to the next state */
1579ca3176e7SBrian Feldman 		c->type = SSH_CHANNEL_OPENING;
15804f52dfbbSDag-Erling Smørgrav 		port_open_helper(ssh, c, "direct-tcpip");
15814f52dfbbSDag-Erling Smørgrav 	}
15824f52dfbbSDag-Erling Smørgrav }
15834f52dfbbSDag-Erling Smørgrav 
15844f52dfbbSDag-Erling Smørgrav /* simulate read-error */
15854f52dfbbSDag-Erling Smørgrav static void
15864f52dfbbSDag-Erling Smørgrav rdynamic_close(struct ssh *ssh, Channel *c)
15874f52dfbbSDag-Erling Smørgrav {
15884f52dfbbSDag-Erling Smørgrav 	c->type = SSH_CHANNEL_OPEN;
15894f52dfbbSDag-Erling Smørgrav 	chan_read_failed(ssh, c);
15904f52dfbbSDag-Erling Smørgrav 	sshbuf_reset(c->input);
15914f52dfbbSDag-Erling Smørgrav 	chan_ibuf_empty(ssh, c);
15924f52dfbbSDag-Erling Smørgrav 	sshbuf_reset(c->output);
15934f52dfbbSDag-Erling Smørgrav 	chan_write_failed(ssh, c);
15944f52dfbbSDag-Erling Smørgrav }
15954f52dfbbSDag-Erling Smørgrav 
15964f52dfbbSDag-Erling Smørgrav /* reverse dynamic port forwarding */
15974f52dfbbSDag-Erling Smørgrav static void
15981323ec57SEd Maste channel_before_prepare_io_rdynamic(struct ssh *ssh, Channel *c)
15994f52dfbbSDag-Erling Smørgrav {
16004f52dfbbSDag-Erling Smørgrav 	const u_char *p;
16014f52dfbbSDag-Erling Smørgrav 	u_int have, len;
16024f52dfbbSDag-Erling Smørgrav 	int r, ret;
16034f52dfbbSDag-Erling Smørgrav 
16044f52dfbbSDag-Erling Smørgrav 	have = sshbuf_len(c->output);
16054f52dfbbSDag-Erling Smørgrav 	debug2("channel %d: pre_rdynamic: have %d", c->self, have);
16064f52dfbbSDag-Erling Smørgrav 	/* sshbuf_dump(c->output, stderr); */
16074f52dfbbSDag-Erling Smørgrav 	/* EOF received */
16084f52dfbbSDag-Erling Smørgrav 	if (c->flags & CHAN_EOF_RCVD) {
160919261079SEd Maste 		if ((r = sshbuf_consume(c->output, have)) != 0)
161019261079SEd Maste 			fatal_fr(r, "channel %d: consume", c->self);
16114f52dfbbSDag-Erling Smørgrav 		rdynamic_close(ssh, c);
16124f52dfbbSDag-Erling Smørgrav 		return;
16134f52dfbbSDag-Erling Smørgrav 	}
16144f52dfbbSDag-Erling Smørgrav 	/* check if the fixed size part of the packet is in buffer. */
16154f52dfbbSDag-Erling Smørgrav 	if (have < 3)
16164f52dfbbSDag-Erling Smørgrav 		return;
16174f52dfbbSDag-Erling Smørgrav 	/* try to guess the protocol */
16184f52dfbbSDag-Erling Smørgrav 	p = sshbuf_ptr(c->output);
16194f52dfbbSDag-Erling Smørgrav 	switch (p[0]) {
16204f52dfbbSDag-Erling Smørgrav 	case 0x04:
16214f52dfbbSDag-Erling Smørgrav 		/* switch input/output for reverse forwarding */
16224f52dfbbSDag-Erling Smørgrav 		ret = channel_decode_socks4(c, c->output, c->input);
16234f52dfbbSDag-Erling Smørgrav 		break;
16244f52dfbbSDag-Erling Smørgrav 	case 0x05:
16254f52dfbbSDag-Erling Smørgrav 		ret = channel_decode_socks5(c, c->output, c->input);
16264f52dfbbSDag-Erling Smørgrav 		break;
16274f52dfbbSDag-Erling Smørgrav 	default:
16284f52dfbbSDag-Erling Smørgrav 		ret = -1;
16294f52dfbbSDag-Erling Smørgrav 		break;
16304f52dfbbSDag-Erling Smørgrav 	}
16314f52dfbbSDag-Erling Smørgrav 	if (ret < 0) {
16324f52dfbbSDag-Erling Smørgrav 		rdynamic_close(ssh, c);
16334f52dfbbSDag-Erling Smørgrav 	} else if (ret == 0) {
16344f52dfbbSDag-Erling Smørgrav 		debug2("channel %d: pre_rdynamic: need more", c->self);
16354f52dfbbSDag-Erling Smørgrav 		/* send socks request to peer */
16364f52dfbbSDag-Erling Smørgrav 		len = sshbuf_len(c->input);
16374f52dfbbSDag-Erling Smørgrav 		if (len > 0 && len < c->remote_window) {
16384f52dfbbSDag-Erling Smørgrav 			if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_DATA)) != 0 ||
16394f52dfbbSDag-Erling Smørgrav 			    (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
16404f52dfbbSDag-Erling Smørgrav 			    (r = sshpkt_put_stringb(ssh, c->input)) != 0 ||
16414f52dfbbSDag-Erling Smørgrav 			    (r = sshpkt_send(ssh)) != 0) {
164219261079SEd Maste 				fatal_fr(r, "channel %i: rdynamic", c->self);
16434f52dfbbSDag-Erling Smørgrav 			}
164419261079SEd Maste 			if ((r = sshbuf_consume(c->input, len)) != 0)
164519261079SEd Maste 				fatal_fr(r, "channel %d: consume", c->self);
16464f52dfbbSDag-Erling Smørgrav 			c->remote_window -= len;
16474f52dfbbSDag-Erling Smørgrav 		}
16484f52dfbbSDag-Erling Smørgrav 	} else if (rdynamic_connect_finish(ssh, c) < 0) {
16494f52dfbbSDag-Erling Smørgrav 		/* the connect failed */
16504f52dfbbSDag-Erling Smørgrav 		rdynamic_close(ssh, c);
1651ca3176e7SBrian Feldman 	}
1652ca3176e7SBrian Feldman }
1653ca3176e7SBrian Feldman 
1654a04a10f8SKris Kennaway /* This is our fake X11 server socket. */
1655af12a3e7SDag-Erling Smørgrav static void
16561323ec57SEd Maste channel_post_x11_listener(struct ssh *ssh, Channel *c)
1657511b41d2SMark Murray {
1658af12a3e7SDag-Erling Smørgrav 	Channel *nc;
1659d4af9e69SDag-Erling Smørgrav 	struct sockaddr_storage addr;
16604f52dfbbSDag-Erling Smørgrav 	int r, newsock, oerrno, remote_port;
1661511b41d2SMark Murray 	socklen_t addrlen;
1662ca3176e7SBrian Feldman 	char buf[16384], *remote_ipaddr;
1663511b41d2SMark Murray 
16641323ec57SEd Maste 	if ((c->io_ready & SSH_CHAN_IO_SOCK_R) == 0)
16654f52dfbbSDag-Erling Smørgrav 		return;
16664f52dfbbSDag-Erling Smørgrav 
1667511b41d2SMark Murray 	debug("X11 connection requested.");
1668511b41d2SMark Murray 	addrlen = sizeof(addr);
1669d4af9e69SDag-Erling Smørgrav 	newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen);
1670af12a3e7SDag-Erling Smørgrav 	if (c->single_connection) {
1671e4a9863fSDag-Erling Smørgrav 		oerrno = errno;
1672221552e4SDag-Erling Smørgrav 		debug2("single_connection: closing X11 listener.");
167319261079SEd Maste 		channel_close_fd(ssh, c, &c->sock);
16744f52dfbbSDag-Erling Smørgrav 		chan_mark_dead(ssh, c);
1675e4a9863fSDag-Erling Smørgrav 		errno = oerrno;
1676af12a3e7SDag-Erling Smørgrav 	}
167719261079SEd Maste 	if (newsock == -1) {
1678e4a9863fSDag-Erling Smørgrav 		if (errno != EINTR && errno != EWOULDBLOCK &&
1679e4a9863fSDag-Erling Smørgrav 		    errno != ECONNABORTED)
1680511b41d2SMark Murray 			error("accept: %.100s", strerror(errno));
1681462c32cbSDag-Erling Smørgrav 		if (errno == EMFILE || errno == ENFILE)
1682e4a9863fSDag-Erling Smørgrav 			c->notbefore = monotime() + 1;
1683a04a10f8SKris Kennaway 		return;
1684511b41d2SMark Murray 	}
1685af12a3e7SDag-Erling Smørgrav 	set_nodelay(newsock);
1686ca3176e7SBrian Feldman 	remote_ipaddr = get_peer_ipaddr(newsock);
1687a04a10f8SKris Kennaway 	remote_port = get_peer_port(newsock);
1688511b41d2SMark Murray 	snprintf(buf, sizeof buf, "X11 connection from %.200s port %d",
1689ca3176e7SBrian Feldman 	    remote_ipaddr, remote_port);
1690a04a10f8SKris Kennaway 
16914f52dfbbSDag-Erling Smørgrav 	nc = channel_new(ssh, "accepted x11 socket",
1692a04a10f8SKris Kennaway 	    SSH_CHANNEL_OPENING, newsock, newsock, -1,
1693221552e4SDag-Erling Smørgrav 	    c->local_window_max, c->local_maxpacket, 0, buf, 1);
16944f52dfbbSDag-Erling Smørgrav 	open_preamble(ssh, __func__, nc, "x11");
169547dd1d1bSDag-Erling Smørgrav 	if ((r = sshpkt_put_cstring(ssh, remote_ipaddr)) != 0 ||
169647dd1d1bSDag-Erling Smørgrav 	    (r = sshpkt_put_u32(ssh, remote_port)) != 0) {
169719261079SEd Maste 		fatal_fr(r, "channel %i: reply", c->self);
1698511b41d2SMark Murray 	}
16994f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_send(ssh)) != 0)
170019261079SEd Maste 		fatal_fr(r, "channel %i: send", c->self);
1701e4a9863fSDag-Erling Smørgrav 	free(remote_ipaddr);
1702a04a10f8SKris Kennaway }
1703511b41d2SMark Murray 
1704af12a3e7SDag-Erling Smørgrav static void
17054f52dfbbSDag-Erling Smørgrav port_open_helper(struct ssh *ssh, Channel *c, char *rtype)
1706ca3176e7SBrian Feldman {
1707f7167e0eSDag-Erling Smørgrav 	char *local_ipaddr = get_local_ipaddr(c->sock);
1708076ad2f8SDag-Erling Smørgrav 	int local_port = c->sock == -1 ? 65536 : get_local_port(c->sock);
1709ca3176e7SBrian Feldman 	char *remote_ipaddr = get_peer_ipaddr(c->sock);
1710d4ecd108SDag-Erling Smørgrav 	int remote_port = get_peer_port(c->sock);
17114f52dfbbSDag-Erling Smørgrav 	int r;
1712ca3176e7SBrian Feldman 
1713b15c8340SDag-Erling Smørgrav 	if (remote_port == -1) {
1714b15c8340SDag-Erling Smørgrav 		/* Fake addr/port to appease peers that validate it (Tectia) */
1715e4a9863fSDag-Erling Smørgrav 		free(remote_ipaddr);
1716b15c8340SDag-Erling Smørgrav 		remote_ipaddr = xstrdup("127.0.0.1");
1717b15c8340SDag-Erling Smørgrav 		remote_port = 65535;
1718b15c8340SDag-Erling Smørgrav 	}
1719b15c8340SDag-Erling Smørgrav 
17204f52dfbbSDag-Erling Smørgrav 	free(c->remote_name);
17214f52dfbbSDag-Erling Smørgrav 	xasprintf(&c->remote_name,
1722ca3176e7SBrian Feldman 	    "%s: listening port %d for %.100s port %d, "
1723f7167e0eSDag-Erling Smørgrav 	    "connect from %.200s port %d to %.100s port %d",
1724ca3176e7SBrian Feldman 	    rtype, c->listening_port, c->path, c->host_port,
1725f7167e0eSDag-Erling Smørgrav 	    remote_ipaddr, remote_port, local_ipaddr, local_port);
1726ca3176e7SBrian Feldman 
17274f52dfbbSDag-Erling Smørgrav 	open_preamble(ssh, __func__, c, rtype);
1728a0ee8cc6SDag-Erling Smørgrav 	if (strcmp(rtype, "direct-tcpip") == 0) {
1729ca3176e7SBrian Feldman 		/* target host, port */
17304f52dfbbSDag-Erling Smørgrav 		if ((r = sshpkt_put_cstring(ssh, c->path)) != 0 ||
173119261079SEd Maste 		    (r = sshpkt_put_u32(ssh, c->host_port)) != 0)
173219261079SEd Maste 			fatal_fr(r, "channel %i: reply", c->self);
1733a0ee8cc6SDag-Erling Smørgrav 	} else if (strcmp(rtype, "direct-streamlocal@openssh.com") == 0) {
1734a0ee8cc6SDag-Erling Smørgrav 		/* target path */
173519261079SEd Maste 		if ((r = sshpkt_put_cstring(ssh, c->path)) != 0)
173619261079SEd Maste 			fatal_fr(r, "channel %i: reply", c->self);
1737a0ee8cc6SDag-Erling Smørgrav 	} else if (strcmp(rtype, "forwarded-streamlocal@openssh.com") == 0) {
1738a0ee8cc6SDag-Erling Smørgrav 		/* listen path */
173919261079SEd Maste 		if ((r = sshpkt_put_cstring(ssh, c->path)) != 0)
174019261079SEd Maste 			fatal_fr(r, "channel %i: reply", c->self);
1741ca3176e7SBrian Feldman 	} else {
1742ca3176e7SBrian Feldman 		/* listen address, port */
17434f52dfbbSDag-Erling Smørgrav 		if ((r = sshpkt_put_cstring(ssh, c->path)) != 0 ||
174419261079SEd Maste 		    (r = sshpkt_put_u32(ssh, local_port)) != 0)
174519261079SEd Maste 			fatal_fr(r, "channel %i: reply", c->self);
1746ca3176e7SBrian Feldman 	}
1747a0ee8cc6SDag-Erling Smørgrav 	if (strcmp(rtype, "forwarded-streamlocal@openssh.com") == 0) {
1748a0ee8cc6SDag-Erling Smørgrav 		/* reserved for future owner/mode info */
174919261079SEd Maste 		if ((r = sshpkt_put_cstring(ssh, "")) != 0)
175019261079SEd Maste 			fatal_fr(r, "channel %i: reply", c->self);
1751a0ee8cc6SDag-Erling Smørgrav 	} else {
1752ca3176e7SBrian Feldman 		/* originator host and port */
17534f52dfbbSDag-Erling Smørgrav 		if ((r = sshpkt_put_cstring(ssh, remote_ipaddr)) != 0 ||
175419261079SEd Maste 		    (r = sshpkt_put_u32(ssh, (u_int)remote_port)) != 0)
175519261079SEd Maste 			fatal_fr(r, "channel %i: reply", c->self);
1756ca3176e7SBrian Feldman 	}
17574f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_send(ssh)) != 0)
175819261079SEd Maste 		fatal_fr(r, "channel %i: send", c->self);
1759e4a9863fSDag-Erling Smørgrav 	free(remote_ipaddr);
1760f7167e0eSDag-Erling Smørgrav 	free(local_ipaddr);
1761ca3176e7SBrian Feldman }
1762ca3176e7SBrian Feldman 
1763557f75e5SDag-Erling Smørgrav void
17644f52dfbbSDag-Erling Smørgrav channel_set_x11_refuse_time(struct ssh *ssh, u_int refuse_time)
1765557f75e5SDag-Erling Smørgrav {
17664f52dfbbSDag-Erling Smørgrav 	ssh->chanctxt->x11_refuse_time = refuse_time;
1767557f75e5SDag-Erling Smørgrav }
1768557f75e5SDag-Erling Smørgrav 
1769511b41d2SMark Murray /*
1770a04a10f8SKris Kennaway  * This socket is listening for connections to a forwarded TCP/IP port.
1771511b41d2SMark Murray  */
1772af12a3e7SDag-Erling Smørgrav static void
17731323ec57SEd Maste channel_post_port_listener(struct ssh *ssh, Channel *c)
1774a04a10f8SKris Kennaway {
1775ca3176e7SBrian Feldman 	Channel *nc;
1776d4af9e69SDag-Erling Smørgrav 	struct sockaddr_storage addr;
1777af12a3e7SDag-Erling Smørgrav 	int newsock, nextstate;
1778a04a10f8SKris Kennaway 	socklen_t addrlen;
1779ca3176e7SBrian Feldman 	char *rtype;
1780a04a10f8SKris Kennaway 
17811323ec57SEd Maste 	if ((c->io_ready & SSH_CHAN_IO_SOCK_R) == 0)
17824f52dfbbSDag-Erling Smørgrav 		return;
17834f52dfbbSDag-Erling Smørgrav 
17844f52dfbbSDag-Erling Smørgrav 	debug("Connection to port %d forwarding to %.100s port %d requested.",
1785a04a10f8SKris Kennaway 	    c->listening_port, c->path, c->host_port);
1786ca3176e7SBrian Feldman 
1787af12a3e7SDag-Erling Smørgrav 	if (c->type == SSH_CHANNEL_RPORT_LISTENER) {
1788af12a3e7SDag-Erling Smørgrav 		nextstate = SSH_CHANNEL_OPENING;
1789af12a3e7SDag-Erling Smørgrav 		rtype = "forwarded-tcpip";
1790a0ee8cc6SDag-Erling Smørgrav 	} else if (c->type == SSH_CHANNEL_RUNIX_LISTENER) {
1791a0ee8cc6SDag-Erling Smørgrav 		nextstate = SSH_CHANNEL_OPENING;
1792a0ee8cc6SDag-Erling Smørgrav 		rtype = "forwarded-streamlocal@openssh.com";
1793a0ee8cc6SDag-Erling Smørgrav 	} else if (c->host_port == PORT_STREAMLOCAL) {
1794a0ee8cc6SDag-Erling Smørgrav 		nextstate = SSH_CHANNEL_OPENING;
1795a0ee8cc6SDag-Erling Smørgrav 		rtype = "direct-streamlocal@openssh.com";
1796a0ee8cc6SDag-Erling Smørgrav 	} else if (c->host_port == 0) {
1797af12a3e7SDag-Erling Smørgrav 		nextstate = SSH_CHANNEL_DYNAMIC;
1798af12a3e7SDag-Erling Smørgrav 		rtype = "dynamic-tcpip";
1799af12a3e7SDag-Erling Smørgrav 	} else {
1800af12a3e7SDag-Erling Smørgrav 		nextstate = SSH_CHANNEL_OPENING;
1801af12a3e7SDag-Erling Smørgrav 		rtype = "direct-tcpip";
1802af12a3e7SDag-Erling Smørgrav 	}
1803ca3176e7SBrian Feldman 
1804511b41d2SMark Murray 	addrlen = sizeof(addr);
1805d4af9e69SDag-Erling Smørgrav 	newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen);
180619261079SEd Maste 	if (newsock == -1) {
1807e4a9863fSDag-Erling Smørgrav 		if (errno != EINTR && errno != EWOULDBLOCK &&
1808e4a9863fSDag-Erling Smørgrav 		    errno != ECONNABORTED)
1809511b41d2SMark Murray 			error("accept: %.100s", strerror(errno));
1810462c32cbSDag-Erling Smørgrav 		if (errno == EMFILE || errno == ENFILE)
1811e4a9863fSDag-Erling Smørgrav 			c->notbefore = monotime() + 1;
1812a04a10f8SKris Kennaway 		return;
1813511b41d2SMark Murray 	}
1814a0ee8cc6SDag-Erling Smørgrav 	if (c->host_port != PORT_STREAMLOCAL)
1815af12a3e7SDag-Erling Smørgrav 		set_nodelay(newsock);
18164f52dfbbSDag-Erling Smørgrav 	nc = channel_new(ssh, rtype, nextstate, newsock, newsock, -1,
1817221552e4SDag-Erling Smørgrav 	    c->local_window_max, c->local_maxpacket, 0, rtype, 1);
1818ca3176e7SBrian Feldman 	nc->listening_port = c->listening_port;
1819ca3176e7SBrian Feldman 	nc->host_port = c->host_port;
1820cce7d346SDag-Erling Smørgrav 	if (c->path != NULL)
1821cce7d346SDag-Erling Smørgrav 		nc->path = xstrdup(c->path);
1822ca3176e7SBrian Feldman 
1823b15c8340SDag-Erling Smørgrav 	if (nextstate != SSH_CHANNEL_DYNAMIC)
18244f52dfbbSDag-Erling Smørgrav 		port_open_helper(ssh, nc, rtype);
1825a04a10f8SKris Kennaway }
1826511b41d2SMark Murray 
1827511b41d2SMark Murray /*
1828a04a10f8SKris Kennaway  * This is the authentication agent socket listening for connections from
1829a04a10f8SKris Kennaway  * clients.
1830511b41d2SMark Murray  */
1831af12a3e7SDag-Erling Smørgrav static void
18321323ec57SEd Maste channel_post_auth_listener(struct ssh *ssh, Channel *c)
1833a04a10f8SKris Kennaway {
1834af12a3e7SDag-Erling Smørgrav 	Channel *nc;
18354f52dfbbSDag-Erling Smørgrav 	int r, newsock;
1836d4af9e69SDag-Erling Smørgrav 	struct sockaddr_storage addr;
1837a04a10f8SKris Kennaway 	socklen_t addrlen;
1838a04a10f8SKris Kennaway 
18391323ec57SEd Maste 	if ((c->io_ready & SSH_CHAN_IO_SOCK_R) == 0)
18404f52dfbbSDag-Erling Smørgrav 		return;
18414f52dfbbSDag-Erling Smørgrav 
1842511b41d2SMark Murray 	addrlen = sizeof(addr);
1843d4af9e69SDag-Erling Smørgrav 	newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen);
184419261079SEd Maste 	if (newsock == -1) {
18454f52dfbbSDag-Erling Smørgrav 		error("accept from auth socket: %.100s", strerror(errno));
1846462c32cbSDag-Erling Smørgrav 		if (errno == EMFILE || errno == ENFILE)
1847e4a9863fSDag-Erling Smørgrav 			c->notbefore = monotime() + 1;
1848a04a10f8SKris Kennaway 		return;
1849511b41d2SMark Murray 	}
18504f52dfbbSDag-Erling Smørgrav 	nc = channel_new(ssh, "accepted auth socket",
1851ca3176e7SBrian Feldman 	    SSH_CHANNEL_OPENING, newsock, newsock, -1,
1852ca3176e7SBrian Feldman 	    c->local_window_max, c->local_maxpacket,
1853221552e4SDag-Erling Smørgrav 	    0, "accepted auth socket", 1);
18544f52dfbbSDag-Erling Smørgrav 	open_preamble(ssh, __func__, nc, "auth-agent@openssh.com");
18554f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_send(ssh)) != 0)
185619261079SEd Maste 		fatal_fr(r, "channel %i", c->self);
1857a04a10f8SKris Kennaway }
1858511b41d2SMark Murray 
1859af12a3e7SDag-Erling Smørgrav static void
18601323ec57SEd Maste channel_post_connecting(struct ssh *ssh, Channel *c)
1861ca3176e7SBrian Feldman {
18624f52dfbbSDag-Erling Smørgrav 	int err = 0, sock, isopen, r;
1863af12a3e7SDag-Erling Smørgrav 	socklen_t sz = sizeof(err);
1864af12a3e7SDag-Erling Smørgrav 
18651323ec57SEd Maste 	if ((c->io_ready & SSH_CHAN_IO_SOCK_W) == 0)
18664f52dfbbSDag-Erling Smørgrav 		return;
18674f52dfbbSDag-Erling Smørgrav 	if (!c->have_remote_id)
186819261079SEd Maste 		fatal_f("channel %d: no remote id", c->self);
18694f52dfbbSDag-Erling Smørgrav 	/* for rdynamic the OPEN_CONFIRMATION has been sent already */
18704f52dfbbSDag-Erling Smørgrav 	isopen = (c->type == SSH_CHANNEL_RDYNAMIC_FINISH);
187119261079SEd Maste 	if (getsockopt(c->sock, SOL_SOCKET, SO_ERROR, &err, &sz) == -1) {
1872af12a3e7SDag-Erling Smørgrav 		err = errno;
1873af12a3e7SDag-Erling Smørgrav 		error("getsockopt SO_ERROR failed");
1874af12a3e7SDag-Erling Smørgrav 	}
1875ca3176e7SBrian Feldman 	if (err == 0) {
1876d4af9e69SDag-Erling Smørgrav 		debug("channel %d: connected to %s port %d",
1877d4af9e69SDag-Erling Smørgrav 		    c->self, c->connect_ctx.host, c->connect_ctx.port);
1878d4af9e69SDag-Erling Smørgrav 		channel_connect_ctx_free(&c->connect_ctx);
1879af12a3e7SDag-Erling Smørgrav 		c->type = SSH_CHANNEL_OPEN;
18804f52dfbbSDag-Erling Smørgrav 		if (isopen) {
18814f52dfbbSDag-Erling Smørgrav 			/* no message necessary */
1882af12a3e7SDag-Erling Smørgrav 		} else {
18834f52dfbbSDag-Erling Smørgrav 			if ((r = sshpkt_start(ssh,
18844f52dfbbSDag-Erling Smørgrav 			    SSH2_MSG_CHANNEL_OPEN_CONFIRMATION)) != 0 ||
18854f52dfbbSDag-Erling Smørgrav 			    (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
18864f52dfbbSDag-Erling Smørgrav 			    (r = sshpkt_put_u32(ssh, c->self)) != 0 ||
18874f52dfbbSDag-Erling Smørgrav 			    (r = sshpkt_put_u32(ssh, c->local_window)) != 0 ||
188819261079SEd Maste 			    (r = sshpkt_put_u32(ssh, c->local_maxpacket)) != 0 ||
188919261079SEd Maste 			    (r = sshpkt_send(ssh)) != 0)
189019261079SEd Maste 				fatal_fr(r, "channel %i open confirm", c->self);
1891af12a3e7SDag-Erling Smørgrav 		}
1892ca3176e7SBrian Feldman 	} else {
1893d4af9e69SDag-Erling Smørgrav 		debug("channel %d: connection failed: %s",
1894ca3176e7SBrian Feldman 		    c->self, strerror(err));
1895d4af9e69SDag-Erling Smørgrav 		/* Try next address, if any */
1896d4af9e69SDag-Erling Smørgrav 		if ((sock = connect_next(&c->connect_ctx)) > 0) {
1897d4af9e69SDag-Erling Smørgrav 			close(c->sock);
1898d4af9e69SDag-Erling Smørgrav 			c->sock = c->rfd = c->wfd = sock;
1899d4af9e69SDag-Erling Smørgrav 			return;
1900d4af9e69SDag-Erling Smørgrav 		}
1901d4af9e69SDag-Erling Smørgrav 		/* Exhausted all addresses */
1902d4af9e69SDag-Erling Smørgrav 		error("connect_to %.100s port %d: failed.",
1903d4af9e69SDag-Erling Smørgrav 		    c->connect_ctx.host, c->connect_ctx.port);
1904d4af9e69SDag-Erling Smørgrav 		channel_connect_ctx_free(&c->connect_ctx);
19054f52dfbbSDag-Erling Smørgrav 		if (isopen) {
19064f52dfbbSDag-Erling Smørgrav 			rdynamic_close(ssh, c);
1907af12a3e7SDag-Erling Smørgrav 		} else {
19084f52dfbbSDag-Erling Smørgrav 			if ((r = sshpkt_start(ssh,
19094f52dfbbSDag-Erling Smørgrav 			    SSH2_MSG_CHANNEL_OPEN_FAILURE)) != 0 ||
19104f52dfbbSDag-Erling Smørgrav 			    (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
191147dd1d1bSDag-Erling Smørgrav 			    (r = sshpkt_put_u32(ssh,
191247dd1d1bSDag-Erling Smørgrav 			    SSH2_OPEN_CONNECT_FAILED)) != 0 ||
191347dd1d1bSDag-Erling Smørgrav 			    (r = sshpkt_put_cstring(ssh, strerror(err))) != 0 ||
191419261079SEd Maste 			    (r = sshpkt_put_cstring(ssh, "")) != 0 ||
191519261079SEd Maste 			    (r = sshpkt_send(ssh)) != 0)
191619261079SEd Maste 				fatal_fr(r, "channel %i: failure", c->self);
19174f52dfbbSDag-Erling Smørgrav 			chan_mark_dead(ssh, c);
1918ca3176e7SBrian Feldman 		}
1919ca3176e7SBrian Feldman 	}
1920ca3176e7SBrian Feldman }
1921ca3176e7SBrian Feldman 
1922af12a3e7SDag-Erling Smørgrav static int
19231323ec57SEd Maste channel_handle_rfd(struct ssh *ssh, Channel *c)
1924a04a10f8SKris Kennaway {
1925aa49c926SDag-Erling Smørgrav 	char buf[CHAN_RBUF];
19264f52dfbbSDag-Erling Smørgrav 	ssize_t len;
19274f52dfbbSDag-Erling Smørgrav 	int r, force;
19281323ec57SEd Maste 	size_t have, avail, maxlen = CHANNEL_MAX_READ;
19291323ec57SEd Maste 	int pty_zeroread = 0;
19301323ec57SEd Maste 
19311323ec57SEd Maste #ifdef PTY_ZEROREAD
19321323ec57SEd Maste 	/* Bug on AIX: read(1) can return 0 for a non-closed fd */
19331323ec57SEd Maste 	pty_zeroread = c->isatty;
19341323ec57SEd Maste #endif
1935511b41d2SMark Murray 
1936d4af9e69SDag-Erling Smørgrav 	force = c->isatty && c->detach_close && c->istate != CHAN_INPUT_CLOSED;
19374f52dfbbSDag-Erling Smørgrav 
19381323ec57SEd Maste 	if (!force && (c->io_ready & SSH_CHAN_IO_RFD) == 0)
19394f52dfbbSDag-Erling Smørgrav 		return 1;
19401323ec57SEd Maste 	if ((avail = sshbuf_avail(c->input)) == 0)
19411323ec57SEd Maste 		return 1; /* Shouldn't happen */
19421323ec57SEd Maste 
19431323ec57SEd Maste 	/*
19441323ec57SEd Maste 	 * For "simple" channels (i.e. not datagram or filtered), we can
19451323ec57SEd Maste 	 * read directly to the channel buffer.
19461323ec57SEd Maste 	 */
19471323ec57SEd Maste 	if (!pty_zeroread && c->input_filter == NULL && !c->datagram) {
19481323ec57SEd Maste 		/* Only OPEN channels have valid rwin */
19491323ec57SEd Maste 		if (c->type == SSH_CHANNEL_OPEN) {
19501323ec57SEd Maste 			if ((have = sshbuf_len(c->input)) >= c->remote_window)
19511323ec57SEd Maste 				return 1; /* shouldn't happen */
19521323ec57SEd Maste 			if (maxlen > c->remote_window - have)
19531323ec57SEd Maste 				maxlen = c->remote_window - have;
19541323ec57SEd Maste 		}
19551323ec57SEd Maste 		if (maxlen > avail)
19561323ec57SEd Maste 			maxlen = avail;
19571323ec57SEd Maste 		if ((r = sshbuf_read(c->rfd, c->input, maxlen, NULL)) != 0) {
19581323ec57SEd Maste 			if (errno == EINTR || (!force &&
19591323ec57SEd Maste 			    (errno == EAGAIN || errno == EWOULDBLOCK)))
19601323ec57SEd Maste 				return 1;
19611323ec57SEd Maste 			debug2("channel %d: read failed rfd %d maxlen %zu: %s",
19621323ec57SEd Maste 			    c->self, c->rfd, maxlen, ssh_err(r));
19631323ec57SEd Maste 			goto rfail;
19641323ec57SEd Maste 		}
19651323ec57SEd Maste 		return 1;
19661323ec57SEd Maste 	}
19674f52dfbbSDag-Erling Smørgrav 
1968333ee039SDag-Erling Smørgrav 	errno = 0;
1969a04a10f8SKris Kennaway 	len = read(c->rfd, buf, sizeof(buf));
19701323ec57SEd Maste 	/* fixup AIX zero-length read with errno set to look more like errors */
19711323ec57SEd Maste 	if (pty_zeroread && len == 0 && errno != 0)
19721323ec57SEd Maste 		len = -1;
197319261079SEd Maste 	if (len == -1 && (errno == EINTR ||
1974d4af9e69SDag-Erling Smørgrav 	    ((errno == EAGAIN || errno == EWOULDBLOCK) && !force)))
1975a04a10f8SKris Kennaway 		return 1;
19761323ec57SEd Maste 	if (len < 0 || (!pty_zeroread && len == 0)) {
19771323ec57SEd Maste 		debug2("channel %d: read<=0 rfd %d len %zd: %s",
19781323ec57SEd Maste 		    c->self, c->rfd, len,
19791323ec57SEd Maste 		    len == 0 ? "closed" : strerror(errno));
19801323ec57SEd Maste  rfail:
1981ca3176e7SBrian Feldman 		if (c->type != SSH_CHANNEL_OPEN) {
1982221552e4SDag-Erling Smørgrav 			debug2("channel %d: not open", c->self);
19834f52dfbbSDag-Erling Smørgrav 			chan_mark_dead(ssh, c);
1984ca3176e7SBrian Feldman 			return -1;
1985a04a10f8SKris Kennaway 		} else {
19864f52dfbbSDag-Erling Smørgrav 			chan_read_failed(ssh, c);
1987a04a10f8SKris Kennaway 		}
1988a04a10f8SKris Kennaway 		return -1;
1989a04a10f8SKris Kennaway 	}
1990b66f2d16SKris Kennaway 	if (c->input_filter != NULL) {
19914f52dfbbSDag-Erling Smørgrav 		if (c->input_filter(ssh, c, buf, len) == -1) {
1992221552e4SDag-Erling Smørgrav 			debug2("channel %d: filter stops", c->self);
19934f52dfbbSDag-Erling Smørgrav 			chan_read_failed(ssh, c);
1994b66f2d16SKris Kennaway 		}
1995b74df5b2SDag-Erling Smørgrav 	} else if (c->datagram) {
19964f52dfbbSDag-Erling Smørgrav 		if ((r = sshbuf_put_string(c->input, buf, len)) != 0)
199719261079SEd Maste 			fatal_fr(r, "channel %i: put datagram", c->self);
199819261079SEd Maste 	} else if ((r = sshbuf_put(c->input, buf, len)) != 0)
199919261079SEd Maste 		fatal_fr(r, "channel %i: put data", c->self);
20001323ec57SEd Maste 
2001a04a10f8SKris Kennaway 	return 1;
2002a04a10f8SKris Kennaway }
2003333ee039SDag-Erling Smørgrav 
2004af12a3e7SDag-Erling Smørgrav static int
20051323ec57SEd Maste channel_handle_wfd(struct ssh *ssh, Channel *c)
2006a04a10f8SKris Kennaway {
2007ca3176e7SBrian Feldman 	struct termios tio;
20084f52dfbbSDag-Erling Smørgrav 	u_char *data = NULL, *buf; /* XXX const; need filter API change */
20094f52dfbbSDag-Erling Smørgrav 	size_t dlen, olen = 0;
20104f52dfbbSDag-Erling Smørgrav 	int r, len;
20114f52dfbbSDag-Erling Smørgrav 
20121323ec57SEd Maste 	if ((c->io_ready & SSH_CHAN_IO_WFD) == 0)
20131323ec57SEd Maste 		return 1;
20141323ec57SEd Maste 	if (sshbuf_len(c->output) == 0)
20154f52dfbbSDag-Erling Smørgrav 		return 1;
2016a04a10f8SKris Kennaway 
2017a04a10f8SKris Kennaway 	/* Send buffered output data to the socket. */
20184f52dfbbSDag-Erling Smørgrav 	olen = sshbuf_len(c->output);
2019b74df5b2SDag-Erling Smørgrav 	if (c->output_filter != NULL) {
20204f52dfbbSDag-Erling Smørgrav 		if ((buf = c->output_filter(ssh, c, &data, &dlen)) == NULL) {
2021b74df5b2SDag-Erling Smørgrav 			debug2("channel %d: filter stops", c->self);
2022b74df5b2SDag-Erling Smørgrav 			if (c->type != SSH_CHANNEL_OPEN)
20234f52dfbbSDag-Erling Smørgrav 				chan_mark_dead(ssh, c);
2024b74df5b2SDag-Erling Smørgrav 			else
20254f52dfbbSDag-Erling Smørgrav 				chan_write_failed(ssh, c);
2026b74df5b2SDag-Erling Smørgrav 			return -1;
2027b74df5b2SDag-Erling Smørgrav 		}
2028b74df5b2SDag-Erling Smørgrav 	} else if (c->datagram) {
20294f52dfbbSDag-Erling Smørgrav 		if ((r = sshbuf_get_string(c->output, &data, &dlen)) != 0)
203019261079SEd Maste 			fatal_fr(r, "channel %i: get datagram", c->self);
20314f52dfbbSDag-Erling Smørgrav 		buf = data;
2032b74df5b2SDag-Erling Smørgrav 	} else {
20334f52dfbbSDag-Erling Smørgrav 		buf = data = sshbuf_mutable_ptr(c->output);
20344f52dfbbSDag-Erling Smørgrav 		dlen = sshbuf_len(c->output);
2035b74df5b2SDag-Erling Smørgrav 	}
2036b74df5b2SDag-Erling Smørgrav 
2037b74df5b2SDag-Erling Smørgrav 	if (c->datagram) {
2038b74df5b2SDag-Erling Smørgrav 		/* ignore truncated writes, datagrams might get lost */
2039b74df5b2SDag-Erling Smørgrav 		len = write(c->wfd, buf, dlen);
2040e4a9863fSDag-Erling Smørgrav 		free(data);
204119261079SEd Maste 		if (len == -1 && (errno == EINTR || errno == EAGAIN ||
2042d4af9e69SDag-Erling Smørgrav 		    errno == EWOULDBLOCK))
2043b74df5b2SDag-Erling Smørgrav 			return 1;
20444f52dfbbSDag-Erling Smørgrav 		if (len <= 0)
20454f52dfbbSDag-Erling Smørgrav 			goto write_fail;
2046e2f6069cSDag-Erling Smørgrav 		goto out;
2047b74df5b2SDag-Erling Smørgrav 	}
20484f52dfbbSDag-Erling Smørgrav 
2049f388f5efSDag-Erling Smørgrav #ifdef _AIX
2050f388f5efSDag-Erling Smørgrav 	/* XXX: Later AIX versions can't push as much data to tty */
20514f52dfbbSDag-Erling Smørgrav 	if (c->wfd_isatty)
20521323ec57SEd Maste 		dlen = MINIMUM(dlen, 8*1024);
2053f388f5efSDag-Erling Smørgrav #endif
2054b74df5b2SDag-Erling Smørgrav 
2055b74df5b2SDag-Erling Smørgrav 	len = write(c->wfd, buf, dlen);
205619261079SEd Maste 	if (len == -1 &&
2057d4af9e69SDag-Erling Smørgrav 	    (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK))
2058a04a10f8SKris Kennaway 		return 1;
2059511b41d2SMark Murray 	if (len <= 0) {
20604f52dfbbSDag-Erling Smørgrav  write_fail:
2061ca3176e7SBrian Feldman 		if (c->type != SSH_CHANNEL_OPEN) {
2062221552e4SDag-Erling Smørgrav 			debug2("channel %d: not open", c->self);
20634f52dfbbSDag-Erling Smørgrav 			chan_mark_dead(ssh, c);
2064ca3176e7SBrian Feldman 			return -1;
2065511b41d2SMark Murray 		} else {
20664f52dfbbSDag-Erling Smørgrav 			chan_write_failed(ssh, c);
2067511b41d2SMark Murray 		}
2068a04a10f8SKris Kennaway 		return -1;
2069511b41d2SMark Murray 	}
20707aee6ffeSDag-Erling Smørgrav #ifndef BROKEN_TCGETATTR_ICANON
20714f52dfbbSDag-Erling Smørgrav 	if (c->isatty && dlen >= 1 && buf[0] != '\r') {
2072e0fbb1d2SBrian Feldman 		if (tcgetattr(c->wfd, &tio) == 0 &&
2073e0fbb1d2SBrian Feldman 		    !(tio.c_lflag & ECHO) && (tio.c_lflag & ICANON)) {
2074e0fbb1d2SBrian Feldman 			/*
2075e0fbb1d2SBrian Feldman 			 * Simulate echo to reduce the impact of
2076ca3176e7SBrian Feldman 			 * traffic analysis. We need to match the
2077ca3176e7SBrian Feldman 			 * size of a SSH2_MSG_CHANNEL_DATA message
2078b74df5b2SDag-Erling Smørgrav 			 * (4 byte channel id + buf)
2079e0fbb1d2SBrian Feldman 			 */
20804f52dfbbSDag-Erling Smørgrav 			if ((r = sshpkt_msg_ignore(ssh, 4+len)) != 0 ||
20814f52dfbbSDag-Erling Smørgrav 			    (r = sshpkt_send(ssh)) != 0)
208219261079SEd Maste 				fatal_fr(r, "channel %i: ignore", c->self);
2083e0fbb1d2SBrian Feldman 		}
2084e0fbb1d2SBrian Feldman 	}
20854f52dfbbSDag-Erling Smørgrav #endif /* BROKEN_TCGETATTR_ICANON */
208619261079SEd Maste 	if ((r = sshbuf_consume(c->output, len)) != 0)
208719261079SEd Maste 		fatal_fr(r, "channel %i: consume", c->self);
2088e2f6069cSDag-Erling Smørgrav  out:
20894f52dfbbSDag-Erling Smørgrav 	c->local_consumed += olen - sshbuf_len(c->output);
20904f52dfbbSDag-Erling Smørgrav 
2091a04a10f8SKris Kennaway 	return 1;
2092511b41d2SMark Murray }
2093333ee039SDag-Erling Smørgrav 
2094af12a3e7SDag-Erling Smørgrav static int
20951323ec57SEd Maste channel_handle_efd_write(struct ssh *ssh, Channel *c)
2096a04a10f8SKris Kennaway {
20974f52dfbbSDag-Erling Smørgrav 	int r;
20984f52dfbbSDag-Erling Smørgrav 	ssize_t len;
2099511b41d2SMark Murray 
21001323ec57SEd Maste 	if ((c->io_ready & SSH_CHAN_IO_EFD_W) == 0)
21011323ec57SEd Maste 		return 1;
21021323ec57SEd Maste 	if (sshbuf_len(c->extended) == 0)
21034f52dfbbSDag-Erling Smørgrav 		return 1;
21044f52dfbbSDag-Erling Smørgrav 
21054f52dfbbSDag-Erling Smørgrav 	len = write(c->efd, sshbuf_ptr(c->extended),
21064f52dfbbSDag-Erling Smørgrav 	    sshbuf_len(c->extended));
21074f52dfbbSDag-Erling Smørgrav 	debug2("channel %d: written %zd to efd %d", c->self, len, c->efd);
210819261079SEd Maste 	if (len == -1 && (errno == EINTR || errno == EAGAIN ||
2109d4af9e69SDag-Erling Smørgrav 	    errno == EWOULDBLOCK))
2110ca3176e7SBrian Feldman 		return 1;
2111ca3176e7SBrian Feldman 	if (len <= 0) {
21124f52dfbbSDag-Erling Smørgrav 		debug2("channel %d: closing write-efd %d", c->self, c->efd);
211319261079SEd Maste 		channel_close_fd(ssh, c, &c->efd);
2114ca3176e7SBrian Feldman 	} else {
211519261079SEd Maste 		if ((r = sshbuf_consume(c->extended, len)) != 0)
211619261079SEd Maste 			fatal_fr(r, "channel %i: consume", c->self);
2117a04a10f8SKris Kennaway 		c->local_consumed += len;
2118a04a10f8SKris Kennaway 	}
21194f52dfbbSDag-Erling Smørgrav 	return 1;
21204f52dfbbSDag-Erling Smørgrav }
21214f52dfbbSDag-Erling Smørgrav 
21224f52dfbbSDag-Erling Smørgrav static int
21231323ec57SEd Maste channel_handle_efd_read(struct ssh *ssh, Channel *c)
21244f52dfbbSDag-Erling Smørgrav {
21254f52dfbbSDag-Erling Smørgrav 	char buf[CHAN_RBUF];
21264f52dfbbSDag-Erling Smørgrav 	ssize_t len;
212719261079SEd Maste 	int r, force;
21284f52dfbbSDag-Erling Smørgrav 
212919261079SEd Maste 	force = c->isatty && c->detach_close && c->istate != CHAN_INPUT_CLOSED;
213019261079SEd Maste 
21311323ec57SEd Maste 	if (!force && (c->io_ready & SSH_CHAN_IO_EFD_R) == 0)
21324f52dfbbSDag-Erling Smørgrav 		return 1;
21334f52dfbbSDag-Erling Smørgrav 
2134a04a10f8SKris Kennaway 	len = read(c->efd, buf, sizeof(buf));
21354f52dfbbSDag-Erling Smørgrav 	debug2("channel %d: read %zd from efd %d", c->self, len, c->efd);
213619261079SEd Maste 	if (len == -1 && (errno == EINTR || ((errno == EAGAIN ||
213719261079SEd Maste 	    errno == EWOULDBLOCK) && !force)))
2138ca3176e7SBrian Feldman 		return 1;
2139ca3176e7SBrian Feldman 	if (len <= 0) {
214019261079SEd Maste 		debug2("channel %d: closing read-efd %d", c->self, c->efd);
214119261079SEd Maste 		channel_close_fd(ssh, c, &c->efd);
214219261079SEd Maste 	} else if (c->extended_usage == CHAN_EXTENDED_IGNORE)
214319261079SEd Maste 		debug3("channel %d: discard efd", c->self);
214419261079SEd Maste 	else if ((r = sshbuf_put(c->extended, buf, len)) != 0)
214519261079SEd Maste 		fatal_fr(r, "channel %i: append", c->self);
2146a04a10f8SKris Kennaway 	return 1;
2147a04a10f8SKris Kennaway }
2148333ee039SDag-Erling Smørgrav 
214921e764dfSDag-Erling Smørgrav static int
21501323ec57SEd Maste channel_handle_efd(struct ssh *ssh, Channel *c)
2151a04a10f8SKris Kennaway {
21524f52dfbbSDag-Erling Smørgrav 	if (c->efd == -1)
21534f52dfbbSDag-Erling Smørgrav 		return 1;
21544f52dfbbSDag-Erling Smørgrav 
21554f52dfbbSDag-Erling Smørgrav 	/** XXX handle drain efd, too */
21564f52dfbbSDag-Erling Smørgrav 
21574f52dfbbSDag-Erling Smørgrav 	if (c->extended_usage == CHAN_EXTENDED_WRITE)
21581323ec57SEd Maste 		return channel_handle_efd_write(ssh, c);
21594f52dfbbSDag-Erling Smørgrav 	else if (c->extended_usage == CHAN_EXTENDED_READ ||
21604f52dfbbSDag-Erling Smørgrav 	    c->extended_usage == CHAN_EXTENDED_IGNORE)
21611323ec57SEd Maste 		return channel_handle_efd_read(ssh, c);
21624f52dfbbSDag-Erling Smørgrav 
21634f52dfbbSDag-Erling Smørgrav 	return 1;
21644f52dfbbSDag-Erling Smørgrav }
21654f52dfbbSDag-Erling Smørgrav 
21664f52dfbbSDag-Erling Smørgrav static int
21674f52dfbbSDag-Erling Smørgrav channel_check_window(struct ssh *ssh, Channel *c)
21684f52dfbbSDag-Erling Smørgrav {
21694f52dfbbSDag-Erling Smørgrav 	int r;
21704f52dfbbSDag-Erling Smørgrav 
2171ca3176e7SBrian Feldman 	if (c->type == SSH_CHANNEL_OPEN &&
2172ca3176e7SBrian Feldman 	    !(c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD)) &&
2173d4af9e69SDag-Erling Smørgrav 	    ((c->local_window_max - c->local_window >
2174d4af9e69SDag-Erling Smørgrav 	    c->local_maxpacket*3) ||
2175d4af9e69SDag-Erling Smørgrav 	    c->local_window < c->local_window_max/2) &&
2176a04a10f8SKris Kennaway 	    c->local_consumed > 0) {
21774f52dfbbSDag-Erling Smørgrav 		if (!c->have_remote_id)
217819261079SEd Maste 			fatal_f("channel %d: no remote id", c->self);
21794f52dfbbSDag-Erling Smørgrav 		if ((r = sshpkt_start(ssh,
21804f52dfbbSDag-Erling Smørgrav 		    SSH2_MSG_CHANNEL_WINDOW_ADJUST)) != 0 ||
21814f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
21824f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_u32(ssh, c->local_consumed)) != 0 ||
21834f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_send(ssh)) != 0) {
218419261079SEd Maste 			fatal_fr(r, "channel %i", c->self);
21854f52dfbbSDag-Erling Smørgrav 		}
218619261079SEd Maste 		debug2("channel %d: window %d sent adjust %d", c->self,
218719261079SEd Maste 		    c->local_window, c->local_consumed);
218860c59fadSDag-Erling Smørgrav 		c->local_window += c->local_consumed;
2189a04a10f8SKris Kennaway 		c->local_consumed = 0;
2190a04a10f8SKris Kennaway 	}
2191a04a10f8SKris Kennaway 	return 1;
2192a04a10f8SKris Kennaway }
2193a04a10f8SKris Kennaway 
2194af12a3e7SDag-Erling Smørgrav static void
21951323ec57SEd Maste channel_post_open(struct ssh *ssh, Channel *c)
2196a04a10f8SKris Kennaway {
21971323ec57SEd Maste 	channel_handle_rfd(ssh, c);
21981323ec57SEd Maste 	channel_handle_wfd(ssh, c);
21991323ec57SEd Maste 	channel_handle_efd(ssh, c);
22004f52dfbbSDag-Erling Smørgrav 	channel_check_window(ssh, c);
2201a04a10f8SKris Kennaway }
2202a04a10f8SKris Kennaway 
2203b15c8340SDag-Erling Smørgrav static u_int
22044f52dfbbSDag-Erling Smørgrav read_mux(struct ssh *ssh, Channel *c, u_int need)
2205b15c8340SDag-Erling Smørgrav {
2206b15c8340SDag-Erling Smørgrav 	char buf[CHAN_RBUF];
22074f52dfbbSDag-Erling Smørgrav 	ssize_t len;
2208b15c8340SDag-Erling Smørgrav 	u_int rlen;
22094f52dfbbSDag-Erling Smørgrav 	int r;
2210b15c8340SDag-Erling Smørgrav 
22114f52dfbbSDag-Erling Smørgrav 	if (sshbuf_len(c->input) < need) {
22124f52dfbbSDag-Erling Smørgrav 		rlen = need - sshbuf_len(c->input);
2213ca86bcf2SDag-Erling Smørgrav 		len = read(c->rfd, buf, MINIMUM(rlen, CHAN_RBUF));
221419261079SEd Maste 		if (len == -1 && (errno == EINTR || errno == EAGAIN))
22154f52dfbbSDag-Erling Smørgrav 			return sshbuf_len(c->input);
2216b15c8340SDag-Erling Smørgrav 		if (len <= 0) {
22174f52dfbbSDag-Erling Smørgrav 			debug2("channel %d: ctl read<=0 rfd %d len %zd",
2218b15c8340SDag-Erling Smørgrav 			    c->self, c->rfd, len);
22194f52dfbbSDag-Erling Smørgrav 			chan_read_failed(ssh, c);
2220b15c8340SDag-Erling Smørgrav 			return 0;
222119261079SEd Maste 		} else if ((r = sshbuf_put(c->input, buf, len)) != 0)
222219261079SEd Maste 			fatal_fr(r, "channel %i: append", c->self);
22234f52dfbbSDag-Erling Smørgrav 	}
22244f52dfbbSDag-Erling Smørgrav 	return sshbuf_len(c->input);
2225b15c8340SDag-Erling Smørgrav }
2226b15c8340SDag-Erling Smørgrav 
2227b15c8340SDag-Erling Smørgrav static void
22281323ec57SEd Maste channel_post_mux_client_read(struct ssh *ssh, Channel *c)
2229b15c8340SDag-Erling Smørgrav {
2230b15c8340SDag-Erling Smørgrav 	u_int need;
2231b15c8340SDag-Erling Smørgrav 
22321323ec57SEd Maste 	if ((c->io_ready & SSH_CHAN_IO_RFD) == 0)
22334f52dfbbSDag-Erling Smørgrav 		return;
22344f52dfbbSDag-Erling Smørgrav 	if (c->istate != CHAN_INPUT_OPEN && c->istate != CHAN_INPUT_WAIT_DRAIN)
22354f52dfbbSDag-Erling Smørgrav 		return;
22364f52dfbbSDag-Erling Smørgrav 	if (c->mux_pause)
22374f52dfbbSDag-Erling Smørgrav 		return;
2238b15c8340SDag-Erling Smørgrav 
2239b15c8340SDag-Erling Smørgrav 	/*
2240b15c8340SDag-Erling Smørgrav 	 * Don't not read past the precise end of packets to
2241b15c8340SDag-Erling Smørgrav 	 * avoid disrupting fd passing.
2242b15c8340SDag-Erling Smørgrav 	 */
22434f52dfbbSDag-Erling Smørgrav 	if (read_mux(ssh, c, 4) < 4) /* read header */
2244b15c8340SDag-Erling Smørgrav 		return;
22454f52dfbbSDag-Erling Smørgrav 	/* XXX sshbuf_peek_u32 */
22464f52dfbbSDag-Erling Smørgrav 	need = PEEK_U32(sshbuf_ptr(c->input));
2247b15c8340SDag-Erling Smørgrav #define CHANNEL_MUX_MAX_PACKET	(256 * 1024)
2248b15c8340SDag-Erling Smørgrav 	if (need > CHANNEL_MUX_MAX_PACKET) {
2249b15c8340SDag-Erling Smørgrav 		debug2("channel %d: packet too big %u > %u",
2250b15c8340SDag-Erling Smørgrav 		    c->self, CHANNEL_MUX_MAX_PACKET, need);
22514f52dfbbSDag-Erling Smørgrav 		chan_rcvd_oclose(ssh, c);
2252b15c8340SDag-Erling Smørgrav 		return;
2253b15c8340SDag-Erling Smørgrav 	}
22544f52dfbbSDag-Erling Smørgrav 	if (read_mux(ssh, c, need + 4) < need + 4) /* read body */
2255b15c8340SDag-Erling Smørgrav 		return;
22564f52dfbbSDag-Erling Smørgrav 	if (c->mux_rcb(ssh, c) != 0) {
2257b15c8340SDag-Erling Smørgrav 		debug("channel %d: mux_rcb failed", c->self);
22584f52dfbbSDag-Erling Smørgrav 		chan_mark_dead(ssh, c);
2259b15c8340SDag-Erling Smørgrav 		return;
2260b15c8340SDag-Erling Smørgrav 	}
2261b15c8340SDag-Erling Smørgrav }
2262b15c8340SDag-Erling Smørgrav 
2263b15c8340SDag-Erling Smørgrav static void
22641323ec57SEd Maste channel_post_mux_client_write(struct ssh *ssh, Channel *c)
22654f52dfbbSDag-Erling Smørgrav {
22664f52dfbbSDag-Erling Smørgrav 	ssize_t len;
22674f52dfbbSDag-Erling Smørgrav 	int r;
22684f52dfbbSDag-Erling Smørgrav 
22691323ec57SEd Maste 	if ((c->io_ready & SSH_CHAN_IO_WFD) == 0)
22701323ec57SEd Maste 		return;
22711323ec57SEd Maste 	if (sshbuf_len(c->output) == 0)
22724f52dfbbSDag-Erling Smørgrav 		return;
22734f52dfbbSDag-Erling Smørgrav 
22744f52dfbbSDag-Erling Smørgrav 	len = write(c->wfd, sshbuf_ptr(c->output), sshbuf_len(c->output));
227519261079SEd Maste 	if (len == -1 && (errno == EINTR || errno == EAGAIN))
22764f52dfbbSDag-Erling Smørgrav 		return;
22774f52dfbbSDag-Erling Smørgrav 	if (len <= 0) {
22784f52dfbbSDag-Erling Smørgrav 		chan_mark_dead(ssh, c);
22794f52dfbbSDag-Erling Smørgrav 		return;
22804f52dfbbSDag-Erling Smørgrav 	}
22814f52dfbbSDag-Erling Smørgrav 	if ((r = sshbuf_consume(c->output, len)) != 0)
228219261079SEd Maste 		fatal_fr(r, "channel %i: consume", c->self);
22834f52dfbbSDag-Erling Smørgrav }
22844f52dfbbSDag-Erling Smørgrav 
22854f52dfbbSDag-Erling Smørgrav static void
22861323ec57SEd Maste channel_post_mux_client(struct ssh *ssh, Channel *c)
22874f52dfbbSDag-Erling Smørgrav {
22881323ec57SEd Maste 	channel_post_mux_client_read(ssh, c);
22891323ec57SEd Maste 	channel_post_mux_client_write(ssh, c);
22904f52dfbbSDag-Erling Smørgrav }
22914f52dfbbSDag-Erling Smørgrav 
22924f52dfbbSDag-Erling Smørgrav static void
22931323ec57SEd Maste channel_post_mux_listener(struct ssh *ssh, Channel *c)
2294b15c8340SDag-Erling Smørgrav {
2295b15c8340SDag-Erling Smørgrav 	Channel *nc;
2296b15c8340SDag-Erling Smørgrav 	struct sockaddr_storage addr;
2297b15c8340SDag-Erling Smørgrav 	socklen_t addrlen;
2298b15c8340SDag-Erling Smørgrav 	int newsock;
2299b15c8340SDag-Erling Smørgrav 	uid_t euid;
2300b15c8340SDag-Erling Smørgrav 	gid_t egid;
2301b15c8340SDag-Erling Smørgrav 
23021323ec57SEd Maste 	if ((c->io_ready & SSH_CHAN_IO_SOCK_R) == 0)
2303b15c8340SDag-Erling Smørgrav 		return;
2304b15c8340SDag-Erling Smørgrav 
2305b15c8340SDag-Erling Smørgrav 	debug("multiplexing control connection");
2306b15c8340SDag-Erling Smørgrav 
2307b15c8340SDag-Erling Smørgrav 	/*
2308b15c8340SDag-Erling Smørgrav 	 * Accept connection on control socket
2309b15c8340SDag-Erling Smørgrav 	 */
2310b15c8340SDag-Erling Smørgrav 	memset(&addr, 0, sizeof(addr));
2311b15c8340SDag-Erling Smørgrav 	addrlen = sizeof(addr);
2312b15c8340SDag-Erling Smørgrav 	if ((newsock = accept(c->sock, (struct sockaddr*)&addr,
2313b15c8340SDag-Erling Smørgrav 	    &addrlen)) == -1) {
231419261079SEd Maste 		error_f("accept: %s", strerror(errno));
2315462c32cbSDag-Erling Smørgrav 		if (errno == EMFILE || errno == ENFILE)
2316e4a9863fSDag-Erling Smørgrav 			c->notbefore = monotime() + 1;
2317b15c8340SDag-Erling Smørgrav 		return;
2318b15c8340SDag-Erling Smørgrav 	}
2319b15c8340SDag-Erling Smørgrav 
232019261079SEd Maste 	if (getpeereid(newsock, &euid, &egid) == -1) {
232119261079SEd Maste 		error_f("getpeereid failed: %s", strerror(errno));
2322b15c8340SDag-Erling Smørgrav 		close(newsock);
2323b15c8340SDag-Erling Smørgrav 		return;
2324b15c8340SDag-Erling Smørgrav 	}
2325b15c8340SDag-Erling Smørgrav 	if ((euid != 0) && (getuid() != euid)) {
2326b15c8340SDag-Erling Smørgrav 		error("multiplex uid mismatch: peer euid %u != uid %u",
2327b15c8340SDag-Erling Smørgrav 		    (u_int)euid, (u_int)getuid());
2328b15c8340SDag-Erling Smørgrav 		close(newsock);
2329b15c8340SDag-Erling Smørgrav 		return;
2330b15c8340SDag-Erling Smørgrav 	}
23314f52dfbbSDag-Erling Smørgrav 	nc = channel_new(ssh, "multiplex client", SSH_CHANNEL_MUX_CLIENT,
2332b15c8340SDag-Erling Smørgrav 	    newsock, newsock, -1, c->local_window_max,
2333b15c8340SDag-Erling Smørgrav 	    c->local_maxpacket, 0, "mux-control", 1);
2334b15c8340SDag-Erling Smørgrav 	nc->mux_rcb = c->mux_rcb;
233519261079SEd Maste 	debug3_f("new mux channel %d fd %d", nc->self, nc->sock);
2336b15c8340SDag-Erling Smørgrav 	/* establish state */
23374f52dfbbSDag-Erling Smørgrav 	nc->mux_rcb(ssh, nc);
2338b15c8340SDag-Erling Smørgrav 	/* mux state transitions must not elicit protocol messages */
2339b15c8340SDag-Erling Smørgrav 	nc->flags |= CHAN_LOCAL;
2340b15c8340SDag-Erling Smørgrav }
2341b15c8340SDag-Erling Smørgrav 
2342af12a3e7SDag-Erling Smørgrav static void
23434f52dfbbSDag-Erling Smørgrav channel_handler_init(struct ssh_channels *sc)
2344a04a10f8SKris Kennaway {
23454f52dfbbSDag-Erling Smørgrav 	chan_fn **pre, **post;
2346f388f5efSDag-Erling Smørgrav 
23474f52dfbbSDag-Erling Smørgrav 	if ((pre = calloc(SSH_CHANNEL_MAX_TYPE, sizeof(*pre))) == NULL ||
23484f52dfbbSDag-Erling Smørgrav 	    (post = calloc(SSH_CHANNEL_MAX_TYPE, sizeof(*post))) == NULL)
234919261079SEd Maste 		fatal_f("allocation failed");
2350511b41d2SMark Murray 
23514f52dfbbSDag-Erling Smørgrav 	pre[SSH_CHANNEL_OPEN] =			&channel_pre_open;
23524f52dfbbSDag-Erling Smørgrav 	pre[SSH_CHANNEL_X11_OPEN] =		&channel_pre_x11_open;
23534f52dfbbSDag-Erling Smørgrav 	pre[SSH_CHANNEL_PORT_LISTENER] =	&channel_pre_listener;
23544f52dfbbSDag-Erling Smørgrav 	pre[SSH_CHANNEL_RPORT_LISTENER] =	&channel_pre_listener;
23554f52dfbbSDag-Erling Smørgrav 	pre[SSH_CHANNEL_UNIX_LISTENER] =	&channel_pre_listener;
23564f52dfbbSDag-Erling Smørgrav 	pre[SSH_CHANNEL_RUNIX_LISTENER] =	&channel_pre_listener;
23574f52dfbbSDag-Erling Smørgrav 	pre[SSH_CHANNEL_X11_LISTENER] =		&channel_pre_listener;
23584f52dfbbSDag-Erling Smørgrav 	pre[SSH_CHANNEL_AUTH_SOCKET] =		&channel_pre_listener;
23594f52dfbbSDag-Erling Smørgrav 	pre[SSH_CHANNEL_CONNECTING] =		&channel_pre_connecting;
23604f52dfbbSDag-Erling Smørgrav 	pre[SSH_CHANNEL_DYNAMIC] =		&channel_pre_dynamic;
23614f52dfbbSDag-Erling Smørgrav 	pre[SSH_CHANNEL_RDYNAMIC_FINISH] =	&channel_pre_connecting;
23624f52dfbbSDag-Erling Smørgrav 	pre[SSH_CHANNEL_MUX_LISTENER] =		&channel_pre_listener;
23634f52dfbbSDag-Erling Smørgrav 	pre[SSH_CHANNEL_MUX_CLIENT] =		&channel_pre_mux_client;
2364a04a10f8SKris Kennaway 
23654f52dfbbSDag-Erling Smørgrav 	post[SSH_CHANNEL_OPEN] =		&channel_post_open;
23664f52dfbbSDag-Erling Smørgrav 	post[SSH_CHANNEL_PORT_LISTENER] =	&channel_post_port_listener;
23674f52dfbbSDag-Erling Smørgrav 	post[SSH_CHANNEL_RPORT_LISTENER] =	&channel_post_port_listener;
23684f52dfbbSDag-Erling Smørgrav 	post[SSH_CHANNEL_UNIX_LISTENER] =	&channel_post_port_listener;
23694f52dfbbSDag-Erling Smørgrav 	post[SSH_CHANNEL_RUNIX_LISTENER] =	&channel_post_port_listener;
23704f52dfbbSDag-Erling Smørgrav 	post[SSH_CHANNEL_X11_LISTENER] =	&channel_post_x11_listener;
23714f52dfbbSDag-Erling Smørgrav 	post[SSH_CHANNEL_AUTH_SOCKET] =		&channel_post_auth_listener;
23724f52dfbbSDag-Erling Smørgrav 	post[SSH_CHANNEL_CONNECTING] =		&channel_post_connecting;
23734f52dfbbSDag-Erling Smørgrav 	post[SSH_CHANNEL_DYNAMIC] =		&channel_post_open;
23744f52dfbbSDag-Erling Smørgrav 	post[SSH_CHANNEL_RDYNAMIC_FINISH] =	&channel_post_connecting;
23754f52dfbbSDag-Erling Smørgrav 	post[SSH_CHANNEL_MUX_LISTENER] =	&channel_post_mux_listener;
23764f52dfbbSDag-Erling Smørgrav 	post[SSH_CHANNEL_MUX_CLIENT] =		&channel_post_mux_client;
2377a04a10f8SKris Kennaway 
23784f52dfbbSDag-Erling Smørgrav 	sc->channel_pre = pre;
23794f52dfbbSDag-Erling Smørgrav 	sc->channel_post = post;
2380a04a10f8SKris Kennaway }
2381a04a10f8SKris Kennaway 
2382af12a3e7SDag-Erling Smørgrav /* gc dead channels */
2383af12a3e7SDag-Erling Smørgrav static void
23844f52dfbbSDag-Erling Smørgrav channel_garbage_collect(struct ssh *ssh, Channel *c)
2385af12a3e7SDag-Erling Smørgrav {
2386af12a3e7SDag-Erling Smørgrav 	if (c == NULL)
2387af12a3e7SDag-Erling Smørgrav 		return;
2388af12a3e7SDag-Erling Smørgrav 	if (c->detach_user != NULL) {
23894f52dfbbSDag-Erling Smørgrav 		if (!chan_is_dead(ssh, c, c->detach_close))
2390af12a3e7SDag-Erling Smørgrav 			return;
23912f513db7SEd Maste 
2392221552e4SDag-Erling Smørgrav 		debug2("channel %d: gc: notify user", c->self);
23934f52dfbbSDag-Erling Smørgrav 		c->detach_user(ssh, c->self, NULL);
2394af12a3e7SDag-Erling Smørgrav 		/* if we still have a callback */
2395af12a3e7SDag-Erling Smørgrav 		if (c->detach_user != NULL)
2396af12a3e7SDag-Erling Smørgrav 			return;
2397221552e4SDag-Erling Smørgrav 		debug2("channel %d: gc: user detached", c->self);
2398af12a3e7SDag-Erling Smørgrav 	}
23994f52dfbbSDag-Erling Smørgrav 	if (!chan_is_dead(ssh, c, 1))
2400af12a3e7SDag-Erling Smørgrav 		return;
2401221552e4SDag-Erling Smørgrav 	debug2("channel %d: garbage collecting", c->self);
24024f52dfbbSDag-Erling Smørgrav 	channel_free(ssh, c);
2403af12a3e7SDag-Erling Smørgrav }
2404af12a3e7SDag-Erling Smørgrav 
24054f52dfbbSDag-Erling Smørgrav enum channel_table { CHAN_PRE, CHAN_POST };
24064f52dfbbSDag-Erling Smørgrav 
2407af12a3e7SDag-Erling Smørgrav static void
24081323ec57SEd Maste channel_handler(struct ssh *ssh, int table, time_t *unpause_secs)
2409a04a10f8SKris Kennaway {
24104f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
24114f52dfbbSDag-Erling Smørgrav 	chan_fn **ftab = table == CHAN_PRE ? sc->channel_pre : sc->channel_post;
2412b15c8340SDag-Erling Smørgrav 	u_int i, oalloc;
2413a04a10f8SKris Kennaway 	Channel *c;
2414462c32cbSDag-Erling Smørgrav 	time_t now;
2415a04a10f8SKris Kennaway 
2416e4a9863fSDag-Erling Smørgrav 	now = monotime();
2417462c32cbSDag-Erling Smørgrav 	if (unpause_secs != NULL)
2418462c32cbSDag-Erling Smørgrav 		*unpause_secs = 0;
24194f52dfbbSDag-Erling Smørgrav 	for (i = 0, oalloc = sc->channels_alloc; i < oalloc; i++) {
24204f52dfbbSDag-Erling Smørgrav 		c = sc->channels[i];
2421af12a3e7SDag-Erling Smørgrav 		if (c == NULL)
2422511b41d2SMark Murray 			continue;
2423b15c8340SDag-Erling Smørgrav 		if (c->delayed) {
24244f52dfbbSDag-Erling Smørgrav 			if (table == CHAN_PRE)
2425b15c8340SDag-Erling Smørgrav 				c->delayed = 0;
2426b15c8340SDag-Erling Smørgrav 			else
2427b15c8340SDag-Erling Smørgrav 				continue;
2428b15c8340SDag-Erling Smørgrav 		}
2429462c32cbSDag-Erling Smørgrav 		if (ftab[c->type] != NULL) {
2430462c32cbSDag-Erling Smørgrav 			/*
2431462c32cbSDag-Erling Smørgrav 			 * Run handlers that are not paused.
2432462c32cbSDag-Erling Smørgrav 			 */
2433462c32cbSDag-Erling Smørgrav 			if (c->notbefore <= now)
24341323ec57SEd Maste 				(*ftab[c->type])(ssh, c);
2435462c32cbSDag-Erling Smørgrav 			else if (unpause_secs != NULL) {
2436462c32cbSDag-Erling Smørgrav 				/*
2437462c32cbSDag-Erling Smørgrav 				 * Collect the time that the earliest
2438462c32cbSDag-Erling Smørgrav 				 * channel comes off pause.
2439462c32cbSDag-Erling Smørgrav 				 */
244019261079SEd Maste 				debug3_f("chan %d: skip for %d more "
244119261079SEd Maste 				    "seconds", c->self,
2442462c32cbSDag-Erling Smørgrav 				    (int)(c->notbefore - now));
2443462c32cbSDag-Erling Smørgrav 				if (*unpause_secs == 0 ||
2444462c32cbSDag-Erling Smørgrav 				    (c->notbefore - now) < *unpause_secs)
2445462c32cbSDag-Erling Smørgrav 					*unpause_secs = c->notbefore - now;
2446462c32cbSDag-Erling Smørgrav 			}
2447462c32cbSDag-Erling Smørgrav 		}
24484f52dfbbSDag-Erling Smørgrav 		channel_garbage_collect(ssh, c);
2449511b41d2SMark Murray 	}
2450462c32cbSDag-Erling Smørgrav 	if (unpause_secs != NULL && *unpause_secs != 0)
245119261079SEd Maste 		debug3_f("first channel unpauses in %d seconds",
245219261079SEd Maste 		    (int)*unpause_secs);
2453511b41d2SMark Murray }
2454a04a10f8SKris Kennaway 
2455af12a3e7SDag-Erling Smørgrav /*
24561323ec57SEd Maste  * Create sockets before preparing IO.
24574f52dfbbSDag-Erling Smørgrav  * This is necessary for things that need to happen after reading
24581323ec57SEd Maste  * the network-input but need to be completed before IO event setup, e.g.
24591323ec57SEd Maste  * because they may create new channels.
24604f52dfbbSDag-Erling Smørgrav  */
24614f52dfbbSDag-Erling Smørgrav static void
24621323ec57SEd Maste channel_before_prepare_io(struct ssh *ssh)
24634f52dfbbSDag-Erling Smørgrav {
24644f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
24654f52dfbbSDag-Erling Smørgrav 	Channel *c;
24664f52dfbbSDag-Erling Smørgrav 	u_int i, oalloc;
24674f52dfbbSDag-Erling Smørgrav 
24684f52dfbbSDag-Erling Smørgrav 	for (i = 0, oalloc = sc->channels_alloc; i < oalloc; i++) {
24694f52dfbbSDag-Erling Smørgrav 		c = sc->channels[i];
24704f52dfbbSDag-Erling Smørgrav 		if (c == NULL)
24714f52dfbbSDag-Erling Smørgrav 			continue;
24724f52dfbbSDag-Erling Smørgrav 		if (c->type == SSH_CHANNEL_RDYNAMIC_OPEN)
24731323ec57SEd Maste 			channel_before_prepare_io_rdynamic(ssh, c);
24744f52dfbbSDag-Erling Smørgrav 	}
24754f52dfbbSDag-Erling Smørgrav }
24764f52dfbbSDag-Erling Smørgrav 
24771323ec57SEd Maste static void
24781323ec57SEd Maste dump_channel_poll(const char *func, const char *what, Channel *c,
24791323ec57SEd Maste     u_int pollfd_offset, struct pollfd *pfd)
2480a04a10f8SKris Kennaway {
24811323ec57SEd Maste #ifdef DEBUG_CHANNEL_POLL
248287c1498dSEd Maste 	debug3("%s: channel %d: %s r%d w%d e%d s%d c->pfds [ %d %d %d %d ] "
248387c1498dSEd Maste 	    "io_want 0x%02x io_ready 0x%02x pfd[%u].fd=%d "
248487c1498dSEd Maste 	    "pfd.ev 0x%02x pfd.rev 0x%02x", func, c->self, what,
248587c1498dSEd Maste 	    c->rfd, c->wfd, c->efd, c->sock,
248687c1498dSEd Maste 	    c->pfds[0], c->pfds[1], c->pfds[2], c->pfds[3],
248787c1498dSEd Maste 	    c->io_want, c->io_ready,
248887c1498dSEd Maste 	    pollfd_offset, pfd->fd, pfd->events, pfd->revents);
24891323ec57SEd Maste #endif
2490ca3176e7SBrian Feldman }
2491ca3176e7SBrian Feldman 
24921323ec57SEd Maste /* Prepare pollfd entries for a single channel */
24931323ec57SEd Maste static void
24941323ec57SEd Maste channel_prepare_pollfd(Channel *c, u_int *next_pollfd,
24951323ec57SEd Maste     struct pollfd *pfd, u_int npfd)
24961323ec57SEd Maste {
249787c1498dSEd Maste 	u_int ev, p = *next_pollfd;
24981323ec57SEd Maste 
24991323ec57SEd Maste 	if (c == NULL)
25001323ec57SEd Maste 		return;
25011323ec57SEd Maste 	if (p + 4 > npfd) {
25021323ec57SEd Maste 		/* Shouldn't happen */
25031323ec57SEd Maste 		fatal_f("channel %d: bad pfd offset %u (max %u)",
25041323ec57SEd Maste 		    c->self, p, npfd);
25051323ec57SEd Maste 	}
250687c1498dSEd Maste 	c->pfds[0] = c->pfds[1] = c->pfds[2] = c->pfds[3] = -1;
25071323ec57SEd Maste 	/*
25081323ec57SEd Maste 	 * prepare c->rfd
25091323ec57SEd Maste 	 *
25101323ec57SEd Maste 	 * This is a special case, since c->rfd might be the same as
25111323ec57SEd Maste 	 * c->wfd, c->efd and/or c->sock. Handle those here if they want
25121323ec57SEd Maste 	 * IO too.
25131323ec57SEd Maste 	 */
25141323ec57SEd Maste 	if (c->rfd != -1) {
251587c1498dSEd Maste 		ev = 0;
25161323ec57SEd Maste 		if ((c->io_want & SSH_CHAN_IO_RFD) != 0)
251787c1498dSEd Maste 			ev |= POLLIN;
25181323ec57SEd Maste 		/* rfd == wfd */
251987c1498dSEd Maste 		if (c->wfd == c->rfd) {
252087c1498dSEd Maste 			if ((c->io_want & SSH_CHAN_IO_WFD) != 0)
252187c1498dSEd Maste 				ev |= POLLOUT;
252287c1498dSEd Maste 		}
25231323ec57SEd Maste 		/* rfd == efd */
252487c1498dSEd Maste 		if (c->efd == c->rfd) {
252587c1498dSEd Maste 			if ((c->io_want & SSH_CHAN_IO_EFD_R) != 0)
252687c1498dSEd Maste 				ev |= POLLIN;
252787c1498dSEd Maste 			if ((c->io_want & SSH_CHAN_IO_EFD_W) != 0)
252887c1498dSEd Maste 				ev |= POLLOUT;
252987c1498dSEd Maste 		}
25301323ec57SEd Maste 		/* rfd == sock */
253187c1498dSEd Maste 		if (c->sock == c->rfd) {
253287c1498dSEd Maste 			if ((c->io_want & SSH_CHAN_IO_SOCK_R) != 0)
253387c1498dSEd Maste 				ev |= POLLIN;
253487c1498dSEd Maste 			if ((c->io_want & SSH_CHAN_IO_SOCK_W) != 0)
253587c1498dSEd Maste 				ev |= POLLOUT;
253687c1498dSEd Maste 		}
253787c1498dSEd Maste 		/* Pack a pfd entry if any event armed for this fd */
253887c1498dSEd Maste 		if (ev != 0) {
253987c1498dSEd Maste 			c->pfds[0] = p;
254087c1498dSEd Maste 			pfd[p].fd = c->rfd;
254187c1498dSEd Maste 			pfd[p].events = ev;
25421323ec57SEd Maste 			dump_channel_poll(__func__, "rfd", c, p, &pfd[p]);
25431323ec57SEd Maste 			p++;
25441323ec57SEd Maste 		}
254587c1498dSEd Maste 	}
254687c1498dSEd Maste 	/* prepare c->wfd if wanting IO and not already handled above */
25471323ec57SEd Maste 	if (c->wfd != -1 && c->rfd != c->wfd) {
254887c1498dSEd Maste 		ev = 0;
254987c1498dSEd Maste 		if ((c->io_want & SSH_CHAN_IO_WFD))
255087c1498dSEd Maste 			ev |= POLLOUT;
255187c1498dSEd Maste 		/* Pack a pfd entry if any event armed for this fd */
255287c1498dSEd Maste 		if (ev != 0) {
255387c1498dSEd Maste 			c->pfds[1] = p;
25541323ec57SEd Maste 			pfd[p].fd = c->wfd;
255587c1498dSEd Maste 			pfd[p].events = ev;
25561323ec57SEd Maste 			dump_channel_poll(__func__, "wfd", c, p, &pfd[p]);
25571323ec57SEd Maste 			p++;
25581323ec57SEd Maste 		}
255987c1498dSEd Maste 	}
256087c1498dSEd Maste 	/* prepare c->efd if wanting IO and not already handled above */
25611323ec57SEd Maste 	if (c->efd != -1 && c->rfd != c->efd) {
256287c1498dSEd Maste 		ev = 0;
25631323ec57SEd Maste 		if ((c->io_want & SSH_CHAN_IO_EFD_R) != 0)
256487c1498dSEd Maste 			ev |= POLLIN;
25651323ec57SEd Maste 		if ((c->io_want & SSH_CHAN_IO_EFD_W) != 0)
256687c1498dSEd Maste 			ev |= POLLOUT;
256787c1498dSEd Maste 		/* Pack a pfd entry if any event armed for this fd */
256887c1498dSEd Maste 		if (ev != 0) {
256987c1498dSEd Maste 			c->pfds[2] = p;
257087c1498dSEd Maste 			pfd[p].fd = c->efd;
257187c1498dSEd Maste 			pfd[p].events = ev;
25721323ec57SEd Maste 			dump_channel_poll(__func__, "efd", c, p, &pfd[p]);
25731323ec57SEd Maste 			p++;
25741323ec57SEd Maste 		}
257587c1498dSEd Maste 	}
257687c1498dSEd Maste 	/* prepare c->sock if wanting IO and not already handled above */
25771323ec57SEd Maste 	if (c->sock != -1 && c->rfd != c->sock) {
257887c1498dSEd Maste 		ev = 0;
257987c1498dSEd Maste 		if ((c->io_want & SSH_CHAN_IO_SOCK_R) != 0)
258087c1498dSEd Maste 			ev |= POLLIN;
258187c1498dSEd Maste 		if ((c->io_want & SSH_CHAN_IO_SOCK_W) != 0)
258287c1498dSEd Maste 			ev |= POLLOUT;
258387c1498dSEd Maste 		/* Pack a pfd entry if any event armed for this fd */
258487c1498dSEd Maste 		if (ev != 0) {
258587c1498dSEd Maste 			c->pfds[3] = p;
25861323ec57SEd Maste 			pfd[p].fd = c->sock;
25871323ec57SEd Maste 			pfd[p].events = 0;
25881323ec57SEd Maste 			dump_channel_poll(__func__, "sock", c, p, &pfd[p]);
25891323ec57SEd Maste 			p++;
25901323ec57SEd Maste 		}
259187c1498dSEd Maste 	}
25921323ec57SEd Maste 	*next_pollfd = p;
25931323ec57SEd Maste }
25941323ec57SEd Maste 
25951323ec57SEd Maste /* * Allocate/prepare poll structure */
25961323ec57SEd Maste void
25971323ec57SEd Maste channel_prepare_poll(struct ssh *ssh, struct pollfd **pfdp, u_int *npfd_allocp,
25981323ec57SEd Maste     u_int *npfd_activep, u_int npfd_reserved, time_t *minwait_secs)
25991323ec57SEd Maste {
26001323ec57SEd Maste 	struct ssh_channels *sc = ssh->chanctxt;
26011323ec57SEd Maste 	u_int i, oalloc, p, npfd = npfd_reserved;
26021323ec57SEd Maste 
26031323ec57SEd Maste 	channel_before_prepare_io(ssh); /* might create a new channel */
2604*9fce8d41SEd Maste 	/* clear out I/O flags from last poll */
2605*9fce8d41SEd Maste 	for (i = 0; i < sc->channels_alloc; i++) {
2606*9fce8d41SEd Maste 		if (sc->channels[i] == NULL)
2607*9fce8d41SEd Maste 			continue;
2608*9fce8d41SEd Maste 		sc->channels[i]->io_want = sc->channels[i]->io_ready = 0;
2609*9fce8d41SEd Maste 	}
26101323ec57SEd Maste 	/* Allocate 4x pollfd for each channel (rfd, wfd, efd, sock) */
26111323ec57SEd Maste 	if (sc->channels_alloc >= (INT_MAX / 4) - npfd_reserved)
26121323ec57SEd Maste 		fatal_f("too many channels"); /* shouldn't happen */
26134f52dfbbSDag-Erling Smørgrav 	if (!ssh_packet_is_rekeying(ssh))
26141323ec57SEd Maste 		npfd += sc->channels_alloc * 4;
26151323ec57SEd Maste 	if (npfd > *npfd_allocp) {
26161323ec57SEd Maste 		*pfdp = xrecallocarray(*pfdp, *npfd_allocp,
26171323ec57SEd Maste 		    npfd, sizeof(**pfdp));
26181323ec57SEd Maste 		*npfd_allocp = npfd;
26191323ec57SEd Maste 	}
26201323ec57SEd Maste 	*npfd_activep = npfd_reserved;
26211323ec57SEd Maste 	if (ssh_packet_is_rekeying(ssh))
26221323ec57SEd Maste 		return;
26231323ec57SEd Maste 
26241323ec57SEd Maste 	oalloc = sc->channels_alloc;
26251323ec57SEd Maste 
26261323ec57SEd Maste 	channel_handler(ssh, CHAN_PRE, minwait_secs);
26271323ec57SEd Maste 
26281323ec57SEd Maste 	if (oalloc != sc->channels_alloc) {
26291323ec57SEd Maste 		/* shouldn't happen */
26301323ec57SEd Maste 		fatal_f("channels_alloc changed during CHAN_PRE "
26311323ec57SEd Maste 		    "(was %u, now %u)", oalloc, sc->channels_alloc);
26321323ec57SEd Maste 	}
26331323ec57SEd Maste 
26341323ec57SEd Maste 	/* Prepare pollfd */
26351323ec57SEd Maste 	p = npfd_reserved;
26361323ec57SEd Maste 	for (i = 0; i < sc->channels_alloc; i++)
26371323ec57SEd Maste 		channel_prepare_pollfd(sc->channels[i], &p, *pfdp, npfd);
26381323ec57SEd Maste 	*npfd_activep = p;
26391323ec57SEd Maste }
26401323ec57SEd Maste 
26411323ec57SEd Maste static void
264287c1498dSEd Maste fd_ready(Channel *c, int p, struct pollfd *pfds, u_int npfd, int fd,
26431323ec57SEd Maste     const char *what, u_int revents_mask, u_int ready)
26441323ec57SEd Maste {
26451323ec57SEd Maste 	struct pollfd *pfd = &pfds[p];
26461323ec57SEd Maste 
26471323ec57SEd Maste 	if (fd == -1)
26481323ec57SEd Maste 		return;
264987c1498dSEd Maste 	if (p == -1 || (u_int)p >= npfd)
265087c1498dSEd Maste 		fatal_f("channel %d: bad pfd %d (max %u)", c->self, p, npfd);
26511323ec57SEd Maste 	dump_channel_poll(__func__, what, c, p, pfd);
26521323ec57SEd Maste 	if (pfd->fd != fd) {
26531323ec57SEd Maste 		fatal("channel %d: inconsistent %s fd=%d pollfd[%u].fd %d "
26541323ec57SEd Maste 		    "r%d w%d e%d s%d", c->self, what, fd, p, pfd->fd,
26551323ec57SEd Maste 		    c->rfd, c->wfd, c->efd, c->sock);
26561323ec57SEd Maste 	}
26571323ec57SEd Maste 	if ((pfd->revents & POLLNVAL) != 0) {
26581323ec57SEd Maste 		fatal("channel %d: invalid %s pollfd[%u].fd %d r%d w%d e%d s%d",
26591323ec57SEd Maste 		    c->self, what, p, pfd->fd, c->rfd, c->wfd, c->efd, c->sock);
26601323ec57SEd Maste 	}
26611323ec57SEd Maste 	if ((pfd->revents & (revents_mask|POLLHUP|POLLERR)) != 0)
26621323ec57SEd Maste 		c->io_ready |= ready & c->io_want;
2663a04a10f8SKris Kennaway }
2664a04a10f8SKris Kennaway 
2665af12a3e7SDag-Erling Smørgrav /*
26661323ec57SEd Maste  * After poll, perform any appropriate operations for channels which have
2667af12a3e7SDag-Erling Smørgrav  * events pending.
2668af12a3e7SDag-Erling Smørgrav  */
2669a04a10f8SKris Kennaway void
26701323ec57SEd Maste channel_after_poll(struct ssh *ssh, struct pollfd *pfd, u_int npfd)
2671a04a10f8SKris Kennaway {
26721323ec57SEd Maste 	struct ssh_channels *sc = ssh->chanctxt;
267387c1498dSEd Maste 	u_int i;
267487c1498dSEd Maste 	int p;
26751323ec57SEd Maste 	Channel *c;
26761323ec57SEd Maste 
26771323ec57SEd Maste #ifdef DEBUG_CHANNEL_POLL
267887c1498dSEd Maste 	for (p = 0; p < (int)npfd; p++) {
26791323ec57SEd Maste 		if (pfd[p].revents == 0)
26801323ec57SEd Maste 			continue;
26811323ec57SEd Maste 		debug_f("pfd[%u].fd %d rev 0x%04x",
26821323ec57SEd Maste 		    p, pfd[p].fd, pfd[p].revents);
26831323ec57SEd Maste 	}
26841323ec57SEd Maste #endif
26851323ec57SEd Maste 
26861323ec57SEd Maste 	/* Convert pollfd into c->io_ready */
26871323ec57SEd Maste 	for (i = 0; i < sc->channels_alloc; i++) {
26881323ec57SEd Maste 		c = sc->channels[i];
268987c1498dSEd Maste 		if (c == NULL)
26901323ec57SEd Maste 			continue;
26911323ec57SEd Maste 		/* if rfd is shared with efd/sock then wfd should be too */
26921323ec57SEd Maste 		if (c->rfd != -1 && c->wfd != -1 && c->rfd != c->wfd &&
26931323ec57SEd Maste 		    (c->rfd == c->efd || c->rfd == c->sock)) {
26941323ec57SEd Maste 			/* Shouldn't happen */
26951323ec57SEd Maste 			fatal_f("channel %d: unexpected fds r%d w%d e%d s%d",
26961323ec57SEd Maste 			    c->self, c->rfd, c->wfd, c->efd, c->sock);
26971323ec57SEd Maste 		}
26981323ec57SEd Maste 		c->io_ready = 0;
26991323ec57SEd Maste 		/* rfd, potentially shared with wfd, efd and sock */
270087c1498dSEd Maste 		if (c->rfd != -1 && (p = c->pfds[0]) != -1) {
270187c1498dSEd Maste 			fd_ready(c, p, pfd, npfd, c->rfd,
270287c1498dSEd Maste 			    "rfd", POLLIN, SSH_CHAN_IO_RFD);
27031323ec57SEd Maste 			if (c->rfd == c->wfd) {
270487c1498dSEd Maste 				fd_ready(c, p, pfd, npfd, c->wfd,
270587c1498dSEd Maste 				    "wfd/r", POLLOUT, SSH_CHAN_IO_WFD);
27061323ec57SEd Maste 			}
27071323ec57SEd Maste 			if (c->rfd == c->efd) {
270887c1498dSEd Maste 				fd_ready(c, p, pfd, npfd, c->efd,
270987c1498dSEd Maste 				    "efdr/r", POLLIN, SSH_CHAN_IO_EFD_R);
271087c1498dSEd Maste 				fd_ready(c, p, pfd, npfd, c->efd,
271187c1498dSEd Maste 				    "efdw/r", POLLOUT, SSH_CHAN_IO_EFD_W);
27121323ec57SEd Maste 			}
27131323ec57SEd Maste 			if (c->rfd == c->sock) {
271487c1498dSEd Maste 				fd_ready(c, p, pfd, npfd, c->sock,
271587c1498dSEd Maste 				    "sockr/r", POLLIN, SSH_CHAN_IO_SOCK_R);
271687c1498dSEd Maste 				fd_ready(c, p, pfd, npfd, c->sock,
271787c1498dSEd Maste 				    "sockw/r", POLLOUT, SSH_CHAN_IO_SOCK_W);
27181323ec57SEd Maste 			}
271987c1498dSEd Maste 			dump_channel_poll(__func__, "rfd", c, p, pfd);
27201323ec57SEd Maste 		}
27211323ec57SEd Maste 		/* wfd */
272287c1498dSEd Maste 		if (c->wfd != -1 && c->wfd != c->rfd &&
272387c1498dSEd Maste 		    (p = c->pfds[1]) != -1) {
272487c1498dSEd Maste 			fd_ready(c, p, pfd, npfd, c->wfd,
272587c1498dSEd Maste 			    "wfd", POLLOUT, SSH_CHAN_IO_WFD);
272687c1498dSEd Maste 			dump_channel_poll(__func__, "wfd", c, p, pfd);
27271323ec57SEd Maste 		}
27281323ec57SEd Maste 		/* efd */
272987c1498dSEd Maste 		if (c->efd != -1 && c->efd != c->rfd &&
273087c1498dSEd Maste 		    (p = c->pfds[2]) != -1) {
273187c1498dSEd Maste 			fd_ready(c, p, pfd, npfd, c->efd,
273287c1498dSEd Maste 			    "efdr", POLLIN, SSH_CHAN_IO_EFD_R);
273387c1498dSEd Maste 			fd_ready(c, p, pfd, npfd, c->efd,
273487c1498dSEd Maste 			    "efdw", POLLOUT, SSH_CHAN_IO_EFD_W);
273587c1498dSEd Maste 			dump_channel_poll(__func__, "efd", c, p, pfd);
27361323ec57SEd Maste 		}
27371323ec57SEd Maste 		/* sock */
273887c1498dSEd Maste 		if (c->sock != -1 && c->sock != c->rfd &&
273987c1498dSEd Maste 		    (p = c->pfds[3]) != -1) {
274087c1498dSEd Maste 			fd_ready(c, p, pfd, npfd, c->sock,
274187c1498dSEd Maste 			    "sockr", POLLIN, SSH_CHAN_IO_SOCK_R);
274287c1498dSEd Maste 			fd_ready(c, p, pfd, npfd, c->sock,
274387c1498dSEd Maste 			    "sockw", POLLOUT, SSH_CHAN_IO_SOCK_W);
274487c1498dSEd Maste 			dump_channel_poll(__func__, "sock", c, p, pfd);
27451323ec57SEd Maste 		}
27461323ec57SEd Maste 	}
27471323ec57SEd Maste 	channel_handler(ssh, CHAN_POST, NULL);
2748511b41d2SMark Murray }
2749511b41d2SMark Murray 
27504f52dfbbSDag-Erling Smørgrav /*
27514f52dfbbSDag-Erling Smørgrav  * Enqueue data for channels with open or draining c->input.
27524f52dfbbSDag-Erling Smørgrav  */
27534f52dfbbSDag-Erling Smørgrav static void
27544f52dfbbSDag-Erling Smørgrav channel_output_poll_input_open(struct ssh *ssh, Channel *c)
27554f52dfbbSDag-Erling Smørgrav {
27564f52dfbbSDag-Erling Smørgrav 	size_t len, plen;
27574f52dfbbSDag-Erling Smørgrav 	const u_char *pkt;
27584f52dfbbSDag-Erling Smørgrav 	int r;
27594f52dfbbSDag-Erling Smørgrav 
27604f52dfbbSDag-Erling Smørgrav 	if ((len = sshbuf_len(c->input)) == 0) {
27614f52dfbbSDag-Erling Smørgrav 		if (c->istate == CHAN_INPUT_WAIT_DRAIN) {
27624f52dfbbSDag-Erling Smørgrav 			/*
27634f52dfbbSDag-Erling Smørgrav 			 * input-buffer is empty and read-socket shutdown:
27644f52dfbbSDag-Erling Smørgrav 			 * tell peer, that we will not send more data:
27654f52dfbbSDag-Erling Smørgrav 			 * send IEOF.
27664f52dfbbSDag-Erling Smørgrav 			 * hack for extended data: delay EOF if EFD still
27674f52dfbbSDag-Erling Smørgrav 			 * in use.
27684f52dfbbSDag-Erling Smørgrav 			 */
27694f52dfbbSDag-Erling Smørgrav 			if (CHANNEL_EFD_INPUT_ACTIVE(c))
27704f52dfbbSDag-Erling Smørgrav 				debug2("channel %d: "
27714f52dfbbSDag-Erling Smørgrav 				    "ibuf_empty delayed efd %d/(%zu)",
27724f52dfbbSDag-Erling Smørgrav 				    c->self, c->efd, sshbuf_len(c->extended));
27734f52dfbbSDag-Erling Smørgrav 			else
27744f52dfbbSDag-Erling Smørgrav 				chan_ibuf_empty(ssh, c);
27754f52dfbbSDag-Erling Smørgrav 		}
27764f52dfbbSDag-Erling Smørgrav 		return;
27774f52dfbbSDag-Erling Smørgrav 	}
27784f52dfbbSDag-Erling Smørgrav 
27794f52dfbbSDag-Erling Smørgrav 	if (!c->have_remote_id)
278019261079SEd Maste 		fatal_f("channel %d: no remote id", c->self);
27814f52dfbbSDag-Erling Smørgrav 
27824f52dfbbSDag-Erling Smørgrav 	if (c->datagram) {
27834f52dfbbSDag-Erling Smørgrav 		/* Check datagram will fit; drop if not */
27844f52dfbbSDag-Erling Smørgrav 		if ((r = sshbuf_get_string_direct(c->input, &pkt, &plen)) != 0)
278519261079SEd Maste 			fatal_fr(r, "channel %i: get datagram", c->self);
27864f52dfbbSDag-Erling Smørgrav 		/*
27874f52dfbbSDag-Erling Smørgrav 		 * XXX this does tail-drop on the datagram queue which is
27884f52dfbbSDag-Erling Smørgrav 		 * usually suboptimal compared to head-drop. Better to have
27894f52dfbbSDag-Erling Smørgrav 		 * backpressure at read time? (i.e. read + discard)
27904f52dfbbSDag-Erling Smørgrav 		 */
27914f52dfbbSDag-Erling Smørgrav 		if (plen > c->remote_window || plen > c->remote_maxpacket) {
27924f52dfbbSDag-Erling Smørgrav 			debug("channel %d: datagram too big", c->self);
27934f52dfbbSDag-Erling Smørgrav 			return;
27944f52dfbbSDag-Erling Smørgrav 		}
27954f52dfbbSDag-Erling Smørgrav 		/* Enqueue it */
27964f52dfbbSDag-Erling Smørgrav 		if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_DATA)) != 0 ||
27974f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
27984f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_string(ssh, pkt, plen)) != 0 ||
279919261079SEd Maste 		    (r = sshpkt_send(ssh)) != 0)
280019261079SEd Maste 			fatal_fr(r, "channel %i: send datagram", c->self);
28014f52dfbbSDag-Erling Smørgrav 		c->remote_window -= plen;
28024f52dfbbSDag-Erling Smørgrav 		return;
28034f52dfbbSDag-Erling Smørgrav 	}
28044f52dfbbSDag-Erling Smørgrav 
28054f52dfbbSDag-Erling Smørgrav 	/* Enqueue packet for buffered data. */
28064f52dfbbSDag-Erling Smørgrav 	if (len > c->remote_window)
28074f52dfbbSDag-Erling Smørgrav 		len = c->remote_window;
28084f52dfbbSDag-Erling Smørgrav 	if (len > c->remote_maxpacket)
28094f52dfbbSDag-Erling Smørgrav 		len = c->remote_maxpacket;
28104f52dfbbSDag-Erling Smørgrav 	if (len == 0)
28114f52dfbbSDag-Erling Smørgrav 		return;
28124f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_DATA)) != 0 ||
28134f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
28144f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_string(ssh, sshbuf_ptr(c->input), len)) != 0 ||
281519261079SEd Maste 	    (r = sshpkt_send(ssh)) != 0)
281619261079SEd Maste 		fatal_fr(r, "channel %i: send data", c->self);
28174f52dfbbSDag-Erling Smørgrav 	if ((r = sshbuf_consume(c->input, len)) != 0)
281819261079SEd Maste 		fatal_fr(r, "channel %i: consume", c->self);
28194f52dfbbSDag-Erling Smørgrav 	c->remote_window -= len;
28204f52dfbbSDag-Erling Smørgrav }
28214f52dfbbSDag-Erling Smørgrav 
28224f52dfbbSDag-Erling Smørgrav /*
28234f52dfbbSDag-Erling Smørgrav  * Enqueue data for channels with open c->extended in read mode.
28244f52dfbbSDag-Erling Smørgrav  */
28254f52dfbbSDag-Erling Smørgrav static void
28264f52dfbbSDag-Erling Smørgrav channel_output_poll_extended_read(struct ssh *ssh, Channel *c)
28274f52dfbbSDag-Erling Smørgrav {
28284f52dfbbSDag-Erling Smørgrav 	size_t len;
28294f52dfbbSDag-Erling Smørgrav 	int r;
28304f52dfbbSDag-Erling Smørgrav 
28314f52dfbbSDag-Erling Smørgrav 	if ((len = sshbuf_len(c->extended)) == 0)
28324f52dfbbSDag-Erling Smørgrav 		return;
28334f52dfbbSDag-Erling Smørgrav 
28344f52dfbbSDag-Erling Smørgrav 	debug2("channel %d: rwin %u elen %zu euse %d", c->self,
28354f52dfbbSDag-Erling Smørgrav 	    c->remote_window, sshbuf_len(c->extended), c->extended_usage);
28364f52dfbbSDag-Erling Smørgrav 	if (len > c->remote_window)
28374f52dfbbSDag-Erling Smørgrav 		len = c->remote_window;
28384f52dfbbSDag-Erling Smørgrav 	if (len > c->remote_maxpacket)
28394f52dfbbSDag-Erling Smørgrav 		len = c->remote_maxpacket;
28404f52dfbbSDag-Erling Smørgrav 	if (len == 0)
28414f52dfbbSDag-Erling Smørgrav 		return;
28424f52dfbbSDag-Erling Smørgrav 	if (!c->have_remote_id)
284319261079SEd Maste 		fatal_f("channel %d: no remote id", c->self);
28444f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_EXTENDED_DATA)) != 0 ||
28454f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
28464f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u32(ssh, SSH2_EXTENDED_DATA_STDERR)) != 0 ||
28474f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_string(ssh, sshbuf_ptr(c->extended), len)) != 0 ||
284819261079SEd Maste 	    (r = sshpkt_send(ssh)) != 0)
284919261079SEd Maste 		fatal_fr(r, "channel %i: data", c->self);
28504f52dfbbSDag-Erling Smørgrav 	if ((r = sshbuf_consume(c->extended, len)) != 0)
285119261079SEd Maste 		fatal_fr(r, "channel %i: consume", c->self);
28524f52dfbbSDag-Erling Smørgrav 	c->remote_window -= len;
28534f52dfbbSDag-Erling Smørgrav 	debug2("channel %d: sent ext data %zu", c->self, len);
28544f52dfbbSDag-Erling Smørgrav }
2855af12a3e7SDag-Erling Smørgrav 
2856ca3176e7SBrian Feldman /* If there is data to send to the connection, enqueue some of it now. */
2857511b41d2SMark Murray void
28584f52dfbbSDag-Erling Smørgrav channel_output_poll(struct ssh *ssh)
2859511b41d2SMark Murray {
28604f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
2861a04a10f8SKris Kennaway 	Channel *c;
28624f52dfbbSDag-Erling Smørgrav 	u_int i;
2863511b41d2SMark Murray 
28644f52dfbbSDag-Erling Smørgrav 	for (i = 0; i < sc->channels_alloc; i++) {
28654f52dfbbSDag-Erling Smørgrav 		c = sc->channels[i];
2866af12a3e7SDag-Erling Smørgrav 		if (c == NULL)
2867af12a3e7SDag-Erling Smørgrav 			continue;
2868511b41d2SMark Murray 
2869af12a3e7SDag-Erling Smørgrav 		/*
2870af12a3e7SDag-Erling Smørgrav 		 * We are only interested in channels that can have buffered
2871af12a3e7SDag-Erling Smørgrav 		 * incoming data.
2872af12a3e7SDag-Erling Smørgrav 		 */
2873a04a10f8SKris Kennaway 		if (c->type != SSH_CHANNEL_OPEN)
2874511b41d2SMark Murray 			continue;
28754f52dfbbSDag-Erling Smørgrav 		if ((c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD))) {
2876ca3176e7SBrian Feldman 			/* XXX is this true? */
28774f52dfbbSDag-Erling Smørgrav 			debug3("channel %d: will not send data after close",
28784f52dfbbSDag-Erling Smørgrav 			    c->self);
2879511b41d2SMark Murray 			continue;
2880511b41d2SMark Murray 		}
2881511b41d2SMark Murray 
2882511b41d2SMark Murray 		/* Get the amount of buffered data for this channel. */
28834f52dfbbSDag-Erling Smørgrav 		if (c->istate == CHAN_INPUT_OPEN ||
28844f52dfbbSDag-Erling Smørgrav 		    c->istate == CHAN_INPUT_WAIT_DRAIN)
28854f52dfbbSDag-Erling Smørgrav 			channel_output_poll_input_open(ssh, c);
2886a04a10f8SKris Kennaway 		/* Send extended data, i.e. stderr */
28874f52dfbbSDag-Erling Smørgrav 		if (!(c->flags & CHAN_EOF_SENT) &&
28884f52dfbbSDag-Erling Smørgrav 		    c->extended_usage == CHAN_EXTENDED_READ)
28894f52dfbbSDag-Erling Smørgrav 			channel_output_poll_extended_read(ssh, c);
2890511b41d2SMark Murray 	}
2891511b41d2SMark Murray }
2892511b41d2SMark Murray 
2893ca86bcf2SDag-Erling Smørgrav /* -- mux proxy support  */
2894ca86bcf2SDag-Erling Smørgrav 
2895ca86bcf2SDag-Erling Smørgrav /*
2896ca86bcf2SDag-Erling Smørgrav  * When multiplexing channel messages for mux clients we have to deal
2897ca86bcf2SDag-Erling Smørgrav  * with downstream messages from the mux client and upstream messages
2898ca86bcf2SDag-Erling Smørgrav  * from the ssh server:
2899ca86bcf2SDag-Erling Smørgrav  * 1) Handling downstream messages is straightforward and happens
2900ca86bcf2SDag-Erling Smørgrav  *    in channel_proxy_downstream():
2901ca86bcf2SDag-Erling Smørgrav  *    - We forward all messages (mostly) unmodified to the server.
2902ca86bcf2SDag-Erling Smørgrav  *    - However, in order to route messages from upstream to the correct
2903ca86bcf2SDag-Erling Smørgrav  *      downstream client, we have to replace the channel IDs used by the
2904ca86bcf2SDag-Erling Smørgrav  *      mux clients with a unique channel ID because the mux clients might
2905ca86bcf2SDag-Erling Smørgrav  *      use conflicting channel IDs.
2906ca86bcf2SDag-Erling Smørgrav  *    - so we inspect and change both SSH2_MSG_CHANNEL_OPEN and
2907ca86bcf2SDag-Erling Smørgrav  *      SSH2_MSG_CHANNEL_OPEN_CONFIRMATION messages, create a local
2908ca86bcf2SDag-Erling Smørgrav  *      SSH_CHANNEL_MUX_PROXY channel and replace the mux clients ID
2909ca86bcf2SDag-Erling Smørgrav  *      with the newly allocated channel ID.
2910ca86bcf2SDag-Erling Smørgrav  * 2) Upstream messages are received by matching SSH_CHANNEL_MUX_PROXY
2911190cef3dSDag-Erling Smørgrav  *    channels and processed by channel_proxy_upstream(). The local channel ID
2912ca86bcf2SDag-Erling Smørgrav  *    is then translated back to the original mux client ID.
2913ca86bcf2SDag-Erling Smørgrav  * 3) In both cases we need to keep track of matching SSH2_MSG_CHANNEL_CLOSE
2914ca86bcf2SDag-Erling Smørgrav  *    messages so we can clean up SSH_CHANNEL_MUX_PROXY channels.
2915ca86bcf2SDag-Erling Smørgrav  * 4) The SSH_CHANNEL_MUX_PROXY channels also need to closed when the
2916ca86bcf2SDag-Erling Smørgrav  *    downstream mux client are removed.
2917ca86bcf2SDag-Erling Smørgrav  * 5) Handling SSH2_MSG_CHANNEL_OPEN messages from the upstream server
2918ca86bcf2SDag-Erling Smørgrav  *    requires more work, because they are not addressed to a specific
2919ca86bcf2SDag-Erling Smørgrav  *    channel. E.g. client_request_forwarded_tcpip() needs to figure
2920ca86bcf2SDag-Erling Smørgrav  *    out whether the request is addressed to the local client or a
2921ca86bcf2SDag-Erling Smørgrav  *    specific downstream client based on the listen-address/port.
2922190cef3dSDag-Erling Smørgrav  * 6) Agent and X11-Forwarding have a similar problem and are currently
2923ca86bcf2SDag-Erling Smørgrav  *    not supported as the matching session/channel cannot be identified
2924ca86bcf2SDag-Erling Smørgrav  *    easily.
2925ca86bcf2SDag-Erling Smørgrav  */
2926ca86bcf2SDag-Erling Smørgrav 
2927ca86bcf2SDag-Erling Smørgrav /*
2928ca86bcf2SDag-Erling Smørgrav  * receive packets from downstream mux clients:
2929ca86bcf2SDag-Erling Smørgrav  * channel callback fired on read from mux client, creates
2930ca86bcf2SDag-Erling Smørgrav  * SSH_CHANNEL_MUX_PROXY channels and translates channel IDs
2931ca86bcf2SDag-Erling Smørgrav  * on channel creation.
2932ca86bcf2SDag-Erling Smørgrav  */
2933ca86bcf2SDag-Erling Smørgrav int
29344f52dfbbSDag-Erling Smørgrav channel_proxy_downstream(struct ssh *ssh, Channel *downstream)
2935ca86bcf2SDag-Erling Smørgrav {
2936ca86bcf2SDag-Erling Smørgrav 	Channel *c = NULL;
2937ca86bcf2SDag-Erling Smørgrav 	struct sshbuf *original = NULL, *modified = NULL;
2938ca86bcf2SDag-Erling Smørgrav 	const u_char *cp;
2939ca86bcf2SDag-Erling Smørgrav 	char *ctype = NULL, *listen_host = NULL;
2940ca86bcf2SDag-Erling Smørgrav 	u_char type;
2941ca86bcf2SDag-Erling Smørgrav 	size_t have;
29424f52dfbbSDag-Erling Smørgrav 	int ret = -1, r;
2943ca86bcf2SDag-Erling Smørgrav 	u_int id, remote_id, listen_port;
2944ca86bcf2SDag-Erling Smørgrav 
29454f52dfbbSDag-Erling Smørgrav 	/* sshbuf_dump(downstream->input, stderr); */
29464f52dfbbSDag-Erling Smørgrav 	if ((r = sshbuf_get_string_direct(downstream->input, &cp, &have))
2947ca86bcf2SDag-Erling Smørgrav 	    != 0) {
294819261079SEd Maste 		error_fr(r, "parse");
2949ca86bcf2SDag-Erling Smørgrav 		return -1;
2950ca86bcf2SDag-Erling Smørgrav 	}
2951ca86bcf2SDag-Erling Smørgrav 	if (have < 2) {
295219261079SEd Maste 		error_f("short message");
2953ca86bcf2SDag-Erling Smørgrav 		return -1;
2954ca86bcf2SDag-Erling Smørgrav 	}
2955ca86bcf2SDag-Erling Smørgrav 	type = cp[1];
2956ca86bcf2SDag-Erling Smørgrav 	/* skip padlen + type */
2957ca86bcf2SDag-Erling Smørgrav 	cp += 2;
2958ca86bcf2SDag-Erling Smørgrav 	have -= 2;
2959ca86bcf2SDag-Erling Smørgrav 	if (ssh_packet_log_type(type))
296019261079SEd Maste 		debug3_f("channel %u: down->up: type %u",
2961ca86bcf2SDag-Erling Smørgrav 		    downstream->self, type);
2962ca86bcf2SDag-Erling Smørgrav 
2963ca86bcf2SDag-Erling Smørgrav 	switch (type) {
2964ca86bcf2SDag-Erling Smørgrav 	case SSH2_MSG_CHANNEL_OPEN:
2965ca86bcf2SDag-Erling Smørgrav 		if ((original = sshbuf_from(cp, have)) == NULL ||
2966ca86bcf2SDag-Erling Smørgrav 		    (modified = sshbuf_new()) == NULL) {
296719261079SEd Maste 			error_f("alloc");
2968ca86bcf2SDag-Erling Smørgrav 			goto out;
2969ca86bcf2SDag-Erling Smørgrav 		}
2970ca86bcf2SDag-Erling Smørgrav 		if ((r = sshbuf_get_cstring(original, &ctype, NULL)) != 0 ||
2971ca86bcf2SDag-Erling Smørgrav 		    (r = sshbuf_get_u32(original, &id)) != 0) {
297219261079SEd Maste 			error_fr(r, "parse");
2973ca86bcf2SDag-Erling Smørgrav 			goto out;
2974ca86bcf2SDag-Erling Smørgrav 		}
29754f52dfbbSDag-Erling Smørgrav 		c = channel_new(ssh, "mux proxy", SSH_CHANNEL_MUX_PROXY,
2976ca86bcf2SDag-Erling Smørgrav 		    -1, -1, -1, 0, 0, 0, ctype, 1);
2977ca86bcf2SDag-Erling Smørgrav 		c->mux_ctx = downstream;	/* point to mux client */
2978ca86bcf2SDag-Erling Smørgrav 		c->mux_downstream_id = id;	/* original downstream id */
2979ca86bcf2SDag-Erling Smørgrav 		if ((r = sshbuf_put_cstring(modified, ctype)) != 0 ||
2980ca86bcf2SDag-Erling Smørgrav 		    (r = sshbuf_put_u32(modified, c->self)) != 0 ||
2981ca86bcf2SDag-Erling Smørgrav 		    (r = sshbuf_putb(modified, original)) != 0) {
298219261079SEd Maste 			error_fr(r, "compose");
29834f52dfbbSDag-Erling Smørgrav 			channel_free(ssh, c);
2984ca86bcf2SDag-Erling Smørgrav 			goto out;
2985ca86bcf2SDag-Erling Smørgrav 		}
2986ca86bcf2SDag-Erling Smørgrav 		break;
2987ca86bcf2SDag-Erling Smørgrav 	case SSH2_MSG_CHANNEL_OPEN_CONFIRMATION:
2988ca86bcf2SDag-Erling Smørgrav 		/*
2989ca86bcf2SDag-Erling Smørgrav 		 * Almost the same as SSH2_MSG_CHANNEL_OPEN, except then we
2990ca86bcf2SDag-Erling Smørgrav 		 * need to parse 'remote_id' instead of 'ctype'.
2991ca86bcf2SDag-Erling Smørgrav 		 */
2992ca86bcf2SDag-Erling Smørgrav 		if ((original = sshbuf_from(cp, have)) == NULL ||
2993ca86bcf2SDag-Erling Smørgrav 		    (modified = sshbuf_new()) == NULL) {
299419261079SEd Maste 			error_f("alloc");
2995ca86bcf2SDag-Erling Smørgrav 			goto out;
2996ca86bcf2SDag-Erling Smørgrav 		}
2997ca86bcf2SDag-Erling Smørgrav 		if ((r = sshbuf_get_u32(original, &remote_id)) != 0 ||
2998ca86bcf2SDag-Erling Smørgrav 		    (r = sshbuf_get_u32(original, &id)) != 0) {
299919261079SEd Maste 			error_fr(r, "parse");
3000ca86bcf2SDag-Erling Smørgrav 			goto out;
3001ca86bcf2SDag-Erling Smørgrav 		}
30024f52dfbbSDag-Erling Smørgrav 		c = channel_new(ssh, "mux proxy", SSH_CHANNEL_MUX_PROXY,
3003ca86bcf2SDag-Erling Smørgrav 		    -1, -1, -1, 0, 0, 0, "mux-down-connect", 1);
3004ca86bcf2SDag-Erling Smørgrav 		c->mux_ctx = downstream;	/* point to mux client */
3005ca86bcf2SDag-Erling Smørgrav 		c->mux_downstream_id = id;
3006ca86bcf2SDag-Erling Smørgrav 		c->remote_id = remote_id;
30074f52dfbbSDag-Erling Smørgrav 		c->have_remote_id = 1;
3008ca86bcf2SDag-Erling Smørgrav 		if ((r = sshbuf_put_u32(modified, remote_id)) != 0 ||
3009ca86bcf2SDag-Erling Smørgrav 		    (r = sshbuf_put_u32(modified, c->self)) != 0 ||
3010ca86bcf2SDag-Erling Smørgrav 		    (r = sshbuf_putb(modified, original)) != 0) {
301119261079SEd Maste 			error_fr(r, "compose");
30124f52dfbbSDag-Erling Smørgrav 			channel_free(ssh, c);
3013ca86bcf2SDag-Erling Smørgrav 			goto out;
3014ca86bcf2SDag-Erling Smørgrav 		}
3015ca86bcf2SDag-Erling Smørgrav 		break;
3016ca86bcf2SDag-Erling Smørgrav 	case SSH2_MSG_GLOBAL_REQUEST:
3017ca86bcf2SDag-Erling Smørgrav 		if ((original = sshbuf_from(cp, have)) == NULL) {
301819261079SEd Maste 			error_f("alloc");
3019ca86bcf2SDag-Erling Smørgrav 			goto out;
3020ca86bcf2SDag-Erling Smørgrav 		}
3021ca86bcf2SDag-Erling Smørgrav 		if ((r = sshbuf_get_cstring(original, &ctype, NULL)) != 0) {
302219261079SEd Maste 			error_fr(r, "parse");
3023ca86bcf2SDag-Erling Smørgrav 			goto out;
3024ca86bcf2SDag-Erling Smørgrav 		}
3025ca86bcf2SDag-Erling Smørgrav 		if (strcmp(ctype, "tcpip-forward") != 0) {
302619261079SEd Maste 			error_f("unsupported request %s", ctype);
3027ca86bcf2SDag-Erling Smørgrav 			goto out;
3028ca86bcf2SDag-Erling Smørgrav 		}
3029ca86bcf2SDag-Erling Smørgrav 		if ((r = sshbuf_get_u8(original, NULL)) != 0 ||
3030ca86bcf2SDag-Erling Smørgrav 		    (r = sshbuf_get_cstring(original, &listen_host, NULL)) != 0 ||
3031ca86bcf2SDag-Erling Smørgrav 		    (r = sshbuf_get_u32(original, &listen_port)) != 0) {
303219261079SEd Maste 			error_fr(r, "parse");
3033ca86bcf2SDag-Erling Smørgrav 			goto out;
3034ca86bcf2SDag-Erling Smørgrav 		}
3035ca86bcf2SDag-Erling Smørgrav 		if (listen_port > 65535) {
303619261079SEd Maste 			error_f("tcpip-forward for %s: bad port %u",
303719261079SEd Maste 			    listen_host, listen_port);
3038ca86bcf2SDag-Erling Smørgrav 			goto out;
3039ca86bcf2SDag-Erling Smørgrav 		}
3040ca86bcf2SDag-Erling Smørgrav 		/* Record that connection to this host/port is permitted. */
3041190cef3dSDag-Erling Smørgrav 		permission_set_add(ssh, FORWARD_USER, FORWARD_LOCAL, "<mux>", -1,
30424f52dfbbSDag-Erling Smørgrav 		    listen_host, NULL, (int)listen_port, downstream);
3043ca86bcf2SDag-Erling Smørgrav 		listen_host = NULL;
3044ca86bcf2SDag-Erling Smørgrav 		break;
3045ca86bcf2SDag-Erling Smørgrav 	case SSH2_MSG_CHANNEL_CLOSE:
3046ca86bcf2SDag-Erling Smørgrav 		if (have < 4)
3047ca86bcf2SDag-Erling Smørgrav 			break;
3048ca86bcf2SDag-Erling Smørgrav 		remote_id = PEEK_U32(cp);
30494f52dfbbSDag-Erling Smørgrav 		if ((c = channel_by_remote_id(ssh, remote_id)) != NULL) {
3050ca86bcf2SDag-Erling Smørgrav 			if (c->flags & CHAN_CLOSE_RCVD)
30514f52dfbbSDag-Erling Smørgrav 				channel_free(ssh, c);
3052ca86bcf2SDag-Erling Smørgrav 			else
3053ca86bcf2SDag-Erling Smørgrav 				c->flags |= CHAN_CLOSE_SENT;
3054ca86bcf2SDag-Erling Smørgrav 		}
3055ca86bcf2SDag-Erling Smørgrav 		break;
3056ca86bcf2SDag-Erling Smørgrav 	}
3057ca86bcf2SDag-Erling Smørgrav 	if (modified) {
3058ca86bcf2SDag-Erling Smørgrav 		if ((r = sshpkt_start(ssh, type)) != 0 ||
3059ca86bcf2SDag-Erling Smørgrav 		    (r = sshpkt_putb(ssh, modified)) != 0 ||
3060ca86bcf2SDag-Erling Smørgrav 		    (r = sshpkt_send(ssh)) != 0) {
306119261079SEd Maste 			error_fr(r, "send");
3062ca86bcf2SDag-Erling Smørgrav 			goto out;
3063ca86bcf2SDag-Erling Smørgrav 		}
3064ca86bcf2SDag-Erling Smørgrav 	} else {
3065ca86bcf2SDag-Erling Smørgrav 		if ((r = sshpkt_start(ssh, type)) != 0 ||
3066ca86bcf2SDag-Erling Smørgrav 		    (r = sshpkt_put(ssh, cp, have)) != 0 ||
3067ca86bcf2SDag-Erling Smørgrav 		    (r = sshpkt_send(ssh)) != 0) {
306819261079SEd Maste 			error_fr(r, "send");
3069ca86bcf2SDag-Erling Smørgrav 			goto out;
3070ca86bcf2SDag-Erling Smørgrav 		}
3071ca86bcf2SDag-Erling Smørgrav 	}
3072ca86bcf2SDag-Erling Smørgrav 	ret = 0;
3073ca86bcf2SDag-Erling Smørgrav  out:
3074ca86bcf2SDag-Erling Smørgrav 	free(ctype);
3075ca86bcf2SDag-Erling Smørgrav 	free(listen_host);
3076ca86bcf2SDag-Erling Smørgrav 	sshbuf_free(original);
3077ca86bcf2SDag-Erling Smørgrav 	sshbuf_free(modified);
3078ca86bcf2SDag-Erling Smørgrav 	return ret;
3079ca86bcf2SDag-Erling Smørgrav }
3080ca86bcf2SDag-Erling Smørgrav 
3081ca86bcf2SDag-Erling Smørgrav /*
3082ca86bcf2SDag-Erling Smørgrav  * receive packets from upstream server and de-multiplex packets
3083ca86bcf2SDag-Erling Smørgrav  * to correct downstream:
3084ca86bcf2SDag-Erling Smørgrav  * implemented as a helper for channel input handlers,
3085ca86bcf2SDag-Erling Smørgrav  * replaces local (proxy) channel ID with downstream channel ID.
3086ca86bcf2SDag-Erling Smørgrav  */
3087ca86bcf2SDag-Erling Smørgrav int
30884f52dfbbSDag-Erling Smørgrav channel_proxy_upstream(Channel *c, int type, u_int32_t seq, struct ssh *ssh)
3089ca86bcf2SDag-Erling Smørgrav {
3090ca86bcf2SDag-Erling Smørgrav 	struct sshbuf *b = NULL;
3091ca86bcf2SDag-Erling Smørgrav 	Channel *downstream;
3092ca86bcf2SDag-Erling Smørgrav 	const u_char *cp = NULL;
3093ca86bcf2SDag-Erling Smørgrav 	size_t len;
3094ca86bcf2SDag-Erling Smørgrav 	int r;
3095ca86bcf2SDag-Erling Smørgrav 
3096ca86bcf2SDag-Erling Smørgrav 	/*
3097ca86bcf2SDag-Erling Smørgrav 	 * When receiving packets from the peer we need to check whether we
3098ca86bcf2SDag-Erling Smørgrav 	 * need to forward the packets to the mux client. In this case we
3099190cef3dSDag-Erling Smørgrav 	 * restore the original channel id and keep track of CLOSE messages,
3100ca86bcf2SDag-Erling Smørgrav 	 * so we can cleanup the channel.
3101ca86bcf2SDag-Erling Smørgrav 	 */
3102ca86bcf2SDag-Erling Smørgrav 	if (c == NULL || c->type != SSH_CHANNEL_MUX_PROXY)
3103ca86bcf2SDag-Erling Smørgrav 		return 0;
3104ca86bcf2SDag-Erling Smørgrav 	if ((downstream = c->mux_ctx) == NULL)
3105ca86bcf2SDag-Erling Smørgrav 		return 0;
3106ca86bcf2SDag-Erling Smørgrav 	switch (type) {
3107ca86bcf2SDag-Erling Smørgrav 	case SSH2_MSG_CHANNEL_CLOSE:
3108ca86bcf2SDag-Erling Smørgrav 	case SSH2_MSG_CHANNEL_DATA:
3109ca86bcf2SDag-Erling Smørgrav 	case SSH2_MSG_CHANNEL_EOF:
3110ca86bcf2SDag-Erling Smørgrav 	case SSH2_MSG_CHANNEL_EXTENDED_DATA:
3111ca86bcf2SDag-Erling Smørgrav 	case SSH2_MSG_CHANNEL_OPEN_CONFIRMATION:
3112ca86bcf2SDag-Erling Smørgrav 	case SSH2_MSG_CHANNEL_OPEN_FAILURE:
3113ca86bcf2SDag-Erling Smørgrav 	case SSH2_MSG_CHANNEL_WINDOW_ADJUST:
3114ca86bcf2SDag-Erling Smørgrav 	case SSH2_MSG_CHANNEL_SUCCESS:
3115ca86bcf2SDag-Erling Smørgrav 	case SSH2_MSG_CHANNEL_FAILURE:
3116ca86bcf2SDag-Erling Smørgrav 	case SSH2_MSG_CHANNEL_REQUEST:
3117ca86bcf2SDag-Erling Smørgrav 		break;
3118ca86bcf2SDag-Erling Smørgrav 	default:
311919261079SEd Maste 		debug2_f("channel %u: unsupported type %u", c->self, type);
3120ca86bcf2SDag-Erling Smørgrav 		return 0;
3121ca86bcf2SDag-Erling Smørgrav 	}
3122ca86bcf2SDag-Erling Smørgrav 	if ((b = sshbuf_new()) == NULL) {
312319261079SEd Maste 		error_f("alloc reply");
3124ca86bcf2SDag-Erling Smørgrav 		goto out;
3125ca86bcf2SDag-Erling Smørgrav 	}
3126ca86bcf2SDag-Erling Smørgrav 	/* get remaining payload (after id) */
3127ca86bcf2SDag-Erling Smørgrav 	cp = sshpkt_ptr(ssh, &len);
3128ca86bcf2SDag-Erling Smørgrav 	if (cp == NULL) {
312919261079SEd Maste 		error_f("no packet");
3130ca86bcf2SDag-Erling Smørgrav 		goto out;
3131ca86bcf2SDag-Erling Smørgrav 	}
3132ca86bcf2SDag-Erling Smørgrav 	/* translate id and send to muxclient */
3133ca86bcf2SDag-Erling Smørgrav 	if ((r = sshbuf_put_u8(b, 0)) != 0 ||	/* padlen */
3134ca86bcf2SDag-Erling Smørgrav 	    (r = sshbuf_put_u8(b, type)) != 0 ||
3135ca86bcf2SDag-Erling Smørgrav 	    (r = sshbuf_put_u32(b, c->mux_downstream_id)) != 0 ||
3136ca86bcf2SDag-Erling Smørgrav 	    (r = sshbuf_put(b, cp, len)) != 0 ||
31374f52dfbbSDag-Erling Smørgrav 	    (r = sshbuf_put_stringb(downstream->output, b)) != 0) {
313819261079SEd Maste 		error_fr(r, "compose muxclient");
3139ca86bcf2SDag-Erling Smørgrav 		goto out;
3140ca86bcf2SDag-Erling Smørgrav 	}
3141ca86bcf2SDag-Erling Smørgrav 	/* sshbuf_dump(b, stderr); */
3142ca86bcf2SDag-Erling Smørgrav 	if (ssh_packet_log_type(type))
314319261079SEd Maste 		debug3_f("channel %u: up->down: type %u", c->self, type);
3144ca86bcf2SDag-Erling Smørgrav  out:
3145ca86bcf2SDag-Erling Smørgrav 	/* update state */
3146ca86bcf2SDag-Erling Smørgrav 	switch (type) {
3147ca86bcf2SDag-Erling Smørgrav 	case SSH2_MSG_CHANNEL_OPEN_CONFIRMATION:
3148ca86bcf2SDag-Erling Smørgrav 		/* record remote_id for SSH2_MSG_CHANNEL_CLOSE */
31494f52dfbbSDag-Erling Smørgrav 		if (cp && len > 4) {
3150ca86bcf2SDag-Erling Smørgrav 			c->remote_id = PEEK_U32(cp);
31514f52dfbbSDag-Erling Smørgrav 			c->have_remote_id = 1;
31524f52dfbbSDag-Erling Smørgrav 		}
3153ca86bcf2SDag-Erling Smørgrav 		break;
3154ca86bcf2SDag-Erling Smørgrav 	case SSH2_MSG_CHANNEL_CLOSE:
3155ca86bcf2SDag-Erling Smørgrav 		if (c->flags & CHAN_CLOSE_SENT)
31564f52dfbbSDag-Erling Smørgrav 			channel_free(ssh, c);
3157ca86bcf2SDag-Erling Smørgrav 		else
3158ca86bcf2SDag-Erling Smørgrav 			c->flags |= CHAN_CLOSE_RCVD;
3159ca86bcf2SDag-Erling Smørgrav 		break;
3160ca86bcf2SDag-Erling Smørgrav 	}
3161ca86bcf2SDag-Erling Smørgrav 	sshbuf_free(b);
3162ca86bcf2SDag-Erling Smørgrav 	return 1;
3163ca86bcf2SDag-Erling Smørgrav }
3164af12a3e7SDag-Erling Smørgrav 
3165af12a3e7SDag-Erling Smørgrav /* -- protocol input */
3166511b41d2SMark Murray 
31674f52dfbbSDag-Erling Smørgrav /* Parse a channel ID from the current packet */
31684f52dfbbSDag-Erling Smørgrav static int
31694f52dfbbSDag-Erling Smørgrav channel_parse_id(struct ssh *ssh, const char *where, const char *what)
3170511b41d2SMark Murray {
31714f52dfbbSDag-Erling Smørgrav 	u_int32_t id;
31724f52dfbbSDag-Erling Smørgrav 	int r;
31734f52dfbbSDag-Erling Smørgrav 
31744f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_get_u32(ssh, &id)) != 0) {
317519261079SEd Maste 		error_r(r, "%s: parse id", where);
31764f52dfbbSDag-Erling Smørgrav 		ssh_packet_disconnect(ssh, "Invalid %s message", what);
31774f52dfbbSDag-Erling Smørgrav 	}
31784f52dfbbSDag-Erling Smørgrav 	if (id > INT_MAX) {
317919261079SEd Maste 		error_r(r, "%s: bad channel id %u", where, id);
31804f52dfbbSDag-Erling Smørgrav 		ssh_packet_disconnect(ssh, "Invalid %s channel id", what);
31814f52dfbbSDag-Erling Smørgrav 	}
31824f52dfbbSDag-Erling Smørgrav 	return (int)id;
31834f52dfbbSDag-Erling Smørgrav }
31844f52dfbbSDag-Erling Smørgrav 
31854f52dfbbSDag-Erling Smørgrav /* Lookup a channel from an ID in the current packet */
31864f52dfbbSDag-Erling Smørgrav static Channel *
31874f52dfbbSDag-Erling Smørgrav channel_from_packet_id(struct ssh *ssh, const char *where, const char *what)
31884f52dfbbSDag-Erling Smørgrav {
31894f52dfbbSDag-Erling Smørgrav 	int id = channel_parse_id(ssh, where, what);
3190a04a10f8SKris Kennaway 	Channel *c;
3191511b41d2SMark Murray 
31924f52dfbbSDag-Erling Smørgrav 	if ((c = channel_lookup(ssh, id)) == NULL) {
31934f52dfbbSDag-Erling Smørgrav 		ssh_packet_disconnect(ssh,
31944f52dfbbSDag-Erling Smørgrav 		    "%s packet referred to nonexistent channel %d", what, id);
31954f52dfbbSDag-Erling Smørgrav 	}
31964f52dfbbSDag-Erling Smørgrav 	return c;
31974f52dfbbSDag-Erling Smørgrav }
31984f52dfbbSDag-Erling Smørgrav 
31994f52dfbbSDag-Erling Smørgrav int
32004f52dfbbSDag-Erling Smørgrav channel_input_data(int type, u_int32_t seq, struct ssh *ssh)
32014f52dfbbSDag-Erling Smørgrav {
32024f52dfbbSDag-Erling Smørgrav 	const u_char *data;
32034f52dfbbSDag-Erling Smørgrav 	size_t data_len, win_len;
32044f52dfbbSDag-Erling Smørgrav 	Channel *c = channel_from_packet_id(ssh, __func__, "data");
32054f52dfbbSDag-Erling Smørgrav 	int r;
32064f52dfbbSDag-Erling Smørgrav 
32074f52dfbbSDag-Erling Smørgrav 	if (channel_proxy_upstream(c, type, seq, ssh))
3208ca86bcf2SDag-Erling Smørgrav 		return 0;
3209511b41d2SMark Murray 
3210511b41d2SMark Murray 	/* Ignore any data for non-open channels (might happen on close) */
3211a04a10f8SKris Kennaway 	if (c->type != SSH_CHANNEL_OPEN &&
32124f52dfbbSDag-Erling Smørgrav 	    c->type != SSH_CHANNEL_RDYNAMIC_OPEN &&
32134f52dfbbSDag-Erling Smørgrav 	    c->type != SSH_CHANNEL_RDYNAMIC_FINISH &&
3214a04a10f8SKris Kennaway 	    c->type != SSH_CHANNEL_X11_OPEN)
3215bc5531deSDag-Erling Smørgrav 		return 0;
3216511b41d2SMark Murray 
3217511b41d2SMark Murray 	/* Get the data. */
321819261079SEd Maste 	if ((r = sshpkt_get_string_direct(ssh, &data, &data_len)) != 0 ||
321919261079SEd Maste             (r = sshpkt_get_end(ssh)) != 0)
322019261079SEd Maste 		fatal_fr(r, "channel %i: get data", c->self);
32214f52dfbbSDag-Erling Smørgrav 
3222e2f6069cSDag-Erling Smørgrav 	win_len = data_len;
3223e2f6069cSDag-Erling Smørgrav 	if (c->datagram)
3224e2f6069cSDag-Erling Smørgrav 		win_len += 4;  /* string length header */
3225a04a10f8SKris Kennaway 
3226476cd3b2SDag-Erling Smørgrav 	/*
32274f52dfbbSDag-Erling Smørgrav 	 * The sending side reduces its window as it sends data, so we
32284f52dfbbSDag-Erling Smørgrav 	 * must 'fake' consumption of the data in order to ensure that window
32294f52dfbbSDag-Erling Smørgrav 	 * updates are sent back. Otherwise the connection might deadlock.
3230476cd3b2SDag-Erling Smørgrav 	 */
32314f52dfbbSDag-Erling Smørgrav 	if (c->ostate != CHAN_OUTPUT_OPEN) {
3232e2f6069cSDag-Erling Smørgrav 		c->local_window -= win_len;
3233e2f6069cSDag-Erling Smørgrav 		c->local_consumed += win_len;
3234bc5531deSDag-Erling Smørgrav 		return 0;
3235476cd3b2SDag-Erling Smørgrav 	}
3236476cd3b2SDag-Erling Smørgrav 
3237e2f6069cSDag-Erling Smørgrav 	if (win_len > c->local_maxpacket) {
32384f52dfbbSDag-Erling Smørgrav 		logit("channel %d: rcvd big packet %zu, maxpack %u",
3239e2f6069cSDag-Erling Smørgrav 		    c->self, win_len, c->local_maxpacket);
32404f52dfbbSDag-Erling Smørgrav 		return 0;
3241a04a10f8SKris Kennaway 	}
3242e2f6069cSDag-Erling Smørgrav 	if (win_len > c->local_window) {
32434f52dfbbSDag-Erling Smørgrav 		logit("channel %d: rcvd too much data %zu, win %u",
3244e2f6069cSDag-Erling Smørgrav 		    c->self, win_len, c->local_window);
3245bc5531deSDag-Erling Smørgrav 		return 0;
3246a04a10f8SKris Kennaway 	}
3247e2f6069cSDag-Erling Smørgrav 	c->local_window -= win_len;
32484f52dfbbSDag-Erling Smørgrav 
32494f52dfbbSDag-Erling Smørgrav 	if (c->datagram) {
32504f52dfbbSDag-Erling Smørgrav 		if ((r = sshbuf_put_string(c->output, data, data_len)) != 0)
325119261079SEd Maste 			fatal_fr(r, "channel %i: append datagram", c->self);
32524f52dfbbSDag-Erling Smørgrav 	} else if ((r = sshbuf_put(c->output, data, data_len)) != 0)
325319261079SEd Maste 		fatal_fr(r, "channel %i: append data", c->self);
32544f52dfbbSDag-Erling Smørgrav 
3255bc5531deSDag-Erling Smørgrav 	return 0;
3256511b41d2SMark Murray }
3257af12a3e7SDag-Erling Smørgrav 
3258bc5531deSDag-Erling Smørgrav int
32594f52dfbbSDag-Erling Smørgrav channel_input_extended_data(int type, u_int32_t seq, struct ssh *ssh)
3260a04a10f8SKris Kennaway {
32614f52dfbbSDag-Erling Smørgrav 	const u_char *data;
32624f52dfbbSDag-Erling Smørgrav 	size_t data_len;
32634f52dfbbSDag-Erling Smørgrav 	u_int32_t tcode;
32644f52dfbbSDag-Erling Smørgrav 	Channel *c = channel_from_packet_id(ssh, __func__, "extended data");
32654f52dfbbSDag-Erling Smørgrav 	int r;
3266a04a10f8SKris Kennaway 
32674f52dfbbSDag-Erling Smørgrav 	if (channel_proxy_upstream(c, type, seq, ssh))
3268ca86bcf2SDag-Erling Smørgrav 		return 0;
3269a04a10f8SKris Kennaway 	if (c->type != SSH_CHANNEL_OPEN) {
32704f52dfbbSDag-Erling Smørgrav 		logit("channel %d: ext data for non open", c->self);
3271bc5531deSDag-Erling Smørgrav 		return 0;
3272a04a10f8SKris Kennaway 	}
327380628bacSDag-Erling Smørgrav 	if (c->flags & CHAN_EOF_RCVD) {
327419261079SEd Maste 		if (ssh->compat & SSH_BUG_EXTEOF)
32754f52dfbbSDag-Erling Smørgrav 			debug("channel %d: accepting ext data after eof",
32764f52dfbbSDag-Erling Smørgrav 			    c->self);
327780628bacSDag-Erling Smørgrav 		else
32784f52dfbbSDag-Erling Smørgrav 			ssh_packet_disconnect(ssh, "Received extended_data "
32794f52dfbbSDag-Erling Smørgrav 			    "after EOF on channel %d.", c->self);
328080628bacSDag-Erling Smørgrav 	}
32814f52dfbbSDag-Erling Smørgrav 
32824f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_get_u32(ssh, &tcode)) != 0) {
328319261079SEd Maste 		error_fr(r, "parse tcode");
32844f52dfbbSDag-Erling Smørgrav 		ssh_packet_disconnect(ssh, "Invalid extended_data message");
32854f52dfbbSDag-Erling Smørgrav 	}
3286a04a10f8SKris Kennaway 	if (c->efd == -1 ||
3287a04a10f8SKris Kennaway 	    c->extended_usage != CHAN_EXTENDED_WRITE ||
3288a04a10f8SKris Kennaway 	    tcode != SSH2_EXTENDED_DATA_STDERR) {
3289221552e4SDag-Erling Smørgrav 		logit("channel %d: bad ext data", c->self);
3290bc5531deSDag-Erling Smørgrav 		return 0;
3291a04a10f8SKris Kennaway 	}
329219261079SEd Maste 	if ((r = sshpkt_get_string_direct(ssh, &data, &data_len)) != 0 ||
329319261079SEd Maste             (r = sshpkt_get_end(ssh)) != 0) {
329419261079SEd Maste 		error_fr(r, "parse data");
32954f52dfbbSDag-Erling Smørgrav 		ssh_packet_disconnect(ssh, "Invalid extended_data message");
32964f52dfbbSDag-Erling Smørgrav 	}
32974f52dfbbSDag-Erling Smørgrav 
3298a04a10f8SKris Kennaway 	if (data_len > c->local_window) {
32994f52dfbbSDag-Erling Smørgrav 		logit("channel %d: rcvd too much extended_data %zu, win %u",
3300a04a10f8SKris Kennaway 		    c->self, data_len, c->local_window);
3301bc5531deSDag-Erling Smørgrav 		return 0;
3302a04a10f8SKris Kennaway 	}
33034f52dfbbSDag-Erling Smørgrav 	debug2("channel %d: rcvd ext data %zu", c->self, data_len);
33044f52dfbbSDag-Erling Smørgrav 	/* XXX sshpkt_getb? */
33054f52dfbbSDag-Erling Smørgrav 	if ((r = sshbuf_put(c->extended, data, data_len)) != 0)
330619261079SEd Maste 		error_fr(r, "append");
3307a04a10f8SKris Kennaway 	c->local_window -= data_len;
3308bc5531deSDag-Erling Smørgrav 	return 0;
3309a04a10f8SKris Kennaway }
3310a04a10f8SKris Kennaway 
3311bc5531deSDag-Erling Smørgrav int
33124f52dfbbSDag-Erling Smørgrav channel_input_ieof(int type, u_int32_t seq, struct ssh *ssh)
3313a04a10f8SKris Kennaway {
33144f52dfbbSDag-Erling Smørgrav 	Channel *c = channel_from_packet_id(ssh, __func__, "ieof");
331519261079SEd Maste 	int r;
3316a04a10f8SKris Kennaway 
331719261079SEd Maste         if ((r = sshpkt_get_end(ssh)) != 0) {
331819261079SEd Maste 		error_fr(r, "parse data");
331919261079SEd Maste 		ssh_packet_disconnect(ssh, "Invalid ieof message");
332019261079SEd Maste 	}
33214f52dfbbSDag-Erling Smørgrav 
33224f52dfbbSDag-Erling Smørgrav 	if (channel_proxy_upstream(c, type, seq, ssh))
3323ca86bcf2SDag-Erling Smørgrav 		return 0;
33244f52dfbbSDag-Erling Smørgrav 	chan_rcvd_ieof(ssh, c);
3325af12a3e7SDag-Erling Smørgrav 
3326af12a3e7SDag-Erling Smørgrav 	/* XXX force input close */
3327af12a3e7SDag-Erling Smørgrav 	if (c->force_drain && c->istate == CHAN_INPUT_OPEN) {
3328af12a3e7SDag-Erling Smørgrav 		debug("channel %d: FORCE input drain", c->self);
3329af12a3e7SDag-Erling Smørgrav 		c->istate = CHAN_INPUT_WAIT_DRAIN;
33304f52dfbbSDag-Erling Smørgrav 		if (sshbuf_len(c->input) == 0)
33314f52dfbbSDag-Erling Smørgrav 			chan_ibuf_empty(ssh, c);
3332af12a3e7SDag-Erling Smørgrav 	}
3333bc5531deSDag-Erling Smørgrav 	return 0;
3334a04a10f8SKris Kennaway }
3335511b41d2SMark Murray 
3336bc5531deSDag-Erling Smørgrav int
33374f52dfbbSDag-Erling Smørgrav channel_input_oclose(int type, u_int32_t seq, struct ssh *ssh)
3338511b41d2SMark Murray {
33394f52dfbbSDag-Erling Smørgrav 	Channel *c = channel_from_packet_id(ssh, __func__, "oclose");
334019261079SEd Maste 	int r;
3341511b41d2SMark Murray 
33424f52dfbbSDag-Erling Smørgrav 	if (channel_proxy_upstream(c, type, seq, ssh))
3343ca86bcf2SDag-Erling Smørgrav 		return 0;
334419261079SEd Maste         if ((r = sshpkt_get_end(ssh)) != 0) {
334519261079SEd Maste 		error_fr(r, "parse data");
334619261079SEd Maste 		ssh_packet_disconnect(ssh, "Invalid oclose message");
334719261079SEd Maste 	}
33484f52dfbbSDag-Erling Smørgrav 	chan_rcvd_oclose(ssh, c);
3349bc5531deSDag-Erling Smørgrav 	return 0;
3350511b41d2SMark Murray }
3351511b41d2SMark Murray 
3352bc5531deSDag-Erling Smørgrav int
33534f52dfbbSDag-Erling Smørgrav channel_input_open_confirmation(int type, u_int32_t seq, struct ssh *ssh)
3354a04a10f8SKris Kennaway {
33554f52dfbbSDag-Erling Smørgrav 	Channel *c = channel_from_packet_id(ssh, __func__, "open confirmation");
33564f52dfbbSDag-Erling Smørgrav 	u_int32_t remote_window, remote_maxpacket;
33574f52dfbbSDag-Erling Smørgrav 	int r;
3358af12a3e7SDag-Erling Smørgrav 
33594f52dfbbSDag-Erling Smørgrav 	if (channel_proxy_upstream(c, type, seq, ssh))
3360ca86bcf2SDag-Erling Smørgrav 		return 0;
3361ca86bcf2SDag-Erling Smørgrav 	if (c->type != SSH_CHANNEL_OPENING)
336219261079SEd Maste 		ssh_packet_disconnect(ssh, "Received open confirmation for "
33634f52dfbbSDag-Erling Smørgrav 		    "non-opening channel %d.", c->self);
33644f52dfbbSDag-Erling Smørgrav 	/*
33654f52dfbbSDag-Erling Smørgrav 	 * Record the remote channel number and mark that the channel
33664f52dfbbSDag-Erling Smørgrav 	 * is now open.
33674f52dfbbSDag-Erling Smørgrav 	 */
33684f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_get_u32(ssh, &c->remote_id)) != 0 ||
33694f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_get_u32(ssh, &remote_window)) != 0 ||
337019261079SEd Maste 	    (r = sshpkt_get_u32(ssh, &remote_maxpacket)) != 0 ||
337119261079SEd Maste             (r = sshpkt_get_end(ssh)) != 0) {
337219261079SEd Maste 		error_fr(r, "window/maxpacket");
337319261079SEd Maste 		ssh_packet_disconnect(ssh, "Invalid open confirmation message");
33744f52dfbbSDag-Erling Smørgrav 	}
3375a04a10f8SKris Kennaway 
33764f52dfbbSDag-Erling Smørgrav 	c->have_remote_id = 1;
33774f52dfbbSDag-Erling Smørgrav 	c->remote_window = remote_window;
33784f52dfbbSDag-Erling Smørgrav 	c->remote_maxpacket = remote_maxpacket;
33794f52dfbbSDag-Erling Smørgrav 	c->type = SSH_CHANNEL_OPEN;
3380d4af9e69SDag-Erling Smørgrav 	if (c->open_confirm) {
338119261079SEd Maste 		debug2_f("channel %d: callback start", c->self);
33824f52dfbbSDag-Erling Smørgrav 		c->open_confirm(ssh, c->self, 1, c->open_confirm_ctx);
338319261079SEd Maste 		debug2_f("channel %d: callback done", c->self);
3384a04a10f8SKris Kennaway 	}
3385221552e4SDag-Erling Smørgrav 	debug2("channel %d: open confirm rwindow %u rmax %u", c->self,
3386a04a10f8SKris Kennaway 	    c->remote_window, c->remote_maxpacket);
3387bc5531deSDag-Erling Smørgrav 	return 0;
3388af12a3e7SDag-Erling Smørgrav }
3389af12a3e7SDag-Erling Smørgrav 
3390af12a3e7SDag-Erling Smørgrav static char *
3391af12a3e7SDag-Erling Smørgrav reason2txt(int reason)
3392af12a3e7SDag-Erling Smørgrav {
3393af12a3e7SDag-Erling Smørgrav 	switch (reason) {
3394af12a3e7SDag-Erling Smørgrav 	case SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED:
3395af12a3e7SDag-Erling Smørgrav 		return "administratively prohibited";
3396af12a3e7SDag-Erling Smørgrav 	case SSH2_OPEN_CONNECT_FAILED:
3397af12a3e7SDag-Erling Smørgrav 		return "connect failed";
3398af12a3e7SDag-Erling Smørgrav 	case SSH2_OPEN_UNKNOWN_CHANNEL_TYPE:
3399af12a3e7SDag-Erling Smørgrav 		return "unknown channel type";
3400af12a3e7SDag-Erling Smørgrav 	case SSH2_OPEN_RESOURCE_SHORTAGE:
3401af12a3e7SDag-Erling Smørgrav 		return "resource shortage";
3402af12a3e7SDag-Erling Smørgrav 	}
3403af12a3e7SDag-Erling Smørgrav 	return "unknown reason";
3404a04a10f8SKris Kennaway }
3405a04a10f8SKris Kennaway 
3406bc5531deSDag-Erling Smørgrav int
34074f52dfbbSDag-Erling Smørgrav channel_input_open_failure(int type, u_int32_t seq, struct ssh *ssh)
3408a04a10f8SKris Kennaway {
34094f52dfbbSDag-Erling Smørgrav 	Channel *c = channel_from_packet_id(ssh, __func__, "open failure");
34104f52dfbbSDag-Erling Smørgrav 	u_int32_t reason;
34114f52dfbbSDag-Erling Smørgrav 	char *msg = NULL;
34124f52dfbbSDag-Erling Smørgrav 	int r;
3413a04a10f8SKris Kennaway 
34144f52dfbbSDag-Erling Smørgrav 	if (channel_proxy_upstream(c, type, seq, ssh))
3415ca86bcf2SDag-Erling Smørgrav 		return 0;
3416ca86bcf2SDag-Erling Smørgrav 	if (c->type != SSH_CHANNEL_OPENING)
341719261079SEd Maste 		ssh_packet_disconnect(ssh, "Received open failure for "
34184f52dfbbSDag-Erling Smørgrav 		    "non-opening channel %d.", c->self);
34194f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_get_u32(ssh, &reason)) != 0) {
342019261079SEd Maste 		error_fr(r, "parse reason");
342119261079SEd Maste 		ssh_packet_disconnect(ssh, "Invalid open failure message");
3422ca3176e7SBrian Feldman 	}
34234f52dfbbSDag-Erling Smørgrav 	/* skip language */
34244f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_get_cstring(ssh, &msg, NULL)) != 0 ||
342519261079SEd Maste 	    (r = sshpkt_get_string_direct(ssh, NULL, NULL)) != 0 ||
342619261079SEd Maste             (r = sshpkt_get_end(ssh)) != 0) {
342719261079SEd Maste 		error_fr(r, "parse msg/lang");
342819261079SEd Maste 		ssh_packet_disconnect(ssh, "Invalid open failure message");
34294f52dfbbSDag-Erling Smørgrav 	}
34304f52dfbbSDag-Erling Smørgrav 	logit("channel %d: open failed: %s%s%s", c->self,
3431af12a3e7SDag-Erling Smørgrav 	    reason2txt(reason), msg ? ": ": "", msg ? msg : "");
3432e4a9863fSDag-Erling Smørgrav 	free(msg);
3433e2f6069cSDag-Erling Smørgrav 	if (c->open_confirm) {
343419261079SEd Maste 		debug2_f("channel %d: callback start", c->self);
34354f52dfbbSDag-Erling Smørgrav 		c->open_confirm(ssh, c->self, 0, c->open_confirm_ctx);
343619261079SEd Maste 		debug2_f("channel %d: callback done", c->self);
3437e2f6069cSDag-Erling Smørgrav 	}
3438cce7d346SDag-Erling Smørgrav 	/* Schedule the channel for cleanup/deletion. */
34394f52dfbbSDag-Erling Smørgrav 	chan_mark_dead(ssh, c);
3440bc5531deSDag-Erling Smørgrav 	return 0;
3441a04a10f8SKris Kennaway }
3442a04a10f8SKris Kennaway 
3443bc5531deSDag-Erling Smørgrav int
34444f52dfbbSDag-Erling Smørgrav channel_input_window_adjust(int type, u_int32_t seq, struct ssh *ssh)
3445a04a10f8SKris Kennaway {
34464f52dfbbSDag-Erling Smørgrav 	int id = channel_parse_id(ssh, __func__, "window adjust");
3447a04a10f8SKris Kennaway 	Channel *c;
34484f52dfbbSDag-Erling Smørgrav 	u_int32_t adjust;
34494f52dfbbSDag-Erling Smørgrav 	u_int new_rwin;
34504f52dfbbSDag-Erling Smørgrav 	int r;
3451a04a10f8SKris Kennaway 
34524f52dfbbSDag-Erling Smørgrav 	if ((c = channel_lookup(ssh, id)) == NULL) {
3453b74df5b2SDag-Erling Smørgrav 		logit("Received window adjust for non-open channel %d.", id);
3454bc5531deSDag-Erling Smørgrav 		return 0;
3455511b41d2SMark Murray 	}
34564f52dfbbSDag-Erling Smørgrav 
34574f52dfbbSDag-Erling Smørgrav 	if (channel_proxy_upstream(c, type, seq, ssh))
3458ca86bcf2SDag-Erling Smørgrav 		return 0;
345919261079SEd Maste 	if ((r = sshpkt_get_u32(ssh, &adjust)) != 0 ||
346019261079SEd Maste             (r = sshpkt_get_end(ssh)) != 0) {
346119261079SEd Maste 		error_fr(r, "parse adjust");
346219261079SEd Maste 		ssh_packet_disconnect(ssh, "Invalid window adjust message");
34634f52dfbbSDag-Erling Smørgrav 	}
34644f52dfbbSDag-Erling Smørgrav 	debug2("channel %d: rcvd adjust %u", c->self, adjust);
34654f52dfbbSDag-Erling Smørgrav 	if ((new_rwin = c->remote_window + adjust) < c->remote_window) {
3466557f75e5SDag-Erling Smørgrav 		fatal("channel %d: adjust %u overflows remote window %u",
34674f52dfbbSDag-Erling Smørgrav 		    c->self, adjust, c->remote_window);
34684f52dfbbSDag-Erling Smørgrav 	}
34694f52dfbbSDag-Erling Smørgrav 	c->remote_window = new_rwin;
3470bc5531deSDag-Erling Smørgrav 	return 0;
3471511b41d2SMark Murray }
3472511b41d2SMark Murray 
3473bc5531deSDag-Erling Smørgrav int
34744f52dfbbSDag-Erling Smørgrav channel_input_status_confirm(int type, u_int32_t seq, struct ssh *ssh)
3475af12a3e7SDag-Erling Smørgrav {
34764f52dfbbSDag-Erling Smørgrav 	int id = channel_parse_id(ssh, __func__, "status confirm");
3477d4af9e69SDag-Erling Smørgrav 	Channel *c;
3478d4af9e69SDag-Erling Smørgrav 	struct channel_confirm *cc;
3479d4af9e69SDag-Erling Smørgrav 
3480d4af9e69SDag-Erling Smørgrav 	/* Reset keepalive timeout */
348119261079SEd Maste 	ssh_packet_set_alive_timeouts(ssh, 0);
3482d4af9e69SDag-Erling Smørgrav 
348319261079SEd Maste 	debug2_f("type %d id %d", type, id);
3484d4af9e69SDag-Erling Smørgrav 
34854f52dfbbSDag-Erling Smørgrav 	if ((c = channel_lookup(ssh, id)) == NULL) {
348619261079SEd Maste 		logit_f("%d: unknown", id);
3487bc5531deSDag-Erling Smørgrav 		return 0;
3488d4af9e69SDag-Erling Smørgrav 	}
34894f52dfbbSDag-Erling Smørgrav 	if (channel_proxy_upstream(c, type, seq, ssh))
3490ca86bcf2SDag-Erling Smørgrav 		return 0;
349119261079SEd Maste         if (sshpkt_get_end(ssh) != 0)
349219261079SEd Maste 		ssh_packet_disconnect(ssh, "Invalid status confirm message");
3493d4af9e69SDag-Erling Smørgrav 	if ((cc = TAILQ_FIRST(&c->status_confirms)) == NULL)
3494bc5531deSDag-Erling Smørgrav 		return 0;
34954f52dfbbSDag-Erling Smørgrav 	cc->cb(ssh, type, c, cc->ctx);
3496d4af9e69SDag-Erling Smørgrav 	TAILQ_REMOVE(&c->status_confirms, cc, entry);
349719261079SEd Maste 	freezero(cc, sizeof(*cc));
3498bc5531deSDag-Erling Smørgrav 	return 0;
3499d4af9e69SDag-Erling Smørgrav }
3500af12a3e7SDag-Erling Smørgrav 
3501af12a3e7SDag-Erling Smørgrav /* -- tcp forwarding */
3502511b41d2SMark Murray 
3503511b41d2SMark Murray void
35044f52dfbbSDag-Erling Smørgrav channel_set_af(struct ssh *ssh, int af)
3505511b41d2SMark Murray {
35064f52dfbbSDag-Erling Smørgrav 	ssh->chanctxt->IPv4or6 = af;
3507511b41d2SMark Murray }
3508511b41d2SMark Murray 
350989986192SBrooks Davis 
3510462c32cbSDag-Erling Smørgrav /*
3511462c32cbSDag-Erling Smørgrav  * Determine whether or not a port forward listens to loopback, the
3512462c32cbSDag-Erling Smørgrav  * specified address or wildcard. On the client, a specified bind
3513462c32cbSDag-Erling Smørgrav  * address will always override gateway_ports. On the server, a
3514462c32cbSDag-Erling Smørgrav  * gateway_ports of 1 (``yes'') will override the client's specification
3515462c32cbSDag-Erling Smørgrav  * and force a wildcard bind, whereas a value of 2 (``clientspecified'')
3516462c32cbSDag-Erling Smørgrav  * will bind to whatever address the client asked for.
3517462c32cbSDag-Erling Smørgrav  *
3518462c32cbSDag-Erling Smørgrav  * Special-case listen_addrs are:
3519462c32cbSDag-Erling Smørgrav  *
3520462c32cbSDag-Erling Smørgrav  * "0.0.0.0"               -> wildcard v4/v6 if SSH_OLD_FORWARD_ADDR
3521462c32cbSDag-Erling Smørgrav  * "" (empty string), "*"  -> wildcard v4/v6
3522462c32cbSDag-Erling Smørgrav  * "localhost"             -> loopback v4/v6
3523a0ee8cc6SDag-Erling Smørgrav  * "127.0.0.1" / "::1"     -> accepted even if gateway_ports isn't set
3524462c32cbSDag-Erling Smørgrav  */
3525462c32cbSDag-Erling Smørgrav static const char *
352619261079SEd Maste channel_fwd_bind_addr(struct ssh *ssh, const char *listen_addr, int *wildcardp,
3527a0ee8cc6SDag-Erling Smørgrav     int is_client, struct ForwardOptions *fwd_opts)
3528462c32cbSDag-Erling Smørgrav {
3529462c32cbSDag-Erling Smørgrav 	const char *addr = NULL;
3530462c32cbSDag-Erling Smørgrav 	int wildcard = 0;
3531462c32cbSDag-Erling Smørgrav 
3532462c32cbSDag-Erling Smørgrav 	if (listen_addr == NULL) {
3533462c32cbSDag-Erling Smørgrav 		/* No address specified: default to gateway_ports setting */
3534a0ee8cc6SDag-Erling Smørgrav 		if (fwd_opts->gateway_ports)
3535462c32cbSDag-Erling Smørgrav 			wildcard = 1;
3536a0ee8cc6SDag-Erling Smørgrav 	} else if (fwd_opts->gateway_ports || is_client) {
353719261079SEd Maste 		if (((ssh->compat & SSH_OLD_FORWARD_ADDR) &&
3538462c32cbSDag-Erling Smørgrav 		    strcmp(listen_addr, "0.0.0.0") == 0 && is_client == 0) ||
3539462c32cbSDag-Erling Smørgrav 		    *listen_addr == '\0' || strcmp(listen_addr, "*") == 0 ||
3540a0ee8cc6SDag-Erling Smørgrav 		    (!is_client && fwd_opts->gateway_ports == 1)) {
3541462c32cbSDag-Erling Smørgrav 			wildcard = 1;
3542f7167e0eSDag-Erling Smørgrav 			/*
3543f7167e0eSDag-Erling Smørgrav 			 * Notify client if they requested a specific listen
3544f7167e0eSDag-Erling Smørgrav 			 * address and it was overridden.
3545f7167e0eSDag-Erling Smørgrav 			 */
3546f7167e0eSDag-Erling Smørgrav 			if (*listen_addr != '\0' &&
3547f7167e0eSDag-Erling Smørgrav 			    strcmp(listen_addr, "0.0.0.0") != 0 &&
3548f7167e0eSDag-Erling Smørgrav 			    strcmp(listen_addr, "*") != 0) {
354919261079SEd Maste 				ssh_packet_send_debug(ssh,
355019261079SEd Maste 				    "Forwarding listen address "
3551f7167e0eSDag-Erling Smørgrav 				    "\"%s\" overridden by server "
3552f7167e0eSDag-Erling Smørgrav 				    "GatewayPorts", listen_addr);
3553f7167e0eSDag-Erling Smørgrav 			}
3554a0ee8cc6SDag-Erling Smørgrav 		} else if (strcmp(listen_addr, "localhost") != 0 ||
3555a0ee8cc6SDag-Erling Smørgrav 		    strcmp(listen_addr, "127.0.0.1") == 0 ||
3556a0ee8cc6SDag-Erling Smørgrav 		    strcmp(listen_addr, "::1") == 0) {
355719261079SEd Maste 			/*
355819261079SEd Maste 			 * Accept explicit localhost address when
355919261079SEd Maste 			 * GatewayPorts=yes. The "localhost" hostname is
356019261079SEd Maste 			 * deliberately skipped here so it will listen on all
356119261079SEd Maste 			 * available local address families.
356219261079SEd Maste 			 */
3563a0ee8cc6SDag-Erling Smørgrav 			addr = listen_addr;
3564f7167e0eSDag-Erling Smørgrav 		}
3565a0ee8cc6SDag-Erling Smørgrav 	} else if (strcmp(listen_addr, "127.0.0.1") == 0 ||
3566a0ee8cc6SDag-Erling Smørgrav 	    strcmp(listen_addr, "::1") == 0) {
3567a0ee8cc6SDag-Erling Smørgrav 		/*
3568a0ee8cc6SDag-Erling Smørgrav 		 * If a specific IPv4/IPv6 localhost address has been
3569a0ee8cc6SDag-Erling Smørgrav 		 * requested then accept it even if gateway_ports is in
3570a0ee8cc6SDag-Erling Smørgrav 		 * effect. This allows the client to prefer IPv4 or IPv6.
3571a0ee8cc6SDag-Erling Smørgrav 		 */
3572462c32cbSDag-Erling Smørgrav 		addr = listen_addr;
3573462c32cbSDag-Erling Smørgrav 	}
3574462c32cbSDag-Erling Smørgrav 	if (wildcardp != NULL)
3575462c32cbSDag-Erling Smørgrav 		*wildcardp = wildcard;
3576462c32cbSDag-Erling Smørgrav 	return addr;
3577462c32cbSDag-Erling Smørgrav }
3578462c32cbSDag-Erling Smørgrav 
3579af12a3e7SDag-Erling Smørgrav static int
35804f52dfbbSDag-Erling Smørgrav channel_setup_fwd_listener_tcpip(struct ssh *ssh, int type,
35814f52dfbbSDag-Erling Smørgrav     struct Forward *fwd, int *allocated_listen_port,
35824f52dfbbSDag-Erling Smørgrav     struct ForwardOptions *fwd_opts)
3583511b41d2SMark Murray {
3584af12a3e7SDag-Erling Smørgrav 	Channel *c;
3585b74df5b2SDag-Erling Smørgrav 	int sock, r, success = 0, wildcard = 0, is_client;
3586511b41d2SMark Murray 	struct addrinfo hints, *ai, *aitop;
3587aa49c926SDag-Erling Smørgrav 	const char *host, *addr;
3588af12a3e7SDag-Erling Smørgrav 	char ntop[NI_MAXHOST], strport[NI_MAXSERV];
3589cce7d346SDag-Erling Smørgrav 	in_port_t *lport_p;
3590511b41d2SMark Murray 
3591aa49c926SDag-Erling Smørgrav 	is_client = (type == SSH_CHANNEL_PORT_LISTENER);
3592511b41d2SMark Murray 
3593557f75e5SDag-Erling Smørgrav 	if (is_client && fwd->connect_path != NULL) {
3594557f75e5SDag-Erling Smørgrav 		host = fwd->connect_path;
3595557f75e5SDag-Erling Smørgrav 	} else {
3596557f75e5SDag-Erling Smørgrav 		host = (type == SSH_CHANNEL_RPORT_LISTENER) ?
3597557f75e5SDag-Erling Smørgrav 		    fwd->listen_host : fwd->connect_host;
3598af12a3e7SDag-Erling Smørgrav 		if (host == NULL) {
3599af12a3e7SDag-Erling Smørgrav 			error("No forward host name.");
3600d4ecd108SDag-Erling Smørgrav 			return 0;
3601ca3176e7SBrian Feldman 		}
3602cce7d346SDag-Erling Smørgrav 		if (strlen(host) >= NI_MAXHOST) {
3603ca3176e7SBrian Feldman 			error("Forward host name too long.");
3604d4ecd108SDag-Erling Smørgrav 			return 0;
3605ca3176e7SBrian Feldman 		}
3606557f75e5SDag-Erling Smørgrav 	}
3607ca3176e7SBrian Feldman 
3608462c32cbSDag-Erling Smørgrav 	/* Determine the bind address, cf. channel_fwd_bind_addr() comment */
360919261079SEd Maste 	addr = channel_fwd_bind_addr(ssh, fwd->listen_host, &wildcard,
3610a0ee8cc6SDag-Erling Smørgrav 	    is_client, fwd_opts);
361119261079SEd Maste 	debug3_f("type %d wildcard %d addr %s", type, wildcard,
361219261079SEd Maste 	    (addr == NULL) ? "NULL" : addr);
3613aa49c926SDag-Erling Smørgrav 
3614aa49c926SDag-Erling Smørgrav 	/*
3615511b41d2SMark Murray 	 * getaddrinfo returns a loopback address if the hostname is
3616511b41d2SMark Murray 	 * set to NULL and hints.ai_flags is not AI_PASSIVE
3617511b41d2SMark Murray 	 */
3618511b41d2SMark Murray 	memset(&hints, 0, sizeof(hints));
36194f52dfbbSDag-Erling Smørgrav 	hints.ai_family = ssh->chanctxt->IPv4or6;
3620aa49c926SDag-Erling Smørgrav 	hints.ai_flags = wildcard ? AI_PASSIVE : 0;
3621511b41d2SMark Murray 	hints.ai_socktype = SOCK_STREAM;
3622a0ee8cc6SDag-Erling Smørgrav 	snprintf(strport, sizeof strport, "%d", fwd->listen_port);
3623aa49c926SDag-Erling Smørgrav 	if ((r = getaddrinfo(addr, strport, &hints, &aitop)) != 0) {
3624aa49c926SDag-Erling Smørgrav 		if (addr == NULL) {
3625aa49c926SDag-Erling Smørgrav 			/* This really shouldn't happen */
362619261079SEd Maste 			ssh_packet_disconnect(ssh, "getaddrinfo: fatal error: %s",
3627d4af9e69SDag-Erling Smørgrav 			    ssh_gai_strerror(r));
3628aa49c926SDag-Erling Smørgrav 		} else {
362919261079SEd Maste 			error_f("getaddrinfo(%.64s): %s", addr,
3630d4af9e69SDag-Erling Smørgrav 			    ssh_gai_strerror(r));
3631aa49c926SDag-Erling Smørgrav 		}
3632d4ecd108SDag-Erling Smørgrav 		return 0;
3633aa49c926SDag-Erling Smørgrav 	}
3634cce7d346SDag-Erling Smørgrav 	if (allocated_listen_port != NULL)
3635cce7d346SDag-Erling Smørgrav 		*allocated_listen_port = 0;
3636511b41d2SMark Murray 	for (ai = aitop; ai; ai = ai->ai_next) {
3637cce7d346SDag-Erling Smørgrav 		switch (ai->ai_family) {
3638cce7d346SDag-Erling Smørgrav 		case AF_INET:
3639cce7d346SDag-Erling Smørgrav 			lport_p = &((struct sockaddr_in *)ai->ai_addr)->
3640cce7d346SDag-Erling Smørgrav 			    sin_port;
3641cce7d346SDag-Erling Smørgrav 			break;
3642cce7d346SDag-Erling Smørgrav 		case AF_INET6:
3643cce7d346SDag-Erling Smørgrav 			lport_p = &((struct sockaddr_in6 *)ai->ai_addr)->
3644cce7d346SDag-Erling Smørgrav 			    sin6_port;
3645cce7d346SDag-Erling Smørgrav 			break;
3646cce7d346SDag-Erling Smørgrav 		default:
3647511b41d2SMark Murray 			continue;
3648cce7d346SDag-Erling Smørgrav 		}
3649cce7d346SDag-Erling Smørgrav 		/*
3650cce7d346SDag-Erling Smørgrav 		 * If allocating a port for -R forwards, then use the
3651cce7d346SDag-Erling Smørgrav 		 * same port for all address families.
3652cce7d346SDag-Erling Smørgrav 		 */
36534f52dfbbSDag-Erling Smørgrav 		if (type == SSH_CHANNEL_RPORT_LISTENER &&
36544f52dfbbSDag-Erling Smørgrav 		    fwd->listen_port == 0 && allocated_listen_port != NULL &&
36554f52dfbbSDag-Erling Smørgrav 		    *allocated_listen_port > 0)
3656cce7d346SDag-Erling Smørgrav 			*lport_p = htons(*allocated_listen_port);
3657cce7d346SDag-Erling Smørgrav 
3658511b41d2SMark Murray 		if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop),
36594f52dfbbSDag-Erling Smørgrav 		    strport, sizeof(strport),
36604f52dfbbSDag-Erling Smørgrav 		    NI_NUMERICHOST|NI_NUMERICSERV) != 0) {
366119261079SEd Maste 			error_f("getnameinfo failed");
3662511b41d2SMark Murray 			continue;
3663511b41d2SMark Murray 		}
3664511b41d2SMark Murray 		/* Create a port to listen for the host. */
3665221552e4SDag-Erling Smørgrav 		sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
366619261079SEd Maste 		if (sock == -1) {
3667511b41d2SMark Murray 			/* this is no error since kernel may not support ipv6 */
366847dd1d1bSDag-Erling Smørgrav 			verbose("socket [%s]:%s: %.100s", ntop, strport,
366947dd1d1bSDag-Erling Smørgrav 			    strerror(errno));
3670511b41d2SMark Murray 			continue;
3671511b41d2SMark Murray 		}
3672b74df5b2SDag-Erling Smørgrav 
367347dd1d1bSDag-Erling Smørgrav 		set_reuseaddr(sock);
3674b15c8340SDag-Erling Smørgrav 		if (ai->ai_family == AF_INET6)
3675b15c8340SDag-Erling Smørgrav 			sock_set_v6only(sock);
3676f388f5efSDag-Erling Smørgrav 
3677cce7d346SDag-Erling Smørgrav 		debug("Local forwarding listening on %s port %s.",
3678cce7d346SDag-Erling Smørgrav 		    ntop, strport);
3679511b41d2SMark Murray 
3680511b41d2SMark Murray 		/* Bind the socket to the address. */
368119261079SEd Maste 		if (bind(sock, ai->ai_addr, ai->ai_addrlen) == -1) {
36824f52dfbbSDag-Erling Smørgrav 			/*
36834f52dfbbSDag-Erling Smørgrav 			 * address can be in if use ipv6 address is
36844f52dfbbSDag-Erling Smørgrav 			 * already bound
36854f52dfbbSDag-Erling Smørgrav 			 */
3686989dd127SDag-Erling Smørgrav 			if (!ai->ai_next)
368747dd1d1bSDag-Erling Smørgrav 				error("bind [%s]:%s: %.100s",
368847dd1d1bSDag-Erling Smørgrav 				    ntop, strport, strerror(errno));
3689989dd127SDag-Erling Smørgrav 			else
369047dd1d1bSDag-Erling Smørgrav 				verbose("bind [%s]:%s: %.100s",
369147dd1d1bSDag-Erling Smørgrav 				    ntop, strport, strerror(errno));
3692989dd127SDag-Erling Smørgrav 
3693511b41d2SMark Murray 			close(sock);
3694511b41d2SMark Murray 			continue;
3695511b41d2SMark Murray 		}
3696511b41d2SMark Murray 		/* Start listening for connections on the socket. */
369719261079SEd Maste 		if (listen(sock, SSH_LISTEN_BACKLOG) == -1) {
369847dd1d1bSDag-Erling Smørgrav 			error("listen [%s]:%s: %.100s", ntop, strport,
369947dd1d1bSDag-Erling Smørgrav 			    strerror(errno));
3700511b41d2SMark Murray 			close(sock);
3701511b41d2SMark Murray 			continue;
3702511b41d2SMark Murray 		}
3703cce7d346SDag-Erling Smørgrav 
3704cce7d346SDag-Erling Smørgrav 		/*
3705a0ee8cc6SDag-Erling Smørgrav 		 * fwd->listen_port == 0 requests a dynamically allocated port -
3706cce7d346SDag-Erling Smørgrav 		 * record what we got.
3707cce7d346SDag-Erling Smørgrav 		 */
37084f52dfbbSDag-Erling Smørgrav 		if (type == SSH_CHANNEL_RPORT_LISTENER &&
37094f52dfbbSDag-Erling Smørgrav 		    fwd->listen_port == 0 &&
3710cce7d346SDag-Erling Smørgrav 		    allocated_listen_port != NULL &&
3711cce7d346SDag-Erling Smørgrav 		    *allocated_listen_port == 0) {
3712076ad2f8SDag-Erling Smørgrav 			*allocated_listen_port = get_local_port(sock);
3713cce7d346SDag-Erling Smørgrav 			debug("Allocated listen port %d",
3714cce7d346SDag-Erling Smørgrav 			    *allocated_listen_port);
3715cce7d346SDag-Erling Smørgrav 		}
3716cce7d346SDag-Erling Smørgrav 
371760c59fadSDag-Erling Smørgrav 		/* Allocate a channel number for the socket. */
37184f52dfbbSDag-Erling Smørgrav 		c = channel_new(ssh, "port listener", type, sock, sock, -1,
3719a04a10f8SKris Kennaway 		    CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT,
3720221552e4SDag-Erling Smørgrav 		    0, "port listener", 1);
3721cce7d346SDag-Erling Smørgrav 		c->path = xstrdup(host);
3722a0ee8cc6SDag-Erling Smørgrav 		c->host_port = fwd->connect_port;
3723462c32cbSDag-Erling Smørgrav 		c->listening_addr = addr == NULL ? NULL : xstrdup(addr);
3724a0ee8cc6SDag-Erling Smørgrav 		if (fwd->listen_port == 0 && allocated_listen_port != NULL &&
372519261079SEd Maste 		    !(ssh->compat & SSH_BUG_DYNAMIC_RPORT))
3726462c32cbSDag-Erling Smørgrav 			c->listening_port = *allocated_listen_port;
3727462c32cbSDag-Erling Smørgrav 		else
3728a0ee8cc6SDag-Erling Smørgrav 			c->listening_port = fwd->listen_port;
3729511b41d2SMark Murray 		success = 1;
3730511b41d2SMark Murray 	}
3731511b41d2SMark Murray 	if (success == 0)
373219261079SEd Maste 		error_f("cannot listen to port: %d", fwd->listen_port);
3733511b41d2SMark Murray 	freeaddrinfo(aitop);
3734ca3176e7SBrian Feldman 	return success;
3735511b41d2SMark Murray }
3736511b41d2SMark Murray 
3737a0ee8cc6SDag-Erling Smørgrav static int
37384f52dfbbSDag-Erling Smørgrav channel_setup_fwd_listener_streamlocal(struct ssh *ssh, int type,
37394f52dfbbSDag-Erling Smørgrav     struct Forward *fwd, struct ForwardOptions *fwd_opts)
3740a0ee8cc6SDag-Erling Smørgrav {
3741a0ee8cc6SDag-Erling Smørgrav 	struct sockaddr_un sunaddr;
3742a0ee8cc6SDag-Erling Smørgrav 	const char *path;
3743a0ee8cc6SDag-Erling Smørgrav 	Channel *c;
3744a0ee8cc6SDag-Erling Smørgrav 	int port, sock;
3745a0ee8cc6SDag-Erling Smørgrav 	mode_t omask;
3746a0ee8cc6SDag-Erling Smørgrav 
3747a0ee8cc6SDag-Erling Smørgrav 	switch (type) {
3748a0ee8cc6SDag-Erling Smørgrav 	case SSH_CHANNEL_UNIX_LISTENER:
3749a0ee8cc6SDag-Erling Smørgrav 		if (fwd->connect_path != NULL) {
3750a0ee8cc6SDag-Erling Smørgrav 			if (strlen(fwd->connect_path) > sizeof(sunaddr.sun_path)) {
3751a0ee8cc6SDag-Erling Smørgrav 				error("Local connecting path too long: %s",
3752a0ee8cc6SDag-Erling Smørgrav 				    fwd->connect_path);
3753a0ee8cc6SDag-Erling Smørgrav 				return 0;
3754a0ee8cc6SDag-Erling Smørgrav 			}
3755a0ee8cc6SDag-Erling Smørgrav 			path = fwd->connect_path;
3756a0ee8cc6SDag-Erling Smørgrav 			port = PORT_STREAMLOCAL;
3757a0ee8cc6SDag-Erling Smørgrav 		} else {
3758a0ee8cc6SDag-Erling Smørgrav 			if (fwd->connect_host == NULL) {
3759a0ee8cc6SDag-Erling Smørgrav 				error("No forward host name.");
3760a0ee8cc6SDag-Erling Smørgrav 				return 0;
3761a0ee8cc6SDag-Erling Smørgrav 			}
3762a0ee8cc6SDag-Erling Smørgrav 			if (strlen(fwd->connect_host) >= NI_MAXHOST) {
3763a0ee8cc6SDag-Erling Smørgrav 				error("Forward host name too long.");
3764a0ee8cc6SDag-Erling Smørgrav 				return 0;
3765a0ee8cc6SDag-Erling Smørgrav 			}
3766a0ee8cc6SDag-Erling Smørgrav 			path = fwd->connect_host;
3767a0ee8cc6SDag-Erling Smørgrav 			port = fwd->connect_port;
3768a0ee8cc6SDag-Erling Smørgrav 		}
3769a0ee8cc6SDag-Erling Smørgrav 		break;
3770a0ee8cc6SDag-Erling Smørgrav 	case SSH_CHANNEL_RUNIX_LISTENER:
3771a0ee8cc6SDag-Erling Smørgrav 		path = fwd->listen_path;
3772a0ee8cc6SDag-Erling Smørgrav 		port = PORT_STREAMLOCAL;
3773a0ee8cc6SDag-Erling Smørgrav 		break;
3774a0ee8cc6SDag-Erling Smørgrav 	default:
377519261079SEd Maste 		error_f("unexpected channel type %d", type);
3776a0ee8cc6SDag-Erling Smørgrav 		return 0;
3777a0ee8cc6SDag-Erling Smørgrav 	}
3778a0ee8cc6SDag-Erling Smørgrav 
3779a0ee8cc6SDag-Erling Smørgrav 	if (fwd->listen_path == NULL) {
3780a0ee8cc6SDag-Erling Smørgrav 		error("No forward path name.");
3781a0ee8cc6SDag-Erling Smørgrav 		return 0;
3782a0ee8cc6SDag-Erling Smørgrav 	}
3783a0ee8cc6SDag-Erling Smørgrav 	if (strlen(fwd->listen_path) > sizeof(sunaddr.sun_path)) {
3784a0ee8cc6SDag-Erling Smørgrav 		error("Local listening path too long: %s", fwd->listen_path);
3785a0ee8cc6SDag-Erling Smørgrav 		return 0;
3786a0ee8cc6SDag-Erling Smørgrav 	}
3787a0ee8cc6SDag-Erling Smørgrav 
378819261079SEd Maste 	debug3_f("type %d path %s", type, fwd->listen_path);
3789a0ee8cc6SDag-Erling Smørgrav 
3790a0ee8cc6SDag-Erling Smørgrav 	/* Start a Unix domain listener. */
3791a0ee8cc6SDag-Erling Smørgrav 	omask = umask(fwd_opts->streamlocal_bind_mask);
3792a0ee8cc6SDag-Erling Smørgrav 	sock = unix_listener(fwd->listen_path, SSH_LISTEN_BACKLOG,
3793a0ee8cc6SDag-Erling Smørgrav 	    fwd_opts->streamlocal_bind_unlink);
3794a0ee8cc6SDag-Erling Smørgrav 	umask(omask);
3795a0ee8cc6SDag-Erling Smørgrav 	if (sock < 0)
3796a0ee8cc6SDag-Erling Smørgrav 		return 0;
3797a0ee8cc6SDag-Erling Smørgrav 
3798a0ee8cc6SDag-Erling Smørgrav 	debug("Local forwarding listening on path %s.", fwd->listen_path);
3799a0ee8cc6SDag-Erling Smørgrav 
3800a0ee8cc6SDag-Erling Smørgrav 	/* Allocate a channel number for the socket. */
38014f52dfbbSDag-Erling Smørgrav 	c = channel_new(ssh, "unix listener", type, sock, sock, -1,
3802a0ee8cc6SDag-Erling Smørgrav 	    CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT,
3803a0ee8cc6SDag-Erling Smørgrav 	    0, "unix listener", 1);
3804a0ee8cc6SDag-Erling Smørgrav 	c->path = xstrdup(path);
3805a0ee8cc6SDag-Erling Smørgrav 	c->host_port = port;
3806a0ee8cc6SDag-Erling Smørgrav 	c->listening_port = PORT_STREAMLOCAL;
3807a0ee8cc6SDag-Erling Smørgrav 	c->listening_addr = xstrdup(fwd->listen_path);
3808a0ee8cc6SDag-Erling Smørgrav 	return 1;
3809a0ee8cc6SDag-Erling Smørgrav }
3810a0ee8cc6SDag-Erling Smørgrav 
3811a0ee8cc6SDag-Erling Smørgrav static int
38124f52dfbbSDag-Erling Smørgrav channel_cancel_rport_listener_tcpip(struct ssh *ssh,
38134f52dfbbSDag-Erling Smørgrav     const char *host, u_short port)
381421e764dfSDag-Erling Smørgrav {
381521e764dfSDag-Erling Smørgrav 	u_int i;
381621e764dfSDag-Erling Smørgrav 	int found = 0;
381721e764dfSDag-Erling Smørgrav 
38184f52dfbbSDag-Erling Smørgrav 	for (i = 0; i < ssh->chanctxt->channels_alloc; i++) {
38194f52dfbbSDag-Erling Smørgrav 		Channel *c = ssh->chanctxt->channels[i];
3820462c32cbSDag-Erling Smørgrav 		if (c == NULL || c->type != SSH_CHANNEL_RPORT_LISTENER)
3821462c32cbSDag-Erling Smørgrav 			continue;
3822462c32cbSDag-Erling Smørgrav 		if (strcmp(c->path, host) == 0 && c->listening_port == port) {
382319261079SEd Maste 			debug2_f("close channel %d", i);
38244f52dfbbSDag-Erling Smørgrav 			channel_free(ssh, c);
3825462c32cbSDag-Erling Smørgrav 			found = 1;
3826462c32cbSDag-Erling Smørgrav 		}
3827462c32cbSDag-Erling Smørgrav 	}
382821e764dfSDag-Erling Smørgrav 
38294f52dfbbSDag-Erling Smørgrav 	return found;
3830462c32cbSDag-Erling Smørgrav }
3831462c32cbSDag-Erling Smørgrav 
3832a0ee8cc6SDag-Erling Smørgrav static int
38334f52dfbbSDag-Erling Smørgrav channel_cancel_rport_listener_streamlocal(struct ssh *ssh, const char *path)
3834462c32cbSDag-Erling Smørgrav {
3835462c32cbSDag-Erling Smørgrav 	u_int i;
3836462c32cbSDag-Erling Smørgrav 	int found = 0;
3837a0ee8cc6SDag-Erling Smørgrav 
38384f52dfbbSDag-Erling Smørgrav 	for (i = 0; i < ssh->chanctxt->channels_alloc; i++) {
38394f52dfbbSDag-Erling Smørgrav 		Channel *c = ssh->chanctxt->channels[i];
3840a0ee8cc6SDag-Erling Smørgrav 		if (c == NULL || c->type != SSH_CHANNEL_RUNIX_LISTENER)
3841a0ee8cc6SDag-Erling Smørgrav 			continue;
3842a0ee8cc6SDag-Erling Smørgrav 		if (c->path == NULL)
3843a0ee8cc6SDag-Erling Smørgrav 			continue;
3844a0ee8cc6SDag-Erling Smørgrav 		if (strcmp(c->path, path) == 0) {
384519261079SEd Maste 			debug2_f("close channel %d", i);
38464f52dfbbSDag-Erling Smørgrav 			channel_free(ssh, c);
3847a0ee8cc6SDag-Erling Smørgrav 			found = 1;
3848a0ee8cc6SDag-Erling Smørgrav 		}
3849a0ee8cc6SDag-Erling Smørgrav 	}
3850a0ee8cc6SDag-Erling Smørgrav 
38514f52dfbbSDag-Erling Smørgrav 	return found;
3852a0ee8cc6SDag-Erling Smørgrav }
3853a0ee8cc6SDag-Erling Smørgrav 
3854a0ee8cc6SDag-Erling Smørgrav int
38554f52dfbbSDag-Erling Smørgrav channel_cancel_rport_listener(struct ssh *ssh, struct Forward *fwd)
3856a0ee8cc6SDag-Erling Smørgrav {
38574f52dfbbSDag-Erling Smørgrav 	if (fwd->listen_path != NULL) {
38584f52dfbbSDag-Erling Smørgrav 		return channel_cancel_rport_listener_streamlocal(ssh,
38594f52dfbbSDag-Erling Smørgrav 		    fwd->listen_path);
38604f52dfbbSDag-Erling Smørgrav 	} else {
38614f52dfbbSDag-Erling Smørgrav 		return channel_cancel_rport_listener_tcpip(ssh,
38624f52dfbbSDag-Erling Smørgrav 		    fwd->listen_host, fwd->listen_port);
38634f52dfbbSDag-Erling Smørgrav 	}
3864a0ee8cc6SDag-Erling Smørgrav }
3865a0ee8cc6SDag-Erling Smørgrav 
3866a0ee8cc6SDag-Erling Smørgrav static int
38674f52dfbbSDag-Erling Smørgrav channel_cancel_lport_listener_tcpip(struct ssh *ssh,
38684f52dfbbSDag-Erling Smørgrav     const char *lhost, u_short lport, int cport,
38694f52dfbbSDag-Erling Smørgrav     struct ForwardOptions *fwd_opts)
3870a0ee8cc6SDag-Erling Smørgrav {
3871a0ee8cc6SDag-Erling Smørgrav 	u_int i;
3872a0ee8cc6SDag-Erling Smørgrav 	int found = 0;
387319261079SEd Maste 	const char *addr = channel_fwd_bind_addr(ssh, lhost, NULL, 1, fwd_opts);
3874462c32cbSDag-Erling Smørgrav 
38754f52dfbbSDag-Erling Smørgrav 	for (i = 0; i < ssh->chanctxt->channels_alloc; i++) {
38764f52dfbbSDag-Erling Smørgrav 		Channel *c = ssh->chanctxt->channels[i];
3877462c32cbSDag-Erling Smørgrav 		if (c == NULL || c->type != SSH_CHANNEL_PORT_LISTENER)
3878462c32cbSDag-Erling Smørgrav 			continue;
3879462c32cbSDag-Erling Smørgrav 		if (c->listening_port != lport)
3880462c32cbSDag-Erling Smørgrav 			continue;
3881462c32cbSDag-Erling Smørgrav 		if (cport == CHANNEL_CANCEL_PORT_STATIC) {
3882462c32cbSDag-Erling Smørgrav 			/* skip dynamic forwardings */
3883462c32cbSDag-Erling Smørgrav 			if (c->host_port == 0)
3884462c32cbSDag-Erling Smørgrav 				continue;
3885462c32cbSDag-Erling Smørgrav 		} else {
3886462c32cbSDag-Erling Smørgrav 			if (c->host_port != cport)
3887462c32cbSDag-Erling Smørgrav 				continue;
3888462c32cbSDag-Erling Smørgrav 		}
3889462c32cbSDag-Erling Smørgrav 		if ((c->listening_addr == NULL && addr != NULL) ||
3890462c32cbSDag-Erling Smørgrav 		    (c->listening_addr != NULL && addr == NULL))
3891462c32cbSDag-Erling Smørgrav 			continue;
3892462c32cbSDag-Erling Smørgrav 		if (addr == NULL || strcmp(c->listening_addr, addr) == 0) {
389319261079SEd Maste 			debug2_f("close channel %d", i);
38944f52dfbbSDag-Erling Smørgrav 			channel_free(ssh, c);
389521e764dfSDag-Erling Smørgrav 			found = 1;
389621e764dfSDag-Erling Smørgrav 		}
389721e764dfSDag-Erling Smørgrav 	}
389821e764dfSDag-Erling Smørgrav 
38994f52dfbbSDag-Erling Smørgrav 	return found;
390021e764dfSDag-Erling Smørgrav }
390121e764dfSDag-Erling Smørgrav 
3902a0ee8cc6SDag-Erling Smørgrav static int
39034f52dfbbSDag-Erling Smørgrav channel_cancel_lport_listener_streamlocal(struct ssh *ssh, const char *path)
3904a0ee8cc6SDag-Erling Smørgrav {
3905a0ee8cc6SDag-Erling Smørgrav 	u_int i;
3906a0ee8cc6SDag-Erling Smørgrav 	int found = 0;
3907a0ee8cc6SDag-Erling Smørgrav 
3908a0ee8cc6SDag-Erling Smørgrav 	if (path == NULL) {
390919261079SEd Maste 		error_f("no path specified.");
3910a0ee8cc6SDag-Erling Smørgrav 		return 0;
3911a0ee8cc6SDag-Erling Smørgrav 	}
3912a0ee8cc6SDag-Erling Smørgrav 
39134f52dfbbSDag-Erling Smørgrav 	for (i = 0; i < ssh->chanctxt->channels_alloc; i++) {
39144f52dfbbSDag-Erling Smørgrav 		Channel *c = ssh->chanctxt->channels[i];
3915a0ee8cc6SDag-Erling Smørgrav 		if (c == NULL || c->type != SSH_CHANNEL_UNIX_LISTENER)
3916a0ee8cc6SDag-Erling Smørgrav 			continue;
3917a0ee8cc6SDag-Erling Smørgrav 		if (c->listening_addr == NULL)
3918a0ee8cc6SDag-Erling Smørgrav 			continue;
3919a0ee8cc6SDag-Erling Smørgrav 		if (strcmp(c->listening_addr, path) == 0) {
392019261079SEd Maste 			debug2_f("close channel %d", i);
39214f52dfbbSDag-Erling Smørgrav 			channel_free(ssh, c);
3922a0ee8cc6SDag-Erling Smørgrav 			found = 1;
3923a0ee8cc6SDag-Erling Smørgrav 		}
3924a0ee8cc6SDag-Erling Smørgrav 	}
3925a0ee8cc6SDag-Erling Smørgrav 
39264f52dfbbSDag-Erling Smørgrav 	return found;
3927a0ee8cc6SDag-Erling Smørgrav }
3928a0ee8cc6SDag-Erling Smørgrav 
3929a0ee8cc6SDag-Erling Smørgrav int
39304f52dfbbSDag-Erling Smørgrav channel_cancel_lport_listener(struct ssh *ssh,
39314f52dfbbSDag-Erling Smørgrav     struct Forward *fwd, int cport, struct ForwardOptions *fwd_opts)
3932af12a3e7SDag-Erling Smørgrav {
3933a0ee8cc6SDag-Erling Smørgrav 	if (fwd->listen_path != NULL) {
39344f52dfbbSDag-Erling Smørgrav 		return channel_cancel_lport_listener_streamlocal(ssh,
39354f52dfbbSDag-Erling Smørgrav 		    fwd->listen_path);
39364f52dfbbSDag-Erling Smørgrav 	} else {
39374f52dfbbSDag-Erling Smørgrav 		return channel_cancel_lport_listener_tcpip(ssh,
39384f52dfbbSDag-Erling Smørgrav 		    fwd->listen_host, fwd->listen_port, cport, fwd_opts);
39394f52dfbbSDag-Erling Smørgrav 	}
39404f52dfbbSDag-Erling Smørgrav }
39414f52dfbbSDag-Erling Smørgrav 
39424f52dfbbSDag-Erling Smørgrav /* protocol local port fwd, used by ssh */
39434f52dfbbSDag-Erling Smørgrav int
39444f52dfbbSDag-Erling Smørgrav channel_setup_local_fwd_listener(struct ssh *ssh,
39454f52dfbbSDag-Erling Smørgrav     struct Forward *fwd, struct ForwardOptions *fwd_opts)
39464f52dfbbSDag-Erling Smørgrav {
39474f52dfbbSDag-Erling Smørgrav 	if (fwd->listen_path != NULL) {
39484f52dfbbSDag-Erling Smørgrav 		return channel_setup_fwd_listener_streamlocal(ssh,
3949a0ee8cc6SDag-Erling Smørgrav 		    SSH_CHANNEL_UNIX_LISTENER, fwd, fwd_opts);
3950a0ee8cc6SDag-Erling Smørgrav 	} else {
39514f52dfbbSDag-Erling Smørgrav 		return channel_setup_fwd_listener_tcpip(ssh,
39524f52dfbbSDag-Erling Smørgrav 		    SSH_CHANNEL_PORT_LISTENER, fwd, NULL, fwd_opts);
3953a0ee8cc6SDag-Erling Smørgrav 	}
3954af12a3e7SDag-Erling Smørgrav }
3955af12a3e7SDag-Erling Smørgrav 
3956190cef3dSDag-Erling Smørgrav /* Matches a remote forwarding permission against a requested forwarding */
3957190cef3dSDag-Erling Smørgrav static int
3958190cef3dSDag-Erling Smørgrav remote_open_match(struct permission *allowed_open, struct Forward *fwd)
3959190cef3dSDag-Erling Smørgrav {
3960190cef3dSDag-Erling Smørgrav 	int ret;
3961190cef3dSDag-Erling Smørgrav 	char *lhost;
3962190cef3dSDag-Erling Smørgrav 
3963190cef3dSDag-Erling Smørgrav 	/* XXX add ACLs for streamlocal */
3964190cef3dSDag-Erling Smørgrav 	if (fwd->listen_path != NULL)
3965190cef3dSDag-Erling Smørgrav 		return 1;
3966190cef3dSDag-Erling Smørgrav 
3967190cef3dSDag-Erling Smørgrav 	if (fwd->listen_host == NULL || allowed_open->listen_host == NULL)
3968190cef3dSDag-Erling Smørgrav 		return 0;
3969190cef3dSDag-Erling Smørgrav 
3970190cef3dSDag-Erling Smørgrav 	if (allowed_open->listen_port != FWD_PERMIT_ANY_PORT &&
3971190cef3dSDag-Erling Smørgrav 	    allowed_open->listen_port != fwd->listen_port)
3972190cef3dSDag-Erling Smørgrav 		return 0;
3973190cef3dSDag-Erling Smørgrav 
3974190cef3dSDag-Erling Smørgrav 	/* Match hostnames case-insensitively */
3975190cef3dSDag-Erling Smørgrav 	lhost = xstrdup(fwd->listen_host);
3976190cef3dSDag-Erling Smørgrav 	lowercase(lhost);
3977190cef3dSDag-Erling Smørgrav 	ret = match_pattern(lhost, allowed_open->listen_host);
3978190cef3dSDag-Erling Smørgrav 	free(lhost);
3979190cef3dSDag-Erling Smørgrav 
3980190cef3dSDag-Erling Smørgrav 	return ret;
3981190cef3dSDag-Erling Smørgrav }
3982190cef3dSDag-Erling Smørgrav 
3983190cef3dSDag-Erling Smørgrav /* Checks whether a requested remote forwarding is permitted */
3984190cef3dSDag-Erling Smørgrav static int
3985190cef3dSDag-Erling Smørgrav check_rfwd_permission(struct ssh *ssh, struct Forward *fwd)
3986190cef3dSDag-Erling Smørgrav {
3987190cef3dSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
3988190cef3dSDag-Erling Smørgrav 	struct permission_set *pset = &sc->remote_perms;
3989190cef3dSDag-Erling Smørgrav 	u_int i, permit, permit_adm = 1;
3990190cef3dSDag-Erling Smørgrav 	struct permission *perm;
3991190cef3dSDag-Erling Smørgrav 
3992190cef3dSDag-Erling Smørgrav 	/* XXX apply GatewayPorts override before checking? */
3993190cef3dSDag-Erling Smørgrav 
3994190cef3dSDag-Erling Smørgrav 	permit = pset->all_permitted;
3995190cef3dSDag-Erling Smørgrav 	if (!permit) {
3996190cef3dSDag-Erling Smørgrav 		for (i = 0; i < pset->num_permitted_user; i++) {
3997190cef3dSDag-Erling Smørgrav 			perm = &pset->permitted_user[i];
3998190cef3dSDag-Erling Smørgrav 			if (remote_open_match(perm, fwd)) {
3999190cef3dSDag-Erling Smørgrav 				permit = 1;
4000190cef3dSDag-Erling Smørgrav 				break;
4001190cef3dSDag-Erling Smørgrav 			}
4002190cef3dSDag-Erling Smørgrav 		}
4003190cef3dSDag-Erling Smørgrav 	}
4004190cef3dSDag-Erling Smørgrav 
4005190cef3dSDag-Erling Smørgrav 	if (pset->num_permitted_admin > 0) {
4006190cef3dSDag-Erling Smørgrav 		permit_adm = 0;
4007190cef3dSDag-Erling Smørgrav 		for (i = 0; i < pset->num_permitted_admin; i++) {
4008190cef3dSDag-Erling Smørgrav 			perm = &pset->permitted_admin[i];
4009190cef3dSDag-Erling Smørgrav 			if (remote_open_match(perm, fwd)) {
4010190cef3dSDag-Erling Smørgrav 				permit_adm = 1;
4011190cef3dSDag-Erling Smørgrav 				break;
4012190cef3dSDag-Erling Smørgrav 			}
4013190cef3dSDag-Erling Smørgrav 		}
4014190cef3dSDag-Erling Smørgrav 	}
4015190cef3dSDag-Erling Smørgrav 
4016190cef3dSDag-Erling Smørgrav 	return permit && permit_adm;
4017190cef3dSDag-Erling Smørgrav }
4018190cef3dSDag-Erling Smørgrav 
4019af12a3e7SDag-Erling Smørgrav /* protocol v2 remote port fwd, used by sshd */
4020af12a3e7SDag-Erling Smørgrav int
40214f52dfbbSDag-Erling Smørgrav channel_setup_remote_fwd_listener(struct ssh *ssh, struct Forward *fwd,
4022a0ee8cc6SDag-Erling Smørgrav     int *allocated_listen_port, struct ForwardOptions *fwd_opts)
4023af12a3e7SDag-Erling Smørgrav {
4024190cef3dSDag-Erling Smørgrav 	if (!check_rfwd_permission(ssh, fwd)) {
402519261079SEd Maste 		ssh_packet_send_debug(ssh, "port forwarding refused");
402619261079SEd Maste 		if (fwd->listen_path != NULL)
402719261079SEd Maste 			/* XXX always allowed, see remote_open_match() */
402819261079SEd Maste 			logit("Received request from %.100s port %d to "
402919261079SEd Maste 			    "remote forward to path \"%.100s\", "
403019261079SEd Maste 			    "but the request was denied.",
403119261079SEd Maste 			    ssh_remote_ipaddr(ssh), ssh_remote_port(ssh),
403219261079SEd Maste 			    fwd->listen_path);
403319261079SEd Maste 		else if(fwd->listen_host != NULL)
403419261079SEd Maste 			logit("Received request from %.100s port %d to "
403519261079SEd Maste 			    "remote forward to host %.100s port %d, "
403619261079SEd Maste 			    "but the request was denied.",
403719261079SEd Maste 			    ssh_remote_ipaddr(ssh), ssh_remote_port(ssh),
403819261079SEd Maste 			    fwd->listen_host, fwd->listen_port );
403919261079SEd Maste 		else
404019261079SEd Maste 			logit("Received request from %.100s port %d to remote "
404119261079SEd Maste 			    "forward, but the request was denied.",
404219261079SEd Maste 			    ssh_remote_ipaddr(ssh), ssh_remote_port(ssh));
4043190cef3dSDag-Erling Smørgrav 		return 0;
4044190cef3dSDag-Erling Smørgrav 	}
4045a0ee8cc6SDag-Erling Smørgrav 	if (fwd->listen_path != NULL) {
40464f52dfbbSDag-Erling Smørgrav 		return channel_setup_fwd_listener_streamlocal(ssh,
4047a0ee8cc6SDag-Erling Smørgrav 		    SSH_CHANNEL_RUNIX_LISTENER, fwd, fwd_opts);
4048a0ee8cc6SDag-Erling Smørgrav 	} else {
40494f52dfbbSDag-Erling Smørgrav 		return channel_setup_fwd_listener_tcpip(ssh,
4050a0ee8cc6SDag-Erling Smørgrav 		    SSH_CHANNEL_RPORT_LISTENER, fwd, allocated_listen_port,
4051a0ee8cc6SDag-Erling Smørgrav 		    fwd_opts);
4052a0ee8cc6SDag-Erling Smørgrav 	}
4053af12a3e7SDag-Erling Smørgrav }
4054af12a3e7SDag-Erling Smørgrav 
4055511b41d2SMark Murray /*
4056462c32cbSDag-Erling Smørgrav  * Translate the requested rfwd listen host to something usable for
4057462c32cbSDag-Erling Smørgrav  * this server.
4058462c32cbSDag-Erling Smørgrav  */
4059462c32cbSDag-Erling Smørgrav static const char *
4060462c32cbSDag-Erling Smørgrav channel_rfwd_bind_host(const char *listen_host)
4061462c32cbSDag-Erling Smørgrav {
4062462c32cbSDag-Erling Smørgrav 	if (listen_host == NULL) {
4063462c32cbSDag-Erling Smørgrav 		return "localhost";
4064462c32cbSDag-Erling Smørgrav 	} else if (*listen_host == '\0' || strcmp(listen_host, "*") == 0) {
4065462c32cbSDag-Erling Smørgrav 		return "";
4066462c32cbSDag-Erling Smørgrav 	} else
4067462c32cbSDag-Erling Smørgrav 		return listen_host;
4068462c32cbSDag-Erling Smørgrav }
4069462c32cbSDag-Erling Smørgrav 
4070462c32cbSDag-Erling Smørgrav /*
4071511b41d2SMark Murray  * Initiate forwarding of connections to port "port" on remote host through
4072511b41d2SMark Murray  * the secure channel to host:port from local side.
4073462c32cbSDag-Erling Smørgrav  * Returns handle (index) for updating the dynamic listen port with
4074190cef3dSDag-Erling Smørgrav  * channel_update_permission().
4075511b41d2SMark Murray  */
4076333ee039SDag-Erling Smørgrav int
40774f52dfbbSDag-Erling Smørgrav channel_request_remote_forwarding(struct ssh *ssh, struct Forward *fwd)
4078511b41d2SMark Murray {
40794f52dfbbSDag-Erling Smørgrav 	int r, success = 0, idx = -1;
40804f52dfbbSDag-Erling Smørgrav 	char *host_to_connect, *listen_host, *listen_path;
40814f52dfbbSDag-Erling Smørgrav 	int port_to_connect, listen_port;
4082ca3176e7SBrian Feldman 
4083511b41d2SMark Murray 	/* Send the forward request to the remote side. */
4084a0ee8cc6SDag-Erling Smørgrav 	if (fwd->listen_path != NULL) {
40854f52dfbbSDag-Erling Smørgrav 		if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 ||
40864f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_cstring(ssh,
40874f52dfbbSDag-Erling Smørgrav 		    "streamlocal-forward@openssh.com")) != 0 ||
40884f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_u8(ssh, 1)) != 0 || /* want reply */
40894f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_cstring(ssh, fwd->listen_path)) != 0 ||
40904f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_send(ssh)) != 0 ||
40914f52dfbbSDag-Erling Smørgrav 		    (r = ssh_packet_write_wait(ssh)) != 0)
409219261079SEd Maste 			fatal_fr(r, "request streamlocal");
4093a0ee8cc6SDag-Erling Smørgrav 	} else {
40944f52dfbbSDag-Erling Smørgrav 		if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 ||
40954f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_cstring(ssh, "tcpip-forward")) != 0 ||
40964f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_u8(ssh, 1)) != 0 || /* want reply */
40974f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_cstring(ssh,
40984f52dfbbSDag-Erling Smørgrav 		    channel_rfwd_bind_host(fwd->listen_host))) != 0 ||
40994f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_u32(ssh, fwd->listen_port)) != 0 ||
41004f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_send(ssh)) != 0 ||
41014f52dfbbSDag-Erling Smørgrav 		    (r = ssh_packet_write_wait(ssh)) != 0)
410219261079SEd Maste 			fatal_fr(r, "request tcpip-forward");
4103a0ee8cc6SDag-Erling Smørgrav 	}
4104ca3176e7SBrian Feldman 	/* Assume that server accepts the request */
4105ca3176e7SBrian Feldman 	success = 1;
4106ca3176e7SBrian Feldman 	if (success) {
4107e2f6069cSDag-Erling Smørgrav 		/* Record that connection to this host/port is permitted. */
41084f52dfbbSDag-Erling Smørgrav 		host_to_connect = listen_host = listen_path = NULL;
41094f52dfbbSDag-Erling Smørgrav 		port_to_connect = listen_port = 0;
4110a0ee8cc6SDag-Erling Smørgrav 		if (fwd->connect_path != NULL) {
41114f52dfbbSDag-Erling Smørgrav 			host_to_connect = xstrdup(fwd->connect_path);
41124f52dfbbSDag-Erling Smørgrav 			port_to_connect = PORT_STREAMLOCAL;
4113a0ee8cc6SDag-Erling Smørgrav 		} else {
41144f52dfbbSDag-Erling Smørgrav 			host_to_connect = xstrdup(fwd->connect_host);
41154f52dfbbSDag-Erling Smørgrav 			port_to_connect = fwd->connect_port;
4116a0ee8cc6SDag-Erling Smørgrav 		}
4117a0ee8cc6SDag-Erling Smørgrav 		if (fwd->listen_path != NULL) {
41184f52dfbbSDag-Erling Smørgrav 			listen_path = xstrdup(fwd->listen_path);
41194f52dfbbSDag-Erling Smørgrav 			listen_port = PORT_STREAMLOCAL;
4120a0ee8cc6SDag-Erling Smørgrav 		} else {
41214f52dfbbSDag-Erling Smørgrav 			if (fwd->listen_host != NULL)
41224f52dfbbSDag-Erling Smørgrav 				listen_host = xstrdup(fwd->listen_host);
41234f52dfbbSDag-Erling Smørgrav 			listen_port = fwd->listen_port;
4124a0ee8cc6SDag-Erling Smørgrav 		}
4125190cef3dSDag-Erling Smørgrav 		idx = permission_set_add(ssh, FORWARD_USER, FORWARD_LOCAL,
41264f52dfbbSDag-Erling Smørgrav 		    host_to_connect, port_to_connect,
41274f52dfbbSDag-Erling Smørgrav 		    listen_host, listen_path, listen_port, NULL);
4128511b41d2SMark Murray 	}
41294f52dfbbSDag-Erling Smørgrav 	return idx;
4130a04a10f8SKris Kennaway }
4131511b41d2SMark Murray 
4132a0ee8cc6SDag-Erling Smørgrav static int
4133190cef3dSDag-Erling Smørgrav open_match(struct permission *allowed_open, const char *requestedhost,
4134a0ee8cc6SDag-Erling Smørgrav     int requestedport)
4135a0ee8cc6SDag-Erling Smørgrav {
4136a0ee8cc6SDag-Erling Smørgrav 	if (allowed_open->host_to_connect == NULL)
4137a0ee8cc6SDag-Erling Smørgrav 		return 0;
4138a0ee8cc6SDag-Erling Smørgrav 	if (allowed_open->port_to_connect != FWD_PERMIT_ANY_PORT &&
4139a0ee8cc6SDag-Erling Smørgrav 	    allowed_open->port_to_connect != requestedport)
4140a0ee8cc6SDag-Erling Smørgrav 		return 0;
4141076ad2f8SDag-Erling Smørgrav 	if (strcmp(allowed_open->host_to_connect, FWD_PERMIT_ANY_HOST) != 0 &&
4142076ad2f8SDag-Erling Smørgrav 	    strcmp(allowed_open->host_to_connect, requestedhost) != 0)
4143a0ee8cc6SDag-Erling Smørgrav 		return 0;
4144a0ee8cc6SDag-Erling Smørgrav 	return 1;
4145a0ee8cc6SDag-Erling Smørgrav }
4146a0ee8cc6SDag-Erling Smørgrav 
4147a0ee8cc6SDag-Erling Smørgrav /*
4148a0ee8cc6SDag-Erling Smørgrav  * Note that in the listen host/port case
4149a0ee8cc6SDag-Erling Smørgrav  * we don't support FWD_PERMIT_ANY_PORT and
4150a0ee8cc6SDag-Erling Smørgrav  * need to translate between the configured-host (listen_host)
4151a0ee8cc6SDag-Erling Smørgrav  * and what we've sent to the remote server (channel_rfwd_bind_host)
4152a0ee8cc6SDag-Erling Smørgrav  */
4153a0ee8cc6SDag-Erling Smørgrav static int
4154190cef3dSDag-Erling Smørgrav open_listen_match_tcpip(struct permission *allowed_open,
4155a0ee8cc6SDag-Erling Smørgrav     const char *requestedhost, u_short requestedport, int translate)
4156a0ee8cc6SDag-Erling Smørgrav {
4157a0ee8cc6SDag-Erling Smørgrav 	const char *allowed_host;
4158a0ee8cc6SDag-Erling Smørgrav 
4159a0ee8cc6SDag-Erling Smørgrav 	if (allowed_open->host_to_connect == NULL)
4160a0ee8cc6SDag-Erling Smørgrav 		return 0;
4161a0ee8cc6SDag-Erling Smørgrav 	if (allowed_open->listen_port != requestedport)
4162a0ee8cc6SDag-Erling Smørgrav 		return 0;
4163a0ee8cc6SDag-Erling Smørgrav 	if (!translate && allowed_open->listen_host == NULL &&
4164a0ee8cc6SDag-Erling Smørgrav 	    requestedhost == NULL)
4165a0ee8cc6SDag-Erling Smørgrav 		return 1;
4166a0ee8cc6SDag-Erling Smørgrav 	allowed_host = translate ?
4167a0ee8cc6SDag-Erling Smørgrav 	    channel_rfwd_bind_host(allowed_open->listen_host) :
4168a0ee8cc6SDag-Erling Smørgrav 	    allowed_open->listen_host;
4169190cef3dSDag-Erling Smørgrav 	if (allowed_host == NULL || requestedhost == NULL ||
4170a0ee8cc6SDag-Erling Smørgrav 	    strcmp(allowed_host, requestedhost) != 0)
4171a0ee8cc6SDag-Erling Smørgrav 		return 0;
4172a0ee8cc6SDag-Erling Smørgrav 	return 1;
4173a0ee8cc6SDag-Erling Smørgrav }
4174a0ee8cc6SDag-Erling Smørgrav 
4175a0ee8cc6SDag-Erling Smørgrav static int
4176190cef3dSDag-Erling Smørgrav open_listen_match_streamlocal(struct permission *allowed_open,
4177a0ee8cc6SDag-Erling Smørgrav     const char *requestedpath)
4178a0ee8cc6SDag-Erling Smørgrav {
4179a0ee8cc6SDag-Erling Smørgrav 	if (allowed_open->host_to_connect == NULL)
4180a0ee8cc6SDag-Erling Smørgrav 		return 0;
4181a0ee8cc6SDag-Erling Smørgrav 	if (allowed_open->listen_port != PORT_STREAMLOCAL)
4182a0ee8cc6SDag-Erling Smørgrav 		return 0;
4183a0ee8cc6SDag-Erling Smørgrav 	if (allowed_open->listen_path == NULL ||
4184a0ee8cc6SDag-Erling Smørgrav 	    strcmp(allowed_open->listen_path, requestedpath) != 0)
4185a0ee8cc6SDag-Erling Smørgrav 		return 0;
4186a0ee8cc6SDag-Erling Smørgrav 	return 1;
4187a0ee8cc6SDag-Erling Smørgrav }
4188a0ee8cc6SDag-Erling Smørgrav 
4189511b41d2SMark Murray /*
419021e764dfSDag-Erling Smørgrav  * Request cancellation of remote forwarding of connection host:port from
419121e764dfSDag-Erling Smørgrav  * local side.
419221e764dfSDag-Erling Smørgrav  */
4193a0ee8cc6SDag-Erling Smørgrav static int
41944f52dfbbSDag-Erling Smørgrav channel_request_rforward_cancel_tcpip(struct ssh *ssh,
41954f52dfbbSDag-Erling Smørgrav     const char *host, u_short port)
419621e764dfSDag-Erling Smørgrav {
41974f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
4198190cef3dSDag-Erling Smørgrav 	struct permission_set *pset = &sc->local_perms;
41994f52dfbbSDag-Erling Smørgrav 	int r;
42004f52dfbbSDag-Erling Smørgrav 	u_int i;
420119261079SEd Maste 	struct permission *perm = NULL;
420221e764dfSDag-Erling Smørgrav 
4203190cef3dSDag-Erling Smørgrav 	for (i = 0; i < pset->num_permitted_user; i++) {
4204190cef3dSDag-Erling Smørgrav 		perm = &pset->permitted_user[i];
4205190cef3dSDag-Erling Smørgrav 		if (open_listen_match_tcpip(perm, host, port, 0))
420621e764dfSDag-Erling Smørgrav 			break;
4207190cef3dSDag-Erling Smørgrav 		perm = NULL;
420821e764dfSDag-Erling Smørgrav 	}
4209190cef3dSDag-Erling Smørgrav 	if (perm == NULL) {
421019261079SEd Maste 		debug_f("requested forward not found");
4211462c32cbSDag-Erling Smørgrav 		return -1;
421221e764dfSDag-Erling Smørgrav 	}
42134f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 ||
42144f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_cstring(ssh, "cancel-tcpip-forward")) != 0 ||
42154f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u8(ssh, 0)) != 0 || /* want reply */
42164f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_cstring(ssh, channel_rfwd_bind_host(host))) != 0 ||
42174f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u32(ssh, port)) != 0 ||
42184f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_send(ssh)) != 0)
421919261079SEd Maste 		fatal_fr(r, "send cancel");
422021e764dfSDag-Erling Smørgrav 
4221190cef3dSDag-Erling Smørgrav 	fwd_perm_clear(perm); /* unregister */
4222462c32cbSDag-Erling Smørgrav 
4223462c32cbSDag-Erling Smørgrav 	return 0;
422421e764dfSDag-Erling Smørgrav }
422521e764dfSDag-Erling Smørgrav 
422621e764dfSDag-Erling Smørgrav /*
4227a0ee8cc6SDag-Erling Smørgrav  * Request cancellation of remote forwarding of Unix domain socket
4228a0ee8cc6SDag-Erling Smørgrav  * path from local side.
4229a0ee8cc6SDag-Erling Smørgrav  */
4230a0ee8cc6SDag-Erling Smørgrav static int
42314f52dfbbSDag-Erling Smørgrav channel_request_rforward_cancel_streamlocal(struct ssh *ssh, const char *path)
4232a0ee8cc6SDag-Erling Smørgrav {
42334f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
4234190cef3dSDag-Erling Smørgrav 	struct permission_set *pset = &sc->local_perms;
42354f52dfbbSDag-Erling Smørgrav 	int r;
42364f52dfbbSDag-Erling Smørgrav 	u_int i;
423719261079SEd Maste 	struct permission *perm = NULL;
4238a0ee8cc6SDag-Erling Smørgrav 
4239190cef3dSDag-Erling Smørgrav 	for (i = 0; i < pset->num_permitted_user; i++) {
4240190cef3dSDag-Erling Smørgrav 		perm = &pset->permitted_user[i];
4241190cef3dSDag-Erling Smørgrav 		if (open_listen_match_streamlocal(perm, path))
4242a0ee8cc6SDag-Erling Smørgrav 			break;
4243190cef3dSDag-Erling Smørgrav 		perm = NULL;
4244a0ee8cc6SDag-Erling Smørgrav 	}
4245190cef3dSDag-Erling Smørgrav 	if (perm == NULL) {
424619261079SEd Maste 		debug_f("requested forward not found");
4247a0ee8cc6SDag-Erling Smørgrav 		return -1;
4248a0ee8cc6SDag-Erling Smørgrav 	}
42494f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 ||
42504f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_cstring(ssh,
42514f52dfbbSDag-Erling Smørgrav 	    "cancel-streamlocal-forward@openssh.com")) != 0 ||
42524f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u8(ssh, 0)) != 0 || /* want reply */
42534f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_cstring(ssh, path)) != 0 ||
42544f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_send(ssh)) != 0)
425519261079SEd Maste 		fatal_fr(r, "send cancel");
4256a0ee8cc6SDag-Erling Smørgrav 
4257190cef3dSDag-Erling Smørgrav 	fwd_perm_clear(perm); /* unregister */
4258a0ee8cc6SDag-Erling Smørgrav 
4259a0ee8cc6SDag-Erling Smørgrav 	return 0;
4260a0ee8cc6SDag-Erling Smørgrav }
4261a0ee8cc6SDag-Erling Smørgrav 
4262a0ee8cc6SDag-Erling Smørgrav /*
4263a0ee8cc6SDag-Erling Smørgrav  * Request cancellation of remote forwarding of a connection from local side.
4264a0ee8cc6SDag-Erling Smørgrav  */
4265a0ee8cc6SDag-Erling Smørgrav int
42664f52dfbbSDag-Erling Smørgrav channel_request_rforward_cancel(struct ssh *ssh, struct Forward *fwd)
4267a0ee8cc6SDag-Erling Smørgrav {
4268a0ee8cc6SDag-Erling Smørgrav 	if (fwd->listen_path != NULL) {
42694f52dfbbSDag-Erling Smørgrav 		return channel_request_rforward_cancel_streamlocal(ssh,
42704f52dfbbSDag-Erling Smørgrav 		    fwd->listen_path);
4271a0ee8cc6SDag-Erling Smørgrav 	} else {
42724f52dfbbSDag-Erling Smørgrav 		return channel_request_rforward_cancel_tcpip(ssh,
42734f52dfbbSDag-Erling Smørgrav 		    fwd->listen_host,
42744f52dfbbSDag-Erling Smørgrav 		    fwd->listen_port ? fwd->listen_port : fwd->allocated_port);
4275a0ee8cc6SDag-Erling Smørgrav 	}
4276a0ee8cc6SDag-Erling Smørgrav }
4277a0ee8cc6SDag-Erling Smørgrav 
4278a0ee8cc6SDag-Erling Smørgrav /*
4279190cef3dSDag-Erling Smørgrav  * Permits opening to any host/port if permitted_user[] is empty.  This is
4280ca3176e7SBrian Feldman  * usually called by the server, because the user could connect to any port
4281ca3176e7SBrian Feldman  * anyway, and the server has no way to know but to trust the client anyway.
4282ca3176e7SBrian Feldman  */
4283ca3176e7SBrian Feldman void
4284190cef3dSDag-Erling Smørgrav channel_permit_all(struct ssh *ssh, int where)
4285ca3176e7SBrian Feldman {
4286190cef3dSDag-Erling Smørgrav 	struct permission_set *pset = permission_set_get(ssh, where);
4287190cef3dSDag-Erling Smørgrav 
4288190cef3dSDag-Erling Smørgrav 	if (pset->num_permitted_user == 0)
4289190cef3dSDag-Erling Smørgrav 		pset->all_permitted = 1;
4290ca3176e7SBrian Feldman }
4291ca3176e7SBrian Feldman 
4292190cef3dSDag-Erling Smørgrav /*
4293190cef3dSDag-Erling Smørgrav  * Permit the specified host/port for forwarding.
4294190cef3dSDag-Erling Smørgrav  */
4295ca3176e7SBrian Feldman void
4296190cef3dSDag-Erling Smørgrav channel_add_permission(struct ssh *ssh, int who, int where,
4297190cef3dSDag-Erling Smørgrav     char *host, int port)
4298ca3176e7SBrian Feldman {
4299190cef3dSDag-Erling Smørgrav 	int local = where == FORWARD_LOCAL;
4300190cef3dSDag-Erling Smørgrav 	struct permission_set *pset = permission_set_get(ssh, where);
43014f52dfbbSDag-Erling Smørgrav 
4302190cef3dSDag-Erling Smørgrav 	debug("allow %s forwarding to host %s port %d",
4303190cef3dSDag-Erling Smørgrav 	    fwd_ident(who, where), host, port);
4304190cef3dSDag-Erling Smørgrav 	/*
4305190cef3dSDag-Erling Smørgrav 	 * Remote forwards set listen_host/port, local forwards set
4306190cef3dSDag-Erling Smørgrav 	 * host/port_to_connect.
4307190cef3dSDag-Erling Smørgrav 	 */
4308190cef3dSDag-Erling Smørgrav 	permission_set_add(ssh, who, where,
4309190cef3dSDag-Erling Smørgrav 	    local ? host : 0, local ? port : 0,
4310190cef3dSDag-Erling Smørgrav 	    local ? NULL : host, NULL, local ? 0 : port, NULL);
4311190cef3dSDag-Erling Smørgrav 	pset->all_permitted = 0;
4312190cef3dSDag-Erling Smørgrav }
4313190cef3dSDag-Erling Smørgrav 
4314190cef3dSDag-Erling Smørgrav /*
4315190cef3dSDag-Erling Smørgrav  * Administratively disable forwarding.
4316190cef3dSDag-Erling Smørgrav  */
4317190cef3dSDag-Erling Smørgrav void
4318190cef3dSDag-Erling Smørgrav channel_disable_admin(struct ssh *ssh, int where)
4319190cef3dSDag-Erling Smørgrav {
4320190cef3dSDag-Erling Smørgrav 	channel_clear_permission(ssh, FORWARD_ADM, where);
4321190cef3dSDag-Erling Smørgrav 	permission_set_add(ssh, FORWARD_ADM, where,
4322190cef3dSDag-Erling Smørgrav 	    NULL, 0, NULL, NULL, 0, NULL);
4323190cef3dSDag-Erling Smørgrav }
4324190cef3dSDag-Erling Smørgrav 
4325190cef3dSDag-Erling Smørgrav /*
4326190cef3dSDag-Erling Smørgrav  * Clear a list of permitted opens.
4327190cef3dSDag-Erling Smørgrav  */
4328190cef3dSDag-Erling Smørgrav void
4329190cef3dSDag-Erling Smørgrav channel_clear_permission(struct ssh *ssh, int who, int where)
4330190cef3dSDag-Erling Smørgrav {
4331190cef3dSDag-Erling Smørgrav 	struct permission **permp;
4332190cef3dSDag-Erling Smørgrav 	u_int *npermp;
4333190cef3dSDag-Erling Smørgrav 
4334190cef3dSDag-Erling Smørgrav 	permission_set_get_array(ssh, who, where, &permp, &npermp);
4335190cef3dSDag-Erling Smørgrav 	*permp = xrecallocarray(*permp, *npermp, 0, sizeof(**permp));
4336190cef3dSDag-Erling Smørgrav 	*npermp = 0;
4337ca3176e7SBrian Feldman }
4338ca3176e7SBrian Feldman 
4339462c32cbSDag-Erling Smørgrav /*
4340462c32cbSDag-Erling Smørgrav  * Update the listen port for a dynamic remote forward, after
4341462c32cbSDag-Erling Smørgrav  * the actual 'newport' has been allocated. If 'newport' < 0 is
4342462c32cbSDag-Erling Smørgrav  * passed then they entry will be invalidated.
4343462c32cbSDag-Erling Smørgrav  */
4344462c32cbSDag-Erling Smørgrav void
4345190cef3dSDag-Erling Smørgrav channel_update_permission(struct ssh *ssh, int idx, int newport)
4346462c32cbSDag-Erling Smørgrav {
4347190cef3dSDag-Erling Smørgrav 	struct permission_set *pset = &ssh->chanctxt->local_perms;
43484f52dfbbSDag-Erling Smørgrav 
4349190cef3dSDag-Erling Smørgrav 	if (idx < 0 || (u_int)idx >= pset->num_permitted_user) {
435019261079SEd Maste 		debug_f("index out of range: %d num_permitted_user %d",
435119261079SEd Maste 		    idx, pset->num_permitted_user);
4352462c32cbSDag-Erling Smørgrav 		return;
4353462c32cbSDag-Erling Smørgrav 	}
4354462c32cbSDag-Erling Smørgrav 	debug("%s allowed port %d for forwarding to host %s port %d",
4355462c32cbSDag-Erling Smørgrav 	    newport > 0 ? "Updating" : "Removing",
4356462c32cbSDag-Erling Smørgrav 	    newport,
4357190cef3dSDag-Erling Smørgrav 	    pset->permitted_user[idx].host_to_connect,
4358190cef3dSDag-Erling Smørgrav 	    pset->permitted_user[idx].port_to_connect);
43594f52dfbbSDag-Erling Smørgrav 	if (newport <= 0)
4360190cef3dSDag-Erling Smørgrav 		fwd_perm_clear(&pset->permitted_user[idx]);
43614f52dfbbSDag-Erling Smørgrav 	else {
4362190cef3dSDag-Erling Smørgrav 		pset->permitted_user[idx].listen_port =
436319261079SEd Maste 		    (ssh->compat & SSH_BUG_DYNAMIC_RPORT) ? 0 : newport;
4364462c32cbSDag-Erling Smørgrav 	}
4365462c32cbSDag-Erling Smørgrav }
4366462c32cbSDag-Erling Smørgrav 
4367462c32cbSDag-Erling Smørgrav /* returns port number, FWD_PERMIT_ANY_PORT or -1 on error */
4368462c32cbSDag-Erling Smørgrav int
4369462c32cbSDag-Erling Smørgrav permitopen_port(const char *p)
4370462c32cbSDag-Erling Smørgrav {
4371462c32cbSDag-Erling Smørgrav 	int port;
4372462c32cbSDag-Erling Smørgrav 
4373462c32cbSDag-Erling Smørgrav 	if (strcmp(p, "*") == 0)
4374462c32cbSDag-Erling Smørgrav 		return FWD_PERMIT_ANY_PORT;
4375462c32cbSDag-Erling Smørgrav 	if ((port = a2port(p)) > 0)
4376462c32cbSDag-Erling Smørgrav 		return port;
4377462c32cbSDag-Erling Smørgrav 	return -1;
4378462c32cbSDag-Erling Smørgrav }
4379462c32cbSDag-Erling Smørgrav 
4380d4af9e69SDag-Erling Smørgrav /* Try to start non-blocking connect to next host in cctx list */
4381d4af9e69SDag-Erling Smørgrav static int
4382d4af9e69SDag-Erling Smørgrav connect_next(struct channel_connect *cctx)
4383d4af9e69SDag-Erling Smørgrav {
4384d4af9e69SDag-Erling Smørgrav 	int sock, saved_errno;
4385a0ee8cc6SDag-Erling Smørgrav 	struct sockaddr_un *sunaddr;
43864f52dfbbSDag-Erling Smørgrav 	char ntop[NI_MAXHOST];
43874f52dfbbSDag-Erling Smørgrav 	char strport[MAXIMUM(NI_MAXSERV, sizeof(sunaddr->sun_path))];
4388d4af9e69SDag-Erling Smørgrav 
4389d4af9e69SDag-Erling Smørgrav 	for (; cctx->ai; cctx->ai = cctx->ai->ai_next) {
4390a0ee8cc6SDag-Erling Smørgrav 		switch (cctx->ai->ai_family) {
4391a0ee8cc6SDag-Erling Smørgrav 		case AF_UNIX:
4392a0ee8cc6SDag-Erling Smørgrav 			/* unix:pathname instead of host:port */
4393a0ee8cc6SDag-Erling Smørgrav 			sunaddr = (struct sockaddr_un *)cctx->ai->ai_addr;
4394a0ee8cc6SDag-Erling Smørgrav 			strlcpy(ntop, "unix", sizeof(ntop));
4395a0ee8cc6SDag-Erling Smørgrav 			strlcpy(strport, sunaddr->sun_path, sizeof(strport));
4396a0ee8cc6SDag-Erling Smørgrav 			break;
4397a0ee8cc6SDag-Erling Smørgrav 		case AF_INET:
4398a0ee8cc6SDag-Erling Smørgrav 		case AF_INET6:
4399d4af9e69SDag-Erling Smørgrav 			if (getnameinfo(cctx->ai->ai_addr, cctx->ai->ai_addrlen,
4400d4af9e69SDag-Erling Smørgrav 			    ntop, sizeof(ntop), strport, sizeof(strport),
4401d4af9e69SDag-Erling Smørgrav 			    NI_NUMERICHOST|NI_NUMERICSERV) != 0) {
4402d4af9e69SDag-Erling Smørgrav 				error("connect_next: getnameinfo failed");
4403511b41d2SMark Murray 				continue;
4404511b41d2SMark Murray 			}
4405a0ee8cc6SDag-Erling Smørgrav 			break;
4406a0ee8cc6SDag-Erling Smørgrav 		default:
4407a0ee8cc6SDag-Erling Smørgrav 			continue;
4408a0ee8cc6SDag-Erling Smørgrav 		}
4409d4af9e69SDag-Erling Smørgrav 		if ((sock = socket(cctx->ai->ai_family, cctx->ai->ai_socktype,
4410d4af9e69SDag-Erling Smørgrav 		    cctx->ai->ai_protocol)) == -1) {
4411d4af9e69SDag-Erling Smørgrav 			if (cctx->ai->ai_next == NULL)
4412511b41d2SMark Murray 				error("socket: %.100s", strerror(errno));
4413e73e9afaSDag-Erling Smørgrav 			else
4414e73e9afaSDag-Erling Smørgrav 				verbose("socket: %.100s", strerror(errno));
4415511b41d2SMark Murray 			continue;
4416511b41d2SMark Murray 		}
441721e764dfSDag-Erling Smørgrav 		if (set_nonblock(sock) == -1)
441819261079SEd Maste 			fatal_f("set_nonblock(%d)", sock);
4419d4af9e69SDag-Erling Smørgrav 		if (connect(sock, cctx->ai->ai_addr,
4420d4af9e69SDag-Erling Smørgrav 		    cctx->ai->ai_addrlen) == -1 && errno != EINPROGRESS) {
4421d4af9e69SDag-Erling Smørgrav 			debug("connect_next: host %.100s ([%.100s]:%s): "
4422d4af9e69SDag-Erling Smørgrav 			    "%.100s", cctx->host, ntop, strport,
4423511b41d2SMark Murray 			    strerror(errno));
4424d4af9e69SDag-Erling Smørgrav 			saved_errno = errno;
4425511b41d2SMark Murray 			close(sock);
4426d4af9e69SDag-Erling Smørgrav 			errno = saved_errno;
4427511b41d2SMark Murray 			continue;	/* fail -- try next */
4428511b41d2SMark Murray 		}
4429a0ee8cc6SDag-Erling Smørgrav 		if (cctx->ai->ai_family != AF_UNIX)
4430a0ee8cc6SDag-Erling Smørgrav 			set_nodelay(sock);
4431d4af9e69SDag-Erling Smørgrav 		debug("connect_next: host %.100s ([%.100s]:%s) "
4432d4af9e69SDag-Erling Smørgrav 		    "in progress, fd=%d", cctx->host, ntop, strport, sock);
4433d4af9e69SDag-Erling Smørgrav 		cctx->ai = cctx->ai->ai_next;
4434a04a10f8SKris Kennaway 		return sock;
4435a04a10f8SKris Kennaway 	}
4436ca3176e7SBrian Feldman 	return -1;
4437ca3176e7SBrian Feldman }
4438ca3176e7SBrian Feldman 
4439d4af9e69SDag-Erling Smørgrav static void
4440d4af9e69SDag-Erling Smørgrav channel_connect_ctx_free(struct channel_connect *cctx)
4441d4af9e69SDag-Erling Smørgrav {
4442e4a9863fSDag-Erling Smørgrav 	free(cctx->host);
4443a0ee8cc6SDag-Erling Smørgrav 	if (cctx->aitop) {
4444a0ee8cc6SDag-Erling Smørgrav 		if (cctx->aitop->ai_family == AF_UNIX)
4445a0ee8cc6SDag-Erling Smørgrav 			free(cctx->aitop);
4446a0ee8cc6SDag-Erling Smørgrav 		else
4447d4af9e69SDag-Erling Smørgrav 			freeaddrinfo(cctx->aitop);
4448a0ee8cc6SDag-Erling Smørgrav 	}
4449b83788ffSDag-Erling Smørgrav 	memset(cctx, 0, sizeof(*cctx));
4450d4af9e69SDag-Erling Smørgrav }
4451d4af9e69SDag-Erling Smørgrav 
4452d93a896eSDag-Erling Smørgrav /*
44534f52dfbbSDag-Erling Smørgrav  * Return connecting socket to remote host:port or local socket path,
4454d93a896eSDag-Erling Smørgrav  * passing back the failure reason if appropriate.
4455d93a896eSDag-Erling Smørgrav  */
44564f52dfbbSDag-Erling Smørgrav static int
44574f52dfbbSDag-Erling Smørgrav connect_to_helper(struct ssh *ssh, const char *name, int port, int socktype,
44584f52dfbbSDag-Erling Smørgrav     char *ctype, char *rname, struct channel_connect *cctx,
4459d93a896eSDag-Erling Smørgrav     int *reason, const char **errmsg)
4460d4af9e69SDag-Erling Smørgrav {
4461d4af9e69SDag-Erling Smørgrav 	struct addrinfo hints;
4462d4af9e69SDag-Erling Smørgrav 	int gaierr;
4463d4af9e69SDag-Erling Smørgrav 	int sock = -1;
4464d4af9e69SDag-Erling Smørgrav 	char strport[NI_MAXSERV];
4465a0ee8cc6SDag-Erling Smørgrav 
4466a0ee8cc6SDag-Erling Smørgrav 	if (port == PORT_STREAMLOCAL) {
4467a0ee8cc6SDag-Erling Smørgrav 		struct sockaddr_un *sunaddr;
4468a0ee8cc6SDag-Erling Smørgrav 		struct addrinfo *ai;
4469a0ee8cc6SDag-Erling Smørgrav 
4470a0ee8cc6SDag-Erling Smørgrav 		if (strlen(name) > sizeof(sunaddr->sun_path)) {
4471a0ee8cc6SDag-Erling Smørgrav 			error("%.100s: %.100s", name, strerror(ENAMETOOLONG));
44724f52dfbbSDag-Erling Smørgrav 			return -1;
4473a0ee8cc6SDag-Erling Smørgrav 		}
4474a0ee8cc6SDag-Erling Smørgrav 
4475a0ee8cc6SDag-Erling Smørgrav 		/*
4476a0ee8cc6SDag-Erling Smørgrav 		 * Fake up a struct addrinfo for AF_UNIX connections.
4477a0ee8cc6SDag-Erling Smørgrav 		 * channel_connect_ctx_free() must check ai_family
4478a0ee8cc6SDag-Erling Smørgrav 		 * and use free() not freeaddirinfo() for AF_UNIX.
4479a0ee8cc6SDag-Erling Smørgrav 		 */
4480a0ee8cc6SDag-Erling Smørgrav 		ai = xmalloc(sizeof(*ai) + sizeof(*sunaddr));
4481a0ee8cc6SDag-Erling Smørgrav 		memset(ai, 0, sizeof(*ai) + sizeof(*sunaddr));
4482a0ee8cc6SDag-Erling Smørgrav 		ai->ai_addr = (struct sockaddr *)(ai + 1);
4483a0ee8cc6SDag-Erling Smørgrav 		ai->ai_addrlen = sizeof(*sunaddr);
4484a0ee8cc6SDag-Erling Smørgrav 		ai->ai_family = AF_UNIX;
44854f52dfbbSDag-Erling Smørgrav 		ai->ai_socktype = socktype;
4486a0ee8cc6SDag-Erling Smørgrav 		ai->ai_protocol = PF_UNSPEC;
4487a0ee8cc6SDag-Erling Smørgrav 		sunaddr = (struct sockaddr_un *)ai->ai_addr;
4488a0ee8cc6SDag-Erling Smørgrav 		sunaddr->sun_family = AF_UNIX;
4489a0ee8cc6SDag-Erling Smørgrav 		strlcpy(sunaddr->sun_path, name, sizeof(sunaddr->sun_path));
44904f52dfbbSDag-Erling Smørgrav 		cctx->aitop = ai;
4491a0ee8cc6SDag-Erling Smørgrav 	} else {
4492d4af9e69SDag-Erling Smørgrav 		memset(&hints, 0, sizeof(hints));
44934f52dfbbSDag-Erling Smørgrav 		hints.ai_family = ssh->chanctxt->IPv4or6;
44944f52dfbbSDag-Erling Smørgrav 		hints.ai_socktype = socktype;
4495d4af9e69SDag-Erling Smørgrav 		snprintf(strport, sizeof strport, "%d", port);
44964f52dfbbSDag-Erling Smørgrav 		if ((gaierr = getaddrinfo(name, strport, &hints, &cctx->aitop))
4497d93a896eSDag-Erling Smørgrav 		    != 0) {
4498d93a896eSDag-Erling Smørgrav 			if (errmsg != NULL)
4499d93a896eSDag-Erling Smørgrav 				*errmsg = ssh_gai_strerror(gaierr);
4500d93a896eSDag-Erling Smørgrav 			if (reason != NULL)
4501d93a896eSDag-Erling Smørgrav 				*reason = SSH2_OPEN_CONNECT_FAILED;
4502a0ee8cc6SDag-Erling Smørgrav 			error("connect_to %.100s: unknown host (%s)", name,
4503d4af9e69SDag-Erling Smørgrav 			    ssh_gai_strerror(gaierr));
45044f52dfbbSDag-Erling Smørgrav 			return -1;
4505d4af9e69SDag-Erling Smørgrav 		}
4506a0ee8cc6SDag-Erling Smørgrav 	}
4507d4af9e69SDag-Erling Smørgrav 
45084f52dfbbSDag-Erling Smørgrav 	cctx->host = xstrdup(name);
45094f52dfbbSDag-Erling Smørgrav 	cctx->port = port;
45104f52dfbbSDag-Erling Smørgrav 	cctx->ai = cctx->aitop;
4511d4af9e69SDag-Erling Smørgrav 
45124f52dfbbSDag-Erling Smørgrav 	if ((sock = connect_next(cctx)) == -1) {
4513d4af9e69SDag-Erling Smørgrav 		error("connect to %.100s port %d failed: %s",
4514a0ee8cc6SDag-Erling Smørgrav 		    name, port, strerror(errno));
45154f52dfbbSDag-Erling Smørgrav 		return -1;
4516d4af9e69SDag-Erling Smørgrav 	}
45174f52dfbbSDag-Erling Smørgrav 
45184f52dfbbSDag-Erling Smørgrav 	return sock;
4519d4af9e69SDag-Erling Smørgrav }
4520d4af9e69SDag-Erling Smørgrav 
4521d93a896eSDag-Erling Smørgrav /* Return CONNECTING channel to remote host:port or local socket path */
4522d93a896eSDag-Erling Smørgrav static Channel *
45234f52dfbbSDag-Erling Smørgrav connect_to(struct ssh *ssh, const char *host, int port,
45244f52dfbbSDag-Erling Smørgrav     char *ctype, char *rname)
4525d93a896eSDag-Erling Smørgrav {
45264f52dfbbSDag-Erling Smørgrav 	struct channel_connect cctx;
45274f52dfbbSDag-Erling Smørgrav 	Channel *c;
45284f52dfbbSDag-Erling Smørgrav 	int sock;
45294f52dfbbSDag-Erling Smørgrav 
45304f52dfbbSDag-Erling Smørgrav 	memset(&cctx, 0, sizeof(cctx));
45314f52dfbbSDag-Erling Smørgrav 	sock = connect_to_helper(ssh, host, port, SOCK_STREAM, ctype, rname,
45324f52dfbbSDag-Erling Smørgrav 	    &cctx, NULL, NULL);
45334f52dfbbSDag-Erling Smørgrav 	if (sock == -1) {
45344f52dfbbSDag-Erling Smørgrav 		channel_connect_ctx_free(&cctx);
45354f52dfbbSDag-Erling Smørgrav 		return NULL;
45364f52dfbbSDag-Erling Smørgrav 	}
45374f52dfbbSDag-Erling Smørgrav 	c = channel_new(ssh, ctype, SSH_CHANNEL_CONNECTING, sock, sock, -1,
45384f52dfbbSDag-Erling Smørgrav 	    CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, rname, 1);
45394f52dfbbSDag-Erling Smørgrav 	c->host_port = port;
45404f52dfbbSDag-Erling Smørgrav 	c->path = xstrdup(host);
45414f52dfbbSDag-Erling Smørgrav 	c->connect_ctx = cctx;
45424f52dfbbSDag-Erling Smørgrav 
45434f52dfbbSDag-Erling Smørgrav 	return c;
4544d93a896eSDag-Erling Smørgrav }
4545d93a896eSDag-Erling Smørgrav 
4546ca86bcf2SDag-Erling Smørgrav /*
4547ca86bcf2SDag-Erling Smørgrav  * returns either the newly connected channel or the downstream channel
4548ca86bcf2SDag-Erling Smørgrav  * that needs to deal with this connection.
4549ca86bcf2SDag-Erling Smørgrav  */
4550d4af9e69SDag-Erling Smørgrav Channel *
45514f52dfbbSDag-Erling Smørgrav channel_connect_by_listen_address(struct ssh *ssh, const char *listen_host,
4552a0ee8cc6SDag-Erling Smørgrav     u_short listen_port, char *ctype, char *rname)
4553d4af9e69SDag-Erling Smørgrav {
45544f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
4555190cef3dSDag-Erling Smørgrav 	struct permission_set *pset = &sc->local_perms;
45564f52dfbbSDag-Erling Smørgrav 	u_int i;
4557190cef3dSDag-Erling Smørgrav 	struct permission *perm;
4558d4af9e69SDag-Erling Smørgrav 
4559190cef3dSDag-Erling Smørgrav 	for (i = 0; i < pset->num_permitted_user; i++) {
4560190cef3dSDag-Erling Smørgrav 		perm = &pset->permitted_user[i];
4561190cef3dSDag-Erling Smørgrav 		if (open_listen_match_tcpip(perm,
4562190cef3dSDag-Erling Smørgrav 		    listen_host, listen_port, 1)) {
4563190cef3dSDag-Erling Smørgrav 			if (perm->downstream)
4564190cef3dSDag-Erling Smørgrav 				return perm->downstream;
4565190cef3dSDag-Erling Smørgrav 			if (perm->port_to_connect == 0)
45664f52dfbbSDag-Erling Smørgrav 				return rdynamic_connect_prepare(ssh,
45674f52dfbbSDag-Erling Smørgrav 				    ctype, rname);
45684f52dfbbSDag-Erling Smørgrav 			return connect_to(ssh,
4569190cef3dSDag-Erling Smørgrav 			    perm->host_to_connect, perm->port_to_connect,
45704f52dfbbSDag-Erling Smørgrav 			    ctype, rname);
4571d4af9e69SDag-Erling Smørgrav 		}
4572d4af9e69SDag-Erling Smørgrav 	}
4573d4af9e69SDag-Erling Smørgrav 	error("WARNING: Server requests forwarding for unknown listen_port %d",
4574d4af9e69SDag-Erling Smørgrav 	    listen_port);
4575d4af9e69SDag-Erling Smørgrav 	return NULL;
4576d4af9e69SDag-Erling Smørgrav }
4577d4af9e69SDag-Erling Smørgrav 
4578a0ee8cc6SDag-Erling Smørgrav Channel *
45794f52dfbbSDag-Erling Smørgrav channel_connect_by_listen_path(struct ssh *ssh, const char *path,
45804f52dfbbSDag-Erling Smørgrav     char *ctype, char *rname)
4581a0ee8cc6SDag-Erling Smørgrav {
45824f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
4583190cef3dSDag-Erling Smørgrav 	struct permission_set *pset = &sc->local_perms;
45844f52dfbbSDag-Erling Smørgrav 	u_int i;
4585190cef3dSDag-Erling Smørgrav 	struct permission *perm;
4586a0ee8cc6SDag-Erling Smørgrav 
4587190cef3dSDag-Erling Smørgrav 	for (i = 0; i < pset->num_permitted_user; i++) {
4588190cef3dSDag-Erling Smørgrav 		perm = &pset->permitted_user[i];
4589190cef3dSDag-Erling Smørgrav 		if (open_listen_match_streamlocal(perm, path)) {
45904f52dfbbSDag-Erling Smørgrav 			return connect_to(ssh,
4591190cef3dSDag-Erling Smørgrav 			    perm->host_to_connect, perm->port_to_connect,
45924f52dfbbSDag-Erling Smørgrav 			    ctype, rname);
4593a0ee8cc6SDag-Erling Smørgrav 		}
4594a0ee8cc6SDag-Erling Smørgrav 	}
4595a0ee8cc6SDag-Erling Smørgrav 	error("WARNING: Server requests forwarding for unknown path %.100s",
4596a0ee8cc6SDag-Erling Smørgrav 	    path);
4597a0ee8cc6SDag-Erling Smørgrav 	return NULL;
4598a0ee8cc6SDag-Erling Smørgrav }
4599a0ee8cc6SDag-Erling Smørgrav 
4600ca3176e7SBrian Feldman /* Check if connecting to that port is permitted and connect. */
4601d4af9e69SDag-Erling Smørgrav Channel *
46024f52dfbbSDag-Erling Smørgrav channel_connect_to_port(struct ssh *ssh, const char *host, u_short port,
46034f52dfbbSDag-Erling Smørgrav     char *ctype, char *rname, int *reason, const char **errmsg)
4604ca3176e7SBrian Feldman {
46054f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
4606190cef3dSDag-Erling Smørgrav 	struct permission_set *pset = &sc->local_perms;
46074f52dfbbSDag-Erling Smørgrav 	struct channel_connect cctx;
46084f52dfbbSDag-Erling Smørgrav 	Channel *c;
46094f52dfbbSDag-Erling Smørgrav 	u_int i, permit, permit_adm = 1;
46104f52dfbbSDag-Erling Smørgrav 	int sock;
4611190cef3dSDag-Erling Smørgrav 	struct permission *perm;
4612ca3176e7SBrian Feldman 
4613190cef3dSDag-Erling Smørgrav 	permit = pset->all_permitted;
4614ca3176e7SBrian Feldman 	if (!permit) {
4615190cef3dSDag-Erling Smørgrav 		for (i = 0; i < pset->num_permitted_user; i++) {
4616190cef3dSDag-Erling Smørgrav 			perm = &pset->permitted_user[i];
4617190cef3dSDag-Erling Smørgrav 			if (open_match(perm, host, port)) {
4618ca3176e7SBrian Feldman 				permit = 1;
4619a0ee8cc6SDag-Erling Smørgrav 				break;
4620a0ee8cc6SDag-Erling Smørgrav 			}
4621ca3176e7SBrian Feldman 		}
46224f52dfbbSDag-Erling Smørgrav 	}
4623333ee039SDag-Erling Smørgrav 
4624190cef3dSDag-Erling Smørgrav 	if (pset->num_permitted_admin > 0) {
4625333ee039SDag-Erling Smørgrav 		permit_adm = 0;
4626190cef3dSDag-Erling Smørgrav 		for (i = 0; i < pset->num_permitted_admin; i++) {
4627190cef3dSDag-Erling Smørgrav 			perm = &pset->permitted_admin[i];
4628190cef3dSDag-Erling Smørgrav 			if (open_match(perm, host, port)) {
4629333ee039SDag-Erling Smørgrav 				permit_adm = 1;
4630a0ee8cc6SDag-Erling Smørgrav 				break;
4631a0ee8cc6SDag-Erling Smørgrav 			}
4632333ee039SDag-Erling Smørgrav 		}
46334f52dfbbSDag-Erling Smørgrav 	}
4634333ee039SDag-Erling Smørgrav 
4635333ee039SDag-Erling Smørgrav 	if (!permit || !permit_adm) {
463619261079SEd Maste 		logit("Received request from %.100s port %d to connect to "
463719261079SEd Maste 		    "host %.100s port %d, but the request was denied.",
463819261079SEd Maste 		    ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), host, port);
4639d93a896eSDag-Erling Smørgrav 		if (reason != NULL)
4640d93a896eSDag-Erling Smørgrav 			*reason = SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED;
4641d4af9e69SDag-Erling Smørgrav 		return NULL;
4642ca3176e7SBrian Feldman 	}
46434f52dfbbSDag-Erling Smørgrav 
46444f52dfbbSDag-Erling Smørgrav 	memset(&cctx, 0, sizeof(cctx));
46454f52dfbbSDag-Erling Smørgrav 	sock = connect_to_helper(ssh, host, port, SOCK_STREAM, ctype, rname,
46464f52dfbbSDag-Erling Smørgrav 	    &cctx, reason, errmsg);
46474f52dfbbSDag-Erling Smørgrav 	if (sock == -1) {
46484f52dfbbSDag-Erling Smørgrav 		channel_connect_ctx_free(&cctx);
46494f52dfbbSDag-Erling Smørgrav 		return NULL;
46504f52dfbbSDag-Erling Smørgrav 	}
46514f52dfbbSDag-Erling Smørgrav 
46524f52dfbbSDag-Erling Smørgrav 	c = channel_new(ssh, ctype, SSH_CHANNEL_CONNECTING, sock, sock, -1,
46534f52dfbbSDag-Erling Smørgrav 	    CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, rname, 1);
46544f52dfbbSDag-Erling Smørgrav 	c->host_port = port;
46554f52dfbbSDag-Erling Smørgrav 	c->path = xstrdup(host);
46564f52dfbbSDag-Erling Smørgrav 	c->connect_ctx = cctx;
46574f52dfbbSDag-Erling Smørgrav 
46584f52dfbbSDag-Erling Smørgrav 	return c;
4659ca3176e7SBrian Feldman }
4660ca3176e7SBrian Feldman 
4661a0ee8cc6SDag-Erling Smørgrav /* Check if connecting to that path is permitted and connect. */
4662a0ee8cc6SDag-Erling Smørgrav Channel *
46634f52dfbbSDag-Erling Smørgrav channel_connect_to_path(struct ssh *ssh, const char *path,
46644f52dfbbSDag-Erling Smørgrav     char *ctype, char *rname)
4665a0ee8cc6SDag-Erling Smørgrav {
46664f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
4667190cef3dSDag-Erling Smørgrav 	struct permission_set *pset = &sc->local_perms;
46684f52dfbbSDag-Erling Smørgrav 	u_int i, permit, permit_adm = 1;
4669190cef3dSDag-Erling Smørgrav 	struct permission *perm;
4670a0ee8cc6SDag-Erling Smørgrav 
4671190cef3dSDag-Erling Smørgrav 	permit = pset->all_permitted;
4672a0ee8cc6SDag-Erling Smørgrav 	if (!permit) {
4673190cef3dSDag-Erling Smørgrav 		for (i = 0; i < pset->num_permitted_user; i++) {
4674190cef3dSDag-Erling Smørgrav 			perm = &pset->permitted_user[i];
4675190cef3dSDag-Erling Smørgrav 			if (open_match(perm, path, PORT_STREAMLOCAL)) {
4676a0ee8cc6SDag-Erling Smørgrav 				permit = 1;
4677a0ee8cc6SDag-Erling Smørgrav 				break;
4678a0ee8cc6SDag-Erling Smørgrav 			}
4679a0ee8cc6SDag-Erling Smørgrav 		}
46804f52dfbbSDag-Erling Smørgrav 	}
4681a0ee8cc6SDag-Erling Smørgrav 
4682190cef3dSDag-Erling Smørgrav 	if (pset->num_permitted_admin > 0) {
4683a0ee8cc6SDag-Erling Smørgrav 		permit_adm = 0;
4684190cef3dSDag-Erling Smørgrav 		for (i = 0; i < pset->num_permitted_admin; i++) {
4685190cef3dSDag-Erling Smørgrav 			perm = &pset->permitted_admin[i];
4686190cef3dSDag-Erling Smørgrav 			if (open_match(perm, path, PORT_STREAMLOCAL)) {
4687a0ee8cc6SDag-Erling Smørgrav 				permit_adm = 1;
4688a0ee8cc6SDag-Erling Smørgrav 				break;
4689a0ee8cc6SDag-Erling Smørgrav 			}
4690a0ee8cc6SDag-Erling Smørgrav 		}
46914f52dfbbSDag-Erling Smørgrav 	}
4692a0ee8cc6SDag-Erling Smørgrav 
4693a0ee8cc6SDag-Erling Smørgrav 	if (!permit || !permit_adm) {
4694a0ee8cc6SDag-Erling Smørgrav 		logit("Received request to connect to path %.100s, "
4695a0ee8cc6SDag-Erling Smørgrav 		    "but the request was denied.", path);
4696a0ee8cc6SDag-Erling Smørgrav 		return NULL;
4697a0ee8cc6SDag-Erling Smørgrav 	}
46984f52dfbbSDag-Erling Smørgrav 	return connect_to(ssh, path, PORT_STREAMLOCAL, ctype, rname);
4699a0ee8cc6SDag-Erling Smørgrav }
4700a0ee8cc6SDag-Erling Smørgrav 
470121e764dfSDag-Erling Smørgrav void
47024f52dfbbSDag-Erling Smørgrav channel_send_window_changes(struct ssh *ssh)
470321e764dfSDag-Erling Smørgrav {
47044f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
470521e764dfSDag-Erling Smørgrav 	struct winsize ws;
47064f52dfbbSDag-Erling Smørgrav 	int r;
47074f52dfbbSDag-Erling Smørgrav 	u_int i;
470821e764dfSDag-Erling Smørgrav 
47094f52dfbbSDag-Erling Smørgrav 	for (i = 0; i < sc->channels_alloc; i++) {
47104f52dfbbSDag-Erling Smørgrav 		if (sc->channels[i] == NULL || !sc->channels[i]->client_tty ||
47114f52dfbbSDag-Erling Smørgrav 		    sc->channels[i]->type != SSH_CHANNEL_OPEN)
471221e764dfSDag-Erling Smørgrav 			continue;
471319261079SEd Maste 		if (ioctl(sc->channels[i]->rfd, TIOCGWINSZ, &ws) == -1)
471421e764dfSDag-Erling Smørgrav 			continue;
47154f52dfbbSDag-Erling Smørgrav 		channel_request_start(ssh, i, "window-change", 0);
47164f52dfbbSDag-Erling Smørgrav 		if ((r = sshpkt_put_u32(ssh, (u_int)ws.ws_col)) != 0 ||
47174f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_u32(ssh, (u_int)ws.ws_row)) != 0 ||
47184f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_u32(ssh, (u_int)ws.ws_xpixel)) != 0 ||
47194f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_put_u32(ssh, (u_int)ws.ws_ypixel)) != 0 ||
47204f52dfbbSDag-Erling Smørgrav 		    (r = sshpkt_send(ssh)) != 0)
472119261079SEd Maste 			fatal_fr(r, "channel %u; send window-change", i);
472221e764dfSDag-Erling Smørgrav 	}
472321e764dfSDag-Erling Smørgrav }
472421e764dfSDag-Erling Smørgrav 
47254f52dfbbSDag-Erling Smørgrav /* Return RDYNAMIC_OPEN channel: channel allows SOCKS, but is not connected */
47264f52dfbbSDag-Erling Smørgrav static Channel *
47274f52dfbbSDag-Erling Smørgrav rdynamic_connect_prepare(struct ssh *ssh, char *ctype, char *rname)
47284f52dfbbSDag-Erling Smørgrav {
47294f52dfbbSDag-Erling Smørgrav 	Channel *c;
47304f52dfbbSDag-Erling Smørgrav 	int r;
47314f52dfbbSDag-Erling Smørgrav 
47324f52dfbbSDag-Erling Smørgrav 	c = channel_new(ssh, ctype, SSH_CHANNEL_RDYNAMIC_OPEN, -1, -1, -1,
47334f52dfbbSDag-Erling Smørgrav 	    CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, rname, 1);
47344f52dfbbSDag-Erling Smørgrav 	c->host_port = 0;
47354f52dfbbSDag-Erling Smørgrav 	c->path = NULL;
47364f52dfbbSDag-Erling Smørgrav 
47374f52dfbbSDag-Erling Smørgrav 	/*
47384f52dfbbSDag-Erling Smørgrav 	 * We need to open the channel before we have a FD,
47394f52dfbbSDag-Erling Smørgrav 	 * so that we can get SOCKS header from peer.
47404f52dfbbSDag-Erling Smørgrav 	 */
47414f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_OPEN_CONFIRMATION)) != 0 ||
47424f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 ||
47434f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u32(ssh, c->self)) != 0 ||
47444f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u32(ssh, c->local_window)) != 0 ||
474519261079SEd Maste 	    (r = sshpkt_put_u32(ssh, c->local_maxpacket)) != 0)
474619261079SEd Maste 		fatal_fr(r, "channel %i; confirm", c->self);
47474f52dfbbSDag-Erling Smørgrav 	return c;
47484f52dfbbSDag-Erling Smørgrav }
47494f52dfbbSDag-Erling Smørgrav 
47504f52dfbbSDag-Erling Smørgrav /* Return CONNECTING socket to remote host:port or local socket path */
47514f52dfbbSDag-Erling Smørgrav static int
47524f52dfbbSDag-Erling Smørgrav rdynamic_connect_finish(struct ssh *ssh, Channel *c)
47534f52dfbbSDag-Erling Smørgrav {
475419261079SEd Maste 	struct ssh_channels *sc = ssh->chanctxt;
475519261079SEd Maste 	struct permission_set *pset = &sc->local_perms;
475619261079SEd Maste 	struct permission *perm;
47574f52dfbbSDag-Erling Smørgrav 	struct channel_connect cctx;
475819261079SEd Maste 	u_int i, permit_adm = 1;
47594f52dfbbSDag-Erling Smørgrav 	int sock;
47604f52dfbbSDag-Erling Smørgrav 
476119261079SEd Maste 	if (pset->num_permitted_admin > 0) {
476219261079SEd Maste 		permit_adm = 0;
476319261079SEd Maste 		for (i = 0; i < pset->num_permitted_admin; i++) {
476419261079SEd Maste 			perm = &pset->permitted_admin[i];
476519261079SEd Maste 			if (open_match(perm, c->path, c->host_port)) {
476619261079SEd Maste 				permit_adm = 1;
476719261079SEd Maste 				break;
476819261079SEd Maste 			}
476919261079SEd Maste 		}
477019261079SEd Maste 	}
477119261079SEd Maste 	if (!permit_adm) {
477219261079SEd Maste 		debug_f("requested forward not permitted");
477319261079SEd Maste 		return -1;
477419261079SEd Maste 	}
477519261079SEd Maste 
47764f52dfbbSDag-Erling Smørgrav 	memset(&cctx, 0, sizeof(cctx));
47774f52dfbbSDag-Erling Smørgrav 	sock = connect_to_helper(ssh, c->path, c->host_port, SOCK_STREAM, NULL,
47784f52dfbbSDag-Erling Smørgrav 	    NULL, &cctx, NULL, NULL);
47794f52dfbbSDag-Erling Smørgrav 	if (sock == -1)
47804f52dfbbSDag-Erling Smørgrav 		channel_connect_ctx_free(&cctx);
47814f52dfbbSDag-Erling Smørgrav 	else {
47824f52dfbbSDag-Erling Smørgrav 		/* similar to SSH_CHANNEL_CONNECTING but we've already sent the open */
47834f52dfbbSDag-Erling Smørgrav 		c->type = SSH_CHANNEL_RDYNAMIC_FINISH;
47844f52dfbbSDag-Erling Smørgrav 		c->connect_ctx = cctx;
47854f52dfbbSDag-Erling Smørgrav 		channel_register_fds(ssh, c, sock, sock, -1, 0, 1, 0);
47864f52dfbbSDag-Erling Smørgrav 	}
47874f52dfbbSDag-Erling Smørgrav 	return sock;
47884f52dfbbSDag-Erling Smørgrav }
47894f52dfbbSDag-Erling Smørgrav 
4790af12a3e7SDag-Erling Smørgrav /* -- X11 forwarding */
4791511b41d2SMark Murray 
4792511b41d2SMark Murray /*
4793511b41d2SMark Murray  * Creates an internet domain socket for listening for X11 connections.
4794a82e551fSDag-Erling Smørgrav  * Returns 0 and a suitable display number for the DISPLAY variable
4795a82e551fSDag-Erling Smørgrav  * stored in display_numberp , or -1 if an error occurs.
4796511b41d2SMark Murray  */
4797af12a3e7SDag-Erling Smørgrav int
47984f52dfbbSDag-Erling Smørgrav x11_create_display_inet(struct ssh *ssh, int x11_display_offset,
47994f52dfbbSDag-Erling Smørgrav     int x11_use_localhost, int single_connection,
48004f52dfbbSDag-Erling Smørgrav     u_int *display_numberp, int **chanids)
4801511b41d2SMark Murray {
4802af12a3e7SDag-Erling Smørgrav 	Channel *nc = NULL;
4803511b41d2SMark Murray 	int display_number, sock;
4804511b41d2SMark Murray 	u_short port;
4805511b41d2SMark Murray 	struct addrinfo hints, *ai, *aitop;
4806511b41d2SMark Murray 	char strport[NI_MAXSERV];
4807511b41d2SMark Murray 	int gaierr, n, num_socks = 0, socks[NUM_SOCKS];
4808511b41d2SMark Murray 
4809b74df5b2SDag-Erling Smørgrav 	if (chanids == NULL)
4810b74df5b2SDag-Erling Smørgrav 		return -1;
4811b74df5b2SDag-Erling Smørgrav 
4812511b41d2SMark Murray 	for (display_number = x11_display_offset;
4813511b41d2SMark Murray 	    display_number < MAX_DISPLAYS;
4814511b41d2SMark Murray 	    display_number++) {
4815511b41d2SMark Murray 		port = 6000 + display_number;
4816511b41d2SMark Murray 		memset(&hints, 0, sizeof(hints));
48174f52dfbbSDag-Erling Smørgrav 		hints.ai_family = ssh->chanctxt->IPv4or6;
4818af12a3e7SDag-Erling Smørgrav 		hints.ai_flags = x11_use_localhost ? 0: AI_PASSIVE;
4819511b41d2SMark Murray 		hints.ai_socktype = SOCK_STREAM;
4820511b41d2SMark Murray 		snprintf(strport, sizeof strport, "%d", port);
48214f52dfbbSDag-Erling Smørgrav 		if ((gaierr = getaddrinfo(NULL, strport,
48224f52dfbbSDag-Erling Smørgrav 		    &hints, &aitop)) != 0) {
4823d4af9e69SDag-Erling Smørgrav 			error("getaddrinfo: %.100s", ssh_gai_strerror(gaierr));
4824af12a3e7SDag-Erling Smørgrav 			return -1;
4825511b41d2SMark Murray 		}
4826511b41d2SMark Murray 		for (ai = aitop; ai; ai = ai->ai_next) {
48274f52dfbbSDag-Erling Smørgrav 			if (ai->ai_family != AF_INET &&
48284f52dfbbSDag-Erling Smørgrav 			    ai->ai_family != AF_INET6)
4829511b41d2SMark Murray 				continue;
4830221552e4SDag-Erling Smørgrav 			sock = socket(ai->ai_family, ai->ai_socktype,
4831221552e4SDag-Erling Smørgrav 			    ai->ai_protocol);
483219261079SEd Maste 			if (sock == -1) {
48338ad9b54aSDag-Erling Smørgrav 				if ((errno != EINVAL) && (errno != EAFNOSUPPORT)
48348ad9b54aSDag-Erling Smørgrav #ifdef EPFNOSUPPORT
48358ad9b54aSDag-Erling Smørgrav 				    && (errno != EPFNOSUPPORT)
48368ad9b54aSDag-Erling Smørgrav #endif
48378ad9b54aSDag-Erling Smørgrav 				    ) {
4838511b41d2SMark Murray 					error("socket: %.100s", strerror(errno));
483921e764dfSDag-Erling Smørgrav 					freeaddrinfo(aitop);
4840af12a3e7SDag-Erling Smørgrav 					return -1;
4841989dd127SDag-Erling Smørgrav 				} else {
4842989dd127SDag-Erling Smørgrav 					debug("x11_create_display_inet: Socket family %d not supported",
4843989dd127SDag-Erling Smørgrav 						 ai->ai_family);
4844989dd127SDag-Erling Smørgrav 					continue;
4845511b41d2SMark Murray 				}
4846989dd127SDag-Erling Smørgrav 			}
4847b15c8340SDag-Erling Smørgrav 			if (ai->ai_family == AF_INET6)
4848b15c8340SDag-Erling Smørgrav 				sock_set_v6only(sock);
4849d4af9e69SDag-Erling Smørgrav 			if (x11_use_localhost)
485047dd1d1bSDag-Erling Smørgrav 				set_reuseaddr(sock);
485119261079SEd Maste 			if (bind(sock, ai->ai_addr, ai->ai_addrlen) == -1) {
485219261079SEd Maste 				debug2_f("bind port %d: %.100s", port,
485319261079SEd Maste 				    strerror(errno));
4854511b41d2SMark Murray 				close(sock);
48554f52dfbbSDag-Erling Smørgrav 				for (n = 0; n < num_socks; n++)
4856511b41d2SMark Murray 					close(socks[n]);
4857511b41d2SMark Murray 				num_socks = 0;
4858511b41d2SMark Murray 				break;
4859511b41d2SMark Murray 			}
4860511b41d2SMark Murray 			socks[num_socks++] = sock;
4861511b41d2SMark Murray 			if (num_socks == NUM_SOCKS)
4862511b41d2SMark Murray 				break;
4863511b41d2SMark Murray 		}
4864ca3176e7SBrian Feldman 		freeaddrinfo(aitop);
4865511b41d2SMark Murray 		if (num_socks > 0)
4866511b41d2SMark Murray 			break;
4867511b41d2SMark Murray 	}
4868511b41d2SMark Murray 	if (display_number >= MAX_DISPLAYS) {
4869511b41d2SMark Murray 		error("Failed to allocate internet-domain X11 display socket.");
4870af12a3e7SDag-Erling Smørgrav 		return -1;
4871511b41d2SMark Murray 	}
4872511b41d2SMark Murray 	/* Start listening for connections on the socket. */
4873511b41d2SMark Murray 	for (n = 0; n < num_socks; n++) {
4874511b41d2SMark Murray 		sock = socks[n];
487519261079SEd Maste 		if (listen(sock, SSH_LISTEN_BACKLOG) == -1) {
4876511b41d2SMark Murray 			error("listen: %.100s", strerror(errno));
4877511b41d2SMark Murray 			close(sock);
4878af12a3e7SDag-Erling Smørgrav 			return -1;
4879511b41d2SMark Murray 		}
4880511b41d2SMark Murray 	}
4881511b41d2SMark Murray 
4882511b41d2SMark Murray 	/* Allocate a channel for each socket. */
4883333ee039SDag-Erling Smørgrav 	*chanids = xcalloc(num_socks + 1, sizeof(**chanids));
4884511b41d2SMark Murray 	for (n = 0; n < num_socks; n++) {
4885511b41d2SMark Murray 		sock = socks[n];
48864f52dfbbSDag-Erling Smørgrav 		nc = channel_new(ssh, "x11 listener",
4887a04a10f8SKris Kennaway 		    SSH_CHANNEL_X11_LISTENER, sock, sock, -1,
4888a04a10f8SKris Kennaway 		    CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT,
4889221552e4SDag-Erling Smørgrav 		    0, "X11 inet listener", 1);
4890af12a3e7SDag-Erling Smørgrav 		nc->single_connection = single_connection;
4891d4ecd108SDag-Erling Smørgrav 		(*chanids)[n] = nc->self;
4892511b41d2SMark Murray 	}
4893d4ecd108SDag-Erling Smørgrav 	(*chanids)[n] = -1;
4894511b41d2SMark Murray 
4895af12a3e7SDag-Erling Smørgrav 	/* Return the display number for the DISPLAY environment variable. */
4896a82e551fSDag-Erling Smørgrav 	*display_numberp = display_number;
48974f52dfbbSDag-Erling Smørgrav 	return 0;
4898511b41d2SMark Murray }
4899511b41d2SMark Murray 
4900af12a3e7SDag-Erling Smørgrav static int
4901cce7d346SDag-Erling Smørgrav connect_local_xsocket_path(const char *pathname)
4902511b41d2SMark Murray {
4903511b41d2SMark Murray 	int sock;
4904511b41d2SMark Murray 	struct sockaddr_un addr;
4905511b41d2SMark Murray 
4906511b41d2SMark Murray 	sock = socket(AF_UNIX, SOCK_STREAM, 0);
490719261079SEd Maste 	if (sock == -1)
4908511b41d2SMark Murray 		error("socket: %.100s", strerror(errno));
4909511b41d2SMark Murray 	memset(&addr, 0, sizeof(addr));
4910511b41d2SMark Murray 	addr.sun_family = AF_UNIX;
4911cce7d346SDag-Erling Smørgrav 	strlcpy(addr.sun_path, pathname, sizeof addr.sun_path);
4912511b41d2SMark Murray 	if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) == 0)
4913511b41d2SMark Murray 		return sock;
4914511b41d2SMark Murray 	close(sock);
4915511b41d2SMark Murray 	error("connect %.100s: %.100s", addr.sun_path, strerror(errno));
4916511b41d2SMark Murray 	return -1;
4917511b41d2SMark Murray }
4918511b41d2SMark Murray 
4919cce7d346SDag-Erling Smørgrav static int
4920cce7d346SDag-Erling Smørgrav connect_local_xsocket(u_int dnr)
4921cce7d346SDag-Erling Smørgrav {
4922cce7d346SDag-Erling Smørgrav 	char buf[1024];
4923cce7d346SDag-Erling Smørgrav 	snprintf(buf, sizeof buf, _PATH_UNIX_X, dnr);
4924cce7d346SDag-Erling Smørgrav 	return connect_local_xsocket_path(buf);
4925cce7d346SDag-Erling Smørgrav }
4926cce7d346SDag-Erling Smørgrav 
4927d93a896eSDag-Erling Smørgrav #ifdef __APPLE__
4928d93a896eSDag-Erling Smørgrav static int
4929d93a896eSDag-Erling Smørgrav is_path_to_xsocket(const char *display, char *path, size_t pathlen)
4930d93a896eSDag-Erling Smørgrav {
4931d93a896eSDag-Erling Smørgrav 	struct stat sbuf;
4932d93a896eSDag-Erling Smørgrav 
4933d93a896eSDag-Erling Smørgrav 	if (strlcpy(path, display, pathlen) >= pathlen) {
4934d93a896eSDag-Erling Smørgrav 		error("%s: display path too long", __func__);
4935d93a896eSDag-Erling Smørgrav 		return 0;
4936d93a896eSDag-Erling Smørgrav 	}
4937d93a896eSDag-Erling Smørgrav 	if (display[0] != '/')
4938d93a896eSDag-Erling Smørgrav 		return 0;
4939d93a896eSDag-Erling Smørgrav 	if (stat(path, &sbuf) == 0) {
4940d93a896eSDag-Erling Smørgrav 		return 1;
4941d93a896eSDag-Erling Smørgrav 	} else {
4942d93a896eSDag-Erling Smørgrav 		char *dot = strrchr(path, '.');
4943d93a896eSDag-Erling Smørgrav 		if (dot != NULL) {
4944d93a896eSDag-Erling Smørgrav 			*dot = '\0';
4945d93a896eSDag-Erling Smørgrav 			if (stat(path, &sbuf) == 0) {
4946d93a896eSDag-Erling Smørgrav 				return 1;
4947d93a896eSDag-Erling Smørgrav 			}
4948d93a896eSDag-Erling Smørgrav 		}
4949d93a896eSDag-Erling Smørgrav 	}
4950d93a896eSDag-Erling Smørgrav 	return 0;
4951d93a896eSDag-Erling Smørgrav }
4952d93a896eSDag-Erling Smørgrav #endif
4953d93a896eSDag-Erling Smørgrav 
4954a04a10f8SKris Kennaway int
49554f52dfbbSDag-Erling Smørgrav x11_connect_display(struct ssh *ssh)
4956511b41d2SMark Murray {
4957333ee039SDag-Erling Smørgrav 	u_int display_number;
4958511b41d2SMark Murray 	const char *display;
4959a04a10f8SKris Kennaway 	char buf[1024], *cp;
4960511b41d2SMark Murray 	struct addrinfo hints, *ai, *aitop;
4961511b41d2SMark Murray 	char strport[NI_MAXSERV];
4962333ee039SDag-Erling Smørgrav 	int gaierr, sock = 0;
4963511b41d2SMark Murray 
4964511b41d2SMark Murray 	/* Try to open a socket for the local X server. */
4965511b41d2SMark Murray 	display = getenv("DISPLAY");
4966511b41d2SMark Murray 	if (!display) {
4967511b41d2SMark Murray 		error("DISPLAY not set.");
4968a04a10f8SKris Kennaway 		return -1;
4969511b41d2SMark Murray 	}
4970511b41d2SMark Murray 	/*
4971511b41d2SMark Murray 	 * Now we decode the value of the DISPLAY variable and make a
4972511b41d2SMark Murray 	 * connection to the real X server.
4973511b41d2SMark Murray 	 */
4974511b41d2SMark Murray 
4975cce7d346SDag-Erling Smørgrav #ifdef __APPLE__
4976d93a896eSDag-Erling Smørgrav 	/* Check if display is a path to a socket (as set by launchd). */
4977d93a896eSDag-Erling Smørgrav 	{
4978d93a896eSDag-Erling Smørgrav 		char path[PATH_MAX];
4979d93a896eSDag-Erling Smørgrav 
4980d93a896eSDag-Erling Smørgrav 		if (is_path_to_xsocket(display, path, sizeof(path))) {
4981d93a896eSDag-Erling Smørgrav 			debug("x11_connect_display: $DISPLAY is launchd");
4982d93a896eSDag-Erling Smørgrav 
4983d93a896eSDag-Erling Smørgrav 			/* Create a socket. */
4984d93a896eSDag-Erling Smørgrav 			sock = connect_local_xsocket_path(path);
4985cce7d346SDag-Erling Smørgrav 			if (sock < 0)
4986cce7d346SDag-Erling Smørgrav 				return -1;
4987cce7d346SDag-Erling Smørgrav 
4988cce7d346SDag-Erling Smørgrav 			/* OK, we now have a connection to the display. */
4989cce7d346SDag-Erling Smørgrav 			return sock;
4990cce7d346SDag-Erling Smørgrav 		}
4991d93a896eSDag-Erling Smørgrav 	}
4992cce7d346SDag-Erling Smørgrav #endif
4993511b41d2SMark Murray 	/*
4994511b41d2SMark Murray 	 * Check if it is a unix domain socket.  Unix domain displays are in
4995511b41d2SMark Murray 	 * one of the following formats: unix:d[.s], :d[.s], ::d[.s]
4996511b41d2SMark Murray 	 */
4997511b41d2SMark Murray 	if (strncmp(display, "unix:", 5) == 0 ||
4998511b41d2SMark Murray 	    display[0] == ':') {
4999511b41d2SMark Murray 		/* Connect to the unix domain socket. */
50004f52dfbbSDag-Erling Smørgrav 		if (sscanf(strrchr(display, ':') + 1, "%u",
50014f52dfbbSDag-Erling Smørgrav 		    &display_number) != 1) {
50024f52dfbbSDag-Erling Smørgrav 			error("Could not parse display number from DISPLAY: "
50034f52dfbbSDag-Erling Smørgrav 			    "%.100s", display);
5004a04a10f8SKris Kennaway 			return -1;
5005511b41d2SMark Murray 		}
5006511b41d2SMark Murray 		/* Create a socket. */
5007511b41d2SMark Murray 		sock = connect_local_xsocket(display_number);
5008511b41d2SMark Murray 		if (sock < 0)
5009a04a10f8SKris Kennaway 			return -1;
5010511b41d2SMark Murray 
5011511b41d2SMark Murray 		/* OK, we now have a connection to the display. */
5012a04a10f8SKris Kennaway 		return sock;
5013511b41d2SMark Murray 	}
5014511b41d2SMark Murray 	/*
5015511b41d2SMark Murray 	 * Connect to an inet socket.  The DISPLAY value is supposedly
5016511b41d2SMark Murray 	 * hostname:d[.s], where hostname may also be numeric IP address.
5017511b41d2SMark Murray 	 */
5018af12a3e7SDag-Erling Smørgrav 	strlcpy(buf, display, sizeof(buf));
5019511b41d2SMark Murray 	cp = strchr(buf, ':');
5020511b41d2SMark Murray 	if (!cp) {
5021511b41d2SMark Murray 		error("Could not find ':' in DISPLAY: %.100s", display);
5022a04a10f8SKris Kennaway 		return -1;
5023511b41d2SMark Murray 	}
5024511b41d2SMark Murray 	*cp = 0;
50254f52dfbbSDag-Erling Smørgrav 	/*
50264f52dfbbSDag-Erling Smørgrav 	 * buf now contains the host name.  But first we parse the
50274f52dfbbSDag-Erling Smørgrav 	 * display number.
50284f52dfbbSDag-Erling Smørgrav 	 */
5029333ee039SDag-Erling Smørgrav 	if (sscanf(cp + 1, "%u", &display_number) != 1) {
5030511b41d2SMark Murray 		error("Could not parse display number from DISPLAY: %.100s",
5031511b41d2SMark Murray 		    display);
5032a04a10f8SKris Kennaway 		return -1;
5033511b41d2SMark Murray 	}
5034511b41d2SMark Murray 
5035511b41d2SMark Murray 	/* Look up the host address */
5036511b41d2SMark Murray 	memset(&hints, 0, sizeof(hints));
50374f52dfbbSDag-Erling Smørgrav 	hints.ai_family = ssh->chanctxt->IPv4or6;
5038511b41d2SMark Murray 	hints.ai_socktype = SOCK_STREAM;
5039333ee039SDag-Erling Smørgrav 	snprintf(strport, sizeof strport, "%u", 6000 + display_number);
5040511b41d2SMark Murray 	if ((gaierr = getaddrinfo(buf, strport, &hints, &aitop)) != 0) {
5041d4af9e69SDag-Erling Smørgrav 		error("%.100s: unknown host. (%s)", buf,
5042d4af9e69SDag-Erling Smørgrav 		ssh_gai_strerror(gaierr));
5043a04a10f8SKris Kennaway 		return -1;
5044511b41d2SMark Murray 	}
5045511b41d2SMark Murray 	for (ai = aitop; ai; ai = ai->ai_next) {
5046511b41d2SMark Murray 		/* Create a socket. */
5047221552e4SDag-Erling Smørgrav 		sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
504819261079SEd Maste 		if (sock == -1) {
5049221552e4SDag-Erling Smørgrav 			debug2("socket: %.100s", strerror(errno));
5050511b41d2SMark Murray 			continue;
5051511b41d2SMark Murray 		}
5052511b41d2SMark Murray 		/* Connect it to the display. */
505319261079SEd Maste 		if (connect(sock, ai->ai_addr, ai->ai_addrlen) == -1) {
5054333ee039SDag-Erling Smørgrav 			debug2("connect %.100s port %u: %.100s", buf,
5055a04a10f8SKris Kennaway 			    6000 + display_number, strerror(errno));
5056511b41d2SMark Murray 			close(sock);
5057511b41d2SMark Murray 			continue;
5058511b41d2SMark Murray 		}
5059511b41d2SMark Murray 		/* Success */
5060511b41d2SMark Murray 		break;
5061a04a10f8SKris Kennaway 	}
5062511b41d2SMark Murray 	freeaddrinfo(aitop);
5063511b41d2SMark Murray 	if (!ai) {
50644f52dfbbSDag-Erling Smørgrav 		error("connect %.100s port %u: %.100s", buf,
50654f52dfbbSDag-Erling Smørgrav 		    6000 + display_number, strerror(errno));
5066a04a10f8SKris Kennaway 		return -1;
5067511b41d2SMark Murray 	}
5068af12a3e7SDag-Erling Smørgrav 	set_nodelay(sock);
5069a04a10f8SKris Kennaway 	return sock;
5070a04a10f8SKris Kennaway }
5071511b41d2SMark Murray 
5072a04a10f8SKris Kennaway /*
5073511b41d2SMark Murray  * Requests forwarding of X11 connections, generates fake authentication
5074511b41d2SMark Murray  * data, and enables authentication spoofing.
5075af12a3e7SDag-Erling Smørgrav  * This should be called in the client only.
5076511b41d2SMark Murray  */
5077511b41d2SMark Murray void
50784f52dfbbSDag-Erling Smørgrav x11_request_forwarding_with_spoofing(struct ssh *ssh, int client_session_id,
50794f52dfbbSDag-Erling Smørgrav     const char *disp, const char *proto, const char *data, int want_reply)
5080511b41d2SMark Murray {
50814f52dfbbSDag-Erling Smørgrav 	struct ssh_channels *sc = ssh->chanctxt;
5082ca3176e7SBrian Feldman 	u_int data_len = (u_int) strlen(data) / 2;
5083d4ecd108SDag-Erling Smørgrav 	u_int i, value;
5084511b41d2SMark Murray 	const char *cp;
50854f52dfbbSDag-Erling Smørgrav 	char *new_data;
50864f52dfbbSDag-Erling Smørgrav 	int r, screen_number;
5087511b41d2SMark Murray 
50884f52dfbbSDag-Erling Smørgrav 	if (sc->x11_saved_display == NULL)
50894f52dfbbSDag-Erling Smørgrav 		sc->x11_saved_display = xstrdup(disp);
50904f52dfbbSDag-Erling Smørgrav 	else if (strcmp(disp, sc->x11_saved_display) != 0) {
5091d4ecd108SDag-Erling Smørgrav 		error("x11_request_forwarding_with_spoofing: different "
5092d4ecd108SDag-Erling Smørgrav 		    "$DISPLAY already forwarded");
5093d4ecd108SDag-Erling Smørgrav 		return;
5094d4ecd108SDag-Erling Smørgrav 	}
5095d4ecd108SDag-Erling Smørgrav 
5096d4ecd108SDag-Erling Smørgrav 	cp = strchr(disp, ':');
5097511b41d2SMark Murray 	if (cp)
5098511b41d2SMark Murray 		cp = strchr(cp, '.');
5099511b41d2SMark Murray 	if (cp)
5100333ee039SDag-Erling Smørgrav 		screen_number = (u_int)strtonum(cp + 1, 0, 400, NULL);
5101511b41d2SMark Murray 	else
5102511b41d2SMark Murray 		screen_number = 0;
5103511b41d2SMark Murray 
51044f52dfbbSDag-Erling Smørgrav 	if (sc->x11_saved_proto == NULL) {
5105511b41d2SMark Murray 		/* Save protocol name. */
51064f52dfbbSDag-Erling Smørgrav 		sc->x11_saved_proto = xstrdup(proto);
5107ca86bcf2SDag-Erling Smørgrav 
5108ca86bcf2SDag-Erling Smørgrav 		/* Extract real authentication data. */
51094f52dfbbSDag-Erling Smørgrav 		sc->x11_saved_data = xmalloc(data_len);
5110511b41d2SMark Murray 		for (i = 0; i < data_len; i++) {
511119261079SEd Maste 			if (sscanf(data + 2 * i, "%2x", &value) != 1) {
5112d4ecd108SDag-Erling Smørgrav 				fatal("x11_request_forwarding: bad "
5113d4ecd108SDag-Erling Smørgrav 				    "authentication data: %.100s", data);
511419261079SEd Maste 			}
51154f52dfbbSDag-Erling Smørgrav 			sc->x11_saved_data[i] = value;
5116511b41d2SMark Murray 		}
51174f52dfbbSDag-Erling Smørgrav 		sc->x11_saved_data_len = data_len;
5118ca86bcf2SDag-Erling Smørgrav 
5119ca86bcf2SDag-Erling Smørgrav 		/* Generate fake data of the same length. */
51204f52dfbbSDag-Erling Smørgrav 		sc->x11_fake_data = xmalloc(data_len);
51214f52dfbbSDag-Erling Smørgrav 		arc4random_buf(sc->x11_fake_data, data_len);
51224f52dfbbSDag-Erling Smørgrav 		sc->x11_fake_data_len = data_len;
5123d4ecd108SDag-Erling Smørgrav 	}
5124511b41d2SMark Murray 
5125511b41d2SMark Murray 	/* Convert the fake data into hex. */
51264f52dfbbSDag-Erling Smørgrav 	new_data = tohex(sc->x11_fake_data, data_len);
5127511b41d2SMark Murray 
5128511b41d2SMark Murray 	/* Send the request packet. */
51294f52dfbbSDag-Erling Smørgrav 	channel_request_start(ssh, client_session_id, "x11-req", want_reply);
51304f52dfbbSDag-Erling Smørgrav 	if ((r = sshpkt_put_u8(ssh, 0)) != 0 || /* bool: single connection */
51314f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_cstring(ssh, proto)) != 0 ||
51324f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_cstring(ssh, new_data)) != 0 ||
51334f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_put_u32(ssh, screen_number)) != 0 ||
51344f52dfbbSDag-Erling Smørgrav 	    (r = sshpkt_send(ssh)) != 0 ||
51354f52dfbbSDag-Erling Smørgrav 	    (r = ssh_packet_write_wait(ssh)) != 0)
513619261079SEd Maste 		fatal_fr(r, "send x11-req");
5137e4a9863fSDag-Erling Smørgrav 	free(new_data);
5138511b41d2SMark Murray }
5139