channels.c revision 197679
1197679Sdes/* $OpenBSD: channels.c,v 1.296 2009/05/25 06:48:00 andreas 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>
56162856Sdes#include <netdb.h>
57162856Sdes#include <stdio.h>
58162856Sdes#include <stdlib.h>
59162856Sdes#include <string.h>
60162856Sdes#include <termios.h>
61162856Sdes#include <unistd.h>
62162856Sdes#include <stdarg.h>
63162856Sdes
64181111Sdes#include "openbsd-compat/sys-queue.h"
65162856Sdes#include "xmalloc.h"
6657429Smarkm#include "ssh.h"
6776262Sgreen#include "ssh1.h"
6876262Sgreen#include "ssh2.h"
6957429Smarkm#include "packet.h"
7076262Sgreen#include "log.h"
7176262Sgreen#include "misc.h"
72162856Sdes#include "buffer.h"
7357429Smarkm#include "channels.h"
7457429Smarkm#include "compat.h"
7576262Sgreen#include "canohost.h"
7665668Skris#include "key.h"
7765668Skris#include "authfd.h"
7892559Sdes#include "pathnames.h"
7965668Skris
8092559Sdes/* -- channel core */
8157429Smarkm
8257429Smarkm/*
8357429Smarkm * Pointer to an array containing all allocated channels.  The array is
8457429Smarkm * dynamically extended as needed.
8557429Smarkm */
8692559Sdesstatic Channel **channels = NULL;
8757429Smarkm
8857429Smarkm/*
8957429Smarkm * Size of the channel array.  All slots of the array must always be
9092559Sdes * initialized (at least the type field); unused slots set to NULL
9157429Smarkm */
92137019Sdesstatic u_int channels_alloc = 0;
9357429Smarkm
9457429Smarkm/*
9557429Smarkm * Maximum file descriptor value used in any of the channels.  This is
9692559Sdes * updated in channel_new.
9757429Smarkm */
9876262Sgreenstatic int channel_max_fd = 0;
9957429Smarkm
10057429Smarkm
10192559Sdes/* -- tcp forwarding */
10257429Smarkm
10357429Smarkm/*
10457429Smarkm * Data structure for storing which hosts are permitted for forward requests.
10557429Smarkm * The local sides of any remote forwards are stored in this array to prevent
10657429Smarkm * a corrupt remote server from accessing arbitrary TCP/IP ports on our local
10757429Smarkm * network (which might be behind a firewall).
10857429Smarkm */
10957429Smarkmtypedef struct {
11060573Skris	char *host_to_connect;		/* Connect to 'host'. */
11160573Skris	u_short port_to_connect;	/* Connect to 'port'. */
11260573Skris	u_short listen_port;		/* Remote side should listen port number. */
11357429Smarkm} ForwardPermission;
11457429Smarkm
115162856Sdes/* List of all permitted host/port pairs to connect by the user. */
11657429Smarkmstatic ForwardPermission permitted_opens[SSH_MAX_FORWARDS_PER_DIRECTION];
11792559Sdes
118162856Sdes/* List of all permitted host/port pairs to connect by the admin. */
119162856Sdesstatic ForwardPermission permitted_adm_opens[SSH_MAX_FORWARDS_PER_DIRECTION];
120162856Sdes
121162856Sdes/* Number of permitted host/port pairs in the array permitted by the user. */
12257429Smarkmstatic int num_permitted_opens = 0;
123162856Sdes
124162856Sdes/* Number of permitted host/port pair in the array permitted by the admin. */
125162856Sdesstatic int num_adm_permitted_opens = 0;
126162856Sdes
12757429Smarkm/*
12857429Smarkm * If this is true, all opens are permitted.  This is the case on the server
12957429Smarkm * on which we have to trust the client anyway, and the user could do
13057429Smarkm * anything after logging in anyway.
13157429Smarkm */
13257429Smarkmstatic int all_opens_permitted = 0;
13357429Smarkm
13457429Smarkm
13592559Sdes/* -- X11 forwarding */
13692559Sdes
13792559Sdes/* Maximum number of fake X11 displays to try. */
13892559Sdes#define MAX_DISPLAYS  1000
13992559Sdes
140149753Sdes/* Saved X11 local (client) display. */
141149753Sdesstatic char *x11_saved_display = NULL;
142149753Sdes
14392559Sdes/* Saved X11 authentication protocol name. */
14492559Sdesstatic char *x11_saved_proto = NULL;
14592559Sdes
14692559Sdes/* Saved X11 authentication data.  This is the real data. */
14792559Sdesstatic char *x11_saved_data = NULL;
14892559Sdesstatic u_int x11_saved_data_len = 0;
14992559Sdes
15092559Sdes/*
15192559Sdes * Fake X11 authentication data.  This is what the server will be sending us;
15292559Sdes * we should replace any occurrences of this by the real data.
15392559Sdes */
154162856Sdesstatic u_char *x11_fake_data = NULL;
15592559Sdesstatic u_int x11_fake_data_len;
15692559Sdes
15792559Sdes
15892559Sdes/* -- agent forwarding */
15992559Sdes
16092559Sdes#define	NUM_SOCKS	10
16192559Sdes
16276262Sgreen/* AF_UNSPEC or AF_INET or AF_INET6 */
16398941Sdesstatic int IPv4or6 = AF_UNSPEC;
16476262Sgreen
16592559Sdes/* helper */
16692559Sdesstatic void port_open_helper(Channel *c, char *rtype);
16776262Sgreen
168181111Sdes/* non-blocking connect helpers */
169181111Sdesstatic int connect_next(struct channel_connect *);
170181111Sdesstatic void channel_connect_ctx_free(struct channel_connect *);
171181111Sdes
17292559Sdes/* -- channel core */
17357429Smarkm
17460573SkrisChannel *
175157019Sdeschannel_by_id(int id)
17660573Skris{
17760573Skris	Channel *c;
17892559Sdes
179137019Sdes	if (id < 0 || (u_int)id >= channels_alloc) {
180157019Sdes		logit("channel_by_id: %d: bad id", id);
18160573Skris		return NULL;
18260573Skris	}
18392559Sdes	c = channels[id];
18492559Sdes	if (c == NULL) {
185157019Sdes		logit("channel_by_id: %d: bad id: channel free", id);
18660573Skris		return NULL;
18760573Skris	}
18860573Skris	return c;
18960573Skris}
19060573Skris
19157429Smarkm/*
192157019Sdes * Returns the channel if it is allowed to receive protocol messages.
193157019Sdes * Private channels, like listening sockets, may not receive messages.
194157019Sdes */
195157019SdesChannel *
196157019Sdeschannel_lookup(int id)
197157019Sdes{
198157019Sdes	Channel *c;
199157019Sdes
200157019Sdes	if ((c = channel_by_id(id)) == NULL)
201157019Sdes		return (NULL);
202157019Sdes
203162856Sdes	switch (c->type) {
204157019Sdes	case SSH_CHANNEL_X11_OPEN:
205157019Sdes	case SSH_CHANNEL_LARVAL:
206157019Sdes	case SSH_CHANNEL_CONNECTING:
207157019Sdes	case SSH_CHANNEL_DYNAMIC:
208157019Sdes	case SSH_CHANNEL_OPENING:
209157019Sdes	case SSH_CHANNEL_OPEN:
210157019Sdes	case SSH_CHANNEL_INPUT_DRAINING:
211157019Sdes	case SSH_CHANNEL_OUTPUT_DRAINING:
212157019Sdes		return (c);
213157019Sdes	}
214157019Sdes	logit("Non-public channel %d, type %d.", id, c->type);
215157019Sdes	return (NULL);
216157019Sdes}
217157019Sdes
218157019Sdes/*
21960573Skris * Register filedescriptors for a channel, used when allocating a channel or
22060573Skris * when the channel consumer/producer is ready, e.g. shell exec'd
22160573Skris */
22292559Sdesstatic void
22369587Sgreenchannel_register_fds(Channel *c, int rfd, int wfd, int efd,
224181111Sdes    int extusage, int nonblock, int is_tty)
22560573Skris{
22660573Skris	/* Update the maximum file descriptor value. */
22776262Sgreen	channel_max_fd = MAX(channel_max_fd, rfd);
22876262Sgreen	channel_max_fd = MAX(channel_max_fd, wfd);
22976262Sgreen	channel_max_fd = MAX(channel_max_fd, efd);
23076262Sgreen
23160573Skris	/* XXX set close-on-exec -markus */
23260573Skris
23360573Skris	c->rfd = rfd;
23460573Skris	c->wfd = wfd;
23560573Skris	c->sock = (rfd == wfd) ? rfd : -1;
236137019Sdes	c->ctl_fd = -1; /* XXX: set elsewhere */
23760573Skris	c->efd = efd;
23860573Skris	c->extended_usage = extusage;
23969587Sgreen
240181111Sdes	if ((c->isatty = is_tty) != 0)
241124207Sdes		debug2("channel %d: rfd %d isatty", c->self, c->rfd);
242181111Sdes	c->wfd_isatty = is_tty || isatty(c->wfd);
24374500Sgreen
24469587Sgreen	/* enable nonblocking mode */
24569587Sgreen	if (nonblock) {
24669587Sgreen		if (rfd != -1)
24769587Sgreen			set_nonblock(rfd);
24869587Sgreen		if (wfd != -1)
24969587Sgreen			set_nonblock(wfd);
25069587Sgreen		if (efd != -1)
25169587Sgreen			set_nonblock(efd);
25269587Sgreen	}
25360573Skris}
25460573Skris
25560573Skris/*
25657429Smarkm * Allocate a new channel object and set its type and socket. This will cause
25757429Smarkm * remote_name to be freed.
25857429Smarkm */
25992559SdesChannel *
26060573Skrischannel_new(char *ctype, int type, int rfd, int wfd, int efd,
26199063Sdes    u_int window, u_int maxpack, int extusage, char *remote_name, int nonblock)
26257429Smarkm{
263137019Sdes	int found;
264137019Sdes	u_int i;
26557429Smarkm	Channel *c;
26657429Smarkm
26757429Smarkm	/* Do initial allocation if this is the first call. */
26857429Smarkm	if (channels_alloc == 0) {
26957429Smarkm		channels_alloc = 10;
270162856Sdes		channels = xcalloc(channels_alloc, sizeof(Channel *));
27157429Smarkm		for (i = 0; i < channels_alloc; i++)
27292559Sdes			channels[i] = NULL;
27357429Smarkm	}
27457429Smarkm	/* Try to find a free slot where to put the new channel. */
27557429Smarkm	for (found = -1, i = 0; i < channels_alloc; i++)
27692559Sdes		if (channels[i] == NULL) {
27757429Smarkm			/* Found a free slot. */
278137019Sdes			found = (int)i;
27957429Smarkm			break;
28057429Smarkm		}
281137019Sdes	if (found < 0) {
28257429Smarkm		/* There are no free slots.  Take last+1 slot and expand the array.  */
28357429Smarkm		found = channels_alloc;
28499063Sdes		if (channels_alloc > 10000)
28599063Sdes			fatal("channel_new: internal error: channels_alloc %d "
28699063Sdes			    "too big.", channels_alloc);
287162856Sdes		channels = xrealloc(channels, channels_alloc + 10,
288162856Sdes		    sizeof(Channel *));
289120489Sjoe		channels_alloc += 10;
29069587Sgreen		debug2("channel: expanding %d", channels_alloc);
29157429Smarkm		for (i = found; i < channels_alloc; i++)
29292559Sdes			channels[i] = NULL;
29357429Smarkm	}
29492559Sdes	/* Initialize and return new channel. */
295162856Sdes	c = channels[found] = xcalloc(1, sizeof(Channel));
29657429Smarkm	buffer_init(&c->input);
29757429Smarkm	buffer_init(&c->output);
29860573Skris	buffer_init(&c->extended);
299192595Sdes	c->path = NULL;
30092559Sdes	c->ostate = CHAN_OUTPUT_OPEN;
30192559Sdes	c->istate = CHAN_INPUT_OPEN;
30292559Sdes	c->flags = 0;
303181111Sdes	channel_register_fds(c, rfd, wfd, efd, extusage, nonblock, 0);
30457429Smarkm	c->self = found;
30557429Smarkm	c->type = type;
30660573Skris	c->ctype = ctype;
30760573Skris	c->local_window = window;
30860573Skris	c->local_window_max = window;
30960573Skris	c->local_consumed = 0;
31060573Skris	c->local_maxpacket = maxpack;
31157429Smarkm	c->remote_id = -1;
312124207Sdes	c->remote_name = xstrdup(remote_name);
31360573Skris	c->remote_window = 0;
31460573Skris	c->remote_maxpacket = 0;
31592559Sdes	c->force_drain = 0;
31692559Sdes	c->single_connection = 0;
31792559Sdes	c->detach_user = NULL;
318157019Sdes	c->detach_close = 0;
319181111Sdes	c->open_confirm = NULL;
320181111Sdes	c->open_confirm_ctx = NULL;
32165668Skris	c->input_filter = NULL;
322157019Sdes	c->output_filter = NULL;
323181111Sdes	c->filter_ctx = NULL;
324181111Sdes	c->filter_cleanup = NULL;
325181111Sdes	TAILQ_INIT(&c->status_confirms);
32657429Smarkm	debug("channel %d: new [%s]", found, remote_name);
32792559Sdes	return c;
32857429Smarkm}
32992559Sdes
33092559Sdesstatic int
33192559Sdeschannel_find_maxfd(void)
33292559Sdes{
333137019Sdes	u_int i;
334137019Sdes	int max = 0;
33592559Sdes	Channel *c;
33692559Sdes
33792559Sdes	for (i = 0; i < channels_alloc; i++) {
33892559Sdes		c = channels[i];
33992559Sdes		if (c != NULL) {
34092559Sdes			max = MAX(max, c->rfd);
34192559Sdes			max = MAX(max, c->wfd);
34292559Sdes			max = MAX(max, c->efd);
34392559Sdes		}
34492559Sdes	}
34592559Sdes	return max;
34692559Sdes}
34792559Sdes
34860573Skrisint
34992559Sdeschannel_close_fd(int *fdp)
35060573Skris{
35192559Sdes	int ret = 0, fd = *fdp;
35292559Sdes
35392559Sdes	if (fd != -1) {
35492559Sdes		ret = close(fd);
35592559Sdes		*fdp = -1;
35692559Sdes		if (fd == channel_max_fd)
35792559Sdes			channel_max_fd = channel_find_maxfd();
35892559Sdes	}
35992559Sdes	return ret;
36060573Skris}
36157429Smarkm
36260573Skris/* Close all channel fd/socket. */
36392559Sdesstatic void
36460573Skrischannel_close_fds(Channel *c)
36557429Smarkm{
366137019Sdes	debug3("channel %d: close_fds r %d w %d e %d c %d",
367137019Sdes	    c->self, c->rfd, c->wfd, c->efd, c->ctl_fd);
36892559Sdes
36992559Sdes	channel_close_fd(&c->sock);
370137019Sdes	channel_close_fd(&c->ctl_fd);
37192559Sdes	channel_close_fd(&c->rfd);
37292559Sdes	channel_close_fd(&c->wfd);
37392559Sdes	channel_close_fd(&c->efd);
37460573Skris}
37557429Smarkm
37660573Skris/* Free the channel and close its fd/socket. */
37760573Skrisvoid
37892559Sdeschannel_free(Channel *c)
37960573Skris{
38092559Sdes	char *s;
381137019Sdes	u_int i, n;
382181111Sdes	struct channel_confirm *cc;
38376262Sgreen
38492559Sdes	for (n = 0, i = 0; i < channels_alloc; i++)
38592559Sdes		if (channels[i])
38692559Sdes			n++;
387137019Sdes	debug("channel %d: free: %s, nchannels %u", c->self,
38892559Sdes	    c->remote_name ? c->remote_name : "???", n);
38992559Sdes
39092559Sdes	s = channel_open_message();
391124207Sdes	debug3("channel %d: status: %s", c->self, s);
39276262Sgreen	xfree(s);
39376262Sgreen
39460573Skris	if (c->sock != -1)
39560573Skris		shutdown(c->sock, SHUT_RDWR);
396137019Sdes	if (c->ctl_fd != -1)
397137019Sdes		shutdown(c->ctl_fd, SHUT_RDWR);
39860573Skris	channel_close_fds(c);
39960573Skris	buffer_free(&c->input);
40060573Skris	buffer_free(&c->output);
40160573Skris	buffer_free(&c->extended);
40260573Skris	if (c->remote_name) {
40360573Skris		xfree(c->remote_name);
40460573Skris		c->remote_name = NULL;
40560573Skris	}
406192595Sdes	if (c->path) {
407192595Sdes		xfree(c->path);
408192595Sdes		c->path = NULL;
409192595Sdes	}
410181111Sdes	while ((cc = TAILQ_FIRST(&c->status_confirms)) != NULL) {
411181111Sdes		if (cc->abandon_cb != NULL)
412181111Sdes			cc->abandon_cb(c, cc->ctx);
413181111Sdes		TAILQ_REMOVE(&c->status_confirms, cc, entry);
414181111Sdes		bzero(cc, sizeof(*cc));
415181111Sdes		xfree(cc);
416181111Sdes	}
417181111Sdes	if (c->filter_cleanup != NULL && c->filter_ctx != NULL)
418181111Sdes		c->filter_cleanup(c->self, c->filter_ctx);
41992559Sdes	channels[c->self] = NULL;
42092559Sdes	xfree(c);
42157429Smarkm}
42257429Smarkm
42392559Sdesvoid
42492559Sdeschannel_free_all(void)
42592559Sdes{
426137019Sdes	u_int i;
42792559Sdes
42892559Sdes	for (i = 0; i < channels_alloc; i++)
42992559Sdes		if (channels[i] != NULL)
43092559Sdes			channel_free(channels[i]);
43192559Sdes}
43292559Sdes
43357429Smarkm/*
43492559Sdes * Closes the sockets/fds of all channels.  This is used to close extra file
43592559Sdes * descriptors after a fork.
43692559Sdes */
43792559Sdesvoid
43892559Sdeschannel_close_all(void)
43992559Sdes{
440137019Sdes	u_int i;
44192559Sdes
44292559Sdes	for (i = 0; i < channels_alloc; i++)
44392559Sdes		if (channels[i] != NULL)
44492559Sdes			channel_close_fds(channels[i]);
44592559Sdes}
44692559Sdes
44792559Sdes/*
44892559Sdes * Stop listening to channels.
44992559Sdes */
45092559Sdesvoid
45192559Sdeschannel_stop_listening(void)
45292559Sdes{
453137019Sdes	u_int i;
45492559Sdes	Channel *c;
45592559Sdes
45692559Sdes	for (i = 0; i < channels_alloc; i++) {
45792559Sdes		c = channels[i];
45892559Sdes		if (c != NULL) {
45992559Sdes			switch (c->type) {
46092559Sdes			case SSH_CHANNEL_AUTH_SOCKET:
46192559Sdes			case SSH_CHANNEL_PORT_LISTENER:
46292559Sdes			case SSH_CHANNEL_RPORT_LISTENER:
46392559Sdes			case SSH_CHANNEL_X11_LISTENER:
46492559Sdes				channel_close_fd(&c->sock);
46592559Sdes				channel_free(c);
46692559Sdes				break;
46792559Sdes			}
46892559Sdes		}
46992559Sdes	}
47092559Sdes}
47192559Sdes
47292559Sdes/*
47392559Sdes * Returns true if no channel has too much buffered data, and false if one or
47492559Sdes * more channel is overfull.
47592559Sdes */
47692559Sdesint
47792559Sdeschannel_not_very_much_buffered_data(void)
47892559Sdes{
47992559Sdes	u_int i;
48092559Sdes	Channel *c;
48192559Sdes
48292559Sdes	for (i = 0; i < channels_alloc; i++) {
48392559Sdes		c = channels[i];
48492559Sdes		if (c != NULL && c->type == SSH_CHANNEL_OPEN) {
48592559Sdes#if 0
48692559Sdes			if (!compat20 &&
48792559Sdes			    buffer_len(&c->input) > packet_get_maxsize()) {
488113911Sdes				debug2("channel %d: big input buffer %d",
48992559Sdes				    c->self, buffer_len(&c->input));
49092559Sdes				return 0;
49192559Sdes			}
49292559Sdes#endif
49392559Sdes			if (buffer_len(&c->output) > packet_get_maxsize()) {
494124207Sdes				debug2("channel %d: big output buffer %u > %u",
49592559Sdes				    c->self, buffer_len(&c->output),
49692559Sdes				    packet_get_maxsize());
49792559Sdes				return 0;
49892559Sdes			}
49992559Sdes		}
50092559Sdes	}
50192559Sdes	return 1;
50292559Sdes}
50392559Sdes
50492559Sdes/* Returns true if any channel is still open. */
50592559Sdesint
50692559Sdeschannel_still_open(void)
50792559Sdes{
508137019Sdes	u_int i;
50992559Sdes	Channel *c;
51092559Sdes
51192559Sdes	for (i = 0; i < channels_alloc; i++) {
51292559Sdes		c = channels[i];
51392559Sdes		if (c == NULL)
51492559Sdes			continue;
51592559Sdes		switch (c->type) {
51692559Sdes		case SSH_CHANNEL_X11_LISTENER:
51792559Sdes		case SSH_CHANNEL_PORT_LISTENER:
51892559Sdes		case SSH_CHANNEL_RPORT_LISTENER:
51992559Sdes		case SSH_CHANNEL_CLOSED:
52092559Sdes		case SSH_CHANNEL_AUTH_SOCKET:
52192559Sdes		case SSH_CHANNEL_DYNAMIC:
52292559Sdes		case SSH_CHANNEL_CONNECTING:
52392559Sdes		case SSH_CHANNEL_ZOMBIE:
52492559Sdes			continue;
52592559Sdes		case SSH_CHANNEL_LARVAL:
52692559Sdes			if (!compat20)
52792559Sdes				fatal("cannot happen: SSH_CHANNEL_LARVAL");
52892559Sdes			continue;
52992559Sdes		case SSH_CHANNEL_OPENING:
53092559Sdes		case SSH_CHANNEL_OPEN:
53192559Sdes		case SSH_CHANNEL_X11_OPEN:
53292559Sdes			return 1;
53392559Sdes		case SSH_CHANNEL_INPUT_DRAINING:
53492559Sdes		case SSH_CHANNEL_OUTPUT_DRAINING:
53592559Sdes			if (!compat13)
53692559Sdes				fatal("cannot happen: OUT_DRAIN");
53792559Sdes			return 1;
53892559Sdes		default:
53992559Sdes			fatal("channel_still_open: bad channel type %d", c->type);
54092559Sdes			/* NOTREACHED */
54192559Sdes		}
54292559Sdes	}
54392559Sdes	return 0;
54492559Sdes}
54592559Sdes
54692559Sdes/* Returns the id of an open channel suitable for keepaliving */
54792559Sdesint
54892559Sdeschannel_find_open(void)
54992559Sdes{
550137019Sdes	u_int i;
55192559Sdes	Channel *c;
55292559Sdes
55392559Sdes	for (i = 0; i < channels_alloc; i++) {
55492559Sdes		c = channels[i];
555137019Sdes		if (c == NULL || c->remote_id < 0)
55692559Sdes			continue;
55792559Sdes		switch (c->type) {
55892559Sdes		case SSH_CHANNEL_CLOSED:
55992559Sdes		case SSH_CHANNEL_DYNAMIC:
56092559Sdes		case SSH_CHANNEL_X11_LISTENER:
56192559Sdes		case SSH_CHANNEL_PORT_LISTENER:
56292559Sdes		case SSH_CHANNEL_RPORT_LISTENER:
56392559Sdes		case SSH_CHANNEL_OPENING:
56492559Sdes		case SSH_CHANNEL_CONNECTING:
56592559Sdes		case SSH_CHANNEL_ZOMBIE:
56692559Sdes			continue;
56792559Sdes		case SSH_CHANNEL_LARVAL:
56892559Sdes		case SSH_CHANNEL_AUTH_SOCKET:
56992559Sdes		case SSH_CHANNEL_OPEN:
57092559Sdes		case SSH_CHANNEL_X11_OPEN:
57192559Sdes			return i;
57292559Sdes		case SSH_CHANNEL_INPUT_DRAINING:
57392559Sdes		case SSH_CHANNEL_OUTPUT_DRAINING:
57492559Sdes			if (!compat13)
57592559Sdes				fatal("cannot happen: OUT_DRAIN");
57692559Sdes			return i;
57792559Sdes		default:
57892559Sdes			fatal("channel_find_open: bad channel type %d", c->type);
57992559Sdes			/* NOTREACHED */
58092559Sdes		}
58192559Sdes	}
58292559Sdes	return -1;
58392559Sdes}
58492559Sdes
58592559Sdes
58692559Sdes/*
58792559Sdes * Returns a message describing the currently open forwarded connections,
58892559Sdes * suitable for sending to the client.  The message contains crlf pairs for
58992559Sdes * newlines.
59092559Sdes */
59192559Sdeschar *
59292559Sdeschannel_open_message(void)
59392559Sdes{
59492559Sdes	Buffer buffer;
59592559Sdes	Channel *c;
59692559Sdes	char buf[1024], *cp;
597137019Sdes	u_int i;
59892559Sdes
59992559Sdes	buffer_init(&buffer);
60092559Sdes	snprintf(buf, sizeof buf, "The following connections are open:\r\n");
60192559Sdes	buffer_append(&buffer, buf, strlen(buf));
60292559Sdes	for (i = 0; i < channels_alloc; i++) {
60392559Sdes		c = channels[i];
60492559Sdes		if (c == NULL)
60592559Sdes			continue;
60692559Sdes		switch (c->type) {
60792559Sdes		case SSH_CHANNEL_X11_LISTENER:
60892559Sdes		case SSH_CHANNEL_PORT_LISTENER:
60992559Sdes		case SSH_CHANNEL_RPORT_LISTENER:
61092559Sdes		case SSH_CHANNEL_CLOSED:
61192559Sdes		case SSH_CHANNEL_AUTH_SOCKET:
61292559Sdes		case SSH_CHANNEL_ZOMBIE:
61392559Sdes			continue;
61492559Sdes		case SSH_CHANNEL_LARVAL:
61592559Sdes		case SSH_CHANNEL_OPENING:
61692559Sdes		case SSH_CHANNEL_CONNECTING:
61792559Sdes		case SSH_CHANNEL_DYNAMIC:
61892559Sdes		case SSH_CHANNEL_OPEN:
61992559Sdes		case SSH_CHANNEL_X11_OPEN:
62092559Sdes		case SSH_CHANNEL_INPUT_DRAINING:
62192559Sdes		case SSH_CHANNEL_OUTPUT_DRAINING:
622137019Sdes			snprintf(buf, sizeof buf,
623137019Sdes			    "  #%d %.300s (t%d r%d i%d/%d o%d/%d fd %d/%d cfd %d)\r\n",
62492559Sdes			    c->self, c->remote_name,
62592559Sdes			    c->type, c->remote_id,
62692559Sdes			    c->istate, buffer_len(&c->input),
62792559Sdes			    c->ostate, buffer_len(&c->output),
628137019Sdes			    c->rfd, c->wfd, c->ctl_fd);
62992559Sdes			buffer_append(&buffer, buf, strlen(buf));
63092559Sdes			continue;
63192559Sdes		default:
63292559Sdes			fatal("channel_open_message: bad channel type %d", c->type);
63392559Sdes			/* NOTREACHED */
63492559Sdes		}
63592559Sdes	}
63692559Sdes	buffer_append(&buffer, "\0", 1);
63792559Sdes	cp = xstrdup(buffer_ptr(&buffer));
63892559Sdes	buffer_free(&buffer);
63992559Sdes	return cp;
64092559Sdes}
64192559Sdes
64292559Sdesvoid
64392559Sdeschannel_send_open(int id)
64492559Sdes{
64592559Sdes	Channel *c = channel_lookup(id);
646106130Sdes
64792559Sdes	if (c == NULL) {
648124207Sdes		logit("channel_send_open: %d: bad id", id);
64992559Sdes		return;
65092559Sdes	}
651113911Sdes	debug2("channel %d: send open", id);
65292559Sdes	packet_start(SSH2_MSG_CHANNEL_OPEN);
65392559Sdes	packet_put_cstring(c->ctype);
65492559Sdes	packet_put_int(c->self);
65592559Sdes	packet_put_int(c->local_window);
65692559Sdes	packet_put_int(c->local_maxpacket);
65792559Sdes	packet_send();
65892559Sdes}
65992559Sdes
66092559Sdesvoid
661113911Sdeschannel_request_start(int id, char *service, int wantconfirm)
66292559Sdes{
663113911Sdes	Channel *c = channel_lookup(id);
664106130Sdes
66592559Sdes	if (c == NULL) {
666124207Sdes		logit("channel_request_start: %d: unknown channel id", id);
66792559Sdes		return;
66892559Sdes	}
669137019Sdes	debug2("channel %d: request %s confirm %d", id, service, wantconfirm);
67092559Sdes	packet_start(SSH2_MSG_CHANNEL_REQUEST);
67192559Sdes	packet_put_int(c->remote_id);
67292559Sdes	packet_put_cstring(service);
67392559Sdes	packet_put_char(wantconfirm);
67492559Sdes}
675162856Sdes
67692559Sdesvoid
677181111Sdeschannel_register_status_confirm(int id, channel_confirm_cb *cb,
678181111Sdes    channel_confirm_abandon_cb *abandon_cb, void *ctx)
67992559Sdes{
680181111Sdes	struct channel_confirm *cc;
681181111Sdes	Channel *c;
682181111Sdes
683181111Sdes	if ((c = channel_lookup(id)) == NULL)
684181111Sdes		fatal("channel_register_expect: %d: bad id", id);
685181111Sdes
686181111Sdes	cc = xmalloc(sizeof(*cc));
687181111Sdes	cc->cb = cb;
688181111Sdes	cc->abandon_cb = abandon_cb;
689181111Sdes	cc->ctx = ctx;
690181111Sdes	TAILQ_INSERT_TAIL(&c->status_confirms, cc, entry);
691181111Sdes}
692181111Sdes
693181111Sdesvoid
694181111Sdeschannel_register_open_confirm(int id, channel_callback_fn *fn, void *ctx)
695181111Sdes{
69692559Sdes	Channel *c = channel_lookup(id);
697106130Sdes
69892559Sdes	if (c == NULL) {
699192595Sdes		logit("channel_register_open_confirm: %d: bad id", id);
70092559Sdes		return;
70192559Sdes	}
702181111Sdes	c->open_confirm = fn;
703181111Sdes	c->open_confirm_ctx = ctx;
70492559Sdes}
705162856Sdes
70692559Sdesvoid
707157019Sdeschannel_register_cleanup(int id, channel_callback_fn *fn, int do_close)
70892559Sdes{
709157019Sdes	Channel *c = channel_by_id(id);
710106130Sdes
71192559Sdes	if (c == NULL) {
712124207Sdes		logit("channel_register_cleanup: %d: bad id", id);
71392559Sdes		return;
71492559Sdes	}
71592559Sdes	c->detach_user = fn;
716157019Sdes	c->detach_close = do_close;
71792559Sdes}
718162856Sdes
71992559Sdesvoid
72092559Sdeschannel_cancel_cleanup(int id)
72192559Sdes{
722157019Sdes	Channel *c = channel_by_id(id);
723106130Sdes
72492559Sdes	if (c == NULL) {
725124207Sdes		logit("channel_cancel_cleanup: %d: bad id", id);
72692559Sdes		return;
72792559Sdes	}
72892559Sdes	c->detach_user = NULL;
729157019Sdes	c->detach_close = 0;
73092559Sdes}
731162856Sdes
73292559Sdesvoid
733157019Sdeschannel_register_filter(int id, channel_infilter_fn *ifn,
734181111Sdes    channel_outfilter_fn *ofn, channel_filter_cleanup_fn *cfn, void *ctx)
73592559Sdes{
73692559Sdes	Channel *c = channel_lookup(id);
737106130Sdes
73892559Sdes	if (c == NULL) {
739124207Sdes		logit("channel_register_filter: %d: bad id", id);
74092559Sdes		return;
74192559Sdes	}
742157019Sdes	c->input_filter = ifn;
743157019Sdes	c->output_filter = ofn;
744181111Sdes	c->filter_ctx = ctx;
745181111Sdes	c->filter_cleanup = cfn;
74692559Sdes}
74792559Sdes
74892559Sdesvoid
74992559Sdeschannel_set_fds(int id, int rfd, int wfd, int efd,
750181111Sdes    int extusage, int nonblock, int is_tty, u_int window_max)
75192559Sdes{
75292559Sdes	Channel *c = channel_lookup(id);
753106130Sdes
75492559Sdes	if (c == NULL || c->type != SSH_CHANNEL_LARVAL)
75592559Sdes		fatal("channel_activate for non-larval channel %d.", id);
756181111Sdes	channel_register_fds(c, rfd, wfd, efd, extusage, nonblock, is_tty);
75792559Sdes	c->type = SSH_CHANNEL_OPEN;
75892559Sdes	c->local_window = c->local_window_max = window_max;
75992559Sdes	packet_start(SSH2_MSG_CHANNEL_WINDOW_ADJUST);
76092559Sdes	packet_put_int(c->remote_id);
76192559Sdes	packet_put_int(c->local_window);
76292559Sdes	packet_send();
76392559Sdes}
76492559Sdes
76592559Sdes/*
76660573Skris * 'channel_pre*' are called just before select() to add any bits relevant to
76760573Skris * channels in the select bitmasks.
76857429Smarkm */
76960573Skris/*
77060573Skris * 'channel_post*': perform any appropriate operations for channels which
77160573Skris * have events pending.
77260573Skris */
773162856Sdestypedef void chan_fn(Channel *c, fd_set *readset, fd_set *writeset);
77460573Skrischan_fn *channel_pre[SSH_CHANNEL_MAX_TYPE];
77560573Skrischan_fn *channel_post[SSH_CHANNEL_MAX_TYPE];
77657429Smarkm
777162856Sdes/* ARGSUSED */
77892559Sdesstatic void
779162856Sdeschannel_pre_listener(Channel *c, fd_set *readset, fd_set *writeset)
78057429Smarkm{
78160573Skris	FD_SET(c->sock, readset);
78260573Skris}
78360573Skris
784162856Sdes/* ARGSUSED */
78592559Sdesstatic void
786162856Sdeschannel_pre_connecting(Channel *c, fd_set *readset, fd_set *writeset)
78776262Sgreen{
78876262Sgreen	debug3("channel %d: waiting for connection", c->self);
78976262Sgreen	FD_SET(c->sock, writeset);
79076262Sgreen}
79176262Sgreen
79292559Sdesstatic void
793162856Sdeschannel_pre_open_13(Channel *c, fd_set *readset, fd_set *writeset)
79460573Skris{
79560573Skris	if (buffer_len(&c->input) < packet_get_maxsize())
79660573Skris		FD_SET(c->sock, readset);
79760573Skris	if (buffer_len(&c->output) > 0)
79860573Skris		FD_SET(c->sock, writeset);
79960573Skris}
80060573Skris
80192559Sdesstatic void
802162856Sdeschannel_pre_open(Channel *c, fd_set *readset, fd_set *writeset)
80360573Skris{
80492559Sdes	u_int limit = compat20 ? c->remote_window : packet_get_maxsize();
80560573Skris
80660573Skris	if (c->istate == CHAN_INPUT_OPEN &&
80792559Sdes	    limit > 0 &&
808162856Sdes	    buffer_len(&c->input) < limit &&
809162856Sdes	    buffer_check_alloc(&c->input, CHAN_RBUF))
81060573Skris		FD_SET(c->rfd, readset);
81160573Skris	if (c->ostate == CHAN_OUTPUT_OPEN ||
81260573Skris	    c->ostate == CHAN_OUTPUT_WAIT_DRAIN) {
81360573Skris		if (buffer_len(&c->output) > 0) {
81460573Skris			FD_SET(c->wfd, writeset);
81560573Skris		} else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN) {
81698684Sdes			if (CHANNEL_EFD_OUTPUT_ACTIVE(c))
817149753Sdes				debug2("channel %d: obuf_empty delayed efd %d/(%d)",
818149753Sdes				    c->self, c->efd, buffer_len(&c->extended));
81998684Sdes			else
82098684Sdes				chan_obuf_empty(c);
82160573Skris		}
82260573Skris	}
82360573Skris	/** XXX check close conditions, too */
824181111Sdes	if (compat20 && c->efd != -1 &&
825181111Sdes	    !(c->istate == CHAN_INPUT_CLOSED && c->ostate == CHAN_OUTPUT_CLOSED)) {
82660573Skris		if (c->extended_usage == CHAN_EXTENDED_WRITE &&
82760573Skris		    buffer_len(&c->extended) > 0)
82860573Skris			FD_SET(c->efd, writeset);
82998684Sdes		else if (!(c->flags & CHAN_EOF_SENT) &&
83098684Sdes		    c->extended_usage == CHAN_EXTENDED_READ &&
83160573Skris		    buffer_len(&c->extended) < c->remote_window)
83260573Skris			FD_SET(c->efd, readset);
83360573Skris	}
834137019Sdes	/* XXX: What about efd? races? */
835137019Sdes	if (compat20 && c->ctl_fd != -1 &&
836137019Sdes	    c->istate == CHAN_INPUT_OPEN && c->ostate == CHAN_OUTPUT_OPEN)
837137019Sdes		FD_SET(c->ctl_fd, readset);
83860573Skris}
83960573Skris
840162856Sdes/* ARGSUSED */
84192559Sdesstatic void
842162856Sdeschannel_pre_input_draining(Channel *c, fd_set *readset, fd_set *writeset)
84360573Skris{
84460573Skris	if (buffer_len(&c->input) == 0) {
84560573Skris		packet_start(SSH_MSG_CHANNEL_CLOSE);
84660573Skris		packet_put_int(c->remote_id);
84760573Skris		packet_send();
84860573Skris		c->type = SSH_CHANNEL_CLOSED;
849124207Sdes		debug2("channel %d: closing after input drain.", c->self);
85060573Skris	}
85160573Skris}
85260573Skris
853162856Sdes/* ARGSUSED */
85492559Sdesstatic void
855162856Sdeschannel_pre_output_draining(Channel *c, fd_set *readset, fd_set *writeset)
85660573Skris{
85760573Skris	if (buffer_len(&c->output) == 0)
85892559Sdes		chan_mark_dead(c);
85960573Skris	else
86060573Skris		FD_SET(c->sock, writeset);
86160573Skris}
86260573Skris
86360573Skris/*
86460573Skris * This is a special state for X11 authentication spoofing.  An opened X11
86560573Skris * connection (when authentication spoofing is being done) remains in this
86660573Skris * state until the first packet has been completely read.  The authentication
86760573Skris * data in that packet is then substituted by the real data if it matches the
86860573Skris * fake data, and the channel is put into normal mode.
86960573Skris * XXX All this happens at the client side.
87092559Sdes * Returns: 0 = need more data, -1 = wrong cookie, 1 = ok
87160573Skris */
87292559Sdesstatic int
87392559Sdesx11_open_helper(Buffer *b)
87460573Skris{
87576262Sgreen	u_char *ucp;
87676262Sgreen	u_int proto_len, data_len;
87757429Smarkm
87860573Skris	/* Check if the fixed size part of the packet is in buffer. */
87992559Sdes	if (buffer_len(b) < 12)
88060573Skris		return 0;
88157429Smarkm
88260573Skris	/* Parse the lengths of variable-length fields. */
88392559Sdes	ucp = buffer_ptr(b);
88460573Skris	if (ucp[0] == 0x42) {	/* Byte order MSB first. */
88560573Skris		proto_len = 256 * ucp[6] + ucp[7];
88660573Skris		data_len = 256 * ucp[8] + ucp[9];
88760573Skris	} else if (ucp[0] == 0x6c) {	/* Byte order LSB first. */
88860573Skris		proto_len = ucp[6] + 256 * ucp[7];
88960573Skris		data_len = ucp[8] + 256 * ucp[9];
89060573Skris	} else {
891124207Sdes		debug2("Initial X11 packet contains bad byte order byte: 0x%x",
89292559Sdes		    ucp[0]);
89360573Skris		return -1;
89460573Skris	}
89557429Smarkm
89660573Skris	/* Check if the whole packet is in buffer. */
89792559Sdes	if (buffer_len(b) <
89860573Skris	    12 + ((proto_len + 3) & ~3) + ((data_len + 3) & ~3))
89960573Skris		return 0;
90057429Smarkm
90160573Skris	/* Check if authentication protocol matches. */
90260573Skris	if (proto_len != strlen(x11_saved_proto) ||
90360573Skris	    memcmp(ucp + 12, x11_saved_proto, proto_len) != 0) {
904124207Sdes		debug2("X11 connection uses different authentication protocol.");
90560573Skris		return -1;
90660573Skris	}
90760573Skris	/* Check if authentication data matches our fake data. */
90860573Skris	if (data_len != x11_fake_data_len ||
90960573Skris	    memcmp(ucp + 12 + ((proto_len + 3) & ~3),
91060573Skris		x11_fake_data, x11_fake_data_len) != 0) {
911124207Sdes		debug2("X11 auth data does not match fake data.");
91260573Skris		return -1;
91360573Skris	}
91460573Skris	/* Check fake data length */
91560573Skris	if (x11_fake_data_len != x11_saved_data_len) {
91660573Skris		error("X11 fake_data_len %d != saved_data_len %d",
91760573Skris		    x11_fake_data_len, x11_saved_data_len);
91860573Skris		return -1;
91960573Skris	}
92060573Skris	/*
92160573Skris	 * Received authentication protocol and data match
92260573Skris	 * our fake data. Substitute the fake data with real
92360573Skris	 * data.
92460573Skris	 */
92560573Skris	memcpy(ucp + 12 + ((proto_len + 3) & ~3),
92660573Skris	    x11_saved_data, x11_saved_data_len);
92760573Skris	return 1;
92860573Skris}
92957429Smarkm
93092559Sdesstatic void
931162856Sdeschannel_pre_x11_open_13(Channel *c, fd_set *readset, fd_set *writeset)
93260573Skris{
93392559Sdes	int ret = x11_open_helper(&c->output);
934106130Sdes
93560573Skris	if (ret == 1) {
93660573Skris		/* Start normal processing for the channel. */
93760573Skris		c->type = SSH_CHANNEL_OPEN;
93860573Skris		channel_pre_open_13(c, readset, writeset);
93960573Skris	} else if (ret == -1) {
94060573Skris		/*
94160573Skris		 * We have received an X11 connection that has bad
94260573Skris		 * authentication information.
94360573Skris		 */
944124207Sdes		logit("X11 connection rejected because of wrong authentication.");
94560573Skris		buffer_clear(&c->input);
94660573Skris		buffer_clear(&c->output);
94792559Sdes		channel_close_fd(&c->sock);
94860573Skris		c->sock = -1;
94960573Skris		c->type = SSH_CHANNEL_CLOSED;
95060573Skris		packet_start(SSH_MSG_CHANNEL_CLOSE);
95160573Skris		packet_put_int(c->remote_id);
95260573Skris		packet_send();
95360573Skris	}
95460573Skris}
95557429Smarkm
95692559Sdesstatic void
957162856Sdeschannel_pre_x11_open(Channel *c, fd_set *readset, fd_set *writeset)
95860573Skris{
95992559Sdes	int ret = x11_open_helper(&c->output);
96092559Sdes
96192559Sdes	/* c->force_drain = 1; */
96292559Sdes
96360573Skris	if (ret == 1) {
96460573Skris		c->type = SSH_CHANNEL_OPEN;
96592559Sdes		channel_pre_open(c, readset, writeset);
96692559Sdes	} else if (ret == -1) {
967124207Sdes		logit("X11 connection rejected because of wrong authentication.");
968124207Sdes		debug2("X11 rejected %d i%d/o%d", c->self, c->istate, c->ostate);
96992559Sdes		chan_read_failed(c);
97092559Sdes		buffer_clear(&c->input);
97192559Sdes		chan_ibuf_empty(c);
97292559Sdes		buffer_clear(&c->output);
97392559Sdes		/* for proto v1, the peer will send an IEOF */
97460573Skris		if (compat20)
97592559Sdes			chan_write_failed(c);
97660573Skris		else
97792559Sdes			c->type = SSH_CHANNEL_OPEN;
978124207Sdes		debug2("X11 closed %d i%d/o%d", c->self, c->istate, c->ostate);
97960573Skris	}
98060573Skris}
98157429Smarkm
98276262Sgreen/* try to decode a socks4 header */
983162856Sdes/* ARGSUSED */
98492559Sdesstatic int
985162856Sdeschannel_decode_socks4(Channel *c, fd_set *readset, fd_set *writeset)
98676262Sgreen{
987106130Sdes	char *p, *host;
988192595Sdes	u_int len, have, i, found, need;
98992559Sdes	char username[256];
99076262Sgreen	struct {
99176262Sgreen		u_int8_t version;
99276262Sgreen		u_int8_t command;
99376262Sgreen		u_int16_t dest_port;
99476262Sgreen		struct in_addr dest_addr;
99576262Sgreen	} s4_req, s4_rsp;
99676262Sgreen
99776262Sgreen	debug2("channel %d: decode socks4", c->self);
99876262Sgreen
99976262Sgreen	have = buffer_len(&c->input);
100076262Sgreen	len = sizeof(s4_req);
100176262Sgreen	if (have < len)
100276262Sgreen		return 0;
100376262Sgreen	p = buffer_ptr(&c->input);
1004192595Sdes
1005192595Sdes	need = 1;
1006192595Sdes	/* SOCKS4A uses an invalid IP address 0.0.0.x */
1007192595Sdes	if (p[4] == 0 && p[5] == 0 && p[6] == 0 && p[7] != 0) {
1008192595Sdes		debug2("channel %d: socks4a request", c->self);
1009192595Sdes		/* ... and needs an extra string (the hostname) */
1010192595Sdes		need = 2;
1011192595Sdes	}
1012192595Sdes	/* Check for terminating NUL on the string(s) */
101376262Sgreen	for (found = 0, i = len; i < have; i++) {
101476262Sgreen		if (p[i] == '\0') {
1015192595Sdes			found++;
1016192595Sdes			if (found == need)
1017192595Sdes				break;
101876262Sgreen		}
101976262Sgreen		if (i > 1024) {
102076262Sgreen			/* the peer is probably sending garbage */
102176262Sgreen			debug("channel %d: decode socks4: too long",
102276262Sgreen			    c->self);
102376262Sgreen			return -1;
102476262Sgreen		}
102576262Sgreen	}
1026192595Sdes	if (found < need)
102776262Sgreen		return 0;
102876262Sgreen	buffer_get(&c->input, (char *)&s4_req.version, 1);
102976262Sgreen	buffer_get(&c->input, (char *)&s4_req.command, 1);
103076262Sgreen	buffer_get(&c->input, (char *)&s4_req.dest_port, 2);
103176262Sgreen	buffer_get(&c->input, (char *)&s4_req.dest_addr, 4);
103276262Sgreen	have = buffer_len(&c->input);
103376262Sgreen	p = buffer_ptr(&c->input);
103476262Sgreen	len = strlen(p);
103576262Sgreen	debug2("channel %d: decode socks4: user %s/%d", c->self, p, len);
1036192595Sdes	len++;					/* trailing '\0' */
103776262Sgreen	if (len > have)
103876262Sgreen		fatal("channel %d: decode socks4: len %d > have %d",
103976262Sgreen		    c->self, len, have);
104076262Sgreen	strlcpy(username, p, sizeof(username));
104176262Sgreen	buffer_consume(&c->input, len);
104276262Sgreen
1043192595Sdes	if (c->path != NULL) {
1044192595Sdes		xfree(c->path);
1045192595Sdes		c->path = NULL;
1046192595Sdes	}
1047192595Sdes	if (need == 1) {			/* SOCKS4: one string */
1048192595Sdes		host = inet_ntoa(s4_req.dest_addr);
1049192595Sdes		c->path = xstrdup(host);
1050192595Sdes	} else {				/* SOCKS4A: two strings */
1051192595Sdes		have = buffer_len(&c->input);
1052192595Sdes		p = buffer_ptr(&c->input);
1053192595Sdes		len = strlen(p);
1054192595Sdes		debug2("channel %d: decode socks4a: host %s/%d",
1055192595Sdes		    c->self, p, len);
1056192595Sdes		len++;				/* trailing '\0' */
1057192595Sdes		if (len > have)
1058192595Sdes			fatal("channel %d: decode socks4a: len %d > have %d",
1059192595Sdes			    c->self, len, have);
1060192595Sdes		if (len > NI_MAXHOST) {
1061192595Sdes			error("channel %d: hostname \"%.100s\" too long",
1062192595Sdes			    c->self, p);
1063192595Sdes			return -1;
1064192595Sdes		}
1065192595Sdes		c->path = xstrdup(p);
1066192595Sdes		buffer_consume(&c->input, len);
1067192595Sdes	}
106876262Sgreen	c->host_port = ntohs(s4_req.dest_port);
106992559Sdes
1070124207Sdes	debug2("channel %d: dynamic request: socks4 host %s port %u command %u",
1071192595Sdes	    c->self, c->path, c->host_port, s4_req.command);
107276262Sgreen
107376262Sgreen	if (s4_req.command != 1) {
1074192595Sdes		debug("channel %d: cannot handle: %s cn %d",
1075192595Sdes		    c->self, need == 1 ? "SOCKS4" : "SOCKS4A", s4_req.command);
107676262Sgreen		return -1;
107776262Sgreen	}
107876262Sgreen	s4_rsp.version = 0;			/* vn: 0 for reply */
107976262Sgreen	s4_rsp.command = 90;			/* cd: req granted */
108076262Sgreen	s4_rsp.dest_port = 0;			/* ignored */
108176262Sgreen	s4_rsp.dest_addr.s_addr = INADDR_ANY;	/* ignored */
1082162856Sdes	buffer_append(&c->output, &s4_rsp, sizeof(s4_rsp));
108376262Sgreen	return 1;
108476262Sgreen}
108576262Sgreen
1086124207Sdes/* try to decode a socks5 header */
1087124207Sdes#define SSH_SOCKS5_AUTHDONE	0x1000
1088124207Sdes#define SSH_SOCKS5_NOAUTH	0x00
1089124207Sdes#define SSH_SOCKS5_IPV4		0x01
1090124207Sdes#define SSH_SOCKS5_DOMAIN	0x03
1091124207Sdes#define SSH_SOCKS5_IPV6		0x04
1092124207Sdes#define SSH_SOCKS5_CONNECT	0x01
1093124207Sdes#define SSH_SOCKS5_SUCCESS	0x00
1094124207Sdes
1095162856Sdes/* ARGSUSED */
1096124207Sdesstatic int
1097162856Sdeschannel_decode_socks5(Channel *c, fd_set *readset, fd_set *writeset)
1098124207Sdes{
1099124207Sdes	struct {
1100124207Sdes		u_int8_t version;
1101124207Sdes		u_int8_t command;
1102124207Sdes		u_int8_t reserved;
1103124207Sdes		u_int8_t atyp;
1104124207Sdes	} s5_req, s5_rsp;
1105124207Sdes	u_int16_t dest_port;
1106192595Sdes	u_char *p, dest_addr[255+1], ntop[INET6_ADDRSTRLEN];
1107162856Sdes	u_int have, need, i, found, nmethods, addrlen, af;
1108124207Sdes
1109124207Sdes	debug2("channel %d: decode socks5", c->self);
1110124207Sdes	p = buffer_ptr(&c->input);
1111124207Sdes	if (p[0] != 0x05)
1112124207Sdes		return -1;
1113124207Sdes	have = buffer_len(&c->input);
1114124207Sdes	if (!(c->flags & SSH_SOCKS5_AUTHDONE)) {
1115124207Sdes		/* format: ver | nmethods | methods */
1116126273Sdes		if (have < 2)
1117124207Sdes			return 0;
1118124207Sdes		nmethods = p[1];
1119124207Sdes		if (have < nmethods + 2)
1120124207Sdes			return 0;
1121124207Sdes		/* look for method: "NO AUTHENTICATION REQUIRED" */
1122181111Sdes		for (found = 0, i = 2; i < nmethods + 2; i++) {
1123162856Sdes			if (p[i] == SSH_SOCKS5_NOAUTH) {
1124124207Sdes				found = 1;
1125124207Sdes				break;
1126124207Sdes			}
1127124207Sdes		}
1128124207Sdes		if (!found) {
1129124207Sdes			debug("channel %d: method SSH_SOCKS5_NOAUTH not found",
1130124207Sdes			    c->self);
1131124207Sdes			return -1;
1132124207Sdes		}
1133124207Sdes		buffer_consume(&c->input, nmethods + 2);
1134124207Sdes		buffer_put_char(&c->output, 0x05);		/* version */
1135124207Sdes		buffer_put_char(&c->output, SSH_SOCKS5_NOAUTH);	/* method */
1136124207Sdes		FD_SET(c->sock, writeset);
1137124207Sdes		c->flags |= SSH_SOCKS5_AUTHDONE;
1138124207Sdes		debug2("channel %d: socks5 auth done", c->self);
1139124207Sdes		return 0;				/* need more */
1140124207Sdes	}
1141124207Sdes	debug2("channel %d: socks5 post auth", c->self);
1142124207Sdes	if (have < sizeof(s5_req)+1)
1143124207Sdes		return 0;			/* need more */
1144162856Sdes	memcpy(&s5_req, p, sizeof(s5_req));
1145124207Sdes	if (s5_req.version != 0x05 ||
1146124207Sdes	    s5_req.command != SSH_SOCKS5_CONNECT ||
1147124207Sdes	    s5_req.reserved != 0x00) {
1148124207Sdes		debug2("channel %d: only socks5 connect supported", c->self);
1149124207Sdes		return -1;
1150124207Sdes	}
1151147005Sdes	switch (s5_req.atyp){
1152124207Sdes	case SSH_SOCKS5_IPV4:
1153124207Sdes		addrlen = 4;
1154124207Sdes		af = AF_INET;
1155124207Sdes		break;
1156124207Sdes	case SSH_SOCKS5_DOMAIN:
1157124207Sdes		addrlen = p[sizeof(s5_req)];
1158124207Sdes		af = -1;
1159124207Sdes		break;
1160124207Sdes	case SSH_SOCKS5_IPV6:
1161124207Sdes		addrlen = 16;
1162124207Sdes		af = AF_INET6;
1163124207Sdes		break;
1164124207Sdes	default:
1165124207Sdes		debug2("channel %d: bad socks5 atyp %d", c->self, s5_req.atyp);
1166124207Sdes		return -1;
1167124207Sdes	}
1168162856Sdes	need = sizeof(s5_req) + addrlen + 2;
1169162856Sdes	if (s5_req.atyp == SSH_SOCKS5_DOMAIN)
1170162856Sdes		need++;
1171162856Sdes	if (have < need)
1172124207Sdes		return 0;
1173124207Sdes	buffer_consume(&c->input, sizeof(s5_req));
1174124207Sdes	if (s5_req.atyp == SSH_SOCKS5_DOMAIN)
1175124207Sdes		buffer_consume(&c->input, 1);    /* host string length */
1176124207Sdes	buffer_get(&c->input, (char *)&dest_addr, addrlen);
1177124207Sdes	buffer_get(&c->input, (char *)&dest_port, 2);
1178124207Sdes	dest_addr[addrlen] = '\0';
1179192595Sdes	if (c->path != NULL) {
1180192595Sdes		xfree(c->path);
1181192595Sdes		c->path = NULL;
1182192595Sdes	}
1183192595Sdes	if (s5_req.atyp == SSH_SOCKS5_DOMAIN) {
1184192595Sdes		if (addrlen >= NI_MAXHOST) {
1185192595Sdes			error("channel %d: dynamic request: socks5 hostname "
1186192595Sdes			    "\"%.100s\" too long", c->self, dest_addr);
1187192595Sdes			return -1;
1188192595Sdes		}
1189192595Sdes		c->path = xstrdup(dest_addr);
1190192595Sdes	} else {
1191192595Sdes		if (inet_ntop(af, dest_addr, ntop, sizeof(ntop)) == NULL)
1192192595Sdes			return -1;
1193192595Sdes		c->path = xstrdup(ntop);
1194192595Sdes	}
1195124207Sdes	c->host_port = ntohs(dest_port);
1196126273Sdes
1197124207Sdes	debug2("channel %d: dynamic request: socks5 host %s port %u command %u",
1198124207Sdes	    c->self, c->path, c->host_port, s5_req.command);
1199124207Sdes
1200124207Sdes	s5_rsp.version = 0x05;
1201124207Sdes	s5_rsp.command = SSH_SOCKS5_SUCCESS;
1202124207Sdes	s5_rsp.reserved = 0;			/* ignored */
1203124207Sdes	s5_rsp.atyp = SSH_SOCKS5_IPV4;
1204124207Sdes	((struct in_addr *)&dest_addr)->s_addr = INADDR_ANY;
1205124207Sdes	dest_port = 0;				/* ignored */
1206124207Sdes
1207162856Sdes	buffer_append(&c->output, &s5_rsp, sizeof(s5_rsp));
1208162856Sdes	buffer_append(&c->output, &dest_addr, sizeof(struct in_addr));
1209162856Sdes	buffer_append(&c->output, &dest_port, sizeof(dest_port));
1210124207Sdes	return 1;
1211124207Sdes}
1212124207Sdes
121376262Sgreen/* dynamic port forwarding */
121492559Sdesstatic void
1215162856Sdeschannel_pre_dynamic(Channel *c, fd_set *readset, fd_set *writeset)
121676262Sgreen{
121776262Sgreen	u_char *p;
1218149753Sdes	u_int have;
1219149753Sdes	int ret;
122076262Sgreen
122176262Sgreen	have = buffer_len(&c->input);
122292559Sdes	c->delayed = 0;
122376262Sgreen	debug2("channel %d: pre_dynamic: have %d", c->self, have);
122476262Sgreen	/* buffer_dump(&c->input); */
122576262Sgreen	/* check if the fixed size part of the packet is in buffer. */
1226124207Sdes	if (have < 3) {
122776262Sgreen		/* need more */
122876262Sgreen		FD_SET(c->sock, readset);
122976262Sgreen		return;
123076262Sgreen	}
123176262Sgreen	/* try to guess the protocol */
123276262Sgreen	p = buffer_ptr(&c->input);
123376262Sgreen	switch (p[0]) {
123476262Sgreen	case 0x04:
123576262Sgreen		ret = channel_decode_socks4(c, readset, writeset);
123676262Sgreen		break;
1237124207Sdes	case 0x05:
1238124207Sdes		ret = channel_decode_socks5(c, readset, writeset);
1239124207Sdes		break;
124076262Sgreen	default:
124176262Sgreen		ret = -1;
124276262Sgreen		break;
124376262Sgreen	}
124476262Sgreen	if (ret < 0) {
124592559Sdes		chan_mark_dead(c);
124676262Sgreen	} else if (ret == 0) {
124776262Sgreen		debug2("channel %d: pre_dynamic: need more", c->self);
124876262Sgreen		/* need more */
124976262Sgreen		FD_SET(c->sock, readset);
125076262Sgreen	} else {
125176262Sgreen		/* switch to the next state */
125276262Sgreen		c->type = SSH_CHANNEL_OPENING;
125376262Sgreen		port_open_helper(c, "direct-tcpip");
125476262Sgreen	}
125576262Sgreen}
125676262Sgreen
125760573Skris/* This is our fake X11 server socket. */
1258162856Sdes/* ARGSUSED */
125992559Sdesstatic void
1260162856Sdeschannel_post_x11_listener(Channel *c, fd_set *readset, fd_set *writeset)
126160573Skris{
126292559Sdes	Channel *nc;
1263181111Sdes	struct sockaddr_storage addr;
126492559Sdes	int newsock;
126560573Skris	socklen_t addrlen;
126676262Sgreen	char buf[16384], *remote_ipaddr;
126760573Skris	int remote_port;
126857429Smarkm
126960573Skris	if (FD_ISSET(c->sock, readset)) {
127060573Skris		debug("X11 connection requested.");
127160573Skris		addrlen = sizeof(addr);
1272181111Sdes		newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen);
127392559Sdes		if (c->single_connection) {
1274124207Sdes			debug2("single_connection: closing X11 listener.");
127592559Sdes			channel_close_fd(&c->sock);
127692559Sdes			chan_mark_dead(c);
127792559Sdes		}
127860573Skris		if (newsock < 0) {
127960573Skris			error("accept: %.100s", strerror(errno));
128060573Skris			return;
128160573Skris		}
128292559Sdes		set_nodelay(newsock);
128376262Sgreen		remote_ipaddr = get_peer_ipaddr(newsock);
128460573Skris		remote_port = get_peer_port(newsock);
128560573Skris		snprintf(buf, sizeof buf, "X11 connection from %.200s port %d",
128676262Sgreen		    remote_ipaddr, remote_port);
128757429Smarkm
128892559Sdes		nc = channel_new("accepted x11 socket",
128960573Skris		    SSH_CHANNEL_OPENING, newsock, newsock, -1,
1290124207Sdes		    c->local_window_max, c->local_maxpacket, 0, buf, 1);
129160573Skris		if (compat20) {
129260573Skris			packet_start(SSH2_MSG_CHANNEL_OPEN);
129360573Skris			packet_put_cstring("x11");
129492559Sdes			packet_put_int(nc->self);
129592559Sdes			packet_put_int(nc->local_window_max);
129692559Sdes			packet_put_int(nc->local_maxpacket);
129776262Sgreen			/* originator ipaddr and port */
129876262Sgreen			packet_put_cstring(remote_ipaddr);
129960573Skris			if (datafellows & SSH_BUG_X11FWD) {
1300124207Sdes				debug2("ssh2 x11 bug compat mode");
130157429Smarkm			} else {
130260573Skris				packet_put_int(remote_port);
130357429Smarkm			}
130460573Skris			packet_send();
130560573Skris		} else {
130660573Skris			packet_start(SSH_SMSG_X11_OPEN);
130792559Sdes			packet_put_int(nc->self);
130892559Sdes			if (packet_get_protocol_flags() &
130992559Sdes			    SSH_PROTOFLAG_HOST_IN_FWD_OPEN)
131092559Sdes				packet_put_cstring(buf);
131160573Skris			packet_send();
131257429Smarkm		}
131376262Sgreen		xfree(remote_ipaddr);
131457429Smarkm	}
131557429Smarkm}
131657429Smarkm
131792559Sdesstatic void
131876262Sgreenport_open_helper(Channel *c, char *rtype)
131976262Sgreen{
132076262Sgreen	int direct;
132176262Sgreen	char buf[1024];
132276262Sgreen	char *remote_ipaddr = get_peer_ipaddr(c->sock);
1323149753Sdes	int remote_port = get_peer_port(c->sock);
132476262Sgreen
132576262Sgreen	direct = (strcmp(rtype, "direct-tcpip") == 0);
132676262Sgreen
132776262Sgreen	snprintf(buf, sizeof buf,
132876262Sgreen	    "%s: listening port %d for %.100s port %d, "
132976262Sgreen	    "connect from %.200s port %d",
133076262Sgreen	    rtype, c->listening_port, c->path, c->host_port,
133176262Sgreen	    remote_ipaddr, remote_port);
133276262Sgreen
133376262Sgreen	xfree(c->remote_name);
133476262Sgreen	c->remote_name = xstrdup(buf);
133576262Sgreen
133676262Sgreen	if (compat20) {
133776262Sgreen		packet_start(SSH2_MSG_CHANNEL_OPEN);
133876262Sgreen		packet_put_cstring(rtype);
133976262Sgreen		packet_put_int(c->self);
134076262Sgreen		packet_put_int(c->local_window_max);
134176262Sgreen		packet_put_int(c->local_maxpacket);
134276262Sgreen		if (direct) {
134376262Sgreen			/* target host, port */
134476262Sgreen			packet_put_cstring(c->path);
134576262Sgreen			packet_put_int(c->host_port);
134676262Sgreen		} else {
134776262Sgreen			/* listen address, port */
134876262Sgreen			packet_put_cstring(c->path);
134976262Sgreen			packet_put_int(c->listening_port);
135076262Sgreen		}
135176262Sgreen		/* originator host and port */
135276262Sgreen		packet_put_cstring(remote_ipaddr);
1353149753Sdes		packet_put_int((u_int)remote_port);
135476262Sgreen		packet_send();
135576262Sgreen	} else {
135676262Sgreen		packet_start(SSH_MSG_PORT_OPEN);
135776262Sgreen		packet_put_int(c->self);
135876262Sgreen		packet_put_cstring(c->path);
135976262Sgreen		packet_put_int(c->host_port);
136092559Sdes		if (packet_get_protocol_flags() &
136192559Sdes		    SSH_PROTOFLAG_HOST_IN_FWD_OPEN)
136276262Sgreen			packet_put_cstring(c->remote_name);
136376262Sgreen		packet_send();
136476262Sgreen	}
136576262Sgreen	xfree(remote_ipaddr);
136676262Sgreen}
136776262Sgreen
1368157019Sdesstatic void
1369157019Sdeschannel_set_reuseaddr(int fd)
1370157019Sdes{
1371157019Sdes	int on = 1;
1372157019Sdes
1373157019Sdes	/*
1374157019Sdes	 * Set socket options.
1375157019Sdes	 * Allow local port reuse in TIME_WAIT.
1376157019Sdes	 */
1377157019Sdes	if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1)
1378157019Sdes		error("setsockopt SO_REUSEADDR fd %d: %s", fd, strerror(errno));
1379157019Sdes}
1380157019Sdes
138157429Smarkm/*
138260573Skris * This socket is listening for connections to a forwarded TCP/IP port.
138357429Smarkm */
1384162856Sdes/* ARGSUSED */
138592559Sdesstatic void
1386162856Sdeschannel_post_port_listener(Channel *c, fd_set *readset, fd_set *writeset)
138757429Smarkm{
138876262Sgreen	Channel *nc;
1389181111Sdes	struct sockaddr_storage addr;
139092559Sdes	int newsock, nextstate;
139157429Smarkm	socklen_t addrlen;
139276262Sgreen	char *rtype;
139357429Smarkm
139460573Skris	if (FD_ISSET(c->sock, readset)) {
139560573Skris		debug("Connection to port %d forwarding "
139660573Skris		    "to %.100s port %d requested.",
139760573Skris		    c->listening_port, c->path, c->host_port);
139876262Sgreen
139992559Sdes		if (c->type == SSH_CHANNEL_RPORT_LISTENER) {
140092559Sdes			nextstate = SSH_CHANNEL_OPENING;
140192559Sdes			rtype = "forwarded-tcpip";
140292559Sdes		} else {
140392559Sdes			if (c->host_port == 0) {
140492559Sdes				nextstate = SSH_CHANNEL_DYNAMIC;
140592559Sdes				rtype = "dynamic-tcpip";
140692559Sdes			} else {
140792559Sdes				nextstate = SSH_CHANNEL_OPENING;
140892559Sdes				rtype = "direct-tcpip";
140992559Sdes			}
141092559Sdes		}
141176262Sgreen
141260573Skris		addrlen = sizeof(addr);
1413181111Sdes		newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen);
141460573Skris		if (newsock < 0) {
141560573Skris			error("accept: %.100s", strerror(errno));
141660573Skris			return;
141760573Skris		}
141892559Sdes		set_nodelay(newsock);
1419124207Sdes		nc = channel_new(rtype, nextstate, newsock, newsock, -1,
1420124207Sdes		    c->local_window_max, c->local_maxpacket, 0, rtype, 1);
142176262Sgreen		nc->listening_port = c->listening_port;
142276262Sgreen		nc->host_port = c->host_port;
1423192595Sdes		if (c->path != NULL)
1424192595Sdes			nc->path = xstrdup(c->path);
142576262Sgreen
142692559Sdes		if (nextstate == SSH_CHANNEL_DYNAMIC) {
142792559Sdes			/*
142892559Sdes			 * do not call the channel_post handler until
142992559Sdes			 * this flag has been reset by a pre-handler.
143092559Sdes			 * otherwise the FD_ISSET calls might overflow
143192559Sdes			 */
143292559Sdes			nc->delayed = 1;
143392559Sdes		} else {
143476262Sgreen			port_open_helper(nc, rtype);
143592559Sdes		}
143660573Skris	}
143760573Skris}
143857429Smarkm
143960573Skris/*
144060573Skris * This is the authentication agent socket listening for connections from
144160573Skris * clients.
144260573Skris */
1443162856Sdes/* ARGSUSED */
144492559Sdesstatic void
1445162856Sdeschannel_post_auth_listener(Channel *c, fd_set *readset, fd_set *writeset)
144660573Skris{
144792559Sdes	Channel *nc;
144892559Sdes	int newsock;
1449181111Sdes	struct sockaddr_storage addr;
145060573Skris	socklen_t addrlen;
145157429Smarkm
145260573Skris	if (FD_ISSET(c->sock, readset)) {
145360573Skris		addrlen = sizeof(addr);
1454181111Sdes		newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen);
145560573Skris		if (newsock < 0) {
145660573Skris			error("accept from auth socket: %.100s", strerror(errno));
145760573Skris			return;
145860573Skris		}
145992559Sdes		nc = channel_new("accepted auth socket",
146076262Sgreen		    SSH_CHANNEL_OPENING, newsock, newsock, -1,
146176262Sgreen		    c->local_window_max, c->local_maxpacket,
1462124207Sdes		    0, "accepted auth socket", 1);
146376262Sgreen		if (compat20) {
146476262Sgreen			packet_start(SSH2_MSG_CHANNEL_OPEN);
146576262Sgreen			packet_put_cstring("auth-agent@openssh.com");
146692559Sdes			packet_put_int(nc->self);
146776262Sgreen			packet_put_int(c->local_window_max);
146876262Sgreen			packet_put_int(c->local_maxpacket);
146976262Sgreen		} else {
147076262Sgreen			packet_start(SSH_SMSG_AGENT_OPEN);
147192559Sdes			packet_put_int(nc->self);
147276262Sgreen		}
147360573Skris		packet_send();
147460573Skris	}
147560573Skris}
147657429Smarkm
1477162856Sdes/* ARGSUSED */
147892559Sdesstatic void
1479162856Sdeschannel_post_connecting(Channel *c, fd_set *readset, fd_set *writeset)
148076262Sgreen{
1481181111Sdes	int err = 0, sock;
148292559Sdes	socklen_t sz = sizeof(err);
148392559Sdes
148476262Sgreen	if (FD_ISSET(c->sock, writeset)) {
148592559Sdes		if (getsockopt(c->sock, SOL_SOCKET, SO_ERROR, &err, &sz) < 0) {
148692559Sdes			err = errno;
148792559Sdes			error("getsockopt SO_ERROR failed");
148892559Sdes		}
148992559Sdes		if (err == 0) {
1490181111Sdes			debug("channel %d: connected to %s port %d",
1491181111Sdes			    c->self, c->connect_ctx.host, c->connect_ctx.port);
1492181111Sdes			channel_connect_ctx_free(&c->connect_ctx);
149392559Sdes			c->type = SSH_CHANNEL_OPEN;
149492559Sdes			if (compat20) {
149592559Sdes				packet_start(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION);
149692559Sdes				packet_put_int(c->remote_id);
149792559Sdes				packet_put_int(c->self);
149892559Sdes				packet_put_int(c->local_window);
149992559Sdes				packet_put_int(c->local_maxpacket);
150092559Sdes			} else {
150192559Sdes				packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION);
150292559Sdes				packet_put_int(c->remote_id);
150392559Sdes				packet_put_int(c->self);
150492559Sdes			}
150576262Sgreen		} else {
1506181111Sdes			debug("channel %d: connection failed: %s",
150792559Sdes			    c->self, strerror(err));
1508181111Sdes			/* Try next address, if any */
1509181111Sdes			if ((sock = connect_next(&c->connect_ctx)) > 0) {
1510181111Sdes				close(c->sock);
1511181111Sdes				c->sock = c->rfd = c->wfd = sock;
1512181111Sdes				channel_max_fd = channel_find_maxfd();
1513181111Sdes				return;
1514181111Sdes			}
1515181111Sdes			/* Exhausted all addresses */
1516181111Sdes			error("connect_to %.100s port %d: failed.",
1517181111Sdes			    c->connect_ctx.host, c->connect_ctx.port);
1518181111Sdes			channel_connect_ctx_free(&c->connect_ctx);
151992559Sdes			if (compat20) {
152092559Sdes				packet_start(SSH2_MSG_CHANNEL_OPEN_FAILURE);
152192559Sdes				packet_put_int(c->remote_id);
152292559Sdes				packet_put_int(SSH2_OPEN_CONNECT_FAILED);
152392559Sdes				if (!(datafellows & SSH_BUG_OPENFAILURE)) {
152492559Sdes					packet_put_cstring(strerror(err));
152592559Sdes					packet_put_cstring("");
152692559Sdes				}
152776262Sgreen			} else {
152892559Sdes				packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE);
152992559Sdes				packet_put_int(c->remote_id);
153076262Sgreen			}
153192559Sdes			chan_mark_dead(c);
153276262Sgreen		}
153392559Sdes		packet_send();
153476262Sgreen	}
153576262Sgreen}
153676262Sgreen
1537162856Sdes/* ARGSUSED */
153892559Sdesstatic int
1539162856Sdeschannel_handle_rfd(Channel *c, fd_set *readset, fd_set *writeset)
154060573Skris{
1541147005Sdes	char buf[CHAN_RBUF];
1542181111Sdes	int len, force;
154357429Smarkm
1544181111Sdes	force = c->isatty && c->detach_close && c->istate != CHAN_INPUT_CLOSED;
1545181111Sdes	if (c->rfd != -1 && (force || FD_ISSET(c->rfd, readset))) {
1546162856Sdes		errno = 0;
154760573Skris		len = read(c->rfd, buf, sizeof(buf));
1548181111Sdes		if (len < 0 && (errno == EINTR ||
1549181111Sdes		    ((errno == EAGAIN || errno == EWOULDBLOCK) && !force)))
155060573Skris			return 1;
1551162856Sdes#ifndef PTY_ZEROREAD
155278827Sgreen		if (len <= 0) {
1553162856Sdes#else
1554162856Sdes		if ((!c->isatty && len <= 0) ||
1555162856Sdes		    (c->isatty && (len < 0 || (len == 0 && errno != 0)))) {
1556162856Sdes#endif
1557124207Sdes			debug2("channel %d: read<=0 rfd %d len %d",
155860573Skris			    c->self, c->rfd, len);
155976262Sgreen			if (c->type != SSH_CHANNEL_OPEN) {
1560124207Sdes				debug2("channel %d: not open", c->self);
156192559Sdes				chan_mark_dead(c);
156276262Sgreen				return -1;
156376262Sgreen			} else if (compat13) {
156492559Sdes				buffer_clear(&c->output);
156560573Skris				c->type = SSH_CHANNEL_INPUT_DRAINING;
1566124207Sdes				debug2("channel %d: input draining.", c->self);
156760573Skris			} else {
156860573Skris				chan_read_failed(c);
156957429Smarkm			}
157060573Skris			return -1;
157160573Skris		}
157292559Sdes		if (c->input_filter != NULL) {
157365668Skris			if (c->input_filter(c, buf, len) == -1) {
1574124207Sdes				debug2("channel %d: filter stops", c->self);
157565668Skris				chan_read_failed(c);
157665668Skris			}
1577157019Sdes		} else if (c->datagram) {
1578157019Sdes			buffer_put_string(&c->input, buf, len);
157965668Skris		} else {
158065668Skris			buffer_append(&c->input, buf, len);
158165668Skris		}
158260573Skris	}
158360573Skris	return 1;
158460573Skris}
1585162856Sdes
1586162856Sdes/* ARGSUSED */
158792559Sdesstatic int
1588162856Sdeschannel_handle_wfd(Channel *c, fd_set *readset, fd_set *writeset)
158960573Skris{
159076262Sgreen	struct termios tio;
1591157019Sdes	u_char *data = NULL, *buf;
159292559Sdes	u_int dlen;
159360573Skris	int len;
159460573Skris
159560573Skris	/* Send buffered output data to the socket. */
159660573Skris	if (c->wfd != -1 &&
159760573Skris	    FD_ISSET(c->wfd, writeset) &&
159860573Skris	    buffer_len(&c->output) > 0) {
1599157019Sdes		if (c->output_filter != NULL) {
1600157019Sdes			if ((buf = c->output_filter(c, &data, &dlen)) == NULL) {
1601157019Sdes				debug2("channel %d: filter stops", c->self);
1602157019Sdes				if (c->type != SSH_CHANNEL_OPEN)
1603157019Sdes					chan_mark_dead(c);
1604157019Sdes				else
1605157019Sdes					chan_write_failed(c);
1606157019Sdes				return -1;
1607157019Sdes			}
1608157019Sdes		} else if (c->datagram) {
1609157019Sdes			buf = data = buffer_get_string(&c->output, &dlen);
1610157019Sdes		} else {
1611157019Sdes			buf = data = buffer_ptr(&c->output);
1612157019Sdes			dlen = buffer_len(&c->output);
1613157019Sdes		}
1614157019Sdes
1615157019Sdes		if (c->datagram) {
1616157019Sdes			/* ignore truncated writes, datagrams might get lost */
1617157019Sdes			c->local_consumed += dlen + 4;
1618157019Sdes			len = write(c->wfd, buf, dlen);
1619157019Sdes			xfree(data);
1620181111Sdes			if (len < 0 && (errno == EINTR || errno == EAGAIN ||
1621181111Sdes			    errno == EWOULDBLOCK))
1622157019Sdes				return 1;
1623157019Sdes			if (len <= 0) {
1624157019Sdes				if (c->type != SSH_CHANNEL_OPEN)
1625157019Sdes					chan_mark_dead(c);
1626157019Sdes				else
1627157019Sdes					chan_write_failed(c);
1628157019Sdes				return -1;
1629157019Sdes			}
1630157019Sdes			return 1;
1631157019Sdes		}
1632106130Sdes#ifdef _AIX
1633126273Sdes		/* XXX: Later AIX versions can't push as much data to tty */
1634126273Sdes		if (compat20 && c->wfd_isatty)
1635126273Sdes			dlen = MIN(dlen, 8*1024);
1636106130Sdes#endif
1637157019Sdes
1638157019Sdes		len = write(c->wfd, buf, dlen);
1639181111Sdes		if (len < 0 &&
1640181111Sdes		    (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK))
164160573Skris			return 1;
164260573Skris		if (len <= 0) {
164376262Sgreen			if (c->type != SSH_CHANNEL_OPEN) {
1644124207Sdes				debug2("channel %d: not open", c->self);
164592559Sdes				chan_mark_dead(c);
164676262Sgreen				return -1;
164776262Sgreen			} else if (compat13) {
164892559Sdes				buffer_clear(&c->output);
1649124207Sdes				debug2("channel %d: input draining.", c->self);
165060573Skris				c->type = SSH_CHANNEL_INPUT_DRAINING;
165160573Skris			} else {
165260573Skris				chan_write_failed(c);
165357429Smarkm			}
165460573Skris			return -1;
165560573Skris		}
1656197679Sdes#ifndef BROKEN_TCGETATTR_ICANON
1657157019Sdes		if (compat20 && c->isatty && dlen >= 1 && buf[0] != '\r') {
165874500Sgreen			if (tcgetattr(c->wfd, &tio) == 0 &&
165974500Sgreen			    !(tio.c_lflag & ECHO) && (tio.c_lflag & ICANON)) {
166074500Sgreen				/*
166174500Sgreen				 * Simulate echo to reduce the impact of
166276262Sgreen				 * traffic analysis. We need to match the
166376262Sgreen				 * size of a SSH2_MSG_CHANNEL_DATA message
1664157019Sdes				 * (4 byte channel id + buf)
166574500Sgreen				 */
166676262Sgreen				packet_send_ignore(4 + len);
166774500Sgreen				packet_send();
166874500Sgreen			}
166974500Sgreen		}
1670197679Sdes#endif
167160573Skris		buffer_consume(&c->output, len);
167260573Skris		if (compat20 && len > 0) {
167360573Skris			c->local_consumed += len;
167460573Skris		}
167560573Skris	}
167660573Skris	return 1;
167760573Skris}
1678162856Sdes
167992559Sdesstatic int
1680162856Sdeschannel_handle_efd(Channel *c, fd_set *readset, fd_set *writeset)
168160573Skris{
1682147005Sdes	char buf[CHAN_RBUF];
168360573Skris	int len;
168457429Smarkm
168560573Skris/** XXX handle drain efd, too */
168660573Skris	if (c->efd != -1) {
168760573Skris		if (c->extended_usage == CHAN_EXTENDED_WRITE &&
168860573Skris		    FD_ISSET(c->efd, writeset) &&
168960573Skris		    buffer_len(&c->extended) > 0) {
169060573Skris			len = write(c->efd, buffer_ptr(&c->extended),
169160573Skris			    buffer_len(&c->extended));
169269587Sgreen			debug2("channel %d: written %d to efd %d",
169360573Skris			    c->self, len, c->efd);
1694181111Sdes			if (len < 0 && (errno == EINTR || errno == EAGAIN ||
1695181111Sdes			    errno == EWOULDBLOCK))
169676262Sgreen				return 1;
169776262Sgreen			if (len <= 0) {
169876262Sgreen				debug2("channel %d: closing write-efd %d",
169976262Sgreen				    c->self, c->efd);
170092559Sdes				channel_close_fd(&c->efd);
170176262Sgreen			} else {
170260573Skris				buffer_consume(&c->extended, len);
170360573Skris				c->local_consumed += len;
170457429Smarkm			}
170560573Skris		} else if (c->extended_usage == CHAN_EXTENDED_READ &&
1706181111Sdes		    (c->detach_close || FD_ISSET(c->efd, readset))) {
170760573Skris			len = read(c->efd, buf, sizeof(buf));
170869587Sgreen			debug2("channel %d: read %d from efd %d",
170992559Sdes			    c->self, len, c->efd);
1710181111Sdes			if (len < 0 && (errno == EINTR || ((errno == EAGAIN ||
1711181111Sdes			    errno == EWOULDBLOCK) && !c->detach_close)))
171276262Sgreen				return 1;
171376262Sgreen			if (len <= 0) {
171476262Sgreen				debug2("channel %d: closing read-efd %d",
171560573Skris				    c->self, c->efd);
171692559Sdes				channel_close_fd(&c->efd);
171776262Sgreen			} else {
171860573Skris				buffer_append(&c->extended, buf, len);
171976262Sgreen			}
172060573Skris		}
172160573Skris	}
172260573Skris	return 1;
172360573Skris}
1724162856Sdes
1725162856Sdes/* ARGSUSED */
172692559Sdesstatic int
1727162856Sdeschannel_handle_ctl(Channel *c, fd_set *readset, fd_set *writeset)
1728137019Sdes{
1729137019Sdes	char buf[16];
1730137019Sdes	int len;
1731137019Sdes
1732137019Sdes	/* Monitor control fd to detect if the slave client exits */
1733137019Sdes	if (c->ctl_fd != -1 && FD_ISSET(c->ctl_fd, readset)) {
1734137019Sdes		len = read(c->ctl_fd, buf, sizeof(buf));
1735181111Sdes		if (len < 0 &&
1736181111Sdes		    (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK))
1737137019Sdes			return 1;
1738137019Sdes		if (len <= 0) {
1739137019Sdes			debug2("channel %d: ctl read<=0", c->self);
1740137019Sdes			if (c->type != SSH_CHANNEL_OPEN) {
1741137019Sdes				debug2("channel %d: not open", c->self);
1742137019Sdes				chan_mark_dead(c);
1743137019Sdes				return -1;
1744137019Sdes			} else {
1745137019Sdes				chan_read_failed(c);
1746137019Sdes				chan_write_failed(c);
1747137019Sdes			}
1748137019Sdes			return -1;
1749137019Sdes		} else
1750137019Sdes			fatal("%s: unexpected data on ctl fd", __func__);
1751137019Sdes	}
1752137019Sdes	return 1;
1753137019Sdes}
1754162856Sdes
1755137019Sdesstatic int
175676262Sgreenchannel_check_window(Channel *c)
175760573Skris{
175876262Sgreen	if (c->type == SSH_CHANNEL_OPEN &&
175976262Sgreen	    !(c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD)) &&
1760181111Sdes	    ((c->local_window_max - c->local_window >
1761181111Sdes	    c->local_maxpacket*3) ||
1762181111Sdes	    c->local_window < c->local_window_max/2) &&
176360573Skris	    c->local_consumed > 0) {
176460573Skris		packet_start(SSH2_MSG_CHANNEL_WINDOW_ADJUST);
176560573Skris		packet_put_int(c->remote_id);
176660573Skris		packet_put_int(c->local_consumed);
176760573Skris		packet_send();
176869587Sgreen		debug2("channel %d: window %d sent adjust %d",
176960573Skris		    c->self, c->local_window,
177060573Skris		    c->local_consumed);
177160573Skris		c->local_window += c->local_consumed;
177260573Skris		c->local_consumed = 0;
177360573Skris	}
177460573Skris	return 1;
177560573Skris}
177657429Smarkm
177792559Sdesstatic void
1778162856Sdeschannel_post_open(Channel *c, fd_set *readset, fd_set *writeset)
177960573Skris{
178092559Sdes	if (c->delayed)
178192559Sdes		return;
178260573Skris	channel_handle_rfd(c, readset, writeset);
178360573Skris	channel_handle_wfd(c, readset, writeset);
178492559Sdes	if (!compat20)
178592559Sdes		return;
178660573Skris	channel_handle_efd(c, readset, writeset);
1787137019Sdes	channel_handle_ctl(c, readset, writeset);
178876262Sgreen	channel_check_window(c);
178960573Skris}
179060573Skris
1791162856Sdes/* ARGSUSED */
179292559Sdesstatic void
1793162856Sdeschannel_post_output_drain_13(Channel *c, fd_set *readset, fd_set *writeset)
179460573Skris{
179560573Skris	int len;
1796106130Sdes
179760573Skris	/* Send buffered output data to the socket. */
179860573Skris	if (FD_ISSET(c->sock, writeset) && buffer_len(&c->output) > 0) {
179960573Skris		len = write(c->sock, buffer_ptr(&c->output),
180060573Skris			    buffer_len(&c->output));
180160573Skris		if (len <= 0)
180292559Sdes			buffer_clear(&c->output);
180360573Skris		else
180460573Skris			buffer_consume(&c->output, len);
180560573Skris	}
180660573Skris}
180760573Skris
180892559Sdesstatic void
180960573Skrischannel_handler_init_20(void)
181060573Skris{
181192559Sdes	channel_pre[SSH_CHANNEL_OPEN] =			&channel_pre_open;
181260573Skris	channel_pre[SSH_CHANNEL_X11_OPEN] =		&channel_pre_x11_open;
181360573Skris	channel_pre[SSH_CHANNEL_PORT_LISTENER] =	&channel_pre_listener;
181476262Sgreen	channel_pre[SSH_CHANNEL_RPORT_LISTENER] =	&channel_pre_listener;
181560573Skris	channel_pre[SSH_CHANNEL_X11_LISTENER] =		&channel_pre_listener;
181676262Sgreen	channel_pre[SSH_CHANNEL_AUTH_SOCKET] =		&channel_pre_listener;
181776262Sgreen	channel_pre[SSH_CHANNEL_CONNECTING] =		&channel_pre_connecting;
181876262Sgreen	channel_pre[SSH_CHANNEL_DYNAMIC] =		&channel_pre_dynamic;
181960573Skris
182092559Sdes	channel_post[SSH_CHANNEL_OPEN] =		&channel_post_open;
182160573Skris	channel_post[SSH_CHANNEL_PORT_LISTENER] =	&channel_post_port_listener;
182276262Sgreen	channel_post[SSH_CHANNEL_RPORT_LISTENER] =	&channel_post_port_listener;
182360573Skris	channel_post[SSH_CHANNEL_X11_LISTENER] =	&channel_post_x11_listener;
182476262Sgreen	channel_post[SSH_CHANNEL_AUTH_SOCKET] =		&channel_post_auth_listener;
182576262Sgreen	channel_post[SSH_CHANNEL_CONNECTING] =		&channel_post_connecting;
182692559Sdes	channel_post[SSH_CHANNEL_DYNAMIC] =		&channel_post_open;
182760573Skris}
182860573Skris
182992559Sdesstatic void
183060573Skrischannel_handler_init_13(void)
183160573Skris{
183260573Skris	channel_pre[SSH_CHANNEL_OPEN] =			&channel_pre_open_13;
183360573Skris	channel_pre[SSH_CHANNEL_X11_OPEN] =		&channel_pre_x11_open_13;
183460573Skris	channel_pre[SSH_CHANNEL_X11_LISTENER] =		&channel_pre_listener;
183560573Skris	channel_pre[SSH_CHANNEL_PORT_LISTENER] =	&channel_pre_listener;
183660573Skris	channel_pre[SSH_CHANNEL_AUTH_SOCKET] =		&channel_pre_listener;
183760573Skris	channel_pre[SSH_CHANNEL_INPUT_DRAINING] =	&channel_pre_input_draining;
183860573Skris	channel_pre[SSH_CHANNEL_OUTPUT_DRAINING] =	&channel_pre_output_draining;
183976262Sgreen	channel_pre[SSH_CHANNEL_CONNECTING] =		&channel_pre_connecting;
184076262Sgreen	channel_pre[SSH_CHANNEL_DYNAMIC] =		&channel_pre_dynamic;
184160573Skris
184292559Sdes	channel_post[SSH_CHANNEL_OPEN] =		&channel_post_open;
184360573Skris	channel_post[SSH_CHANNEL_X11_LISTENER] =	&channel_post_x11_listener;
184460573Skris	channel_post[SSH_CHANNEL_PORT_LISTENER] =	&channel_post_port_listener;
184560573Skris	channel_post[SSH_CHANNEL_AUTH_SOCKET] =		&channel_post_auth_listener;
184660573Skris	channel_post[SSH_CHANNEL_OUTPUT_DRAINING] =	&channel_post_output_drain_13;
184776262Sgreen	channel_post[SSH_CHANNEL_CONNECTING] =		&channel_post_connecting;
184892559Sdes	channel_post[SSH_CHANNEL_DYNAMIC] =		&channel_post_open;
184960573Skris}
185060573Skris
185192559Sdesstatic void
185260573Skrischannel_handler_init_15(void)
185360573Skris{
185492559Sdes	channel_pre[SSH_CHANNEL_OPEN] =			&channel_pre_open;
185560573Skris	channel_pre[SSH_CHANNEL_X11_OPEN] =		&channel_pre_x11_open;
185660573Skris	channel_pre[SSH_CHANNEL_X11_LISTENER] =		&channel_pre_listener;
185760573Skris	channel_pre[SSH_CHANNEL_PORT_LISTENER] =	&channel_pre_listener;
185860573Skris	channel_pre[SSH_CHANNEL_AUTH_SOCKET] =		&channel_pre_listener;
185976262Sgreen	channel_pre[SSH_CHANNEL_CONNECTING] =		&channel_pre_connecting;
186076262Sgreen	channel_pre[SSH_CHANNEL_DYNAMIC] =		&channel_pre_dynamic;
186160573Skris
186260573Skris	channel_post[SSH_CHANNEL_X11_LISTENER] =	&channel_post_x11_listener;
186360573Skris	channel_post[SSH_CHANNEL_PORT_LISTENER] =	&channel_post_port_listener;
186460573Skris	channel_post[SSH_CHANNEL_AUTH_SOCKET] =		&channel_post_auth_listener;
186592559Sdes	channel_post[SSH_CHANNEL_OPEN] =		&channel_post_open;
186676262Sgreen	channel_post[SSH_CHANNEL_CONNECTING] =		&channel_post_connecting;
186792559Sdes	channel_post[SSH_CHANNEL_DYNAMIC] =		&channel_post_open;
186860573Skris}
186960573Skris
187092559Sdesstatic void
187160573Skrischannel_handler_init(void)
187260573Skris{
187360573Skris	int i;
1874106130Sdes
187592559Sdes	for (i = 0; i < SSH_CHANNEL_MAX_TYPE; i++) {
187660573Skris		channel_pre[i] = NULL;
187760573Skris		channel_post[i] = NULL;
187860573Skris	}
187960573Skris	if (compat20)
188060573Skris		channel_handler_init_20();
188160573Skris	else if (compat13)
188260573Skris		channel_handler_init_13();
188360573Skris	else
188460573Skris		channel_handler_init_15();
188560573Skris}
188660573Skris
188792559Sdes/* gc dead channels */
188892559Sdesstatic void
188992559Sdeschannel_garbage_collect(Channel *c)
189092559Sdes{
189192559Sdes	if (c == NULL)
189292559Sdes		return;
189392559Sdes	if (c->detach_user != NULL) {
1894157019Sdes		if (!chan_is_dead(c, c->detach_close))
189592559Sdes			return;
1896124207Sdes		debug2("channel %d: gc: notify user", c->self);
189792559Sdes		c->detach_user(c->self, NULL);
189892559Sdes		/* if we still have a callback */
189992559Sdes		if (c->detach_user != NULL)
190092559Sdes			return;
1901124207Sdes		debug2("channel %d: gc: user detached", c->self);
190292559Sdes	}
190392559Sdes	if (!chan_is_dead(c, 1))
190492559Sdes		return;
1905124207Sdes	debug2("channel %d: garbage collecting", c->self);
190692559Sdes	channel_free(c);
190792559Sdes}
190892559Sdes
190992559Sdesstatic void
1910162856Sdeschannel_handler(chan_fn *ftab[], fd_set *readset, fd_set *writeset)
191160573Skris{
191260573Skris	static int did_init = 0;
1913137019Sdes	u_int i;
191460573Skris	Channel *c;
191560573Skris
191660573Skris	if (!did_init) {
191760573Skris		channel_handler_init();
191860573Skris		did_init = 1;
191960573Skris	}
192060573Skris	for (i = 0; i < channels_alloc; i++) {
192192559Sdes		c = channels[i];
192292559Sdes		if (c == NULL)
192357429Smarkm			continue;
192492559Sdes		if (ftab[c->type] != NULL)
192592559Sdes			(*ftab[c->type])(c, readset, writeset);
192692559Sdes		channel_garbage_collect(c);
192757429Smarkm	}
192857429Smarkm}
192957429Smarkm
193092559Sdes/*
193192559Sdes * Allocate/update select bitmasks and add any bits relevant to channels in
193292559Sdes * select bitmasks.
193392559Sdes */
193460573Skrisvoid
193576262Sgreenchannel_prepare_select(fd_set **readsetp, fd_set **writesetp, int *maxfdp,
1936137019Sdes    u_int *nallocp, int rekeying)
193760573Skris{
1938162856Sdes	u_int n, sz, nfdset;
193976262Sgreen
194076262Sgreen	n = MAX(*maxfdp, channel_max_fd);
194176262Sgreen
1942162856Sdes	nfdset = howmany(n+1, NFDBITS);
1943162856Sdes	/* Explicitly test here, because xrealloc isn't always called */
1944162856Sdes	if (nfdset && SIZE_T_MAX / nfdset < sizeof(fd_mask))
1945162856Sdes		fatal("channel_prepare_select: max_fd (%d) is too large", n);
1946162856Sdes	sz = nfdset * sizeof(fd_mask);
1947162856Sdes
194892559Sdes	/* perhaps check sz < nalloc/2 and shrink? */
194992559Sdes	if (*readsetp == NULL || sz > *nallocp) {
1950162856Sdes		*readsetp = xrealloc(*readsetp, nfdset, sizeof(fd_mask));
1951162856Sdes		*writesetp = xrealloc(*writesetp, nfdset, sizeof(fd_mask));
195292559Sdes		*nallocp = sz;
195376262Sgreen	}
195492559Sdes	*maxfdp = n;
195576262Sgreen	memset(*readsetp, 0, sz);
195676262Sgreen	memset(*writesetp, 0, sz);
195776262Sgreen
195876262Sgreen	if (!rekeying)
195976262Sgreen		channel_handler(channel_pre, *readsetp, *writesetp);
196060573Skris}
196160573Skris
196292559Sdes/*
196392559Sdes * After select, perform any appropriate operations for channels which have
196492559Sdes * events pending.
196592559Sdes */
196660573Skrisvoid
1967162856Sdeschannel_after_select(fd_set *readset, fd_set *writeset)
196860573Skris{
196960573Skris	channel_handler(channel_post, readset, writeset);
197060573Skris}
197160573Skris
197292559Sdes
197376262Sgreen/* If there is data to send to the connection, enqueue some of it now. */
197460573Skrisvoid
197592559Sdeschannel_output_poll(void)
197657429Smarkm{
197760573Skris	Channel *c;
1978137019Sdes	u_int i, len;
197957429Smarkm
198057429Smarkm	for (i = 0; i < channels_alloc; i++) {
198192559Sdes		c = channels[i];
198292559Sdes		if (c == NULL)
198392559Sdes			continue;
198457429Smarkm
198592559Sdes		/*
198692559Sdes		 * We are only interested in channels that can have buffered
198792559Sdes		 * incoming data.
198892559Sdes		 */
198957429Smarkm		if (compat13) {
199060573Skris			if (c->type != SSH_CHANNEL_OPEN &&
199160573Skris			    c->type != SSH_CHANNEL_INPUT_DRAINING)
199257429Smarkm				continue;
199357429Smarkm		} else {
199460573Skris			if (c->type != SSH_CHANNEL_OPEN)
199557429Smarkm				continue;
199657429Smarkm		}
199760573Skris		if (compat20 &&
199860573Skris		    (c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD))) {
199976262Sgreen			/* XXX is this true? */
200092559Sdes			debug3("channel %d: will not send data after close", c->self);
200160573Skris			continue;
200260573Skris		}
200357429Smarkm
200457429Smarkm		/* Get the amount of buffered data for this channel. */
200576262Sgreen		if ((c->istate == CHAN_INPUT_OPEN ||
200676262Sgreen		    c->istate == CHAN_INPUT_WAIT_DRAIN) &&
200776262Sgreen		    (len = buffer_len(&c->input)) > 0) {
2008157019Sdes			if (c->datagram) {
2009157019Sdes				if (len > 0) {
2010157019Sdes					u_char *data;
2011157019Sdes					u_int dlen;
2012157019Sdes
2013157019Sdes					data = buffer_get_string(&c->input,
2014157019Sdes					    &dlen);
2015157019Sdes					packet_start(SSH2_MSG_CHANNEL_DATA);
2016157019Sdes					packet_put_int(c->remote_id);
2017157019Sdes					packet_put_string(data, dlen);
2018157019Sdes					packet_send();
2019157019Sdes					c->remote_window -= dlen + 4;
2020157019Sdes					xfree(data);
2021157019Sdes				}
2022157019Sdes				continue;
2023157019Sdes			}
202492559Sdes			/*
202592559Sdes			 * Send some data for the other side over the secure
202692559Sdes			 * connection.
202792559Sdes			 */
202860573Skris			if (compat20) {
202960573Skris				if (len > c->remote_window)
203060573Skris					len = c->remote_window;
203160573Skris				if (len > c->remote_maxpacket)
203260573Skris					len = c->remote_maxpacket;
203357429Smarkm			} else {
203460573Skris				if (packet_is_interactive()) {
203560573Skris					if (len > 1024)
203660573Skris						len = 512;
203760573Skris				} else {
203860573Skris					/* Keep the packets at reasonable size. */
203960573Skris					if (len > packet_get_maxsize()/2)
204060573Skris						len = packet_get_maxsize()/2;
204160573Skris				}
204257429Smarkm			}
204360573Skris			if (len > 0) {
204460573Skris				packet_start(compat20 ?
204560573Skris				    SSH2_MSG_CHANNEL_DATA : SSH_MSG_CHANNEL_DATA);
204660573Skris				packet_put_int(c->remote_id);
204760573Skris				packet_put_string(buffer_ptr(&c->input), len);
204860573Skris				packet_send();
204960573Skris				buffer_consume(&c->input, len);
205060573Skris				c->remote_window -= len;
205160573Skris			}
205260573Skris		} else if (c->istate == CHAN_INPUT_WAIT_DRAIN) {
205357429Smarkm			if (compat13)
205457429Smarkm				fatal("cannot happen: istate == INPUT_WAIT_DRAIN for proto 1.3");
205557429Smarkm			/*
205657429Smarkm			 * input-buffer is empty and read-socket shutdown:
205798684Sdes			 * tell peer, that we will not send more data: send IEOF.
205898684Sdes			 * hack for extended data: delay EOF if EFD still in use.
205957429Smarkm			 */
206098684Sdes			if (CHANNEL_EFD_INPUT_ACTIVE(c))
2061149753Sdes				debug2("channel %d: ibuf_empty delayed efd %d/(%d)",
2062149753Sdes				    c->self, c->efd, buffer_len(&c->extended));
206398684Sdes			else
206498684Sdes				chan_ibuf_empty(c);
206557429Smarkm		}
206660573Skris		/* Send extended data, i.e. stderr */
206760573Skris		if (compat20 &&
206898684Sdes		    !(c->flags & CHAN_EOF_SENT) &&
206960573Skris		    c->remote_window > 0 &&
207060573Skris		    (len = buffer_len(&c->extended)) > 0 &&
207160573Skris		    c->extended_usage == CHAN_EXTENDED_READ) {
207299063Sdes			debug2("channel %d: rwin %u elen %u euse %d",
207376262Sgreen			    c->self, c->remote_window, buffer_len(&c->extended),
207476262Sgreen			    c->extended_usage);
207560573Skris			if (len > c->remote_window)
207660573Skris				len = c->remote_window;
207760573Skris			if (len > c->remote_maxpacket)
207860573Skris				len = c->remote_maxpacket;
207960573Skris			packet_start(SSH2_MSG_CHANNEL_EXTENDED_DATA);
208060573Skris			packet_put_int(c->remote_id);
208160573Skris			packet_put_int(SSH2_EXTENDED_DATA_STDERR);
208260573Skris			packet_put_string(buffer_ptr(&c->extended), len);
208360573Skris			packet_send();
208460573Skris			buffer_consume(&c->extended, len);
208560573Skris			c->remote_window -= len;
208676262Sgreen			debug2("channel %d: sent ext data %d", c->self, len);
208760573Skris		}
208857429Smarkm	}
208957429Smarkm}
209057429Smarkm
209157429Smarkm
209292559Sdes/* -- protocol input */
209392559Sdes
2094162856Sdes/* ARGSUSED */
209560573Skrisvoid
209692559Sdeschannel_input_data(int type, u_int32_t seq, void *ctxt)
209757429Smarkm{
209857429Smarkm	int id;
209957429Smarkm	char *data;
210076262Sgreen	u_int data_len;
210160573Skris	Channel *c;
210257429Smarkm
210357429Smarkm	/* Get the channel number and verify it. */
210457429Smarkm	id = packet_get_int();
210560573Skris	c = channel_lookup(id);
210660573Skris	if (c == NULL)
210757429Smarkm		packet_disconnect("Received data for nonexistent channel %d.", id);
210857429Smarkm
210957429Smarkm	/* Ignore any data for non-open channels (might happen on close) */
211060573Skris	if (c->type != SSH_CHANNEL_OPEN &&
211160573Skris	    c->type != SSH_CHANNEL_X11_OPEN)
211257429Smarkm		return;
211357429Smarkm
211457429Smarkm	/* Get the data. */
2115181111Sdes	data = packet_get_string_ptr(&data_len);
211660573Skris
2117126273Sdes	/*
2118126273Sdes	 * Ignore data for protocol > 1.3 if output end is no longer open.
2119126273Sdes	 * For protocol 2 the sending side is reducing its window as it sends
2120126273Sdes	 * data, so we must 'fake' consumption of the data in order to ensure
2121126273Sdes	 * that window updates are sent back.  Otherwise the connection might
2122126273Sdes	 * deadlock.
2123126273Sdes	 */
2124126273Sdes	if (!compat13 && c->ostate != CHAN_OUTPUT_OPEN) {
2125126273Sdes		if (compat20) {
2126126273Sdes			c->local_window -= data_len;
2127126273Sdes			c->local_consumed += data_len;
2128126273Sdes		}
2129126273Sdes		return;
2130126273Sdes	}
2131126273Sdes
213292559Sdes	if (compat20) {
213360573Skris		if (data_len > c->local_maxpacket) {
2134124207Sdes			logit("channel %d: rcvd big packet %d, maxpack %d",
213560573Skris			    c->self, data_len, c->local_maxpacket);
213660573Skris		}
213760573Skris		if (data_len > c->local_window) {
2138124207Sdes			logit("channel %d: rcvd too much data %d, win %d",
213960573Skris			    c->self, data_len, c->local_window);
214060573Skris			return;
214160573Skris		}
214260573Skris		c->local_window -= data_len;
214360573Skris	}
2144157019Sdes	if (c->datagram)
2145157019Sdes		buffer_put_string(&c->output, data, data_len);
2146157019Sdes	else
2147157019Sdes		buffer_append(&c->output, data, data_len);
2148181111Sdes	packet_check_eom();
214957429Smarkm}
215092559Sdes
2151162856Sdes/* ARGSUSED */
215260573Skrisvoid
215392559Sdeschannel_input_extended_data(int type, u_int32_t seq, void *ctxt)
215460573Skris{
215560573Skris	int id;
215660573Skris	char *data;
215799063Sdes	u_int data_len, tcode;
215860573Skris	Channel *c;
215957429Smarkm
216060573Skris	/* Get the channel number and verify it. */
216160573Skris	id = packet_get_int();
216260573Skris	c = channel_lookup(id);
216360573Skris
216460573Skris	if (c == NULL)
216560573Skris		packet_disconnect("Received extended_data for bad channel %d.", id);
216660573Skris	if (c->type != SSH_CHANNEL_OPEN) {
2167124207Sdes		logit("channel %d: ext data for non open", id);
216860573Skris		return;
216960573Skris	}
217098684Sdes	if (c->flags & CHAN_EOF_RCVD) {
217198684Sdes		if (datafellows & SSH_BUG_EXTEOF)
217298684Sdes			debug("channel %d: accepting ext data after eof", id);
217398684Sdes		else
217498684Sdes			packet_disconnect("Received extended_data after EOF "
217598684Sdes			    "on channel %d.", id);
217698684Sdes	}
217760573Skris	tcode = packet_get_int();
217860573Skris	if (c->efd == -1 ||
217960573Skris	    c->extended_usage != CHAN_EXTENDED_WRITE ||
218060573Skris	    tcode != SSH2_EXTENDED_DATA_STDERR) {
2181124207Sdes		logit("channel %d: bad ext data", c->self);
218260573Skris		return;
218360573Skris	}
218460573Skris	data = packet_get_string(&data_len);
218592559Sdes	packet_check_eom();
218660573Skris	if (data_len > c->local_window) {
2187124207Sdes		logit("channel %d: rcvd too much extended_data %d, win %d",
218860573Skris		    c->self, data_len, c->local_window);
218960573Skris		xfree(data);
219060573Skris		return;
219160573Skris	}
219269587Sgreen	debug2("channel %d: rcvd ext data %d", c->self, data_len);
219360573Skris	c->local_window -= data_len;
219460573Skris	buffer_append(&c->extended, data, data_len);
219560573Skris	xfree(data);
219660573Skris}
219760573Skris
2198162856Sdes/* ARGSUSED */
219960573Skrisvoid
220092559Sdeschannel_input_ieof(int type, u_int32_t seq, void *ctxt)
220160573Skris{
220260573Skris	int id;
220360573Skris	Channel *c;
220457429Smarkm
220560573Skris	id = packet_get_int();
220692559Sdes	packet_check_eom();
220760573Skris	c = channel_lookup(id);
220860573Skris	if (c == NULL)
220960573Skris		packet_disconnect("Received ieof for nonexistent channel %d.", id);
221060573Skris	chan_rcvd_ieof(c);
221192559Sdes
221292559Sdes	/* XXX force input close */
221392559Sdes	if (c->force_drain && c->istate == CHAN_INPUT_OPEN) {
221492559Sdes		debug("channel %d: FORCE input drain", c->self);
221592559Sdes		c->istate = CHAN_INPUT_WAIT_DRAIN;
221692559Sdes		if (buffer_len(&c->input) == 0)
221792559Sdes			chan_ibuf_empty(c);
221892559Sdes	}
221992559Sdes
222060573Skris}
222160573Skris
2222162856Sdes/* ARGSUSED */
222360573Skrisvoid
222492559Sdeschannel_input_close(int type, u_int32_t seq, void *ctxt)
222557429Smarkm{
222660573Skris	int id;
222760573Skris	Channel *c;
222857429Smarkm
222960573Skris	id = packet_get_int();
223092559Sdes	packet_check_eom();
223160573Skris	c = channel_lookup(id);
223260573Skris	if (c == NULL)
223360573Skris		packet_disconnect("Received close for nonexistent channel %d.", id);
223457429Smarkm
223557429Smarkm	/*
223657429Smarkm	 * Send a confirmation that we have closed the channel and no more
223757429Smarkm	 * data is coming for it.
223857429Smarkm	 */
223957429Smarkm	packet_start(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION);
224060573Skris	packet_put_int(c->remote_id);
224157429Smarkm	packet_send();
224257429Smarkm
224357429Smarkm	/*
224457429Smarkm	 * If the channel is in closed state, we have sent a close request,
224557429Smarkm	 * and the other side will eventually respond with a confirmation.
224657429Smarkm	 * Thus, we cannot free the channel here, because then there would be
224757429Smarkm	 * no-one to receive the confirmation.  The channel gets freed when
224857429Smarkm	 * the confirmation arrives.
224957429Smarkm	 */
225060573Skris	if (c->type != SSH_CHANNEL_CLOSED) {
225157429Smarkm		/*
225257429Smarkm		 * Not a closed channel - mark it as draining, which will
225357429Smarkm		 * cause it to be freed later.
225457429Smarkm		 */
225592559Sdes		buffer_clear(&c->input);
225660573Skris		c->type = SSH_CHANNEL_OUTPUT_DRAINING;
225757429Smarkm	}
225857429Smarkm}
225957429Smarkm
226060573Skris/* proto version 1.5 overloads CLOSE_CONFIRMATION with OCLOSE */
2261162856Sdes/* ARGSUSED */
226260573Skrisvoid
226392559Sdeschannel_input_oclose(int type, u_int32_t seq, void *ctxt)
226460573Skris{
226560573Skris	int id = packet_get_int();
226660573Skris	Channel *c = channel_lookup(id);
226792559Sdes
226892559Sdes	packet_check_eom();
226960573Skris	if (c == NULL)
227060573Skris		packet_disconnect("Received oclose for nonexistent channel %d.", id);
227160573Skris	chan_rcvd_oclose(c);
227260573Skris}
227357429Smarkm
2274162856Sdes/* ARGSUSED */
227560573Skrisvoid
227692559Sdeschannel_input_close_confirmation(int type, u_int32_t seq, void *ctxt)
227757429Smarkm{
227860573Skris	int id = packet_get_int();
227960573Skris	Channel *c = channel_lookup(id);
228057429Smarkm
228192559Sdes	packet_check_eom();
228260573Skris	if (c == NULL)
228360573Skris		packet_disconnect("Received close confirmation for "
228460573Skris		    "out-of-range channel %d.", id);
228560573Skris	if (c->type != SSH_CHANNEL_CLOSED)
228660573Skris		packet_disconnect("Received close confirmation for "
228760573Skris		    "non-closed channel %d (type %d).", id, c->type);
228892559Sdes	channel_free(c);
228960573Skris}
229057429Smarkm
2291162856Sdes/* ARGSUSED */
229260573Skrisvoid
229392559Sdeschannel_input_open_confirmation(int type, u_int32_t seq, void *ctxt)
229460573Skris{
229560573Skris	int id, remote_id;
229660573Skris	Channel *c;
229760573Skris
229860573Skris	id = packet_get_int();
229960573Skris	c = channel_lookup(id);
230060573Skris
230160573Skris	if (c==NULL || c->type != SSH_CHANNEL_OPENING)
230260573Skris		packet_disconnect("Received open confirmation for "
230360573Skris		    "non-opening channel %d.", id);
230460573Skris	remote_id = packet_get_int();
230560573Skris	/* Record the remote channel number and mark that the channel is now open. */
230660573Skris	c->remote_id = remote_id;
230760573Skris	c->type = SSH_CHANNEL_OPEN;
230860573Skris
230960573Skris	if (compat20) {
231060573Skris		c->remote_window = packet_get_int();
231160573Skris		c->remote_maxpacket = packet_get_int();
2312181111Sdes		if (c->open_confirm) {
231369587Sgreen			debug2("callback start");
2314181111Sdes			c->open_confirm(c->self, c->open_confirm_ctx);
231569587Sgreen			debug2("callback done");
231660573Skris		}
2317124207Sdes		debug2("channel %d: open confirm rwindow %u rmax %u", c->self,
231860573Skris		    c->remote_window, c->remote_maxpacket);
231957429Smarkm	}
232092559Sdes	packet_check_eom();
232157429Smarkm}
232257429Smarkm
232392559Sdesstatic char *
232492559Sdesreason2txt(int reason)
232592559Sdes{
232692559Sdes	switch (reason) {
232792559Sdes	case SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED:
232892559Sdes		return "administratively prohibited";
232992559Sdes	case SSH2_OPEN_CONNECT_FAILED:
233092559Sdes		return "connect failed";
233192559Sdes	case SSH2_OPEN_UNKNOWN_CHANNEL_TYPE:
233292559Sdes		return "unknown channel type";
233392559Sdes	case SSH2_OPEN_RESOURCE_SHORTAGE:
233492559Sdes		return "resource shortage";
233592559Sdes	}
233692559Sdes	return "unknown reason";
233792559Sdes}
233892559Sdes
2339162856Sdes/* ARGSUSED */
234060573Skrisvoid
234192559Sdeschannel_input_open_failure(int type, u_int32_t seq, void *ctxt)
234257429Smarkm{
234376262Sgreen	int id, reason;
234476262Sgreen	char *msg = NULL, *lang = NULL;
234560573Skris	Channel *c;
234657429Smarkm
234760573Skris	id = packet_get_int();
234860573Skris	c = channel_lookup(id);
234957429Smarkm
235060573Skris	if (c==NULL || c->type != SSH_CHANNEL_OPENING)
235160573Skris		packet_disconnect("Received open failure for "
235260573Skris		    "non-opening channel %d.", id);
235360573Skris	if (compat20) {
235476262Sgreen		reason = packet_get_int();
235592559Sdes		if (!(datafellows & SSH_BUG_OPENFAILURE)) {
235676262Sgreen			msg  = packet_get_string(NULL);
235776262Sgreen			lang = packet_get_string(NULL);
235876262Sgreen		}
2359124207Sdes		logit("channel %d: open failed: %s%s%s", id,
236092559Sdes		    reason2txt(reason), msg ? ": ": "", msg ? msg : "");
236176262Sgreen		if (msg != NULL)
236276262Sgreen			xfree(msg);
236376262Sgreen		if (lang != NULL)
236476262Sgreen			xfree(lang);
236560573Skris	}
236692559Sdes	packet_check_eom();
2367192595Sdes	/* Schedule the channel for cleanup/deletion. */
2368192595Sdes	chan_mark_dead(c);
236957429Smarkm}
237057429Smarkm
2371162856Sdes/* ARGSUSED */
237260573Skrisvoid
237392559Sdeschannel_input_window_adjust(int type, u_int32_t seq, void *ctxt)
237460573Skris{
237560573Skris	Channel *c;
237699063Sdes	int id;
237799063Sdes	u_int adjust;
237857429Smarkm
237960573Skris	if (!compat20)
238060573Skris		return;
238160573Skris
238257429Smarkm	/* Get the channel number and verify it. */
238360573Skris	id = packet_get_int();
238460573Skris	c = channel_lookup(id);
238557429Smarkm
2386157019Sdes	if (c == NULL) {
2387157019Sdes		logit("Received window adjust for non-open channel %d.", id);
238860573Skris		return;
238960573Skris	}
239060573Skris	adjust = packet_get_int();
239192559Sdes	packet_check_eom();
239299063Sdes	debug2("channel %d: rcvd adjust %u", id, adjust);
239360573Skris	c->remote_window += adjust;
239457429Smarkm}
239557429Smarkm
2396162856Sdes/* ARGSUSED */
239760573Skrisvoid
239892559Sdeschannel_input_port_open(int type, u_int32_t seq, void *ctxt)
239957429Smarkm{
240092559Sdes	Channel *c = NULL;
240192559Sdes	u_short host_port;
240292559Sdes	char *host, *originator_string;
2403181111Sdes	int remote_id;
240457429Smarkm
240592559Sdes	remote_id = packet_get_int();
240692559Sdes	host = packet_get_string(NULL);
240792559Sdes	host_port = packet_get_int();
240857429Smarkm
240992559Sdes	if (packet_get_protocol_flags() & SSH_PROTOFLAG_HOST_IN_FWD_OPEN) {
241092559Sdes		originator_string = packet_get_string(NULL);
241192559Sdes	} else {
241292559Sdes		originator_string = xstrdup("unknown (remote did not supply name)");
241392559Sdes	}
241492559Sdes	packet_check_eom();
2415181111Sdes	c = channel_connect_to(host, host_port,
2416181111Sdes	    "connected socket", originator_string);
2417124207Sdes	xfree(originator_string);
2418181111Sdes	xfree(host);
241992559Sdes	if (c == NULL) {
242092559Sdes		packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE);
242192559Sdes		packet_put_int(remote_id);
242292559Sdes		packet_send();
2423181111Sdes	} else
2424181111Sdes		c->remote_id = remote_id;
242557429Smarkm}
242657429Smarkm
2427181111Sdes/* ARGSUSED */
2428181111Sdesvoid
2429181111Sdeschannel_input_status_confirm(int type, u_int32_t seq, void *ctxt)
2430181111Sdes{
2431181111Sdes	Channel *c;
2432181111Sdes	struct channel_confirm *cc;
2433192595Sdes	int id;
243457429Smarkm
2435181111Sdes	/* Reset keepalive timeout */
2436197679Sdes	packet_set_alive_timeouts(0);
2437181111Sdes
2438192595Sdes	id = packet_get_int();
2439181111Sdes	packet_check_eom();
2440181111Sdes
2441192595Sdes	debug2("channel_input_status_confirm: type %d id %d", type, id);
2442181111Sdes
2443192595Sdes	if ((c = channel_lookup(id)) == NULL) {
2444192595Sdes		logit("channel_input_status_confirm: %d: unknown", id);
2445181111Sdes		return;
2446181111Sdes	}
2447181111Sdes	;
2448181111Sdes	if ((cc = TAILQ_FIRST(&c->status_confirms)) == NULL)
2449181111Sdes		return;
2450181111Sdes	cc->cb(type, c, cc->ctx);
2451181111Sdes	TAILQ_REMOVE(&c->status_confirms, cc, entry);
2452181111Sdes	bzero(cc, sizeof(*cc));
2453181111Sdes	xfree(cc);
2454181111Sdes}
2455181111Sdes
245692559Sdes/* -- tcp forwarding */
245757429Smarkm
245892559Sdesvoid
245992559Sdeschannel_set_af(int af)
246076262Sgreen{
246192559Sdes	IPv4or6 = af;
246276262Sgreen}
246376262Sgreen
246492559Sdesstatic int
2465192595Sdeschannel_setup_fwd_listener(int type, const char *listen_addr,
2466192595Sdes    u_short listen_port, int *allocated_listen_port,
246792559Sdes    const char *host_to_connect, u_short port_to_connect, int gateway_ports)
246857429Smarkm{
246992559Sdes	Channel *c;
2470157019Sdes	int sock, r, success = 0, wildcard = 0, is_client;
247157429Smarkm	struct addrinfo hints, *ai, *aitop;
2472147005Sdes	const char *host, *addr;
247357429Smarkm	char ntop[NI_MAXHOST], strport[NI_MAXSERV];
2474192595Sdes	in_port_t *lport_p;
247557429Smarkm
247692559Sdes	host = (type == SSH_CHANNEL_RPORT_LISTENER) ?
247792559Sdes	    listen_addr : host_to_connect;
2478147005Sdes	is_client = (type == SSH_CHANNEL_PORT_LISTENER);
247957429Smarkm
248092559Sdes	if (host == NULL) {
248192559Sdes		error("No forward host name.");
2482149753Sdes		return 0;
248376262Sgreen	}
2484192595Sdes	if (strlen(host) >= NI_MAXHOST) {
248576262Sgreen		error("Forward host name too long.");
2486149753Sdes		return 0;
248776262Sgreen	}
248876262Sgreen
248957429Smarkm	/*
2490147005Sdes	 * Determine whether or not a port forward listens to loopback,
2491147005Sdes	 * specified address or wildcard. On the client, a specified bind
2492147005Sdes	 * address will always override gateway_ports. On the server, a
2493147005Sdes	 * gateway_ports of 1 (``yes'') will override the client's
2494147005Sdes	 * specification and force a wildcard bind, whereas a value of 2
2495147005Sdes	 * (``clientspecified'') will bind to whatever address the client
2496147005Sdes	 * asked for.
2497147005Sdes	 *
2498147005Sdes	 * Special-case listen_addrs are:
2499147005Sdes	 *
2500147005Sdes	 * "0.0.0.0"               -> wildcard v4/v6 if SSH_OLD_FORWARD_ADDR
2501147005Sdes	 * "" (empty string), "*"  -> wildcard v4/v6
2502147005Sdes	 * "localhost"             -> loopback v4/v6
2503147005Sdes	 */
2504147005Sdes	addr = NULL;
2505147005Sdes	if (listen_addr == NULL) {
2506147005Sdes		/* No address specified: default to gateway_ports setting */
2507147005Sdes		if (gateway_ports)
2508147005Sdes			wildcard = 1;
2509147005Sdes	} else if (gateway_ports || is_client) {
2510147005Sdes		if (((datafellows & SSH_OLD_FORWARD_ADDR) &&
2511181111Sdes		    strcmp(listen_addr, "0.0.0.0") == 0 && is_client == 0) ||
2512147005Sdes		    *listen_addr == '\0' || strcmp(listen_addr, "*") == 0 ||
2513147005Sdes		    (!is_client && gateway_ports == 1))
2514147005Sdes			wildcard = 1;
2515147005Sdes		else if (strcmp(listen_addr, "localhost") != 0)
2516147005Sdes			addr = listen_addr;
2517147005Sdes	}
2518147005Sdes
2519147005Sdes	debug3("channel_setup_fwd_listener: type %d wildcard %d addr %s",
2520147005Sdes	    type, wildcard, (addr == NULL) ? "NULL" : addr);
2521147005Sdes
2522147005Sdes	/*
252357429Smarkm	 * getaddrinfo returns a loopback address if the hostname is
252457429Smarkm	 * set to NULL and hints.ai_flags is not AI_PASSIVE
252557429Smarkm	 */
252657429Smarkm	memset(&hints, 0, sizeof(hints));
252757429Smarkm	hints.ai_family = IPv4or6;
2528147005Sdes	hints.ai_flags = wildcard ? AI_PASSIVE : 0;
252957429Smarkm	hints.ai_socktype = SOCK_STREAM;
253076262Sgreen	snprintf(strport, sizeof strport, "%d", listen_port);
2531147005Sdes	if ((r = getaddrinfo(addr, strport, &hints, &aitop)) != 0) {
2532147005Sdes		if (addr == NULL) {
2533147005Sdes			/* This really shouldn't happen */
2534147005Sdes			packet_disconnect("getaddrinfo: fatal error: %s",
2535181111Sdes			    ssh_gai_strerror(r));
2536147005Sdes		} else {
2537149753Sdes			error("channel_setup_fwd_listener: "
2538181111Sdes			    "getaddrinfo(%.64s): %s", addr,
2539181111Sdes			    ssh_gai_strerror(r));
2540147005Sdes		}
2541149753Sdes		return 0;
2542147005Sdes	}
2543192595Sdes	if (allocated_listen_port != NULL)
2544192595Sdes		*allocated_listen_port = 0;
254557429Smarkm	for (ai = aitop; ai; ai = ai->ai_next) {
2546192595Sdes		switch (ai->ai_family) {
2547192595Sdes		case AF_INET:
2548192595Sdes			lport_p = &((struct sockaddr_in *)ai->ai_addr)->
2549192595Sdes			    sin_port;
2550192595Sdes			break;
2551192595Sdes		case AF_INET6:
2552192595Sdes			lport_p = &((struct sockaddr_in6 *)ai->ai_addr)->
2553192595Sdes			    sin6_port;
2554192595Sdes			break;
2555192595Sdes		default:
255657429Smarkm			continue;
2557192595Sdes		}
2558192595Sdes		/*
2559192595Sdes		 * If allocating a port for -R forwards, then use the
2560192595Sdes		 * same port for all address families.
2561192595Sdes		 */
2562192595Sdes		if (type == SSH_CHANNEL_RPORT_LISTENER && listen_port == 0 &&
2563192595Sdes		    allocated_listen_port != NULL && *allocated_listen_port > 0)
2564192595Sdes			*lport_p = htons(*allocated_listen_port);
2565192595Sdes
256657429Smarkm		if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop),
256757429Smarkm		    strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV) != 0) {
256892559Sdes			error("channel_setup_fwd_listener: getnameinfo failed");
256957429Smarkm			continue;
257057429Smarkm		}
257157429Smarkm		/* Create a port to listen for the host. */
2572124207Sdes		sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
257357429Smarkm		if (sock < 0) {
257457429Smarkm			/* this is no error since kernel may not support ipv6 */
257557429Smarkm			verbose("socket: %.100s", strerror(errno));
257657429Smarkm			continue;
257757429Smarkm		}
2578106130Sdes
2579157019Sdes		channel_set_reuseaddr(sock);
2580157019Sdes
2581192595Sdes		debug("Local forwarding listening on %s port %s.",
2582192595Sdes		    ntop, strport);
258357429Smarkm
258457429Smarkm		/* Bind the socket to the address. */
258557429Smarkm		if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) {
258657429Smarkm			/* address can be in use ipv6 address is already bound */
258798941Sdes			if (!ai->ai_next)
258898941Sdes				error("bind: %.100s", strerror(errno));
258998941Sdes			else
259098941Sdes				verbose("bind: %.100s", strerror(errno));
259198941Sdes
259257429Smarkm			close(sock);
259357429Smarkm			continue;
259457429Smarkm		}
259557429Smarkm		/* Start listening for connections on the socket. */
2596126273Sdes		if (listen(sock, SSH_LISTEN_BACKLOG) < 0) {
259757429Smarkm			error("listen: %.100s", strerror(errno));
259857429Smarkm			close(sock);
259957429Smarkm			continue;
260057429Smarkm		}
2601192595Sdes
2602192595Sdes		/*
2603192595Sdes		 * listen_port == 0 requests a dynamically allocated port -
2604192595Sdes		 * record what we got.
2605192595Sdes		 */
2606192595Sdes		if (type == SSH_CHANNEL_RPORT_LISTENER && listen_port == 0 &&
2607192595Sdes		    allocated_listen_port != NULL &&
2608192595Sdes		    *allocated_listen_port == 0) {
2609192595Sdes			*allocated_listen_port = get_sock_port(sock, 1);
2610192595Sdes			debug("Allocated listen port %d",
2611192595Sdes			    *allocated_listen_port);
2612192595Sdes		}
2613192595Sdes
261457429Smarkm		/* Allocate a channel number for the socket. */
261592559Sdes		c = channel_new("port listener", type, sock, sock, -1,
261660573Skris		    CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT,
2617124207Sdes		    0, "port listener", 1);
2618192595Sdes		c->path = xstrdup(host);
261992559Sdes		c->host_port = port_to_connect;
262092559Sdes		c->listening_port = listen_port;
262157429Smarkm		success = 1;
262257429Smarkm	}
262357429Smarkm	if (success == 0)
262492559Sdes		error("channel_setup_fwd_listener: cannot listen to port: %d",
262576262Sgreen		    listen_port);
262657429Smarkm	freeaddrinfo(aitop);
262776262Sgreen	return success;
262857429Smarkm}
262957429Smarkm
2630137019Sdesint
2631137019Sdeschannel_cancel_rport_listener(const char *host, u_short port)
2632137019Sdes{
2633137019Sdes	u_int i;
2634137019Sdes	int found = 0;
2635137019Sdes
2636147005Sdes	for (i = 0; i < channels_alloc; i++) {
2637137019Sdes		Channel *c = channels[i];
2638137019Sdes
2639137019Sdes		if (c != NULL && c->type == SSH_CHANNEL_RPORT_LISTENER &&
2640192595Sdes		    strcmp(c->path, host) == 0 && c->listening_port == port) {
2641147005Sdes			debug2("%s: close channel %d", __func__, i);
2642137019Sdes			channel_free(c);
2643137019Sdes			found = 1;
2644137019Sdes		}
2645137019Sdes	}
2646137019Sdes
2647137019Sdes	return (found);
2648137019Sdes}
2649137019Sdes
265092559Sdes/* protocol local port fwd, used by ssh (and sshd in v1) */
265192559Sdesint
2652147005Sdeschannel_setup_local_fwd_listener(const char *listen_host, u_short listen_port,
265392559Sdes    const char *host_to_connect, u_short port_to_connect, int gateway_ports)
265492559Sdes{
265592559Sdes	return channel_setup_fwd_listener(SSH_CHANNEL_PORT_LISTENER,
2656192595Sdes	    listen_host, listen_port, NULL, host_to_connect, port_to_connect,
2657147005Sdes	    gateway_ports);
265892559Sdes}
265992559Sdes
266092559Sdes/* protocol v2 remote port fwd, used by sshd */
266192559Sdesint
266292559Sdeschannel_setup_remote_fwd_listener(const char *listen_address,
2663192595Sdes    u_short listen_port, int *allocated_listen_port, int gateway_ports)
266492559Sdes{
266592559Sdes	return channel_setup_fwd_listener(SSH_CHANNEL_RPORT_LISTENER,
2666192595Sdes	    listen_address, listen_port, allocated_listen_port,
2667192595Sdes	    NULL, 0, gateway_ports);
266892559Sdes}
266992559Sdes
267057429Smarkm/*
267157429Smarkm * Initiate forwarding of connections to port "port" on remote host through
267257429Smarkm * the secure channel to host:port from local side.
267357429Smarkm */
267457429Smarkm
2675162856Sdesint
2676147005Sdeschannel_request_remote_forwarding(const char *listen_host, u_short listen_port,
267776262Sgreen    const char *host_to_connect, u_short port_to_connect)
267857429Smarkm{
267992559Sdes	int type, success = 0;
268076262Sgreen
268157429Smarkm	/* Record locally that connection to this host/port is permitted. */
268257429Smarkm	if (num_permitted_opens >= SSH_MAX_FORWARDS_PER_DIRECTION)
268357429Smarkm		fatal("channel_request_remote_forwarding: too many forwards");
268457429Smarkm
268557429Smarkm	/* Send the forward request to the remote side. */
268660573Skris	if (compat20) {
2687147005Sdes		const char *address_to_bind;
2688181111Sdes		if (listen_host == NULL) {
2689181111Sdes			if (datafellows & SSH_BUG_RFWD_ADDR)
2690181111Sdes				address_to_bind = "127.0.0.1";
2691181111Sdes			else
2692181111Sdes				address_to_bind = "localhost";
2693181111Sdes		} else if (*listen_host == '\0' ||
2694181111Sdes			   strcmp(listen_host, "*") == 0) {
2695181111Sdes			if (datafellows & SSH_BUG_RFWD_ADDR)
2696181111Sdes				address_to_bind = "0.0.0.0";
2697181111Sdes			else
2698181111Sdes				address_to_bind = "";
2699181111Sdes		} else
2700147005Sdes			address_to_bind = listen_host;
2701147005Sdes
270260573Skris		packet_start(SSH2_MSG_GLOBAL_REQUEST);
270360573Skris		packet_put_cstring("tcpip-forward");
270498684Sdes		packet_put_char(1);			/* boolean: want reply */
270560573Skris		packet_put_cstring(address_to_bind);
270660573Skris		packet_put_int(listen_port);
270776262Sgreen		packet_send();
270876262Sgreen		packet_write_wait();
270976262Sgreen		/* Assume that server accepts the request */
271076262Sgreen		success = 1;
271160573Skris	} else {
271260573Skris		packet_start(SSH_CMSG_PORT_FORWARD_REQUEST);
271360573Skris		packet_put_int(listen_port);
271460573Skris		packet_put_cstring(host_to_connect);
271560573Skris		packet_put_int(port_to_connect);
271660573Skris		packet_send();
271760573Skris		packet_write_wait();
271876262Sgreen
271976262Sgreen		/* Wait for response from the remote side. */
272092559Sdes		type = packet_read();
272176262Sgreen		switch (type) {
272276262Sgreen		case SSH_SMSG_SUCCESS:
272376262Sgreen			success = 1;
272476262Sgreen			break;
272576262Sgreen		case SSH_SMSG_FAILURE:
272676262Sgreen			break;
272776262Sgreen		default:
272876262Sgreen			/* Unknown packet */
272976262Sgreen			packet_disconnect("Protocol error for port forward request:"
273076262Sgreen			    "received packet type %d.", type);
273176262Sgreen		}
273260573Skris	}
273376262Sgreen	if (success) {
273476262Sgreen		permitted_opens[num_permitted_opens].host_to_connect = xstrdup(host_to_connect);
273576262Sgreen		permitted_opens[num_permitted_opens].port_to_connect = port_to_connect;
273676262Sgreen		permitted_opens[num_permitted_opens].listen_port = listen_port;
273776262Sgreen		num_permitted_opens++;
273876262Sgreen	}
2739162856Sdes	return (success ? 0 : -1);
274057429Smarkm}
274157429Smarkm
274257429Smarkm/*
2743137019Sdes * Request cancellation of remote forwarding of connection host:port from
2744137019Sdes * local side.
2745137019Sdes */
2746137019Sdesvoid
2747147005Sdeschannel_request_rforward_cancel(const char *host, u_short port)
2748137019Sdes{
2749137019Sdes	int i;
2750137019Sdes
2751137019Sdes	if (!compat20)
2752137019Sdes		return;
2753137019Sdes
2754137019Sdes	for (i = 0; i < num_permitted_opens; i++) {
2755137019Sdes		if (permitted_opens[i].host_to_connect != NULL &&
2756137019Sdes		    permitted_opens[i].listen_port == port)
2757137019Sdes			break;
2758137019Sdes	}
2759137019Sdes	if (i >= num_permitted_opens) {
2760137019Sdes		debug("%s: requested forward not found", __func__);
2761137019Sdes		return;
2762137019Sdes	}
2763137019Sdes	packet_start(SSH2_MSG_GLOBAL_REQUEST);
2764137019Sdes	packet_put_cstring("cancel-tcpip-forward");
2765137019Sdes	packet_put_char(0);
2766147005Sdes	packet_put_cstring(host == NULL ? "" : host);
2767137019Sdes	packet_put_int(port);
2768137019Sdes	packet_send();
2769137019Sdes
2770137019Sdes	permitted_opens[i].listen_port = 0;
2771137019Sdes	permitted_opens[i].port_to_connect = 0;
2772157019Sdes	xfree(permitted_opens[i].host_to_connect);
2773137019Sdes	permitted_opens[i].host_to_connect = NULL;
2774137019Sdes}
2775137019Sdes
2776137019Sdes/*
277757429Smarkm * This is called after receiving CHANNEL_FORWARDING_REQUEST.  This initates
277857429Smarkm * listening for the port, and sends back a success reply (or disconnect
2779162856Sdes * message if there was an error).
278057429Smarkm */
2781162856Sdesint
278260573Skrischannel_input_port_forward_request(int is_root, int gateway_ports)
278357429Smarkm{
278457429Smarkm	u_short port, host_port;
2785162856Sdes	int success = 0;
278657429Smarkm	char *hostname;
278757429Smarkm
278857429Smarkm	/* Get arguments from the packet. */
278957429Smarkm	port = packet_get_int();
279057429Smarkm	hostname = packet_get_string(NULL);
279157429Smarkm	host_port = packet_get_int();
279257429Smarkm
279398941Sdes#ifndef HAVE_CYGWIN
279457429Smarkm	/*
279557429Smarkm	 * Check that an unprivileged user is not trying to forward a
279657429Smarkm	 * privileged port.
279757429Smarkm	 */
279857429Smarkm	if (port < IPPORT_RESERVED && !is_root)
2799124207Sdes		packet_disconnect(
2800124207Sdes		    "Requested forwarding of port %d but user is not root.",
2801124207Sdes		    port);
2802124207Sdes	if (host_port == 0)
2803124207Sdes		packet_disconnect("Dynamic forwarding denied.");
280498941Sdes#endif
2805124207Sdes
280676262Sgreen	/* Initiate forwarding */
2807162856Sdes	success = channel_setup_local_fwd_listener(NULL, port, hostname,
2808147005Sdes	    host_port, gateway_ports);
280957429Smarkm
281057429Smarkm	/* Free the argument string. */
281157429Smarkm	xfree(hostname);
2812162856Sdes
2813162856Sdes	return (success ? 0 : -1);
281457429Smarkm}
281557429Smarkm
281676262Sgreen/*
281776262Sgreen * Permits opening to any host/port if permitted_opens[] is empty.  This is
281876262Sgreen * usually called by the server, because the user could connect to any port
281976262Sgreen * anyway, and the server has no way to know but to trust the client anyway.
282076262Sgreen */
282176262Sgreenvoid
282292559Sdeschannel_permit_all_opens(void)
282376262Sgreen{
282476262Sgreen	if (num_permitted_opens == 0)
282576262Sgreen		all_opens_permitted = 1;
282676262Sgreen}
282776262Sgreen
282876262Sgreenvoid
282976262Sgreenchannel_add_permitted_opens(char *host, int port)
283076262Sgreen{
283176262Sgreen	if (num_permitted_opens >= SSH_MAX_FORWARDS_PER_DIRECTION)
2832162856Sdes		fatal("channel_add_permitted_opens: too many forwards");
283376262Sgreen	debug("allow port forwarding to host %s port %d", host, port);
283476262Sgreen
283576262Sgreen	permitted_opens[num_permitted_opens].host_to_connect = xstrdup(host);
283676262Sgreen	permitted_opens[num_permitted_opens].port_to_connect = port;
283776262Sgreen	num_permitted_opens++;
283876262Sgreen
283976262Sgreen	all_opens_permitted = 0;
284076262Sgreen}
284176262Sgreen
2842162856Sdesint
2843162856Sdeschannel_add_adm_permitted_opens(char *host, int port)
2844162856Sdes{
2845162856Sdes	if (num_adm_permitted_opens >= SSH_MAX_FORWARDS_PER_DIRECTION)
2846162856Sdes		fatal("channel_add_adm_permitted_opens: too many forwards");
2847162856Sdes	debug("config allows port forwarding to host %s port %d", host, port);
2848162856Sdes
2849162856Sdes	permitted_adm_opens[num_adm_permitted_opens].host_to_connect
2850162856Sdes	     = xstrdup(host);
2851162856Sdes	permitted_adm_opens[num_adm_permitted_opens].port_to_connect = port;
2852162856Sdes	return ++num_adm_permitted_opens;
2853162856Sdes}
2854162856Sdes
285576262Sgreenvoid
285676262Sgreenchannel_clear_permitted_opens(void)
285776262Sgreen{
285876262Sgreen	int i;
285976262Sgreen
286076262Sgreen	for (i = 0; i < num_permitted_opens; i++)
2861137019Sdes		if (permitted_opens[i].host_to_connect != NULL)
2862137019Sdes			xfree(permitted_opens[i].host_to_connect);
286376262Sgreen	num_permitted_opens = 0;
2864162856Sdes}
286576262Sgreen
2866162856Sdesvoid
2867162856Sdeschannel_clear_adm_permitted_opens(void)
2868162856Sdes{
2869162856Sdes	int i;
2870162856Sdes
2871162856Sdes	for (i = 0; i < num_adm_permitted_opens; i++)
2872162856Sdes		if (permitted_adm_opens[i].host_to_connect != NULL)
2873162856Sdes			xfree(permitted_adm_opens[i].host_to_connect);
2874162856Sdes	num_adm_permitted_opens = 0;
287576262Sgreen}
287676262Sgreen
2877181111Sdesvoid
2878181111Sdeschannel_print_adm_permitted_opens(void)
2879181111Sdes{
2880181111Sdes	int i;
2881181111Sdes
2882192595Sdes	printf("permitopen");
2883192595Sdes	if (num_adm_permitted_opens == 0) {
2884192595Sdes		printf(" any\n");
2885192595Sdes		return;
2886192595Sdes	}
2887181111Sdes	for (i = 0; i < num_adm_permitted_opens; i++)
2888181111Sdes		if (permitted_adm_opens[i].host_to_connect != NULL)
2889181111Sdes			printf(" %s:%d", permitted_adm_opens[i].host_to_connect,
2890181111Sdes			    permitted_adm_opens[i].port_to_connect);
2891192595Sdes	printf("\n");
2892181111Sdes}
2893181111Sdes
2894181111Sdes/* Try to start non-blocking connect to next host in cctx list */
289592559Sdesstatic int
2896181111Sdesconnect_next(struct channel_connect *cctx)
289760573Skris{
2898181111Sdes	int sock, saved_errno;
289960573Skris	char ntop[NI_MAXHOST], strport[NI_MAXSERV];
290060573Skris
2901181111Sdes	for (; cctx->ai; cctx->ai = cctx->ai->ai_next) {
2902181111Sdes		if (cctx->ai->ai_family != AF_INET &&
2903181111Sdes		    cctx->ai->ai_family != AF_INET6)
290460573Skris			continue;
2905181111Sdes		if (getnameinfo(cctx->ai->ai_addr, cctx->ai->ai_addrlen,
2906181111Sdes		    ntop, sizeof(ntop), strport, sizeof(strport),
2907181111Sdes		    NI_NUMERICHOST|NI_NUMERICSERV) != 0) {
2908181111Sdes			error("connect_next: getnameinfo failed");
290960573Skris			continue;
291060573Skris		}
2911181111Sdes		if ((sock = socket(cctx->ai->ai_family, cctx->ai->ai_socktype,
2912181111Sdes		    cctx->ai->ai_protocol)) == -1) {
2913181111Sdes			if (cctx->ai->ai_next == NULL)
2914113911Sdes				error("socket: %.100s", strerror(errno));
2915113911Sdes			else
2916113911Sdes				verbose("socket: %.100s", strerror(errno));
291760573Skris			continue;
291860573Skris		}
2919137019Sdes		if (set_nonblock(sock) == -1)
2920137019Sdes			fatal("%s: set_nonblock(%d)", __func__, sock);
2921181111Sdes		if (connect(sock, cctx->ai->ai_addr,
2922181111Sdes		    cctx->ai->ai_addrlen) == -1 && errno != EINPROGRESS) {
2923181111Sdes			debug("connect_next: host %.100s ([%.100s]:%s): "
2924181111Sdes			    "%.100s", cctx->host, ntop, strport,
292560573Skris			    strerror(errno));
2926181111Sdes			saved_errno = errno;
292760573Skris			close(sock);
2928181111Sdes			errno = saved_errno;
292976262Sgreen			continue;	/* fail -- try next */
293060573Skris		}
2931181111Sdes		debug("connect_next: host %.100s ([%.100s]:%s) "
2932181111Sdes		    "in progress, fd=%d", cctx->host, ntop, strport, sock);
2933181111Sdes		cctx->ai = cctx->ai->ai_next;
2934181111Sdes		set_nodelay(sock);
2935181111Sdes		return sock;
2936181111Sdes	}
2937181111Sdes	return -1;
2938181111Sdes}
293960573Skris
2940181111Sdesstatic void
2941181111Sdeschannel_connect_ctx_free(struct channel_connect *cctx)
2942181111Sdes{
2943181111Sdes	xfree(cctx->host);
2944181111Sdes	if (cctx->aitop)
2945181111Sdes		freeaddrinfo(cctx->aitop);
2946181111Sdes	bzero(cctx, sizeof(*cctx));
2947181111Sdes	cctx->host = NULL;
2948181111Sdes	cctx->ai = cctx->aitop = NULL;
2949181111Sdes}
2950181111Sdes
2951181111Sdes/* Return CONNECTING channel to remote host, port */
2952181111Sdesstatic Channel *
2953181111Sdesconnect_to(const char *host, u_short port, char *ctype, char *rname)
2954181111Sdes{
2955181111Sdes	struct addrinfo hints;
2956181111Sdes	int gaierr;
2957181111Sdes	int sock = -1;
2958181111Sdes	char strport[NI_MAXSERV];
2959181111Sdes	struct channel_connect cctx;
2960181111Sdes	Channel *c;
2961181111Sdes
2962181111Sdes	memset(&cctx, 0, sizeof(cctx));
2963181111Sdes	memset(&hints, 0, sizeof(hints));
2964181111Sdes	hints.ai_family = IPv4or6;
2965181111Sdes	hints.ai_socktype = SOCK_STREAM;
2966181111Sdes	snprintf(strport, sizeof strport, "%d", port);
2967181111Sdes	if ((gaierr = getaddrinfo(host, strport, &hints, &cctx.aitop)) != 0) {
2968181111Sdes		error("connect_to %.100s: unknown host (%s)", host,
2969181111Sdes		    ssh_gai_strerror(gaierr));
2970181111Sdes		return NULL;
297160573Skris	}
2972181111Sdes
2973181111Sdes	cctx.host = xstrdup(host);
2974181111Sdes	cctx.port = port;
2975181111Sdes	cctx.ai = cctx.aitop;
2976181111Sdes
2977181111Sdes	if ((sock = connect_next(&cctx)) == -1) {
2978181111Sdes		error("connect to %.100s port %d failed: %s",
2979181111Sdes		    host, port, strerror(errno));
2980181111Sdes		channel_connect_ctx_free(&cctx);
2981181111Sdes		return NULL;
298260573Skris	}
2983181111Sdes	c = channel_new(ctype, SSH_CHANNEL_CONNECTING, sock, sock, -1,
2984181111Sdes	    CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, rname, 1);
2985181111Sdes	c->connect_ctx = cctx;
2986181111Sdes	return c;
298760573Skris}
298876262Sgreen
2989181111SdesChannel *
2990181111Sdeschannel_connect_by_listen_address(u_short listen_port, char *ctype, char *rname)
299176262Sgreen{
299276262Sgreen	int i;
299376262Sgreen
2994181111Sdes	for (i = 0; i < num_permitted_opens; i++) {
2995137019Sdes		if (permitted_opens[i].host_to_connect != NULL &&
2996181111Sdes		    permitted_opens[i].listen_port == listen_port) {
299776262Sgreen			return connect_to(
299876262Sgreen			    permitted_opens[i].host_to_connect,
2999181111Sdes			    permitted_opens[i].port_to_connect, ctype, rname);
3000181111Sdes		}
3001181111Sdes	}
300276262Sgreen	error("WARNING: Server requests forwarding for unknown listen_port %d",
300376262Sgreen	    listen_port);
3004181111Sdes	return NULL;
300576262Sgreen}
300676262Sgreen
300776262Sgreen/* Check if connecting to that port is permitted and connect. */
3008181111SdesChannel *
3009181111Sdeschannel_connect_to(const char *host, u_short port, char *ctype, char *rname)
301076262Sgreen{
3011162856Sdes	int i, permit, permit_adm = 1;
301276262Sgreen
301376262Sgreen	permit = all_opens_permitted;
301476262Sgreen	if (!permit) {
301576262Sgreen		for (i = 0; i < num_permitted_opens; i++)
3016137019Sdes			if (permitted_opens[i].host_to_connect != NULL &&
3017137019Sdes			    permitted_opens[i].port_to_connect == port &&
301876262Sgreen			    strcmp(permitted_opens[i].host_to_connect, host) == 0)
301976262Sgreen				permit = 1;
3020162856Sdes	}
302176262Sgreen
3022162856Sdes	if (num_adm_permitted_opens > 0) {
3023162856Sdes		permit_adm = 0;
3024162856Sdes		for (i = 0; i < num_adm_permitted_opens; i++)
3025162856Sdes			if (permitted_adm_opens[i].host_to_connect != NULL &&
3026162856Sdes			    permitted_adm_opens[i].port_to_connect == port &&
3027162856Sdes			    strcmp(permitted_adm_opens[i].host_to_connect, host)
3028162856Sdes			    == 0)
3029162856Sdes				permit_adm = 1;
303076262Sgreen	}
3031162856Sdes
3032162856Sdes	if (!permit || !permit_adm) {
3033124207Sdes		logit("Received request to connect to host %.100s port %d, "
303476262Sgreen		    "but the request was denied.", host, port);
3035181111Sdes		return NULL;
303676262Sgreen	}
3037181111Sdes	return connect_to(host, port, ctype, rname);
303876262Sgreen}
303976262Sgreen
3040137019Sdesvoid
3041137019Sdeschannel_send_window_changes(void)
3042137019Sdes{
3043137019Sdes	u_int i;
3044137019Sdes	struct winsize ws;
3045137019Sdes
3046137019Sdes	for (i = 0; i < channels_alloc; i++) {
3047147005Sdes		if (channels[i] == NULL || !channels[i]->client_tty ||
3048137019Sdes		    channels[i]->type != SSH_CHANNEL_OPEN)
3049137019Sdes			continue;
3050137019Sdes		if (ioctl(channels[i]->rfd, TIOCGWINSZ, &ws) < 0)
3051137019Sdes			continue;
3052137019Sdes		channel_request_start(i, "window-change", 0);
3053162856Sdes		packet_put_int((u_int)ws.ws_col);
3054162856Sdes		packet_put_int((u_int)ws.ws_row);
3055162856Sdes		packet_put_int((u_int)ws.ws_xpixel);
3056162856Sdes		packet_put_int((u_int)ws.ws_ypixel);
3057137019Sdes		packet_send();
3058137019Sdes	}
3059137019Sdes}
3060137019Sdes
306192559Sdes/* -- X11 forwarding */
306257429Smarkm
306357429Smarkm/*
306457429Smarkm * Creates an internet domain socket for listening for X11 connections.
306599063Sdes * Returns 0 and a suitable display number for the DISPLAY variable
306699063Sdes * stored in display_numberp , or -1 if an error occurs.
306757429Smarkm */
306892559Sdesint
306992559Sdesx11_create_display_inet(int x11_display_offset, int x11_use_localhost,
3070149753Sdes    int single_connection, u_int *display_numberp, int **chanids)
307157429Smarkm{
307292559Sdes	Channel *nc = NULL;
307357429Smarkm	int display_number, sock;
307457429Smarkm	u_short port;
307557429Smarkm	struct addrinfo hints, *ai, *aitop;
307657429Smarkm	char strport[NI_MAXSERV];
307757429Smarkm	int gaierr, n, num_socks = 0, socks[NUM_SOCKS];
307857429Smarkm
3079157019Sdes	if (chanids == NULL)
3080157019Sdes		return -1;
3081157019Sdes
308257429Smarkm	for (display_number = x11_display_offset;
308392559Sdes	    display_number < MAX_DISPLAYS;
308492559Sdes	    display_number++) {
308557429Smarkm		port = 6000 + display_number;
308657429Smarkm		memset(&hints, 0, sizeof(hints));
308757429Smarkm		hints.ai_family = IPv4or6;
308892559Sdes		hints.ai_flags = x11_use_localhost ? 0: AI_PASSIVE;
308957429Smarkm		hints.ai_socktype = SOCK_STREAM;
309057429Smarkm		snprintf(strport, sizeof strport, "%d", port);
309157429Smarkm		if ((gaierr = getaddrinfo(NULL, strport, &hints, &aitop)) != 0) {
3092181111Sdes			error("getaddrinfo: %.100s", ssh_gai_strerror(gaierr));
309392559Sdes			return -1;
309457429Smarkm		}
309557429Smarkm		for (ai = aitop; ai; ai = ai->ai_next) {
309657429Smarkm			if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6)
309757429Smarkm				continue;
3098124207Sdes			sock = socket(ai->ai_family, ai->ai_socktype,
3099124207Sdes			    ai->ai_protocol);
310057429Smarkm			if (sock < 0) {
310198941Sdes				if ((errno != EINVAL) && (errno != EAFNOSUPPORT)) {
310298941Sdes					error("socket: %.100s", strerror(errno));
3103137019Sdes					freeaddrinfo(aitop);
310498941Sdes					return -1;
310598941Sdes				} else {
310698941Sdes					debug("x11_create_display_inet: Socket family %d not supported",
310798941Sdes						 ai->ai_family);
310898941Sdes					continue;
310998941Sdes				}
311057429Smarkm			}
311198941Sdes#ifdef IPV6_V6ONLY
311298941Sdes			if (ai->ai_family == AF_INET6) {
311398941Sdes				int on = 1;
311498941Sdes				if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) < 0)
311598941Sdes					error("setsockopt IPV6_V6ONLY: %.100s", strerror(errno));
311698941Sdes			}
311798941Sdes#endif
3118181111Sdes			if (x11_use_localhost)
3119181111Sdes				channel_set_reuseaddr(sock);
312057429Smarkm			if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) {
3121124207Sdes				debug2("bind port %d: %.100s", port, strerror(errno));
312257429Smarkm				close(sock);
312398941Sdes
312457429Smarkm				for (n = 0; n < num_socks; n++) {
312557429Smarkm					close(socks[n]);
312657429Smarkm				}
312757429Smarkm				num_socks = 0;
312857429Smarkm				break;
312957429Smarkm			}
313057429Smarkm			socks[num_socks++] = sock;
313157429Smarkm			if (num_socks == NUM_SOCKS)
313257429Smarkm				break;
313357429Smarkm		}
313476262Sgreen		freeaddrinfo(aitop);
313557429Smarkm		if (num_socks > 0)
313657429Smarkm			break;
313757429Smarkm	}
313857429Smarkm	if (display_number >= MAX_DISPLAYS) {
313957429Smarkm		error("Failed to allocate internet-domain X11 display socket.");
314092559Sdes		return -1;
314157429Smarkm	}
314257429Smarkm	/* Start listening for connections on the socket. */
314357429Smarkm	for (n = 0; n < num_socks; n++) {
314457429Smarkm		sock = socks[n];
3145126273Sdes		if (listen(sock, SSH_LISTEN_BACKLOG) < 0) {
314657429Smarkm			error("listen: %.100s", strerror(errno));
314757429Smarkm			close(sock);
314892559Sdes			return -1;
314957429Smarkm		}
315057429Smarkm	}
315157429Smarkm
315257429Smarkm	/* Allocate a channel for each socket. */
3153162856Sdes	*chanids = xcalloc(num_socks + 1, sizeof(**chanids));
315457429Smarkm	for (n = 0; n < num_socks; n++) {
315557429Smarkm		sock = socks[n];
315692559Sdes		nc = channel_new("x11 listener",
315760573Skris		    SSH_CHANNEL_X11_LISTENER, sock, sock, -1,
315860573Skris		    CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT,
3159124207Sdes		    0, "X11 inet listener", 1);
316092559Sdes		nc->single_connection = single_connection;
3161157019Sdes		(*chanids)[n] = nc->self;
316257429Smarkm	}
3163157019Sdes	(*chanids)[n] = -1;
316457429Smarkm
316592559Sdes	/* Return the display number for the DISPLAY environment variable. */
316699063Sdes	*display_numberp = display_number;
316799063Sdes	return (0);
316857429Smarkm}
316957429Smarkm
317092559Sdesstatic int
3171192595Sdesconnect_local_xsocket_path(const char *pathname)
317257429Smarkm{
317357429Smarkm	int sock;
317457429Smarkm	struct sockaddr_un addr;
317557429Smarkm
317692559Sdes	sock = socket(AF_UNIX, SOCK_STREAM, 0);
317792559Sdes	if (sock < 0)
317892559Sdes		error("socket: %.100s", strerror(errno));
317992559Sdes	memset(&addr, 0, sizeof(addr));
318092559Sdes	addr.sun_family = AF_UNIX;
3181192595Sdes	strlcpy(addr.sun_path, pathname, sizeof addr.sun_path);
3182162856Sdes	if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) == 0)
318392559Sdes		return sock;
318492559Sdes	close(sock);
318557429Smarkm	error("connect %.100s: %.100s", addr.sun_path, strerror(errno));
318657429Smarkm	return -1;
318757429Smarkm}
318857429Smarkm
3189192595Sdesstatic int
3190192595Sdesconnect_local_xsocket(u_int dnr)
3191192595Sdes{
3192192595Sdes	char buf[1024];
3193192595Sdes	snprintf(buf, sizeof buf, _PATH_UNIX_X, dnr);
3194192595Sdes	return connect_local_xsocket_path(buf);
3195192595Sdes}
3196192595Sdes
319760573Skrisint
319860573Skrisx11_connect_display(void)
319957429Smarkm{
3200162856Sdes	u_int display_number;
320157429Smarkm	const char *display;
320260573Skris	char buf[1024], *cp;
320357429Smarkm	struct addrinfo hints, *ai, *aitop;
320457429Smarkm	char strport[NI_MAXSERV];
3205162856Sdes	int gaierr, sock = 0;
320657429Smarkm
320757429Smarkm	/* Try to open a socket for the local X server. */
320857429Smarkm	display = getenv("DISPLAY");
320957429Smarkm	if (!display) {
321057429Smarkm		error("DISPLAY not set.");
321160573Skris		return -1;
321257429Smarkm	}
321357429Smarkm	/*
321457429Smarkm	 * Now we decode the value of the DISPLAY variable and make a
321557429Smarkm	 * connection to the real X server.
321657429Smarkm	 */
321757429Smarkm
3218192595Sdes	/* Check if the display is from launchd. */
3219192595Sdes#ifdef __APPLE__
3220192595Sdes	if (strncmp(display, "/tmp/launch", 11) == 0) {
3221192595Sdes		sock = connect_local_xsocket_path(display);
3222192595Sdes		if (sock < 0)
3223192595Sdes			return -1;
3224192595Sdes
3225192595Sdes		/* OK, we now have a connection to the display. */
3226192595Sdes		return sock;
3227192595Sdes	}
3228192595Sdes#endif
322957429Smarkm	/*
323057429Smarkm	 * Check if it is a unix domain socket.  Unix domain displays are in
323157429Smarkm	 * one of the following formats: unix:d[.s], :d[.s], ::d[.s]
323257429Smarkm	 */
323357429Smarkm	if (strncmp(display, "unix:", 5) == 0 ||
323457429Smarkm	    display[0] == ':') {
323557429Smarkm		/* Connect to the unix domain socket. */
3236162856Sdes		if (sscanf(strrchr(display, ':') + 1, "%u", &display_number) != 1) {
323757429Smarkm			error("Could not parse display number from DISPLAY: %.100s",
323892559Sdes			    display);
323960573Skris			return -1;
324057429Smarkm		}
324157429Smarkm		/* Create a socket. */
324257429Smarkm		sock = connect_local_xsocket(display_number);
324357429Smarkm		if (sock < 0)
324460573Skris			return -1;
324557429Smarkm
324657429Smarkm		/* OK, we now have a connection to the display. */
324760573Skris		return sock;
324857429Smarkm	}
324957429Smarkm	/*
325057429Smarkm	 * Connect to an inet socket.  The DISPLAY value is supposedly
325157429Smarkm	 * hostname:d[.s], where hostname may also be numeric IP address.
325257429Smarkm	 */
325392559Sdes	strlcpy(buf, display, sizeof(buf));
325457429Smarkm	cp = strchr(buf, ':');
325557429Smarkm	if (!cp) {
325657429Smarkm		error("Could not find ':' in DISPLAY: %.100s", display);
325760573Skris		return -1;
325857429Smarkm	}
325957429Smarkm	*cp = 0;
326057429Smarkm	/* buf now contains the host name.  But first we parse the display number. */
3261162856Sdes	if (sscanf(cp + 1, "%u", &display_number) != 1) {
326257429Smarkm		error("Could not parse display number from DISPLAY: %.100s",
326392559Sdes		    display);
326460573Skris		return -1;
326557429Smarkm	}
326657429Smarkm
326757429Smarkm	/* Look up the host address */
326857429Smarkm	memset(&hints, 0, sizeof(hints));
326957429Smarkm	hints.ai_family = IPv4or6;
327057429Smarkm	hints.ai_socktype = SOCK_STREAM;
3271162856Sdes	snprintf(strport, sizeof strport, "%u", 6000 + display_number);
327257429Smarkm	if ((gaierr = getaddrinfo(buf, strport, &hints, &aitop)) != 0) {
3273181111Sdes		error("%.100s: unknown host. (%s)", buf,
3274181111Sdes		ssh_gai_strerror(gaierr));
327560573Skris		return -1;
327657429Smarkm	}
327757429Smarkm	for (ai = aitop; ai; ai = ai->ai_next) {
327857429Smarkm		/* Create a socket. */
3279124207Sdes		sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
328057429Smarkm		if (sock < 0) {
3281124207Sdes			debug2("socket: %.100s", strerror(errno));
328260573Skris			continue;
328360573Skris		}
328460573Skris		/* Connect it to the display. */
328560573Skris		if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0) {
3286162856Sdes			debug2("connect %.100s port %u: %.100s", buf,
328760573Skris			    6000 + display_number, strerror(errno));
328860573Skris			close(sock);
328960573Skris			continue;
329060573Skris		}
329160573Skris		/* Success */
329260573Skris		break;
329357429Smarkm	}
329457429Smarkm	freeaddrinfo(aitop);
329557429Smarkm	if (!ai) {
3296162856Sdes		error("connect %.100s port %u: %.100s", buf, 6000 + display_number,
329757429Smarkm		    strerror(errno));
329860573Skris		return -1;
329957429Smarkm	}
330092559Sdes	set_nodelay(sock);
330160573Skris	return sock;
330260573Skris}
330357429Smarkm
330460573Skris/*
330560573Skris * This is called when SSH_SMSG_X11_OPEN is received.  The packet contains
330660573Skris * the remote channel number.  We should do whatever we want, and respond
330760573Skris * with either SSH_MSG_OPEN_CONFIRMATION or SSH_MSG_OPEN_FAILURE.
330860573Skris */
330957429Smarkm
3310162856Sdes/* ARGSUSED */
331160573Skrisvoid
331292559Sdesx11_input_open(int type, u_int32_t seq, void *ctxt)
331360573Skris{
331492559Sdes	Channel *c = NULL;
331592559Sdes	int remote_id, sock = 0;
331660573Skris	char *remote_host;
331757429Smarkm
331892559Sdes	debug("Received X11 open request.");
331957429Smarkm
332092559Sdes	remote_id = packet_get_int();
332192559Sdes
332292559Sdes	if (packet_get_protocol_flags() & SSH_PROTOFLAG_HOST_IN_FWD_OPEN) {
332392559Sdes		remote_host = packet_get_string(NULL);
332460573Skris	} else {
332560573Skris		remote_host = xstrdup("unknown (remote did not supply name)");
332660573Skris	}
332792559Sdes	packet_check_eom();
332860573Skris
332960573Skris	/* Obtain a connection to the real X display. */
333060573Skris	sock = x11_connect_display();
333192559Sdes	if (sock != -1) {
333292559Sdes		/* Allocate a channel for this connection. */
333392559Sdes		c = channel_new("connected x11 socket",
333492559Sdes		    SSH_CHANNEL_X11_OPEN, sock, sock, -1, 0, 0, 0,
333592559Sdes		    remote_host, 1);
333692559Sdes		c->remote_id = remote_id;
333792559Sdes		c->force_drain = 1;
333892559Sdes	}
3339124207Sdes	xfree(remote_host);
334092559Sdes	if (c == NULL) {
334160573Skris		/* Send refusal to the remote host. */
334260573Skris		packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE);
334392559Sdes		packet_put_int(remote_id);
334460573Skris	} else {
334560573Skris		/* Send a confirmation to the remote host. */
334660573Skris		packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION);
334792559Sdes		packet_put_int(remote_id);
334892559Sdes		packet_put_int(c->self);
334960573Skris	}
335092559Sdes	packet_send();
335157429Smarkm}
335257429Smarkm
335369587Sgreen/* dummy protocol handler that denies SSH-1 requests (agent/x11) */
3354162856Sdes/* ARGSUSED */
335569587Sgreenvoid
335692559Sdesdeny_input_open(int type, u_int32_t seq, void *ctxt)
335769587Sgreen{
335869587Sgreen	int rchan = packet_get_int();
3359106130Sdes
336092559Sdes	switch (type) {
336169587Sgreen	case SSH_SMSG_AGENT_OPEN:
336269587Sgreen		error("Warning: ssh server tried agent forwarding.");
336369587Sgreen		break;
336469587Sgreen	case SSH_SMSG_X11_OPEN:
336569587Sgreen		error("Warning: ssh server tried X11 forwarding.");
336669587Sgreen		break;
336769587Sgreen	default:
336892559Sdes		error("deny_input_open: type %d", type);
336969587Sgreen		break;
337069587Sgreen	}
3371157019Sdes	error("Warning: this is probably a break-in attempt by a malicious server.");
337269587Sgreen	packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE);
337369587Sgreen	packet_put_int(rchan);
337469587Sgreen	packet_send();
337569587Sgreen}
337669587Sgreen
337757429Smarkm/*
337857429Smarkm * Requests forwarding of X11 connections, generates fake authentication
337957429Smarkm * data, and enables authentication spoofing.
338092559Sdes * This should be called in the client only.
338157429Smarkm */
338260573Skrisvoid
3383149753Sdesx11_request_forwarding_with_spoofing(int client_session_id, const char *disp,
338460573Skris    const char *proto, const char *data)
338557429Smarkm{
338676262Sgreen	u_int data_len = (u_int) strlen(data) / 2;
3387149753Sdes	u_int i, value;
338857429Smarkm	char *new_data;
338957429Smarkm	int screen_number;
339057429Smarkm	const char *cp;
3391137019Sdes	u_int32_t rnd = 0;
339257429Smarkm
3393149753Sdes	if (x11_saved_display == NULL)
3394149753Sdes		x11_saved_display = xstrdup(disp);
3395149753Sdes	else if (strcmp(disp, x11_saved_display) != 0) {
3396149753Sdes		error("x11_request_forwarding_with_spoofing: different "
3397149753Sdes		    "$DISPLAY already forwarded");
3398149753Sdes		return;
3399149753Sdes	}
3400149753Sdes
3401162856Sdes	cp = strchr(disp, ':');
340257429Smarkm	if (cp)
340357429Smarkm		cp = strchr(cp, '.');
340457429Smarkm	if (cp)
3405162856Sdes		screen_number = (u_int)strtonum(cp + 1, 0, 400, NULL);
340657429Smarkm	else
340757429Smarkm		screen_number = 0;
340857429Smarkm
3409149753Sdes	if (x11_saved_proto == NULL) {
3410149753Sdes		/* Save protocol name. */
3411149753Sdes		x11_saved_proto = xstrdup(proto);
3412149753Sdes		/*
3413149753Sdes		 * Extract real authentication data and generate fake data
3414149753Sdes		 * of the same length.
3415149753Sdes		 */
3416149753Sdes		x11_saved_data = xmalloc(data_len);
3417149753Sdes		x11_fake_data = xmalloc(data_len);
3418149753Sdes		for (i = 0; i < data_len; i++) {
3419149753Sdes			if (sscanf(data + 2 * i, "%2x", &value) != 1)
3420149753Sdes				fatal("x11_request_forwarding: bad "
3421149753Sdes				    "authentication data: %.100s", data);
3422149753Sdes			if (i % 4 == 0)
3423149753Sdes				rnd = arc4random();
3424149753Sdes			x11_saved_data[i] = value;
3425149753Sdes			x11_fake_data[i] = rnd & 0xff;
3426149753Sdes			rnd >>= 8;
3427149753Sdes		}
3428149753Sdes		x11_saved_data_len = data_len;
3429149753Sdes		x11_fake_data_len = data_len;
343057429Smarkm	}
343157429Smarkm
343257429Smarkm	/* Convert the fake data into hex. */
3433149753Sdes	new_data = tohex(x11_fake_data, data_len);
343457429Smarkm
343557429Smarkm	/* Send the request packet. */
343660573Skris	if (compat20) {
343760573Skris		channel_request_start(client_session_id, "x11-req", 0);
343860573Skris		packet_put_char(0);	/* XXX bool single connection */
343960573Skris	} else {
344060573Skris		packet_start(SSH_CMSG_X11_REQUEST_FORWARDING);
344160573Skris	}
344260573Skris	packet_put_cstring(proto);
344360573Skris	packet_put_cstring(new_data);
344457429Smarkm	packet_put_int(screen_number);
344557429Smarkm	packet_send();
344657429Smarkm	packet_write_wait();
344757429Smarkm	xfree(new_data);
344857429Smarkm}
344957429Smarkm
345092559Sdes
345192559Sdes/* -- agent forwarding */
345292559Sdes
345357429Smarkm/* Sends a message to the server to request authentication fd forwarding. */
345457429Smarkm
345560573Skrisvoid
345692559Sdesauth_request_forwarding(void)
345757429Smarkm{
345857429Smarkm	packet_start(SSH_CMSG_AGENT_REQUEST_FORWARDING);
345957429Smarkm	packet_send();
346057429Smarkm	packet_write_wait();
346157429Smarkm}
3462