xref: /onnv-gate/usr/src/cmd/ssh/libssh/common/channels.c (revision 4907:a4c8fe4ba841)
10Sstevel@tonic-gate /*
24764Sjp161948  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
30Sstevel@tonic-gate  * Use is subject to license terms.
40Sstevel@tonic-gate  */
50Sstevel@tonic-gate /*
60Sstevel@tonic-gate  * Author: Tatu Ylonen <ylo@cs.hut.fi>
70Sstevel@tonic-gate  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
80Sstevel@tonic-gate  *                    All rights reserved
90Sstevel@tonic-gate  * This file contains functions for generic socket connection forwarding.
100Sstevel@tonic-gate  * There is also code for initiating connection forwarding for X11 connections,
110Sstevel@tonic-gate  * arbitrary tcp/ip connections, and the authentication agent connection.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * As far as I am concerned, the code I have written for this software
140Sstevel@tonic-gate  * can be used freely for any purpose.  Any derived versions of this
150Sstevel@tonic-gate  * software must be clearly marked as such, and if the derived work is
160Sstevel@tonic-gate  * incompatible with the protocol description in the RFC file, it must be
170Sstevel@tonic-gate  * called by a name other than "ssh" or "Secure Shell".
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * SSH2 support added by Markus Friedl.
200Sstevel@tonic-gate  * Copyright (c) 1999, 2000, 2001, 2002 Markus Friedl.  All rights reserved.
210Sstevel@tonic-gate  * Copyright (c) 1999 Dug Song.  All rights reserved.
220Sstevel@tonic-gate  * Copyright (c) 1999 Theo de Raadt.  All rights reserved.
230Sstevel@tonic-gate  *
240Sstevel@tonic-gate  * Redistribution and use in source and binary forms, with or without
250Sstevel@tonic-gate  * modification, are permitted provided that the following conditions
260Sstevel@tonic-gate  * are met:
270Sstevel@tonic-gate  * 1. Redistributions of source code must retain the above copyright
280Sstevel@tonic-gate  *    notice, this list of conditions and the following disclaimer.
290Sstevel@tonic-gate  * 2. Redistributions in binary form must reproduce the above copyright
300Sstevel@tonic-gate  *    notice, this list of conditions and the following disclaimer in the
310Sstevel@tonic-gate  *    documentation and/or other materials provided with the distribution.
320Sstevel@tonic-gate  *
330Sstevel@tonic-gate  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
340Sstevel@tonic-gate  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
350Sstevel@tonic-gate  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
360Sstevel@tonic-gate  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
370Sstevel@tonic-gate  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
380Sstevel@tonic-gate  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
390Sstevel@tonic-gate  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
400Sstevel@tonic-gate  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
410Sstevel@tonic-gate  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
420Sstevel@tonic-gate  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
430Sstevel@tonic-gate  */
440Sstevel@tonic-gate 
450Sstevel@tonic-gate #include "includes.h"
460Sstevel@tonic-gate RCSID("$OpenBSD: channels.c,v 1.183 2002/09/17 07:47:02 itojun Exp $");
470Sstevel@tonic-gate 
480Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
490Sstevel@tonic-gate 
500Sstevel@tonic-gate #include "ssh.h"
510Sstevel@tonic-gate #include "ssh1.h"
520Sstevel@tonic-gate #include "ssh2.h"
530Sstevel@tonic-gate #include "packet.h"
540Sstevel@tonic-gate #include "xmalloc.h"
550Sstevel@tonic-gate #include "log.h"
560Sstevel@tonic-gate #include "misc.h"
570Sstevel@tonic-gate #include "channels.h"
580Sstevel@tonic-gate #include "compat.h"
590Sstevel@tonic-gate #include "canohost.h"
600Sstevel@tonic-gate #include "key.h"
610Sstevel@tonic-gate #include "authfd.h"
620Sstevel@tonic-gate #include "pathnames.h"
634764Sjp161948 #include "bufaux.h"
640Sstevel@tonic-gate 
650Sstevel@tonic-gate 
660Sstevel@tonic-gate /* -- channel core */
670Sstevel@tonic-gate 
680Sstevel@tonic-gate /*
690Sstevel@tonic-gate  * Pointer to an array containing all allocated channels.  The array is
700Sstevel@tonic-gate  * dynamically extended as needed.
710Sstevel@tonic-gate  */
720Sstevel@tonic-gate static Channel **channels = NULL;
730Sstevel@tonic-gate 
740Sstevel@tonic-gate /*
750Sstevel@tonic-gate  * Size of the channel array.  All slots of the array must always be
760Sstevel@tonic-gate  * initialized (at least the type field); unused slots set to NULL
770Sstevel@tonic-gate  */
780Sstevel@tonic-gate static int channels_alloc = 0;
790Sstevel@tonic-gate 
800Sstevel@tonic-gate /*
810Sstevel@tonic-gate  * Maximum file descriptor value used in any of the channels.  This is
820Sstevel@tonic-gate  * updated in channel_new.
830Sstevel@tonic-gate  */
840Sstevel@tonic-gate static int channel_max_fd = 0;
850Sstevel@tonic-gate 
860Sstevel@tonic-gate 
870Sstevel@tonic-gate /* -- tcp forwarding */
880Sstevel@tonic-gate 
890Sstevel@tonic-gate /*
900Sstevel@tonic-gate  * Data structure for storing which hosts are permitted for forward requests.
910Sstevel@tonic-gate  * The local sides of any remote forwards are stored in this array to prevent
920Sstevel@tonic-gate  * a corrupt remote server from accessing arbitrary TCP/IP ports on our local
930Sstevel@tonic-gate  * network (which might be behind a firewall).
940Sstevel@tonic-gate  */
950Sstevel@tonic-gate typedef struct {
960Sstevel@tonic-gate 	char *host_to_connect;		/* Connect to 'host'. */
970Sstevel@tonic-gate 	u_short port_to_connect;	/* Connect to 'port'. */
980Sstevel@tonic-gate 	u_short listen_port;		/* Remote side should listen port number. */
990Sstevel@tonic-gate } ForwardPermission;
1000Sstevel@tonic-gate 
1010Sstevel@tonic-gate /* List of all permitted host/port pairs to connect. */
1020Sstevel@tonic-gate static ForwardPermission permitted_opens[SSH_MAX_FORWARDS_PER_DIRECTION];
1030Sstevel@tonic-gate 
1040Sstevel@tonic-gate /* Number of permitted host/port pairs in the array. */
1050Sstevel@tonic-gate static int num_permitted_opens = 0;
1060Sstevel@tonic-gate /*
1070Sstevel@tonic-gate  * If this is true, all opens are permitted.  This is the case on the server
1080Sstevel@tonic-gate  * on which we have to trust the client anyway, and the user could do
1090Sstevel@tonic-gate  * anything after logging in anyway.
1100Sstevel@tonic-gate  */
1110Sstevel@tonic-gate static int all_opens_permitted = 0;
1120Sstevel@tonic-gate 
1130Sstevel@tonic-gate 
1140Sstevel@tonic-gate /* -- X11 forwarding */
1150Sstevel@tonic-gate 
1160Sstevel@tonic-gate /* Maximum number of fake X11 displays to try. */
1170Sstevel@tonic-gate #define MAX_DISPLAYS  1000
1180Sstevel@tonic-gate 
1190Sstevel@tonic-gate /* Saved X11 authentication protocol name. */
1200Sstevel@tonic-gate static char *x11_saved_proto = NULL;
1210Sstevel@tonic-gate 
1220Sstevel@tonic-gate /* Saved X11 authentication data.  This is the real data. */
1230Sstevel@tonic-gate static char *x11_saved_data = NULL;
1240Sstevel@tonic-gate static u_int x11_saved_data_len = 0;
1250Sstevel@tonic-gate 
1260Sstevel@tonic-gate /*
1270Sstevel@tonic-gate  * Fake X11 authentication data.  This is what the server will be sending us;
1280Sstevel@tonic-gate  * we should replace any occurrences of this by the real data.
1290Sstevel@tonic-gate  */
130*4907Sjp161948 static u_char *x11_fake_data = NULL;
1310Sstevel@tonic-gate static u_int x11_fake_data_len;
1320Sstevel@tonic-gate 
1330Sstevel@tonic-gate 
1340Sstevel@tonic-gate /* -- agent forwarding */
1350Sstevel@tonic-gate 
1360Sstevel@tonic-gate #define	NUM_SOCKS	10
1370Sstevel@tonic-gate 
1380Sstevel@tonic-gate /* AF_UNSPEC or AF_INET or AF_INET6 */
1390Sstevel@tonic-gate static int IPv4or6 = AF_UNSPEC;
1400Sstevel@tonic-gate 
1410Sstevel@tonic-gate /* helper */
1420Sstevel@tonic-gate static void port_open_helper(Channel *c, char *rtype);
1430Sstevel@tonic-gate 
1440Sstevel@tonic-gate /* -- channel core */
1450Sstevel@tonic-gate 
1460Sstevel@tonic-gate Channel *
1470Sstevel@tonic-gate channel_lookup(int id)
1480Sstevel@tonic-gate {
1490Sstevel@tonic-gate 	Channel *c;
1500Sstevel@tonic-gate 
1510Sstevel@tonic-gate 	if (id < 0 || id >= channels_alloc) {
1520Sstevel@tonic-gate 		log("channel_lookup: %d: bad id", id);
1530Sstevel@tonic-gate 		return NULL;
1540Sstevel@tonic-gate 	}
1550Sstevel@tonic-gate 	c = channels[id];
1560Sstevel@tonic-gate 	if (c == NULL) {
1570Sstevel@tonic-gate 		log("channel_lookup: %d: bad id: channel free", id);
1580Sstevel@tonic-gate 		return NULL;
1590Sstevel@tonic-gate 	}
1600Sstevel@tonic-gate 	return c;
1610Sstevel@tonic-gate }
1620Sstevel@tonic-gate 
1630Sstevel@tonic-gate /*
1640Sstevel@tonic-gate  * Register filedescriptors for a channel, used when allocating a channel or
1650Sstevel@tonic-gate  * when the channel consumer/producer is ready, e.g. shell exec'd
1660Sstevel@tonic-gate  */
1670Sstevel@tonic-gate 
1680Sstevel@tonic-gate static void
1690Sstevel@tonic-gate channel_register_fds(Channel *c, int rfd, int wfd, int efd,
1700Sstevel@tonic-gate     int extusage, int nonblock)
1710Sstevel@tonic-gate {
1720Sstevel@tonic-gate 	/* Update the maximum file descriptor value. */
1730Sstevel@tonic-gate 	channel_max_fd = MAX(channel_max_fd, rfd);
1740Sstevel@tonic-gate 	channel_max_fd = MAX(channel_max_fd, wfd);
1750Sstevel@tonic-gate 	channel_max_fd = MAX(channel_max_fd, efd);
1760Sstevel@tonic-gate 
1770Sstevel@tonic-gate 	/* XXX set close-on-exec -markus */
1780Sstevel@tonic-gate 
1790Sstevel@tonic-gate 	c->rfd = rfd;
1800Sstevel@tonic-gate 	c->wfd = wfd;
1810Sstevel@tonic-gate 	c->sock = (rfd == wfd) ? rfd : -1;
1820Sstevel@tonic-gate 	c->efd = efd;
1830Sstevel@tonic-gate 	c->extended_usage = extusage;
1840Sstevel@tonic-gate 
1850Sstevel@tonic-gate 	/* XXX ugly hack: nonblock is only set by the server */
1860Sstevel@tonic-gate 	if (nonblock && isatty(c->rfd)) {
1870Sstevel@tonic-gate 		debug("channel %d: rfd %d isatty", c->self, c->rfd);
1880Sstevel@tonic-gate 		c->isatty = 1;
1890Sstevel@tonic-gate 		if (!isatty(c->wfd)) {
1900Sstevel@tonic-gate 			error("channel %d: wfd %d is not a tty?",
1910Sstevel@tonic-gate 			    c->self, c->wfd);
1920Sstevel@tonic-gate 		}
1930Sstevel@tonic-gate 	} else {
1940Sstevel@tonic-gate 		c->isatty = 0;
1950Sstevel@tonic-gate 	}
1960Sstevel@tonic-gate 	c->wfd_isatty = isatty(c->wfd);
1970Sstevel@tonic-gate 
1980Sstevel@tonic-gate 	/* enable nonblocking mode */
1990Sstevel@tonic-gate 	if (nonblock) {
2000Sstevel@tonic-gate 		if (rfd != -1)
2010Sstevel@tonic-gate 			set_nonblock(rfd);
2020Sstevel@tonic-gate 		if (wfd != -1)
2030Sstevel@tonic-gate 			set_nonblock(wfd);
2040Sstevel@tonic-gate 		if (efd != -1)
2050Sstevel@tonic-gate 			set_nonblock(efd);
2060Sstevel@tonic-gate 	}
2070Sstevel@tonic-gate }
2080Sstevel@tonic-gate 
2090Sstevel@tonic-gate /*
2100Sstevel@tonic-gate  * Allocate a new channel object and set its type and socket. This will cause
2110Sstevel@tonic-gate  * remote_name to be freed.
2120Sstevel@tonic-gate  */
2130Sstevel@tonic-gate 
2140Sstevel@tonic-gate Channel *
2150Sstevel@tonic-gate channel_new(char *ctype, int type, int rfd, int wfd, int efd,
2160Sstevel@tonic-gate     u_int window, u_int maxpack, int extusage, char *remote_name, int nonblock)
2170Sstevel@tonic-gate {
2180Sstevel@tonic-gate 	int i, found;
2190Sstevel@tonic-gate 	Channel *c;
2200Sstevel@tonic-gate 
2210Sstevel@tonic-gate 	/* Do initial allocation if this is the first call. */
2220Sstevel@tonic-gate 	if (channels_alloc == 0) {
2230Sstevel@tonic-gate 		channels_alloc = 10;
2240Sstevel@tonic-gate 		channels = xmalloc(channels_alloc * sizeof(Channel *));
2250Sstevel@tonic-gate 		for (i = 0; i < channels_alloc; i++)
2260Sstevel@tonic-gate 			channels[i] = NULL;
2270Sstevel@tonic-gate 		fatal_add_cleanup((void (*) (void *)) channel_free_all, NULL);
2280Sstevel@tonic-gate 	}
2290Sstevel@tonic-gate 	/* Try to find a free slot where to put the new channel. */
2300Sstevel@tonic-gate 	for (found = -1, i = 0; i < channels_alloc; i++)
2310Sstevel@tonic-gate 		if (channels[i] == NULL) {
2320Sstevel@tonic-gate 			/* Found a free slot. */
2330Sstevel@tonic-gate 			found = i;
2340Sstevel@tonic-gate 			break;
2350Sstevel@tonic-gate 		}
2360Sstevel@tonic-gate 	if (found == -1) {
2370Sstevel@tonic-gate 		/* There are no free slots.  Take last+1 slot and expand the array.  */
2380Sstevel@tonic-gate 		found = channels_alloc;
2390Sstevel@tonic-gate 		if (channels_alloc > 10000)
2400Sstevel@tonic-gate 			fatal("channel_new: internal error: channels_alloc %d "
2410Sstevel@tonic-gate 			    "too big.", channels_alloc);
2420Sstevel@tonic-gate 		channels = xrealloc(channels,
2430Sstevel@tonic-gate 		    (channels_alloc + 10) * sizeof(Channel *));
2440Sstevel@tonic-gate 		channels_alloc += 10;
2450Sstevel@tonic-gate 		debug2("channel: expanding %d", channels_alloc);
2460Sstevel@tonic-gate 		for (i = found; i < channels_alloc; i++)
2470Sstevel@tonic-gate 			channels[i] = NULL;
2480Sstevel@tonic-gate 	}
2490Sstevel@tonic-gate 	/* Initialize and return new channel. */
2500Sstevel@tonic-gate 	c = channels[found] = xmalloc(sizeof(Channel));
2510Sstevel@tonic-gate 	memset(c, 0, sizeof(Channel));
2520Sstevel@tonic-gate 	buffer_init(&c->input);
2530Sstevel@tonic-gate 	buffer_init(&c->output);
2540Sstevel@tonic-gate 	buffer_init(&c->extended);
2550Sstevel@tonic-gate 	c->ostate = CHAN_OUTPUT_OPEN;
2560Sstevel@tonic-gate 	c->istate = CHAN_INPUT_OPEN;
2570Sstevel@tonic-gate 	c->flags = 0;
2580Sstevel@tonic-gate 	channel_register_fds(c, rfd, wfd, efd, extusage, nonblock);
2590Sstevel@tonic-gate 	c->self = found;
2600Sstevel@tonic-gate 	c->type = type;
2610Sstevel@tonic-gate 	c->ctype = ctype;
2620Sstevel@tonic-gate 	c->local_window = window;
2630Sstevel@tonic-gate 	c->local_window_max = window;
2640Sstevel@tonic-gate 	c->local_consumed = 0;
2650Sstevel@tonic-gate 	c->local_maxpacket = maxpack;
2660Sstevel@tonic-gate 	c->remote_id = -1;
2670Sstevel@tonic-gate 	c->remote_name = remote_name;
2680Sstevel@tonic-gate 	c->remote_window = 0;
2690Sstevel@tonic-gate 	c->remote_maxpacket = 0;
2700Sstevel@tonic-gate 	c->force_drain = 0;
2710Sstevel@tonic-gate 	c->single_connection = 0;
2720Sstevel@tonic-gate 	c->detach_user = NULL;
2730Sstevel@tonic-gate 	c->confirm = NULL;
2740Sstevel@tonic-gate 	c->input_filter = NULL;
2750Sstevel@tonic-gate 	debug("channel %d: new [%s]", found, remote_name);
2760Sstevel@tonic-gate 	return c;
2770Sstevel@tonic-gate }
2780Sstevel@tonic-gate 
2790Sstevel@tonic-gate static int
2800Sstevel@tonic-gate channel_find_maxfd(void)
2810Sstevel@tonic-gate {
2820Sstevel@tonic-gate 	int i, max = 0;
2830Sstevel@tonic-gate 	Channel *c;
2840Sstevel@tonic-gate 
2850Sstevel@tonic-gate 	for (i = 0; i < channels_alloc; i++) {
2860Sstevel@tonic-gate 		c = channels[i];
2870Sstevel@tonic-gate 		if (c != NULL) {
2880Sstevel@tonic-gate 			max = MAX(max, c->rfd);
2890Sstevel@tonic-gate 			max = MAX(max, c->wfd);
2900Sstevel@tonic-gate 			max = MAX(max, c->efd);
2910Sstevel@tonic-gate 		}
2920Sstevel@tonic-gate 	}
2930Sstevel@tonic-gate 	return max;
2940Sstevel@tonic-gate }
2950Sstevel@tonic-gate 
2960Sstevel@tonic-gate int
2970Sstevel@tonic-gate channel_close_fd(int *fdp)
2980Sstevel@tonic-gate {
2990Sstevel@tonic-gate 	int ret = 0, fd = *fdp;
3000Sstevel@tonic-gate 
3010Sstevel@tonic-gate 	if (fd != -1) {
3020Sstevel@tonic-gate 		ret = close(fd);
3030Sstevel@tonic-gate 		*fdp = -1;
3040Sstevel@tonic-gate 		if (fd == channel_max_fd)
3050Sstevel@tonic-gate 			channel_max_fd = channel_find_maxfd();
3060Sstevel@tonic-gate 	}
3070Sstevel@tonic-gate 	return ret;
3080Sstevel@tonic-gate }
3090Sstevel@tonic-gate 
3100Sstevel@tonic-gate /* Close all channel fd/socket. */
3110Sstevel@tonic-gate 
3120Sstevel@tonic-gate static void
3130Sstevel@tonic-gate channel_close_fds(Channel *c)
3140Sstevel@tonic-gate {
3150Sstevel@tonic-gate 	debug3("channel_close_fds: channel %d: r %d w %d e %d",
3160Sstevel@tonic-gate 	    c->self, c->rfd, c->wfd, c->efd);
3170Sstevel@tonic-gate 
3180Sstevel@tonic-gate 	channel_close_fd(&c->sock);
3190Sstevel@tonic-gate 	channel_close_fd(&c->rfd);
3200Sstevel@tonic-gate 	channel_close_fd(&c->wfd);
3210Sstevel@tonic-gate 	channel_close_fd(&c->efd);
3220Sstevel@tonic-gate }
3230Sstevel@tonic-gate 
3240Sstevel@tonic-gate /* Free the channel and close its fd/socket. */
3250Sstevel@tonic-gate 
3260Sstevel@tonic-gate void
3270Sstevel@tonic-gate channel_free(Channel *c)
3280Sstevel@tonic-gate {
3290Sstevel@tonic-gate 	char *s;
3300Sstevel@tonic-gate 	int i, n;
3310Sstevel@tonic-gate 
3320Sstevel@tonic-gate 	for (n = 0, i = 0; i < channels_alloc; i++)
3330Sstevel@tonic-gate 		if (channels[i])
3340Sstevel@tonic-gate 			n++;
3350Sstevel@tonic-gate 	debug("channel_free: channel %d: %s, nchannels %d", c->self,
3360Sstevel@tonic-gate 	    c->remote_name ? c->remote_name : "???", n);
3370Sstevel@tonic-gate 
3380Sstevel@tonic-gate 	s = channel_open_message();
3390Sstevel@tonic-gate 	debug3("channel_free: status: %s", s);
3400Sstevel@tonic-gate 	xfree(s);
3410Sstevel@tonic-gate 
3420Sstevel@tonic-gate 	if (c->sock != -1)
3430Sstevel@tonic-gate 		shutdown(c->sock, SHUT_RDWR);
3440Sstevel@tonic-gate 	channel_close_fds(c);
3450Sstevel@tonic-gate 	buffer_free(&c->input);
3460Sstevel@tonic-gate 	buffer_free(&c->output);
3470Sstevel@tonic-gate 	buffer_free(&c->extended);
3480Sstevel@tonic-gate 	if (c->remote_name) {
3490Sstevel@tonic-gate 		xfree(c->remote_name);
3500Sstevel@tonic-gate 		c->remote_name = NULL;
3510Sstevel@tonic-gate 	}
3520Sstevel@tonic-gate 	channels[c->self] = NULL;
3530Sstevel@tonic-gate 	xfree(c);
3540Sstevel@tonic-gate }
3550Sstevel@tonic-gate 
3560Sstevel@tonic-gate void
3570Sstevel@tonic-gate channel_free_all(void)
3580Sstevel@tonic-gate {
3590Sstevel@tonic-gate 	int i;
3600Sstevel@tonic-gate 
3610Sstevel@tonic-gate 	for (i = 0; i < channels_alloc; i++)
3620Sstevel@tonic-gate 		if (channels[i] != NULL)
3630Sstevel@tonic-gate 			channel_free(channels[i]);
3640Sstevel@tonic-gate }
3650Sstevel@tonic-gate 
3660Sstevel@tonic-gate /*
3670Sstevel@tonic-gate  * Closes the sockets/fds of all channels.  This is used to close extra file
3680Sstevel@tonic-gate  * descriptors after a fork.
3690Sstevel@tonic-gate  */
3700Sstevel@tonic-gate 
3710Sstevel@tonic-gate void
3720Sstevel@tonic-gate channel_close_all(void)
3730Sstevel@tonic-gate {
3740Sstevel@tonic-gate 	int i;
3750Sstevel@tonic-gate 
3760Sstevel@tonic-gate 	for (i = 0; i < channels_alloc; i++)
3770Sstevel@tonic-gate 		if (channels[i] != NULL)
3780Sstevel@tonic-gate 			channel_close_fds(channels[i]);
3790Sstevel@tonic-gate }
3800Sstevel@tonic-gate 
3810Sstevel@tonic-gate /*
3820Sstevel@tonic-gate  * Stop listening to channels.
3830Sstevel@tonic-gate  */
3840Sstevel@tonic-gate 
3850Sstevel@tonic-gate void
3860Sstevel@tonic-gate channel_stop_listening(void)
3870Sstevel@tonic-gate {
3880Sstevel@tonic-gate 	int i;
3890Sstevel@tonic-gate 	Channel *c;
3900Sstevel@tonic-gate 
3910Sstevel@tonic-gate 	for (i = 0; i < channels_alloc; i++) {
3920Sstevel@tonic-gate 		c = channels[i];
3930Sstevel@tonic-gate 		if (c != NULL) {
3940Sstevel@tonic-gate 			switch (c->type) {
3950Sstevel@tonic-gate 			case SSH_CHANNEL_AUTH_SOCKET:
3960Sstevel@tonic-gate 			case SSH_CHANNEL_PORT_LISTENER:
3970Sstevel@tonic-gate 			case SSH_CHANNEL_RPORT_LISTENER:
3980Sstevel@tonic-gate 			case SSH_CHANNEL_X11_LISTENER:
3990Sstevel@tonic-gate 				channel_close_fd(&c->sock);
4000Sstevel@tonic-gate 				channel_free(c);
4010Sstevel@tonic-gate 				break;
4020Sstevel@tonic-gate 			}
4030Sstevel@tonic-gate 		}
4040Sstevel@tonic-gate 	}
4050Sstevel@tonic-gate }
4060Sstevel@tonic-gate 
4070Sstevel@tonic-gate /*
4080Sstevel@tonic-gate  * Returns true if no channel has too much buffered data, and false if one or
4090Sstevel@tonic-gate  * more channel is overfull.
4100Sstevel@tonic-gate  */
4110Sstevel@tonic-gate 
4120Sstevel@tonic-gate int
4130Sstevel@tonic-gate channel_not_very_much_buffered_data(void)
4140Sstevel@tonic-gate {
4150Sstevel@tonic-gate 	u_int i;
4160Sstevel@tonic-gate 	Channel *c;
4170Sstevel@tonic-gate 
4180Sstevel@tonic-gate 	for (i = 0; i < channels_alloc; i++) {
4190Sstevel@tonic-gate 		c = channels[i];
4200Sstevel@tonic-gate 		if (c != NULL && c->type == SSH_CHANNEL_OPEN) {
4210Sstevel@tonic-gate #if 0
4220Sstevel@tonic-gate 			if (!compat20 &&
4230Sstevel@tonic-gate 			    buffer_len(&c->input) > packet_get_maxsize()) {
4240Sstevel@tonic-gate 				debug("channel %d: big input buffer %d",
4250Sstevel@tonic-gate 				    c->self, buffer_len(&c->input));
4260Sstevel@tonic-gate 				return 0;
4270Sstevel@tonic-gate 			}
4280Sstevel@tonic-gate #endif
4290Sstevel@tonic-gate 			if (buffer_len(&c->output) > packet_get_maxsize()) {
4300Sstevel@tonic-gate 				debug("channel %d: big output buffer %d > %d",
4310Sstevel@tonic-gate 				    c->self, buffer_len(&c->output),
4320Sstevel@tonic-gate 				    packet_get_maxsize());
4330Sstevel@tonic-gate 				return 0;
4340Sstevel@tonic-gate 			}
4350Sstevel@tonic-gate 		}
4360Sstevel@tonic-gate 	}
4370Sstevel@tonic-gate 	return 1;
4380Sstevel@tonic-gate }
4390Sstevel@tonic-gate 
4400Sstevel@tonic-gate /* Returns true if any channel is still open. */
4410Sstevel@tonic-gate 
4420Sstevel@tonic-gate int
4430Sstevel@tonic-gate channel_still_open(void)
4440Sstevel@tonic-gate {
4450Sstevel@tonic-gate 	int i;
4460Sstevel@tonic-gate 	Channel *c;
4470Sstevel@tonic-gate 
4480Sstevel@tonic-gate 	for (i = 0; i < channels_alloc; i++) {
4490Sstevel@tonic-gate 		c = channels[i];
4500Sstevel@tonic-gate 		if (c == NULL)
4510Sstevel@tonic-gate 			continue;
4520Sstevel@tonic-gate 		switch (c->type) {
4530Sstevel@tonic-gate 		case SSH_CHANNEL_X11_LISTENER:
4540Sstevel@tonic-gate 		case SSH_CHANNEL_PORT_LISTENER:
4550Sstevel@tonic-gate 		case SSH_CHANNEL_RPORT_LISTENER:
4560Sstevel@tonic-gate 		case SSH_CHANNEL_CLOSED:
4570Sstevel@tonic-gate 		case SSH_CHANNEL_AUTH_SOCKET:
4580Sstevel@tonic-gate 		case SSH_CHANNEL_DYNAMIC:
4590Sstevel@tonic-gate 		case SSH_CHANNEL_CONNECTING:
4600Sstevel@tonic-gate 		case SSH_CHANNEL_ZOMBIE:
4610Sstevel@tonic-gate 			continue;
4620Sstevel@tonic-gate 		case SSH_CHANNEL_LARVAL:
4630Sstevel@tonic-gate 			if (!compat20)
4640Sstevel@tonic-gate 				fatal("cannot happen: SSH_CHANNEL_LARVAL");
4650Sstevel@tonic-gate 			continue;
4660Sstevel@tonic-gate 		case SSH_CHANNEL_OPENING:
4670Sstevel@tonic-gate 		case SSH_CHANNEL_OPEN:
4680Sstevel@tonic-gate 		case SSH_CHANNEL_X11_OPEN:
4690Sstevel@tonic-gate 			return 1;
4700Sstevel@tonic-gate 		case SSH_CHANNEL_INPUT_DRAINING:
4710Sstevel@tonic-gate 		case SSH_CHANNEL_OUTPUT_DRAINING:
4720Sstevel@tonic-gate 			if (!compat13)
4730Sstevel@tonic-gate 				fatal("cannot happen: OUT_DRAIN");
4740Sstevel@tonic-gate 			return 1;
4750Sstevel@tonic-gate 		default:
4760Sstevel@tonic-gate 			fatal("channel_still_open: bad channel type %d", c->type);
4770Sstevel@tonic-gate 			/* NOTREACHED */
4780Sstevel@tonic-gate 		}
4790Sstevel@tonic-gate 	}
4800Sstevel@tonic-gate 	return 0;
4810Sstevel@tonic-gate }
4820Sstevel@tonic-gate 
4830Sstevel@tonic-gate /* Returns the id of an open channel suitable for keepaliving */
4840Sstevel@tonic-gate 
4850Sstevel@tonic-gate int
4860Sstevel@tonic-gate channel_find_open(void)
4870Sstevel@tonic-gate {
4880Sstevel@tonic-gate 	int i;
4890Sstevel@tonic-gate 	Channel *c;
4900Sstevel@tonic-gate 
4910Sstevel@tonic-gate 	for (i = 0; i < channels_alloc; i++) {
4920Sstevel@tonic-gate 		c = channels[i];
4930Sstevel@tonic-gate 		if (c == NULL)
4940Sstevel@tonic-gate 			continue;
4950Sstevel@tonic-gate 		switch (c->type) {
4960Sstevel@tonic-gate 		case SSH_CHANNEL_CLOSED:
4970Sstevel@tonic-gate 		case SSH_CHANNEL_DYNAMIC:
4980Sstevel@tonic-gate 		case SSH_CHANNEL_X11_LISTENER:
4990Sstevel@tonic-gate 		case SSH_CHANNEL_PORT_LISTENER:
5000Sstevel@tonic-gate 		case SSH_CHANNEL_RPORT_LISTENER:
5010Sstevel@tonic-gate 		case SSH_CHANNEL_OPENING:
5020Sstevel@tonic-gate 		case SSH_CHANNEL_CONNECTING:
5030Sstevel@tonic-gate 		case SSH_CHANNEL_ZOMBIE:
5040Sstevel@tonic-gate 			continue;
5050Sstevel@tonic-gate 		case SSH_CHANNEL_LARVAL:
5060Sstevel@tonic-gate 		case SSH_CHANNEL_AUTH_SOCKET:
5070Sstevel@tonic-gate 		case SSH_CHANNEL_OPEN:
5080Sstevel@tonic-gate 		case SSH_CHANNEL_X11_OPEN:
5090Sstevel@tonic-gate 			return i;
5100Sstevel@tonic-gate 		case SSH_CHANNEL_INPUT_DRAINING:
5110Sstevel@tonic-gate 		case SSH_CHANNEL_OUTPUT_DRAINING:
5120Sstevel@tonic-gate 			if (!compat13)
5130Sstevel@tonic-gate 				fatal("cannot happen: OUT_DRAIN");
5140Sstevel@tonic-gate 			return i;
5150Sstevel@tonic-gate 		default:
5160Sstevel@tonic-gate 			fatal("channel_find_open: bad channel type %d", c->type);
5170Sstevel@tonic-gate 			/* NOTREACHED */
5180Sstevel@tonic-gate 		}
5190Sstevel@tonic-gate 	}
5200Sstevel@tonic-gate 	return -1;
5210Sstevel@tonic-gate }
5220Sstevel@tonic-gate 
5230Sstevel@tonic-gate 
5240Sstevel@tonic-gate /*
5250Sstevel@tonic-gate  * Returns a message describing the currently open forwarded connections,
5260Sstevel@tonic-gate  * suitable for sending to the client.  The message contains crlf pairs for
5270Sstevel@tonic-gate  * newlines.
5280Sstevel@tonic-gate  */
5290Sstevel@tonic-gate 
5300Sstevel@tonic-gate char *
5310Sstevel@tonic-gate channel_open_message(void)
5320Sstevel@tonic-gate {
5330Sstevel@tonic-gate 	Buffer buffer;
5340Sstevel@tonic-gate 	Channel *c;
5350Sstevel@tonic-gate 	char buf[1024], *cp;
5360Sstevel@tonic-gate 	int i;
5370Sstevel@tonic-gate 
5380Sstevel@tonic-gate 	buffer_init(&buffer);
5390Sstevel@tonic-gate 	snprintf(buf, sizeof buf, "The following connections are open:\r\n");
5400Sstevel@tonic-gate 	buffer_append(&buffer, buf, strlen(buf));
5410Sstevel@tonic-gate 	for (i = 0; i < channels_alloc; i++) {
5420Sstevel@tonic-gate 		c = channels[i];
5430Sstevel@tonic-gate 		if (c == NULL)
5440Sstevel@tonic-gate 			continue;
5450Sstevel@tonic-gate 		switch (c->type) {
5460Sstevel@tonic-gate 		case SSH_CHANNEL_X11_LISTENER:
5470Sstevel@tonic-gate 		case SSH_CHANNEL_PORT_LISTENER:
5480Sstevel@tonic-gate 		case SSH_CHANNEL_RPORT_LISTENER:
5490Sstevel@tonic-gate 		case SSH_CHANNEL_CLOSED:
5500Sstevel@tonic-gate 		case SSH_CHANNEL_AUTH_SOCKET:
5510Sstevel@tonic-gate 		case SSH_CHANNEL_ZOMBIE:
5520Sstevel@tonic-gate 			continue;
5530Sstevel@tonic-gate 		case SSH_CHANNEL_LARVAL:
5540Sstevel@tonic-gate 		case SSH_CHANNEL_OPENING:
5550Sstevel@tonic-gate 		case SSH_CHANNEL_CONNECTING:
5560Sstevel@tonic-gate 		case SSH_CHANNEL_DYNAMIC:
5570Sstevel@tonic-gate 		case SSH_CHANNEL_OPEN:
5580Sstevel@tonic-gate 		case SSH_CHANNEL_X11_OPEN:
5590Sstevel@tonic-gate 		case SSH_CHANNEL_INPUT_DRAINING:
5600Sstevel@tonic-gate 		case SSH_CHANNEL_OUTPUT_DRAINING:
5610Sstevel@tonic-gate 			snprintf(buf, sizeof buf, "  #%d %.300s (t%d r%d i%d/%d o%d/%d fd %d/%d)\r\n",
5620Sstevel@tonic-gate 			    c->self, c->remote_name,
5630Sstevel@tonic-gate 			    c->type, c->remote_id,
5640Sstevel@tonic-gate 			    c->istate, buffer_len(&c->input),
5650Sstevel@tonic-gate 			    c->ostate, buffer_len(&c->output),
5660Sstevel@tonic-gate 			    c->rfd, c->wfd);
5670Sstevel@tonic-gate 			buffer_append(&buffer, buf, strlen(buf));
5680Sstevel@tonic-gate 			continue;
5690Sstevel@tonic-gate 		default:
5700Sstevel@tonic-gate 			fatal("channel_open_message: bad channel type %d", c->type);
5710Sstevel@tonic-gate 			/* NOTREACHED */
5720Sstevel@tonic-gate 		}
5730Sstevel@tonic-gate 	}
5740Sstevel@tonic-gate 	buffer_append(&buffer, "\0", 1);
5750Sstevel@tonic-gate 	cp = xstrdup(buffer_ptr(&buffer));
5760Sstevel@tonic-gate 	buffer_free(&buffer);
5770Sstevel@tonic-gate 	return cp;
5780Sstevel@tonic-gate }
5790Sstevel@tonic-gate 
5800Sstevel@tonic-gate void
5810Sstevel@tonic-gate channel_send_open(int id)
5820Sstevel@tonic-gate {
5830Sstevel@tonic-gate 	Channel *c = channel_lookup(id);
5840Sstevel@tonic-gate 
5850Sstevel@tonic-gate 	if (c == NULL) {
5860Sstevel@tonic-gate 		log("channel_send_open: %d: bad id", id);
5870Sstevel@tonic-gate 		return;
5880Sstevel@tonic-gate 	}
5890Sstevel@tonic-gate 	debug("send channel open %d", id);
5900Sstevel@tonic-gate 	packet_start(SSH2_MSG_CHANNEL_OPEN);
5910Sstevel@tonic-gate 	packet_put_cstring(c->ctype);
5920Sstevel@tonic-gate 	packet_put_int(c->self);
5930Sstevel@tonic-gate 	packet_put_int(c->local_window);
5940Sstevel@tonic-gate 	packet_put_int(c->local_maxpacket);
5950Sstevel@tonic-gate 	packet_send();
5960Sstevel@tonic-gate }
5970Sstevel@tonic-gate 
5980Sstevel@tonic-gate void
5990Sstevel@tonic-gate channel_request_start(int local_id, char *service, int wantconfirm)
6000Sstevel@tonic-gate {
6010Sstevel@tonic-gate 	Channel *c = channel_lookup(local_id);
6020Sstevel@tonic-gate 
6030Sstevel@tonic-gate 	if (c == NULL) {
6040Sstevel@tonic-gate 		log("channel_request_start: %d: unknown channel id", local_id);
6050Sstevel@tonic-gate 		return;
6060Sstevel@tonic-gate 	}
6070Sstevel@tonic-gate 	debug("channel request %d: %s", local_id, service) ;
6080Sstevel@tonic-gate 	packet_start(SSH2_MSG_CHANNEL_REQUEST);
6090Sstevel@tonic-gate 	packet_put_int(c->remote_id);
6100Sstevel@tonic-gate 	packet_put_cstring(service);
6110Sstevel@tonic-gate 	packet_put_char(wantconfirm);
6120Sstevel@tonic-gate }
6130Sstevel@tonic-gate void
6140Sstevel@tonic-gate channel_register_confirm(int id, channel_callback_fn *fn)
6150Sstevel@tonic-gate {
6160Sstevel@tonic-gate 	Channel *c = channel_lookup(id);
6170Sstevel@tonic-gate 
6180Sstevel@tonic-gate 	if (c == NULL) {
6190Sstevel@tonic-gate 		log("channel_register_comfirm: %d: bad id", id);
6200Sstevel@tonic-gate 		return;
6210Sstevel@tonic-gate 	}
6220Sstevel@tonic-gate 	c->confirm = fn;
6230Sstevel@tonic-gate }
6240Sstevel@tonic-gate void
6250Sstevel@tonic-gate channel_register_cleanup(int id, channel_callback_fn *fn)
6260Sstevel@tonic-gate {
6270Sstevel@tonic-gate 	Channel *c = channel_lookup(id);
6280Sstevel@tonic-gate 
6290Sstevel@tonic-gate 	if (c == NULL) {
6300Sstevel@tonic-gate 		log("channel_register_cleanup: %d: bad id", id);
6310Sstevel@tonic-gate 		return;
6320Sstevel@tonic-gate 	}
6330Sstevel@tonic-gate 	c->detach_user = fn;
6340Sstevel@tonic-gate }
6350Sstevel@tonic-gate void
6360Sstevel@tonic-gate channel_cancel_cleanup(int id)
6370Sstevel@tonic-gate {
6380Sstevel@tonic-gate 	Channel *c = channel_lookup(id);
6390Sstevel@tonic-gate 
6400Sstevel@tonic-gate 	if (c == NULL) {
6410Sstevel@tonic-gate 		log("channel_cancel_cleanup: %d: bad id", id);
6420Sstevel@tonic-gate 		return;
6430Sstevel@tonic-gate 	}
6440Sstevel@tonic-gate 	c->detach_user = NULL;
6450Sstevel@tonic-gate }
6460Sstevel@tonic-gate void
6470Sstevel@tonic-gate channel_register_filter(int id, channel_filter_fn *fn)
6480Sstevel@tonic-gate {
6490Sstevel@tonic-gate 	Channel *c = channel_lookup(id);
6500Sstevel@tonic-gate 
6510Sstevel@tonic-gate 	if (c == NULL) {
6520Sstevel@tonic-gate 		log("channel_register_filter: %d: bad id", id);
6530Sstevel@tonic-gate 		return;
6540Sstevel@tonic-gate 	}
6550Sstevel@tonic-gate 	c->input_filter = fn;
6560Sstevel@tonic-gate }
6570Sstevel@tonic-gate 
6580Sstevel@tonic-gate void
6590Sstevel@tonic-gate channel_set_fds(int id, int rfd, int wfd, int efd,
6600Sstevel@tonic-gate     int extusage, int nonblock, u_int window_max)
6610Sstevel@tonic-gate {
6620Sstevel@tonic-gate 	Channel *c = channel_lookup(id);
6630Sstevel@tonic-gate 
6640Sstevel@tonic-gate 	if (c == NULL || c->type != SSH_CHANNEL_LARVAL)
6650Sstevel@tonic-gate 		fatal("channel_activate for non-larval channel %d.", id);
6660Sstevel@tonic-gate 	channel_register_fds(c, rfd, wfd, efd, extusage, nonblock);
6670Sstevel@tonic-gate 	c->type = SSH_CHANNEL_OPEN;
6680Sstevel@tonic-gate 	c->local_window = c->local_window_max = window_max;
6690Sstevel@tonic-gate 	packet_start(SSH2_MSG_CHANNEL_WINDOW_ADJUST);
6700Sstevel@tonic-gate 	packet_put_int(c->remote_id);
6710Sstevel@tonic-gate 	packet_put_int(c->local_window);
6720Sstevel@tonic-gate 	packet_send();
6730Sstevel@tonic-gate }
6740Sstevel@tonic-gate 
6750Sstevel@tonic-gate void
6760Sstevel@tonic-gate channel_set_wait_for_exit(int id, int wait_for_exit)
6770Sstevel@tonic-gate {
6780Sstevel@tonic-gate 	Channel *c = channel_lookup(id);
6790Sstevel@tonic-gate 
6800Sstevel@tonic-gate 	if (c == NULL || c->type != SSH_CHANNEL_OPEN)
6810Sstevel@tonic-gate 		fatal("channel_set_wait_for_exit for non-open channel %d.", id);
6820Sstevel@tonic-gate 
6830Sstevel@tonic-gate 	debug3("channel_set_wait_for_exit %d, %d (type: %d)", id, wait_for_exit, c->type);
6840Sstevel@tonic-gate 	c->wait_for_exit = wait_for_exit;
6850Sstevel@tonic-gate }
6860Sstevel@tonic-gate 
6870Sstevel@tonic-gate /*
6880Sstevel@tonic-gate  * 'channel_pre*' are called just before select() to add any bits relevant to
6890Sstevel@tonic-gate  * channels in the select bitmasks.
6900Sstevel@tonic-gate  */
6910Sstevel@tonic-gate /*
6920Sstevel@tonic-gate  * 'channel_post*': perform any appropriate operations for channels which
6930Sstevel@tonic-gate  * have events pending.
6940Sstevel@tonic-gate  */
6950Sstevel@tonic-gate typedef void chan_fn(Channel *c, fd_set * readset, fd_set * writeset);
6960Sstevel@tonic-gate chan_fn *channel_pre[SSH_CHANNEL_MAX_TYPE];
6970Sstevel@tonic-gate chan_fn *channel_post[SSH_CHANNEL_MAX_TYPE];
6980Sstevel@tonic-gate 
6990Sstevel@tonic-gate static void
7000Sstevel@tonic-gate channel_pre_listener(Channel *c, fd_set * readset, fd_set * writeset)
7010Sstevel@tonic-gate {
7020Sstevel@tonic-gate 	FD_SET(c->sock, readset);
7030Sstevel@tonic-gate }
7040Sstevel@tonic-gate 
7050Sstevel@tonic-gate static void
7060Sstevel@tonic-gate channel_pre_connecting(Channel *c, fd_set * readset, fd_set * writeset)
7070Sstevel@tonic-gate {
7080Sstevel@tonic-gate 	debug3("channel %d: waiting for connection", c->self);
7090Sstevel@tonic-gate 	FD_SET(c->sock, writeset);
7100Sstevel@tonic-gate }
7110Sstevel@tonic-gate 
7120Sstevel@tonic-gate static void
7130Sstevel@tonic-gate channel_pre_open_13(Channel *c, fd_set * readset, fd_set * writeset)
7140Sstevel@tonic-gate {
7150Sstevel@tonic-gate 	if (buffer_len(&c->input) < packet_get_maxsize())
7160Sstevel@tonic-gate 		FD_SET(c->sock, readset);
7170Sstevel@tonic-gate 	if (buffer_len(&c->output) > 0)
7180Sstevel@tonic-gate 		FD_SET(c->sock, writeset);
7190Sstevel@tonic-gate }
7200Sstevel@tonic-gate 
7210Sstevel@tonic-gate static void
7220Sstevel@tonic-gate channel_pre_open(Channel *c, fd_set * readset, fd_set * writeset)
7230Sstevel@tonic-gate {
7240Sstevel@tonic-gate 	u_int limit = compat20 ? c->remote_window : packet_get_maxsize();
7250Sstevel@tonic-gate 
7260Sstevel@tonic-gate 	if (c->istate == CHAN_INPUT_OPEN &&
7270Sstevel@tonic-gate 	    limit > 0 &&
7280Sstevel@tonic-gate 	    buffer_len(&c->input) < limit)
7290Sstevel@tonic-gate 		FD_SET(c->rfd, readset);
7300Sstevel@tonic-gate 	if (c->ostate == CHAN_OUTPUT_OPEN ||
7310Sstevel@tonic-gate 	    c->ostate == CHAN_OUTPUT_WAIT_DRAIN) {
7320Sstevel@tonic-gate 		if (buffer_len(&c->output) > 0) {
7330Sstevel@tonic-gate 			FD_SET(c->wfd, writeset);
7340Sstevel@tonic-gate 		} else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN) {
7350Sstevel@tonic-gate 			if (CHANNEL_EFD_OUTPUT_ACTIVE(c))
7360Sstevel@tonic-gate 			       debug2("channel %d: obuf_empty delayed efd %d/(%d)",
7370Sstevel@tonic-gate 				   c->self, c->efd, buffer_len(&c->extended));
7380Sstevel@tonic-gate 			else
7390Sstevel@tonic-gate 				chan_obuf_empty(c);
7400Sstevel@tonic-gate 		}
7410Sstevel@tonic-gate 	}
7420Sstevel@tonic-gate 	/** XXX check close conditions, too */
7430Sstevel@tonic-gate 	if (compat20 && c->efd != -1) {
7440Sstevel@tonic-gate 		if (c->extended_usage == CHAN_EXTENDED_WRITE &&
7450Sstevel@tonic-gate 		    buffer_len(&c->extended) > 0)
7460Sstevel@tonic-gate 			FD_SET(c->efd, writeset);
7470Sstevel@tonic-gate 		else if (!(c->flags & CHAN_EOF_SENT) &&
7480Sstevel@tonic-gate 		    c->extended_usage == CHAN_EXTENDED_READ &&
7490Sstevel@tonic-gate 		    buffer_len(&c->extended) < c->remote_window)
7500Sstevel@tonic-gate 			FD_SET(c->efd, readset);
7510Sstevel@tonic-gate 	}
7520Sstevel@tonic-gate }
7530Sstevel@tonic-gate 
7540Sstevel@tonic-gate static void
7550Sstevel@tonic-gate channel_pre_input_draining(Channel *c, fd_set * readset, fd_set * writeset)
7560Sstevel@tonic-gate {
7570Sstevel@tonic-gate 	if (buffer_len(&c->input) == 0) {
7580Sstevel@tonic-gate 		packet_start(SSH_MSG_CHANNEL_CLOSE);
7590Sstevel@tonic-gate 		packet_put_int(c->remote_id);
7600Sstevel@tonic-gate 		packet_send();
7610Sstevel@tonic-gate 		c->type = SSH_CHANNEL_CLOSED;
7620Sstevel@tonic-gate 		debug("channel %d: closing after input drain.", c->self);
7630Sstevel@tonic-gate 	}
7640Sstevel@tonic-gate }
7650Sstevel@tonic-gate 
7660Sstevel@tonic-gate static void
7670Sstevel@tonic-gate channel_pre_output_draining(Channel *c, fd_set * readset, fd_set * writeset)
7680Sstevel@tonic-gate {
7690Sstevel@tonic-gate 	if (buffer_len(&c->output) == 0)
7700Sstevel@tonic-gate 		chan_mark_dead(c);
7710Sstevel@tonic-gate 	else
7720Sstevel@tonic-gate 		FD_SET(c->sock, writeset);
7730Sstevel@tonic-gate }
7740Sstevel@tonic-gate 
7750Sstevel@tonic-gate /*
7760Sstevel@tonic-gate  * This is a special state for X11 authentication spoofing.  An opened X11
7770Sstevel@tonic-gate  * connection (when authentication spoofing is being done) remains in this
7780Sstevel@tonic-gate  * state until the first packet has been completely read.  The authentication
7790Sstevel@tonic-gate  * data in that packet is then substituted by the real data if it matches the
7800Sstevel@tonic-gate  * fake data, and the channel is put into normal mode.
7810Sstevel@tonic-gate  * XXX All this happens at the client side.
7820Sstevel@tonic-gate  * Returns: 0 = need more data, -1 = wrong cookie, 1 = ok
7830Sstevel@tonic-gate  */
7840Sstevel@tonic-gate static int
7850Sstevel@tonic-gate x11_open_helper(Buffer *b)
7860Sstevel@tonic-gate {
7870Sstevel@tonic-gate 	u_char *ucp;
7880Sstevel@tonic-gate 	u_int proto_len, data_len;
7890Sstevel@tonic-gate 
7900Sstevel@tonic-gate 	/* Check if the fixed size part of the packet is in buffer. */
7910Sstevel@tonic-gate 	if (buffer_len(b) < 12)
7920Sstevel@tonic-gate 		return 0;
7930Sstevel@tonic-gate 
7940Sstevel@tonic-gate 	/* Parse the lengths of variable-length fields. */
7950Sstevel@tonic-gate 	ucp = buffer_ptr(b);
7960Sstevel@tonic-gate 	if (ucp[0] == 0x42) {	/* Byte order MSB first. */
7970Sstevel@tonic-gate 		proto_len = 256 * ucp[6] + ucp[7];
7980Sstevel@tonic-gate 		data_len = 256 * ucp[8] + ucp[9];
7990Sstevel@tonic-gate 	} else if (ucp[0] == 0x6c) {	/* Byte order LSB first. */
8000Sstevel@tonic-gate 		proto_len = ucp[6] + 256 * ucp[7];
8010Sstevel@tonic-gate 		data_len = ucp[8] + 256 * ucp[9];
8020Sstevel@tonic-gate 	} else {
8030Sstevel@tonic-gate 		debug("Initial X11 packet contains bad byte order byte: 0x%x",
8040Sstevel@tonic-gate 		    ucp[0]);
8050Sstevel@tonic-gate 		return -1;
8060Sstevel@tonic-gate 	}
8070Sstevel@tonic-gate 
8080Sstevel@tonic-gate 	/* Check if the whole packet is in buffer. */
8090Sstevel@tonic-gate 	if (buffer_len(b) <
8100Sstevel@tonic-gate 	    12 + ((proto_len + 3) & ~3) + ((data_len + 3) & ~3))
8110Sstevel@tonic-gate 		return 0;
8120Sstevel@tonic-gate 
8130Sstevel@tonic-gate 	/* Check if authentication protocol matches. */
8140Sstevel@tonic-gate 	if (proto_len != strlen(x11_saved_proto) ||
8150Sstevel@tonic-gate 	    memcmp(ucp + 12, x11_saved_proto, proto_len) != 0) {
8160Sstevel@tonic-gate 		debug("X11 connection uses different authentication protocol.");
8170Sstevel@tonic-gate 		return -1;
8180Sstevel@tonic-gate 	}
8190Sstevel@tonic-gate 	/* Check if authentication data matches our fake data. */
8200Sstevel@tonic-gate 	if (data_len != x11_fake_data_len ||
8210Sstevel@tonic-gate 	    memcmp(ucp + 12 + ((proto_len + 3) & ~3),
8220Sstevel@tonic-gate 		x11_fake_data, x11_fake_data_len) != 0) {
8230Sstevel@tonic-gate 		debug("X11 auth data does not match fake data.");
8240Sstevel@tonic-gate 		return -1;
8250Sstevel@tonic-gate 	}
8260Sstevel@tonic-gate 	/* Check fake data length */
8270Sstevel@tonic-gate 	if (x11_fake_data_len != x11_saved_data_len) {
8280Sstevel@tonic-gate 		error("X11 fake_data_len %d != saved_data_len %d",
8290Sstevel@tonic-gate 		    x11_fake_data_len, x11_saved_data_len);
8300Sstevel@tonic-gate 		return -1;
8310Sstevel@tonic-gate 	}
8320Sstevel@tonic-gate 	/*
8330Sstevel@tonic-gate 	 * Received authentication protocol and data match
8340Sstevel@tonic-gate 	 * our fake data. Substitute the fake data with real
8350Sstevel@tonic-gate 	 * data.
8360Sstevel@tonic-gate 	 */
8370Sstevel@tonic-gate 	memcpy(ucp + 12 + ((proto_len + 3) & ~3),
8380Sstevel@tonic-gate 	    x11_saved_data, x11_saved_data_len);
8390Sstevel@tonic-gate 	return 1;
8400Sstevel@tonic-gate }
8410Sstevel@tonic-gate 
8420Sstevel@tonic-gate static void
8430Sstevel@tonic-gate channel_pre_x11_open_13(Channel *c, fd_set * readset, fd_set * writeset)
8440Sstevel@tonic-gate {
8450Sstevel@tonic-gate 	int ret = x11_open_helper(&c->output);
8460Sstevel@tonic-gate 
8470Sstevel@tonic-gate 	if (ret == 1) {
8480Sstevel@tonic-gate 		/* Start normal processing for the channel. */
8490Sstevel@tonic-gate 		c->type = SSH_CHANNEL_OPEN;
8500Sstevel@tonic-gate 		channel_pre_open_13(c, readset, writeset);
8510Sstevel@tonic-gate 	} else if (ret == -1) {
8520Sstevel@tonic-gate 		/*
8530Sstevel@tonic-gate 		 * We have received an X11 connection that has bad
8540Sstevel@tonic-gate 		 * authentication information.
8550Sstevel@tonic-gate 		 */
8560Sstevel@tonic-gate 		log("X11 connection rejected because of wrong authentication.");
8570Sstevel@tonic-gate 		buffer_clear(&c->input);
8580Sstevel@tonic-gate 		buffer_clear(&c->output);
8590Sstevel@tonic-gate 		channel_close_fd(&c->sock);
8600Sstevel@tonic-gate 		c->sock = -1;
8610Sstevel@tonic-gate 		c->type = SSH_CHANNEL_CLOSED;
8620Sstevel@tonic-gate 		packet_start(SSH_MSG_CHANNEL_CLOSE);
8630Sstevel@tonic-gate 		packet_put_int(c->remote_id);
8640Sstevel@tonic-gate 		packet_send();
8650Sstevel@tonic-gate 	}
8660Sstevel@tonic-gate }
8670Sstevel@tonic-gate 
8680Sstevel@tonic-gate static void
8690Sstevel@tonic-gate channel_pre_x11_open(Channel *c, fd_set * readset, fd_set * writeset)
8700Sstevel@tonic-gate {
8710Sstevel@tonic-gate 	int ret = x11_open_helper(&c->output);
8720Sstevel@tonic-gate 
8730Sstevel@tonic-gate 	/* c->force_drain = 1; */
8740Sstevel@tonic-gate 
8750Sstevel@tonic-gate 	if (ret == 1) {
8760Sstevel@tonic-gate 		c->type = SSH_CHANNEL_OPEN;
8770Sstevel@tonic-gate 		channel_pre_open(c, readset, writeset);
8780Sstevel@tonic-gate 	} else if (ret == -1) {
8790Sstevel@tonic-gate 		log("X11 connection rejected because of wrong authentication.");
8800Sstevel@tonic-gate 		debug("X11 rejected %d i%d/o%d", c->self, c->istate, c->ostate);
8810Sstevel@tonic-gate 		chan_read_failed(c);
8820Sstevel@tonic-gate 		buffer_clear(&c->input);
8830Sstevel@tonic-gate 		chan_ibuf_empty(c);
8840Sstevel@tonic-gate 		buffer_clear(&c->output);
8850Sstevel@tonic-gate 		/* for proto v1, the peer will send an IEOF */
8860Sstevel@tonic-gate 		if (compat20)
8870Sstevel@tonic-gate 			chan_write_failed(c);
8880Sstevel@tonic-gate 		else
8890Sstevel@tonic-gate 			c->type = SSH_CHANNEL_OPEN;
8900Sstevel@tonic-gate 		debug("X11 closed %d i%d/o%d", c->self, c->istate, c->ostate);
8910Sstevel@tonic-gate 	}
8920Sstevel@tonic-gate }
8930Sstevel@tonic-gate 
8940Sstevel@tonic-gate /* try to decode a socks4 header */
8950Sstevel@tonic-gate static int
8960Sstevel@tonic-gate channel_decode_socks4(Channel *c, fd_set * readset, fd_set * writeset)
8970Sstevel@tonic-gate {
8980Sstevel@tonic-gate 	char *p, *host;
8990Sstevel@tonic-gate 	int len, have, i, found;
9000Sstevel@tonic-gate 	char username[256];
9010Sstevel@tonic-gate 	struct {
9020Sstevel@tonic-gate 		u_int8_t version;
9030Sstevel@tonic-gate 		u_int8_t command;
9040Sstevel@tonic-gate 		u_int16_t dest_port;
9050Sstevel@tonic-gate 		struct in_addr dest_addr;
9060Sstevel@tonic-gate 	} s4_req, s4_rsp;
9070Sstevel@tonic-gate 
9080Sstevel@tonic-gate 	debug2("channel %d: decode socks4", c->self);
9090Sstevel@tonic-gate 
9100Sstevel@tonic-gate 	have = buffer_len(&c->input);
9110Sstevel@tonic-gate 	len = sizeof(s4_req);
9120Sstevel@tonic-gate 	if (have < len)
9130Sstevel@tonic-gate 		return 0;
9140Sstevel@tonic-gate 	p = buffer_ptr(&c->input);
9150Sstevel@tonic-gate 	for (found = 0, i = len; i < have; i++) {
9160Sstevel@tonic-gate 		if (p[i] == '\0') {
9170Sstevel@tonic-gate 			found = 1;
9180Sstevel@tonic-gate 			break;
9190Sstevel@tonic-gate 		}
9200Sstevel@tonic-gate 		if (i > 1024) {
9210Sstevel@tonic-gate 			/* the peer is probably sending garbage */
9220Sstevel@tonic-gate 			debug("channel %d: decode socks4: too long",
9230Sstevel@tonic-gate 			    c->self);
9240Sstevel@tonic-gate 			return -1;
9250Sstevel@tonic-gate 		}
9260Sstevel@tonic-gate 	}
9270Sstevel@tonic-gate 	if (!found)
9280Sstevel@tonic-gate 		return 0;
9290Sstevel@tonic-gate 	buffer_get(&c->input, (char *)&s4_req.version, 1);
9300Sstevel@tonic-gate 	buffer_get(&c->input, (char *)&s4_req.command, 1);
9310Sstevel@tonic-gate 	buffer_get(&c->input, (char *)&s4_req.dest_port, 2);
9320Sstevel@tonic-gate 	buffer_get(&c->input, (char *)&s4_req.dest_addr, 4);
9330Sstevel@tonic-gate 	have = buffer_len(&c->input);
9340Sstevel@tonic-gate 	p = buffer_ptr(&c->input);
9350Sstevel@tonic-gate 	len = strlen(p);
9360Sstevel@tonic-gate 	debug2("channel %d: decode socks4: user %s/%d", c->self, p, len);
9370Sstevel@tonic-gate 	if (len > have)
9380Sstevel@tonic-gate 		fatal("channel %d: decode socks4: len %d > have %d",
9390Sstevel@tonic-gate 		    c->self, len, have);
9400Sstevel@tonic-gate 	strlcpy(username, p, sizeof(username));
9410Sstevel@tonic-gate 	buffer_consume(&c->input, len);
9420Sstevel@tonic-gate 	buffer_consume(&c->input, 1);		/* trailing '\0' */
9430Sstevel@tonic-gate 
9440Sstevel@tonic-gate 	host = inet_ntoa(s4_req.dest_addr);
9450Sstevel@tonic-gate 	strlcpy(c->path, host, sizeof(c->path));
9460Sstevel@tonic-gate 	c->host_port = ntohs(s4_req.dest_port);
9470Sstevel@tonic-gate 
9480Sstevel@tonic-gate 	debug("channel %d: dynamic request: socks4 host %s port %u command %u",
9490Sstevel@tonic-gate 	    c->self, host, c->host_port, s4_req.command);
9500Sstevel@tonic-gate 
9510Sstevel@tonic-gate 	if (s4_req.command != 1) {
9520Sstevel@tonic-gate 		debug("channel %d: cannot handle: socks4 cn %d",
9530Sstevel@tonic-gate 		    c->self, s4_req.command);
9540Sstevel@tonic-gate 		return -1;
9550Sstevel@tonic-gate 	}
9560Sstevel@tonic-gate 	s4_rsp.version = 0;			/* vn: 0 for reply */
9570Sstevel@tonic-gate 	s4_rsp.command = 90;			/* cd: req granted */
9580Sstevel@tonic-gate 	s4_rsp.dest_port = 0;			/* ignored */
9590Sstevel@tonic-gate 	s4_rsp.dest_addr.s_addr = INADDR_ANY;	/* ignored */
9600Sstevel@tonic-gate 	buffer_append(&c->output, (char *)&s4_rsp, sizeof(s4_rsp));
9610Sstevel@tonic-gate 	return 1;
9620Sstevel@tonic-gate }
9630Sstevel@tonic-gate 
9644764Sjp161948 /* try to decode a socks5 header */
9654764Sjp161948 #define SSH_SOCKS5_AUTHDONE	0x1000
9664764Sjp161948 #define SSH_SOCKS5_NOAUTH	0x00
9674764Sjp161948 #define SSH_SOCKS5_IPV4		0x01
9684764Sjp161948 #define SSH_SOCKS5_DOMAIN	0x03
9694764Sjp161948 #define SSH_SOCKS5_IPV6		0x04
9704764Sjp161948 #define SSH_SOCKS5_CONNECT	0x01
9714764Sjp161948 #define SSH_SOCKS5_SUCCESS	0x00
9724764Sjp161948 
9734764Sjp161948 /* ARGSUSED */
9744764Sjp161948 static int
9754764Sjp161948 channel_decode_socks5(Channel *c, fd_set *readset, fd_set *writeset)
9764764Sjp161948 {
9774764Sjp161948 	struct {
9784764Sjp161948 		u_int8_t version;
9794764Sjp161948 		u_int8_t command;
9804764Sjp161948 		u_int8_t reserved;
9814764Sjp161948 		u_int8_t atyp;
9824764Sjp161948 	} s5_req, s5_rsp;
9834764Sjp161948 	u_int16_t dest_port;
9844764Sjp161948 	u_char *p, dest_addr[255+1];
9854764Sjp161948 	u_int have, need, i, found, nmethods, addrlen;
9864764Sjp161948 	struct in_addr bnd_addr;
9874764Sjp161948 	int af;
9884764Sjp161948 
9894764Sjp161948 	debug2("channel %d: decode socks5", c->self);
9904764Sjp161948 	p = buffer_ptr(&c->input);
9914764Sjp161948 	if (p[0] != 0x05)
9924764Sjp161948 		return -1;
9934764Sjp161948 	have = buffer_len(&c->input);
9944764Sjp161948 	if (!(c->flags & SSH_SOCKS5_AUTHDONE)) {
9954764Sjp161948 		/* format: ver | nmethods | methods */
9964764Sjp161948 		if (have < 2)
9974764Sjp161948 			return 0;
9984764Sjp161948 		nmethods = p[1];
9994764Sjp161948 		if (have < nmethods + 2)
10004764Sjp161948 			return 0;
10014764Sjp161948 		/* look for method: "NO AUTHENTICATION REQUIRED" */
10024764Sjp161948 		for (found = 0, i = 2 ; i < nmethods + 2; i++) {
10034764Sjp161948 			if (p[i] == SSH_SOCKS5_NOAUTH) {
10044764Sjp161948 				found = 1;
10054764Sjp161948 				break;
10064764Sjp161948 			}
10074764Sjp161948 		}
10084764Sjp161948 		if (!found) {
10094764Sjp161948 			error("channel %d: socks5 authentication methods not implemented",
10104764Sjp161948 			    c->self);
10114764Sjp161948 			error("channel %d: forwarding failed: "
10124764Sjp161948 			    "SSH_SOCKS5_NOAUTH method not found", c->self);
10134764Sjp161948 			return -1;
10144764Sjp161948 		}
10154764Sjp161948 		buffer_consume(&c->input, nmethods + 2);
10164764Sjp161948 		buffer_put_char(&c->output, 0x05);		/* version */
10174764Sjp161948 		buffer_put_char(&c->output, SSH_SOCKS5_NOAUTH);	/* method */
10184764Sjp161948 		FD_SET(c->sock, writeset);
10194764Sjp161948 		c->flags |= SSH_SOCKS5_AUTHDONE;
10204764Sjp161948 		debug2("channel %d: socks5 auth done", c->self);
10214764Sjp161948 		return 0;				/* need more */
10224764Sjp161948 	}
10234764Sjp161948 	debug2("channel %d: socks5 post auth", c->self);
10244764Sjp161948 	if (have < sizeof(s5_req)+1)
10254764Sjp161948 		return 0;			/* need more */
10264764Sjp161948 	memcpy(&s5_req, p, sizeof(s5_req));
10274764Sjp161948 	if (s5_req.version != 0x05 ||
10284764Sjp161948 	    s5_req.command != SSH_SOCKS5_CONNECT ||
10294764Sjp161948 	    s5_req.reserved != 0x00) {
10304764Sjp161948 		error("channel %d: forwarding failed: "
10314764Sjp161948 		    "only socks5 connect is supported", c->self);
10324764Sjp161948 		return -1;
10334764Sjp161948 	}
10344764Sjp161948 	switch (s5_req.atyp){
10354764Sjp161948 	case SSH_SOCKS5_IPV4:
10364764Sjp161948 		addrlen = 4;
10374764Sjp161948 		af = AF_INET;
10384764Sjp161948 		break;
10394764Sjp161948 	case SSH_SOCKS5_DOMAIN:
10404764Sjp161948 		addrlen = p[sizeof(s5_req)];
10414764Sjp161948 		af = -1;
10424764Sjp161948 		break;
10434764Sjp161948 	case SSH_SOCKS5_IPV6:
10444764Sjp161948 		addrlen = 16;
10454764Sjp161948 		af = AF_INET6;
10464764Sjp161948 		break;
10474764Sjp161948 	default:
10484764Sjp161948 		error("channel %d: forwarding failed: "
10494764Sjp161948 		    "bad socks5 atyp %d", c->self, s5_req.atyp);
10504764Sjp161948 		return -1;
10514764Sjp161948 	}
10524764Sjp161948 	need = sizeof(s5_req) + addrlen + 2;
10534764Sjp161948 	if (s5_req.atyp == SSH_SOCKS5_DOMAIN)
10544764Sjp161948 		need++;
10554764Sjp161948 	if (have < need)
10564764Sjp161948 		return 0;
10574764Sjp161948 	buffer_consume(&c->input, sizeof(s5_req));
10584764Sjp161948 	if (s5_req.atyp == SSH_SOCKS5_DOMAIN)
10594764Sjp161948 		buffer_consume(&c->input, 1);    /* host string length */
10604764Sjp161948 	buffer_get(&c->input, (char *)&dest_addr, addrlen);
10614764Sjp161948 	buffer_get(&c->input, (char *)&dest_port, 2);
10624764Sjp161948 	dest_addr[addrlen] = '\0';
10634764Sjp161948 	if (s5_req.atyp == SSH_SOCKS5_DOMAIN)
10644764Sjp161948 		strlcpy(c->path, (char *)dest_addr, sizeof(c->path));
10654764Sjp161948 	else if (inet_ntop(af, dest_addr, c->path, sizeof(c->path)) == NULL)
10664764Sjp161948 		return -1;
10674764Sjp161948 	c->host_port = ntohs(dest_port);
10684764Sjp161948 
10694764Sjp161948 	debug2("channel %d: dynamic request: socks5 host %s port %u command %u",
10704764Sjp161948 	    c->self, c->path, c->host_port, s5_req.command);
10714764Sjp161948 
10724764Sjp161948 	s5_rsp.version = 0x05;
10734764Sjp161948 	s5_rsp.command = SSH_SOCKS5_SUCCESS;
10744764Sjp161948 	s5_rsp.reserved = 0;			/* ignored */
10754764Sjp161948 	s5_rsp.atyp = SSH_SOCKS5_IPV4;
10764764Sjp161948 	bzero(&bnd_addr, sizeof(bnd_addr));
10774764Sjp161948 	bnd_addr.s_addr = htonl(INADDR_ANY);
10784764Sjp161948 	dest_port = 0;				/* ignored */
10794764Sjp161948 
10804764Sjp161948 	buffer_append(&c->output, &s5_rsp, sizeof(s5_rsp));
10814764Sjp161948 	buffer_append(&c->output, &bnd_addr, sizeof(struct in_addr));
10824764Sjp161948 	buffer_append(&c->output, &dest_port, sizeof(dest_port));
10834764Sjp161948 	return 1;
10844764Sjp161948 }
10854764Sjp161948 
10860Sstevel@tonic-gate /* dynamic port forwarding */
10870Sstevel@tonic-gate static void
10880Sstevel@tonic-gate channel_pre_dynamic(Channel *c, fd_set * readset, fd_set * writeset)
10890Sstevel@tonic-gate {
10900Sstevel@tonic-gate 	u_char *p;
10910Sstevel@tonic-gate 	int have, ret;
10920Sstevel@tonic-gate 
10930Sstevel@tonic-gate 	have = buffer_len(&c->input);
10940Sstevel@tonic-gate 	c->delayed = 0;
10950Sstevel@tonic-gate 	debug2("channel %d: pre_dynamic: have %d", c->self, have);
10960Sstevel@tonic-gate 	/* buffer_dump(&c->input); */
10970Sstevel@tonic-gate 	/* check if the fixed size part of the packet is in buffer. */
10984764Sjp161948 	if (have < 3) {
10990Sstevel@tonic-gate 		/* need more */
11000Sstevel@tonic-gate 		FD_SET(c->sock, readset);
11010Sstevel@tonic-gate 		return;
11020Sstevel@tonic-gate 	}
11030Sstevel@tonic-gate 	/* try to guess the protocol */
11040Sstevel@tonic-gate 	p = buffer_ptr(&c->input);
11050Sstevel@tonic-gate 	switch (p[0]) {
11060Sstevel@tonic-gate 	case 0x04:
11070Sstevel@tonic-gate 		ret = channel_decode_socks4(c, readset, writeset);
11080Sstevel@tonic-gate 		break;
11094764Sjp161948 	case 0x05:
11104764Sjp161948 		ret = channel_decode_socks5(c, readset, writeset);
11114764Sjp161948 		break;
11120Sstevel@tonic-gate 	default:
11134764Sjp161948 		error("channel %d: forwarding failed: unknown socks "
11144764Sjp161948 		    "version 0x%02X", c->self, p[0]);
11150Sstevel@tonic-gate 		ret = -1;
11160Sstevel@tonic-gate 		break;
11170Sstevel@tonic-gate 	}
11180Sstevel@tonic-gate 	if (ret < 0) {
11190Sstevel@tonic-gate 		chan_mark_dead(c);
11200Sstevel@tonic-gate 	} else if (ret == 0) {
11210Sstevel@tonic-gate 		debug2("channel %d: pre_dynamic: need more", c->self);
11220Sstevel@tonic-gate 		/* need more */
11230Sstevel@tonic-gate 		FD_SET(c->sock, readset);
11240Sstevel@tonic-gate 	} else {
11250Sstevel@tonic-gate 		/* switch to the next state */
11260Sstevel@tonic-gate 		c->type = SSH_CHANNEL_OPENING;
11270Sstevel@tonic-gate 		port_open_helper(c, "direct-tcpip");
11280Sstevel@tonic-gate 	}
11290Sstevel@tonic-gate }
11300Sstevel@tonic-gate 
11310Sstevel@tonic-gate /* This is our fake X11 server socket. */
11320Sstevel@tonic-gate static void
11330Sstevel@tonic-gate channel_post_x11_listener(Channel *c, fd_set * readset, fd_set * writeset)
11340Sstevel@tonic-gate {
11350Sstevel@tonic-gate 	Channel *nc;
11360Sstevel@tonic-gate 	struct sockaddr addr;
11370Sstevel@tonic-gate 	int newsock;
11380Sstevel@tonic-gate 	socklen_t addrlen;
11390Sstevel@tonic-gate 	char buf[16384], *remote_ipaddr;
11400Sstevel@tonic-gate 	int remote_port;
11410Sstevel@tonic-gate 
11420Sstevel@tonic-gate 	if (FD_ISSET(c->sock, readset)) {
11430Sstevel@tonic-gate 		debug("X11 connection requested.");
11440Sstevel@tonic-gate 		addrlen = sizeof(addr);
11450Sstevel@tonic-gate 		newsock = accept(c->sock, &addr, &addrlen);
11460Sstevel@tonic-gate 		if (c->single_connection) {
11470Sstevel@tonic-gate 			debug("single_connection: closing X11 listener.");
11480Sstevel@tonic-gate 			channel_close_fd(&c->sock);
11490Sstevel@tonic-gate 			chan_mark_dead(c);
11500Sstevel@tonic-gate 		}
11510Sstevel@tonic-gate 		if (newsock < 0) {
11520Sstevel@tonic-gate 			error("accept: %.100s", strerror(errno));
11530Sstevel@tonic-gate 			return;
11540Sstevel@tonic-gate 		}
11550Sstevel@tonic-gate 		set_nodelay(newsock);
11560Sstevel@tonic-gate 		remote_ipaddr = get_peer_ipaddr(newsock);
11570Sstevel@tonic-gate 		remote_port = get_peer_port(newsock);
11580Sstevel@tonic-gate 		snprintf(buf, sizeof buf, "X11 connection from %.200s port %d",
11590Sstevel@tonic-gate 		    remote_ipaddr, remote_port);
11600Sstevel@tonic-gate 
11610Sstevel@tonic-gate 		nc = channel_new("accepted x11 socket",
11620Sstevel@tonic-gate 		    SSH_CHANNEL_OPENING, newsock, newsock, -1,
11630Sstevel@tonic-gate 		    c->local_window_max, c->local_maxpacket,
11640Sstevel@tonic-gate 		    0, xstrdup(buf), 1);
11650Sstevel@tonic-gate 		if (compat20) {
11660Sstevel@tonic-gate 			packet_start(SSH2_MSG_CHANNEL_OPEN);
11670Sstevel@tonic-gate 			packet_put_cstring("x11");
11680Sstevel@tonic-gate 			packet_put_int(nc->self);
11690Sstevel@tonic-gate 			packet_put_int(nc->local_window_max);
11700Sstevel@tonic-gate 			packet_put_int(nc->local_maxpacket);
11710Sstevel@tonic-gate 			/* originator ipaddr and port */
11720Sstevel@tonic-gate 			packet_put_cstring(remote_ipaddr);
11730Sstevel@tonic-gate 			if (datafellows & SSH_BUG_X11FWD) {
11740Sstevel@tonic-gate 				debug("ssh2 x11 bug compat mode");
11750Sstevel@tonic-gate 			} else {
11760Sstevel@tonic-gate 				packet_put_int(remote_port);
11770Sstevel@tonic-gate 			}
11780Sstevel@tonic-gate 			packet_send();
11790Sstevel@tonic-gate 		} else {
11800Sstevel@tonic-gate 			packet_start(SSH_SMSG_X11_OPEN);
11810Sstevel@tonic-gate 			packet_put_int(nc->self);
11820Sstevel@tonic-gate 			if (packet_get_protocol_flags() &
11830Sstevel@tonic-gate 			    SSH_PROTOFLAG_HOST_IN_FWD_OPEN)
11840Sstevel@tonic-gate 				packet_put_cstring(buf);
11850Sstevel@tonic-gate 			packet_send();
11860Sstevel@tonic-gate 		}
11870Sstevel@tonic-gate 		xfree(remote_ipaddr);
11880Sstevel@tonic-gate 	}
11890Sstevel@tonic-gate }
11900Sstevel@tonic-gate 
11910Sstevel@tonic-gate static void
11920Sstevel@tonic-gate port_open_helper(Channel *c, char *rtype)
11930Sstevel@tonic-gate {
11940Sstevel@tonic-gate 	int direct;
11950Sstevel@tonic-gate 	char buf[1024];
11960Sstevel@tonic-gate 	char *remote_ipaddr = get_peer_ipaddr(c->sock);
11970Sstevel@tonic-gate 	u_short remote_port = get_peer_port(c->sock);
11980Sstevel@tonic-gate 
11990Sstevel@tonic-gate 	direct = (strcmp(rtype, "direct-tcpip") == 0);
12000Sstevel@tonic-gate 
12010Sstevel@tonic-gate 	snprintf(buf, sizeof buf,
12020Sstevel@tonic-gate 	    "%s: listening port %d for %.100s port %d, "
12030Sstevel@tonic-gate 	    "connect from %.200s port %d",
12040Sstevel@tonic-gate 	    rtype, c->listening_port, c->path, c->host_port,
12050Sstevel@tonic-gate 	    remote_ipaddr, remote_port);
12060Sstevel@tonic-gate 
12070Sstevel@tonic-gate 	xfree(c->remote_name);
12080Sstevel@tonic-gate 	c->remote_name = xstrdup(buf);
12090Sstevel@tonic-gate 
12100Sstevel@tonic-gate 	if (compat20) {
12110Sstevel@tonic-gate 		packet_start(SSH2_MSG_CHANNEL_OPEN);
12120Sstevel@tonic-gate 		packet_put_cstring(rtype);
12130Sstevel@tonic-gate 		packet_put_int(c->self);
12140Sstevel@tonic-gate 		packet_put_int(c->local_window_max);
12150Sstevel@tonic-gate 		packet_put_int(c->local_maxpacket);
12160Sstevel@tonic-gate 		if (direct) {
12170Sstevel@tonic-gate 			/* target host, port */
12180Sstevel@tonic-gate 			packet_put_cstring(c->path);
12190Sstevel@tonic-gate 			packet_put_int(c->host_port);
12200Sstevel@tonic-gate 		} else {
12210Sstevel@tonic-gate 			/* listen address, port */
12220Sstevel@tonic-gate 			packet_put_cstring(c->path);
12230Sstevel@tonic-gate 			packet_put_int(c->listening_port);
12240Sstevel@tonic-gate 		}
12250Sstevel@tonic-gate 		/* originator host and port */
12260Sstevel@tonic-gate 		packet_put_cstring(remote_ipaddr);
12270Sstevel@tonic-gate 		packet_put_int(remote_port);
12280Sstevel@tonic-gate 		packet_send();
12290Sstevel@tonic-gate 	} else {
12300Sstevel@tonic-gate 		packet_start(SSH_MSG_PORT_OPEN);
12310Sstevel@tonic-gate 		packet_put_int(c->self);
12320Sstevel@tonic-gate 		packet_put_cstring(c->path);
12330Sstevel@tonic-gate 		packet_put_int(c->host_port);
12340Sstevel@tonic-gate 		if (packet_get_protocol_flags() &
12350Sstevel@tonic-gate 		    SSH_PROTOFLAG_HOST_IN_FWD_OPEN)
12360Sstevel@tonic-gate 			packet_put_cstring(c->remote_name);
12370Sstevel@tonic-gate 		packet_send();
12380Sstevel@tonic-gate 	}
12390Sstevel@tonic-gate 	xfree(remote_ipaddr);
12400Sstevel@tonic-gate }
12410Sstevel@tonic-gate 
12420Sstevel@tonic-gate /*
12430Sstevel@tonic-gate  * This socket is listening for connections to a forwarded TCP/IP port.
12440Sstevel@tonic-gate  */
12450Sstevel@tonic-gate static void
12460Sstevel@tonic-gate channel_post_port_listener(Channel *c, fd_set * readset, fd_set * writeset)
12470Sstevel@tonic-gate {
12480Sstevel@tonic-gate 	Channel *nc;
12490Sstevel@tonic-gate 	struct sockaddr addr;
12500Sstevel@tonic-gate 	int newsock, nextstate;
12510Sstevel@tonic-gate 	socklen_t addrlen;
12520Sstevel@tonic-gate 	char *rtype;
12530Sstevel@tonic-gate 
12540Sstevel@tonic-gate 	if (FD_ISSET(c->sock, readset)) {
12550Sstevel@tonic-gate 		debug("Connection to port %d forwarding "
12560Sstevel@tonic-gate 		    "to %.100s port %d requested.",
12570Sstevel@tonic-gate 		    c->listening_port, c->path, c->host_port);
12580Sstevel@tonic-gate 
12590Sstevel@tonic-gate 		if (c->type == SSH_CHANNEL_RPORT_LISTENER) {
12600Sstevel@tonic-gate 			nextstate = SSH_CHANNEL_OPENING;
12610Sstevel@tonic-gate 			rtype = "forwarded-tcpip";
12620Sstevel@tonic-gate 		} else {
12630Sstevel@tonic-gate 			if (c->host_port == 0) {
12640Sstevel@tonic-gate 				nextstate = SSH_CHANNEL_DYNAMIC;
12650Sstevel@tonic-gate 				rtype = "dynamic-tcpip";
12660Sstevel@tonic-gate 			} else {
12670Sstevel@tonic-gate 				nextstate = SSH_CHANNEL_OPENING;
12680Sstevel@tonic-gate 				rtype = "direct-tcpip";
12690Sstevel@tonic-gate 			}
12700Sstevel@tonic-gate 		}
12710Sstevel@tonic-gate 
12720Sstevel@tonic-gate 		addrlen = sizeof(addr);
12730Sstevel@tonic-gate 		newsock = accept(c->sock, &addr, &addrlen);
12740Sstevel@tonic-gate 		if (newsock < 0) {
12750Sstevel@tonic-gate 			error("accept: %.100s", strerror(errno));
12760Sstevel@tonic-gate 			return;
12770Sstevel@tonic-gate 		}
12780Sstevel@tonic-gate 		set_nodelay(newsock);
12790Sstevel@tonic-gate 		nc = channel_new(rtype,
12800Sstevel@tonic-gate 		    nextstate, newsock, newsock, -1,
12810Sstevel@tonic-gate 		    c->local_window_max, c->local_maxpacket,
12820Sstevel@tonic-gate 		    0, xstrdup(rtype), 1);
12830Sstevel@tonic-gate 		nc->listening_port = c->listening_port;
12840Sstevel@tonic-gate 		nc->host_port = c->host_port;
12850Sstevel@tonic-gate 		strlcpy(nc->path, c->path, sizeof(nc->path));
12860Sstevel@tonic-gate 
12870Sstevel@tonic-gate 		if (nextstate == SSH_CHANNEL_DYNAMIC) {
12880Sstevel@tonic-gate 			/*
12890Sstevel@tonic-gate 			 * do not call the channel_post handler until
12900Sstevel@tonic-gate 			 * this flag has been reset by a pre-handler.
12910Sstevel@tonic-gate 			 * otherwise the FD_ISSET calls might overflow
12920Sstevel@tonic-gate 			 */
12930Sstevel@tonic-gate 			nc->delayed = 1;
12940Sstevel@tonic-gate 		} else {
12950Sstevel@tonic-gate 			port_open_helper(nc, rtype);
12960Sstevel@tonic-gate 		}
12970Sstevel@tonic-gate 	}
12980Sstevel@tonic-gate }
12990Sstevel@tonic-gate 
13000Sstevel@tonic-gate /*
13010Sstevel@tonic-gate  * This is the authentication agent socket listening for connections from
13020Sstevel@tonic-gate  * clients.
13030Sstevel@tonic-gate  */
13040Sstevel@tonic-gate static void
13050Sstevel@tonic-gate channel_post_auth_listener(Channel *c, fd_set * readset, fd_set * writeset)
13060Sstevel@tonic-gate {
13070Sstevel@tonic-gate 	Channel *nc;
13080Sstevel@tonic-gate 	char *name;
13090Sstevel@tonic-gate 	int newsock;
13100Sstevel@tonic-gate 	struct sockaddr addr;
13110Sstevel@tonic-gate 	socklen_t addrlen;
13120Sstevel@tonic-gate 
13130Sstevel@tonic-gate 	if (FD_ISSET(c->sock, readset)) {
13140Sstevel@tonic-gate 		addrlen = sizeof(addr);
13150Sstevel@tonic-gate 		newsock = accept(c->sock, &addr, &addrlen);
13160Sstevel@tonic-gate 		if (newsock < 0) {
13170Sstevel@tonic-gate 			error("accept from auth socket: %.100s", strerror(errno));
13180Sstevel@tonic-gate 			return;
13190Sstevel@tonic-gate 		}
13200Sstevel@tonic-gate 		name = xstrdup("accepted auth socket");
13210Sstevel@tonic-gate 		nc = channel_new("accepted auth socket",
13220Sstevel@tonic-gate 		    SSH_CHANNEL_OPENING, newsock, newsock, -1,
13230Sstevel@tonic-gate 		    c->local_window_max, c->local_maxpacket,
13240Sstevel@tonic-gate 		    0, name, 1);
13250Sstevel@tonic-gate 		if (compat20) {
13260Sstevel@tonic-gate 			packet_start(SSH2_MSG_CHANNEL_OPEN);
13270Sstevel@tonic-gate 			packet_put_cstring("auth-agent@openssh.com");
13280Sstevel@tonic-gate 			packet_put_int(nc->self);
13290Sstevel@tonic-gate 			packet_put_int(c->local_window_max);
13300Sstevel@tonic-gate 			packet_put_int(c->local_maxpacket);
13310Sstevel@tonic-gate 		} else {
13320Sstevel@tonic-gate 			packet_start(SSH_SMSG_AGENT_OPEN);
13330Sstevel@tonic-gate 			packet_put_int(nc->self);
13340Sstevel@tonic-gate 		}
13350Sstevel@tonic-gate 		packet_send();
13360Sstevel@tonic-gate 	}
13370Sstevel@tonic-gate }
13380Sstevel@tonic-gate 
13390Sstevel@tonic-gate static void
13400Sstevel@tonic-gate channel_post_connecting(Channel *c, fd_set * readset, fd_set * writeset)
13410Sstevel@tonic-gate {
13420Sstevel@tonic-gate 	int err = 0;
13430Sstevel@tonic-gate 	socklen_t sz = sizeof(err);
13440Sstevel@tonic-gate 
13450Sstevel@tonic-gate 	if (FD_ISSET(c->sock, writeset)) {
13460Sstevel@tonic-gate 		if (getsockopt(c->sock, SOL_SOCKET, SO_ERROR, &err, &sz) < 0) {
13470Sstevel@tonic-gate 			err = errno;
13480Sstevel@tonic-gate 			error("getsockopt SO_ERROR failed");
13490Sstevel@tonic-gate 		}
13500Sstevel@tonic-gate 		if (err == 0) {
13510Sstevel@tonic-gate 			debug("channel %d: connected", c->self);
13520Sstevel@tonic-gate 			c->type = SSH_CHANNEL_OPEN;
13530Sstevel@tonic-gate 			if (compat20) {
13540Sstevel@tonic-gate 				packet_start(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION);
13550Sstevel@tonic-gate 				packet_put_int(c->remote_id);
13560Sstevel@tonic-gate 				packet_put_int(c->self);
13570Sstevel@tonic-gate 				packet_put_int(c->local_window);
13580Sstevel@tonic-gate 				packet_put_int(c->local_maxpacket);
13590Sstevel@tonic-gate 			} else {
13600Sstevel@tonic-gate 				packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION);
13610Sstevel@tonic-gate 				packet_put_int(c->remote_id);
13620Sstevel@tonic-gate 				packet_put_int(c->self);
13630Sstevel@tonic-gate 			}
13640Sstevel@tonic-gate 		} else {
13650Sstevel@tonic-gate 			debug("channel %d: not connected: %s",
13660Sstevel@tonic-gate 			    c->self, strerror(err));
13670Sstevel@tonic-gate 			if (compat20) {
13680Sstevel@tonic-gate 				packet_start(SSH2_MSG_CHANNEL_OPEN_FAILURE);
13690Sstevel@tonic-gate 				packet_put_int(c->remote_id);
13700Sstevel@tonic-gate 				packet_put_int(SSH2_OPEN_CONNECT_FAILED);
13710Sstevel@tonic-gate 				if (!(datafellows & SSH_BUG_OPENFAILURE)) {
13720Sstevel@tonic-gate 					packet_put_cstring(strerror(err));
13730Sstevel@tonic-gate 					packet_put_cstring("");
13740Sstevel@tonic-gate 				}
13750Sstevel@tonic-gate 			} else {
13760Sstevel@tonic-gate 				packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE);
13770Sstevel@tonic-gate 				packet_put_int(c->remote_id);
13780Sstevel@tonic-gate 			}
13790Sstevel@tonic-gate 			chan_mark_dead(c);
13800Sstevel@tonic-gate 		}
13810Sstevel@tonic-gate 		packet_send();
13820Sstevel@tonic-gate 	}
13830Sstevel@tonic-gate }
13840Sstevel@tonic-gate 
13850Sstevel@tonic-gate static int
13860Sstevel@tonic-gate channel_handle_rfd(Channel *c, fd_set * readset, fd_set * writeset)
13870Sstevel@tonic-gate {
13880Sstevel@tonic-gate 	char buf[16*1024];
13890Sstevel@tonic-gate 	int len;
13900Sstevel@tonic-gate 
13910Sstevel@tonic-gate 	if (c->rfd != -1 &&
13920Sstevel@tonic-gate 	    FD_ISSET(c->rfd, readset)) {
13930Sstevel@tonic-gate 		len = read(c->rfd, buf, sizeof(buf));
13940Sstevel@tonic-gate 		if (len < 0 && (errno == EINTR || errno == EAGAIN))
13950Sstevel@tonic-gate 			return 1;
13960Sstevel@tonic-gate 		if (len <= 0) {
13970Sstevel@tonic-gate 			debug("channel %d: read<=0 rfd %d len %d",
13980Sstevel@tonic-gate 			    c->self, c->rfd, len);
13990Sstevel@tonic-gate 			if (c->type != SSH_CHANNEL_OPEN) {
14000Sstevel@tonic-gate 				debug("channel %d: not open", c->self);
14010Sstevel@tonic-gate 				chan_mark_dead(c);
14020Sstevel@tonic-gate 				return -1;
14030Sstevel@tonic-gate 			} else if (compat13) {
14040Sstevel@tonic-gate 				buffer_clear(&c->output);
14050Sstevel@tonic-gate 				c->type = SSH_CHANNEL_INPUT_DRAINING;
14060Sstevel@tonic-gate 				debug("channel %d: input draining.", c->self);
14070Sstevel@tonic-gate 			} else {
14080Sstevel@tonic-gate 				chan_read_failed(c);
14090Sstevel@tonic-gate 			}
14100Sstevel@tonic-gate 			return -1;
14110Sstevel@tonic-gate 		}
14120Sstevel@tonic-gate 		if (c->input_filter != NULL) {
14130Sstevel@tonic-gate 			if (c->input_filter(c, buf, len) == -1) {
14140Sstevel@tonic-gate 				debug("channel %d: filter stops", c->self);
14150Sstevel@tonic-gate 				chan_read_failed(c);
14160Sstevel@tonic-gate 			}
14170Sstevel@tonic-gate 		} else {
14180Sstevel@tonic-gate 			buffer_append(&c->input, buf, len);
14190Sstevel@tonic-gate 		}
14200Sstevel@tonic-gate 	}
14210Sstevel@tonic-gate 	return 1;
14220Sstevel@tonic-gate }
14230Sstevel@tonic-gate static int
14240Sstevel@tonic-gate channel_handle_wfd(Channel *c, fd_set * readset, fd_set * writeset)
14250Sstevel@tonic-gate {
14260Sstevel@tonic-gate 	struct termios tio;
14270Sstevel@tonic-gate 	u_char *data;
14280Sstevel@tonic-gate 	u_int dlen;
14290Sstevel@tonic-gate 	int len;
14300Sstevel@tonic-gate 
14310Sstevel@tonic-gate 	/* Send buffered output data to the socket. */
14320Sstevel@tonic-gate 	if (c->wfd != -1 &&
14330Sstevel@tonic-gate 	    FD_ISSET(c->wfd, writeset) &&
14340Sstevel@tonic-gate 	    buffer_len(&c->output) > 0) {
14350Sstevel@tonic-gate 		data = buffer_ptr(&c->output);
14360Sstevel@tonic-gate 		dlen = buffer_len(&c->output);
14370Sstevel@tonic-gate #ifdef _AIX
14380Sstevel@tonic-gate 		/* XXX: Later AIX versions can't push as much data to tty */
14390Sstevel@tonic-gate 		if (compat20 && c->wfd_isatty && dlen > 8*1024)
14400Sstevel@tonic-gate 			dlen = 8*1024;
14410Sstevel@tonic-gate #endif
14420Sstevel@tonic-gate 		len = write(c->wfd, data, dlen);
14430Sstevel@tonic-gate 		if (len < 0 && (errno == EINTR || errno == EAGAIN))
14440Sstevel@tonic-gate 			return 1;
14450Sstevel@tonic-gate 		if (len <= 0) {
14460Sstevel@tonic-gate 			if (c->type != SSH_CHANNEL_OPEN) {
14470Sstevel@tonic-gate 				debug("channel %d: not open", c->self);
14480Sstevel@tonic-gate 				chan_mark_dead(c);
14490Sstevel@tonic-gate 				return -1;
14500Sstevel@tonic-gate 			} else if (compat13) {
14510Sstevel@tonic-gate 				buffer_clear(&c->output);
14520Sstevel@tonic-gate 				debug("channel %d: input draining.", c->self);
14530Sstevel@tonic-gate 				c->type = SSH_CHANNEL_INPUT_DRAINING;
14540Sstevel@tonic-gate 			} else {
14550Sstevel@tonic-gate 				chan_write_failed(c);
14560Sstevel@tonic-gate 			}
14570Sstevel@tonic-gate 			return -1;
14580Sstevel@tonic-gate 		}
14590Sstevel@tonic-gate 		if (compat20 && c->isatty && dlen >= 1 && data[0] != '\r') {
14600Sstevel@tonic-gate 			if (tcgetattr(c->wfd, &tio) == 0 &&
14610Sstevel@tonic-gate 			    !(tio.c_lflag & ECHO) && (tio.c_lflag & ICANON)) {
14620Sstevel@tonic-gate 				/*
14630Sstevel@tonic-gate 				 * Simulate echo to reduce the impact of
14640Sstevel@tonic-gate 				 * traffic analysis. We need to match the
14650Sstevel@tonic-gate 				 * size of a SSH2_MSG_CHANNEL_DATA message
14660Sstevel@tonic-gate 				 * (4 byte channel id + data)
14670Sstevel@tonic-gate 				 */
14680Sstevel@tonic-gate 				packet_send_ignore(4 + len);
14690Sstevel@tonic-gate 				packet_send();
14700Sstevel@tonic-gate 			}
14710Sstevel@tonic-gate 		}
14720Sstevel@tonic-gate 		buffer_consume(&c->output, len);
14730Sstevel@tonic-gate 		if (compat20 && len > 0) {
14740Sstevel@tonic-gate 			c->local_consumed += len;
14750Sstevel@tonic-gate 		}
14760Sstevel@tonic-gate 	}
14770Sstevel@tonic-gate 	return 1;
14780Sstevel@tonic-gate }
14790Sstevel@tonic-gate static int
14800Sstevel@tonic-gate channel_handle_efd(Channel *c, fd_set * readset, fd_set * writeset)
14810Sstevel@tonic-gate {
14820Sstevel@tonic-gate 	char buf[16*1024];
14830Sstevel@tonic-gate 	int len;
14840Sstevel@tonic-gate 
14850Sstevel@tonic-gate /** XXX handle drain efd, too */
14860Sstevel@tonic-gate 	if (c->efd != -1) {
14870Sstevel@tonic-gate 		if (c->extended_usage == CHAN_EXTENDED_WRITE &&
14880Sstevel@tonic-gate 		    FD_ISSET(c->efd, writeset) &&
14890Sstevel@tonic-gate 		    buffer_len(&c->extended) > 0) {
14900Sstevel@tonic-gate 			len = write(c->efd, buffer_ptr(&c->extended),
14910Sstevel@tonic-gate 			    buffer_len(&c->extended));
14920Sstevel@tonic-gate 			debug2("channel %d: written %d to efd %d",
14930Sstevel@tonic-gate 			    c->self, len, c->efd);
14940Sstevel@tonic-gate 			if (len < 0 && (errno == EINTR || errno == EAGAIN))
14950Sstevel@tonic-gate 				return 1;
14960Sstevel@tonic-gate 			if (len <= 0) {
14970Sstevel@tonic-gate 				debug2("channel %d: closing write-efd %d",
14980Sstevel@tonic-gate 				    c->self, c->efd);
14990Sstevel@tonic-gate 				channel_close_fd(&c->efd);
15000Sstevel@tonic-gate 			} else {
15010Sstevel@tonic-gate 				buffer_consume(&c->extended, len);
15020Sstevel@tonic-gate 				c->local_consumed += len;
15030Sstevel@tonic-gate 			}
15040Sstevel@tonic-gate 		} else if (c->extended_usage == CHAN_EXTENDED_READ &&
15050Sstevel@tonic-gate 		    FD_ISSET(c->efd, readset)) {
15060Sstevel@tonic-gate 			len = read(c->efd, buf, sizeof(buf));
15070Sstevel@tonic-gate 			debug2("channel %d: read %d from efd %d",
15080Sstevel@tonic-gate 			    c->self, len, c->efd);
15090Sstevel@tonic-gate 			if (len < 0 && (errno == EINTR || errno == EAGAIN))
15100Sstevel@tonic-gate 				return 1;
15110Sstevel@tonic-gate 			if (len <= 0) {
15120Sstevel@tonic-gate 				debug2("channel %d: closing read-efd %d",
15130Sstevel@tonic-gate 				    c->self, c->efd);
15140Sstevel@tonic-gate 				channel_close_fd(&c->efd);
15150Sstevel@tonic-gate 			} else {
15160Sstevel@tonic-gate 				buffer_append(&c->extended, buf, len);
15170Sstevel@tonic-gate 			}
15180Sstevel@tonic-gate 		}
15190Sstevel@tonic-gate 	}
15200Sstevel@tonic-gate 	return 1;
15210Sstevel@tonic-gate }
15220Sstevel@tonic-gate static int
15230Sstevel@tonic-gate channel_check_window(Channel *c)
15240Sstevel@tonic-gate {
15250Sstevel@tonic-gate 	if (c->type == SSH_CHANNEL_OPEN &&
15260Sstevel@tonic-gate 	    !(c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD)) &&
15270Sstevel@tonic-gate 	    c->local_window < c->local_window_max/2 &&
15280Sstevel@tonic-gate 	    c->local_consumed > 0) {
15290Sstevel@tonic-gate 		packet_start(SSH2_MSG_CHANNEL_WINDOW_ADJUST);
15300Sstevel@tonic-gate 		packet_put_int(c->remote_id);
15310Sstevel@tonic-gate 		packet_put_int(c->local_consumed);
15320Sstevel@tonic-gate 		packet_send();
15330Sstevel@tonic-gate 		debug2("channel %d: window %d sent adjust %d",
15340Sstevel@tonic-gate 		    c->self, c->local_window,
15350Sstevel@tonic-gate 		    c->local_consumed);
15360Sstevel@tonic-gate 		c->local_window += c->local_consumed;
15370Sstevel@tonic-gate 		c->local_consumed = 0;
15380Sstevel@tonic-gate 	}
15390Sstevel@tonic-gate 	return 1;
15400Sstevel@tonic-gate }
15410Sstevel@tonic-gate 
15420Sstevel@tonic-gate static void
15430Sstevel@tonic-gate channel_post_open(Channel *c, fd_set * readset, fd_set * writeset)
15440Sstevel@tonic-gate {
15450Sstevel@tonic-gate 	if (c->delayed)
15460Sstevel@tonic-gate 		return;
15470Sstevel@tonic-gate 	channel_handle_rfd(c, readset, writeset);
15480Sstevel@tonic-gate 	channel_handle_wfd(c, readset, writeset);
15490Sstevel@tonic-gate 	if (!compat20)
15500Sstevel@tonic-gate 		return;
15510Sstevel@tonic-gate 	channel_handle_efd(c, readset, writeset);
15520Sstevel@tonic-gate 	channel_check_window(c);
15530Sstevel@tonic-gate }
15540Sstevel@tonic-gate 
15550Sstevel@tonic-gate static void
15560Sstevel@tonic-gate channel_post_output_drain_13(Channel *c, fd_set * readset, fd_set * writeset)
15570Sstevel@tonic-gate {
15580Sstevel@tonic-gate 	int len;
15590Sstevel@tonic-gate 
15600Sstevel@tonic-gate 	/* Send buffered output data to the socket. */
15610Sstevel@tonic-gate 	if (FD_ISSET(c->sock, writeset) && buffer_len(&c->output) > 0) {
15620Sstevel@tonic-gate 		len = write(c->sock, buffer_ptr(&c->output),
15630Sstevel@tonic-gate 			    buffer_len(&c->output));
15640Sstevel@tonic-gate 		if (len <= 0)
15650Sstevel@tonic-gate 			buffer_clear(&c->output);
15660Sstevel@tonic-gate 		else
15670Sstevel@tonic-gate 			buffer_consume(&c->output, len);
15680Sstevel@tonic-gate 	}
15690Sstevel@tonic-gate }
15700Sstevel@tonic-gate 
15710Sstevel@tonic-gate static void
15720Sstevel@tonic-gate channel_handler_init_20(void)
15730Sstevel@tonic-gate {
15740Sstevel@tonic-gate 	channel_pre[SSH_CHANNEL_OPEN] =			&channel_pre_open;
15750Sstevel@tonic-gate 	channel_pre[SSH_CHANNEL_X11_OPEN] =		&channel_pre_x11_open;
15760Sstevel@tonic-gate 	channel_pre[SSH_CHANNEL_PORT_LISTENER] =	&channel_pre_listener;
15770Sstevel@tonic-gate 	channel_pre[SSH_CHANNEL_RPORT_LISTENER] =	&channel_pre_listener;
15780Sstevel@tonic-gate 	channel_pre[SSH_CHANNEL_X11_LISTENER] =		&channel_pre_listener;
15790Sstevel@tonic-gate 	channel_pre[SSH_CHANNEL_AUTH_SOCKET] =		&channel_pre_listener;
15800Sstevel@tonic-gate 	channel_pre[SSH_CHANNEL_CONNECTING] =		&channel_pre_connecting;
15810Sstevel@tonic-gate 	channel_pre[SSH_CHANNEL_DYNAMIC] =		&channel_pre_dynamic;
15820Sstevel@tonic-gate 
15830Sstevel@tonic-gate 	channel_post[SSH_CHANNEL_OPEN] =		&channel_post_open;
15840Sstevel@tonic-gate 	channel_post[SSH_CHANNEL_PORT_LISTENER] =	&channel_post_port_listener;
15850Sstevel@tonic-gate 	channel_post[SSH_CHANNEL_RPORT_LISTENER] =	&channel_post_port_listener;
15860Sstevel@tonic-gate 	channel_post[SSH_CHANNEL_X11_LISTENER] =	&channel_post_x11_listener;
15870Sstevel@tonic-gate 	channel_post[SSH_CHANNEL_AUTH_SOCKET] =		&channel_post_auth_listener;
15880Sstevel@tonic-gate 	channel_post[SSH_CHANNEL_CONNECTING] =		&channel_post_connecting;
15890Sstevel@tonic-gate 	channel_post[SSH_CHANNEL_DYNAMIC] =		&channel_post_open;
15900Sstevel@tonic-gate }
15910Sstevel@tonic-gate 
15920Sstevel@tonic-gate static void
15930Sstevel@tonic-gate channel_handler_init_13(void)
15940Sstevel@tonic-gate {
15950Sstevel@tonic-gate 	channel_pre[SSH_CHANNEL_OPEN] =			&channel_pre_open_13;
15960Sstevel@tonic-gate 	channel_pre[SSH_CHANNEL_X11_OPEN] =		&channel_pre_x11_open_13;
15970Sstevel@tonic-gate 	channel_pre[SSH_CHANNEL_X11_LISTENER] =		&channel_pre_listener;
15980Sstevel@tonic-gate 	channel_pre[SSH_CHANNEL_PORT_LISTENER] =	&channel_pre_listener;
15990Sstevel@tonic-gate 	channel_pre[SSH_CHANNEL_AUTH_SOCKET] =		&channel_pre_listener;
16000Sstevel@tonic-gate 	channel_pre[SSH_CHANNEL_INPUT_DRAINING] =	&channel_pre_input_draining;
16010Sstevel@tonic-gate 	channel_pre[SSH_CHANNEL_OUTPUT_DRAINING] =	&channel_pre_output_draining;
16020Sstevel@tonic-gate 	channel_pre[SSH_CHANNEL_CONNECTING] =		&channel_pre_connecting;
16030Sstevel@tonic-gate 	channel_pre[SSH_CHANNEL_DYNAMIC] =		&channel_pre_dynamic;
16040Sstevel@tonic-gate 
16050Sstevel@tonic-gate 	channel_post[SSH_CHANNEL_OPEN] =		&channel_post_open;
16060Sstevel@tonic-gate 	channel_post[SSH_CHANNEL_X11_LISTENER] =	&channel_post_x11_listener;
16070Sstevel@tonic-gate 	channel_post[SSH_CHANNEL_PORT_LISTENER] =	&channel_post_port_listener;
16080Sstevel@tonic-gate 	channel_post[SSH_CHANNEL_AUTH_SOCKET] =		&channel_post_auth_listener;
16090Sstevel@tonic-gate 	channel_post[SSH_CHANNEL_OUTPUT_DRAINING] =	&channel_post_output_drain_13;
16100Sstevel@tonic-gate 	channel_post[SSH_CHANNEL_CONNECTING] =		&channel_post_connecting;
16110Sstevel@tonic-gate 	channel_post[SSH_CHANNEL_DYNAMIC] =		&channel_post_open;
16120Sstevel@tonic-gate }
16130Sstevel@tonic-gate 
16140Sstevel@tonic-gate static void
16150Sstevel@tonic-gate channel_handler_init_15(void)
16160Sstevel@tonic-gate {
16170Sstevel@tonic-gate 	channel_pre[SSH_CHANNEL_OPEN] =			&channel_pre_open;
16180Sstevel@tonic-gate 	channel_pre[SSH_CHANNEL_X11_OPEN] =		&channel_pre_x11_open;
16190Sstevel@tonic-gate 	channel_pre[SSH_CHANNEL_X11_LISTENER] =		&channel_pre_listener;
16200Sstevel@tonic-gate 	channel_pre[SSH_CHANNEL_PORT_LISTENER] =	&channel_pre_listener;
16210Sstevel@tonic-gate 	channel_pre[SSH_CHANNEL_AUTH_SOCKET] =		&channel_pre_listener;
16220Sstevel@tonic-gate 	channel_pre[SSH_CHANNEL_CONNECTING] =		&channel_pre_connecting;
16230Sstevel@tonic-gate 	channel_pre[SSH_CHANNEL_DYNAMIC] =		&channel_pre_dynamic;
16240Sstevel@tonic-gate 
16250Sstevel@tonic-gate 	channel_post[SSH_CHANNEL_X11_LISTENER] =	&channel_post_x11_listener;
16260Sstevel@tonic-gate 	channel_post[SSH_CHANNEL_PORT_LISTENER] =	&channel_post_port_listener;
16270Sstevel@tonic-gate 	channel_post[SSH_CHANNEL_AUTH_SOCKET] =		&channel_post_auth_listener;
16280Sstevel@tonic-gate 	channel_post[SSH_CHANNEL_OPEN] =		&channel_post_open;
16290Sstevel@tonic-gate 	channel_post[SSH_CHANNEL_CONNECTING] =		&channel_post_connecting;
16300Sstevel@tonic-gate 	channel_post[SSH_CHANNEL_DYNAMIC] =		&channel_post_open;
16310Sstevel@tonic-gate }
16320Sstevel@tonic-gate 
16330Sstevel@tonic-gate static void
16340Sstevel@tonic-gate channel_handler_init(void)
16350Sstevel@tonic-gate {
16360Sstevel@tonic-gate 	int i;
16370Sstevel@tonic-gate 
16380Sstevel@tonic-gate 	for (i = 0; i < SSH_CHANNEL_MAX_TYPE; i++) {
16390Sstevel@tonic-gate 		channel_pre[i] = NULL;
16400Sstevel@tonic-gate 		channel_post[i] = NULL;
16410Sstevel@tonic-gate 	}
16420Sstevel@tonic-gate 	if (compat20)
16430Sstevel@tonic-gate 		channel_handler_init_20();
16440Sstevel@tonic-gate 	else if (compat13)
16450Sstevel@tonic-gate 		channel_handler_init_13();
16460Sstevel@tonic-gate 	else
16470Sstevel@tonic-gate 		channel_handler_init_15();
16480Sstevel@tonic-gate }
16490Sstevel@tonic-gate 
16500Sstevel@tonic-gate /* gc dead channels */
16510Sstevel@tonic-gate static void
16520Sstevel@tonic-gate channel_garbage_collect(Channel *c)
16530Sstevel@tonic-gate {
16540Sstevel@tonic-gate 	if (c == NULL)
16550Sstevel@tonic-gate 		return;
16560Sstevel@tonic-gate 	if (c->detach_user != NULL) {
16570Sstevel@tonic-gate 		if (!chan_is_dead(c, 0))
16580Sstevel@tonic-gate 			return;
16590Sstevel@tonic-gate 		debug("channel %d: gc: notify user", c->self);
16600Sstevel@tonic-gate 		c->detach_user(c->self, NULL);
16610Sstevel@tonic-gate 		/* if we still have a callback */
16620Sstevel@tonic-gate 		if (c->detach_user != NULL)
16630Sstevel@tonic-gate 			return;
16640Sstevel@tonic-gate 		debug("channel %d: gc: user detached", c->self);
16650Sstevel@tonic-gate 	}
16660Sstevel@tonic-gate 	if (!c->wait_for_exit && !chan_is_dead(c, 1))
16670Sstevel@tonic-gate 		return;
16680Sstevel@tonic-gate 	debug("channel %d: garbage collecting", c->self);
16690Sstevel@tonic-gate 	channel_free(c);
16700Sstevel@tonic-gate }
16710Sstevel@tonic-gate 
16720Sstevel@tonic-gate static void
16730Sstevel@tonic-gate channel_handler(chan_fn *ftab[], fd_set * readset, fd_set * writeset)
16740Sstevel@tonic-gate {
16750Sstevel@tonic-gate 	static int did_init = 0;
16760Sstevel@tonic-gate 	int i;
16770Sstevel@tonic-gate 	Channel *c;
16780Sstevel@tonic-gate 
16790Sstevel@tonic-gate 	if (!did_init) {
16800Sstevel@tonic-gate 		channel_handler_init();
16810Sstevel@tonic-gate 		did_init = 1;
16820Sstevel@tonic-gate 	}
16830Sstevel@tonic-gate 	for (i = 0; i < channels_alloc; i++) {
16840Sstevel@tonic-gate 		c = channels[i];
16850Sstevel@tonic-gate 		if (c == NULL)
16860Sstevel@tonic-gate 			continue;
16870Sstevel@tonic-gate 		if (ftab[c->type] != NULL)
16880Sstevel@tonic-gate 			(*ftab[c->type])(c, readset, writeset);
16890Sstevel@tonic-gate 		channel_garbage_collect(c);
16900Sstevel@tonic-gate 	}
16910Sstevel@tonic-gate }
16920Sstevel@tonic-gate 
16930Sstevel@tonic-gate /*
16940Sstevel@tonic-gate  * Allocate/update select bitmasks and add any bits relevant to channels in
16950Sstevel@tonic-gate  * select bitmasks.
16960Sstevel@tonic-gate  */
16970Sstevel@tonic-gate void
16980Sstevel@tonic-gate channel_prepare_select(fd_set **readsetp, fd_set **writesetp, int *maxfdp,
16990Sstevel@tonic-gate     int *nallocp, int rekeying)
17000Sstevel@tonic-gate {
17010Sstevel@tonic-gate 	int n;
17020Sstevel@tonic-gate 	u_int sz;
17030Sstevel@tonic-gate 
17040Sstevel@tonic-gate 	n = MAX(*maxfdp, channel_max_fd);
17050Sstevel@tonic-gate 
17060Sstevel@tonic-gate 	sz = howmany(n+1, NFDBITS) * sizeof(fd_mask);
17070Sstevel@tonic-gate 	/* perhaps check sz < nalloc/2 and shrink? */
17080Sstevel@tonic-gate 	if (*readsetp == NULL || sz > *nallocp) {
17090Sstevel@tonic-gate 		*readsetp = xrealloc(*readsetp, sz);
17100Sstevel@tonic-gate 		*writesetp = xrealloc(*writesetp, sz);
17110Sstevel@tonic-gate 		*nallocp = sz;
17120Sstevel@tonic-gate 	}
17130Sstevel@tonic-gate 	*maxfdp = n;
17140Sstevel@tonic-gate 	memset(*readsetp, 0, sz);
17150Sstevel@tonic-gate 	memset(*writesetp, 0, sz);
17160Sstevel@tonic-gate 
17170Sstevel@tonic-gate 	if (!rekeying)
17180Sstevel@tonic-gate 		channel_handler(channel_pre, *readsetp, *writesetp);
17190Sstevel@tonic-gate }
17200Sstevel@tonic-gate 
17210Sstevel@tonic-gate /*
17220Sstevel@tonic-gate  * After select, perform any appropriate operations for channels which have
17230Sstevel@tonic-gate  * events pending.
17240Sstevel@tonic-gate  */
17250Sstevel@tonic-gate void
17260Sstevel@tonic-gate channel_after_select(fd_set * readset, fd_set * writeset)
17270Sstevel@tonic-gate {
17280Sstevel@tonic-gate 	channel_handler(channel_post, readset, writeset);
17290Sstevel@tonic-gate }
17300Sstevel@tonic-gate 
17310Sstevel@tonic-gate 
17320Sstevel@tonic-gate /* If there is data to send to the connection, enqueue some of it now. */
17330Sstevel@tonic-gate 
17340Sstevel@tonic-gate void
17350Sstevel@tonic-gate channel_output_poll(void)
17360Sstevel@tonic-gate {
17370Sstevel@tonic-gate 	Channel *c;
17380Sstevel@tonic-gate 	int i;
17390Sstevel@tonic-gate 	u_int len;
17400Sstevel@tonic-gate 
17410Sstevel@tonic-gate 	for (i = 0; i < channels_alloc; i++) {
17420Sstevel@tonic-gate 		c = channels[i];
17430Sstevel@tonic-gate 		if (c == NULL)
17440Sstevel@tonic-gate 			continue;
17450Sstevel@tonic-gate 
17460Sstevel@tonic-gate 		/*
17470Sstevel@tonic-gate 		 * We are only interested in channels that can have buffered
17480Sstevel@tonic-gate 		 * incoming data.
17490Sstevel@tonic-gate 		 */
17500Sstevel@tonic-gate 		if (compat13) {
17510Sstevel@tonic-gate 			if (c->type != SSH_CHANNEL_OPEN &&
17520Sstevel@tonic-gate 			    c->type != SSH_CHANNEL_INPUT_DRAINING)
17530Sstevel@tonic-gate 				continue;
17540Sstevel@tonic-gate 		} else {
17550Sstevel@tonic-gate 			if (c->type != SSH_CHANNEL_OPEN)
17560Sstevel@tonic-gate 				continue;
17570Sstevel@tonic-gate 		}
17580Sstevel@tonic-gate 		if (compat20 &&
17590Sstevel@tonic-gate 		    (c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD))) {
17600Sstevel@tonic-gate 			/* XXX is this true? */
17610Sstevel@tonic-gate 			debug3("channel %d: will not send data after close", c->self);
17620Sstevel@tonic-gate 			continue;
17630Sstevel@tonic-gate 		}
17640Sstevel@tonic-gate 
17650Sstevel@tonic-gate 		/* Get the amount of buffered data for this channel. */
17660Sstevel@tonic-gate 		if ((c->istate == CHAN_INPUT_OPEN ||
17670Sstevel@tonic-gate 		    c->istate == CHAN_INPUT_WAIT_DRAIN) &&
17680Sstevel@tonic-gate 		    (len = buffer_len(&c->input)) > 0) {
17690Sstevel@tonic-gate 			/*
17700Sstevel@tonic-gate 			 * Send some data for the other side over the secure
17710Sstevel@tonic-gate 			 * connection.
17720Sstevel@tonic-gate 			 */
17730Sstevel@tonic-gate 			if (compat20) {
17740Sstevel@tonic-gate 				if (len > c->remote_window)
17750Sstevel@tonic-gate 					len = c->remote_window;
17760Sstevel@tonic-gate 				if (len > c->remote_maxpacket)
17770Sstevel@tonic-gate 					len = c->remote_maxpacket;
17780Sstevel@tonic-gate 			} else {
17790Sstevel@tonic-gate 				if (packet_is_interactive()) {
17800Sstevel@tonic-gate 					if (len > 1024)
17810Sstevel@tonic-gate 						len = 512;
17820Sstevel@tonic-gate 				} else {
17830Sstevel@tonic-gate 					/* Keep the packets at reasonable size. */
17840Sstevel@tonic-gate 					if (len > packet_get_maxsize()/2)
17850Sstevel@tonic-gate 						len = packet_get_maxsize()/2;
17860Sstevel@tonic-gate 				}
17870Sstevel@tonic-gate 			}
17880Sstevel@tonic-gate 			if (len > 0) {
17890Sstevel@tonic-gate 				packet_start(compat20 ?
17900Sstevel@tonic-gate 				    SSH2_MSG_CHANNEL_DATA : SSH_MSG_CHANNEL_DATA);
17910Sstevel@tonic-gate 				packet_put_int(c->remote_id);
17920Sstevel@tonic-gate 				packet_put_string(buffer_ptr(&c->input), len);
17930Sstevel@tonic-gate 				packet_send();
17940Sstevel@tonic-gate 				buffer_consume(&c->input, len);
17950Sstevel@tonic-gate 				c->remote_window -= len;
17960Sstevel@tonic-gate 			}
17970Sstevel@tonic-gate 		} else if (c->istate == CHAN_INPUT_WAIT_DRAIN) {
17980Sstevel@tonic-gate 			if (compat13)
17990Sstevel@tonic-gate 				fatal("cannot happen: istate == INPUT_WAIT_DRAIN for proto 1.3");
18000Sstevel@tonic-gate 			/*
18010Sstevel@tonic-gate 			 * input-buffer is empty and read-socket shutdown:
18020Sstevel@tonic-gate 			 * tell peer, that we will not send more data: send IEOF.
18030Sstevel@tonic-gate 			 * hack for extended data: delay EOF if EFD still in use.
18040Sstevel@tonic-gate 			 */
18050Sstevel@tonic-gate 			if (CHANNEL_EFD_INPUT_ACTIVE(c))
18060Sstevel@tonic-gate 			       debug2("channel %d: ibuf_empty delayed efd %d/(%d)",
18070Sstevel@tonic-gate 				   c->self, c->efd, buffer_len(&c->extended));
18080Sstevel@tonic-gate 			else
18090Sstevel@tonic-gate 				chan_ibuf_empty(c);
18100Sstevel@tonic-gate 		}
18110Sstevel@tonic-gate 		/* Send extended data, i.e. stderr */
18120Sstevel@tonic-gate 		if (compat20 &&
18130Sstevel@tonic-gate 		    !(c->flags & CHAN_EOF_SENT) &&
18140Sstevel@tonic-gate 		    c->remote_window > 0 &&
18150Sstevel@tonic-gate 		    (len = buffer_len(&c->extended)) > 0 &&
18160Sstevel@tonic-gate 		    c->extended_usage == CHAN_EXTENDED_READ) {
18170Sstevel@tonic-gate 			debug2("channel %d: rwin %u elen %u euse %d",
18180Sstevel@tonic-gate 			    c->self, c->remote_window, buffer_len(&c->extended),
18190Sstevel@tonic-gate 			    c->extended_usage);
18200Sstevel@tonic-gate 			if (len > c->remote_window)
18210Sstevel@tonic-gate 				len = c->remote_window;
18220Sstevel@tonic-gate 			if (len > c->remote_maxpacket)
18230Sstevel@tonic-gate 				len = c->remote_maxpacket;
18240Sstevel@tonic-gate 			packet_start(SSH2_MSG_CHANNEL_EXTENDED_DATA);
18250Sstevel@tonic-gate 			packet_put_int(c->remote_id);
18260Sstevel@tonic-gate 			packet_put_int(SSH2_EXTENDED_DATA_STDERR);
18270Sstevel@tonic-gate 			packet_put_string(buffer_ptr(&c->extended), len);
18280Sstevel@tonic-gate 			packet_send();
18290Sstevel@tonic-gate 			buffer_consume(&c->extended, len);
18300Sstevel@tonic-gate 			c->remote_window -= len;
18310Sstevel@tonic-gate 			debug2("channel %d: sent ext data %d", c->self, len);
18320Sstevel@tonic-gate 		}
18330Sstevel@tonic-gate 	}
18340Sstevel@tonic-gate }
18350Sstevel@tonic-gate 
18360Sstevel@tonic-gate 
18370Sstevel@tonic-gate /* -- protocol input */
18380Sstevel@tonic-gate 
18390Sstevel@tonic-gate void
18400Sstevel@tonic-gate channel_input_data(int type, u_int32_t seq, void *ctxt)
18410Sstevel@tonic-gate {
18420Sstevel@tonic-gate 	int id;
18430Sstevel@tonic-gate 	char *data;
18440Sstevel@tonic-gate 	u_int data_len;
18450Sstevel@tonic-gate 	Channel *c;
18460Sstevel@tonic-gate 
18470Sstevel@tonic-gate 	/* Get the channel number and verify it. */
18480Sstevel@tonic-gate 	id = packet_get_int();
18490Sstevel@tonic-gate 	c = channel_lookup(id);
18500Sstevel@tonic-gate 	if (c == NULL)
18510Sstevel@tonic-gate 		packet_disconnect("Received data for nonexistent channel %d.", id);
18520Sstevel@tonic-gate 
18530Sstevel@tonic-gate 	/* Ignore any data for non-open channels (might happen on close) */
18540Sstevel@tonic-gate 	if (c->type != SSH_CHANNEL_OPEN &&
18550Sstevel@tonic-gate 	    c->type != SSH_CHANNEL_X11_OPEN)
18560Sstevel@tonic-gate 		return;
18570Sstevel@tonic-gate 
18580Sstevel@tonic-gate 	/* same for protocol 1.5 if output end is no longer open */
18590Sstevel@tonic-gate 	if (!compat13 && c->ostate != CHAN_OUTPUT_OPEN)
18600Sstevel@tonic-gate 		return;
18610Sstevel@tonic-gate 
18620Sstevel@tonic-gate 	/* Get the data. */
18630Sstevel@tonic-gate 	data = packet_get_string(&data_len);
18640Sstevel@tonic-gate 
18650Sstevel@tonic-gate 	if (compat20) {
18660Sstevel@tonic-gate 		if (data_len > c->local_maxpacket) {
18670Sstevel@tonic-gate 			log("channel %d: rcvd big packet %d, maxpack %d",
18680Sstevel@tonic-gate 			    c->self, data_len, c->local_maxpacket);
18690Sstevel@tonic-gate 		}
18700Sstevel@tonic-gate 		if (data_len > c->local_window) {
18710Sstevel@tonic-gate 			log("channel %d: rcvd too much data %d, win %d",
18720Sstevel@tonic-gate 			    c->self, data_len, c->local_window);
18730Sstevel@tonic-gate 			xfree(data);
18740Sstevel@tonic-gate 			return;
18750Sstevel@tonic-gate 		}
18760Sstevel@tonic-gate 		c->local_window -= data_len;
18770Sstevel@tonic-gate 	}
18780Sstevel@tonic-gate 	packet_check_eom();
18790Sstevel@tonic-gate 	buffer_append(&c->output, data, data_len);
18800Sstevel@tonic-gate 	xfree(data);
18810Sstevel@tonic-gate }
18820Sstevel@tonic-gate 
18830Sstevel@tonic-gate void
18840Sstevel@tonic-gate channel_input_extended_data(int type, u_int32_t seq, void *ctxt)
18850Sstevel@tonic-gate {
18860Sstevel@tonic-gate 	int id;
18870Sstevel@tonic-gate 	char *data;
18880Sstevel@tonic-gate 	u_int data_len, tcode;
18890Sstevel@tonic-gate 	Channel *c;
18900Sstevel@tonic-gate 
18910Sstevel@tonic-gate 	/* Get the channel number and verify it. */
18920Sstevel@tonic-gate 	id = packet_get_int();
18930Sstevel@tonic-gate 	c = channel_lookup(id);
18940Sstevel@tonic-gate 
18950Sstevel@tonic-gate 	if (c == NULL)
18960Sstevel@tonic-gate 		packet_disconnect("Received extended_data for bad channel %d.", id);
18970Sstevel@tonic-gate 	if (c->type != SSH_CHANNEL_OPEN) {
18980Sstevel@tonic-gate 		log("channel %d: ext data for non open", id);
18990Sstevel@tonic-gate 		return;
19000Sstevel@tonic-gate 	}
19010Sstevel@tonic-gate 	if (c->flags & CHAN_EOF_RCVD) {
19020Sstevel@tonic-gate 		if (datafellows & SSH_BUG_EXTEOF)
19030Sstevel@tonic-gate 			debug("channel %d: accepting ext data after eof", id);
19040Sstevel@tonic-gate 		else
19050Sstevel@tonic-gate 			packet_disconnect("Received extended_data after EOF "
19060Sstevel@tonic-gate 			    "on channel %d.", id);
19070Sstevel@tonic-gate 	}
19080Sstevel@tonic-gate 	tcode = packet_get_int();
19090Sstevel@tonic-gate 	if (c->efd == -1 ||
19100Sstevel@tonic-gate 	    c->extended_usage != CHAN_EXTENDED_WRITE ||
19110Sstevel@tonic-gate 	    tcode != SSH2_EXTENDED_DATA_STDERR) {
19120Sstevel@tonic-gate 		log("channel %d: bad ext data", c->self);
19130Sstevel@tonic-gate 		return;
19140Sstevel@tonic-gate 	}
19150Sstevel@tonic-gate 	data = packet_get_string(&data_len);
19160Sstevel@tonic-gate 	packet_check_eom();
19170Sstevel@tonic-gate 	if (data_len > c->local_window) {
19180Sstevel@tonic-gate 		log("channel %d: rcvd too much extended_data %d, win %d",
19190Sstevel@tonic-gate 		    c->self, data_len, c->local_window);
19200Sstevel@tonic-gate 		xfree(data);
19210Sstevel@tonic-gate 		return;
19220Sstevel@tonic-gate 	}
19230Sstevel@tonic-gate 	debug2("channel %d: rcvd ext data %d", c->self, data_len);
19240Sstevel@tonic-gate 	c->local_window -= data_len;
19250Sstevel@tonic-gate 	buffer_append(&c->extended, data, data_len);
19260Sstevel@tonic-gate 	xfree(data);
19270Sstevel@tonic-gate }
19280Sstevel@tonic-gate 
19290Sstevel@tonic-gate void
19300Sstevel@tonic-gate channel_input_ieof(int type, u_int32_t seq, void *ctxt)
19310Sstevel@tonic-gate {
19320Sstevel@tonic-gate 	int id;
19330Sstevel@tonic-gate 	Channel *c;
19340Sstevel@tonic-gate 
19350Sstevel@tonic-gate 	id = packet_get_int();
19360Sstevel@tonic-gate 	packet_check_eom();
19370Sstevel@tonic-gate 	c = channel_lookup(id);
19380Sstevel@tonic-gate 	if (c == NULL)
19390Sstevel@tonic-gate 		packet_disconnect("Received ieof for nonexistent channel %d.", id);
19400Sstevel@tonic-gate 	chan_rcvd_ieof(c);
19410Sstevel@tonic-gate 
19420Sstevel@tonic-gate 	/* XXX force input close */
19430Sstevel@tonic-gate 	if (c->force_drain && c->istate == CHAN_INPUT_OPEN) {
19440Sstevel@tonic-gate 		debug("channel %d: FORCE input drain", c->self);
19450Sstevel@tonic-gate 		c->istate = CHAN_INPUT_WAIT_DRAIN;
19460Sstevel@tonic-gate 		if (buffer_len(&c->input) == 0)
19470Sstevel@tonic-gate 			chan_ibuf_empty(c);
19480Sstevel@tonic-gate 	}
19490Sstevel@tonic-gate 
19500Sstevel@tonic-gate }
19510Sstevel@tonic-gate 
19520Sstevel@tonic-gate void
19530Sstevel@tonic-gate channel_input_close(int type, u_int32_t seq, void *ctxt)
19540Sstevel@tonic-gate {
19550Sstevel@tonic-gate 	int id;
19560Sstevel@tonic-gate 	Channel *c;
19570Sstevel@tonic-gate 
19580Sstevel@tonic-gate 	id = packet_get_int();
19590Sstevel@tonic-gate 	packet_check_eom();
19600Sstevel@tonic-gate 	c = channel_lookup(id);
19610Sstevel@tonic-gate 	if (c == NULL)
19620Sstevel@tonic-gate 		packet_disconnect("Received close for nonexistent channel %d.", id);
19630Sstevel@tonic-gate 
19640Sstevel@tonic-gate 	/*
19650Sstevel@tonic-gate 	 * Send a confirmation that we have closed the channel and no more
19660Sstevel@tonic-gate 	 * data is coming for it.
19670Sstevel@tonic-gate 	 */
19680Sstevel@tonic-gate 	packet_start(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION);
19690Sstevel@tonic-gate 	packet_put_int(c->remote_id);
19700Sstevel@tonic-gate 	packet_send();
19710Sstevel@tonic-gate 
19720Sstevel@tonic-gate 	/*
19730Sstevel@tonic-gate 	 * If the channel is in closed state, we have sent a close request,
19740Sstevel@tonic-gate 	 * and the other side will eventually respond with a confirmation.
19750Sstevel@tonic-gate 	 * Thus, we cannot free the channel here, because then there would be
19760Sstevel@tonic-gate 	 * no-one to receive the confirmation.  The channel gets freed when
19770Sstevel@tonic-gate 	 * the confirmation arrives.
19780Sstevel@tonic-gate 	 */
19790Sstevel@tonic-gate 	if (c->type != SSH_CHANNEL_CLOSED) {
19800Sstevel@tonic-gate 		/*
19810Sstevel@tonic-gate 		 * Not a closed channel - mark it as draining, which will
19820Sstevel@tonic-gate 		 * cause it to be freed later.
19830Sstevel@tonic-gate 		 */
19840Sstevel@tonic-gate 		buffer_clear(&c->input);
19850Sstevel@tonic-gate 		c->type = SSH_CHANNEL_OUTPUT_DRAINING;
19860Sstevel@tonic-gate 	}
19870Sstevel@tonic-gate }
19880Sstevel@tonic-gate 
19890Sstevel@tonic-gate /* proto version 1.5 overloads CLOSE_CONFIRMATION with OCLOSE */
19900Sstevel@tonic-gate void
19910Sstevel@tonic-gate channel_input_oclose(int type, u_int32_t seq, void *ctxt)
19920Sstevel@tonic-gate {
19930Sstevel@tonic-gate 	int id = packet_get_int();
19940Sstevel@tonic-gate 	Channel *c = channel_lookup(id);
19950Sstevel@tonic-gate 
19960Sstevel@tonic-gate 	packet_check_eom();
19970Sstevel@tonic-gate 	if (c == NULL)
19980Sstevel@tonic-gate 		packet_disconnect("Received oclose for nonexistent channel %d.", id);
19990Sstevel@tonic-gate 	chan_rcvd_oclose(c);
20000Sstevel@tonic-gate }
20010Sstevel@tonic-gate 
20020Sstevel@tonic-gate void
20030Sstevel@tonic-gate channel_input_close_confirmation(int type, u_int32_t seq, void *ctxt)
20040Sstevel@tonic-gate {
20050Sstevel@tonic-gate 	int id = packet_get_int();
20060Sstevel@tonic-gate 	Channel *c = channel_lookup(id);
20070Sstevel@tonic-gate 
20080Sstevel@tonic-gate 	packet_check_eom();
20090Sstevel@tonic-gate 	if (c == NULL)
20100Sstevel@tonic-gate 		packet_disconnect("Received close confirmation for "
20110Sstevel@tonic-gate 		    "out-of-range channel %d.", id);
20120Sstevel@tonic-gate 	if (c->type != SSH_CHANNEL_CLOSED)
20130Sstevel@tonic-gate 		packet_disconnect("Received close confirmation for "
20140Sstevel@tonic-gate 		    "non-closed channel %d (type %d).", id, c->type);
20150Sstevel@tonic-gate 	channel_free(c);
20160Sstevel@tonic-gate }
20170Sstevel@tonic-gate 
20180Sstevel@tonic-gate void
20190Sstevel@tonic-gate channel_input_open_confirmation(int type, u_int32_t seq, void *ctxt)
20200Sstevel@tonic-gate {
20210Sstevel@tonic-gate 	int id, remote_id;
20220Sstevel@tonic-gate 	Channel *c;
20230Sstevel@tonic-gate 
20240Sstevel@tonic-gate 	id = packet_get_int();
20250Sstevel@tonic-gate 	c = channel_lookup(id);
20260Sstevel@tonic-gate 
20270Sstevel@tonic-gate 	if (c==NULL || c->type != SSH_CHANNEL_OPENING)
20280Sstevel@tonic-gate 		packet_disconnect("Received open confirmation for "
20290Sstevel@tonic-gate 		    "non-opening channel %d.", id);
20300Sstevel@tonic-gate 	remote_id = packet_get_int();
20310Sstevel@tonic-gate 	/* Record the remote channel number and mark that the channel is now open. */
20320Sstevel@tonic-gate 	c->remote_id = remote_id;
20330Sstevel@tonic-gate 	c->type = SSH_CHANNEL_OPEN;
20340Sstevel@tonic-gate 
20350Sstevel@tonic-gate 	if (compat20) {
20360Sstevel@tonic-gate 		c->remote_window = packet_get_int();
20370Sstevel@tonic-gate 		c->remote_maxpacket = packet_get_int();
20380Sstevel@tonic-gate 		if (c->confirm) {
20390Sstevel@tonic-gate 			debug2("callback start");
20400Sstevel@tonic-gate 			c->confirm(c->self, NULL);
20410Sstevel@tonic-gate 			debug2("callback done");
20420Sstevel@tonic-gate 		}
20430Sstevel@tonic-gate 		debug("channel %d: open confirm rwindow %u rmax %u", c->self,
20440Sstevel@tonic-gate 		    c->remote_window, c->remote_maxpacket);
20450Sstevel@tonic-gate 	}
20460Sstevel@tonic-gate 	packet_check_eom();
20470Sstevel@tonic-gate }
20480Sstevel@tonic-gate 
20490Sstevel@tonic-gate static char *
20500Sstevel@tonic-gate reason2txt(int reason)
20510Sstevel@tonic-gate {
20520Sstevel@tonic-gate 	switch (reason) {
20530Sstevel@tonic-gate 	case SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED:
20540Sstevel@tonic-gate 		return "administratively prohibited";
20550Sstevel@tonic-gate 	case SSH2_OPEN_CONNECT_FAILED:
20560Sstevel@tonic-gate 		return "connect failed";
20570Sstevel@tonic-gate 	case SSH2_OPEN_UNKNOWN_CHANNEL_TYPE:
20580Sstevel@tonic-gate 		return "unknown channel type";
20590Sstevel@tonic-gate 	case SSH2_OPEN_RESOURCE_SHORTAGE:
20600Sstevel@tonic-gate 		return "resource shortage";
20610Sstevel@tonic-gate 	}
20620Sstevel@tonic-gate 	return "unknown reason";
20630Sstevel@tonic-gate }
20640Sstevel@tonic-gate 
20650Sstevel@tonic-gate void
20660Sstevel@tonic-gate channel_input_open_failure(int type, u_int32_t seq, void *ctxt)
20670Sstevel@tonic-gate {
20680Sstevel@tonic-gate 	int id, reason;
20690Sstevel@tonic-gate 	char *msg = NULL, *lang = NULL;
20700Sstevel@tonic-gate 	Channel *c;
20710Sstevel@tonic-gate 
20720Sstevel@tonic-gate 	id = packet_get_int();
20730Sstevel@tonic-gate 	c = channel_lookup(id);
20740Sstevel@tonic-gate 
20750Sstevel@tonic-gate 	if (c==NULL || c->type != SSH_CHANNEL_OPENING)
20760Sstevel@tonic-gate 		packet_disconnect("Received open failure for "
20770Sstevel@tonic-gate 		    "non-opening channel %d.", id);
20780Sstevel@tonic-gate 	if (compat20) {
20790Sstevel@tonic-gate 		reason = packet_get_int();
20800Sstevel@tonic-gate 		if (!(datafellows & SSH_BUG_OPENFAILURE)) {
20810Sstevel@tonic-gate 			msg  = packet_get_string(NULL);
20820Sstevel@tonic-gate 			lang = packet_get_string(NULL);
20830Sstevel@tonic-gate 		}
20840Sstevel@tonic-gate 		log("channel %d: open failed: %s%s%s", id,
20850Sstevel@tonic-gate 		    reason2txt(reason), msg ? ": ": "", msg ? msg : "");
20860Sstevel@tonic-gate 		if (msg != NULL)
20870Sstevel@tonic-gate 			xfree(msg);
20880Sstevel@tonic-gate 		if (lang != NULL)
20890Sstevel@tonic-gate 			xfree(lang);
20900Sstevel@tonic-gate 	}
20910Sstevel@tonic-gate 	packet_check_eom();
20920Sstevel@tonic-gate 	/* Free the channel.  This will also close the socket. */
20930Sstevel@tonic-gate 	channel_free(c);
20940Sstevel@tonic-gate }
20950Sstevel@tonic-gate 
20960Sstevel@tonic-gate void
20970Sstevel@tonic-gate channel_input_window_adjust(int type, u_int32_t seq, void *ctxt)
20980Sstevel@tonic-gate {
20990Sstevel@tonic-gate 	Channel *c;
21000Sstevel@tonic-gate 	int id;
21010Sstevel@tonic-gate 	u_int adjust;
21020Sstevel@tonic-gate 
21030Sstevel@tonic-gate 	if (!compat20)
21040Sstevel@tonic-gate 		return;
21050Sstevel@tonic-gate 
21060Sstevel@tonic-gate 	/* Get the channel number and verify it. */
21070Sstevel@tonic-gate 	id = packet_get_int();
21080Sstevel@tonic-gate 	c = channel_lookup(id);
21090Sstevel@tonic-gate 
21100Sstevel@tonic-gate 	if (c == NULL || c->type != SSH_CHANNEL_OPEN) {
21110Sstevel@tonic-gate 		log("Received window adjust for "
21120Sstevel@tonic-gate 		    "non-open channel %d.", id);
21130Sstevel@tonic-gate 		return;
21140Sstevel@tonic-gate 	}
21150Sstevel@tonic-gate 	adjust = packet_get_int();
21160Sstevel@tonic-gate 	packet_check_eom();
21170Sstevel@tonic-gate 	debug2("channel %d: rcvd adjust %u", id, adjust);
21180Sstevel@tonic-gate 	c->remote_window += adjust;
21190Sstevel@tonic-gate }
21200Sstevel@tonic-gate 
21210Sstevel@tonic-gate void
21220Sstevel@tonic-gate channel_input_port_open(int type, u_int32_t seq, void *ctxt)
21230Sstevel@tonic-gate {
21240Sstevel@tonic-gate 	Channel *c = NULL;
21250Sstevel@tonic-gate 	u_short host_port;
21260Sstevel@tonic-gate 	char *host, *originator_string;
21270Sstevel@tonic-gate 	int remote_id, sock = -1;
21280Sstevel@tonic-gate 
21290Sstevel@tonic-gate 	remote_id = packet_get_int();
21300Sstevel@tonic-gate 	host = packet_get_string(NULL);
21310Sstevel@tonic-gate 	host_port = packet_get_int();
21320Sstevel@tonic-gate 
21330Sstevel@tonic-gate 	if (packet_get_protocol_flags() & SSH_PROTOFLAG_HOST_IN_FWD_OPEN) {
21340Sstevel@tonic-gate 		originator_string = packet_get_string(NULL);
21350Sstevel@tonic-gate 	} else {
21360Sstevel@tonic-gate 		originator_string = xstrdup("unknown (remote did not supply name)");
21370Sstevel@tonic-gate 	}
21380Sstevel@tonic-gate 	packet_check_eom();
21390Sstevel@tonic-gate 	sock = channel_connect_to(host, host_port);
21400Sstevel@tonic-gate 	if (sock != -1) {
21410Sstevel@tonic-gate 		c = channel_new("connected socket",
21420Sstevel@tonic-gate 		    SSH_CHANNEL_CONNECTING, sock, sock, -1, 0, 0, 0,
21430Sstevel@tonic-gate 		    originator_string, 1);
21440Sstevel@tonic-gate 		c->remote_id = remote_id;
21450Sstevel@tonic-gate 	}
21460Sstevel@tonic-gate 	if (c == NULL) {
21470Sstevel@tonic-gate 		packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE);
21480Sstevel@tonic-gate 		packet_put_int(remote_id);
21490Sstevel@tonic-gate 		packet_send();
21500Sstevel@tonic-gate 	}
21510Sstevel@tonic-gate 	xfree(host);
21520Sstevel@tonic-gate }
21530Sstevel@tonic-gate 
21540Sstevel@tonic-gate 
21550Sstevel@tonic-gate /* -- tcp forwarding */
21560Sstevel@tonic-gate 
21570Sstevel@tonic-gate void
21580Sstevel@tonic-gate channel_set_af(int af)
21590Sstevel@tonic-gate {
21600Sstevel@tonic-gate 	IPv4or6 = af;
21610Sstevel@tonic-gate }
21620Sstevel@tonic-gate 
21630Sstevel@tonic-gate static int
21640Sstevel@tonic-gate channel_setup_fwd_listener(int type, const char *listen_addr, u_short listen_port,
21650Sstevel@tonic-gate     const char *host_to_connect, u_short port_to_connect, int gateway_ports)
21660Sstevel@tonic-gate {
21670Sstevel@tonic-gate 	Channel *c;
21680Sstevel@tonic-gate 	int success, sock, on = 1;
21690Sstevel@tonic-gate 	struct addrinfo hints, *ai, *aitop;
21700Sstevel@tonic-gate 	const char *host;
21710Sstevel@tonic-gate 	char ntop[NI_MAXHOST], strport[NI_MAXSERV];
21720Sstevel@tonic-gate 
21730Sstevel@tonic-gate 	success = 0;
21740Sstevel@tonic-gate 	host = (type == SSH_CHANNEL_RPORT_LISTENER) ?
21750Sstevel@tonic-gate 	    listen_addr : host_to_connect;
21760Sstevel@tonic-gate 
21770Sstevel@tonic-gate 	if (host == NULL) {
21780Sstevel@tonic-gate 		error("No forward host name.");
21790Sstevel@tonic-gate 		return success;
21800Sstevel@tonic-gate 	}
21810Sstevel@tonic-gate 	if (strlen(host) > SSH_CHANNEL_PATH_LEN - 1) {
21820Sstevel@tonic-gate 		error("Forward host name too long.");
21830Sstevel@tonic-gate 		return success;
21840Sstevel@tonic-gate 	}
21850Sstevel@tonic-gate 
21860Sstevel@tonic-gate 	/*
21870Sstevel@tonic-gate 	 * getaddrinfo returns a loopback address if the hostname is
21880Sstevel@tonic-gate 	 * set to NULL and hints.ai_flags is not AI_PASSIVE
21890Sstevel@tonic-gate 	 */
21900Sstevel@tonic-gate 	memset(&hints, 0, sizeof(hints));
21910Sstevel@tonic-gate 	hints.ai_family = IPv4or6;
21920Sstevel@tonic-gate 	hints.ai_flags = gateway_ports ? AI_PASSIVE : 0;
21930Sstevel@tonic-gate 	hints.ai_socktype = SOCK_STREAM;
21940Sstevel@tonic-gate 	snprintf(strport, sizeof strport, "%d", listen_port);
21950Sstevel@tonic-gate 	if (getaddrinfo(NULL, strport, &hints, &aitop) != 0)
21960Sstevel@tonic-gate 		packet_disconnect("getaddrinfo: fatal error");
21970Sstevel@tonic-gate 
21980Sstevel@tonic-gate 	for (ai = aitop; ai; ai = ai->ai_next) {
21990Sstevel@tonic-gate 		if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6)
22000Sstevel@tonic-gate 			continue;
22010Sstevel@tonic-gate 		if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop),
22020Sstevel@tonic-gate 		    strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV) != 0) {
22030Sstevel@tonic-gate 			error("channel_setup_fwd_listener: getnameinfo failed");
22040Sstevel@tonic-gate 			continue;
22050Sstevel@tonic-gate 		}
22060Sstevel@tonic-gate 		/* Create a port to listen for the host. */
22070Sstevel@tonic-gate 		sock = socket(ai->ai_family, SOCK_STREAM, 0);
22080Sstevel@tonic-gate 		if (sock < 0) {
22090Sstevel@tonic-gate 			/* this is no error since kernel may not support ipv6 */
22100Sstevel@tonic-gate 			verbose("socket: %.100s", strerror(errno));
22110Sstevel@tonic-gate 			continue;
22120Sstevel@tonic-gate 		}
22130Sstevel@tonic-gate 		/*
22140Sstevel@tonic-gate 		 * Set socket options.
22150Sstevel@tonic-gate 		 * Allow local port reuse in TIME_WAIT.
22160Sstevel@tonic-gate 		 */
22170Sstevel@tonic-gate 		if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on,
22180Sstevel@tonic-gate 		    sizeof(on)) == -1)
22190Sstevel@tonic-gate 			error("setsockopt SO_REUSEADDR: %s", strerror(errno));
22200Sstevel@tonic-gate 
22210Sstevel@tonic-gate 		debug("Local forwarding listening on %s port %s.", ntop, strport);
22220Sstevel@tonic-gate 
22230Sstevel@tonic-gate 		/* Bind the socket to the address. */
22240Sstevel@tonic-gate 		if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) {
22250Sstevel@tonic-gate 			/* address can be in use ipv6 address is already bound */
22260Sstevel@tonic-gate 			if (!ai->ai_next)
22270Sstevel@tonic-gate 				error("bind: %.100s", strerror(errno));
22280Sstevel@tonic-gate 			else
22290Sstevel@tonic-gate 				verbose("bind: %.100s", strerror(errno));
22300Sstevel@tonic-gate 
22310Sstevel@tonic-gate 			close(sock);
22320Sstevel@tonic-gate 			continue;
22330Sstevel@tonic-gate 		}
22340Sstevel@tonic-gate 		/* Start listening for connections on the socket. */
22350Sstevel@tonic-gate 		if (listen(sock, 5) < 0) {
22360Sstevel@tonic-gate 			error("listen: %.100s", strerror(errno));
22370Sstevel@tonic-gate 			close(sock);
22380Sstevel@tonic-gate 			continue;
22390Sstevel@tonic-gate 		}
22400Sstevel@tonic-gate 		/* Allocate a channel number for the socket. */
22410Sstevel@tonic-gate 		c = channel_new("port listener", type, sock, sock, -1,
22420Sstevel@tonic-gate 		    CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT,
22430Sstevel@tonic-gate 		    0, xstrdup("port listener"), 1);
22440Sstevel@tonic-gate 		strlcpy(c->path, host, sizeof(c->path));
22450Sstevel@tonic-gate 		c->host_port = port_to_connect;
22460Sstevel@tonic-gate 		c->listening_port = listen_port;
22470Sstevel@tonic-gate 		success = 1;
22480Sstevel@tonic-gate 	}
22490Sstevel@tonic-gate 	if (success == 0)
22500Sstevel@tonic-gate 		error("channel_setup_fwd_listener: cannot listen to port: %d",
22510Sstevel@tonic-gate 		    listen_port);
22520Sstevel@tonic-gate 	freeaddrinfo(aitop);
22530Sstevel@tonic-gate 	return success;
22540Sstevel@tonic-gate }
22550Sstevel@tonic-gate 
22560Sstevel@tonic-gate /* protocol local port fwd, used by ssh (and sshd in v1) */
22570Sstevel@tonic-gate int
22580Sstevel@tonic-gate channel_setup_local_fwd_listener(u_short listen_port,
22590Sstevel@tonic-gate     const char *host_to_connect, u_short port_to_connect, int gateway_ports)
22600Sstevel@tonic-gate {
22610Sstevel@tonic-gate 	return channel_setup_fwd_listener(SSH_CHANNEL_PORT_LISTENER,
22620Sstevel@tonic-gate 	    NULL, listen_port, host_to_connect, port_to_connect, gateway_ports);
22630Sstevel@tonic-gate }
22640Sstevel@tonic-gate 
22650Sstevel@tonic-gate /* protocol v2 remote port fwd, used by sshd */
22660Sstevel@tonic-gate int
22670Sstevel@tonic-gate channel_setup_remote_fwd_listener(const char *listen_address,
22680Sstevel@tonic-gate     u_short listen_port, int gateway_ports)
22690Sstevel@tonic-gate {
22700Sstevel@tonic-gate 	return channel_setup_fwd_listener(SSH_CHANNEL_RPORT_LISTENER,
22710Sstevel@tonic-gate 	    listen_address, listen_port, NULL, 0, gateway_ports);
22720Sstevel@tonic-gate }
22730Sstevel@tonic-gate 
22740Sstevel@tonic-gate /*
22750Sstevel@tonic-gate  * Initiate forwarding of connections to port "port" on remote host through
22760Sstevel@tonic-gate  * the secure channel to host:port from local side.
22770Sstevel@tonic-gate  */
22780Sstevel@tonic-gate 
22790Sstevel@tonic-gate void
22800Sstevel@tonic-gate channel_request_remote_forwarding(u_short listen_port,
22810Sstevel@tonic-gate     const char *host_to_connect, u_short port_to_connect)
22820Sstevel@tonic-gate {
22830Sstevel@tonic-gate 	int type, success = 0;
22840Sstevel@tonic-gate 
22850Sstevel@tonic-gate 	/* Record locally that connection to this host/port is permitted. */
22860Sstevel@tonic-gate 	if (num_permitted_opens >= SSH_MAX_FORWARDS_PER_DIRECTION)
22870Sstevel@tonic-gate 		fatal("channel_request_remote_forwarding: too many forwards");
22880Sstevel@tonic-gate 
22890Sstevel@tonic-gate 	/* Send the forward request to the remote side. */
22900Sstevel@tonic-gate 	if (compat20) {
22910Sstevel@tonic-gate 		const char *address_to_bind = "0.0.0.0";
22920Sstevel@tonic-gate 		packet_start(SSH2_MSG_GLOBAL_REQUEST);
22930Sstevel@tonic-gate 		packet_put_cstring("tcpip-forward");
22940Sstevel@tonic-gate 		packet_put_char(1);			/* boolean: want reply */
22950Sstevel@tonic-gate 		packet_put_cstring(address_to_bind);
22960Sstevel@tonic-gate 		packet_put_int(listen_port);
22970Sstevel@tonic-gate 		packet_send();
22980Sstevel@tonic-gate 		packet_write_wait();
22990Sstevel@tonic-gate 		/* Assume that server accepts the request */
23000Sstevel@tonic-gate 		success = 1;
23010Sstevel@tonic-gate 	} else {
23020Sstevel@tonic-gate 		packet_start(SSH_CMSG_PORT_FORWARD_REQUEST);
23030Sstevel@tonic-gate 		packet_put_int(listen_port);
23040Sstevel@tonic-gate 		packet_put_cstring(host_to_connect);
23050Sstevel@tonic-gate 		packet_put_int(port_to_connect);
23060Sstevel@tonic-gate 		packet_send();
23070Sstevel@tonic-gate 		packet_write_wait();
23080Sstevel@tonic-gate 
23090Sstevel@tonic-gate 		/* Wait for response from the remote side. */
23100Sstevel@tonic-gate 		type = packet_read();
23110Sstevel@tonic-gate 		switch (type) {
23120Sstevel@tonic-gate 		case SSH_SMSG_SUCCESS:
23130Sstevel@tonic-gate 			success = 1;
23140Sstevel@tonic-gate 			break;
23150Sstevel@tonic-gate 		case SSH_SMSG_FAILURE:
23160Sstevel@tonic-gate 			log("Warning: Server denied remote port forwarding.");
23170Sstevel@tonic-gate 			break;
23180Sstevel@tonic-gate 		default:
23190Sstevel@tonic-gate 			/* Unknown packet */
23200Sstevel@tonic-gate 			packet_disconnect("Protocol error for port forward request:"
23210Sstevel@tonic-gate 			    "received packet type %d.", type);
23220Sstevel@tonic-gate 		}
23230Sstevel@tonic-gate 	}
23240Sstevel@tonic-gate 	if (success) {
23250Sstevel@tonic-gate 		permitted_opens[num_permitted_opens].host_to_connect = xstrdup(host_to_connect);
23260Sstevel@tonic-gate 		permitted_opens[num_permitted_opens].port_to_connect = port_to_connect;
23270Sstevel@tonic-gate 		permitted_opens[num_permitted_opens].listen_port = listen_port;
23280Sstevel@tonic-gate 		num_permitted_opens++;
23290Sstevel@tonic-gate 	}
23300Sstevel@tonic-gate }
23310Sstevel@tonic-gate 
23320Sstevel@tonic-gate /*
23330Sstevel@tonic-gate  * This is called after receiving CHANNEL_FORWARDING_REQUEST.  This initates
23340Sstevel@tonic-gate  * listening for the port, and sends back a success reply (or disconnect
23350Sstevel@tonic-gate  * message if there was an error).  This never returns if there was an error.
23360Sstevel@tonic-gate  */
23370Sstevel@tonic-gate 
23380Sstevel@tonic-gate void
23390Sstevel@tonic-gate channel_input_port_forward_request(int is_root, int gateway_ports)
23400Sstevel@tonic-gate {
23410Sstevel@tonic-gate 	u_short port, host_port;
23420Sstevel@tonic-gate 	char *hostname;
23430Sstevel@tonic-gate 
23440Sstevel@tonic-gate 	/* Get arguments from the packet. */
23450Sstevel@tonic-gate 	port = packet_get_int();
23460Sstevel@tonic-gate 	hostname = packet_get_string(NULL);
23470Sstevel@tonic-gate 	host_port = packet_get_int();
23480Sstevel@tonic-gate 
23490Sstevel@tonic-gate #ifndef HAVE_CYGWIN
23500Sstevel@tonic-gate 	/*
23510Sstevel@tonic-gate 	 * Check that an unprivileged user is not trying to forward a
23520Sstevel@tonic-gate 	 * privileged port.
23530Sstevel@tonic-gate 	 */
23540Sstevel@tonic-gate 	if (port < IPPORT_RESERVED && !is_root)
23550Sstevel@tonic-gate 		packet_disconnect("Requested forwarding of port %d but user is not root.",
23560Sstevel@tonic-gate 				  port);
23570Sstevel@tonic-gate #endif
23580Sstevel@tonic-gate 	/* Initiate forwarding */
23590Sstevel@tonic-gate 	channel_setup_local_fwd_listener(port, hostname, host_port, gateway_ports);
23600Sstevel@tonic-gate 
23610Sstevel@tonic-gate 	/* Free the argument string. */
23620Sstevel@tonic-gate 	xfree(hostname);
23630Sstevel@tonic-gate }
23640Sstevel@tonic-gate 
23650Sstevel@tonic-gate /*
23660Sstevel@tonic-gate  * Permits opening to any host/port if permitted_opens[] is empty.  This is
23670Sstevel@tonic-gate  * usually called by the server, because the user could connect to any port
23680Sstevel@tonic-gate  * anyway, and the server has no way to know but to trust the client anyway.
23690Sstevel@tonic-gate  */
23700Sstevel@tonic-gate void
23710Sstevel@tonic-gate channel_permit_all_opens(void)
23720Sstevel@tonic-gate {
23730Sstevel@tonic-gate 	if (num_permitted_opens == 0)
23740Sstevel@tonic-gate 		all_opens_permitted = 1;
23750Sstevel@tonic-gate }
23760Sstevel@tonic-gate 
23770Sstevel@tonic-gate void
23780Sstevel@tonic-gate channel_add_permitted_opens(char *host, int port)
23790Sstevel@tonic-gate {
23800Sstevel@tonic-gate 	if (num_permitted_opens >= SSH_MAX_FORWARDS_PER_DIRECTION)
23810Sstevel@tonic-gate 		fatal("channel_request_remote_forwarding: too many forwards");
23820Sstevel@tonic-gate 	debug("allow port forwarding to host %s port %d", host, port);
23830Sstevel@tonic-gate 
23840Sstevel@tonic-gate 	permitted_opens[num_permitted_opens].host_to_connect = xstrdup(host);
23850Sstevel@tonic-gate 	permitted_opens[num_permitted_opens].port_to_connect = port;
23860Sstevel@tonic-gate 	num_permitted_opens++;
23870Sstevel@tonic-gate 
23880Sstevel@tonic-gate 	all_opens_permitted = 0;
23890Sstevel@tonic-gate }
23900Sstevel@tonic-gate 
23910Sstevel@tonic-gate void
23920Sstevel@tonic-gate channel_clear_permitted_opens(void)
23930Sstevel@tonic-gate {
23940Sstevel@tonic-gate 	int i;
23950Sstevel@tonic-gate 
23960Sstevel@tonic-gate 	for (i = 0; i < num_permitted_opens; i++)
23970Sstevel@tonic-gate 		xfree(permitted_opens[i].host_to_connect);
23980Sstevel@tonic-gate 	num_permitted_opens = 0;
23990Sstevel@tonic-gate 
24000Sstevel@tonic-gate }
24010Sstevel@tonic-gate 
24020Sstevel@tonic-gate 
24030Sstevel@tonic-gate /* return socket to remote host, port */
24040Sstevel@tonic-gate static int
24050Sstevel@tonic-gate connect_to(const char *host, u_short port)
24060Sstevel@tonic-gate {
24070Sstevel@tonic-gate 	struct addrinfo hints, *ai, *aitop;
24080Sstevel@tonic-gate 	char ntop[NI_MAXHOST], strport[NI_MAXSERV];
24090Sstevel@tonic-gate 	int gaierr;
24100Sstevel@tonic-gate 	int sock = -1;
24110Sstevel@tonic-gate 
24120Sstevel@tonic-gate 	memset(&hints, 0, sizeof(hints));
24130Sstevel@tonic-gate 	hints.ai_family = IPv4or6;
24140Sstevel@tonic-gate 	hints.ai_socktype = SOCK_STREAM;
24150Sstevel@tonic-gate 	snprintf(strport, sizeof strport, "%d", port);
24160Sstevel@tonic-gate 	if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0) {
24170Sstevel@tonic-gate 		error("connect_to %.100s: unknown host (%s)", host,
24180Sstevel@tonic-gate 		    gai_strerror(gaierr));
24190Sstevel@tonic-gate 		return -1;
24200Sstevel@tonic-gate 	}
24210Sstevel@tonic-gate 	for (ai = aitop; ai; ai = ai->ai_next) {
24220Sstevel@tonic-gate 		if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6)
24230Sstevel@tonic-gate 			continue;
24240Sstevel@tonic-gate 		if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop),
24250Sstevel@tonic-gate 		    strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV) != 0) {
24260Sstevel@tonic-gate 			error("connect_to: getnameinfo failed");
24270Sstevel@tonic-gate 			continue;
24280Sstevel@tonic-gate 		}
24290Sstevel@tonic-gate 		sock = socket(ai->ai_family, SOCK_STREAM, 0);
24300Sstevel@tonic-gate 		if (sock < 0) {
24310Sstevel@tonic-gate 			error("socket: %.100s", strerror(errno));
24320Sstevel@tonic-gate 			continue;
24330Sstevel@tonic-gate 		}
24340Sstevel@tonic-gate 		if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0)
24350Sstevel@tonic-gate 			fatal("connect_to: F_SETFL: %s", strerror(errno));
24360Sstevel@tonic-gate 		if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0 &&
24370Sstevel@tonic-gate 		    errno != EINPROGRESS) {
24380Sstevel@tonic-gate 			error("connect_to %.100s port %s: %.100s", ntop, strport,
24390Sstevel@tonic-gate 			    strerror(errno));
24400Sstevel@tonic-gate 			close(sock);
24410Sstevel@tonic-gate 			continue;	/* fail -- try next */
24420Sstevel@tonic-gate 		}
24430Sstevel@tonic-gate 		break; /* success */
24440Sstevel@tonic-gate 
24450Sstevel@tonic-gate 	}
24460Sstevel@tonic-gate 	freeaddrinfo(aitop);
24470Sstevel@tonic-gate 	if (!ai) {
24480Sstevel@tonic-gate 		error("connect_to %.100s port %d: failed.", host, port);
24490Sstevel@tonic-gate 		return -1;
24500Sstevel@tonic-gate 	}
24510Sstevel@tonic-gate 	/* success */
24520Sstevel@tonic-gate 	set_nodelay(sock);
24530Sstevel@tonic-gate 	return sock;
24540Sstevel@tonic-gate }
24550Sstevel@tonic-gate 
24560Sstevel@tonic-gate int
24570Sstevel@tonic-gate channel_connect_by_listen_address(u_short listen_port)
24580Sstevel@tonic-gate {
24590Sstevel@tonic-gate 	int i;
24600Sstevel@tonic-gate 
24610Sstevel@tonic-gate 	for (i = 0; i < num_permitted_opens; i++)
24620Sstevel@tonic-gate 		if (permitted_opens[i].listen_port == listen_port)
24630Sstevel@tonic-gate 			return connect_to(
24640Sstevel@tonic-gate 			    permitted_opens[i].host_to_connect,
24650Sstevel@tonic-gate 			    permitted_opens[i].port_to_connect);
24660Sstevel@tonic-gate 	error("WARNING: Server requests forwarding for unknown listen_port %d",
24670Sstevel@tonic-gate 	    listen_port);
24680Sstevel@tonic-gate 	return -1;
24690Sstevel@tonic-gate }
24700Sstevel@tonic-gate 
24710Sstevel@tonic-gate /* Check if connecting to that port is permitted and connect. */
24720Sstevel@tonic-gate int
24730Sstevel@tonic-gate channel_connect_to(const char *host, u_short port)
24740Sstevel@tonic-gate {
24750Sstevel@tonic-gate 	int i, permit;
24760Sstevel@tonic-gate 
24770Sstevel@tonic-gate 	permit = all_opens_permitted;
24780Sstevel@tonic-gate 	if (!permit) {
24790Sstevel@tonic-gate 		for (i = 0; i < num_permitted_opens; i++)
24800Sstevel@tonic-gate 			if (permitted_opens[i].port_to_connect == port &&
24810Sstevel@tonic-gate 			    strcmp(permitted_opens[i].host_to_connect, host) == 0)
24820Sstevel@tonic-gate 				permit = 1;
24830Sstevel@tonic-gate 
24840Sstevel@tonic-gate 	}
24850Sstevel@tonic-gate 	if (!permit) {
24860Sstevel@tonic-gate 		log("Received request to connect to host %.100s port %d, "
24870Sstevel@tonic-gate 		    "but the request was denied.", host, port);
24880Sstevel@tonic-gate 		return -1;
24890Sstevel@tonic-gate 	}
24900Sstevel@tonic-gate 	return connect_to(host, port);
24910Sstevel@tonic-gate }
24920Sstevel@tonic-gate 
24930Sstevel@tonic-gate /* -- X11 forwarding */
24940Sstevel@tonic-gate 
24950Sstevel@tonic-gate /*
24960Sstevel@tonic-gate  * Creates an internet domain socket for listening for X11 connections.
24970Sstevel@tonic-gate  * Returns 0 and a suitable display number for the DISPLAY variable
24980Sstevel@tonic-gate  * stored in display_numberp , or -1 if an error occurs.
24990Sstevel@tonic-gate  */
25000Sstevel@tonic-gate int
25010Sstevel@tonic-gate x11_create_display_inet(int x11_display_offset, int x11_use_localhost,
25020Sstevel@tonic-gate     int single_connection, u_int *display_numberp)
25030Sstevel@tonic-gate {
25040Sstevel@tonic-gate 	Channel *nc = NULL;
25050Sstevel@tonic-gate 	int display_number, sock;
25060Sstevel@tonic-gate 	u_short port;
25070Sstevel@tonic-gate 	struct addrinfo hints, *ai, *aitop;
25080Sstevel@tonic-gate 	char strport[NI_MAXSERV];
25090Sstevel@tonic-gate 	int gaierr, n, num_socks = 0, socks[NUM_SOCKS];
25100Sstevel@tonic-gate 
25110Sstevel@tonic-gate 	for (display_number = x11_display_offset;
25120Sstevel@tonic-gate 	    display_number < MAX_DISPLAYS;
25130Sstevel@tonic-gate 	    display_number++) {
25140Sstevel@tonic-gate 		port = 6000 + display_number;
25150Sstevel@tonic-gate 		memset(&hints, 0, sizeof(hints));
25160Sstevel@tonic-gate 		hints.ai_family = IPv4or6;
25170Sstevel@tonic-gate 		hints.ai_flags = x11_use_localhost ? 0: AI_PASSIVE;
25180Sstevel@tonic-gate 		hints.ai_socktype = SOCK_STREAM;
25190Sstevel@tonic-gate 		snprintf(strport, sizeof strport, "%d", port);
25200Sstevel@tonic-gate 		if ((gaierr = getaddrinfo(NULL, strport, &hints, &aitop)) != 0) {
25210Sstevel@tonic-gate 			error("getaddrinfo: %.100s", gai_strerror(gaierr));
25220Sstevel@tonic-gate 			return -1;
25230Sstevel@tonic-gate 		}
25240Sstevel@tonic-gate 		for (ai = aitop; ai; ai = ai->ai_next) {
25250Sstevel@tonic-gate 			if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6)
25260Sstevel@tonic-gate 				continue;
25270Sstevel@tonic-gate 			sock = socket(ai->ai_family, SOCK_STREAM, 0);
25280Sstevel@tonic-gate 			if (sock < 0) {
25290Sstevel@tonic-gate 				if ((errno != EINVAL) && (errno != EAFNOSUPPORT)) {
25300Sstevel@tonic-gate 					error("socket: %.100s", strerror(errno));
25310Sstevel@tonic-gate 					return -1;
25320Sstevel@tonic-gate 				} else {
25330Sstevel@tonic-gate 					debug("x11_create_display_inet: Socket family %d not supported",
25340Sstevel@tonic-gate 						 ai->ai_family);
25350Sstevel@tonic-gate 					continue;
25360Sstevel@tonic-gate 				}
25370Sstevel@tonic-gate 			}
25380Sstevel@tonic-gate #ifdef IPV6_V6ONLY
25390Sstevel@tonic-gate 			if (ai->ai_family == AF_INET6) {
25400Sstevel@tonic-gate 				int on = 1;
25410Sstevel@tonic-gate 				if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) < 0)
25420Sstevel@tonic-gate 					error("setsockopt IPV6_V6ONLY: %.100s", strerror(errno));
25430Sstevel@tonic-gate 			}
25440Sstevel@tonic-gate #endif
25450Sstevel@tonic-gate 			if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) {
25460Sstevel@tonic-gate 				debug("bind port %d: %.100s", port, strerror(errno));
25470Sstevel@tonic-gate 				close(sock);
25480Sstevel@tonic-gate 
25490Sstevel@tonic-gate 				if (ai->ai_next)
25500Sstevel@tonic-gate 					continue;
25510Sstevel@tonic-gate 
25520Sstevel@tonic-gate 				for (n = 0; n < num_socks; n++) {
25530Sstevel@tonic-gate 					close(socks[n]);
25540Sstevel@tonic-gate 				}
25550Sstevel@tonic-gate 				num_socks = 0;
25560Sstevel@tonic-gate 				break;
25570Sstevel@tonic-gate 			}
25580Sstevel@tonic-gate 			socks[num_socks++] = sock;
25590Sstevel@tonic-gate #ifndef DONT_TRY_OTHER_AF
25600Sstevel@tonic-gate 			if (num_socks == NUM_SOCKS)
25610Sstevel@tonic-gate 				break;
25620Sstevel@tonic-gate #else
25630Sstevel@tonic-gate 			if (x11_use_localhost) {
25640Sstevel@tonic-gate 				if (num_socks == NUM_SOCKS)
25650Sstevel@tonic-gate 					break;
25660Sstevel@tonic-gate 			} else {
25670Sstevel@tonic-gate 				break;
25680Sstevel@tonic-gate 			}
25690Sstevel@tonic-gate #endif
25700Sstevel@tonic-gate 		}
25710Sstevel@tonic-gate 		freeaddrinfo(aitop);
25720Sstevel@tonic-gate 		if (num_socks > 0)
25730Sstevel@tonic-gate 			break;
25740Sstevel@tonic-gate 	}
25750Sstevel@tonic-gate 	if (display_number >= MAX_DISPLAYS) {
25760Sstevel@tonic-gate 		error("Failed to allocate internet-domain X11 display socket.");
25770Sstevel@tonic-gate 		return -1;
25780Sstevel@tonic-gate 	}
25790Sstevel@tonic-gate 	/* Start listening for connections on the socket. */
25800Sstevel@tonic-gate 	for (n = 0; n < num_socks; n++) {
25810Sstevel@tonic-gate 		sock = socks[n];
25820Sstevel@tonic-gate 		if (listen(sock, 5) < 0) {
25830Sstevel@tonic-gate 			error("listen: %.100s", strerror(errno));
25840Sstevel@tonic-gate 			close(sock);
25850Sstevel@tonic-gate 			return -1;
25860Sstevel@tonic-gate 		}
25870Sstevel@tonic-gate 	}
25880Sstevel@tonic-gate 
25890Sstevel@tonic-gate 	/* Allocate a channel for each socket. */
25900Sstevel@tonic-gate 	for (n = 0; n < num_socks; n++) {
25910Sstevel@tonic-gate 		sock = socks[n];
25920Sstevel@tonic-gate 		nc = channel_new("x11 listener",
25930Sstevel@tonic-gate 		    SSH_CHANNEL_X11_LISTENER, sock, sock, -1,
25940Sstevel@tonic-gate 		    CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT,
25950Sstevel@tonic-gate 		    0, xstrdup("X11 inet listener"), 1);
25960Sstevel@tonic-gate 		nc->single_connection = single_connection;
25970Sstevel@tonic-gate 	}
25980Sstevel@tonic-gate 
25990Sstevel@tonic-gate 	/* Return the display number for the DISPLAY environment variable. */
26000Sstevel@tonic-gate 	*display_numberp = display_number;
26010Sstevel@tonic-gate 	return (0);
26020Sstevel@tonic-gate }
26030Sstevel@tonic-gate 
26040Sstevel@tonic-gate static int
26050Sstevel@tonic-gate connect_local_xsocket(u_int dnr)
26060Sstevel@tonic-gate {
26070Sstevel@tonic-gate 	int sock;
26080Sstevel@tonic-gate 	struct sockaddr_un addr;
26090Sstevel@tonic-gate 
26100Sstevel@tonic-gate 	sock = socket(AF_UNIX, SOCK_STREAM, 0);
26110Sstevel@tonic-gate 	if (sock < 0)
26120Sstevel@tonic-gate 		error("socket: %.100s", strerror(errno));
26130Sstevel@tonic-gate 	memset(&addr, 0, sizeof(addr));
26140Sstevel@tonic-gate 	addr.sun_family = AF_UNIX;
26150Sstevel@tonic-gate 	snprintf(addr.sun_path, sizeof addr.sun_path, _PATH_UNIX_X, dnr);
26160Sstevel@tonic-gate 	if (connect(sock, (struct sockaddr *) & addr, sizeof(addr)) == 0)
26170Sstevel@tonic-gate 		return sock;
26180Sstevel@tonic-gate 	close(sock);
26190Sstevel@tonic-gate 	error("connect %.100s: %.100s", addr.sun_path, strerror(errno));
26200Sstevel@tonic-gate 	return -1;
26210Sstevel@tonic-gate }
26220Sstevel@tonic-gate 
26230Sstevel@tonic-gate int
26240Sstevel@tonic-gate x11_connect_display(void)
26250Sstevel@tonic-gate {
26260Sstevel@tonic-gate 	int display_number, sock = 0;
26270Sstevel@tonic-gate 	const char *display;
26280Sstevel@tonic-gate 	char buf[1024], *cp;
26290Sstevel@tonic-gate 	struct addrinfo hints, *ai, *aitop;
26300Sstevel@tonic-gate 	char strport[NI_MAXSERV];
26310Sstevel@tonic-gate 	int gaierr;
26320Sstevel@tonic-gate 
26330Sstevel@tonic-gate 	/* Try to open a socket for the local X server. */
26340Sstevel@tonic-gate 	display = getenv("DISPLAY");
26350Sstevel@tonic-gate 	if (!display) {
26360Sstevel@tonic-gate 		error("DISPLAY not set.");
26370Sstevel@tonic-gate 		return -1;
26380Sstevel@tonic-gate 	}
26390Sstevel@tonic-gate 	/*
26400Sstevel@tonic-gate 	 * Now we decode the value of the DISPLAY variable and make a
26410Sstevel@tonic-gate 	 * connection to the real X server.
26420Sstevel@tonic-gate 	 */
26430Sstevel@tonic-gate 
26440Sstevel@tonic-gate 	/*
26450Sstevel@tonic-gate 	 * Check if it is a unix domain socket.  Unix domain displays are in
26460Sstevel@tonic-gate 	 * one of the following formats: unix:d[.s], :d[.s], ::d[.s]
26470Sstevel@tonic-gate 	 */
26480Sstevel@tonic-gate 	if (strncmp(display, "unix:", 5) == 0 ||
26490Sstevel@tonic-gate 	    display[0] == ':') {
26500Sstevel@tonic-gate 		/* Connect to the unix domain socket. */
26510Sstevel@tonic-gate 		if (sscanf(strrchr(display, ':') + 1, "%d", &display_number) != 1) {
26520Sstevel@tonic-gate 			error("Could not parse display number from DISPLAY: %.100s",
26530Sstevel@tonic-gate 			    display);
26540Sstevel@tonic-gate 			return -1;
26550Sstevel@tonic-gate 		}
26560Sstevel@tonic-gate 		/* Create a socket. */
26570Sstevel@tonic-gate 		sock = connect_local_xsocket(display_number);
26580Sstevel@tonic-gate 		if (sock < 0)
26590Sstevel@tonic-gate 			return -1;
26600Sstevel@tonic-gate 
26610Sstevel@tonic-gate 		/* OK, we now have a connection to the display. */
26620Sstevel@tonic-gate 		return sock;
26630Sstevel@tonic-gate 	}
26640Sstevel@tonic-gate 	/*
26650Sstevel@tonic-gate 	 * Connect to an inet socket.  The DISPLAY value is supposedly
26660Sstevel@tonic-gate 	 * hostname:d[.s], where hostname may also be numeric IP address.
26670Sstevel@tonic-gate 	 */
26680Sstevel@tonic-gate 	strlcpy(buf, display, sizeof(buf));
26690Sstevel@tonic-gate 	cp = strchr(buf, ':');
26700Sstevel@tonic-gate 	if (!cp) {
26710Sstevel@tonic-gate 		error("Could not find ':' in DISPLAY: %.100s", display);
26720Sstevel@tonic-gate 		return -1;
26730Sstevel@tonic-gate 	}
26740Sstevel@tonic-gate 	*cp = 0;
26750Sstevel@tonic-gate 	/* buf now contains the host name.  But first we parse the display number. */
26760Sstevel@tonic-gate 	if (sscanf(cp + 1, "%d", &display_number) != 1) {
26770Sstevel@tonic-gate 		error("Could not parse display number from DISPLAY: %.100s",
26780Sstevel@tonic-gate 		    display);
26790Sstevel@tonic-gate 		return -1;
26800Sstevel@tonic-gate 	}
26810Sstevel@tonic-gate 
26820Sstevel@tonic-gate 	/* Look up the host address */
26830Sstevel@tonic-gate 	memset(&hints, 0, sizeof(hints));
26840Sstevel@tonic-gate 	hints.ai_family = IPv4or6;
26850Sstevel@tonic-gate 	hints.ai_socktype = SOCK_STREAM;
26860Sstevel@tonic-gate 	snprintf(strport, sizeof strport, "%d", 6000 + display_number);
26870Sstevel@tonic-gate 	if ((gaierr = getaddrinfo(buf, strport, &hints, &aitop)) != 0) {
26880Sstevel@tonic-gate 		error("%.100s: unknown host. (%s)", buf, gai_strerror(gaierr));
26890Sstevel@tonic-gate 		return -1;
26900Sstevel@tonic-gate 	}
26910Sstevel@tonic-gate 	for (ai = aitop; ai; ai = ai->ai_next) {
26920Sstevel@tonic-gate 		/* Create a socket. */
26930Sstevel@tonic-gate 		sock = socket(ai->ai_family, SOCK_STREAM, 0);
26940Sstevel@tonic-gate 		if (sock < 0) {
26950Sstevel@tonic-gate 			debug("socket: %.100s", strerror(errno));
26960Sstevel@tonic-gate 			continue;
26970Sstevel@tonic-gate 		}
26980Sstevel@tonic-gate 		/* Connect it to the display. */
26990Sstevel@tonic-gate 		if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0) {
27000Sstevel@tonic-gate 			debug("connect %.100s port %d: %.100s", buf,
27010Sstevel@tonic-gate 			    6000 + display_number, strerror(errno));
27020Sstevel@tonic-gate 			close(sock);
27030Sstevel@tonic-gate 			continue;
27040Sstevel@tonic-gate 		}
27050Sstevel@tonic-gate 		/* Success */
27060Sstevel@tonic-gate 		break;
27070Sstevel@tonic-gate 	}
27080Sstevel@tonic-gate 	freeaddrinfo(aitop);
27090Sstevel@tonic-gate 	if (!ai) {
27100Sstevel@tonic-gate 		error("connect %.100s port %d: %.100s", buf, 6000 + display_number,
27110Sstevel@tonic-gate 		    strerror(errno));
27120Sstevel@tonic-gate 		return -1;
27130Sstevel@tonic-gate 	}
27140Sstevel@tonic-gate 	set_nodelay(sock);
27150Sstevel@tonic-gate 	return sock;
27160Sstevel@tonic-gate }
27170Sstevel@tonic-gate 
27180Sstevel@tonic-gate /*
27190Sstevel@tonic-gate  * This is called when SSH_SMSG_X11_OPEN is received.  The packet contains
27200Sstevel@tonic-gate  * the remote channel number.  We should do whatever we want, and respond
27210Sstevel@tonic-gate  * with either SSH_MSG_OPEN_CONFIRMATION or SSH_MSG_OPEN_FAILURE.
27220Sstevel@tonic-gate  */
27230Sstevel@tonic-gate 
27240Sstevel@tonic-gate void
27250Sstevel@tonic-gate x11_input_open(int type, u_int32_t seq, void *ctxt)
27260Sstevel@tonic-gate {
27270Sstevel@tonic-gate 	Channel *c = NULL;
27280Sstevel@tonic-gate 	int remote_id, sock = 0;
27290Sstevel@tonic-gate 	char *remote_host;
27300Sstevel@tonic-gate 
27310Sstevel@tonic-gate 	debug("Received X11 open request.");
27320Sstevel@tonic-gate 
27330Sstevel@tonic-gate 	remote_id = packet_get_int();
27340Sstevel@tonic-gate 
27350Sstevel@tonic-gate 	if (packet_get_protocol_flags() & SSH_PROTOFLAG_HOST_IN_FWD_OPEN) {
27360Sstevel@tonic-gate 		remote_host = packet_get_string(NULL);
27370Sstevel@tonic-gate 	} else {
27380Sstevel@tonic-gate 		remote_host = xstrdup("unknown (remote did not supply name)");
27390Sstevel@tonic-gate 	}
27400Sstevel@tonic-gate 	packet_check_eom();
27410Sstevel@tonic-gate 
27420Sstevel@tonic-gate 	/* Obtain a connection to the real X display. */
27430Sstevel@tonic-gate 	sock = x11_connect_display();
27440Sstevel@tonic-gate 	if (sock != -1) {
27450Sstevel@tonic-gate 		/* Allocate a channel for this connection. */
27460Sstevel@tonic-gate 		c = channel_new("connected x11 socket",
27470Sstevel@tonic-gate 		    SSH_CHANNEL_X11_OPEN, sock, sock, -1, 0, 0, 0,
27480Sstevel@tonic-gate 		    remote_host, 1);
27490Sstevel@tonic-gate 		c->remote_id = remote_id;
27500Sstevel@tonic-gate 		c->force_drain = 1;
27510Sstevel@tonic-gate 	}
27520Sstevel@tonic-gate 	if (c == NULL) {
27530Sstevel@tonic-gate 		/* Send refusal to the remote host. */
27540Sstevel@tonic-gate 		packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE);
27550Sstevel@tonic-gate 		packet_put_int(remote_id);
27560Sstevel@tonic-gate 	} else {
27570Sstevel@tonic-gate 		/* Send a confirmation to the remote host. */
27580Sstevel@tonic-gate 		packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION);
27590Sstevel@tonic-gate 		packet_put_int(remote_id);
27600Sstevel@tonic-gate 		packet_put_int(c->self);
27610Sstevel@tonic-gate 	}
27620Sstevel@tonic-gate 	packet_send();
27630Sstevel@tonic-gate }
27640Sstevel@tonic-gate 
27650Sstevel@tonic-gate /* dummy protocol handler that denies SSH-1 requests (agent/x11) */
27660Sstevel@tonic-gate void
27670Sstevel@tonic-gate deny_input_open(int type, u_int32_t seq, void *ctxt)
27680Sstevel@tonic-gate {
27690Sstevel@tonic-gate 	int rchan = packet_get_int();
27700Sstevel@tonic-gate 
27710Sstevel@tonic-gate 	switch (type) {
27720Sstevel@tonic-gate 	case SSH_SMSG_AGENT_OPEN:
27730Sstevel@tonic-gate 		error("Warning: ssh server tried agent forwarding.");
27740Sstevel@tonic-gate 		break;
27750Sstevel@tonic-gate 	case SSH_SMSG_X11_OPEN:
27760Sstevel@tonic-gate 		error("Warning: ssh server tried X11 forwarding.");
27770Sstevel@tonic-gate 		break;
27780Sstevel@tonic-gate 	default:
27790Sstevel@tonic-gate 		error("deny_input_open: type %d", type);
27800Sstevel@tonic-gate 		break;
27810Sstevel@tonic-gate 	}
27820Sstevel@tonic-gate 	error("Warning: this is probably a break in attempt by a malicious server.");
27830Sstevel@tonic-gate 	packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE);
27840Sstevel@tonic-gate 	packet_put_int(rchan);
27850Sstevel@tonic-gate 	packet_send();
27860Sstevel@tonic-gate }
27870Sstevel@tonic-gate 
27880Sstevel@tonic-gate /*
27890Sstevel@tonic-gate  * Requests forwarding of X11 connections, generates fake authentication
27900Sstevel@tonic-gate  * data, and enables authentication spoofing.
27910Sstevel@tonic-gate  * This should be called in the client only.
27920Sstevel@tonic-gate  */
27930Sstevel@tonic-gate void
2794*4907Sjp161948 x11_request_forwarding_with_spoofing(int client_session_id, const char *disp,
27950Sstevel@tonic-gate     const char *proto, const char *data)
27960Sstevel@tonic-gate {
27970Sstevel@tonic-gate 	u_int data_len = (u_int) strlen(data) / 2;
2798*4907Sjp161948 	u_int i, value;
27990Sstevel@tonic-gate 	char *new_data;
28000Sstevel@tonic-gate 	int screen_number;
28010Sstevel@tonic-gate 	const char *cp;
28020Sstevel@tonic-gate 	u_int32_t rand = 0;
28030Sstevel@tonic-gate 
2804*4907Sjp161948 	cp = disp;
2805*4907Sjp161948 	if (disp)
2806*4907Sjp161948 		cp = strchr(disp, ':');
28070Sstevel@tonic-gate 	if (cp)
28080Sstevel@tonic-gate 		cp = strchr(cp, '.');
28090Sstevel@tonic-gate 	if (cp)
28100Sstevel@tonic-gate 		screen_number = atoi(cp + 1);
28110Sstevel@tonic-gate 	else
28120Sstevel@tonic-gate 		screen_number = 0;
28130Sstevel@tonic-gate 
28140Sstevel@tonic-gate 	/* Save protocol name. */
28150Sstevel@tonic-gate 	x11_saved_proto = xstrdup(proto);
28160Sstevel@tonic-gate 
28170Sstevel@tonic-gate 	/*
28180Sstevel@tonic-gate 	 * Extract real authentication data and generate fake data of the
28190Sstevel@tonic-gate 	 * same length.
28200Sstevel@tonic-gate 	 */
28210Sstevel@tonic-gate 	x11_saved_data = xmalloc(data_len);
28220Sstevel@tonic-gate 	x11_fake_data = xmalloc(data_len);
28230Sstevel@tonic-gate 	for (i = 0; i < data_len; i++) {
28240Sstevel@tonic-gate 		if (sscanf(data + 2 * i, "%2x", &value) != 1)
28250Sstevel@tonic-gate 			fatal("x11_request_forwarding: bad authentication data: %.100s", data);
28260Sstevel@tonic-gate 		if (i % 4 == 0)
28270Sstevel@tonic-gate 			rand = arc4random();
28280Sstevel@tonic-gate 		x11_saved_data[i] = value;
28290Sstevel@tonic-gate 		x11_fake_data[i] = rand & 0xff;
28300Sstevel@tonic-gate 		rand >>= 8;
28310Sstevel@tonic-gate 	}
28320Sstevel@tonic-gate 	x11_saved_data_len = data_len;
28330Sstevel@tonic-gate 	x11_fake_data_len = data_len;
28340Sstevel@tonic-gate 
28350Sstevel@tonic-gate 	/* Convert the fake data into hex. */
2836*4907Sjp161948 	new_data = tohex(x11_fake_data, data_len);
28370Sstevel@tonic-gate 
28380Sstevel@tonic-gate 	/* Send the request packet. */
28390Sstevel@tonic-gate 	if (compat20) {
28400Sstevel@tonic-gate 		channel_request_start(client_session_id, "x11-req", 0);
28410Sstevel@tonic-gate 		packet_put_char(0);	/* XXX bool single connection */
28420Sstevel@tonic-gate 	} else {
28430Sstevel@tonic-gate 		packet_start(SSH_CMSG_X11_REQUEST_FORWARDING);
28440Sstevel@tonic-gate 	}
28450Sstevel@tonic-gate 	packet_put_cstring(proto);
28460Sstevel@tonic-gate 	packet_put_cstring(new_data);
28470Sstevel@tonic-gate 	packet_put_int(screen_number);
28480Sstevel@tonic-gate 	packet_send();
28490Sstevel@tonic-gate 	packet_write_wait();
28500Sstevel@tonic-gate 	xfree(new_data);
28510Sstevel@tonic-gate }
28520Sstevel@tonic-gate 
28530Sstevel@tonic-gate 
28540Sstevel@tonic-gate /* -- agent forwarding */
28550Sstevel@tonic-gate 
28560Sstevel@tonic-gate /* Sends a message to the server to request authentication fd forwarding. */
28570Sstevel@tonic-gate 
28580Sstevel@tonic-gate void
28590Sstevel@tonic-gate auth_request_forwarding(void)
28600Sstevel@tonic-gate {
28610Sstevel@tonic-gate 	packet_start(SSH_CMSG_AGENT_REQUEST_FORWARDING);
28620Sstevel@tonic-gate 	packet_send();
28630Sstevel@tonic-gate 	packet_write_wait();
28640Sstevel@tonic-gate }
28650Sstevel@tonic-gate 
28660Sstevel@tonic-gate /* This is called to process an SSH_SMSG_AGENT_OPEN message. */
28670Sstevel@tonic-gate 
28680Sstevel@tonic-gate void
28690Sstevel@tonic-gate auth_input_open_request(int type, u_int32_t seq, void *ctxt)
28700Sstevel@tonic-gate {
28710Sstevel@tonic-gate 	Channel *c = NULL;
28720Sstevel@tonic-gate 	int remote_id, sock;
28730Sstevel@tonic-gate 	char *name;
28740Sstevel@tonic-gate 
28750Sstevel@tonic-gate 	/* Read the remote channel number from the message. */
28760Sstevel@tonic-gate 	remote_id = packet_get_int();
28770Sstevel@tonic-gate 	packet_check_eom();
28780Sstevel@tonic-gate 
28790Sstevel@tonic-gate 	/*
28800Sstevel@tonic-gate 	 * Get a connection to the local authentication agent (this may again
28810Sstevel@tonic-gate 	 * get forwarded).
28820Sstevel@tonic-gate 	 */
28830Sstevel@tonic-gate 	sock = ssh_get_authentication_socket();
28840Sstevel@tonic-gate 
28850Sstevel@tonic-gate 	/*
28860Sstevel@tonic-gate 	 * If we could not connect the agent, send an error message back to
28870Sstevel@tonic-gate 	 * the server. This should never happen unless the agent dies,
28880Sstevel@tonic-gate 	 * because authentication forwarding is only enabled if we have an
28890Sstevel@tonic-gate 	 * agent.
28900Sstevel@tonic-gate 	 */
28910Sstevel@tonic-gate 	if (sock >= 0) {
28920Sstevel@tonic-gate 		name = xstrdup("authentication agent connection");
28930Sstevel@tonic-gate 		c = channel_new("", SSH_CHANNEL_OPEN, sock, sock,
28940Sstevel@tonic-gate 		    -1, 0, 0, 0, name, 1);
28950Sstevel@tonic-gate 		c->remote_id = remote_id;
28960Sstevel@tonic-gate 		c->force_drain = 1;
28970Sstevel@tonic-gate 	}
28980Sstevel@tonic-gate 	if (c == NULL) {
28990Sstevel@tonic-gate 		packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE);
29000Sstevel@tonic-gate 		packet_put_int(remote_id);
29010Sstevel@tonic-gate 	} else {
29020Sstevel@tonic-gate 		/* Send a confirmation to the remote host. */
29030Sstevel@tonic-gate 		debug("Forwarding authentication connection.");
29040Sstevel@tonic-gate 		packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION);
29050Sstevel@tonic-gate 		packet_put_int(remote_id);
29060Sstevel@tonic-gate 		packet_put_int(c->self);
29070Sstevel@tonic-gate 	}
29080Sstevel@tonic-gate 	packet_send();
29090Sstevel@tonic-gate }
2910