changeset 9:7f77962de998

- Reworked non-channel fd handling to listener.c - More channel cleaning up
author Matt Johnston <matt@ucc.asn.au>
date Thu, 03 Jun 2004 16:45:53 +0000
parents 425ed5c20157
children 0f7d69d31b9d f76c9389e9e0
files Makefile.in channel.h chansession.h common-channel.c listener.c listener.h localtcpfwd.c localtcpfwd.h options.h remotetcpfwd.c remotetcpfwd.h session.h svr-chansession.c svr-session.c tcpfwd-direct.c tcpfwd-direct.h tcpfwd-remote.c tcpfwd-remote.h tcpfwd.c tcpfwd.h x11fwd.c x11fwd.h
diffstat 22 files changed, 748 insertions(+), 716 deletions(-) [+]
line wrap: on
line diff
--- a/Makefile.in	Wed Jun 02 04:59:49 2004 +0000
+++ b/Makefile.in	Thu Jun 03 16:45:53 2004 +0000
@@ -4,9 +4,9 @@
 COMMONOBJS=dbutil.o common-session.o common-packet.o common-algo.o buffer.o \
 		common-kex.o dss.o bignum.o \
 		signkey.o rsa.o random.o common-channel.o \
-		common-chansession.o queue.o termcodes.o runopts.o \
-		loginrec.o atomicio.o x11fwd.o agentfwd.o localtcpfwd.o compat.o \
-		remotetcpfwd.o tcpfwd.o
+		common-chansession.o queue.o termcodes.o  \
+		loginrec.o atomicio.o x11fwd.o tcpfwd-direct.o compat.o \
+		tcpfwd-remote.o listener.o
 
 SVROBJS=svr-kex.o svr-packet.o svr-algo.o svr-auth.o sshpty.o \
 		svr-authpasswd.o svr-authpubkey.o svr-session.o svr-service.o \
@@ -28,8 +28,8 @@
 		dss.h bignum.h signkey.h rsa.h random.h service.h auth.h authpasswd.h \
 		debug.h channel.h chansession.h debug.h config.h queue.h sshpty.h \
 		termcodes.h gendss.h genrsa.h authpubkey.h runopts.h includes.h \
-		loginrec.h atomicio.h x11fwd.h agentfwd.h localtcpfwd.h compat.h \
-		remotetcpfwd.h tcpfwd.h
+		loginrec.h atomicio.h x11fwd.h agentfwd.h tcpfwd-direct.h compat.h \
+		tcpfwd-remote.h listener.h
 
 ALLOBJS=$(OBJS) $(DROPBEARKEYOBJS) $(DROPBEAROBJS)
 
--- a/channel.h	Wed Jun 02 04:59:49 2004 +0000
+++ b/channel.h	Thu Jun 03 16:45:53 2004 +0000
@@ -114,8 +114,7 @@
 void recv_msg_channel_eof();
 
 #ifdef USE_LISTENERS
-int send_msg_channel_open_init(int fd, struct ChanType *type,
-		const char * typestring);
+int send_msg_channel_open_init(int fd, const struct ChanType *type);
 void recv_msg_channel_open_confirmation();
 void recv_msg_channel_open_failure();
 #endif
--- a/chansession.h	Wed Jun 02 04:59:49 2004 +0000
+++ b/chansession.h	Thu Jun 03 16:45:53 2004 +0000
@@ -27,6 +27,7 @@
 
 #include "loginrec.h"
 #include "channel.h"
+#include "listener.h"
 
 struct ChanSess {
 
@@ -47,7 +48,7 @@
 	unsigned char exitcore;
 	
 #ifndef DISABLE_X11FWD
-	int x11fd; /* set to -1 to indicate forwarding not established */
+	struct Listener * x11listener;
 	int x11port;
 	char * x11authprot;
 	char * x11authcookie;
@@ -56,7 +57,7 @@
 #endif
 
 #ifndef DISABLE_AGENTFWD
-	int agentfd;
+	struct Listener * agentlistener;
 	char * agentfile;
 	char * agentdir;
 #endif
--- a/common-channel.c	Wed Jun 02 04:59:49 2004 +0000
+++ b/common-channel.c	Thu Jun 03 16:45:53 2004 +0000
@@ -32,9 +32,9 @@
 #include "dbutil.h"
 #include "channel.h"
 #include "ssh.h"
-#include "localtcpfwd.h"
-#include "remotetcpfwd.h"
-#include "tcpfwd.h"
+#include "tcpfwd-direct.h"
+#include "tcpfwd-remote.h"
+#include "listener.h"
 
 static void send_msg_channel_open_failure(unsigned int remotechan, int reason,
 		const unsigned char *text, const unsigned char *lang);
@@ -70,8 +70,8 @@
 
 	ses.chantypes = chantypes;
 
-#ifdef USING_TCP_LISTENERS
-	tcp_fwd_initialise();
+#ifdef USING_LISTENERS
+	listeners_initialise();
 #endif
 
 }
@@ -219,9 +219,9 @@
 
 	} /* foreach channel */
 
-	/* Not channel specific */
-#ifdef USING_TCP_LISTENERS
-		handle_tcp_fwd(readfd);
+	/* Listeners such as TCP, X11, agent-auth */
+#ifdef USING_LISTENERS
+	handle_listeners(readfd);
 #endif
 }
 
@@ -429,8 +429,8 @@
 
 	} /* foreach channel */
 
-#ifdef USING_TCP_LISTENERS
-	set_tcp_fwd_fds(readfd);
+#ifdef USING_LISTENERS
+	set_listener_fds(readfd);
 #endif
 
 }
@@ -895,8 +895,7 @@
  * options, with the calling function calling encrypt_packet() after
  * completion. It is mandatory for the caller to encrypt_packet() if
  * DROPBEAR_SUCCESS is returned */
