channels.c revision 207319
1204917Sdes/* $OpenBSD: channels.c,v 1.303 2010/01/30 21:12:08 djm Exp $ */
257429Smarkm/*
357429Smarkm * Author: Tatu Ylonen <ylo@cs.hut.fi>
457429Smarkm * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
557429Smarkm *                    All rights reserved
657429Smarkm * This file contains functions for generic socket connection forwarding.
757429Smarkm * There is also code for initiating connection forwarding for X11 connections,
857429Smarkm * arbitrary tcp/ip connections, and the authentication agent connection.
960573Skris *
1065668Skris * As far as I am concerned, the code I have written for this software
1165668Skris * can be used freely for any purpose.  Any derived versions of this
1265668Skris * software must be clearly marked as such, and if the derived work is
1365668Skris * incompatible with the protocol description in the RFC file, it must be
1465668Skris * called by a name other than "ssh" or "Secure Shell".
1565668Skris *
1660573Skris * SSH2 support added by Markus Friedl.
1792559Sdes * Copyright (c) 1999, 2000, 2001, 2002 Markus Friedl.  All rights reserved.
1865668Skris * Copyright (c) 1999 Dug Song.  All rights reserved.
1965668Skris * Copyright (c) 1999 Theo de Raadt.  All rights reserved.
2065668Skris *
2165668Skris * Redistribution and use in source and binary forms, with or without
2265668Skris * modification, are permitted provided that the following conditions
2365668Skris * are met:
2465668Skris * 1. Redistributions of source code must retain the above copyright
2565668Skris *    notice, this list of conditions and the following disclaimer.
2665668Skris * 2. Redistributions in binary form must reproduce the above copyright
2765668Skris *    notice, this list of conditions and the following disclaimer in the
2865668Skris *    documentation and/or other materials provided with the distribution.
2965668Skris *
3065668Skris * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
3165668Skris * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
3265668Skris * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
3365668Skris * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
3465668Skris * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
3565668Skris * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
3665668Skris * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
3765668Skris * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3865668Skris * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
3965668Skris * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
4057429Smarkm */
4157429Smarkm
4257429Smarkm#include "includes.h"
4357429Smarkm
44162856Sdes#include <sys/types.h>
45162856Sdes#include <sys/ioctl.h>
46162856Sdes#include <sys/un.h>
47162856Sdes#include <sys/socket.h>
48162856Sdes#ifdef HAVE_SYS_TIME_H
49162856Sdes# include <sys/time.h>
50162856Sdes#endif
51162856Sdes
52162856Sdes#include <netinet/in.h>
53162856Sdes#include <arpa/inet.h>
54162856Sdes
55162856Sdes#include <errno.h>
56204917Sdes#include <fcntl.h>
57162856Sdes#include <netdb.h>
58162856Sdes#include <stdio.h>
59162856Sdes#include <stdlib.h>
60162856Sdes#include <string.h>
61162856Sdes#include <termios.h>
62162856Sdes#include <unistd.h>
63162856Sdes#include <stdarg.h>
64162856Sdes
65181111Sdes#include "openbsd-compat/sys-queue.h"
66162856Sdes#include "xmalloc.h"
6757429Smarkm#include "ssh.h"
6876262Sgreen#include "ssh1.h"
6976262Sgreen#include "ssh2.h"
7057429Smarkm#include "packet.h"
7176262Sgreen#include "log.h"
7276262Sgreen#include "misc.h"
73162856Sdes#include "buffer.h"
7457429Smarkm#include "channels.h"
7557429Smarkm#include "compat.h"
7676262Sgreen#include "canohost.h"
7765668Skris#include "key.h"
7865668Skris#include "authfd.h"
7992559Sdes#include "pathnames.h"
8065668Skris
8192559Sdes/* -- channel core */
8257429Smarkm
8357429Smarkm/*
8457429Smarkm * Pointer to an array containing all allocated channels.  The array is
8557429Smarkm * dynamically extended as needed.
8657429Smarkm */
8792559Sdesstatic Channel **channels = NULL;
8857429Smarkm
8957429Smarkm/*
9057429Smarkm * Size of the channel array.  All slots of the array must always be
9192559Sdes * initialized (at least the type field); unused slots set to NULL
9257429Smarkm */
93137019Sdesstatic u_int channels_alloc = 0;
9457429Smarkm
9557429Smarkm/*
9657429Smarkm * Maximum file descriptor value used in any of the channels.  This is
9792559Sdes * updated in channel_new.
9857429Smarkm */
9976262Sgreenstatic int channel_max_fd = 0;
10057429Smarkm
10157429Smarkm
10292559Sdes/* -- tcp forwarding */
10357429Smarkm
10457429Smarkm/*
10557429Smarkm * Data structure for storing which hosts are permitted for forward requests.
10657429Smarkm * The local sides of any remote forwards are stored in this array to prevent
10757429Smarkm * a corrupt remote server from accessing arbitrary TCP/IP ports on our local
10857429Smarkm * network (which might be behind a firewall).
10957429Smarkm */
11057429Smarkmtypedef struct {
11160573Skris	char *host_to_connect;		/* Connect to 'host'. */
11260573Skris	u_short port_to_connect;	/* Connect to 'port'. */
11360573Skris	u_short listen_port;		/* Remote side should listen port number. */
11457429Smarkm} ForwardPermission;
11557429Smarkm
116162856Sdes/* List of all permitted host/port pairs to connect by the user. */
11757429Smarkmstatic ForwardPermission permitted_opens[SSH_MAX_FORWARDS_PER_DIRECTION];
11892559Sdes
119162856Sdes/* List of all permitted host/port pairs to connect by the admin. */
120162856Sdesstatic ForwardPermission permitted_adm_opens[SSH_MAX_FORWARDS_PER_DIRECTION];
121162856Sdes
122162856Sdes/* Number of permitted host/port pairs in the array permitted by the user. */
12357429Smarkmstatic int num_permitted_opens = 0;
124162856Sdes
125162856Sdes/* Number of permitted host/port pair in the array permitted by the admin. */
126162856Sdesstatic int num_adm_permitted_opens = 0;
127162856Sdes
12857429Smarkm/*
12957429Smarkm * If this is true, all opens are permitted.  This is the case on the server
13057429Smarkm * on which we have to trust the client anyway, and the user could do
13157429Smarkm * anything after logging in anyway.
13257429Smarkm */
13357429Smarkmstatic int all_opens_permitted = 0;
13457429Smarkm
13557429Smarkm
13692559Sdes/* -- X11 forwarding */
13792559Sdes
13892559Sdes/* Maximum number of fake X11 displays to try. */
13992559Sdes#define MAX_DISPLAYS  1000
14092559Sdes
141149753Sdes/* Saved X11 local (client) display. */
142149753Sdesstatic char *x11_saved_display = NULL;
143149753Sdes
14492559Sdes/* Saved X11 authentication protocol name. */
14592559Sdesstatic char *x11_saved_proto = NULL;
14692559Sdes
14792559Sdes/* Saved X11 authentication data.  This is the real data. */
14892559Sdesstatic char *x11_saved_data = NULL;
14992559Sdesstatic u_int x11_saved_data_len = 0;
15092559Sdes
15192559Sdes/*
15292559Sdes * Fake X11 authentication data.  This is what the server will be sending us;
15392559Sdes * we should replace any occurrences of this by the real data.
15492559Sdes */
155162856Sdesstatic u_char *x11_fake_data = NULL;
15692559Sdesstatic u_int x11_fake_data_len;
15792559Sdes
15892559Sdes
15992559Sdes/* -- agent forwarding */
16092559Sdes
16192559Sdes#define	NUM_SOCKS	10
16292559Sdes
16376262Sgreen/* AF_UNSPEC or AF_INET or AF_INET6 */
16498941Sdesstatic int IPv4or6 = AF_UNSPEC;
16576262Sgreen
16692559Sdes/* helper */
16792559Sdesstatic void port_open_helper(Channel *c, char *rtype);
16876262Sgreen
169181111Sdes/* non-blocking connect helpers */
170181111Sdesstatic int connect_next(struct channel_connect *);
171181111Sdesstatic void channel_connect_ctx_free(struct channel_connect *);
172181111Sdes
17392559Sdes/* -- channel core */
17457429Smarkm
17560573SkrisChannel *
176157019Sdeschannel_by_id(int id)
17760573Skris{
17860573Skris	Channel *c;
17992559Sdes
180137019Sdes	if (id < 0 || (u_int)id >= channels_alloc) {
181157019Sdes		logit("channel_by_id: %d: bad id", id);
18260573Skris		return NULL;
18360573Skris	}
18492559Sdes	c = channels[id];
18592559Sdes	if (c == NULL) {
186157019Sdes		logit("channel_by_id: %d: bad id: channel free", id);
18760573Skris		return NULL;
18860573Skris	}
18960573Skris	return c;
19060573Skris}
19160573Skris
19257429Smarkm/*
193157019Sdes * Returns the channel if it is allowed to receive protocol messages.
194157019Sdes * Private channels, like listening sockets, may not receive messages.
195157019Sdes */
196157019SdesChannel *
197157019Sdeschannel_lookup(int id)
198157019Sdes{
199157019Sdes	Channel *c;
200157019Sdes
201157019Sdes	if ((c = channel_by_id(id)) == NULL)
202157019Sdes		return (NULL);
203157019Sdes
204162856Sdes	switch (c->type) {
205157019Sdes	case SSH_CHANNEL_X11_OPEN:
206157019Sdes	case SSH_CHANNEL_LARVAL:
207157019Sdes	case SSH_CHANNEL_CONNECTING:
208157019Sdes	case SSH_CHANNEL_DYNAMIC:
209157019Sdes	case SSH_CHANNEL_OPENING:
210157019Sdes	case SSH_CHANNEL_OPEN:
211157019Sdes	case SSH_CHANNEL_INPUT_DRAINING:
212157019Sdes	case SSH_CHANNEL_OUTPUT_DRAINING:
213157019Sdes		return (c);
214157019Sdes	}
215157019Sdes	logit("Non-public channel %d, type %d.", id, c->type);
216157019Sdes	return (NULL);
217157019Sdes}
218157019Sdes
219157019Sdes/*
22060573Skris * Register filedescriptors for a channel, used when allocating a channel or
22160573Skris * when the channel consumer/producer is ready, e.g. shell exec'd
22260573Skris */
22392559Sdesstatic void
22469587Sgreenchannel_register_fds(Channel *c, int rfd, int wfd, int efd,
225181111Sdes    int extusage, int nonblock, int is_tty)
22660573Skris{
22760573Skris	/* Update the maximum file descriptor value. */
22876262Sgreen	channel_max_fd = MAX(channel_max_fd, rfd);
22976262Sgreen	channel_max_fd = MAX(channel_max_fd, wfd);
23076262Sgreen	channel_max_fd = MAX(channel_max_fd, efd);
23176262Sgreen
232204917Sdes	if (rfd != -1)
233204917Sdes		fcntl(rfd, F_SETFD, FD_CLOEXEC);
234204917Sdes	if (wfd != -1 && wfd != rfd)
235204917Sdes		fcntl(wfd, F_SETFD, FD_CLOEXEC);
236204917Sdes	if (efd != -1 && efd != rfd && efd != wfd)
237204917Sdes		fcntl(efd, F_SETFD, FD_CLOEXEC);
23860573Skris
23960573Skris	c->rfd = rfd;
24060573Skris	c->wfd = wfd;
24160573Skris	c->sock = (rfd == wfd) ? rfd : -1;
24260573Skris	c->efd = efd;
24360573Skris	c->extended_usage = extusage;
24469587Sgreen
245181111Sdes	if ((c->isatty = is_tty) != 0)
246124207Sdes		debug2("channel %d: rfd %d isatty", c->self, c->rfd);
247181111Sdes	c->wfd_isatty = is_tty || isatty(c->wfd);
24874500Sgreen
24969587Sgreen	/* enable nonblocking mode */
25069587Sgreen	if (nonblock) {
25169587Sgreen		if (rfd != -1)
25269587Sgreen			set_nonblock(rfd);
25369587Sgreen		if (wfd != -1)
25469587Sgreen			set_nonblock(wfd);
25569587Sgreen		if (efd != -1)
25669587Sgreen			set_nonblock(efd);
25769587Sgreen	}
25860573Skris}
25960573Skris
26060573Skris/*
26157429Smarkm * Allocate a new channel object and set its type and socket. This will cause
26257429Smarkm * remote_name to be freed.
26357429Smarkm */
26492559SdesChannel *
26560573Skrischannel_new(char *ctype, int type, int rfd, int wfd, int efd,
26699063Sdes    u_int window, u_int maxpack, int extusage, char *remote_name, int nonblock)
26757429Smarkm{
268137019Sdes	int found;
269137019Sdes	u_int i;
27057429Smarkm	Channel *c;
27157429Smarkm
27257429Smarkm	/* Do initial allocation if this is the first call. */
27357429Smarkm	if (channels_alloc == 0) {
27457429Smarkm		channels_alloc = 10;
275162856Sdes		channels = xcalloc(channels_alloc, sizeof(Channel *));
27657429Smarkm		for (i = 0; i < channels_alloc; i++)
27792559Sdes			channels[i] = NULL;
27857429Smarkm	}
27957429Smarkm	/* Try to find a free slot where to put the new channel. */
28057429Smarkm	for (found = -1, i = 0; i < channels_alloc; i++)
28192559Sdes		if (channels[i] == NULL) {
28257429Smarkm			/* Found a free slot. */
283137019Sdes			found = (int)i;
28457429Smarkm			break;
28557429Smarkm		}
286137019Sdes	if (found < 0) {
28757429Smarkm		/* There are no free slots.  Take last+1 slot and expand the array.  */
28857429Smarkm		found = channels_alloc;
28999063Sdes		if (channels_alloc > 10000)
29099063Sdes			fatal("channel_new: internal error: channels_alloc %d "
29199063Sdes			    "too big.", channels_alloc);
292162856Sdes		channels = xrealloc(channels, channels_alloc + 10,
293162856Sdes		    sizeof(Channel *));
294120489Sjoe		channels_alloc += 10;
29569587Sgreen		debug2("channel: expanding %d", channels_alloc);
29657429Smarkm		for (i = found; i < channels_alloc; i++)
29792559Sdes			channels[i] = NULL;
29857429Smarkm	}
29992559Sdes	/* Initialize and return new channel. */
300162856Sdes	c = channels[found] = xcalloc(1, sizeof(Channel));
30157429Smarkm	buffer_init(&c->input);
30257429Smarkm	buffer_init(&c->output);
30360573Skris	buffer_init(&c->extended);
304192595Sdes	c->path = NULL;
30592559Sdes	c->ostate = CHAN_OUTPUT_OPEN;
30692559Sdes	c->istate = CHAN_INPUT_OPEN;
30792559Sdes	c->flags = 0;
308181111Sdes	channel_register_fds(c, rfd, wfd, efd, extusage, nonblock, 0);
30957429Smarkm	c->self = found;
31057429Smarkm	c->type = type;
31160573Skris	c->ctype = ctype;
31260573Skris	c->local_window = window;
31360573Skris	c->local_window_max = window;
31460573Skris	c->local_consumed = 0;
31560573Skris	c->local_maxpacket = maxpack;
31657429Smarkm	c->remote_id = -1;
317124207Sdes	c->remote_name = xstrdup(remote_name);
31860573Skris	c->remote_window = 0;
31960573Skris	c->remote_maxpacket = 0;
32092559Sdes	c->force_drain = 0;
32192559Sdes	c->single_connection = 0;
32292559Sdes	c->detach_user = NULL;
323157019Sdes	c->detach_close = 0;
324181111Sdes	c->open_confirm = NULL;
325181111Sdes	c->open_confirm_ctx = NULL;
32665668Skris	c->input_filter = NULL;
327157019Sdes	c->output_filter = NULL;
328181111Sdes	c->filter_ctx = NULL;
329181111Sdes	c->filter_cleanup = NULL;
330204917Sdes	c->ctl_chan = -1;
331204917Sdes	c->mux_rcb = NULL;
332204917Sdes	c->mux_ctx = NULL;
333204917Sdes	c->delayed = 1;		/* prevent call to channel_post handler */
334181111Sdes	TAILQ_INIT(&c->status_confirms);
33557429Smarkm	debug("channel %d: new [%s]", found, remote_name);
33692559Sdes	return c;
33757429Smarkm}
33892559Sdes
33992559Sdesstatic int
34092559Sdeschannel_find_maxfd(void)
34192559Sdes{
342137019Sdes	u_int i;
343137019Sdes	int max = 0;
34492559Sdes	Channel *c;
34592559Sdes
34692559Sdes	for (i = 0; i < channels_alloc; i++) {
34792559Sdes		c = channels[i];
34892559Sdes		if (c != NULL) {
34992559Sdes			max = MAX(max, c->rfd);
35092559Sdes			max = MAX(max, c->wfd);
35192559Sdes			max = MAX(max, c->efd);
35292559Sdes		}
35392559Sdes	}
35492559Sdes	return max;
35592559Sdes}
35692559Sdes
35760573Skrisint
35892559Sdeschannel_close_fd(int *fdp)
35960573Skris{
36092559Sdes	int ret = 0, fd = *fdp;
36192559Sdes
36292559Sdes	if (fd != -1) {
36392559Sdes		ret = close(fd);
36492559Sdes		*fdp = -1;
36592559Sdes		if (fd == channel_max_fd)
36692559Sdes			channel_max_fd = channel_find_maxfd();
36792559Sdes	}
36892559Sdes	return ret;
36960573Skris}
37057429Smarkm
37160573Skris/* Close all channel fd/socket. */
37292559Sdesstatic void
37360573Skrischannel_close_fds(Channel *c)
37457429Smarkm{
375204917Sdes	debug3("channel %d: close_fds r %d w %d e %d",
376204917Sdes	    c->self, c->rfd, c->wfd, c->efd);
37792559Sdes
37892559Sdes	channel_close_fd(&c->sock);
37992559Sdes	channel_close_fd(&c->rfd);
38092559Sdes	channel_close_fd(&c->wfd);
38192559Sdes	channel_close_fd(&c->efd);
38260573Skris}
38357429Smarkm
38460573Skris/* Free the channel and close its fd/socket. */
38560573Skrisvoid
38692559Sdeschannel_free(Channel *c)
38760573Skris{
38892559Sdes	char *s;
389137019Sdes	u_int i, n;
390181111Sdes	struct channel_confirm *cc;
39176262Sgreen
39292559Sdes	for (n = 0, i = 0; i < channels_alloc; i++)
39392559Sdes		if (channels[i])
39492559Sdes			n++;
395137019Sdes	debug("channel %d: free: %s, nchannels %u", c->self,
39692559Sdes	    c->remote_name ? c->remote_name : "???", n);
39792559Sdes
39892559Sdes	s = channel_open_message();
399124207Sdes	debug3("channel %d: status: %s", c->self, s);
40076262Sgreen	xfree(s);
40176262Sgreen
40260573Skris	if (c->sock != -1)
40360573Skris		shutdown(c->sock, SHUT_RDWR);
40460573Skris	channel_close_fds(c);
40560573Skris	buffer_free(&c->input);
40660573Skris	buffer_free(&c->output);
40760573Skris	buffer_free(&c->extended);
40860573Skris	if (c->remote_name) {
40960573Skris		xfree(c->remote_name);
41060573Skris		c->remote_name = NULL;
41160573Skris	}
412192595Sdes	if (c->path) {
413192595Sdes		xfree(c->path);
414192595Sdes		c->path = NULL;
415192595Sdes	}
416181111Sdes	while ((cc = TAILQ_FIRST(&c->status_confirms)) != NULL) {
417181111Sdes		if (cc->abandon_cb != NULL)
418181111Sdes			cc->abandon_cb(c, cc->ctx);
419181111Sdes		TAILQ_REMOVE(&c->status_confirms, cc, entry);
420181111Sdes		bzero(cc, sizeof(*cc));
421181111Sdes		xfree(cc);
422181111Sdes	}
423181111Sdes	if (c->filter_cleanup != NULL && c->filter_ctx != NULL)
424181111Sdes		c->filter_cleanup(c->self, c->filter_ctx);
42592559Sdes	channels[c->self] = NULL;
42692559Sdes	xfree(c);
42757429Smarkm}
42857429Smarkm
42992559Sdesvoid
43092559Sdeschannel_free_all(void)
43192559Sdes{
432137019Sdes	u_int i;
43392559Sdes
43492559Sdes	for (i = 0; i < channels_alloc; i++)
43592559Sdes		if (channels[i] != NULL)
43692559Sdes			channel_free(channels[i]);
43792559Sdes}
43892559Sdes
43957429Smarkm/*
44092559Sdes * Closes the sockets/fds of all channels.  This is used to close extra file
44192559Sdes * descriptors after a fork.
44292559Sdes */
44392559Sdesvoid
44492559Sdeschannel_close_all(void)
44592559Sdes{
446137019Sdes	u_int i;
44792559Sdes
44892559Sdes	for (i = 0; i < channels_alloc; i++)
44992559Sdes		if (channels[i] != NULL)
45092559Sdes			channel_close_fds(channels[i]);
45192559Sdes}
45292559Sdes
45392559Sdes/*
45492559Sdes * Stop listening to channels.
45592559Sdes */
45692559Sdesvoid
45792559Sdeschannel_stop_listening(void)
45892559Sdes{
459137019Sdes	u_int i;
46092559Sdes	Channel *c;
46192559Sdes
46292559Sdes	for (i = 0; i < channels_alloc; i++) {
46392559Sdes		c = channels[i];
46492559Sdes		if (c != NULL) {
46592559Sdes			switch (c->type) {
46692559Sdes			case SSH_CHANNEL_AUTH_SOCKET:
46792559Sdes			case SSH_CHANNEL_PORT_LISTENER:
46892559Sdes			case SSH_CHANNEL_RPORT_LISTENER:
46992559Sdes			case SSH_CHANNEL_X11_LISTENER:
47092559Sdes				channel_close_fd(&c->sock);
47192559Sdes				channel_free(c);
47292559Sdes				break;
47392559Sdes			}
47492559Sdes		}
47592559Sdes	}
47692559Sdes}
47792559Sdes
47892559Sdes/*
47992559Sdes * Returns true if no channel has too much buffered data, and false if one or
48092559Sdes * more channel is overfull.
48192559Sdes */
48292559Sdesint
48392559Sdeschannel_not_very_much_buffered_data(void)
48492559Sdes{
48592559Sdes	u_int i;
48692559Sdes	Channel *c;
48792559Sdes
48892559Sdes	for (i = 0; i < channels_alloc; i++) {
48992559Sdes		c = channels[i];
49092559Sdes		if (c != NULL && c->type == SSH_CHANNEL_OPEN) {
49192559Sdes#if 0
49292559Sdes			if (!compat20 &&
49392559Sdes			    buffer_len(&c->input) > packet_get_maxsize()) {
494113911Sdes				debug2("channel %d: big input buffer %d",
49592559Sdes				    c->self, buffer_len(&c->input));
49692559Sdes				return 0;
49792559Sdes			}
49892559Sdes#endif
49992559Sdes			if (buffer_len(&c->output) > packet_get_maxsize()) {
500124207Sdes				debug2("channel %d: big output buffer %u > %u",
50192559Sdes				    c->self, buffer_len(&c->output),
50292559Sdes				    packet_get_maxsize());
50392559Sdes				return 0;
50492559Sdes			}
50592559Sdes		}
50692559Sdes	}
50792559Sdes	return 1;
50892559Sdes}
50992559Sdes
51092559Sdes/* Returns true if any channel is still open. */
51192559Sdesint
51292559Sdeschannel_still_open(void)
51392559Sdes{
514137019Sdes	u_int i;
51592559Sdes	Channel *c;
51692559Sdes
51792559Sdes	for (i = 0; i < channels_alloc; i++) {
51892559Sdes		c = channels[i];
51992559Sdes		if (c == NULL)
52092559Sdes			continue;
52192559Sdes		switch (c->type) {
52292559Sdes		case SSH_CHANNEL_X11_LISTENER:
52392559Sdes		case SSH_CHANNEL_PORT_LISTENER:
52492559Sdes		case SSH_CHANNEL_RPORT_LISTENER:
525204917Sdes		case SSH_CHANNEL_MUX_LISTENER:
52692559Sdes		case SSH_CHANNEL_CLOSED:
52792559Sdes		case SSH_CHANNEL_AUTH_SOCKET:
52892559Sdes		case SSH_CHANNEL_DYNAMIC:
52992559Sdes		case SSH_CHANNEL_CONNECTING:
53092559Sdes		case SSH_CHANNEL_ZOMBIE:
53192559Sdes			continue;
53292559Sdes		case SSH_CHANNEL_LARVAL:
53392559Sdes			if (!compat20)
53492559Sdes				fatal("cannot happen: SSH_CHANNEL_LARVAL");
53592559Sdes			continue;
53692559Sdes		case SSH_CHANNEL_OPENING:
53792559Sdes		case SSH_CHANNEL_OPEN:
53892559Sdes		case SSH_CHANNEL_X11_OPEN:
539204917Sdes		case SSH_CHANNEL_MUX_CLIENT:
54092559Sdes			return 1;
54192559Sdes		case SSH_CHANNEL_INPUT_DRAINING:
54292559Sdes		case SSH_CHANNEL_OUTPUT_DRAINING:
54392559Sdes			if (!compat13)
54492559Sdes				fatal("cannot happen: OUT_DRAIN");
54592559Sdes			return 1;
54692559Sdes		default:
54792559Sdes			fatal("channel_still_open: bad channel type %d", c->type);
54892559Sdes			/* NOTREACHED */
54992559Sdes		}
55092559Sdes	}
55192559Sdes	return 0;
55292559Sdes}
55392559Sdes
55492559Sdes/* Returns the id of an open channel suitable for keepaliving */
55592559Sdesint
55692559Sdeschannel_find_open(void)
55792559Sdes{
558137019Sdes	u_int i;
55992559Sdes	Channel *c;
56092559Sdes
56192559Sdes	for (i = 0; i < channels_alloc; i++) {
56292559Sdes		c = channels[i];
563137019Sdes		if (c == NULL || c->remote_id < 0)
56492559Sdes			continue;
56592559Sdes		switch (c->type) {
56692559Sdes		case SSH_CHANNEL_CLOSED:
56792559Sdes		case SSH_CHANNEL_DYNAMIC:
56892559Sdes		case SSH_CHANNEL_X11_LISTENER:
56992559Sdes		case SSH_CHANNEL_PORT_LISTENER:
57092559Sdes		case SSH_CHANNEL_RPORT_LISTENER:
571204917Sdes		case SSH_CHANNEL_MUX_LISTENER:
572204917Sdes		case SSH_CHANNEL_MUX_CLIENT:
57392559Sdes		case SSH_CHANNEL_OPENING:
57492559Sdes		case SSH_CHANNEL_CONNECTING:
57592559Sdes		case SSH_CHANNEL_ZOMBIE:
57692559Sdes			continue;
57792559Sdes		case SSH_CHANNEL_LARVAL:
57892559Sdes		case SSH_CHANNEL_AUTH_SOCKET:
57992559Sdes		case SSH_CHANNEL_OPEN:
58092559Sdes		case SSH_CHANNEL_X11_OPEN:
58192559Sdes			return i;
58292559Sdes		case SSH_CHANNEL_INPUT_DRAINING:
58392559Sdes		case SSH_CHANNEL_OUTPUT_DRAINING:
58492559Sdes			if (!compat13)
58592559Sdes				fatal("cannot happen: OUT_DRAIN");
58692559Sdes			return i;
58792559Sdes		default:
58892559Sdes			fatal("channel_find_open: bad channel type %d", c->type);
58992559Sdes			/* NOTREACHED */
59092559Sdes		}
59192559Sdes	}
59292559Sdes	return -1;
59392559Sdes}
59492559Sdes
59592559Sdes
59692559Sdes/*
59792559Sdes * Returns a message describing the currently open forwarded connections,
59892559Sdes * suitable for sending to the client.  The message contains crlf pairs for
59992559Sdes * newlines.
60092559Sdes */
60192559Sdeschar *
60292559Sdeschannel_open_message(void)
60392559Sdes{
60492559Sdes	Buffer buffer;
60592559Sdes	Channel *c;
60692559Sdes	char buf[1024], *cp;
607137019Sdes	u_int i;
60892559Sdes
60992559Sdes	buffer_init(&buffer);
61092559Sdes	snprintf(buf, sizeof buf, "The following connections are open:\r\n");
61192559Sdes	buffer_append(&buffer, buf, strlen(buf));
61292559Sdes	for (i = 0; i < channels_alloc; i++) {
61392559Sdes		c = channels[i];
61492559Sdes		if (c == NULL)
61592559Sdes			continue;
61692559Sdes		switch (c->type) {
61792559Sdes		case SSH_CHANNEL_X11_LISTENER:
61892559Sdes		case SSH_CHANNEL_PORT_LISTENER:
61992559Sdes		case SSH_CHANNEL_RPORT_LISTENER:
62092559Sdes		case SSH_CHANNEL_CLOSED:
62192559Sdes		case SSH_CHANNEL_AUTH_SOCKET:
62292559Sdes		case SSH_CHANNEL_ZOMBIE:
623204917Sdes		case SSH_CHANNEL_MUX_CLIENT:
624204917Sdes		case SSH_CHANNEL_MUX_LISTENER:
62592559Sdes			continue;
62692559Sdes		case SSH_CHANNEL_LARVAL:
62792559Sdes		case SSH_CHANNEL_OPENING:
62892559Sdes		case SSH_CHANNEL_CONNECTING:
62992559Sdes		case SSH_CHANNEL_DYNAMIC:
63092559Sdes		case SSH_CHANNEL_OPEN:
63192559Sdes		case SSH_CHANNEL_X11_OPEN:
63292559Sdes		case SSH_CHANNEL_INPUT_DRAINING:
63392559Sdes		case SSH_CHANNEL_OUTPUT_DRAINING:
634137019Sdes			snprintf(buf, sizeof buf,
635204917Sdes			    "  #%d %.300s (t%d r%d i%d/%d o%d/%d fd %d/%d cc %d)\r\n",
63692559Sdes			    c->self, c->remote_name,
63792559Sdes			    c->type, c->remote_id,
63892559Sdes			    c->istate, buffer_len(&c->input),
63992559Sdes			    c->ostate, buffer_len(&c->output),
640204917Sdes			    c->rfd, c->wfd, c->ctl_chan);
64192559Sdes			buffer_append(&buffer, buf, strlen(buf));
64292559Sdes			continue;
64392559Sdes		default:
64492559Sdes			fatal("channel_open_message: bad channel type %d", c->type);
64592559Sdes			/* NOTREACHED */
64692559Sdes		}
64792559Sdes	}
64892559Sdes	buffer_append(&buffer, "\0", 1);
64992559Sdes	cp = xstrdup(buffer_ptr(&buffer));
65092559Sdes	buffer_free(&buffer);
65192559Sdes	return cp;
65292559Sdes}
65392559Sdes
65492559Sdesvoid
65592559Sdeschannel_send_open(int id)
65692559Sdes{
65792559Sdes	Channel *c = channel_lookup(id);
658106130Sdes
65992559Sdes	if (c == NULL) {
660124207Sdes		logit("channel_send_open: %d: bad id", id);
66192559Sdes		return;
66292559Sdes	}
663113911Sdes	debug2("channel %d: send open", id);
66492559Sdes	packet_start(SSH2_MSG_CHANNEL_OPEN);
66592559Sdes	packet_put_cstring(c->ctype);
66692559Sdes	packet_put_int(c->self);
66792559Sdes	packet_put_int(c->local_window);
66892559Sdes	packet_put_int(c->local_maxpacket);
66992559Sdes	packet_send();
67092559Sdes}
67192559Sdes
67292559Sdesvoid
673113911Sdeschannel_request_start(int id, char *service, int wantconfirm)
67492559Sdes{
675113911Sdes	Channel *c = channel_lookup(id);
676106130Sdes
67792559Sdes	if (c == NULL) {
678124207Sdes		logit("channel_request_start: %d: unknown channel id", id);
67992559Sdes		return;
68092559Sdes	}
681137019Sdes	debug2("channel %d: request %s confirm %d", id, service, wantconfirm);
68292559Sdes	packet_start(SSH2_MSG_CHANNEL_REQUEST);
68392559Sdes	packet_put_int(c->remote_id);
68492559Sdes	packet_put_cstring(service);
68592559Sdes	packet_put_char(wantconfirm);
68692559Sdes}
687162856Sdes
68892559Sdesvoid
689181111Sdeschannel_register_status_confirm(int id, channel_confirm_cb *cb,
690181111Sdes    channel_confirm_abandon_cb *abandon_cb, void *ctx)
69192559Sdes{
692181111Sdes	struct channel_confirm *cc;
693181111Sdes	Channel *c;
694181111Sdes
695181111Sdes	if ((c = channel_lookup(id)) == NULL)
696181111Sdes		fatal("channel_register_expect: %d: bad id", id);
697181111Sdes
698181111Sdes	cc = xmalloc(sizeof(*cc));
699181111Sdes	cc->cb = cb;
700181111Sdes	cc->abandon_cb = abandon_cb;
701181111Sdes	cc->ctx = ctx;
702181111Sdes	TAILQ_INSERT_TAIL(&c->status_confirms, cc, entry);
703181111Sdes}
704181111Sdes
705181111Sdesvoid
706181111Sdeschannel_register_open_confirm(int id, channel_callback_fn *fn, void *ctx)
707181111Sdes{
70892559Sdes	Channel *c = channel_lookup(id);
709106130Sdes
71092559Sdes	if (c == NULL) {
711192595Sdes		logit("channel_register_open_confirm: %d: bad id", id);
71292559Sdes		return;
71392559Sdes	}
714181111Sdes	c->open_confirm = fn;
715181111Sdes	c->open_confirm_ctx = ctx;
71692559Sdes}
717162856Sdes
71892559Sdesvoid
719157019Sdeschannel_register_cleanup(int id, channel_callback_fn *fn, int do_close)
72092559Sdes{
721157019Sdes	Channel *c = channel_by_id(id);
722106130Sdes
72392559Sdes	if (c == NULL) {
724124207Sdes		logit("channel_register_cleanup: %d: bad id", id);
72592559Sdes		return;
72692559Sdes	}
72792559Sdes	c->detach_user = fn;
728157019Sdes	c->detach_close = do_close;
72992559Sdes}
730162856Sdes
73192559Sdesvoid
73292559Sdeschannel_cancel_cleanup(int id)
73392559Sdes{
734157019Sdes	Channel *c = channel_by_id(id);
735106130Sdes
73692559Sdes	if (c == NULL) {
737124207Sdes		logit("channel_cancel_cleanup: %d: bad id", id);
73892559Sdes		return;
73992559Sdes	}
74092559Sdes	c->detach_user = NULL;
741157019Sdes	c->detach_close = 0;
74292559Sdes}
743162856Sdes
74492559Sdesvoid
745157019Sdeschannel_register_filter(int id, channel_infilter_fn *ifn,
746181111Sdes    channel_outfilter_fn *ofn, channel_filter_cleanup_fn *cfn, void *ctx)
74792559Sdes{
74892559Sdes	Channel *c = channel_lookup(id);
749106130Sdes
75092559Sdes	if (c == NULL) {
751124207Sdes		logit("channel_register_filter: %d: bad id", id);
75292559Sdes		return;
75392559Sdes	}
754157019Sdes	c->input_filter = ifn;
755157019Sdes	c->output_filter = ofn;
756181111Sdes	c->filter_ctx = ctx;
757181111Sdes	c->filter_cleanup = cfn;
75892559Sdes}
75992559Sdes
76092559Sdesvoid
76192559Sdeschannel_set_fds(int id, int rfd, int wfd, int efd,
762181111Sdes    int extusage, int nonblock, int is_tty, u_int window_max)
76392559Sdes{
76492559Sdes	Channel *c = channel_lookup(id);
765106130Sdes
76692559Sdes	if (c == NULL || c->type != SSH_CHANNEL_LARVAL)
76792559Sdes		fatal("channel_activate for non-larval channel %d.", id);
768181111Sdes	channel_register_fds(c, rfd, wfd, efd, extusage, nonblock, is_tty);
76992559Sdes	c->type = SSH_CHANNEL_OPEN;
77092559Sdes	c->local_window = c->local_window_max = window_max;
77192559Sdes	packet_start(SSH2_MSG_CHANNEL_WINDOW_ADJUST);
77292559Sdes	packet_put_int(c->remote_id);
77392559Sdes	packet_put_int(c->local_window);
77492559Sdes	packet_send();
77592559Sdes}
77692559Sdes
77792559Sdes/*
77860573Skris * 'channel_pre*' are called just before select() to add any bits relevant to
77960573Skris * channels in the select bitmasks.
78057429Smarkm */
78160573Skris/*
78260573Skris * 'channel_post*': perform any appropriate operations for channels which
78360573Skris * have events pending.
78460573Skris */
785162856Sdestypedef void chan_fn(Channel *c, fd_set *readset, fd_set *writeset);
78660573Skrischan_fn *channel_pre[SSH_CHANNEL_MAX_TYPE];
78760573Skrischan_fn *channel_post[SSH_CHANNEL_MAX_TYPE];
78857429Smarkm
789162856Sdes/* ARGSUSED */
79092559Sdesstatic void
791162856Sdeschannel_pre_listener(Channel *c, fd_set *readset, fd_set *writeset)
79257429Smarkm{
79360573Skris	FD_SET(c->sock, readset);
79460573Skris}
79560573Skris
796162856Sdes/* ARGSUSED */
79792559Sdesstatic void
798162856Sdeschannel_pre_connecting(Channel *c, fd_set *readset, fd_set *writeset)
79976262Sgreen{
80076262Sgreen	debug3("channel %d: waiting for connection", c->self);
80176262Sgreen	FD_SET(c->sock, writeset);
80276262Sgreen}
80376262Sgreen
80492559Sdesstatic void
805162856Sdeschannel_pre_open_13(Channel *c, fd_set *readset, fd_set *writeset)
80660573Skris{
80760573Skris	if (buffer_len(&c->input) < packet_get_maxsize())
80860573Skris		FD_SET(c->sock, readset);
80960573Skris	if (buffer_len(&c->output) > 0)
81060573Skris		FD_SET(c->sock, writeset);
81160573Skris}
81260573Skris
81392559Sdesstatic void
814162856Sdeschannel_pre_open(Channel *c, fd_set *readset, fd_set *writeset)
81560573Skris{
81692559Sdes	u_int limit = compat20 ? c->remote_window : packet_get_maxsize();
81760573Skris
81860573Skris	if (c->istate == CHAN_INPUT_OPEN &&
81992559Sdes	    limit > 0 &&
820162856Sdes	    buffer_len(&c->input) < limit &&
821162856Sdes	    buffer_check_alloc(&c->input, CHAN_RBUF))
82260573Skris		FD_SET(c->rfd, readset);
82360573Skris	if (c->ostate == CHAN_OUTPUT_OPEN ||
82460573Skris	    c->ostate == CHAN_OUTPUT_WAIT_DRAIN) {
82560573Skris		if (buffer_len(&c->output) > 0) {
82660573Skris			FD_SET(c->wfd, writeset);
82760573Skris		} else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN) {
82898684Sdes			if (CHANNEL_EFD_OUTPUT_ACTIVE(c))
829149753Sdes				debug2("channel %d: obuf_empty delayed efd %d/(%d)",
830149753Sdes				    c->self, c->efd, buffer_len(&c->extended));
83198684Sdes			else
83298684Sdes				chan_obuf_empty(c);
83360573Skris		}
83460573Skris	}
83560573Skris	/** XXX check close conditions, too */
836181111Sdes	if (compat20 && c->efd != -1 &&
837181111Sdes	    !(c->istate == CHAN_INPUT_CLOSED && c->ostate == CHAN_OUTPUT_CLOSED)) {
83860573Skris		if (c->extended_usage == CHAN_EXTENDED_WRITE &&
83960573Skris		    buffer_len(&c->extended) > 0)
84060573Skris			FD_SET(c->efd, writeset);
84198684Sdes		else if (!(c->flags & CHAN_EOF_SENT) &&
84298684Sdes		    c->extended_usage == CHAN_EXTENDED_READ &&
84360573Skris		    buffer_len(&c->extended) < c->remote_window)
84460573Skris			FD_SET(c->efd, readset);
84560573Skris	}
846137019Sdes	/* XXX: What about efd? races? */
84760573Skris}
84860573Skris
849162856Sdes/* ARGSUSED */
85092559Sdesstatic void
851162856Sdeschannel_pre_input_draining(Channel *c, fd_set *readset, fd_set *writeset)
85260573Skris{
85360573Skris	if (buffer_len(&c->input) == 0) {
85460573Skris		packet_start(SSH_MSG_CHANNEL_CLOSE);
85560573Skris		packet_put_int(c->remote_id);
85660573Skris		packet_send();
85760573Skris		c->type = SSH_CHANNEL_CLOSED;
858124207Sdes		debug2("channel %d: closing after input drain.", c->self);
85960573Skris	}
86060573Skris}
86160573Skris
862162856Sdes/* ARGSUSED */
86392559Sdesstatic void
864162856Sdeschannel_pre_output_draining(Channel *c, fd_set *readset, fd_set *writeset)
86560573Skris{
86660573Skris	if (buffer_len(&c->output) == 0)
86792559Sdes		chan_mark_dead(c);
86860573Skris	else
86960573Skris		FD_SET(c->sock, writeset);
87060573Skris}
87160573Skris
87260573Skris/*
87360573Skris * This is a special state for X11 authentication spoofing.  An opened X11
87460573Skris * connection (when authentication spoofing is being done) remains in this
87560573Skris * state until the first packet has been completely read.  The authentication
87660573Skris * data in that packet is then substituted by the real data if it matches the
87760573Skris * fake data, and the channel is put into normal mode.
87860573Skris * XXX All this happens at the client side.
87992559Sdes * Returns: 0 = need more data, -1 = wrong cookie, 1 = ok
88060573Skris */
88192559Sdesstatic int
88292559Sdesx11_open_helper(Buffer *b)
88360573Skris{
88476262Sgreen	u_char *ucp;
88576262Sgreen	u_int proto_len, data_len;
88657429Smarkm
88760573Skris	/* Check if the fixed size part of the packet is in buffer. */
88892559Sdes	if (buffer_len(b) < 12)
88960573Skris		return 0;
89057429Smarkm
89160573Skris	/* Parse the lengths of variable-length fields. */
89292559Sdes	ucp = buffer_ptr(b);
89360573Skris	if (ucp[0] == 0x42) {	/* Byte order MSB first. */
89460573Skris		proto_len = 256 * ucp[6] + ucp[7];
89560573Skris		data_len = 256 * ucp[8] + ucp[9];
89660573Skris	} else if (ucp[0] == 0x6c) {	/* Byte order LSB first. */
89760573Skris		proto_len = ucp[6] + 256 * ucp[7];
89860573Skris		data_len = ucp[8] + 256 * ucp[9];
89960573Skris	} else {
900124207Sdes		debug2("Initial X11 packet contains bad byte order byte: 0x%x",
90192559Sdes		    ucp[0]);
90260573Skris		return -1;
90360573Skris	}
90457429Smarkm
90560573Skris	/* Check if the whole packet is in buffer. */
90692559Sdes	if (buffer_len(b) <
90760573Skris	    12 + ((proto_len + 3) & ~3) + ((data_len + 3) & ~3))
90860573Skris		return 0;
90957429Smarkm
91060573Skris	/* Check if authentication protocol matches. */
91160573Skris	if (proto_len != strlen(x11_saved_proto) ||
91260573Skris	    memcmp(ucp + 12, x11_saved_proto, proto_len) != 0) {
913124207Sdes		debug2("X11 connection uses different authentication protocol.");
91460573Skris		return -1;
91560573Skris	}
91660573Skris	/* Check if authentication data matches our fake data. */
91760573Skris	if (data_len != x11_fake_data_len ||
91860573Skris	    memcmp(ucp + 12 + ((proto_len + 3) & ~3),
91960573Skris		x11_fake_data, x11_fake_data_len) != 0) {
920124207Sdes		debug2("X11 auth data does not match fake data.");
92160573Skris		return -1;
92260573Skris	}
92360573Skris	/* Check fake data length */
92460573Skris	if (x11_fake_data_len != x11_saved_data_len) {
92560573Skris		error("X11 fake_data_len %d != saved_data_len %d",
92660573Skris		    x11_fake_data_len, x11_saved_data_len);
92760573Skris		return -1;
92860573Skris	}
92960573Skris	/*
93060573Skris	 * Received authentication protocol and data match
93160573Skris	 * our fake data. Substitute the fake data with real
93260573Skris	 * data.
93360573Skris	 */
93460573Skris	memcpy(ucp + 12 + ((proto_len + 3) & ~3),
93560573Skris	    x11_saved_data, x11_saved_data_len);
93660573Skris	return 1;
93760573Skris}
93857429Smarkm
93992559Sdesstatic void
940162856Sdeschannel_pre_x11_open_13(Channel *c, fd_set *readset, fd_set *writeset)
94160573Skris{
94292559Sdes	int ret = x11_open_helper(&c->output);
943106130Sdes
94460573Skris	if (ret == 1) {
94560573Skris		/* Start normal processing for the channel. */
94660573Skris		c->type = SSH_CHANNEL_OPEN;
94760573Skris		channel_pre_open_13(c, readset, writeset);
94860573Skris	} else if (ret == -1) {
94960573Skris		/*
95060573Skris		 * We have received an X11 connection that has bad
95160573Skris		 * authentication information.
95260573Skris		 */
953124207Sdes		logit("X11 connection rejected because of wrong authentication.");
95460573Skris		buffer_clear(&c->input);
95560573Skris		buffer_clear(&c->output);
95692559Sdes		channel_close_fd(&c->sock);
95760573Skris		c->sock = -1;
95860573Skris		c->type = SSH_CHANNEL_CLOSED;
95960573Skris		packet_start(SSH_MSG_CHANNEL_CLOSE);
96060573Skris		packet_put_int(c->remote_id);
96160573Skris		packet_send();
96260573Skris	}
96360573Skris}
96457429Smarkm
96592559Sdesstatic void
966162856Sdeschannel_pre_x11_open(Channel *c, fd_set *readset, fd_set *writeset)
96760573Skris{
96892559Sdes	int ret = x11_open_helper(&c->output);
96992559Sdes
97092559Sdes	/* c->force_drain = 1; */
97192559Sdes
97260573Skris	if (ret == 1) {
97360573Skris		c->type = SSH_CHANNEL_OPEN;
97492559Sdes		channel_pre_open(c, readset, writeset);
97592559Sdes	} else if (ret == -1) {
976124207Sdes		logit("X11 connection rejected because of wrong authentication.");
977124207Sdes		debug2("X11 rejected %d i%d/o%d", c->self, c->istate, c->ostate);
97892559Sdes		chan_read_failed(c);
97992559Sdes		buffer_clear(&c->input);
98092559Sdes		chan_ibuf_empty(c);
98192559Sdes		buffer_clear(&c->output);
98292559Sdes		/* for proto v1, the peer will send an IEOF */
98360573Skris		if (compat20)
98492559Sdes			chan_write_failed(c);
98560573Skris		else
98692559Sdes			c->type = SSH_CHANNEL_OPEN;
987124207Sdes		debug2("X11 closed %d i%d/o%d", c->self, c->istate, c->ostate);
98860573Skris	}
98960573Skris}
99057429Smarkm
991204917Sdesstatic void
992204917Sdeschannel_pre_mux_client(Channel *c, fd_set *readset, fd_set *writeset)
993204917Sdes{
994204917Sdes	if (c->istate == CHAN_INPUT_OPEN &&
995204917Sdes	    buffer_check_alloc(&c->input, CHAN_RBUF))
996204917Sdes		FD_SET(c->rfd, readset);
997204917Sdes	if (c->istate == CHAN_INPUT_WAIT_DRAIN) {
998204917Sdes		/* clear buffer immediately (discard any partial packet) */
999204917Sdes		buffer_clear(&c->input);
1000204917Sdes		chan_ibuf_empty(c);
1001204917Sdes		/* Start output drain. XXX just kill chan? */
1002204917Sdes		chan_rcvd_oclose(c);
1003204917Sdes	}
1004204917Sdes	if (c->ostate == CHAN_OUTPUT_OPEN ||
1005204917Sdes	    c->ostate == CHAN_OUTPUT_WAIT_DRAIN) {
1006204917Sdes		if (buffer_len(&c->output) > 0)
1007204917Sdes			FD_SET(c->wfd, writeset);
1008204917Sdes		else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN)
1009204917Sdes			chan_obuf_empty(c);
1010204917Sdes	}
1011204917Sdes}
1012204917Sdes
101376262Sgreen/* try to decode a socks4 header */
1014162856Sdes/* ARGSUSED */
101592559Sdesstatic int
1016162856Sdeschannel_decode_socks4(Channel *c, fd_set *readset, fd_set *writeset)
101776262Sgreen{
1018106130Sdes	char *p, *host;
1019192595Sdes	u_int len, have, i, found, need;
102092559Sdes	char username[256];
102176262Sgreen	struct {
102276262Sgreen		u_int8_t version;
102376262Sgreen		u_int8_t command;
102476262Sgreen		u_int16_t dest_port;
102576262Sgreen		struct in_addr dest_addr;
102676262Sgreen	} s4_req, s4_rsp;
102776262Sgreen
102876262Sgreen	debug2("channel %d: decode socks4", c->self);
102976262Sgreen
103076262Sgreen	have = buffer_len(&c->input);
103176262Sgreen	len = sizeof(s4_req);
103276262Sgreen	if (have < len)
103376262Sgreen		return 0;
103476262Sgreen	p = buffer_ptr(&c->input);
1035192595Sdes
1036192595Sdes	need = 1;
1037192595Sdes	/* SOCKS4A uses an invalid IP address 0.0.0.x */
1038192595Sdes	if (p[4] == 0 && p[5] == 0 && p[6] == 0 && p[7] != 0) {
1039192595Sdes		debug2("channel %d: socks4a request", c->self);
1040192595Sdes		/* ... and needs an extra string (the hostname) */
1041192595Sdes		need = 2;
1042192595Sdes	}
1043192595Sdes	/* Check for terminating NUL on the string(s) */
104476262Sgreen	for (found = 0, i = len; i < have; i++) {
104576262Sgreen		if (p[i] == '\0') {
1046192595Sdes			found++;
1047192595Sdes			if (found == need)
1048192595Sdes				break;
104976262Sgreen		}
105076262Sgreen		if (i > 1024) {
105176262Sgreen			/* the peer is probably sending garbage */
105276262Sgreen			debug("channel %d: decode socks4: too long",
105376262Sgreen			    c->self);
105476262Sgreen			return -1;
105576262Sgreen		}
105676262Sgreen	}
1057192595Sdes	if (found < need)
105876262Sgreen		return 0;
105976262Sgreen	buffer_get(&c->input, (char *)&s4_req.version, 1);
106076262Sgreen	buffer_get(&c->input, (char *)&s4_req.command, 1);
106176262Sgreen	buffer_get(&c->input, (char *)&s4_req.dest_port, 2);
106276262Sgreen	buffer_get(&c->input, (char *)&s4_req.dest_addr, 4);
106376262Sgreen	have = buffer_len(&c->input);
106476262Sgreen	p = buffer_ptr(&c->input);
106576262Sgreen	len = strlen(p);
106676262Sgreen	debug2("channel %d: decode socks4: user %s/%d", c->self, p, len);
1067192595Sdes	len++;					/* trailing '\0' */
106876262Sgreen	if (len > have)
106976262Sgreen		fatal("channel %d: decode socks4: len %d > have %d",
107076262Sgreen		    c->self, len, have);
107176262Sgreen	strlcpy(username, p, sizeof(username));
107276262Sgreen	buffer_consume(&c->input, len);
107376262Sgreen
1074192595Sdes	if (c->path != NULL) {
1075192595Sdes		xfree(c->path);
1076192595Sdes		c->path = NULL;
1077192595Sdes	}
1078192595Sdes	if (need == 1) {			/* SOCKS4: one string */
1079192595Sdes		host = inet_ntoa(s4_req.dest_addr);
1080192595Sdes		c->path = xstrdup(host);
1081192595Sdes	} else {				/* SOCKS4A: two strings */
1082192595Sdes		have = buffer_len(&c->input);
1083192595Sdes		p = buffer_ptr(&c->input);
1084192595Sdes		len = strlen(p);
1085192595Sdes		debug2("channel %d: decode socks4a: host %s/%d",
1086192595Sdes		    c->self, p, len);
1087192595Sdes		len++;				/* trailing '\0' */
1088192595Sdes		if (len > have)
1089192595Sdes			fatal("channel %d: decode socks4a: len %d > have %d",
1090192595Sdes			    c->self, len, have);
1091192595Sdes		if (len > NI_MAXHOST) {
1092192595Sdes			error("channel %d: hostname \"%.100s\" too long",
1093192595Sdes			    c->self, p);
1094192595Sdes			return -1;
1095192595Sdes		}
1096192595Sdes		c->path = xstrdup(p);
1097192595Sdes		buffer_consume(&c->input, len);
1098192595Sdes	}
109976262Sgreen	c->host_port = ntohs(s4_req.dest_port);
110092559Sdes
1101124207Sdes	debug2("channel %d: dynamic request: socks4 host %s port %u command %u",
1102192595Sdes	    c->self, c->path, c->host_port, s4_req.command);
110376262Sgreen
110476262Sgreen	if (s4_req.command != 1) {
1105192595Sdes		debug("channel %d: cannot handle: %s cn %d",
1106192595Sdes		    c->self, need == 1 ? "SOCKS4" : "SOCKS4A", s4_req.command);
110776262Sgreen		return -1;
110876262Sgreen	}
110976262Sgreen	s4_rsp.version = 0;			/* vn: 0 for reply */
111076262Sgreen	s4_rsp.command = 90;			/* cd: req granted */
111176262Sgreen	s4_rsp.dest_port = 0;			/* ignored */
111276262Sgreen	s4_rsp.dest_addr.s_addr = INADDR_ANY;	/* ignored */
1113162856Sdes	buffer_append(&c->output, &s4_rsp, sizeof(s4_rsp));
111476262Sgreen	return 1;
111576262Sgreen}
111676262Sgreen
1117124207Sdes/* try to decode a socks5 header */
1118124207Sdes#define SSH_SOCKS5_AUTHDONE	0x1000
1119124207Sdes#define SSH_SOCKS5_NOAUTH	0x00
1120124207Sdes#define SSH_SOCKS5_IPV4		0x01
1121124207Sdes#define SSH_SOCKS5_DOMAIN	0x03
1122124207Sdes#define SSH_SOCKS5_IPV6		0x04
1123124207Sdes#define SSH_SOCKS5_CONNECT	0x01
1124124207Sdes#define SSH_SOCKS5_SUCCESS	0x00
1125124207Sdes
1126162856Sdes/* ARGSUSED */
1127124207Sdesstatic int
1128162856Sdeschannel_decode_socks5(Channel *c, fd_set *readset, fd_set *writeset)
1129124207Sdes{
1130124207Sdes	struct {
1131124207Sdes		u_int8_t version;
1132124207Sdes		u_int8_t command;
1133124207Sdes		u_int8_t reserved;
1134124207Sdes		u_int8_t atyp;
1135124207Sdes	} s5_req, s5_rsp;
1136124207Sdes	u_int16_t dest_port;
1137192595Sdes	u_char *p, dest_addr[255+1], ntop[INET6_ADDRSTRLEN];
1138162856Sdes	u_int have, need, i, found, nmethods, addrlen, af;
1139124207Sdes
1140124207Sdes	debug2("channel %d: decode socks5", c->self);
1141124207Sdes	p = buffer_ptr(&c->input);
1142124207Sdes	if (p[0] != 0x05)
1143124207Sdes		return -1;
1144124207Sdes	have = buffer_len(&c->input);
1145124207Sdes	if (!(c->flags & SSH_SOCKS5_AUTHDONE)) {
1146124207Sdes		/* format: ver | nmethods | methods */
1147126273Sdes		if (have < 2)
1148124207Sdes			return 0;
1149124207Sdes		nmethods = p[1];
1150124207Sdes		if (have < nmethods + 2)
1151124207Sdes			return 0;
1152124207Sdes		/* look for method: "NO AUTHENTICATION REQUIRED" */
1153181111Sdes		for (found = 0, i = 2; i < nmethods + 2; i++) {
1154162856Sdes			if (p[i] == SSH_SOCKS5_NOAUTH) {
1155124207Sdes				found = 1;
1156124207Sdes				break;
1157124207Sdes			}
1158124207Sdes		}
1159124207Sdes		if (!found) {
1160124207Sdes			debug("channel %d: method SSH_SOCKS5_NOAUTH not found",
1161124207Sdes			    c->self);
1162124207Sdes			return -1;
1163124207Sdes		}
1164124207Sdes		buffer_consume(&c->input, nmethods + 2);
1165124207Sdes		buffer_put_char(&c->output, 0x05);		/* version */
1166124207Sdes		buffer_put_char(&c->output, SSH_SOCKS5_NOAUTH);	/* method */
1167124207Sdes		FD_SET(c->sock, writeset);
1168124207Sdes		c->flags |= SSH_SOCKS5_AUTHDONE;
1169124207Sdes		debug2("channel %d: socks5 auth done", c->self);
1170124207Sdes		return 0;				/* need more */
1171124207Sdes	}
1172124207Sdes	debug2("channel %d: socks5 post auth", c->self);
1173124207Sdes	if (have < sizeof(s5_req)+1)
1174124207Sdes		return 0;			/* need more */
1175162856Sdes	memcpy(&s5_req, p, sizeof(s5_req));
1176124207Sdes	if (s5_req.version != 0x05 ||
1177124207Sdes	    s5_req.command != SSH_SOCKS5_CONNECT ||
1178124207Sdes	    s5_req.reserved != 0x00) {
1179124207Sdes		debug2("channel %d: only socks5 connect supported", c->self);
1180124207Sdes		return -1;
1181124207Sdes	}
1182147005Sdes	switch (s5_req.atyp){
1183124207Sdes	case SSH_SOCKS5_IPV4:
1184124207Sdes		addrlen = 4;
1185124207Sdes		af = AF_INET;
1186124207Sdes		break;
1187124207Sdes	case SSH_SOCKS5_DOMAIN:
1188124207Sdes		addrlen = p[sizeof(s5_req)];
1189124207Sdes		af = -1;
1190124207Sdes		break;
1191124207Sdes	case SSH_SOCKS5_IPV6:
1192124207Sdes		addrlen = 16;
1193124207Sdes		af = AF_INET6;
1194124207Sdes		break;
1195124207Sdes	default:
1196124207Sdes		debug2("channel %d: bad socks5 atyp %d", c->self, s5_req.atyp);
1197124207Sdes		return -1;
1198124207Sdes	}
1199162856Sdes	need = sizeof(s5_req) + addrlen + 2;
1200162856Sdes	if (s5_req.atyp == SSH_SOCKS5_DOMAIN)
1201162856Sdes		need++;
1202162856Sdes	if (have < need)
1203124207Sdes		return 0;
1204124207Sdes	buffer_consume(&c->input, sizeof(s5_req));
1205124207Sdes	if (s5_req.atyp == SSH_SOCKS5_DOMAIN)
1206124207Sdes		buffer_consume(&c->input, 1);    /* host string length */
1207124207Sdes	buffer_get(&c->input, (char *)&dest_addr, addrlen);
1208124207Sdes	buffer_get(&c->input, (char *)&dest_port, 2);
1209124207Sdes	dest_addr[addrlen] = '\0';
1210192595Sdes	if (c->path != NULL) {
1211192595Sdes		xfree(c->path);
1212192595Sdes		c->path = NULL;
1213192595Sdes	}
1214192595Sdes	if (s5_req.atyp == SSH_SOCKS5_DOMAIN) {
1215192595Sdes		if (addrlen >= NI_MAXHOST) {
1216192595Sdes			error("channel %d: dynamic request: socks5 hostname "
1217192595Sdes			    "\"%.100s\" too long", c->self, dest_addr);
1218192595Sdes			return -1;
1219192595Sdes		}
1220192595Sdes		c->path = xstrdup(dest_addr);
1221192595Sdes	} else {
1222192595Sdes		if (inet_ntop(af, dest_addr, ntop, sizeof(ntop)) == NULL)
1223192595Sdes			return -1;
1224192595Sdes		c->path = xstrdup(ntop);
1225192595Sdes	}
1226124207Sdes	c->host_port = ntohs(dest_port);
1227126273Sdes
1228124207Sdes	debug2("channel %d: dynamic request: socks5 host %s port %u command %u",
1229124207Sdes	    c->self, c->path, c->host_port, s5_req.command);
1230124207Sdes
1231124207Sdes	s5_rsp.version = 0x05;
1232124207Sdes	s5_rsp.command = SSH_SOCKS5_SUCCESS;
1233124207Sdes	s5_rsp.reserved = 0;			/* ignored */
1234124207Sdes	s5_rsp.atyp = SSH_SOCKS5_IPV4;
1235124207Sdes	((struct in_addr *)&dest_addr)->s_addr = INADDR_ANY;
1236124207Sdes	dest_port = 0;				/* ignored */
1237124207Sdes
1238162856Sdes	buffer_append(&c->output, &s5_rsp, sizeof(s5_rsp));
1239162856Sdes	buffer_append(&c->output, &dest_addr, sizeof(struct in_addr));
1240162856Sdes	buffer_append(&c->output, &dest_port, sizeof(dest_port));
1241124207Sdes	return 1;
1242124207Sdes}
1243124207Sdes
1244204917SdesChannel *
1245204917Sdeschannel_connect_stdio_fwd(const char *host_to_connect, u_short port_to_connect,
1246204917Sdes    int in, int out)
1247204917Sdes{
1248204917Sdes	Channel *c;
1249204917Sdes
1250204917Sdes	debug("channel_connect_stdio_fwd %s:%d", host_to_connect,
1251204917Sdes	    port_to_connect);
1252204917Sdes
1253204917Sdes	c = channel_new("stdio-forward", SSH_CHANNEL_OPENING, in, out,
1254204917Sdes	    -1, CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT,
1255204917Sdes	    0, "stdio-forward", /*nonblock*/0);
1256204917Sdes
1257204917Sdes	c->path = xstrdup(host_to_connect);
1258204917Sdes	c->host_port = port_to_connect;
1259204917Sdes	c->listening_port = 0;
1260204917Sdes	c->force_drain = 1;
1261204917Sdes
1262204917Sdes	channel_register_fds(c, in, out, -1, 0, 1, 0);
1263204917Sdes	port_open_helper(c, "direct-tcpip");
1264204917Sdes
1265204917Sdes	return c;
1266204917Sdes}
1267204917Sdes
126876262Sgreen/* dynamic port forwarding */
126992559Sdesstatic void
1270162856Sdeschannel_pre_dynamic(Channel *c, fd_set *readset, fd_set *writeset)
127176262Sgreen{
127276262Sgreen	u_char *p;
1273149753Sdes	u_int have;
1274149753Sdes	int ret;
127576262Sgreen
127676262Sgreen	have = buffer_len(&c->input);
127776262Sgreen	debug2("channel %d: pre_dynamic: have %d", c->self, have);
127876262Sgreen	/* buffer_dump(&c->input); */
127976262Sgreen	/* check if the fixed size part of the packet is in buffer. */
1280124207Sdes	if (have < 3) {
128176262Sgreen		/* need more */
128276262Sgreen		FD_SET(c->sock, readset);
128376262Sgreen		return;
128476262Sgreen	}
128576262Sgreen	/* try to guess the protocol */
128676262Sgreen	p = buffer_ptr(&c->input);
128776262Sgreen	switch (p[0]) {
128876262Sgreen	case 0x04:
128976262Sgreen		ret = channel_decode_socks4(c, readset, writeset);
129076262Sgreen		break;
1291124207Sdes	case 0x05:
1292124207Sdes		ret = channel_decode_socks5(c, readset, writeset);
1293124207Sdes		break;
129476262Sgreen	default:
129576262Sgreen		ret = -1;
129676262Sgreen		break;
129776262Sgreen	}
129876262Sgreen	if (ret < 0) {
129992559Sdes		chan_mark_dead(c);
130076262Sgreen	} else if (ret == 0) {
130176262Sgreen		debug2("channel %d: pre_dynamic: need more", c->self);
130276262Sgreen		/* need more */
130376262Sgreen		FD_SET(c->sock, readset);
130476262Sgreen	} else {
130576262Sgreen		/* switch to the next state */
130676262Sgreen		c->type = SSH_CHANNEL_OPENING;
130776262Sgreen		port_open_helper(c, "direct-tcpip");
130876262Sgreen	}
130976262Sgreen}
131076262Sgreen
131160573Skris/* This is our fake X11 server socket. */
1312162856Sdes/* ARGSUSED */
131392559Sdesstatic void
1314162856Sdeschannel_post_x11_listener(Channel *c, fd_set *readset, fd_set *writeset)
131560573Skris{
131692559Sdes	Channel *nc;
1317181111Sdes	struct sockaddr_storage addr;
131892559Sdes	int newsock;
131960573Skris	socklen_t addrlen;
132076262Sgreen	char buf[16384], *remote_ipaddr;
132160573Skris	int remote_port;
132257429Smarkm
132360573Skris	if (FD_ISSET(c->sock, readset)) {
132460573Skris		debug("X11 connection requested.");
132560573Skris		addrlen = sizeof(addr);
1326181111Sdes		newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen);
132792559Sdes		if (c->single_connection) {
1328124207Sdes			debug2("single_connection: closing X11 listener.");
132992559Sdes			channel_close_fd(&c->sock);
133092559Sdes			chan_mark_dead(c);
133192559Sdes		}
133260573Skris		if (newsock < 0) {
133360573Skris			error("accept: %.100s", strerror(errno));
133460573Skris			return;
133560573Skris		}
133692559Sdes		set_nodelay(newsock);
133776262Sgreen		remote_ipaddr = get_peer_ipaddr(newsock);
133860573Skris		remote_port = get_peer_port(newsock);
133960573Skris		snprintf(buf, sizeof buf, "X11 connection from %.200s port %d",
134076262Sgreen		    remote_ipaddr, remote_port);
134157429Smarkm
134292559Sdes		nc = channel_new("accepted x11 socket",
134360573Skris		    SSH_CHANNEL_OPENING, newsock, newsock, -1,
1344124207Sdes		    c->local_window_max, c->local_maxpacket, 0, buf, 1);
134560573Skris		if (compat20) {
134660573Skris			packet_start(SSH2_MSG_CHANNEL_OPEN);
134760573Skris			packet_put_cstring("x11");
134892559Sdes			packet_put_int(nc->self);
134992559Sdes			packet_put_int(nc->local_window_max);
135092559Sdes			packet_put_int(nc->local_maxpacket);
135176262Sgreen			/* originator ipaddr and port */
135276262Sgreen			packet_put_cstring(remote_ipaddr);
135360573Skris			if (datafellows & SSH_BUG_X11FWD) {
1354124207Sdes				debug2("ssh2 x11 bug compat mode");
135557429Smarkm			} else {
135660573Skris				packet_put_int(remote_port);
135757429Smarkm			}
135860573Skris			packet_send();
135960573Skris		} else {
136060573Skris			packet_start(SSH_SMSG_X11_OPEN);
136192559Sdes			packet_put_int(nc->self);
136292559Sdes			if (packet_get_protocol_flags() &
136392559Sdes			    SSH_PROTOFLAG_HOST_IN_FWD_OPEN)
136492559Sdes				packet_put_cstring(buf);
136560573Skris			packet_send();
136657429Smarkm		}
136776262Sgreen		xfree(remote_ipaddr);
136857429Smarkm	}
136957429Smarkm}
137057429Smarkm
137192559Sdesstatic void
137276262Sgreenport_open_helper(Channel *c, char *rtype)
137376262Sgreen{
137476262Sgreen	int direct;
137576262Sgreen	char buf[1024];
137676262Sgreen	char *remote_ipaddr = get_peer_ipaddr(c->sock);
1377149753Sdes	int remote_port = get_peer_port(c->sock);
137876262Sgreen
1379204917Sdes	if (remote_port == -1) {
1380204917Sdes		/* Fake addr/port to appease peers that validate it (Tectia) */
1381204917Sdes		xfree(remote_ipaddr);
1382204917Sdes		remote_ipaddr = xstrdup("127.0.0.1");
1383204917Sdes		remote_port = 65535;
1384204917Sdes	}
1385204917Sdes
138676262Sgreen	direct = (strcmp(rtype, "direct-tcpip") == 0);
138776262Sgreen
138876262Sgreen	snprintf(buf, sizeof buf,
138976262Sgreen	    "%s: listening port %d for %.100s port %d, "
139076262Sgreen	    "connect from %.200s port %d",
139176262Sgreen	    rtype, c->listening_port, c->path, c->host_port,
139276262Sgreen	    remote_ipaddr, remote_port);
139376262Sgreen
139476262Sgreen	xfree(c->remote_name);
139576262Sgreen	c->remote_name = xstrdup(buf);
139676262Sgreen
139776262Sgreen	if (compat20) {
139876262Sgreen		packet_start(SSH2_MSG_CHANNEL_OPEN);
139976262Sgreen		packet_put_cstring(rtype);
140076262Sgreen		packet_put_int(c->self);
140176262Sgreen		packet_put_int(c->local_window_max);
140276262Sgreen		packet_put_int(c->local_maxpacket);
140376262Sgreen		if (direct) {
140476262Sgreen			/* target host, port */
140576262Sgreen			packet_put_cstring(c->path);
140676262Sgreen			packet_put_int(c->host_port);
140776262Sgreen		} else {
140876262Sgreen			/* listen address, port */
140976262Sgreen			packet_put_cstring(c->path);
141076262Sgreen			packet_put_int(c->listening_port);
141176262Sgreen		}
141276262Sgreen		/* originator host and port */
141376262Sgreen		packet_put_cstring(remote_ipaddr);
1414149753Sdes		packet_put_int((u_int)remote_port);
141576262Sgreen		packet_send();
141676262Sgreen	} else {
141776262Sgreen		packet_start(SSH_MSG_PORT_OPEN);
141876262Sgreen		packet_put_int(c->self);
141976262Sgreen		packet_put_cstring(c->path);
142076262Sgreen		packet_put_int(c->host_port);
142192559Sdes		if (packet_get_protocol_flags() &
142292559Sdes		    SSH_PROTOFLAG_HOST_IN_FWD_OPEN)
142376262Sgreen			packet_put_cstring(c->remote_name);
142476262Sgreen		packet_send();
142576262Sgreen	}
142676262Sgreen	xfree(remote_ipaddr);
142776262Sgreen}
142876262Sgreen
1429157019Sdesstatic void
1430157019Sdeschannel_set_reuseaddr(int fd)
1431157019Sdes{
1432157019Sdes	int on = 1;
1433157019Sdes
1434157019Sdes	/*
1435157019Sdes	 * Set socket options.
1436157019Sdes	 * Allow local port reuse in TIME_WAIT.
1437157019Sdes	 */
1438157019Sdes	if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1)
1439157019Sdes		error("setsockopt SO_REUSEADDR fd %d: %s", fd, strerror(errno));
1440157019Sdes}
1441157019Sdes
144257429Smarkm/*
144360573Skris * This socket is listening for connections to a forwarded TCP/IP port.
144457429Smarkm */
1445162856Sdes/* ARGSUSED */
144692559Sdesstatic void
1447162856Sdeschannel_post_port_listener(Channel *c, fd_set *readset, fd_set *writeset)
144857429Smarkm{
144976262Sgreen	Channel *nc;
1450181111Sdes	struct sockaddr_storage addr;
145192559Sdes	int newsock, nextstate;
145257429Smarkm	socklen_t addrlen;
145376262Sgreen	char *rtype;
145457429Smarkm
145560573Skris	if (FD_ISSET(c->sock, readset)) {
145660573Skris		debug("Connection to port %d forwarding "
145760573Skris		    "to %.100s port %d requested.",
145860573Skris		    c->listening_port, c->path, c->host_port);
145976262Sgreen
146092559Sdes		if (c->type == SSH_CHANNEL_RPORT_LISTENER) {
146192559Sdes			nextstate = SSH_CHANNEL_OPENING;
146292559Sdes			rtype = "forwarded-tcpip";
146392559Sdes		} else {
146492559Sdes			if (c->host_port == 0) {
146592559Sdes				nextstate = SSH_CHANNEL_DYNAMIC;
146692559Sdes				rtype = "dynamic-tcpip";
146792559Sdes			} else {
146892559Sdes				nextstate = SSH_CHANNEL_OPENING;
146992559Sdes				rtype = "direct-tcpip";
147092559Sdes			}
147192559Sdes		}
147276262Sgreen
147360573Skris		addrlen = sizeof(addr);
1474181111Sdes		newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen);
147560573Skris		if (newsock < 0) {
147660573Skris			error("accept: %.100s", strerror(errno));
147760573Skris			return;
147860573Skris		}
147992559Sdes		set_nodelay(newsock);
1480124207Sdes		nc = channel_new(rtype, nextstate, newsock, newsock, -1,
1481124207Sdes		    c->local_window_max, c->local_maxpacket, 0, rtype, 1);
148276262Sgreen		nc->listening_port = c->listening_port;
148376262Sgreen		nc->host_port = c->host_port;
1484192595Sdes		if (c->path != NULL)
1485192595Sdes			nc->path = xstrdup(c->path);
148676262Sgreen
1487204917Sdes		if (nextstate != SSH_CHANNEL_DYNAMIC)
148876262Sgreen			port_open_helper(nc, rtype);
148960573Skris	}
149060573Skris}
149157429Smarkm
149260573Skris/*
149360573Skris * This is the authentication agent socket listening for connections from
149460573Skris * clients.
149560573Skris */
1496162856Sdes/* ARGSUSED */
149792559Sdesstatic void
1498162856Sdeschannel_post_auth_listener(Channel *c, fd_set *readset, fd_set *writeset)
149960573Skris{
150092559Sdes	Channel *nc;
150192559Sdes	int newsock;
1502181111Sdes	struct sockaddr_storage addr;
150360573Skris	socklen_t addrlen;
150457429Smarkm
150560573Skris	if (FD_ISSET(c->sock, readset)) {
150660573Skris		addrlen = sizeof(addr);
1507181111Sdes		newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen);
150860573Skris		if (newsock < 0) {
150960573Skris			error("accept from auth socket: %.100s", strerror(errno));
151060573Skris			return;
151160573Skris		}
151292559Sdes		nc = channel_new("accepted auth socket",
151376262Sgreen		    SSH_CHANNEL_OPENING, newsock, newsock, -1,
151476262Sgreen		    c->local_window_max, c->local_maxpacket,
1515124207Sdes		    0, "accepted auth socket", 1);
151676262Sgreen		if (compat20) {
151776262Sgreen			packet_start(SSH2_MSG_CHANNEL_OPEN);
151876262Sgreen			packet_put_cstring("auth-agent@openssh.com");
151992559Sdes			packet_put_int(nc->self);
152076262Sgreen			packet_put_int(c->local_window_max);
152176262Sgreen			packet_put_int(c->local_maxpacket);
152276262Sgreen		} else {
152376262Sgreen			packet_start(SSH_SMSG_AGENT_OPEN);
152492559Sdes			packet_put_int(nc->self);
152576262Sgreen		}
152660573Skris		packet_send();
152760573Skris	}
152860573Skris}
152957429Smarkm
1530162856Sdes/* ARGSUSED */
153192559Sdesstatic void
1532162856Sdeschannel_post_connecting(Channel *c, fd_set *readset, fd_set *writeset)
153376262Sgreen{
1534181111Sdes	int err = 0, sock;
153592559Sdes	socklen_t sz = sizeof(err);
153692559Sdes
153776262Sgreen	if (FD_ISSET(c->sock, writeset)) {
153892559Sdes		if (getsockopt(c->sock, SOL_SOCKET, SO_ERROR, &err, &sz) < 0) {
153992559Sdes			err = errno;
154092559Sdes			error("getsockopt SO_ERROR failed");
154192559Sdes		}
154292559Sdes		if (err == 0) {
1543181111Sdes			debug("channel %d: connected to %s port %d",
1544181111Sdes			    c->self, c->connect_ctx.host, c->connect_ctx.port);
1545181111Sdes			channel_connect_ctx_free(&c->connect_ctx);
154692559Sdes			c->type = SSH_CHANNEL_OPEN;
154792559Sdes			if (compat20) {
154892559Sdes				packet_start(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION);
154992559Sdes				packet_put_int(c->remote_id);
155092559Sdes				packet_put_int(c->self);
155192559Sdes				packet_put_int(c->local_window);
155292559Sdes				packet_put_int(c->local_maxpacket);
155392559Sdes			} else {
155492559Sdes				packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION);
155592559Sdes				packet_put_int(c->remote_id);
155692559Sdes				packet_put_int(c->self);
155792559Sdes			}
155876262Sgreen		} else {
1559181111Sdes			debug("channel %d: connection failed: %s",
156092559Sdes			    c->self, strerror(err));
1561181111Sdes			/* Try next address, if any */
1562181111Sdes			if ((sock = connect_next(&c->connect_ctx)) > 0) {
1563181111Sdes				close(c->sock);
1564181111Sdes				c->sock = c->rfd = c->wfd = sock;
1565181111Sdes				channel_max_fd = channel_find_maxfd();
1566181111Sdes				return;
1567181111Sdes			}
1568181111Sdes			/* Exhausted all addresses */
1569181111Sdes			error("connect_to %.100s port %d: failed.",
1570181111Sdes			    c->connect_ctx.host, c->connect_ctx.port);
1571181111Sdes			channel_connect_ctx_free(&c->connect_ctx);
157292559Sdes			if (compat20) {
157392559Sdes				packet_start(SSH2_MSG_CHANNEL_OPEN_FAILURE);
157492559Sdes				packet_put_int(c->remote_id);
157592559Sdes				packet_put_int(SSH2_OPEN_CONNECT_FAILED);
157692559Sdes				if (!(datafellows & SSH_BUG_OPENFAILURE)) {
157792559Sdes					packet_put_cstring(strerror(err));
157892559Sdes					packet_put_cstring("");
157992559Sdes				}
158076262Sgreen			} else {
158192559Sdes				packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE);
158292559Sdes				packet_put_int(c->remote_id);
158376262Sgreen			}
158492559Sdes			chan_mark_dead(c);
158576262Sgreen		}
158692559Sdes		packet_send();
158776262Sgreen	}
158876262Sgreen}
158976262Sgreen
1590162856Sdes/* ARGSUSED */
159192559Sdesstatic int
1592162856Sdeschannel_handle_rfd(Channel *c, fd_set *readset, fd_set *writeset)
159360573Skris{
1594147005Sdes	char buf[CHAN_RBUF];
1595181111Sdes	int len, force;
159657429Smarkm
1597181111Sdes	force = c->isatty && c->detach_close && c->istate != CHAN_INPUT_CLOSED;
1598181111Sdes	if (c->rfd != -1 && (force || FD_ISSET(c->rfd, readset))) {
1599162856Sdes		errno = 0;
160060573Skris		len = read(c->rfd, buf, sizeof(buf));
1601181111Sdes		if (len < 0 && (errno == EINTR ||
1602181111Sdes		    ((errno == EAGAIN || errno == EWOULDBLOCK) && !force)))
160360573Skris			return 1;
1604162856Sdes#ifndef PTY_ZEROREAD
160578827Sgreen		if (len <= 0) {
1606162856Sdes#else
1607162856Sdes		if ((!c->isatty && len <= 0) ||
1608162856Sdes		    (c->isatty && (len < 0 || (len == 0 && errno != 0)))) {
1609162856Sdes#endif
1610124207Sdes			debug2("channel %d: read<=0 rfd %d len %d",
161160573Skris			    c->self, c->rfd, len);
161276262Sgreen			if (c->type != SSH_CHANNEL_OPEN) {
1613124207Sdes				debug2("channel %d: not open", c->self);
161492559Sdes				chan_mark_dead(c);
161576262Sgreen				return -1;
161676262Sgreen			} else if (compat13) {
161792559Sdes				buffer_clear(&c->output);
161860573Skris				c->type = SSH_CHANNEL_INPUT_DRAINING;
1619124207Sdes				debug2("channel %d: input draining.", c->self);
162060573Skris			} else {
162160573Skris				chan_read_failed(c);
162257429Smarkm			}
162360573Skris			return -1;
162460573Skris		}
162592559Sdes		if (c->input_filter != NULL) {
162665668Skris			if (c->input_filter(c, buf, len) == -1) {
1627124207Sdes				debug2("channel %d: filter stops", c->self);
162865668Skris				chan_read_failed(c);
162965668Skris			}
1630157019Sdes		} else if (c->datagram) {
1631157019Sdes			buffer_put_string(&c->input, buf, len);
163265668Skris		} else {
163365668Skris			buffer_append(&c->input, buf, len);
163465668Skris		}
163560573Skris	}
163660573Skris	return 1;
163760573Skris}
1638162856Sdes
1639162856Sdes/* ARGSUSED */
164092559Sdesstatic int
1641162856Sdeschannel_handle_wfd(Channel *c, fd_set *readset, fd_set *writeset)
164260573Skris{
164376262Sgreen	struct termios tio;
1644157019Sdes	u_char *data = NULL, *buf;
164592559Sdes	u_int dlen;
164660573Skris	int len;
164760573Skris
164860573Skris	/* Send buffered output data to the socket. */
164960573Skris	if (c->wfd != -1 &&
165060573Skris	    FD_ISSET(c->wfd, writeset) &&
165160573Skris	    buffer_len(&c->output) > 0) {
1652157019Sdes		if (c->output_filter != NULL) {
1653157019Sdes			if ((buf = c->output_filter(c, &data, &dlen)) == NULL) {
1654157019Sdes				debug2("channel %d: filter stops", c->self);
1655157019Sdes				if (c->type != SSH_CHANNEL_OPEN)
1656157019Sdes					chan_mark_dead(c);
1657157019Sdes				else
1658157019Sdes					chan_write_failed(c);
1659157019Sdes				return -1;
1660157019Sdes			}
1661157019Sdes		} else if (c->datagram) {
1662157019Sdes			buf = data = buffer_get_string(&c->output, &dlen);
1663157019Sdes		} else {
1664157019Sdes			buf = data = buffer_ptr(&c->output);
1665157019Sdes			dlen = buffer_len(&c->output);
1666157019Sdes		}
1667157019Sdes
1668157019Sdes		if (c->datagram) {
1669157019Sdes			/* ignore truncated writes, datagrams might get lost */
1670157019Sdes			c->local_consumed += dlen + 4;
1671157019Sdes			len = write(c->wfd, buf, dlen);
1672157019Sdes			xfree(data);
1673181111Sdes			if (len < 0 && (errno == EINTR || errno == EAGAIN ||
1674181111Sdes			    errno == EWOULDBLOCK))
1675157019Sdes				return 1;
1676157019Sdes			if (len <= 0) {
1677157019Sdes				if (c->type != SSH_CHANNEL_OPEN)
1678157019Sdes					chan_mark_dead(c);
1679157019Sdes				else
1680157019Sdes					chan_write_failed(c);
1681157019Sdes				return -1;
1682157019Sdes			}
1683157019Sdes			return 1;
1684157019Sdes		}
1685106130Sdes#ifdef _AIX
1686126273Sdes		/* XXX: Later AIX versions can't push as much data to tty */
1687126273Sdes		if (compat20 && c->wfd_isatty)
1688126273Sdes			dlen = MIN(dlen, 8*1024);
1689106130Sdes#endif
1690157019Sdes
1691157019Sdes		len = write(c->wfd, buf, dlen);
1692181111Sdes		if (len < 0 &&
1693181111Sdes		    (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK))
169460573Skris			return 1;
169560573Skris		if (len <= 0) {
169676262Sgreen			if (c->type != SSH_CHANNEL_OPEN) {
1697124207Sdes				debug2("channel %d: not open", c->self);
169892559Sdes				chan_mark_dead(c);
169976262Sgreen				return -1;
170076262Sgreen			} else if (compat13) {
170192559Sdes				buffer_clear(&c->output);
1702124207Sdes				debug2("channel %d: input draining.", c->self);
170360573Skris				c->type = SSH_CHANNEL_INPUT_DRAINING;
170460573Skris			} else {
170560573Skris				chan_write_failed(c);
170657429Smarkm			}
170760573Skris			return -1;
170860573Skris		}
1709197679Sdes#ifndef BROKEN_TCGETATTR_ICANON
1710157019Sdes		if (compat20 && c->isatty && dlen >= 1 && buf[0] != '\r') {
171174500Sgreen			if (tcgetattr(c->wfd, &tio) == 0 &&
171274500Sgreen			    !(tio.c_lflag & ECHO) && (tio.c_lflag & ICANON)) {
171374500Sgreen				/*
171474500Sgreen				 * Simulate echo to reduce the impact of
171576262Sgreen				 * traffic analysis. We need to match the
171676262Sgreen				 * size of a SSH2_MSG_CHANNEL_DATA message
1717157019Sdes				 * (4 byte channel id + buf)
171874500Sgreen				 */
171976262Sgreen				packet_send_ignore(4 + len);
172074500Sgreen				packet_send();
172174500Sgreen			}
172274500Sgreen		}
1723197679Sdes#endif
172460573Skris		buffer_consume(&c->output, len);
172560573Skris		if (compat20 && len > 0) {
172660573Skris			c->local_consumed += len;
172760573Skris		}
172860573Skris	}
172960573Skris	return 1;
173060573Skris}
1731162856Sdes
173292559Sdesstatic int
1733162856Sdeschannel_handle_efd(Channel *c, fd_set *readset, fd_set *writeset)
173460573Skris{
1735147005Sdes	char buf[CHAN_RBUF];
173660573Skris	int len;
173757429Smarkm
173860573Skris/** XXX handle drain efd, too */
173960573Skris	if (c->efd != -1) {
174060573Skris		if (c->extended_usage == CHAN_EXTENDED_WRITE &&
174160573Skris		    FD_ISSET(c->efd, writeset) &&
174260573Skris		    buffer_len(&c->extended) > 0) {
174360573Skris			len = write(c->efd, buffer_ptr(&c->extended),
174460573Skris			    buffer_len(&c->extended));
174569587Sgreen			debug2("channel %d: written %d to efd %d",
174660573Skris			    c->self, len, c->efd);
1747181111Sdes			if (len < 0 && (errno == EINTR || errno == EAGAIN ||
1748181111Sdes			    errno == EWOULDBLOCK))
174976262Sgreen				return 1;
175076262Sgreen			if (len <= 0) {
175176262Sgreen				debug2("channel %d: closing write-efd %d",
175276262Sgreen				    c->self, c->efd);
175392559Sdes				channel_close_fd(&c->efd);
175476262Sgreen			} else {
175560573Skris				buffer_consume(&c->extended, len);
175660573Skris				c->local_consumed += len;
175757429Smarkm			}
175860573Skris		} else if (c->extended_usage == CHAN_EXTENDED_READ &&
1759181111Sdes		    (c->detach_close || FD_ISSET(c->efd, readset))) {
176060573Skris			len = read(c->efd, buf, sizeof(buf));
176169587Sgreen			debug2("channel %d: read %d from efd %d",
176292559Sdes			    c->self, len, c->efd);
1763181111Sdes			if (len < 0 && (errno == EINTR || ((errno == EAGAIN ||
1764181111Sdes			    errno == EWOULDBLOCK) && !c->detach_close)))
176576262Sgreen				return 1;
176676262Sgreen			if (len <= 0) {
176776262Sgreen				debug2("channel %d: closing read-efd %d",
176860573Skris				    c->self, c->efd);
176992559Sdes				channel_close_fd(&c->efd);
177076262Sgreen			} else {
177160573Skris				buffer_append(&c->extended, buf, len);
177276262Sgreen			}
177360573Skris		}
177460573Skris	}
177560573Skris	return 1;
177660573Skris}
1777162856Sdes
177892559Sdesstatic int
177976262Sgreenchannel_check_window(Channel *c)
178060573Skris{
178176262Sgreen	if (c->type == SSH_CHANNEL_OPEN &&
178276262Sgreen	    !(c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD)) &&
1783181111Sdes	    ((c->local_window_max - c->local_window >
1784181111Sdes	    c->local_maxpacket*3) ||
1785181111Sdes	    c->local_window < c->local_window_max/2) &&
178660573Skris	    c->local_consumed > 0) {
178760573Skris		packet_start(SSH2_MSG_CHANNEL_WINDOW_ADJUST);
178860573Skris		packet_put_int(c->remote_id);
178960573Skris		packet_put_int(c->local_consumed);
179060573Skris		packet_send();
179169587Sgreen		debug2("channel %d: window %d sent adjust %d",
179260573Skris		    c->self, c->local_window,
179360573Skris		    c->local_consumed);
179460573Skris		c->local_window += c->local_consumed;
179560573Skris		c->local_consumed = 0;
179660573Skris	}
179760573Skris	return 1;
179860573Skris}
179957429Smarkm
180092559Sdesstatic void
1801162856Sdeschannel_post_open(Channel *c, fd_set *readset, fd_set *writeset)
180260573Skris{
180360573Skris	channel_handle_rfd(c, readset, writeset);
180460573Skris	channel_handle_wfd(c, readset, writeset);
180592559Sdes	if (!compat20)
180692559Sdes		return;
180760573Skris	channel_handle_efd(c, readset, writeset);
180876262Sgreen	channel_check_window(c);
180960573Skris}
181060573Skris
1811204917Sdesstatic u_int
1812204917Sdesread_mux(Channel *c, u_int need)
1813204917Sdes{
1814204917Sdes	char buf[CHAN_RBUF];
1815204917Sdes	int len;
1816204917Sdes	u_int rlen;
1817204917Sdes
1818204917Sdes	if (buffer_len(&c->input) < need) {
1819204917Sdes		rlen = need - buffer_len(&c->input);
1820204917Sdes		len = read(c->rfd, buf, MIN(rlen, CHAN_RBUF));
1821204917Sdes		if (len <= 0) {
1822204917Sdes			if (errno != EINTR && errno != EAGAIN) {
1823204917Sdes				debug2("channel %d: ctl read<=0 rfd %d len %d",
1824204917Sdes				    c->self, c->rfd, len);
1825204917Sdes				chan_read_failed(c);
1826204917Sdes				return 0;
1827204917Sdes			}
1828204917Sdes		} else
1829204917Sdes			buffer_append(&c->input, buf, len);
1830204917Sdes	}
1831204917Sdes	return buffer_len(&c->input);
1832204917Sdes}
1833204917Sdes
1834204917Sdesstatic void
1835204917Sdeschannel_post_mux_client(Channel *c, fd_set *readset, fd_set *writeset)
1836204917Sdes{
1837204917Sdes	u_int need;
1838204917Sdes	ssize_t len;
1839204917Sdes
1840204917Sdes	if (!compat20)
1841204917Sdes		fatal("%s: entered with !compat20", __func__);
1842204917Sdes
1843204917Sdes	if (c->rfd != -1 && FD_ISSET(c->rfd, readset) &&
1844204917Sdes	    (c->istate == CHAN_INPUT_OPEN ||
1845204917Sdes	    c->istate == CHAN_INPUT_WAIT_DRAIN)) {
1846204917Sdes		/*
1847204917Sdes		 * Don't not read past the precise end of packets to
1848204917Sdes		 * avoid disrupting fd passing.
1849204917Sdes		 */
1850204917Sdes		if (read_mux(c, 4) < 4) /* read header */
1851204917Sdes			return;
1852204917Sdes		need = get_u32(buffer_ptr(&c->input));
1853204917Sdes#define CHANNEL_MUX_MAX_PACKET	(256 * 1024)
1854204917Sdes		if (need > CHANNEL_MUX_MAX_PACKET) {
1855204917Sdes			debug2("channel %d: packet too big %u > %u",
1856204917Sdes			    c->self, CHANNEL_MUX_MAX_PACKET, need);
1857204917Sdes			chan_rcvd_oclose(c);
1858204917Sdes			return;
1859204917Sdes		}
1860204917Sdes		if (read_mux(c, need + 4) < need + 4) /* read body */
1861204917Sdes			return;
1862204917Sdes		if (c->mux_rcb(c) != 0) {
1863204917Sdes			debug("channel %d: mux_rcb failed", c->self);
1864204917Sdes			chan_mark_dead(c);
1865204917Sdes			return;
1866204917Sdes		}
1867204917Sdes	}
1868204917Sdes
1869204917Sdes	if (c->wfd != -1 && FD_ISSET(c->wfd, writeset) &&
1870204917Sdes	    buffer_len(&c->output) > 0) {
1871204917Sdes		len = write(c->wfd, buffer_ptr(&c->output),
1872204917Sdes		    buffer_len(&c->output));
1873204917Sdes		if (len < 0 && (errno == EINTR || errno == EAGAIN))
1874204917Sdes			return;
1875204917Sdes		if (len <= 0) {
1876204917Sdes			chan_mark_dead(c);
1877204917Sdes			return;
1878204917Sdes		}
1879204917Sdes		buffer_consume(&c->output, len);
1880204917Sdes	}
1881204917Sdes}
1882204917Sdes
1883204917Sdesstatic void
1884204917Sdeschannel_post_mux_listener(Channel *c, fd_set *readset, fd_set *writeset)
1885204917Sdes{
1886204917Sdes	Channel *nc;
1887204917Sdes	struct sockaddr_storage addr;
1888204917Sdes	socklen_t addrlen;
1889204917Sdes	int newsock;
1890204917Sdes	uid_t euid;
1891204917Sdes	gid_t egid;
1892204917Sdes
1893204917Sdes	if (!FD_ISSET(c->sock, readset))
1894204917Sdes		return;
1895204917Sdes
1896204917Sdes	debug("multiplexing control connection");
1897204917Sdes
1898204917Sdes	/*
1899204917Sdes	 * Accept connection on control socket
1900204917Sdes	 */
1901204917Sdes	memset(&addr, 0, sizeof(addr));
1902204917Sdes	addrlen = sizeof(addr);
1903204917Sdes	if ((newsock = accept(c->sock, (struct sockaddr*)&addr,
1904204917Sdes	    &addrlen)) == -1) {
1905204917Sdes		error("%s accept: %s", __func__, strerror(errno));
1906204917Sdes		return;
1907204917Sdes	}
1908204917Sdes
1909204917Sdes	if (getpeereid(newsock, &euid, &egid) < 0) {
1910204917Sdes		error("%s getpeereid failed: %s", __func__,
1911204917Sdes		    strerror(errno));
1912204917Sdes		close(newsock);
1913204917Sdes		return;
1914204917Sdes	}
1915204917Sdes	if ((euid != 0) && (getuid() != euid)) {
1916204917Sdes		error("multiplex uid mismatch: peer euid %u != uid %u",
1917204917Sdes		    (u_int)euid, (u_int)getuid());
1918204917Sdes		close(newsock);
1919204917Sdes		return;
1920204917Sdes	}
1921204917Sdes	nc = channel_new("multiplex client", SSH_CHANNEL_MUX_CLIENT,
1922204917Sdes	    newsock, newsock, -1, c->local_window_max,
1923204917Sdes	    c->local_maxpacket, 0, "mux-control", 1);
1924204917Sdes	nc->mux_rcb = c->mux_rcb;
1925204917Sdes	debug3("%s: new mux channel %d fd %d", __func__,
1926204917Sdes	    nc->self, nc->sock);
1927204917Sdes	/* establish state */
1928204917Sdes	nc->mux_rcb(nc);
1929204917Sdes	/* mux state transitions must not elicit protocol messages */
1930204917Sdes	nc->flags |= CHAN_LOCAL;
1931204917Sdes}
1932204917Sdes
1933162856Sdes/* ARGSUSED */
193492559Sdesstatic void
1935162856Sdeschannel_post_output_drain_13(Channel *c, fd_set *readset, fd_set *writeset)
193660573Skris{
193760573Skris	int len;
1938106130Sdes
193960573Skris	/* Send buffered output data to the socket. */
194060573Skris	if (FD_ISSET(c->sock, writeset) && buffer_len(&c->output) > 0) {
194160573Skris		len = write(c->sock, buffer_ptr(&c->output),
194260573Skris			    buffer_len(&c->output));
194360573Skris		if (len <= 0)
194492559Sdes			buffer_clear(&c->output);
194560573Skris		else
194660573Skris			buffer_consume(&c->output, len);
194760573Skris	}
194860573Skris}
194960573Skris
195092559Sdesstatic void
195160573Skrischannel_handler_init_20(void)
195260573Skris{
195392559Sdes	channel_pre[SSH_CHANNEL_OPEN] =			&channel_pre_open;
195460573Skris	channel_pre[SSH_CHANNEL_X11_OPEN] =		&channel_pre_x11_open;
195560573Skris	channel_pre[SSH_CHANNEL_PORT_LISTENER] =	&channel_pre_listener;
195676262Sgreen	channel_pre[SSH_CHANNEL_RPORT_LISTENER] =	&channel_pre_listener;
195760573Skris	channel_pre[SSH_CHANNEL_X11_LISTENER] =		&channel_pre_listener;
195876262Sgreen	channel_pre[SSH_CHANNEL_AUTH_SOCKET] =		&channel_pre_listener;
195976262Sgreen	channel_pre[SSH_CHANNEL_CONNECTING] =		&channel_pre_connecting;
196076262Sgreen	channel_pre[SSH_CHANNEL_DYNAMIC] =		&channel_pre_dynamic;
1961204917Sdes	channel_pre[SSH_CHANNEL_MUX_LISTENER] =		&channel_pre_listener;
1962204917Sdes	channel_pre[SSH_CHANNEL_MUX_CLIENT] =		&channel_pre_mux_client;
196360573Skris
196492559Sdes	channel_post[SSH_CHANNEL_OPEN] =		&channel_post_open;
196560573Skris	channel_post[SSH_CHANNEL_PORT_LISTENER] =	&channel_post_port_listener;
196676262Sgreen	channel_post[SSH_CHANNEL_RPORT_LISTENER] =	&channel_post_port_listener;
196760573Skris	channel_post[SSH_CHANNEL_X11_LISTENER] =	&channel_post_x11_listener;
196876262Sgreen	channel_post[SSH_CHANNEL_AUTH_SOCKET] =		&channel_post_auth_listener;
196976262Sgreen	channel_post[SSH_CHANNEL_CONNECTING] =		&channel_post_connecting;
197092559Sdes	channel_post[SSH_CHANNEL_DYNAMIC] =		&channel_post_open;
1971204917Sdes	channel_post[SSH_CHANNEL_MUX_LISTENER] =	&channel_post_mux_listener;
1972204917Sdes	channel_post[SSH_CHANNEL_MUX_CLIENT] =		&channel_post_mux_client;
197360573Skris}
197460573Skris
197592559Sdesstatic void
197660573Skrischannel_handler_init_13(void)
197760573Skris{
197860573Skris	channel_pre[SSH_CHANNEL_OPEN] =			&channel_pre_open_13;
197960573Skris	channel_pre[SSH_CHANNEL_X11_OPEN] =		&channel_pre_x11_open_13;
198060573Skris	channel_pre[SSH_CHANNEL_X11_LISTENER] =		&channel_pre_listener;
198160573Skris	channel_pre[SSH_CHANNEL_PORT_LISTENER] =	&channel_pre_listener;
198260573Skris	channel_pre[SSH_CHANNEL_AUTH_SOCKET] =		&channel_pre_listener;
198360573Skris	channel_pre[SSH_CHANNEL_INPUT_DRAINING] =	&channel_pre_input_draining;
198460573Skris	channel_pre[SSH_CHANNEL_OUTPUT_DRAINING] =	&channel_pre_output_draining;
198576262Sgreen	channel_pre[SSH_CHANNEL_CONNECTING] =		&channel_pre_connecting;
198676262Sgreen	channel_pre[SSH_CHANNEL_DYNAMIC] =		&channel_pre_dynamic;
198760573Skris
198892559Sdes	channel_post[SSH_CHANNEL_OPEN] =		&channel_post_open;
198960573Skris	channel_post[SSH_CHANNEL_X11_LISTENER] =	&channel_post_x11_listener;
199060573Skris	channel_post[SSH_CHANNEL_PORT_LISTENER] =	&channel_post_port_listener;
199160573Skris	channel_post[SSH_CHANNEL_AUTH_SOCKET] =		&channel_post_auth_listener;
199260573Skris	channel_post[SSH_CHANNEL_OUTPUT_DRAINING] =	&channel_post_output_drain_13;
199376262Sgreen	channel_post[SSH_CHANNEL_CONNECTING] =		&channel_post_connecting;
199492559Sdes	channel_post[SSH_CHANNEL_DYNAMIC] =		&channel_post_open;
199560573Skris}
199660573Skris
199792559Sdesstatic void
199860573Skrischannel_handler_init_15(void)
199960573Skris{
200092559Sdes	channel_pre[SSH_CHANNEL_OPEN] =			&channel_pre_open;
200160573Skris	channel_pre[SSH_CHANNEL_X11_OPEN] =		&channel_pre_x11_open;
200260573Skris	channel_pre[SSH_CHANNEL_X11_LISTENER] =		&channel_pre_listener;
200360573Skris	channel_pre[SSH_CHANNEL_PORT_LISTENER] =	&channel_pre_listener;
200460573Skris	channel_pre[SSH_CHANNEL_AUTH_SOCKET] =		&channel_pre_listener;
200576262Sgreen	channel_pre[SSH_CHANNEL_CONNECTING] =		&channel_pre_connecting;
200676262Sgreen	channel_pre[SSH_CHANNEL_DYNAMIC] =		&channel_pre_dynamic;
200760573Skris
200860573Skris	channel_post[SSH_CHANNEL_X11_LISTENER] =	&channel_post_x11_listener;
200960573Skris	channel_post[SSH_CHANNEL_PORT_LISTENER] =	&channel_post_port_listener;
201060573Skris	channel_post[SSH_CHANNEL_AUTH_SOCKET] =		&channel_post_auth_listener;
201192559Sdes	channel_post[SSH_CHANNEL_OPEN] =		&channel_post_open;
201276262Sgreen	channel_post[SSH_CHANNEL_CONNECTING] =		&channel_post_connecting;
201392559Sdes	channel_post[SSH_CHANNEL_DYNAMIC] =		&channel_post_open;
201460573Skris}
201560573Skris
201692559Sdesstatic void
201760573Skrischannel_handler_init(void)
201860573Skris{
201960573Skris	int i;
2020106130Sdes
202192559Sdes	for (i = 0; i < SSH_CHANNEL_MAX_TYPE; i++) {
202260573Skris		channel_pre[i] = NULL;
202360573Skris		channel_post[i] = NULL;
202460573Skris	}
202560573Skris	if (compat20)
202660573Skris		channel_handler_init_20();
202760573Skris	else if (compat13)
202860573Skris		channel_handler_init_13();
202960573Skris	else
203060573Skris		channel_handler_init_15();
203160573Skris}
203260573Skris
203392559Sdes/* gc dead channels */
203492559Sdesstatic void
203592559Sdeschannel_garbage_collect(Channel *c)
203692559Sdes{
203792559Sdes	if (c == NULL)
203892559Sdes		return;
203992559Sdes	if (c->detach_user != NULL) {
2040157019Sdes		if (!chan_is_dead(c, c->detach_close))
204192559Sdes			return;
2042124207Sdes		debug2("channel %d: gc: notify user", c->self);
204392559Sdes		c->detach_user(c->self, NULL);
204492559Sdes		/* if we still have a callback */
204592559Sdes		if (c->detach_user != NULL)
204692559Sdes			return;
2047124207Sdes		debug2("channel %d: gc: user detached", c->self);
204892559Sdes	}
204992559Sdes	if (!chan_is_dead(c, 1))
205092559Sdes		return;
2051124207Sdes	debug2("channel %d: garbage collecting", c->self);
205292559Sdes	channel_free(c);
205392559Sdes}
205492559Sdes
205592559Sdesstatic void
2056162856Sdeschannel_handler(chan_fn *ftab[], fd_set *readset, fd_set *writeset)
205760573Skris{
205860573Skris	static int did_init = 0;
2059204917Sdes	u_int i, oalloc;
206060573Skris	Channel *c;
206160573Skris
206260573Skris	if (!did_init) {
206360573Skris		channel_handler_init();
206460573Skris		did_init = 1;
206560573Skris	}
2066204917Sdes	for (i = 0, oalloc = channels_alloc; i < oalloc; i++) {
206792559Sdes		c = channels[i];
206892559Sdes		if (c == NULL)
206957429Smarkm			continue;
2070204917Sdes		if (c->delayed) {
2071204917Sdes			if (ftab == channel_pre)
2072204917Sdes				c->delayed = 0;
2073204917Sdes			else
2074204917Sdes				continue;
2075204917Sdes		}
207692559Sdes		if (ftab[c->type] != NULL)
207792559Sdes			(*ftab[c->type])(c, readset, writeset);
207892559Sdes		channel_garbage_collect(c);
207957429Smarkm	}
208057429Smarkm}
208157429Smarkm
208292559Sdes/*
208392559Sdes * Allocate/update select bitmasks and add any bits relevant to channels in
208492559Sdes * select bitmasks.
208592559Sdes */
208660573Skrisvoid
208776262Sgreenchannel_prepare_select(fd_set **readsetp, fd_set **writesetp, int *maxfdp,
2088137019Sdes    u_int *nallocp, int rekeying)
208960573Skris{
2090162856Sdes	u_int n, sz, nfdset;
209176262Sgreen
209276262Sgreen	n = MAX(*maxfdp, channel_max_fd);
209376262Sgreen
2094162856Sdes	nfdset = howmany(n+1, NFDBITS);
2095162856Sdes	/* Explicitly test here, because xrealloc isn't always called */
2096162856Sdes	if (nfdset && SIZE_T_MAX / nfdset < sizeof(fd_mask))
2097162856Sdes		fatal("channel_prepare_select: max_fd (%d) is too large", n);
2098162856Sdes	sz = nfdset * sizeof(fd_mask);
2099162856Sdes
210092559Sdes	/* perhaps check sz < nalloc/2 and shrink? */
210192559Sdes	if (*readsetp == NULL || sz > *nallocp) {
2102162856Sdes		*readsetp = xrealloc(*readsetp, nfdset, sizeof(fd_mask));
2103162856Sdes		*writesetp = xrealloc(*writesetp, nfdset, sizeof(fd_mask));
210492559Sdes		*nallocp = sz;
210576262Sgreen	}
210692559Sdes	*maxfdp = n;
210776262Sgreen	memset(*readsetp, 0, sz);
210876262Sgreen	memset(*writesetp, 0, sz);
210976262Sgreen
211076262Sgreen	if (!rekeying)
211176262Sgreen		channel_handler(channel_pre, *readsetp, *writesetp);
211260573Skris}
211360573Skris
211492559Sdes/*
211592559Sdes * After select, perform any appropriate operations for channels which have
211692559Sdes * events pending.
211792559Sdes */
211860573Skrisvoid
2119162856Sdeschannel_after_select(fd_set *readset, fd_set *writeset)
212060573Skris{
212160573Skris	channel_handler(channel_post, readset, writeset);
212260573Skris}
212360573Skris
212492559Sdes
212576262Sgreen/* If there is data to send to the connection, enqueue some of it now. */
212660573Skrisvoid
212792559Sdeschannel_output_poll(void)
212857429Smarkm{
212960573Skris	Channel *c;
2130137019Sdes	u_int i, len;
213157429Smarkm
213257429Smarkm	for (i = 0; i < channels_alloc; i++) {
213392559Sdes		c = channels[i];
213492559Sdes		if (c == NULL)
213592559Sdes			continue;
213657429Smarkm
213792559Sdes		/*
213892559Sdes		 * We are only interested in channels that can have buffered
213992559Sdes		 * incoming data.
214092559Sdes		 */
214157429Smarkm		if (compat13) {
214260573Skris			if (c->type != SSH_CHANNEL_OPEN &&
214360573Skris			    c->type != SSH_CHANNEL_INPUT_DRAINING)
214457429Smarkm				continue;
214557429Smarkm		} else {
214660573Skris			if (c->type != SSH_CHANNEL_OPEN)
214757429Smarkm				continue;
214857429Smarkm		}
214960573Skris		if (compat20 &&
215060573Skris		    (c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD))) {
215176262Sgreen			/* XXX is this true? */
215292559Sdes			debug3("channel %d: will not send data after close", c->self);
215360573Skris			continue;
215460573Skris		}
215557429Smarkm
215657429Smarkm		/* Get the amount of buffered data for this channel. */
215776262Sgreen		if ((c->istate == CHAN_INPUT_OPEN ||
215876262Sgreen		    c->istate == CHAN_INPUT_WAIT_DRAIN) &&
215976262Sgreen		    (len = buffer_len(&c->input)) > 0) {
2160157019Sdes			if (c->datagram) {
2161157019Sdes				if (len > 0) {
2162157019Sdes					u_char *data;
2163157019Sdes					u_int dlen;
2164157019Sdes
2165157019Sdes					data = buffer_get_string(&c->input,
2166157019Sdes					    &dlen);
2167157019Sdes					packet_start(SSH2_MSG_CHANNEL_DATA);
2168157019Sdes					packet_put_int(c->remote_id);
2169157019Sdes					packet_put_string(data, dlen);
2170157019Sdes					packet_send();
2171157019Sdes					c->remote_window -= dlen + 4;
2172157019Sdes					xfree(data);
2173157019Sdes				}
2174157019Sdes				continue;
2175157019Sdes			}
217692559Sdes			/*
217792559Sdes			 * Send some data for the other side over the secure
217892559Sdes			 * connection.
217992559Sdes			 */
218060573Skris			if (compat20) {
218160573Skris				if (len > c->remote_window)
218260573Skris					len = c->remote_window;
218360573Skris				if (len > c->remote_maxpacket)
218460573Skris					len = c->remote_maxpacket;
218557429Smarkm			} else {
218660573Skris				if (packet_is_interactive()) {
218760573Skris					if (len > 1024)
218860573Skris						len = 512;
218960573Skris				} else {
219060573Skris					/* Keep the packets at reasonable size. */
219160573Skris					if (len > packet_get_maxsize()/2)
219260573Skris						len = packet_get_maxsize()/2;
219360573Skris				}
219457429Smarkm			}
219560573Skris			if (len > 0) {
219660573Skris				packet_start(compat20 ?
219760573Skris				    SSH2_MSG_CHANNEL_DATA : SSH_MSG_CHANNEL_DATA);
219860573Skris				packet_put_int(c->remote_id);
219960573Skris				packet_put_string(buffer_ptr(&c->input), len);
220060573Skris				packet_send();
220160573Skris				buffer_consume(&c->input, len);
220260573Skris				c->remote_window -= len;
220360573Skris			}
220460573Skris		} else if (c->istate == CHAN_INPUT_WAIT_DRAIN) {
220557429Smarkm			if (compat13)
220657429Smarkm				fatal("cannot happen: istate == INPUT_WAIT_DRAIN for proto 1.3");
220757429Smarkm			/*
220857429Smarkm			 * input-buffer is empty and read-socket shutdown:
220998684Sdes			 * tell peer, that we will not send more data: send IEOF.
221098684Sdes			 * hack for extended data: delay EOF if EFD still in use.
221157429Smarkm			 */
221298684Sdes			if (CHANNEL_EFD_INPUT_ACTIVE(c))
2213149753Sdes				debug2("channel %d: ibuf_empty delayed efd %d/(%d)",
2214149753Sdes				    c->self, c->efd, buffer_len(&c->extended));
221598684Sdes			else
221698684Sdes				chan_ibuf_empty(c);
221757429Smarkm		}
221860573Skris		/* Send extended data, i.e. stderr */
221960573Skris		if (compat20 &&
222098684Sdes		    !(c->flags & CHAN_EOF_SENT) &&
222160573Skris		    c->remote_window > 0 &&
222260573Skris		    (len = buffer_len(&c->extended)) > 0 &&
222360573Skris		    c->extended_usage == CHAN_EXTENDED_READ) {
222499063Sdes			debug2("channel %d: rwin %u elen %u euse %d",
222576262Sgreen			    c->self, c->remote_window, buffer_len(&c->extended),
222676262Sgreen			    c->extended_usage);
222760573Skris			if (len > c->remote_window)
222860573Skris				len = c->remote_window;
222960573Skris			if (len > c->remote_maxpacket)
223060573Skris				len = c->remote_maxpacket;
223160573Skris			packet_start(SSH2_MSG_CHANNEL_EXTENDED_DATA);
223260573Skris			packet_put_int(c->remote_id);
223360573Skris			packet_put_int(SSH2_EXTENDED_DATA_STDERR);
223460573Skris			packet_put_string(buffer_ptr(&c->extended), len);
223560573Skris			packet_send();
223660573Skris			buffer_consume(&c->extended, len);
223760573Skris			c->remote_window -= len;
223876262Sgreen			debug2("channel %d: sent ext data %d", c->self, len);
223960573Skris		}
224057429Smarkm	}
224157429Smarkm}
224257429Smarkm
224357429Smarkm
224492559Sdes/* -- protocol input */
224592559Sdes
2246162856Sdes/* ARGSUSED */
224760573Skrisvoid
224892559Sdeschannel_input_data(int type, u_int32_t seq, void *ctxt)
224957429Smarkm{
225057429Smarkm	int id;
225157429Smarkm	char *data;
225276262Sgreen	u_int data_len;
225360573Skris	Channel *c;
225457429Smarkm
225557429Smarkm	/* Get the channel number and verify it. */
225657429Smarkm	id = packet_get_int();
225760573Skris	c = channel_lookup(id);
225860573Skris	if (c == NULL)
225957429Smarkm		packet_disconnect("Received data for nonexistent channel %d.", id);
226057429Smarkm
226157429Smarkm	/* Ignore any data for non-open channels (might happen on close) */
226260573Skris	if (c->type != SSH_CHANNEL_OPEN &&
226360573Skris	    c->type != SSH_CHANNEL_X11_OPEN)
226457429Smarkm		return;
226557429Smarkm
226657429Smarkm	/* Get the data. */
2267181111Sdes	data = packet_get_string_ptr(&data_len);
226860573Skris
2269126273Sdes	/*
2270126273Sdes	 * Ignore data for protocol > 1.3 if output end is no longer open.
2271126273Sdes	 * For protocol 2 the sending side is reducing its window as it sends
2272126273Sdes	 * data, so we must 'fake' consumption of the data in order to ensure
2273126273Sdes	 * that window updates are sent back.  Otherwise the connection might
2274126273Sdes	 * deadlock.
2275126273Sdes	 */
2276126273Sdes	if (!compat13 && c->ostate != CHAN_OUTPUT_OPEN) {
2277126273Sdes		if (compat20) {
2278126273Sdes			c->local_window -= data_len;
2279126273Sdes			c->local_consumed += data_len;
2280126273Sdes		}
2281126273Sdes		return;
2282126273Sdes	}
2283126273Sdes
228492559Sdes	if (compat20) {
228560573Skris		if (data_len > c->local_maxpacket) {
2286124207Sdes			logit("channel %d: rcvd big packet %d, maxpack %d",
228760573Skris			    c->self, data_len, c->local_maxpacket);
228860573Skris		}
228960573Skris		if (data_len > c->local_window) {
2290124207Sdes			logit("channel %d: rcvd too much data %d, win %d",
229160573Skris			    c->self, data_len, c->local_window);
229260573Skris			return;
229360573Skris		}
229460573Skris		c->local_window -= data_len;
229560573Skris	}
2296157019Sdes	if (c->datagram)
2297157019Sdes		buffer_put_string(&c->output, data, data_len);
2298157019Sdes	else
2299157019Sdes		buffer_append(&c->output, data, data_len);
2300181111Sdes	packet_check_eom();
230157429Smarkm}
230292559Sdes
2303162856Sdes/* ARGSUSED */
230460573Skrisvoid
230592559Sdeschannel_input_extended_data(int type, u_int32_t seq, void *ctxt)
230660573Skris{
230760573Skris	int id;
230860573Skris	char *data;
230999063Sdes	u_int data_len, tcode;
231060573Skris	Channel *c;
231157429Smarkm
231260573Skris	/* Get the channel number and verify it. */
231360573Skris	id = packet_get_int();
231460573Skris	c = channel_lookup(id);
231560573Skris
231660573Skris	if (c == NULL)
231760573Skris		packet_disconnect("Received extended_data for bad channel %d.", id);
231860573Skris	if (c->type != SSH_CHANNEL_OPEN) {
2319124207Sdes		logit("channel %d: ext data for non open", id);
232060573Skris		return;
232160573Skris	}
232298684Sdes	if (c->flags & CHAN_EOF_RCVD) {
232398684Sdes		if (datafellows & SSH_BUG_EXTEOF)
232498684Sdes			debug("channel %d: accepting ext data after eof", id);
232598684Sdes		else
232698684Sdes			packet_disconnect("Received extended_data after EOF "
232798684Sdes			    "on channel %d.", id);
232898684Sdes	}
232960573Skris	tcode = packet_get_int();
233060573Skris	if (c->efd == -1 ||
233160573Skris	    c->extended_usage != CHAN_EXTENDED_WRITE ||
233260573Skris	    tcode != SSH2_EXTENDED_DATA_STDERR) {
2333124207Sdes		logit("channel %d: bad ext data", c->self);
233460573Skris		return;
233560573Skris	}
233660573Skris	data = packet_get_string(&data_len);
233792559Sdes	packet_check_eom();
233860573Skris	if (data_len > c->local_window) {
2339124207Sdes		logit("channel %d: rcvd too much extended_data %d, win %d",
234060573Skris		    c->self, data_len, c->local_window);
234160573Skris		xfree(data);
234260573Skris		return;
234360573Skris	}
234469587Sgreen	debug2("channel %d: rcvd ext data %d", c->self, data_len);
234560573Skris	c->local_window -= data_len;
234660573Skris	buffer_append(&c->extended, data, data_len);
234760573Skris	xfree(data);
234860573Skris}
234960573Skris
2350162856Sdes/* ARGSUSED */
235160573Skrisvoid
235292559Sdeschannel_input_ieof(int type, u_int32_t seq, void *ctxt)
235360573Skris{
235460573Skris	int id;
235560573Skris	Channel *c;
235657429Smarkm
235760573Skris	id = packet_get_int();
235892559Sdes	packet_check_eom();
235960573Skris	c = channel_lookup(id);
236060573Skris	if (c == NULL)
236160573Skris		packet_disconnect("Received ieof for nonexistent channel %d.", id);
236260573Skris	chan_rcvd_ieof(c);
236392559Sdes
236492559Sdes	/* XXX force input close */
236592559Sdes	if (c->force_drain && c->istate == CHAN_INPUT_OPEN) {
236692559Sdes		debug("channel %d: FORCE input drain", c->self);
236792559Sdes		c->istate = CHAN_INPUT_WAIT_DRAIN;
236892559Sdes		if (buffer_len(&c->input) == 0)
236992559Sdes			chan_ibuf_empty(c);
237092559Sdes	}
237192559Sdes
237260573Skris}
237360573Skris
2374162856Sdes/* ARGSUSED */
237560573Skrisvoid
237692559Sdeschannel_input_close(int type, u_int32_t seq, void *ctxt)
237757429Smarkm{
237860573Skris	int id;
237960573Skris	Channel *c;
238057429Smarkm
238160573Skris	id = packet_get_int();
238292559Sdes	packet_check_eom();
238360573Skris	c = channel_lookup(id);
238460573Skris	if (c == NULL)
238560573Skris		packet_disconnect("Received close for nonexistent channel %d.", id);
238657429Smarkm
238757429Smarkm	/*
238857429Smarkm	 * Send a confirmation that we have closed the channel and no more
238957429Smarkm	 * data is coming for it.
239057429Smarkm	 */
239157429Smarkm	packet_start(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION);
239260573Skris	packet_put_int(c->remote_id);
239357429Smarkm	packet_send();
239457429Smarkm
239557429Smarkm	/*
239657429Smarkm	 * If the channel is in closed state, we have sent a close request,
239757429Smarkm	 * and the other side will eventually respond with a confirmation.
239857429Smarkm	 * Thus, we cannot free the channel here, because then there would be
239957429Smarkm	 * no-one to receive the confirmation.  The channel gets freed when
240057429Smarkm	 * the confirmation arrives.
240157429Smarkm	 */
240260573Skris	if (c->type != SSH_CHANNEL_CLOSED) {
240357429Smarkm		/*
240457429Smarkm		 * Not a closed channel - mark it as draining, which will
240557429Smarkm		 * cause it to be freed later.
240657429Smarkm		 */
240792559Sdes		buffer_clear(&c->input);
240860573Skris		c->type = SSH_CHANNEL_OUTPUT_DRAINING;
240957429Smarkm	}
241057429Smarkm}
241157429Smarkm
241260573Skris/* proto version 1.5 overloads CLOSE_CONFIRMATION with OCLOSE */
2413162856Sdes/* ARGSUSED */
241460573Skrisvoid
241592559Sdeschannel_input_oclose(int type, u_int32_t seq, void *ctxt)
241660573Skris{
241760573Skris	int id = packet_get_int();
241860573Skris	Channel *c = channel_lookup(id);
241992559Sdes
242092559Sdes	packet_check_eom();
242160573Skris	if (c == NULL)
242260573Skris		packet_disconnect("Received oclose for nonexistent channel %d.", id);
242360573Skris	chan_rcvd_oclose(c);
242460573Skris}
242557429Smarkm
2426162856Sdes/* ARGSUSED */
242760573Skrisvoid
242892559Sdeschannel_input_close_confirmation(int type, u_int32_t seq, void *ctxt)
242957429Smarkm{
243060573Skris	int id = packet_get_int();
243160573Skris	Channel *c = channel_lookup(id);
243257429Smarkm
243392559Sdes	packet_check_eom();
243460573Skris	if (c == NULL)
243560573Skris		packet_disconnect("Received close confirmation for "
243660573Skris		    "out-of-range channel %d.", id);
243760573Skris	if (c->type != SSH_CHANNEL_CLOSED)
243860573Skris		packet_disconnect("Received close confirmation for "
243960573Skris		    "non-closed channel %d (type %d).", id, c->type);
244092559Sdes	channel_free(c);
244160573Skris}
244257429Smarkm
2443162856Sdes/* ARGSUSED */
244460573Skrisvoid
244592559Sdeschannel_input_open_confirmation(int type, u_int32_t seq, void *ctxt)
244660573Skris{
244760573Skris	int id, remote_id;
244860573Skris	Channel *c;
244960573Skris
245060573Skris	id = packet_get_int();
245160573Skris	c = channel_lookup(id);
245260573Skris
245360573Skris	if (c==NULL || c->type != SSH_CHANNEL_OPENING)
245460573Skris		packet_disconnect("Received open confirmation for "
245560573Skris		    "non-opening channel %d.", id);
245660573Skris	remote_id = packet_get_int();
245760573Skris	/* Record the remote channel number and mark that the channel is now open. */
245860573Skris	c->remote_id = remote_id;
245960573Skris	c->type = SSH_CHANNEL_OPEN;
246060573Skris
246160573Skris	if (compat20) {
246260573Skris		c->remote_window = packet_get_int();
246360573Skris		c->remote_maxpacket = packet_get_int();
2464181111Sdes		if (c->open_confirm) {
246569587Sgreen			debug2("callback start");
2466181111Sdes			c->open_confirm(c->self, c->open_confirm_ctx);
246769587Sgreen			debug2("callback done");
246860573Skris		}
2469124207Sdes		debug2("channel %d: open confirm rwindow %u rmax %u", c->self,
247060573Skris		    c->remote_window, c->remote_maxpacket);
247157429Smarkm	}
247292559Sdes	packet_check_eom();
247357429Smarkm}
247457429Smarkm
247592559Sdesstatic char *
247692559Sdesreason2txt(int reason)
247792559Sdes{
247892559Sdes	switch (reason) {
247992559Sdes	case SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED:
248092559Sdes		return "administratively prohibited";
248192559Sdes	case SSH2_OPEN_CONNECT_FAILED:
248292559Sdes		return "connect failed";
248392559Sdes	case SSH2_OPEN_UNKNOWN_CHANNEL_TYPE:
248492559Sdes		return "unknown channel type";
248592559Sdes	case SSH2_OPEN_RESOURCE_SHORTAGE:
248692559Sdes		return "resource shortage";
248792559Sdes	}
248892559Sdes	return "unknown reason";
248992559Sdes}
249092559Sdes
2491162856Sdes/* ARGSUSED */
249260573Skrisvoid
249392559Sdeschannel_input_open_failure(int type, u_int32_t seq, void *ctxt)
249457429Smarkm{
249576262Sgreen	int id, reason;
249676262Sgreen	char *msg = NULL, *lang = NULL;
249760573Skris	Channel *c;
249857429Smarkm
249960573Skris	id = packet_get_int();
250060573Skris	c = channel_lookup(id);
250157429Smarkm
250260573Skris	if (c==NULL || c->type != SSH_CHANNEL_OPENING)
250360573Skris		packet_disconnect("Received open failure for "
250460573Skris		    "non-opening channel %d.", id);
250560573Skris	if (compat20) {
250676262Sgreen		reason = packet_get_int();
250792559Sdes		if (!(datafellows & SSH_BUG_OPENFAILURE)) {
250876262Sgreen			msg  = packet_get_string(NULL);
250976262Sgreen			lang = packet_get_string(NULL);
251076262Sgreen		}
2511124207Sdes		logit("channel %d: open failed: %s%s%s", id,
251292559Sdes		    reason2txt(reason), msg ? ": ": "", msg ? msg : "");
251376262Sgreen		if (msg != NULL)
251476262Sgreen			xfree(msg);
251576262Sgreen		if (lang != NULL)
251676262Sgreen			xfree(lang);
251760573Skris	}
251892559Sdes	packet_check_eom();
2519192595Sdes	/* Schedule the channel for cleanup/deletion. */
2520192595Sdes	chan_mark_dead(c);
252157429Smarkm}
252257429Smarkm
2523162856Sdes/* ARGSUSED */
252460573Skrisvoid
252592559Sdeschannel_input_window_adjust(int type, u_int32_t seq, void *ctxt)
252660573Skris{
252760573Skris	Channel *c;
252899063Sdes	int id;
252999063Sdes	u_int adjust;
253057429Smarkm
253160573Skris	if (!compat20)
253260573Skris		return;
253360573Skris
253457429Smarkm	/* Get the channel number and verify it. */
253560573Skris	id = packet_get_int();
253660573Skris	c = channel_lookup(id);
253757429Smarkm
2538157019Sdes	if (c == NULL) {
2539157019Sdes		logit("Received window adjust for non-open channel %d.", id);
254060573Skris		return;
254160573Skris	}
254260573Skris	adjust = packet_get_int();
254392559Sdes	packet_check_eom();
254499063Sdes	debug2("channel %d: rcvd adjust %u", id, adjust);
254560573Skris	c->remote_window += adjust;
254657429Smarkm}
254757429Smarkm
2548162856Sdes/* ARGSUSED */
254960573Skrisvoid
255092559Sdeschannel_input_port_open(int type, u_int32_t seq, void *ctxt)
255157429Smarkm{
255292559Sdes	Channel *c = NULL;
255392559Sdes	u_short host_port;
255492559Sdes	char *host, *originator_string;
2555181111Sdes	int remote_id;
255657429Smarkm
255792559Sdes	remote_id = packet_get_int();
255892559Sdes	host = packet_get_string(NULL);
255992559Sdes	host_port = packet_get_int();
256057429Smarkm
256192559Sdes	if (packet_get_protocol_flags() & SSH_PROTOFLAG_HOST_IN_FWD_OPEN) {
256292559Sdes		originator_string = packet_get_string(NULL);
256392559Sdes	} else {
256492559Sdes		originator_string = xstrdup("unknown (remote did not supply name)");
256592559Sdes	}
256692559Sdes	packet_check_eom();
2567181111Sdes	c = channel_connect_to(host, host_port,
2568181111Sdes	    "connected socket", originator_string);
2569124207Sdes	xfree(originator_string);
2570181111Sdes	xfree(host);
257192559Sdes	if (c == NULL) {
257292559Sdes		packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE);
257392559Sdes		packet_put_int(remote_id);
257492559Sdes		packet_send();
2575181111Sdes	} else
2576181111Sdes		c->remote_id = remote_id;
257757429Smarkm}
257857429Smarkm
2579181111Sdes/* ARGSUSED */
2580181111Sdesvoid
2581181111Sdeschannel_input_status_confirm(int type, u_int32_t seq, void *ctxt)
2582181111Sdes{
2583181111Sdes	Channel *c;
2584181111Sdes	struct channel_confirm *cc;
2585192595Sdes	int id;
258657429Smarkm
2587181111Sdes	/* Reset keepalive timeout */
2588197679Sdes	packet_set_alive_timeouts(0);
2589181111Sdes
2590192595Sdes	id = packet_get_int();
2591181111Sdes	packet_check_eom();
2592181111Sdes
2593192595Sdes	debug2("channel_input_status_confirm: type %d id %d", type, id);
2594181111Sdes
2595192595Sdes	if ((c = channel_lookup(id)) == NULL) {
2596192595Sdes		logit("channel_input_status_confirm: %d: unknown", id);
2597181111Sdes		return;
2598181111Sdes	}
2599181111Sdes	;
2600181111Sdes	if ((cc = TAILQ_FIRST(&c->status_confirms)) == NULL)
2601181111Sdes		return;
2602181111Sdes	cc->cb(type, c, cc->ctx);
2603181111Sdes	TAILQ_REMOVE(&c->status_confirms, cc, entry);
2604181111Sdes	bzero(cc, sizeof(*cc));
2605181111Sdes	xfree(cc);
2606181111Sdes}
2607181111Sdes
260892559Sdes/* -- tcp forwarding */
260957429Smarkm
261092559Sdesvoid
261192559Sdeschannel_set_af(int af)
261276262Sgreen{
261392559Sdes	IPv4or6 = af;
261476262Sgreen}
261576262Sgreen
261692559Sdesstatic int
2617192595Sdeschannel_setup_fwd_listener(int type, const char *listen_addr,
2618192595Sdes    u_short listen_port, int *allocated_listen_port,
261992559Sdes    const char *host_to_connect, u_short port_to_connect, int gateway_ports)
262057429Smarkm{
262192559Sdes	Channel *c;
2622157019Sdes	int sock, r, success = 0, wildcard = 0, is_client;
262357429Smarkm	struct addrinfo hints, *ai, *aitop;
2624147005Sdes	const char *host, *addr;
262557429Smarkm	char ntop[NI_MAXHOST], strport[NI_MAXSERV];
2626192595Sdes	in_port_t *lport_p;
262757429Smarkm
262892559Sdes	host = (type == SSH_CHANNEL_RPORT_LISTENER) ?
262992559Sdes	    listen_addr : host_to_connect;
2630147005Sdes	is_client = (type == SSH_CHANNEL_PORT_LISTENER);
263157429Smarkm
263292559Sdes	if (host == NULL) {
263392559Sdes		error("No forward host name.");
2634149753Sdes		return 0;
263576262Sgreen	}
2636192595Sdes	if (strlen(host) >= NI_MAXHOST) {
263776262Sgreen		error("Forward host name too long.");
2638149753Sdes		return 0;
263976262Sgreen	}
264076262Sgreen
264157429Smarkm	/*
2642147005Sdes	 * Determine whether or not a port forward listens to loopback,
2643147005Sdes	 * specified address or wildcard. On the client, a specified bind
2644147005Sdes	 * address will always override gateway_ports. On the server, a
2645147005Sdes	 * gateway_ports of 1 (``yes'') will override the client's
2646147005Sdes	 * specification and force a wildcard bind, whereas a value of 2
2647147005Sdes	 * (``clientspecified'') will bind to whatever address the client
2648147005Sdes	 * asked for.
2649147005Sdes	 *
2650147005Sdes	 * Special-case listen_addrs are:
2651147005Sdes	 *
2652147005Sdes	 * "0.0.0.0"               -> wildcard v4/v6 if SSH_OLD_FORWARD_ADDR
2653147005Sdes	 * "" (empty string), "*"  -> wildcard v4/v6
2654147005Sdes	 * "localhost"             -> loopback v4/v6
2655147005Sdes	 */
2656147005Sdes	addr = NULL;
2657147005Sdes	if (listen_addr == NULL) {
2658147005Sdes		/* No address specified: default to gateway_ports setting */
2659147005Sdes		if (gateway_ports)
2660147005Sdes			wildcard = 1;
2661147005Sdes	} else if (gateway_ports || is_client) {
2662147005Sdes		if (((datafellows & SSH_OLD_FORWARD_ADDR) &&
2663181111Sdes		    strcmp(listen_addr, "0.0.0.0") == 0 && is_client == 0) ||
2664147005Sdes		    *listen_addr == '\0' || strcmp(listen_addr, "*") == 0 ||
2665147005Sdes		    (!is_client && gateway_ports == 1))
2666147005Sdes			wildcard = 1;
2667147005Sdes		else if (strcmp(listen_addr, "localhost") != 0)
2668147005Sdes			addr = listen_addr;
2669147005Sdes	}
2670147005Sdes
2671147005Sdes	debug3("channel_setup_fwd_listener: type %d wildcard %d addr %s",
2672147005Sdes	    type, wildcard, (addr == NULL) ? "NULL" : addr);
2673147005Sdes
2674147005Sdes	/*
267557429Smarkm	 * getaddrinfo returns a loopback address if the hostname is
267657429Smarkm	 * set to NULL and hints.ai_flags is not AI_PASSIVE
267757429Smarkm	 */
267857429Smarkm	memset(&hints, 0, sizeof(hints));
267957429Smarkm	hints.ai_family = IPv4or6;
2680147005Sdes	hints.ai_flags = wildcard ? AI_PASSIVE : 0;
268157429Smarkm	hints.ai_socktype = SOCK_STREAM;
268276262Sgreen	snprintf(strport, sizeof strport, "%d", listen_port);
2683147005Sdes	if ((r = getaddrinfo(addr, strport, &hints, &aitop)) != 0) {
2684147005Sdes		if (addr == NULL) {
2685147005Sdes			/* This really shouldn't happen */
2686147005Sdes			packet_disconnect("getaddrinfo: fatal error: %s",
2687181111Sdes			    ssh_gai_strerror(r));
2688147005Sdes		} else {
2689149753Sdes			error("channel_setup_fwd_listener: "
2690181111Sdes			    "getaddrinfo(%.64s): %s", addr,
2691181111Sdes			    ssh_gai_strerror(r));
2692147005Sdes		}
2693149753Sdes		return 0;
2694147005Sdes	}
2695192595Sdes	if (allocated_listen_port != NULL)
2696192595Sdes		*allocated_listen_port = 0;
269757429Smarkm	for (ai = aitop; ai; ai = ai->ai_next) {
2698192595Sdes		switch (ai->ai_family) {
2699192595Sdes		case AF_INET:
2700192595Sdes			lport_p = &((struct sockaddr_in *)ai->ai_addr)->
2701192595Sdes			    sin_port;
2702192595Sdes			break;
2703192595Sdes		case AF_INET6:
2704192595Sdes			lport_p = &((struct sockaddr_in6 *)ai->ai_addr)->
2705192595Sdes			    sin6_port;
2706192595Sdes			break;
2707192595Sdes		default:
270857429Smarkm			continue;
2709192595Sdes		}
2710192595Sdes		/*
2711192595Sdes		 * If allocating a port for -R forwards, then use the
2712192595Sdes		 * same port for all address families.
2713192595Sdes		 */
2714192595Sdes		if (type == SSH_CHANNEL_RPORT_LISTENER && listen_port == 0 &&
2715192595Sdes		    allocated_listen_port != NULL && *allocated_listen_port > 0)
2716192595Sdes			*lport_p = htons(*allocated_listen_port);
2717192595Sdes
271857429Smarkm		if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop),
271957429Smarkm		    strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV) != 0) {
272092559Sdes			error("channel_setup_fwd_listener: getnameinfo failed");
272157429Smarkm			continue;
272257429Smarkm		}
272357429Smarkm		/* Create a port to listen for the host. */
2724124207Sdes		sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
272557429Smarkm		if (sock < 0) {
272657429Smarkm			/* this is no error since kernel may not support ipv6 */
272757429Smarkm			verbose("socket: %.100s", strerror(errno));
272857429Smarkm			continue;
272957429Smarkm		}
2730106130Sdes
2731157019Sdes		channel_set_reuseaddr(sock);
2732204917Sdes		if (ai->ai_family == AF_INET6)
2733204917Sdes			sock_set_v6only(sock);
2734157019Sdes
2735192595Sdes		debug("Local forwarding listening on %s port %s.",
2736192595Sdes		    ntop, strport);
273757429Smarkm
273857429Smarkm		/* Bind the socket to the address. */
273957429Smarkm		if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) {
274057429Smarkm			/* address can be in use ipv6 address is already bound */
274198941Sdes			if (!ai->ai_next)
274298941Sdes				error("bind: %.100s", strerror(errno));
274398941Sdes			else
274498941Sdes				verbose("bind: %.100s", strerror(errno));
274598941Sdes
274657429Smarkm			close(sock);
274757429Smarkm			continue;
274857429Smarkm		}
274957429Smarkm		/* Start listening for connections on the socket. */
2750126273Sdes		if (listen(sock, SSH_LISTEN_BACKLOG) < 0) {
275157429Smarkm			error("listen: %.100s", strerror(errno));
275257429Smarkm			close(sock);
275357429Smarkm			continue;
275457429Smarkm		}
2755192595Sdes
2756192595Sdes		/*
2757192595Sdes		 * listen_port == 0 requests a dynamically allocated port -
2758192595Sdes		 * record what we got.
2759192595Sdes		 */
2760192595Sdes		if (type == SSH_CHANNEL_RPORT_LISTENER && listen_port == 0 &&
2761192595Sdes		    allocated_listen_port != NULL &&
2762192595Sdes		    *allocated_listen_port == 0) {
2763192595Sdes			*allocated_listen_port = get_sock_port(sock, 1);
2764192595Sdes			debug("Allocated listen port %d",
2765192595Sdes			    *allocated_listen_port);
2766192595Sdes		}
2767192595Sdes
276857429Smarkm		/* Allocate a channel number for the socket. */
276992559Sdes		c = channel_new("port listener", type, sock, sock, -1,
277060573Skris		    CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT,
2771124207Sdes		    0, "port listener", 1);
2772192595Sdes		c->path = xstrdup(host);
277392559Sdes		c->host_port = port_to_connect;
277492559Sdes		c->listening_port = listen_port;
277557429Smarkm		success = 1;
277657429Smarkm	}
277757429Smarkm	if (success == 0)
277892559Sdes		error("channel_setup_fwd_listener: cannot listen to port: %d",
277976262Sgreen		    listen_port);
278057429Smarkm	freeaddrinfo(aitop);
278176262Sgreen	return success;
278257429Smarkm}
278357429Smarkm
2784137019Sdesint
2785137019Sdeschannel_cancel_rport_listener(const char *host, u_short port)
2786137019Sdes{
2787137019Sdes	u_int i;
2788137019Sdes	int found = 0;
2789137019Sdes
2790147005Sdes	for (i = 0; i < channels_alloc; i++) {
2791137019Sdes		Channel *c = channels[i];
2792137019Sdes
2793137019Sdes		if (c != NULL && c->type == SSH_CHANNEL_RPORT_LISTENER &&
2794192595Sdes		    strcmp(c->path, host) == 0 && c->listening_port == port) {
2795147005Sdes			debug2("%s: close channel %d", __func__, i);
2796137019Sdes			channel_free(c);
2797137019Sdes			found = 1;
2798137019Sdes		}
2799137019Sdes	}
2800137019Sdes
2801137019Sdes	return (found);
2802137019Sdes}
2803137019Sdes
280492559Sdes/* protocol local port fwd, used by ssh (and sshd in v1) */
280592559Sdesint
2806147005Sdeschannel_setup_local_fwd_listener(const char *listen_host, u_short listen_port,
280792559Sdes    const char *host_to_connect, u_short port_to_connect, int gateway_ports)
280892559Sdes{
280992559Sdes	return channel_setup_fwd_listener(SSH_CHANNEL_PORT_LISTENER,
2810192595Sdes	    listen_host, listen_port, NULL, host_to_connect, port_to_connect,
2811147005Sdes	    gateway_ports);
281292559Sdes}
281392559Sdes
281492559Sdes/* protocol v2 remote port fwd, used by sshd */
281592559Sdesint
281692559Sdeschannel_setup_remote_fwd_listener(const char *listen_address,
2817192595Sdes    u_short listen_port, int *allocated_listen_port, int gateway_ports)
281892559Sdes{
281992559Sdes	return channel_setup_fwd_listener(SSH_CHANNEL_RPORT_LISTENER,
2820192595Sdes	    listen_address, listen_port, allocated_listen_port,
2821192595Sdes	    NULL, 0, gateway_ports);
282292559Sdes}
282392559Sdes
282457429Smarkm/*
282557429Smarkm * Initiate forwarding of connections to port "port" on remote host through
282657429Smarkm * the secure channel to host:port from local side.
282757429Smarkm */
282857429Smarkm
2829162856Sdesint
2830147005Sdeschannel_request_remote_forwarding(const char *listen_host, u_short listen_port,
283176262Sgreen    const char *host_to_connect, u_short port_to_connect)
283257429Smarkm{
283392559Sdes	int type, success = 0;
283476262Sgreen
283557429Smarkm	/* Record locally that connection to this host/port is permitted. */
283657429Smarkm	if (num_permitted_opens >= SSH_MAX_FORWARDS_PER_DIRECTION)
283757429Smarkm		fatal("channel_request_remote_forwarding: too many forwards");
283857429Smarkm
283957429Smarkm	/* Send the forward request to the remote side. */
284060573Skris	if (compat20) {
2841147005Sdes		const char *address_to_bind;
2842181111Sdes		if (listen_host == NULL) {
2843181111Sdes			if (datafellows & SSH_BUG_RFWD_ADDR)
2844181111Sdes				address_to_bind = "127.0.0.1";
2845181111Sdes			else
2846181111Sdes				address_to_bind = "localhost";
2847181111Sdes		} else if (*listen_host == '\0' ||
2848181111Sdes			   strcmp(listen_host, "*") == 0) {
2849181111Sdes			if (datafellows & SSH_BUG_RFWD_ADDR)
2850181111Sdes				address_to_bind = "0.0.0.0";
2851181111Sdes			else
2852181111Sdes				address_to_bind = "";
2853181111Sdes		} else
2854147005Sdes			address_to_bind = listen_host;
2855147005Sdes
285660573Skris		packet_start(SSH2_MSG_GLOBAL_REQUEST);
285760573Skris		packet_put_cstring("tcpip-forward");
285898684Sdes		packet_put_char(1);			/* boolean: want reply */
285960573Skris		packet_put_cstring(address_to_bind);
286060573Skris		packet_put_int(listen_port);
286176262Sgreen		packet_send();
286276262Sgreen		packet_write_wait();
286376262Sgreen		/* Assume that server accepts the request */
286476262Sgreen		success = 1;
286560573Skris	} else {
286660573Skris		packet_start(SSH_CMSG_PORT_FORWARD_REQUEST);
286760573Skris		packet_put_int(listen_port);
286860573Skris		packet_put_cstring(host_to_connect);
286960573Skris		packet_put_int(port_to_connect);
287060573Skris		packet_send();
287160573Skris		packet_write_wait();
287276262Sgreen
287376262Sgreen		/* Wait for response from the remote side. */
287492559Sdes		type = packet_read();
287576262Sgreen		switch (type) {
287676262Sgreen		case SSH_SMSG_SUCCESS:
287776262Sgreen			success = 1;
287876262Sgreen			break;
287976262Sgreen		case SSH_SMSG_FAILURE:
288076262Sgreen			break;
288176262Sgreen		default:
288276262Sgreen			/* Unknown packet */
288376262Sgreen			packet_disconnect("Protocol error for port forward request:"
288476262Sgreen			    "received packet type %d.", type);
288576262Sgreen		}
288660573Skris	}
288776262Sgreen	if (success) {
288876262Sgreen		permitted_opens[num_permitted_opens].host_to_connect = xstrdup(host_to_connect);
288976262Sgreen		permitted_opens[num_permitted_opens].port_to_connect = port_to_connect;
289076262Sgreen		permitted_opens[num_permitted_opens].listen_port = listen_port;
289176262Sgreen		num_permitted_opens++;
289276262Sgreen	}
2893162856Sdes	return (success ? 0 : -1);
289457429Smarkm}
289557429Smarkm
289657429Smarkm/*
2897137019Sdes * Request cancellation of remote forwarding of connection host:port from
2898137019Sdes * local side.
2899137019Sdes */
2900137019Sdesvoid
2901147005Sdeschannel_request_rforward_cancel(const char *host, u_short port)
2902137019Sdes{
2903137019Sdes	int i;
2904137019Sdes
2905137019Sdes	if (!compat20)
2906137019Sdes		return;
2907137019Sdes
2908137019Sdes	for (i = 0; i < num_permitted_opens; i++) {
2909137019Sdes		if (permitted_opens[i].host_to_connect != NULL &&
2910137019Sdes		    permitted_opens[i].listen_port == port)
2911137019Sdes			break;
2912137019Sdes	}
2913137019Sdes	if (i >= num_permitted_opens) {
2914137019Sdes		debug("%s: requested forward not found", __func__);
2915137019Sdes		return;
2916137019Sdes	}
2917137019Sdes	packet_start(SSH2_MSG_GLOBAL_REQUEST);
2918137019Sdes	packet_put_cstring("cancel-tcpip-forward");
2919137019Sdes	packet_put_char(0);
2920147005Sdes	packet_put_cstring(host == NULL ? "" : host);
2921137019Sdes	packet_put_int(port);
2922137019Sdes	packet_send();
2923137019Sdes
2924137019Sdes	permitted_opens[i].listen_port = 0;
2925137019Sdes	permitted_opens[i].port_to_connect = 0;
2926157019Sdes	xfree(permitted_opens[i].host_to_connect);
2927137019Sdes	permitted_opens[i].host_to_connect = NULL;
2928137019Sdes}
2929137019Sdes
2930137019Sdes/*
293157429Smarkm * This is called after receiving CHANNEL_FORWARDING_REQUEST.  This initates
293257429Smarkm * listening for the port, and sends back a success reply (or disconnect
2933162856Sdes * message if there was an error).
293457429Smarkm */
2935162856Sdesint
293660573Skrischannel_input_port_forward_request(int is_root, int gateway_ports)
293757429Smarkm{
293857429Smarkm	u_short port, host_port;
2939162856Sdes	int success = 0;
294057429Smarkm	char *hostname;
294157429Smarkm
294257429Smarkm	/* Get arguments from the packet. */
294357429Smarkm	port = packet_get_int();
294457429Smarkm	hostname = packet_get_string(NULL);
294557429Smarkm	host_port = packet_get_int();
294657429Smarkm
294798941Sdes#ifndef HAVE_CYGWIN
294857429Smarkm	/*
294957429Smarkm	 * Check that an unprivileged user is not trying to forward a
295057429Smarkm	 * privileged port.
295157429Smarkm	 */
295257429Smarkm	if (port < IPPORT_RESERVED && !is_root)
2953124207Sdes		packet_disconnect(
2954124207Sdes		    "Requested forwarding of port %d but user is not root.",
2955124207Sdes		    port);
2956124207Sdes	if (host_port == 0)
2957124207Sdes		packet_disconnect("Dynamic forwarding denied.");
295898941Sdes#endif
2959124207Sdes
296076262Sgreen	/* Initiate forwarding */
2961162856Sdes	success = channel_setup_local_fwd_listener(NULL, port, hostname,
2962147005Sdes	    host_port, gateway_ports);
296357429Smarkm
296457429Smarkm	/* Free the argument string. */
296557429Smarkm	xfree(hostname);
2966162856Sdes
2967162856Sdes	return (success ? 0 : -1);
296857429Smarkm}
296957429Smarkm
297076262Sgreen/*
297176262Sgreen * Permits opening to any host/port if permitted_opens[] is empty.  This is
297276262Sgreen * usually called by the server, because the user could connect to any port
297376262Sgreen * anyway, and the server has no way to know but to trust the client anyway.
297476262Sgreen */
297576262Sgreenvoid
297692559Sdeschannel_permit_all_opens(void)
297776262Sgreen{
297876262Sgreen	if (num_permitted_opens == 0)
297976262Sgreen		all_opens_permitted = 1;
298076262Sgreen}
298176262Sgreen
298276262Sgreenvoid
298376262Sgreenchannel_add_permitted_opens(char *host, int port)
298476262Sgreen{
298576262Sgreen	if (num_permitted_opens >= SSH_MAX_FORWARDS_PER_DIRECTION)
2986162856Sdes		fatal("channel_add_permitted_opens: too many forwards");
298776262Sgreen	debug("allow port forwarding to host %s port %d", host, port);
298876262Sgreen
298976262Sgreen	permitted_opens[num_permitted_opens].host_to_connect = xstrdup(host);
299076262Sgreen	permitted_opens[num_permitted_opens].port_to_connect = port;
299176262Sgreen	num_permitted_opens++;
299276262Sgreen
299376262Sgreen	all_opens_permitted = 0;
299476262Sgreen}
299576262Sgreen
2996162856Sdesint
2997162856Sdeschannel_add_adm_permitted_opens(char *host, int port)
2998162856Sdes{
2999162856Sdes	if (num_adm_permitted_opens >= SSH_MAX_FORWARDS_PER_DIRECTION)
3000162856Sdes		fatal("channel_add_adm_permitted_opens: too many forwards");
3001162856Sdes	debug("config allows port forwarding to host %s port %d", host, port);
3002162856Sdes
3003162856Sdes	permitted_adm_opens[num_adm_permitted_opens].host_to_connect
3004162856Sdes	     = xstrdup(host);
3005162856Sdes	permitted_adm_opens[num_adm_permitted_opens].port_to_connect = port;
3006162856Sdes	return ++num_adm_permitted_opens;
3007162856Sdes}
3008162856Sdes
300976262Sgreenvoid
301076262Sgreenchannel_clear_permitted_opens(void)
301176262Sgreen{
301276262Sgreen	int i;
301376262Sgreen
301476262Sgreen	for (i = 0; i < num_permitted_opens; i++)
3015137019Sdes		if (permitted_opens[i].host_to_connect != NULL)
3016137019Sdes			xfree(permitted_opens[i].host_to_connect);
301776262Sgreen	num_permitted_opens = 0;
3018162856Sdes}
301976262Sgreen
3020162856Sdesvoid
3021162856Sdeschannel_clear_adm_permitted_opens(void)
3022162856Sdes{
3023162856Sdes	int i;
3024162856Sdes
3025162856Sdes	for (i = 0; i < num_adm_permitted_opens; i++)
3026162856Sdes		if (permitted_adm_opens[i].host_to_connect != NULL)
3027162856Sdes			xfree(permitted_adm_opens[i].host_to_connect);
3028162856Sdes	num_adm_permitted_opens = 0;
302976262Sgreen}
303076262Sgreen
3031181111Sdesvoid
3032181111Sdeschannel_print_adm_permitted_opens(void)
3033181111Sdes{
3034181111Sdes	int i;
3035181111Sdes
3036192595Sdes	printf("permitopen");
3037192595Sdes	if (num_adm_permitted_opens == 0) {
3038192595Sdes		printf(" any\n");
3039192595Sdes		return;
3040192595Sdes	}
3041181111Sdes	for (i = 0; i < num_adm_permitted_opens; i++)
3042181111Sdes		if (permitted_adm_opens[i].host_to_connect != NULL)
3043181111Sdes			printf(" %s:%d", permitted_adm_opens[i].host_to_connect,
3044181111Sdes			    permitted_adm_opens[i].port_to_connect);
3045192595Sdes	printf("\n");
3046181111Sdes}
3047181111Sdes
3048181111Sdes/* Try to start non-blocking connect to next host in cctx list */
304992559Sdesstatic int
3050181111Sdesconnect_next(struct channel_connect *cctx)
305160573Skris{
3052181111Sdes	int sock, saved_errno;
305360573Skris	char ntop[NI_MAXHOST], strport[NI_MAXSERV];
305460573Skris
3055181111Sdes	for (; cctx->ai; cctx->ai = cctx->ai->ai_next) {
3056181111Sdes		if (cctx->ai->ai_family != AF_INET &&
3057181111Sdes		    cctx->ai->ai_family != AF_INET6)
305860573Skris			continue;
3059181111Sdes		if (getnameinfo(cctx->ai->ai_addr, cctx->ai->ai_addrlen,
3060181111Sdes		    ntop, sizeof(ntop), strport, sizeof(strport),
3061181111Sdes		    NI_NUMERICHOST|NI_NUMERICSERV) != 0) {
3062181111Sdes			error("connect_next: getnameinfo failed");
306360573Skris			continue;
306460573Skris		}
3065181111Sdes		if ((sock = socket(cctx->ai->ai_family, cctx->ai->ai_socktype,
3066181111Sdes		    cctx->ai->ai_protocol)) == -1) {
3067181111Sdes			if (cctx->ai->ai_next == NULL)
3068113911Sdes				error("socket: %.100s", strerror(errno));
3069113911Sdes			else
3070113911Sdes				verbose("socket: %.100s", strerror(errno));
307160573Skris			continue;
307260573Skris		}
3073137019Sdes		if (set_nonblock(sock) == -1)
3074137019Sdes			fatal("%s: set_nonblock(%d)", __func__, sock);
3075181111Sdes		if (connect(sock, cctx->ai->ai_addr,
3076181111Sdes		    cctx->ai->ai_addrlen) == -1 && errno != EINPROGRESS) {
3077181111Sdes			debug("connect_next: host %.100s ([%.100s]:%s): "
3078181111Sdes			    "%.100s", cctx->host, ntop, strport,
307960573Skris			    strerror(errno));
3080181111Sdes			saved_errno = errno;
308160573Skris			close(sock);
3082181111Sdes			errno = saved_errno;
308376262Sgreen			continue;	/* fail -- try next */
308460573Skris		}
3085181111Sdes		debug("connect_next: host %.100s ([%.100s]:%s) "
3086181111Sdes		    "in progress, fd=%d", cctx->host, ntop, strport, sock);
3087181111Sdes		cctx->ai = cctx->ai->ai_next;
3088181111Sdes		set_nodelay(sock);
3089181111Sdes		return sock;
3090181111Sdes	}
3091181111Sdes	return -1;
3092181111Sdes}
309360573Skris
3094181111Sdesstatic void
3095181111Sdeschannel_connect_ctx_free(struct channel_connect *cctx)
3096181111Sdes{
3097181111Sdes	xfree(cctx->host);
3098181111Sdes	if (cctx->aitop)
3099181111Sdes		freeaddrinfo(cctx->aitop);
3100181111Sdes	bzero(cctx, sizeof(*cctx));
3101181111Sdes	cctx->host = NULL;
3102181111Sdes	cctx->ai = cctx->aitop = NULL;
3103181111Sdes}
3104181111Sdes
3105181111Sdes/* Return CONNECTING channel to remote host, port */
3106181111Sdesstatic Channel *
3107181111Sdesconnect_to(const char *host, u_short port, char *ctype, char *rname)
3108181111Sdes{
3109181111Sdes	struct addrinfo hints;
3110181111Sdes	int gaierr;
3111181111Sdes	int sock = -1;
3112181111Sdes	char strport[NI_MAXSERV];
3113181111Sdes	struct channel_connect cctx;
3114181111Sdes	Channel *c;
3115181111Sdes
3116181111Sdes	memset(&cctx, 0, sizeof(cctx));
3117181111Sdes	memset(&hints, 0, sizeof(hints));
3118181111Sdes	hints.ai_family = IPv4or6;
3119181111Sdes	hints.ai_socktype = SOCK_STREAM;
3120181111Sdes	snprintf(strport, sizeof strport, "%d", port);
3121181111Sdes	if ((gaierr = getaddrinfo(host, strport, &hints, &cctx.aitop)) != 0) {
3122181111Sdes		error("connect_to %.100s: unknown host (%s)", host,
3123181111Sdes		    ssh_gai_strerror(gaierr));
3124181111Sdes		return NULL;
312560573Skris	}
3126181111Sdes
3127181111Sdes	cctx.host = xstrdup(host);
3128181111Sdes	cctx.port = port;
3129181111Sdes	cctx.ai = cctx.aitop;
3130181111Sdes
3131181111Sdes	if ((sock = connect_next(&cctx)) == -1) {
3132181111Sdes		error("connect to %.100s port %d failed: %s",
3133181111Sdes		    host, port, strerror(errno));
3134181111Sdes		channel_connect_ctx_free(&cctx);
3135181111Sdes		return NULL;
313660573Skris	}
3137181111Sdes	c = channel_new(ctype, SSH_CHANNEL_CONNECTING, sock, sock, -1,
3138181111Sdes	    CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, rname, 1);
3139181111Sdes	c->connect_ctx = cctx;
3140181111Sdes	return c;
314160573Skris}
314276262Sgreen
3143181111SdesChannel *
3144181111Sdeschannel_connect_by_listen_address(u_short listen_port, char *ctype, char *rname)
314576262Sgreen{
314676262Sgreen	int i;
314776262Sgreen
3148181111Sdes	for (i = 0; i < num_permitted_opens; i++) {
3149137019Sdes		if (permitted_opens[i].host_to_connect != NULL &&
3150181111Sdes		    permitted_opens[i].listen_port == listen_port) {
315176262Sgreen			return connect_to(
315276262Sgreen			    permitted_opens[i].host_to_connect,
3153181111Sdes			    permitted_opens[i].port_to_connect, ctype, rname);
3154181111Sdes		}
3155181111Sdes	}
315676262Sgreen	error("WARNING: Server requests forwarding for unknown listen_port %d",
315776262Sgreen	    listen_port);
3158181111Sdes	return NULL;
315976262Sgreen}
316076262Sgreen
316176262Sgreen/* Check if connecting to that port is permitted and connect. */
3162181111SdesChannel *
3163181111Sdeschannel_connect_to(const char *host, u_short port, char *ctype, char *rname)
316476262Sgreen{
3165162856Sdes	int i, permit, permit_adm = 1;
316676262Sgreen
316776262Sgreen	permit = all_opens_permitted;
316876262Sgreen	if (!permit) {
316976262Sgreen		for (i = 0; i < num_permitted_opens; i++)
3170137019Sdes			if (permitted_opens[i].host_to_connect != NULL &&
3171137019Sdes			    permitted_opens[i].port_to_connect == port &&
317276262Sgreen			    strcmp(permitted_opens[i].host_to_connect, host) == 0)
317376262Sgreen				permit = 1;
3174162856Sdes	}
317576262Sgreen
3176162856Sdes	if (num_adm_permitted_opens > 0) {
3177162856Sdes		permit_adm = 0;
3178162856Sdes		for (i = 0; i < num_adm_permitted_opens; i++)
3179162856Sdes			if (permitted_adm_opens[i].host_to_connect != NULL &&
3180162856Sdes			    permitted_adm_opens[i].port_to_connect == port &&
3181162856Sdes			    strcmp(permitted_adm_opens[i].host_to_connect, host)
3182162856Sdes			    == 0)
3183162856Sdes				permit_adm = 1;
318476262Sgreen	}
3185162856Sdes
3186162856Sdes	if (!permit || !permit_adm) {
3187124207Sdes		logit("Received request to connect to host %.100s port %d, "
318876262Sgreen		    "but the request was denied.", host, port);
3189181111Sdes		return NULL;
319076262Sgreen	}
3191181111Sdes	return connect_to(host, port, ctype, rname);
319276262Sgreen}
319376262Sgreen
3194137019Sdesvoid
3195137019Sdeschannel_send_window_changes(void)
3196137019Sdes{
3197137019Sdes	u_int i;
3198137019Sdes	struct winsize ws;
3199137019Sdes
3200137019Sdes	for (i = 0; i < channels_alloc; i++) {
3201147005Sdes		if (channels[i] == NULL || !channels[i]->client_tty ||
3202137019Sdes		    channels[i]->type != SSH_CHANNEL_OPEN)
3203137019Sdes			continue;
3204137019Sdes		if (ioctl(channels[i]->rfd, TIOCGWINSZ, &ws) < 0)
3205137019Sdes			continue;
3206137019Sdes		channel_request_start(i, "window-change", 0);
3207162856Sdes		packet_put_int((u_int)ws.ws_col);
3208162856Sdes		packet_put_int((u_int)ws.ws_row);
3209162856Sdes		packet_put_int((u_int)ws.ws_xpixel);
3210162856Sdes		packet_put_int((u_int)ws.ws_ypixel);
3211137019Sdes		packet_send();
3212137019Sdes	}
3213137019Sdes}
3214137019Sdes
321592559Sdes/* -- X11 forwarding */
321657429Smarkm
321757429Smarkm/*
321857429Smarkm * Creates an internet domain socket for listening for X11 connections.
321999063Sdes * Returns 0 and a suitable display number for the DISPLAY variable
322099063Sdes * stored in display_numberp , or -1 if an error occurs.
322157429Smarkm */
322292559Sdesint
322392559Sdesx11_create_display_inet(int x11_display_offset, int x11_use_localhost,
3224149753Sdes    int single_connection, u_int *display_numberp, int **chanids)
322557429Smarkm{
322692559Sdes	Channel *nc = NULL;
322757429Smarkm	int display_number, sock;
322857429Smarkm	u_short port;
322957429Smarkm	struct addrinfo hints, *ai, *aitop;
323057429Smarkm	char strport[NI_MAXSERV];
323157429Smarkm	int gaierr, n, num_socks = 0, socks[NUM_SOCKS];
323257429Smarkm
3233157019Sdes	if (chanids == NULL)
3234157019Sdes		return -1;
3235157019Sdes
323657429Smarkm	for (display_number = x11_display_offset;
323792559Sdes	    display_number < MAX_DISPLAYS;
323892559Sdes	    display_number++) {
323957429Smarkm		port = 6000 + display_number;
324057429Smarkm		memset(&hints, 0, sizeof(hints));
324157429Smarkm		hints.ai_family = IPv4or6;
324292559Sdes		hints.ai_flags = x11_use_localhost ? 0: AI_PASSIVE;
324357429Smarkm		hints.ai_socktype = SOCK_STREAM;
324457429Smarkm		snprintf(strport, sizeof strport, "%d", port);
324557429Smarkm		if ((gaierr = getaddrinfo(NULL, strport, &hints, &aitop)) != 0) {
3246181111Sdes			error("getaddrinfo: %.100s", ssh_gai_strerror(gaierr));
324792559Sdes			return -1;
324857429Smarkm		}
324957429Smarkm		for (ai = aitop; ai; ai = ai->ai_next) {
325057429Smarkm			if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6)
325157429Smarkm				continue;
3252124207Sdes			sock = socket(ai->ai_family, ai->ai_socktype,
3253124207Sdes			    ai->ai_protocol);
325457429Smarkm			if (sock < 0) {
3255207319Sdes				if ((errno != EINVAL) && (errno != EAFNOSUPPORT)
3256207319Sdes#ifdef EPFNOSUPPORT
3257207319Sdes				    && (errno != EPFNOSUPPORT)
3258207319Sdes#endif
3259207319Sdes				    ) {
326098941Sdes					error("socket: %.100s", strerror(errno));
3261137019Sdes					freeaddrinfo(aitop);
326298941Sdes					return -1;
326398941Sdes				} else {
326498941Sdes					debug("x11_create_display_inet: Socket family %d not supported",
326598941Sdes						 ai->ai_family);
326698941Sdes					continue;
326798941Sdes				}
326857429Smarkm			}
3269204917Sdes			if (ai->ai_family == AF_INET6)
3270204917Sdes				sock_set_v6only(sock);
3271181111Sdes			if (x11_use_localhost)
3272181111Sdes				channel_set_reuseaddr(sock);
327357429Smarkm			if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) {
3274124207Sdes				debug2("bind port %d: %.100s", port, strerror(errno));
327557429Smarkm				close(sock);
327698941Sdes
327757429Smarkm				for (n = 0; n < num_socks; n++) {
327857429Smarkm					close(socks[n]);
327957429Smarkm				}
328057429Smarkm				num_socks = 0;
328157429Smarkm				break;
328257429Smarkm			}
328357429Smarkm			socks[num_socks++] = sock;
328457429Smarkm			if (num_socks == NUM_SOCKS)
328557429Smarkm				break;
328657429Smarkm		}
328776262Sgreen		freeaddrinfo(aitop);
328857429Smarkm		if (num_socks > 0)
328957429Smarkm			break;
329057429Smarkm	}
329157429Smarkm	if (display_number >= MAX_DISPLAYS) {
329257429Smarkm		error("Failed to allocate internet-domain X11 display socket.");
329392559Sdes		return -1;
329457429Smarkm	}
329557429Smarkm	/* Start listening for connections on the socket. */
329657429Smarkm	for (n = 0; n < num_socks; n++) {
329757429Smarkm		sock = socks[n];
3298126273Sdes		if (listen(sock, SSH_LISTEN_BACKLOG) < 0) {
329957429Smarkm			error("listen: %.100s", strerror(errno));
330057429Smarkm			close(sock);
330192559Sdes			return -1;
330257429Smarkm		}
330357429Smarkm	}
330457429Smarkm
330557429Smarkm	/* Allocate a channel for each socket. */
3306162856Sdes	*chanids = xcalloc(num_socks + 1, sizeof(**chanids));
330757429Smarkm	for (n = 0; n < num_socks; n++) {
330857429Smarkm		sock = socks[n];
330992559Sdes		nc = channel_new("x11 listener",
331060573Skris		    SSH_CHANNEL_X11_LISTENER, sock, sock, -1,
331160573Skris		    CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT,
3312124207Sdes		    0, "X11 inet listener", 1);
331392559Sdes		nc->single_connection = single_connection;
3314157019Sdes		(*chanids)[n] = nc->self;
331557429Smarkm	}
3316157019Sdes	(*chanids)[n] = -1;
331757429Smarkm
331892559Sdes	/* Return the display number for the DISPLAY environment variable. */
331999063Sdes	*display_numberp = display_number;
332099063Sdes	return (0);
332157429Smarkm}
332257429Smarkm
332392559Sdesstatic int
3324192595Sdesconnect_local_xsocket_path(const char *pathname)
332557429Smarkm{
332657429Smarkm	int sock;
332757429Smarkm	struct sockaddr_un addr;
332857429Smarkm
332992559Sdes	sock = socket(AF_UNIX, SOCK_STREAM, 0);
333092559Sdes	if (sock < 0)
333192559Sdes		error("socket: %.100s", strerror(errno));
333292559Sdes	memset(&addr, 0, sizeof(addr));
333392559Sdes	addr.sun_family = AF_UNIX;
3334192595Sdes	strlcpy(addr.sun_path, pathname, sizeof addr.sun_path);
3335162856Sdes	if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) == 0)
333692559Sdes		return sock;
333792559Sdes	close(sock);
333857429Smarkm	error("connect %.100s: %.100s", addr.sun_path, strerror(errno));
333957429Smarkm	return -1;
334057429Smarkm}
334157429Smarkm
3342192595Sdesstatic int
3343192595Sdesconnect_local_xsocket(u_int dnr)
3344192595Sdes{
3345192595Sdes	char buf[1024];
3346192595Sdes	snprintf(buf, sizeof buf, _PATH_UNIX_X, dnr);
3347192595Sdes	return connect_local_xsocket_path(buf);
3348192595Sdes}
3349192595Sdes
335060573Skrisint
335160573Skrisx11_connect_display(void)
335257429Smarkm{
3353162856Sdes	u_int display_number;
335457429Smarkm	const char *display;
335560573Skris	char buf[1024], *cp;
335657429Smarkm	struct addrinfo hints, *ai, *aitop;
335757429Smarkm	char strport[NI_MAXSERV];
3358162856Sdes	int gaierr, sock = 0;
335957429Smarkm
336057429Smarkm	/* Try to open a socket for the local X server. */
336157429Smarkm	display = getenv("DISPLAY");
336257429Smarkm	if (!display) {
336357429Smarkm		error("DISPLAY not set.");
336460573Skris		return -1;
336557429Smarkm	}
336657429Smarkm	/*
336757429Smarkm	 * Now we decode the value of the DISPLAY variable and make a
336857429Smarkm	 * connection to the real X server.
336957429Smarkm	 */
337057429Smarkm
3371192595Sdes	/* Check if the display is from launchd. */
3372192595Sdes#ifdef __APPLE__
3373192595Sdes	if (strncmp(display, "/tmp/launch", 11) == 0) {
3374192595Sdes		sock = connect_local_xsocket_path(display);
3375192595Sdes		if (sock < 0)
3376192595Sdes			return -1;
3377192595Sdes
3378192595Sdes		/* OK, we now have a connection to the display. */
3379192595Sdes		return sock;
3380192595Sdes	}
3381192595Sdes#endif
338257429Smarkm	/*
338357429Smarkm	 * Check if it is a unix domain socket.  Unix domain displays are in
338457429Smarkm	 * one of the following formats: unix:d[.s], :d[.s], ::d[.s]
338557429Smarkm	 */
338657429Smarkm	if (strncmp(display, "unix:", 5) == 0 ||
338757429Smarkm	    display[0] == ':') {
338857429Smarkm		/* Connect to the unix domain socket. */
3389162856Sdes		if (sscanf(strrchr(display, ':') + 1, "%u", &display_number) != 1) {
339057429Smarkm			error("Could not parse display number from DISPLAY: %.100s",
339192559Sdes			    display);
339260573Skris			return -1;
339357429Smarkm		}
339457429Smarkm		/* Create a socket. */
339557429Smarkm		sock = connect_local_xsocket(display_number);
339657429Smarkm		if (sock < 0)
339760573Skris			return -1;
339857429Smarkm
339957429Smarkm		/* OK, we now have a connection to the display. */
340060573Skris		return sock;
340157429Smarkm	}
340257429Smarkm	/*
340357429Smarkm	 * Connect to an inet socket.  The DISPLAY value is supposedly
340457429Smarkm	 * hostname:d[.s], where hostname may also be numeric IP address.
340557429Smarkm	 */
340692559Sdes	strlcpy(buf, display, sizeof(buf));
340757429Smarkm	cp = strchr(buf, ':');
340857429Smarkm	if (!cp) {
340957429Smarkm		error("Could not find ':' in DISPLAY: %.100s", display);
341060573Skris		return -1;
341157429Smarkm	}
341257429Smarkm	*cp = 0;
341357429Smarkm	/* buf now contains the host name.  But first we parse the display number. */
3414162856Sdes	if (sscanf(cp + 1, "%u", &display_number) != 1) {
341557429Smarkm		error("Could not parse display number from DISPLAY: %.100s",
341692559Sdes		    display);
341760573Skris		return -1;
341857429Smarkm	}
341957429Smarkm
342057429Smarkm	/* Look up the host address */
342157429Smarkm	memset(&hints, 0, sizeof(hints));
342257429Smarkm	hints.ai_family = IPv4or6;
342357429Smarkm	hints.ai_socktype = SOCK_STREAM;
3424162856Sdes	snprintf(strport, sizeof strport, "%u", 6000 + display_number);
342557429Smarkm	if ((gaierr = getaddrinfo(buf, strport, &hints, &aitop)) != 0) {
3426181111Sdes		error("%.100s: unknown host. (%s)", buf,
3427181111Sdes		ssh_gai_strerror(gaierr));
342860573Skris		return -1;
342957429Smarkm	}
343057429Smarkm	for (ai = aitop; ai; ai = ai->ai_next) {
343157429Smarkm		/* Create a socket. */
3432124207Sdes		sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
343357429Smarkm		if (sock < 0) {
3434124207Sdes			debug2("socket: %.100s", strerror(errno));
343560573Skris			continue;
343660573Skris		}
343760573Skris		/* Connect it to the display. */
343860573Skris		if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0) {
3439162856Sdes			debug2("connect %.100s port %u: %.100s", buf,
344060573Skris			    6000 + display_number, strerror(errno));
344160573Skris			close(sock);
344260573Skris			continue;
344360573Skris		}
344460573Skris		/* Success */
344560573Skris		break;
344657429Smarkm	}
344757429Smarkm	freeaddrinfo(aitop);
344857429Smarkm	if (!ai) {
3449162856Sdes		error("connect %.100s port %u: %.100s", buf, 6000 + display_number,
345057429Smarkm		    strerror(errno));
345160573Skris		return -1;
345257429Smarkm	}
345392559Sdes	set_nodelay(sock);
345460573Skris	return sock;
345560573Skris}
345657429Smarkm
345760573Skris/*
345860573Skris * This is called when SSH_SMSG_X11_OPEN is received.  The packet contains
345960573Skris * the remote channel number.  We should do whatever we want, and respond
346060573Skris * with either SSH_MSG_OPEN_CONFIRMATION or SSH_MSG_OPEN_FAILURE.
346160573Skris */
346257429Smarkm
3463162856Sdes/* ARGSUSED */
346460573Skrisvoid
346592559Sdesx11_input_open(int type, u_int32_t seq, void *ctxt)
346660573Skris{
346792559Sdes	Channel *c = NULL;
346892559Sdes	int remote_id, sock = 0;
346960573Skris	char *remote_host;
347057429Smarkm
347192559Sdes	debug("Received X11 open request.");
347257429Smarkm
347392559Sdes	remote_id = packet_get_int();
347492559Sdes
347592559Sdes	if (packet_get_protocol_flags() & SSH_PROTOFLAG_HOST_IN_FWD_OPEN) {
347692559Sdes		remote_host = packet_get_string(NULL);
347760573Skris	} else {
347860573Skris		remote_host = xstrdup("unknown (remote did not supply name)");
347960573Skris	}
348092559Sdes	packet_check_eom();
348160573Skris
348260573Skris	/* Obtain a connection to the real X display. */
348360573Skris	sock = x11_connect_display();
348492559Sdes	if (sock != -1) {
348592559Sdes		/* Allocate a channel for this connection. */
348692559Sdes		c = channel_new("connected x11 socket",
348792559Sdes		    SSH_CHANNEL_X11_OPEN, sock, sock, -1, 0, 0, 0,
348892559Sdes		    remote_host, 1);
348992559Sdes		c->remote_id = remote_id;
349092559Sdes		c->force_drain = 1;
349192559Sdes	}
3492124207Sdes	xfree(remote_host);
349392559Sdes	if (c == NULL) {
349460573Skris		/* Send refusal to the remote host. */
349560573Skris		packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE);
349692559Sdes		packet_put_int(remote_id);
349760573Skris	} else {
349860573Skris		/* Send a confirmation to the remote host. */
349960573Skris		packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION);
350092559Sdes		packet_put_int(remote_id);
350192559Sdes		packet_put_int(c->self);
350260573Skris	}
350392559Sdes	packet_send();
350457429Smarkm}
350557429Smarkm
350669587Sgreen/* dummy protocol handler that denies SSH-1 requests (agent/x11) */
3507162856Sdes/* ARGSUSED */
350869587Sgreenvoid
350992559Sdesdeny_input_open(int type, u_int32_t seq, void *ctxt)
351069587Sgreen{
351169587Sgreen	int rchan = packet_get_int();
3512106130Sdes
351392559Sdes	switch (type) {
351469587Sgreen	case SSH_SMSG_AGENT_OPEN:
351569587Sgreen		error("Warning: ssh server tried agent forwarding.");
351669587Sgreen		break;
351769587Sgreen	case SSH_SMSG_X11_OPEN:
351869587Sgreen		error("Warning: ssh server tried X11 forwarding.");
351969587Sgreen		break;
352069587Sgreen	default:
352192559Sdes		error("deny_input_open: type %d", type);
352269587Sgreen		break;
352369587Sgreen	}
3524157019Sdes	error("Warning: this is probably a break-in attempt by a malicious server.");
352569587Sgreen	packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE);
352669587Sgreen	packet_put_int(rchan);
352769587Sgreen	packet_send();
352869587Sgreen}
352969587Sgreen
353057429Smarkm/*
353157429Smarkm * Requests forwarding of X11 connections, generates fake authentication
353257429Smarkm * data, and enables authentication spoofing.
353392559Sdes * This should be called in the client only.
353457429Smarkm */
353560573Skrisvoid
3536149753Sdesx11_request_forwarding_with_spoofing(int client_session_id, const char *disp,
353760573Skris    const char *proto, const char *data)
353857429Smarkm{
353976262Sgreen	u_int data_len = (u_int) strlen(data) / 2;
3540149753Sdes	u_int i, value;
354157429Smarkm	char *new_data;
354257429Smarkm	int screen_number;
354357429Smarkm	const char *cp;
3544137019Sdes	u_int32_t rnd = 0;
354557429Smarkm
3546149753Sdes	if (x11_saved_display == NULL)
3547149753Sdes		x11_saved_display = xstrdup(disp);
3548149753Sdes	else if (strcmp(disp, x11_saved_display) != 0) {
3549149753Sdes		error("x11_request_forwarding_with_spoofing: different "
3550149753Sdes		    "$DISPLAY already forwarded");
3551149753Sdes		return;
3552149753Sdes	}
3553149753Sdes
3554162856Sdes	cp = strchr(disp, ':');
355557429Smarkm	if (cp)
355657429Smarkm		cp = strchr(cp, '.');
355757429Smarkm	if (cp)
3558162856Sdes		screen_number = (u_int)strtonum(cp + 1, 0, 400, NULL);
355957429Smarkm	else
356057429Smarkm		screen_number = 0;
356157429Smarkm
3562149753Sdes	if (x11_saved_proto == NULL) {
3563149753Sdes		/* Save protocol name. */
3564149753Sdes		x11_saved_proto = xstrdup(proto);
3565149753Sdes		/*
3566149753Sdes		 * Extract real authentication data and generate fake data
3567149753Sdes		 * of the same length.
3568149753Sdes		 */
3569149753Sdes		x11_saved_data = xmalloc(data_len);
3570149753Sdes		x11_fake_data = xmalloc(data_len);
3571149753Sdes		for (i = 0; i < data_len; i++) {
3572149753Sdes			if (sscanf(data + 2 * i, "%2x", &value) != 1)
3573149753Sdes				fatal("x11_request_forwarding: bad "
3574149753Sdes				    "authentication data: %.100s", data);
3575149753Sdes			if (i % 4 == 0)
3576149753Sdes				rnd = arc4random();
3577149753Sdes			x11_saved_data[i] = value;
3578149753Sdes			x11_fake_data[i] = rnd & 0xff;
3579149753Sdes			rnd >>= 8;
3580149753Sdes		}
3581149753Sdes		x11_saved_data_len = data_len;
3582149753Sdes		x11_fake_data_len = data_len;
358357429Smarkm	}
358457429Smarkm
358557429Smarkm	/* Convert the fake data into hex. */
3586149753Sdes	new_data = tohex(x11_fake_data, data_len);
358757429Smarkm
358857429Smarkm	/* Send the request packet. */
358960573Skris	if (compat20) {
359060573Skris		channel_request_start(client_session_id, "x11-req", 0);
359160573Skris		packet_put_char(0);	/* XXX bool single connection */
359260573Skris	} else {
359360573Skris		packet_start(SSH_CMSG_X11_REQUEST_FORWARDING);
359460573Skris	}
359560573Skris	packet_put_cstring(proto);
359660573Skris	packet_put_cstring(new_data);
359757429Smarkm	packet_put_int(screen_number);
359857429Smarkm	packet_send();
359957429Smarkm	packet_write_wait();
360057429Smarkm	xfree(new_data);
360157429Smarkm}
360257429Smarkm
360392559Sdes
360492559Sdes/* -- agent forwarding */
360592559Sdes
360657429Smarkm/* Sends a message to the server to request authentication fd forwarding. */
360757429Smarkm
360860573Skrisvoid
360992559Sdesauth_request_forwarding(void)
361057429Smarkm{
361157429Smarkm	packet_start(SSH_CMSG_AGENT_REQUEST_FORWARDING);
361257429Smarkm	packet_send();
361357429Smarkm	packet_write_wait();
361457429Smarkm}
3615