-int send_msg_channel_open_init(int fd, struct ChanType *type,
-		const char * typestring) {
+int send_msg_channel_open_init(int fd, const struct ChanType *type) {
 
 	struct Channel* chan;
 
@@ -920,7 +919,7 @@
 	CHECKCLEARTOWRITE();
 
 	buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_OPEN);
-	buf_putstring(ses.writepayload, typestring, strlen(typestring));
+	buf_putstring(ses.writepayload, type->name, strlen(type->name));
 	buf_putint(ses.writepayload, chan->index);
 	buf_putint(ses.writepayload, RECV_MAXWINDOW);
 	buf_putint(ses.writepayload, RECV_MAXPACKET);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/listener.c	Thu Jun 03 16:45:53 2004 +0000
@@ -0,0 +1,123 @@
+#include "includes.h"
+#include "listener.h"
+#include "session.h"
+#include "dbutil.h"
+
+void listener_initialise() {
+
+	/* just one slot to start with */
+	ses.listeners = (struct Listener**)m_malloc(sizeof(struct Listener*));
+	ses.listensize = 1;
+	ses.listeners[0] = NULL;
+
+}
+
+void set_listener_fds(fd_set * readfds) {
+
+	unsigned int i;
+	struct Listener *listener;
+
+	/* check each in turn */
+	for (i = 0; i < ses.listensize; i++) {
+		listener = ses.listeners[i];
+		if (listener != NULL) {
+			FD_SET(listener->sock, readfds);
+		}
+	}
+}
+
+
+void handle_listeners(fd_set * readfds) {
+
+	unsigned int i;
+	struct Listener *listener;
+
+	/* check each in turn */
+	for (i = 0; i < ses.listensize; i++) {
+		listener = ses.listeners[i];
+		if (listener != NULL) {
+			if (FD_ISSET(listener->sock, readfds)) {
+				listener->accepter(listener);
+			}
+		}
+	}
+}
+
+
+/* accepter(int fd, void* typedata) is a function to accept connections, 
+ * cleanup(void* typedata) happens when cleaning up */
+struct Listener* new_listener(int sock, int type, void* typedata, 
+		void (*accepter)(struct Listener*), 
+		void (*cleanup)(struct Listener*)) {
+
+	unsigned int i, j;
+	struct Listener *newlisten = NULL;
+	/* try get a new structure to hold it */
+	for (i = 0; i < ses.listensize; i++) {
+		if (ses.listeners[i] == NULL) {
+			break;
+		}
+	}
+
+	/* or create a new one */
+	if (i == ses.listensize) {
+		if (ses.listensize > MAX_LISTENERS) {
+			TRACE(("leave newlistener: too many already"));
+			close(sock);
+			return NULL;
+		}
+		
+		ses.listeners = (struct Listener**)m_realloc(ses.listeners,
+				(ses.listensize+LISTENER_EXTEND_SIZE)
+				*sizeof(struct Listener*));
+
+		ses.listensize += LISTENER_EXTEND_SIZE;
+
+		for (j = i; j < ses.listensize; j++) {
+			ses.listeners[j] = NULL;
+		}
+	}
+
+	ses.maxfd = MAX(ses.maxfd, sock);
+
+	newlisten = (struct Listener*)m_malloc(sizeof(struct Listener));
+	newlisten->index = i;
+	newlisten->type = type;
+	newlisten->typedata = typedata;
+	newlisten->sock = sock;
+	newlisten->accepter = accepter;
+	newlisten->cleanup = cleanup;
+
+	ses.listeners[i] = newlisten;
+	return newlisten;
+}
+
+/* Return the first listener which matches the type-specific comparison
+ * function. Particularly needed for global requests, like tcp */
+struct Listener * get_listener(int type, void* typedata,
+		int (*match)(void*, void*)) {
+
+	unsigned int i;
+	struct Listener* listener;
+
+	for (i = 0, listener = ses.listeners[i]; i < ses.listensize; i++) {
+		if (listener->type == type
+				&& match(typedata, listener->typedata)) {
+			return listener;
+		}
+	}
+
+	return NULL;
+}
+
+void remove_listener(struct Listener* listener) {
+
+	if (listener->cleanup) {
+		listener->cleanup(listener);
+	}
+
+	close(listener->sock);
+	ses.listeners[listener->index] = NULL;
+	m_free(listener);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/listener.h	Thu Jun 03 16:45:53 2004 +0000
@@ -0,0 +1,37 @@
+#ifndef _LISTENER_H
+#define _LISTENER_H
+
+#define MAX_LISTENERS 20
+#define LISTENER_EXTEND_SIZE 1
+
+struct Listener {
+
+	int sock;
+
+	int index; /* index in the array of listeners */
+
+	void (*accepter)(struct Listener*);
+	void (*cleanup)(struct Listener*);
+
+	int type; /* CHANNEL_ID_X11, CHANNEL_ID_AGENT, 
+				 CHANNEL_ID_TCPDIRECT (for clients),
+				 CHANNEL_ID_TCPFORWARDED (for servers) */
+
+	void *typedata;
+
+};
+
+void listener_initialise();
+void handle_listeners(fd_set * readfds);
+void set_listener_fds(fd_set * readfds);
+
+struct Listener* new_listener(int sock, int type, void* typedata, 
+		void (*accepter)(struct Listener*), 
+		void (*cleanup)(struct Listener*));
+
+struct Listener * get_listener(int type, void* typedata,
+		int (*match)(void*, void*));
+
+void remove_listener(struct Listener* listener);
+
+#endif /* _LISTENER_H */
--- a/localtcpfwd.c	Wed Jun 02 04:59:49 2004 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,155 +0,0 @@
-#include "includes.h"
-#include "session.h"
-#include "dbutil.h"
-#include "channel.h"
-#include "localtcpfwd.h"
-
-#ifndef DISABLE_LOCALTCPFWD
-static int newtcpdirect(struct Channel * channel);
-static int newtcp(const char * host, int port);
-
-const struct ChanType chan_tcpdirect = {
-	0, /* sepfds */
-	"direct-tcpip",
-	newtcpdirect, /* init */
-	NULL, /* checkclose */
-	NULL, /* reqhandler */
-	NULL /* closehandler */
-};
-
-
-
-/* Called upon creating a new direct tcp channel (ie we connect out to an
- * address */
-static int newtcpdirect(struct Channel * channel) {
-
-	unsigned char* desthost = NULL;
-	unsigned int destport;
-	unsigned char* orighost = NULL;
-	unsigned int origport;
-	int sock;
-	int len;
-	int ret = DROPBEAR_FAILURE;
-
-	if (ses.opts->nolocaltcp) {
-		TRACE(("leave newtcpdirect: local tcp forwarding disabled"));
-		goto out;
-	}
-
-	desthost = buf_getstring(ses.payload, &len);
-	if (len > MAX_HOST_LEN) {
-		TRACE(("leave newtcpdirect: desthost too long"));
-		goto out;
-	}
-
-	destport = buf_getint(ses.payload);
-	
-	orighost = buf_getstring(ses.payload, &len);
-	if (len > MAX_HOST_LEN) {
-		TRACE(("leave newtcpdirect: orighost too long"));
-		goto out;
-	}
-
-	origport = buf_getint(ses.payload);
-
-	/* best be sure */
-	if (origport > 65535 || destport > 65535) {
-		TRACE(("leave newtcpdirect: port > 65535"));
-		goto out;
-	}
-
-	sock = newtcp(desthost, destport);
-	if (sock < 0) {
-		TRACE(("leave newtcpdirect: sock failed"));
-		goto out;
-	}
-
-	ses.maxfd = MAX(ses.maxfd, sock);
-
-	/* Note that infd is actually the "outgoing" direction on the
-	 * tcp connection, vice versa for outfd.
-	 * We don't set outfd, that will get set after the connection's
-	 * progress succeeds */
-	channel->infd = sock;
-	channel->initconn = 1;
-	
-	ret = DROPBEAR_SUCCESS;
-
-out:
-	m_free(desthost);
-	m_free(orighost);
-	TRACE(("leave newtcpdirect: ret %d", ret));
-	return ret;
-}
-
-/* Initiate a new TCP connection - this is non-blocking, so the socket
- * returned will need to be checked for success when it is first written.
- * Similarities with OpenSSH's connect_to() are not coincidental.
- * Returns -1 on failure */
-static int newtcp(const char * host, int port) {
-
-	int sock = -1;
-	char portstring[6];
-	struct addrinfo *res = NULL, *ai;
-	int val;
-
-	struct addrinfo hints;
-
-	TRACE(("enter newtcp"));
-
-	memset(&hints, 0, sizeof(hints));
-	/* TCP, either ip4 or ip6 */
-	hints.ai_socktype = SOCK_STREAM;
-	hints.ai_family = PF_UNSPEC;
-
-	snprintf(portstring, sizeof(portstring), "%d", port);
-	if (getaddrinfo(host, portstring, &hints, &res) != 0) {
-		if (res) {
-			freeaddrinfo(res);
-		}
-		TRACE(("leave newtcp: failed getaddrinfo"));
-		return -1;
-	}
-
-	/* Use the first socket that works */
-	for (ai = res; ai != NULL; ai = ai->ai_next) {
-		
-		if (ai->ai_family != PF_INET && ai->ai_family != PF_INET6) {
-			continue;
-		}
-
-		sock = socket(ai->ai_family, SOCK_STREAM, 0);
-		if (sock < 0) {
-			TRACE(("TCP socket() failed"));
-			continue;
-		}
-
-		if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) {
-			close(sock);
-			TRACE(("TCP non-blocking failed"));
-			continue;
-		}
-
-		/* non-blocking, so it might return without success (EINPROGRESS) */
-		if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0) {
-			if (errno == EINPROGRESS) {
-				TRACE(("connect in progress"));
-			} else {
-				close(sock);
-				TRACE(("TCP connect failed"));
-				continue;
-			}
-		} 
-		break;
-	}
-
-	freeaddrinfo(res);
-	
-	if (ai == NULL) {
-		return -1;
-	}
-
-	setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void*)&val, sizeof(val));
-	return sock;
-}
-#endif /* DISABLE_LOCALTCPFWD */
--- a/localtcpfwd.h	Wed Jun 02 04:59:49 2004 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,34 +0,0 @@
-/*
- * Dropbear - a SSH2 server
- * 
- * Copyright (c) 2002,2003 Matt Johnston
- * All rights reserved.
- * 
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- * 
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- * 
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE. */
-#ifndef _LOCALTCPFWD_H_
-#define _LOCALTCPFWD_H_
-#ifndef DISABLE_LOCALTCPFWD
-
-#include "includes.h"
-#include "channel.h"
-
-extern const struct ChanType chan_tcpdirect;
-
-#endif
-#endif
--- a/options.h	Wed Jun 02 04:59:49 2004 +0000
+++ b/options.h	Thu Jun 03 16:45:53 2004 +0000
@@ -296,7 +296,7 @@
 #endif
 
 #ifndef ENABLE_LOCALTCPFWD
-#define DISABLE_LOCALTCPFWD
+#define DISABLE_TCPDIRECT
 #endif
 
 #ifndef ENABLE_REMOTETCPFWD
--- a/remotetcpfwd.c	Wed Jun 02 04:59:49 2004 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,302 +0,0 @@
-#include "includes.h"
-#include "ssh.h"
-#include "remotetcpfwd.h"
-#include "dbutil.h"
-#include "session.h"
-#include "buffer.h"
-#include "packet.h"
-#include "tcpfwd.h"
-
-#ifndef DISABLE_REMOTETCPFWD
-
-struct RemoteTCP {
-
-	unsigned char* addr;
-	unsigned int port;
-
-};
-
-static void send_msg_request_success();
-static void send_msg_request_failure();
-static int cancelremotetcp();
-static int remotetcpreq();
-static int newlistener(unsigned char* bindaddr, unsigned int port);
-static void acceptremote(struct TCPListener *listener);
-
-/* At the moment this is completely used for tcp code (with the name reflecting
- * that). If new request types are added, this should be replaced with code
- * similar to the request-switching in chansession.c */
-void recv_msg_global_request_remotetcp() {
-
-	unsigned char* reqname = NULL;
-	unsigned int namelen;
-	unsigned int wantreply = 0;
-	int ret = DROPBEAR_FAILURE;
-
-	TRACE(("enter recv_msg_global_request_remotetcp"));
-
-	if (ses.opts->noremotetcp) {
-		TRACE(("leave recv_msg_global_request_remotetcp: remote tcp forwarding disabled"));
-		goto out;
-	}
-
-	reqname = buf_getstring(ses.payload, &namelen);
-	wantreply = buf_getbyte(ses.payload);
-
-	if (namelen > MAXNAMLEN) {
-		TRACE(("name len is wrong: %d", namelen));
-		goto out;
-	}
-
-	if (strcmp("tcpip-forward", reqname) == 0) {
-		ret = remotetcpreq();
-	} else if (strcmp("cancel-tcpip-forward", reqname) == 0) {
-		ret = cancelremotetcp();
-	} else {
-		TRACE(("reqname isn't tcpip-forward: '%s'", reqname));
-	}
-
-out:
-	if (wantreply) {
-		if (ret == DROPBEAR_SUCCESS) {
-			send_msg_request_success();
-		} else {
-			send_msg_request_failure();
-		}
-	}
-
-	m_free(reqname);
-
-	TRACE(("leave recv_msg_global_request"));
-}
-
-static void acceptremote(struct TCPListener *listener) {
-
-	int fd;
-	struct sockaddr addr;
-	int len;
-	char ipstring[NI_MAXHOST], portstring[NI_MAXSERV];
-	struct RemoteTCP *tcpinfo = (struct RemoteTCP*)(listener->typedata);
-
-	len = sizeof(addr);
-
-	fd = accept(listener->sock, &addr, &len);
-	if (fd < 0) {
-		return;
-	}
-
-	if (getnameinfo(&addr, len, ipstring, sizeof(ipstring), portstring,
-				sizeof(portstring), NI_NUMERICHOST | NI_NUMERICSERV) != 0) {
-		return;
-	}
-
-	/* XXX XXX XXX - type here needs fixing */
-	if (send_msg_channel_open_init(fd, CHANNEL_ID_TCPFORWARDED, 
-				"forwarded-tcpip") == DROPBEAR_SUCCESS) {
-		buf_putstring(ses.writepayload, tcpinfo->addr,
-				strlen(tcpinfo->addr));
-		buf_putint(ses.writepayload, tcpinfo->port);
-		buf_putstring(ses.writepayload, ipstring, strlen(ipstring));
-		buf_putint(ses.writepayload, atol(portstring));
-		encrypt_packet();
-	}
-}
-
-static void cleanupremote(struct TCPListener *listener) {
-
-	struct RemoteTCP *tcpinfo = (struct RemoteTCP*)(listener->typedata);
-
-	m_free(tcpinfo->addr);
-	m_free(tcpinfo);
-}
-
-static void send_msg_request_success() {
-
-	CHECKCLEARTOWRITE();
-	buf_putbyte(ses.writepayload, SSH_MSG_REQUEST_SUCCESS);
-	encrypt_packet();
-
-}
-
-static void send_msg_request_failure() {
-
-	CHECKCLEARTOWRITE();
-	buf_putbyte(ses.writepayload, SSH_MSG_REQUEST_FAILURE);
-	encrypt_packet();
-
-}
-
-static int matchtcp(void* typedata1, void* typedata2) {
-
-	const struct RemoteTCP *info1 = (struct RemoteTCP*)typedata1;
-	const struct RemoteTCP *info2 = (struct RemoteTCP*)typedata2;
-
-	return info1->port == info2->port 
-			&& (strcmp(info1->addr, info2->addr) == 0);
-}
-
-static int cancelremotetcp() {
-
-	int ret = DROPBEAR_FAILURE;
-	unsigned char * bindaddr = NULL;
-	unsigned int addrlen;
-	unsigned int port;
-	struct TCPListener * listener = NULL;
-	struct RemoteTCP tcpinfo;
-
-	TRACE(("enter cancelremotetcp"));
-
-	bindaddr = buf_getstring(ses.payload, &addrlen);
-	if (addrlen > MAX_IP_LEN) {
-		TRACE(("addr len too long: %d", addrlen));
-		goto out;
-	}
-
-	port = buf_getint(ses.payload);
-
-	tcpinfo.addr = bindaddr;
-	tcpinfo.port = port;
-	listener = get_listener(CHANNEL_ID_TCPFORWARDED, &tcpinfo, matchtcp);
-	if (listener) {
-		remove_listener( listener );
-		ret = DROPBEAR_SUCCESS;
-	}
-
-out:
-	m_free(bindaddr);
-	TRACE(("leave cancelremotetcp"));
-	return ret;
-}
-
-static int remotetcpreq() {
-
-	int ret = DROPBEAR_FAILURE;
-	unsigned char * bindaddr = NULL;
-	unsigned int addrlen;
-	unsigned int port;
-
-	TRACE(("enter remotetcpreq"));
-
-	bindaddr = buf_getstring(ses.payload, &addrlen);
-	if (addrlen > MAX_IP_LEN) {
-		TRACE(("addr len too long: %d", addrlen));
-		goto out;
-	}
-
-	port = buf_getint(ses.payload);
-
-	if (port == 0) {
-		dropbear_log(LOG_INFO, "Server chosen tcpfwd ports are unsupported");
-		goto out;
-	}
-
-	if (port < 1 || port > 65535) {
-		TRACE(("invalid port: %d", port));
-		goto out;
-	}
-
-	/* XXX matt - server change
-	if (ses.authstate.pw->pw_uid != 0
-			&& port < IPPORT_RESERVED) {
-		TRACE(("can't assign port < 1024 for non-root"));
-		goto out;
-	}
-	*/
-
-	ret = newlistener(bindaddr, port);
-
-out:
-	if (ret == DROPBEAR_FAILURE) {
-		/* we only free it if a listener wasn't created, since the listener
-		 * has to remember it if it's to be cancelled */
-		m_free(bindaddr);
-	}
-	TRACE(("leave remotetcpreq"));
-	return ret;
-}
-
-static int newlistener(unsigned char* bindaddr, unsigned int port) {
-
-	struct RemoteTCP * tcpinfo = NULL;
-	char portstring[6]; /* "65535\0" */
-	struct addrinfo *res = NULL, *ai = NULL;
-	struct addrinfo hints;
-	int sock = -1;
-	int ret = DROPBEAR_FAILURE;
-
-	TRACE(("enter newlistener"));
-
-	/* first we try to bind, so don't need to do so much cleanup on failure */
-	snprintf(portstring, sizeof(portstring), "%d", port);
-	memset(&hints, 0x0, sizeof(hints));
-	hints.ai_socktype = SOCK_STREAM;
-	hints.ai_family = PF_INET;
-	hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
-
-	if (getaddrinfo(bindaddr, portstring, &hints, &res) < 0) {
-		TRACE(("leave newlistener: getaddrinfo failed: %s",
-					strerror(errno)));
-		goto done;
-	}
-
-	/* find the first one which works */
-	for (ai = res; ai != NULL; ai = ai->ai_next) {
-		if (ai->ai_family != PF_INET && ai->ai_family != PF_INET6) {
-			continue;
-		}
-
-		sock = socket(ai->ai_family, SOCK_STREAM, 0);
-		if (sock < 0) {
-			TRACE(("socket failed: %s", strerror(errno)));
-			goto fail;
-		}
-
-		if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) {
-			TRACE(("bind failed: %s", strerror(errno)));
-			goto fail;
-		}
-
-		if (listen(sock, 20) < 0) {
-			TRACE(("listen failed: %s", strerror(errno)));
-			goto fail;
-		}
-
-		if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) {
-			TRACE(("fcntl nonblocking failed: %s", strerror(errno)));
-			goto fail;
-		}
-
-		/* success */
-		break;
-
-fail:
-		close(sock);
-	}
-
-
-	if (ai == NULL) {
-		TRACE(("no successful sockets"));
-		goto done;
-	}
-
-	tcpinfo = (struct RemoteTCP*)m_malloc(sizeof(struct RemoteTCP));
-	tcpinfo->addr = bindaddr;
-	tcpinfo->port = port;
-
-	ret = new_fwd(sock, CHANNEL_ID_TCPFORWARDED, tcpinfo, 
-			acceptremote, cleanupremote);
-
-	if (ret == DROPBEAR_FAILURE) {
-		m_free(tcpinfo);
-	}
-
-done:
-	if (res) {
-		freeaddrinfo(res);
-	}
-	
-	TRACE(("leave newlistener"));
-	return ret;
-}
-
-#endif /* DISABLE_REMOTETCPFWD */
--- a/remotetcpfwd.h	Wed Jun 02 04:59:49 2004 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,6 +0,0 @@
-#ifndef _REMOTETCPFWD_H
-#define _REMOTETCPFWD_H
-
-void recv_msg_global_request_remotetcp();
-
-#endif /* _REMOTETCPFWD_H */
--- a/session.h	Wed Jun 02 04:59:49 2004 +0000
+++ b/session.h	Thu Jun 03 16:45:53 2004 +0000
@@ -33,7 +33,7 @@
 #include "channel.h"
 #include "queue.h"
 #include "runopts.h"
-#include "remotetcpfwd.h"
+#include "listener.h"
 
 extern int sessinitdone; /* Is set to 0 somewhere */
 extern int exitflag;
@@ -139,8 +139,8 @@
 	
 	/* TCP forwarding - where manage listeners */
 #ifndef DISABLE_REMOTETCPFWD
-	struct TCPListener ** tcplisteners;
-	unsigned int tcplistensize;
+	struct Listener ** listeners;
+	unsigned int listensize;
 #endif
 
 };
--- a/svr-chansession.c	Wed Jun 02 04:59:49 2004 +0000
+++ b/svr-chansession.c	Thu Jun 03 16:45:53 2004 +0000
@@ -201,13 +201,13 @@
 	channel->typedata = chansess;
 
 #ifndef DISABLE_X11FWD
-	chansess->x11fd = -1;
+	chansess->x11listener = NULL;
 	chansess->x11authprot = NULL;
 	chansess->x11authcookie = NULL;
 #endif
 
 #ifndef DISABLE_AGENTFWD
-	chansess->agentfd = -1;
+	chansess->agentlistener = NULL;
 	chansess->agentfile = NULL;
 	chansess->agentdir = NULL;
 #endif
@@ -881,7 +881,7 @@
 	/* only reached on error */
 	dropbear_exit("child failed");
 }
-	
+
 const struct ChanType svrchansess = {
 	0, /* sepfds */
 	"session", /* name */
--- a/svr-session.c	Wed Jun 02 04:59:49 2004 +0000
+++ b/svr-session.c	Thu Jun 03 16:45:53 2004 +0000
@@ -35,7 +35,7 @@
 #include "channel.h"
 #include "chansession.h"
 #include "atomicio.h"
-#include "localtcpfwd.h"
+#include "tcpfwd-direct.h"
 
 static void svr_remoteclosed();
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tcpfwd-direct.c	Thu Jun 03 16:45:53 2004 +0000
@@ -0,0 +1,154 @@
+#include "includes.h"
+#include "session.h"
+#include "dbutil.h"
+#include "channel.h"
+#include "tcpfwd-direct.h"
+
+#ifndef DISABLE_TCPFWD_DIRECT
+static int newtcpdirect(struct Channel * channel);
+static int newtcp(const char * host, int port);
+
+const struct ChanType chan_tcpdirect = {
+	0, /* sepfds */
+	"direct-tcpip",
+	newtcpdirect, /* init */
+	NULL, /* checkclose */
+	NULL, /* reqhandler */
+	NULL /* closehandler */
+};
+
+
+/* Called upon creating a new direct tcp channel (ie we connect out to an
+ * address */
+static int newtcpdirect(struct Channel * channel) {
+
+	unsigned char* desthost = NULL;
+	unsigned int destport;
+	unsigned char* orighost = NULL;
+	unsigned int origport;
+	int sock;
+	int len;
+	int ret = DROPBEAR_FAILURE;
+
+	if (ses.opts->nolocaltcp) {
+		TRACE(("leave newtcpdirect: local tcp forwarding disabled"));
+		goto out;
+	}
+
+	desthost = buf_getstring(ses.payload, &len);
+	if (len > MAX_HOST_LEN) {
+		TRACE(("leave newtcpdirect: desthost too long"));
+		goto out;
+	}
+
+	destport = buf_getint(ses.payload);
+	
+	orighost = buf_getstring(ses.payload, &len);
+	if (len > MAX_HOST_LEN) {
+		TRACE(("leave newtcpdirect: orighost too long"));
+		goto out;
+	}
+
+	origport = buf_getint(ses.payload);
+
+	/* best be sure */
+	if (origport > 65535 || destport > 65535) {
+		TRACE(("leave newtcpdirect: port > 65535"));
+		goto out;
+	}
+
+	sock = newtcp(desthost, destport);
+	if (sock < 0) {
+		TRACE(("leave newtcpdirect: sock failed"));
+		goto out;
+	}
+
+	ses.maxfd = MAX(ses.maxfd, sock);
+
+	/* Note that infd is actually the "outgoing" direction on the
+	 * tcp connection, vice versa for outfd.
+	 * We don't set outfd, that will get set after the connection's
+	 * progress succeeds */
+	channel->infd = sock;
+	channel->initconn = 1;
+	
+	ret = DROPBEAR_SUCCESS;
+
+out:
+	m_free(desthost);
+	m_free(orighost);
+	TRACE(("leave newtcpdirect: ret %d", ret));
+	return ret;
+}
+
+/* Initiate a new TCP connection - this is non-blocking, so the socket
+ * returned will need to be checked for success when it is first written.
+ * Similarities with OpenSSH's connect_to() are not coincidental.
+ * Returns -1 on failure */
+static int newtcp(const char * host, int port) {
+
+	int sock = -1;
+	char portstring[6];
+	struct addrinfo *res = NULL, *ai;
+	int val;
+
+	struct addrinfo hints;
+
+	TRACE(("enter newtcp"));
+
+	memset(&hints, 0, sizeof(hints));
+	/* TCP, either ip4 or ip6 */
+	hints.ai_socktype = SOCK_STREAM;
+	hints.ai_family = PF_UNSPEC;
+
+	snprintf(portstring, sizeof(portstring), "%d", port);
+	if (getaddrinfo(host, portstring, &hints, &res) != 0) {
+		if (res) {
+			freeaddrinfo(res);
+		}
+		TRACE(("leave newtcp: failed getaddrinfo"));
+		return -1;
+	}
+
+	/* Use the first socket that works */
+	for (ai = res; ai != NULL; ai = ai->ai_next) {
+		
+		if (ai->ai_family != PF_INET && ai->ai_family != PF_INET6) {
+			continue;
+		}
+
+		sock = socket(ai->ai_family, SOCK_STREAM, 0);
+		if (sock < 0) {
+			TRACE(("TCP socket() failed"));
+			continue;
+		}
+
+		if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) {
+			close(sock);
+			TRACE(("TCP non-blocking failed"));
+			continue;
+		}
+
+		/* non-blocking, so it might return without success (EINPROGRESS) */
+		if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0) {
+			if (errno == EINPROGRESS) {
+				TRACE(("connect in progress"));
+			} else {
+				close(sock);
+				TRACE(("TCP connect failed"));
+				continue;
+			}
+		} 
+		break;
+	}
+
+	freeaddrinfo(res);
+	
+	if (ai == NULL) {
+		return -1;
+	}
+
+	setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void*)&val, sizeof(val));
+	return sock;
+}
+#endif /* DISABLE_TCPFWD_DIRECT */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tcpfwd-direct.h	Thu Jun 03 16:45:53 2004 +0000
@@ -0,0 +1,34 @@
+/*
+ * Dropbear - a SSH2 server
+ * 
+ * Copyright (c) 2002,2003 Matt Johnston
+ * All rights reserved.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE. */
+#ifndef _TCPFWD_DIRECT_H_
+#define _TCPFWD_DIRECT_H_
+#ifndef DISABLE_TCFWD_DIRECT
+
+#include "includes.h"
+#include "channel.h"
+
+extern const struct ChanType chan_tcpdirect;
+
+#endif
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tcpfwd-remote.c	Thu Jun 03 16:45:53 2004 +0000
@@ -0,0 +1,319 @@
+#include "includes.h"
+#include "ssh.h"
+#include "tcpfwd-remote.h"
+#include "dbutil.h"
+#include "session.h"
+#include "buffer.h"
+#include "packet.h"
+#include "listener.h"
+
+#ifndef DISABLE_REMOTETCPFWD
+
+struct RemoteTCP {
+
+	unsigned char* addr;
+	unsigned int port;
+
+};
+
+static void send_msg_request_success();
+static void send_msg_request_failure();
+static int cancelremotetcp();
+static int remotetcpreq();
+static int listen_tcpfwd(unsigned char* bindaddr, unsigned int port);
+static void acceptremote(struct Listener *listener);
+
+/* At the moment this is completely used for tcp code (with the name reflecting
+ * that). If new request types are added, this should be replaced with code
+ * similar to the request-switching in chansession.c */
+void recv_msg_global_request_remotetcp() {
+
+	unsigned char* reqname = NULL;
+	unsigned int namelen;
+	unsigned int wantreply = 0;
+	int ret = DROPBEAR_FAILURE;
+
+	TRACE(("enter recv_msg_global_request_remotetcp"));
+
+	if (ses.opts->noremotetcp) {
+		TRACE(("leave recv_msg_global_request_remotetcp: remote tcp forwarding disabled"));
+		goto out;
+	}
+
+	reqname = buf_getstring(ses.payload, &namelen);
+	wantreply = buf_getbyte(ses.payload);
+
+	if (namelen > MAXNAMLEN) {
+		TRACE(("name len is wrong: %d", namelen));
+		goto out;
+	}
+
+	if (strcmp("tcpip-forward", reqname) == 0) {
+		ret = remotetcpreq();
+	} else if (strcmp("cancel-tcpip-forward", reqname) == 0) {
+		ret = cancelremotetcp();
+	} else {
+		TRACE(("reqname isn't tcpip-forward: '%s'", reqname));
+	}
+
+out:
+	if (wantreply) {
+		if (ret == DROPBEAR_SUCCESS) {
+			send_msg_request_success();
+		} else {
+			send_msg_request_failure();
+		}
+	}
+
+	m_free(reqname);
+
+	TRACE(("leave recv_msg_global_request"));
+}
+
+static const struct ChanType chan_tcpremote = {
+	0, /* sepfds */
+	"forwarded-tcpip",
+	NULL,
+	NULL,
+	NULL,
+	NULL
+};
+
+
+static void acceptremote(struct Listener *listener) {
+
+	int fd;
+	struct sockaddr addr;
+	int len;
+	char ipstring[NI_MAXHOST], portstring[NI_MAXSERV];
+	struct RemoteTCP *tcpinfo = (struct RemoteTCP*)(listener->typedata);
+
+	len = sizeof(addr);
+
+	fd = accept(listener->sock, &addr, &len);
+	if (fd < 0) {
+		return;
+	}
+
+	if (getnameinfo(&addr, len, ipstring, sizeof(ipstring), portstring,
+				sizeof(portstring), NI_NUMERICHOST | NI_NUMERICSERV) != 0) {
+		return;
+	}
+
+	if (send_msg_channel_open_init(fd, &chan_tcpremote) == DROPBEAR_SUCCESS) {
+
+		buf_putstring(ses.writepayload, tcpinfo->addr,
+				strlen(tcpinfo->addr));
+		buf_putint(ses.writepayload, tcpinfo->port);
+		buf_putstring(ses.writepayload, ipstring, strlen(ipstring));
+		buf_putint(ses.writepayload, atol(portstring));
+		encrypt_packet();
+
+	} else {
+		/* XXX debug? */
+		close(fd);
+	}
+}
+
+static void cleanupremote(struct Listener *listener) {
+
+	struct RemoteTCP *tcpinfo = (struct RemoteTCP*)(listener->typedata);
+
+	m_free(tcpinfo->addr);
+	m_free(tcpinfo);
+}
+
+static void send_msg_request_success() {
+
+	CHECKCLEARTOWRITE();
+	buf_putbyte(ses.writepayload, SSH_MSG_REQUEST_SUCCESS);
+	encrypt_packet();
+
+}
+
+static void send_msg_request_failure() {
+
+	CHECKCLEARTOWRITE();
+	buf_putbyte(ses.writepayload, SSH_MSG_REQUEST_FAILURE);
+	encrypt_packet();
+
+}
+
+static int matchtcp(void* typedata1, void* typedata2) {
+
+	const struct RemoteTCP *info1 = (struct RemoteTCP*)typedata1;
+	const struct RemoteTCP *info2 = (struct RemoteTCP*)typedata2;
+
+	return info1->port == info2->port 
+			&& (strcmp(info1->addr, info2->addr) == 0);
+}
+
+static int cancelremotetcp() {
+
+	int ret = DROPBEAR_FAILURE;
+	unsigned char * bindaddr = NULL;
+	unsigned int addrlen;
+	unsigned int port;
+	struct Listener * listener = NULL;
+	struct RemoteTCP tcpinfo;
+
+	TRACE(("enter cancelremotetcp"));
+
+	bindaddr = buf_getstring(ses.payload, &addrlen);
+	if (addrlen > MAX_IP_LEN) {
+		TRACE(("addr len too long: %d", addrlen));
+		goto out;
+	}
+
+	port = buf_getint(ses.payload);
+
+	tcpinfo.addr = bindaddr;
+	tcpinfo.port = port;
+	listener = get_listener(CHANNEL_ID_TCPFORWARDED, &tcpinfo, matchtcp);
+	if (listener) {
+		remove_listener( listener );
+		ret = DROPBEAR_SUCCESS;
+	}
+
+out:
+	m_free(bindaddr);
+	TRACE(("leave cancelremotetcp"));
+	return ret;
+}
+
+static int remotetcpreq() {
+
+	int ret = DROPBEAR_FAILURE;
+	unsigned char * bindaddr = NULL;
+	unsigned int addrlen;
+	unsigned int port;
+
+	TRACE(("enter remotetcpreq"));
+
+	bindaddr = buf_getstring(ses.payload, &addrlen);
+	if (addrlen > MAX_IP_LEN) {
+		TRACE(("addr len too long: %d", addrlen));
+		goto out;
+	}
+
+	port = buf_getint(ses.payload);
+
+	if (port == 0) {
+		dropbear_log(LOG_INFO, "Server chosen tcpfwd ports are unsupported");
+		goto out;
+	}
+
+	if (port < 1 || port > 65535) {
+		TRACE(("invalid port: %d", port));
+		goto out;
+	}
+
+	/* XXX matt - server change
+	if (ses.authstate.pw->pw_uid != 0
+			&& port < IPPORT_RESERVED) {
+		TRACE(("can't assign port < 1024 for non-root"));
+		goto out;
+	}
+	*/
+
+	ret = listen_tcpfwd(bindaddr, port);
+
+out:
+	if (ret == DROPBEAR_FAILURE) {
+		/* we only free it if a listener wasn't created, since the listener
+		 * has to remember it if it's to be cancelled */
+		m_free(bindaddr);
+	}
+	TRACE(("leave remotetcpreq"));
+	return ret;
+}
+
+static int listen_tcpfwd(unsigned char* bindaddr, unsigned int port) {
+
+	struct RemoteTCP * tcpinfo = NULL;
+	char portstring[6]; /* "65535\0" */
+	struct addrinfo *res = NULL, *ai = NULL;
+	struct addrinfo hints;
+	int sock = -1;
+	struct Listener *listener = NULL;
+
+	TRACE(("enter listen_tcpfwd"));
+
+	/* first we try to bind, so don't need to do so much cleanup on failure */
+	snprintf(portstring, sizeof(portstring), "%d", port);
+	memset(&hints, 0x0, sizeof(hints));
+	hints.ai_socktype = SOCK_STREAM;
+	hints.ai_family = PF_INET;
+	hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
+
+	if (getaddrinfo(bindaddr, portstring, &hints, &res) < 0) {
+		TRACE(("leave listen_tcpfwd: getaddrinfo failed: %s",
+					strerror(errno)));
+		goto done;
+	}
+
+	/* find the first one which works */
+	for (ai = res; ai != NULL; ai = ai->ai_next) {
+		if (ai->ai_family != PF_INET && ai->ai_family != PF_INET6) {
+			continue;
+		}
+
+		sock = socket(ai->ai_family, SOCK_STREAM, 0);
+		if (sock < 0) {
+			TRACE(("socket failed: %s", strerror(errno)));
+			goto fail;
+		}
+
+		if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) {
+			TRACE(("bind failed: %s", strerror(errno)));
+			goto fail;
+		}
+
+		if (listen(sock, 20) < 0) {
+			TRACE(("listen failed: %s", strerror(errno)));
+			goto fail;
+		}
+
+		if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) {
+			TRACE(("fcntl nonblocking failed: %s", strerror(errno)));
+			goto fail;
+		}
+
+		/* success */
+		break;
+
+fail:
+		close(sock);
+	}
+
+
+	if (ai == NULL) {
+		TRACE(("no successful sockets"));
+		goto done;
+	}
+
+	tcpinfo = (struct RemoteTCP*)m_malloc(sizeof(struct RemoteTCP));
+	tcpinfo->addr = bindaddr;
+	tcpinfo->port = port;
+
+	listener = new_listener(sock, CHANNEL_ID_TCPFORWARDED, tcpinfo, 
+			acceptremote, cleanupremote);
+
+	if (listener == NULL) {
+		m_free(tcpinfo);
+	}
+
+done:
+	if (res) {
+		freeaddrinfo(res);
+	}
+	
+	TRACE(("leave listen_tcpfwd"));
+	if (listener == NULL) {
+		return DROPBEAR_FAILURE;
+	} else {
+		return DROPBEAR_SUCCESS;
+	}
+}
+
+#endif /* DISABLE_REMOTETCPFWD */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tcpfwd-remote.h	Thu Jun 03 16:45:53 2004 +0000
@@ -0,0 +1,6 @@
+#ifndef _REMOTETCPFWD_H
+#define _REMOTETCPFWD_H
+
+void recv_msg_global_request_remotetcp();
+
+#endif /* _REMOTETCPFWD_H */
--- a/tcpfwd.c	Wed Jun 02 04:59:49 2004 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,124 +0,0 @@
-#include "includes.h"
-#include "tcpfwd.h"
-#include "session.h"
-#include "dbutil.h"
-
-void tcp_fwd_initialise() {
-
-	/* just one slot to start with */
-	ses.tcplisteners = 
-		(struct TCPListener**)m_malloc(sizeof(struct TCPListener*));
-	ses.tcplistensize = 1;
-	ses.tcplisteners[0] = NULL;
-
-}
-
-void set_tcp_fwd_fds(fd_set * readfds) {
-
-	unsigned int i;
-	struct TCPListener *listener;
-
-	/* check each in turn */
-	for (i = 0; i < ses.tcplistensize; i++) {
-		listener = ses.tcplisteners[i];
-		if (listener != NULL) {
-			FD_SET(listener->sock, readfds);
-		}
-	}
-}
-
-
-void handle_tcp_fwd(fd_set * readfds) {
-
-	unsigned int i;
-	struct TCPListener *listener;
-
-	/* check each in turn */
-	for (i = 0; i < ses.tcplistensize; i++) {
-		listener = ses.tcplisteners[i];
-		if (listener != NULL) {
-			if (FD_ISSET(listener->sock, readfds)) {
-				listener->accepter(listener);
-			}
-		}
-	}
-}
-
-
-/* accepter(int fd, void* typedata) is a function to accept connections, 
- * cleanup(void* typedata) happens when cleaning up */
-int new_fwd(int sock, int type, void* typedata, 
-		void (*accepter)(struct TCPListener*), 
-		void (*cleanup)(struct TCPListener*)) {
-
-	unsigned int i, j;
-	struct TCPListener *newtcp = NULL;
-	/* try get a new structure to hold it */
-	for (i = 0; i < ses.tcplistensize; i++) {
-		if (ses.tcplisteners[i] == NULL) {
-			break;
-		}
-	}
-
-	/* or create a new one */
-	if (i == ses.tcplistensize) {
-		if (ses.tcplistensize > MAX_TCPLISTENERS) {
-			TRACE(("leave newlistener: too many already"));
-			close(sock);
-			return DROPBEAR_FAILURE;
-		}
-		
-		ses.tcplisteners = (struct TCPListener**)m_realloc(ses.tcplisteners,
-				(ses.tcplistensize+TCP_EXTEND_SIZE)
-				*sizeof(struct TCPListener*));
-
-		ses.tcplistensize += TCP_EXTEND_SIZE;
-
-		for (j = i; j < ses.tcplistensize; j++) {
-			ses.tcplisteners[j] = NULL;
-		}
-	}
-
-	ses.maxfd = MAX(ses.maxfd, sock);
-
-	newtcp = (struct TCPListener*)m_malloc(sizeof(struct TCPListener));
-	newtcp->index = i;
-	newtcp->type = type;
-	newtcp->typedata = typedata;
-	newtcp->sock = sock;
-	newtcp->accepter = accepter;
-	newtcp->cleanup = cleanup;
-
-	ses.tcplisteners[i] = newtcp;
-	return DROPBEAR_SUCCESS;
-}
-
-/* Return the first listener which matches the type-specific comparison
- * function */
-struct TCPListener * get_listener(int type, void* typedata,
-		int (*match)(void*, void*)) {
-
-	unsigned int i;
-	struct TCPListener* listener;
-
-	for (i = 0, listener = ses.tcplisteners[i]; i < ses.tcplistensize; i++) {
-		if (listener->type == type
-				&& match(typedata, listener->typedata)) {
-			return listener;
-		}
-	}
-
-	return NULL;
-}
-
-void remove_listener(struct TCPListener* listener) {
-
-	if (listener->cleanup) {
-		listener->cleanup(listener);
-	}
-
-	close(listener->sock);
-	ses.tcplisteners[listener->index] = NULL;
-	m_free(listener);
-
-}
--- a/tcpfwd.h	Wed Jun 02 04:59:49 2004 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,37 +0,0 @@
-#ifndef _TCPFWD_H
-#define _TCPFWD_H
-
-#define MAX_TCPLISTENERS 20
-#define TCP_EXTEND_SIZE 1
-
-struct TCPListener {
-
-	int sock;
-
-	int index; /* index in the array of listeners */
-
-	void (*accepter)(struct TCPListener*);
-	void (*cleanup)(struct TCPListener*);
-
-	int type; /* CHANNEL_ID_X11, CHANNEL_ID_AGENT, 
-				 CHANNEL_ID_TCPDIRECT (for clients),
-				 CHANNEL_ID_TCPFORWARDED (for servers) */
-
-	void *typedata;
-
-};
-
-void tcp_fwd_initialise();
-void handle_tcp_fwd(fd_set * readfds);
-void set_tcp_fwd_fds(fd_set * readfds);
-
-int new_fwd(int sock, int type, void* typedata, 
-		void (*accepter)(struct TCPListener*), 
-		void (*cleanup)(struct TCPListener*));
-
-struct TCPListener * get_listener(int type, void* typedata,
-		int (*match)(void*, void*));
-
-void remove_listener(struct TCPListener* listener);
-
-#endif /* _TCPFWD_H */
--- a/x11fwd.c	Wed Jun 02 04:59:49 2004 +0000
+++ b/x11fwd.c	Thu Jun 03 16:45:53 2004 +0000
@@ -37,6 +37,8 @@
 #define X11BASEPORT 6000
 #define X11BINDBASE 6010
 
+static void x11accept(struct Listener* listener);
+static void x11cleanup(struct Listener *listener);
 static int bindport(int fd);
 static int send_msg_channel_open_x11(int fd, struct sockaddr_in* addr);
 
@@ -44,8 +46,10 @@
 /* returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
 int x11req(struct ChanSess * chansess) {
 
+	int fd;
+
 	/* we already have an x11 connection */
-	if (chansess->x11fd != -1) {
+	if (chansess->x11listener != NULL) {
 		return DROPBEAR_FAILURE;
 	}
 
@@ -55,62 +59,71 @@
 	chansess->x11screennum = buf_getint(ses.payload);
 
 	/* create listening socket */
-	chansess->x11fd = socket(PF_INET, SOCK_STREAM, 0);
-	if (chansess->x11fd < 0) {
+	fd = socket(PF_INET, SOCK_STREAM, 0);
+	if (fd < 0) {
 		goto fail;
 	}
 
 	/* allocate port and bind */
-	chansess->x11port = bindport(chansess->x11fd);
+	chansess->x11port = bindport(fd);
 	if (chansess->x11port < 0) {
 		goto fail;
 	}
 
 	/* listen */
-	if (listen(chansess->x11fd, 20) < 0) {
+	if (listen(fd, 20) < 0) {
 		goto fail;
 	}
 
 	/* set non-blocking */
-	if (fcntl(chansess->x11fd, F_SETFL, O_NONBLOCK) < 0) {
+	if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) {
 		goto fail;
 	}
 
-	/* channel.c's channel fd code will handle the socket now */
-
-	/* set the maxfd so that select() loop will notice it */
-	ses.maxfd = MAX(ses.maxfd, chansess->x11fd);
+	/* listener code will handle the socket now.
+	 * No cleanup handler needed, since listener_remove only happens
+	 * from our cleanup anyway */
+	chansess->x11listener = new_listener( fd, 0, chansess, x11accept, NULL);
+	if (chansess->x11listener == NULL) {
+		goto fail;
+	}
 
 	return DROPBEAR_SUCCESS;
 
 fail:
 	/* cleanup */
-	x11cleanup(chansess);
+	m_free(chansess->x11authprot);
+	m_free(chansess->x11authcookie);
+	close(fd);
 
 	return DROPBEAR_FAILURE;
 }
 
 /* accepts a new X11 socket */
 /* returns DROPBEAR_FAILURE or DROPBEAR_SUCCESS */
-int x11accept(struct ChanSess * chansess) {
+static void x11accept(struct Listener* listener) {
 
 	int fd;
 	struct sockaddr_in addr;
 	int len;
+	int ret;
 
 	len = sizeof(addr);
 
-	fd = accept(chansess->x11fd, (struct sockaddr*)&addr, &len);
+	fd = accept(listener->sock, (struct sockaddr*)&addr, &len);
 	if (fd < 0) {
-		return DROPBEAR_FAILURE;
+		return;
 	}
 
 	/* if single-connection we close it up */
-	if (chansess->x11singleconn) {
-		x11cleanup(chansess);
+	if (((struct ChanSess *)(listener->typedata))->x11singleconn) {
+		x11cleanup(listener);
 	}
 
-	return send_msg_channel_open_x11(fd, &addr);
+	ret = send_msg_channel_open_x11(fd, &addr);
+	if (ret == DROPBEAR_FAILURE) {
+		close(fd);
+	}
 }
 
 /* This is called after switching to the user, and sets up the xauth
@@ -121,7 +134,7 @@
 	FILE * authprog;
 	int val;
 
-	if (chansess->x11fd == -1) {
+	if (chansess->x11listener == NULL) {
 		return;
 	}
 
@@ -154,24 +167,31 @@
 	}
 }
 
-void x11cleanup(struct ChanSess * chansess) {
+static void x11cleanup(struct Listener *listener) {
 
-	if (chansess->x11fd == -1) {
-		return;
-	}
+	struct ChanSess *chansess = (struct ChanSess*)listener->typedata;
 
 	m_free(chansess->x11authprot);
 	m_free(chansess->x11authcookie);
-	close(chansess->x11fd);
-	chansess->x11fd = -1;
+	remove_listener(listener);
+	chansess->x11listener = NULL;
 }
 
+static const struct ChanType chan_x11 = {
+	0, /* sepfds */
+	"x11",
+	NULL, /* inithandler */
+	NULL, /* checkclose */
+	NULL, /* reqhandler */
+	NULL /* closehandler */
+};
+
+
 static int send_msg_channel_open_x11(int fd, struct sockaddr_in* addr) {
 
 	char* ipstring;
 
-	if (send_msg_channel_open_init(fd, CHANNEL_ID_X11, "x11") 
-			== DROPBEAR_SUCCESS) {
+	if (send_msg_channel_open_init(fd, &chan_x11) == DROPBEAR_SUCCESS) {
 		ipstring = inet_ntoa(addr->sin_addr);
 		buf_putstring(ses.writepayload, ipstring, strlen(ipstring));
 		buf_putint(ses.writepayload, addr->sin_port);
--- a/x11fwd.h	Wed Jun 02 04:59:49 2004 +0000
+++ b/x11fwd.h	Thu Jun 03 16:45:53 2004 +0000
@@ -30,8 +30,6 @@
 #include "channel.h"
 
 int x11req(struct ChanSess * chansess);
-int x11accept(struct ChanSess * chansess);
-void x11cleanup(struct ChanSess * chansess);
 void x11setauth(struct ChanSess *chansess);
 
 #endif /* DROPBEAR_X11FWD */