diff --git a/Makefile.in b/Makefile.in
index af24bb018fd653bdb7d1e605bb5df1ce8e05a2f7..68ef5d8216197900fbdfbe740e7ab0a7c40166af 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -46,8 +46,8 @@ HEADERS=options.h dbutil.h session.h packet.h algo.h ssh.h buffer.h kex.h \
 		dss.h bignum.h signkey.h rsa.h random.h service.h auth.h authpasswd.h \
 		debug.h channel.h chansession.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 tcp-accept.h compat.h \
-		tcp-connect.h listener.h
+		loginrec.h atomicio.h x11fwd.h agentfwd.h tcpfwd.h compat.h \
+		listener.h
 
 dropbearobjs=$(COMMONOBJS) $(CLISVROBJS) $(SVROBJS) 
 dbclientobjs=$(COMMONOBJS) $(CLISVROBJS) $(CLIOBJS)
diff --git a/cli-runopts.c b/cli-runopts.c
index 4c84cc834a7f1c0df030a69f7cc4d4731a905c94..26eb634578de02699f76affa23889b5a61b51b4d 100644
--- a/cli-runopts.c
+++ b/cli-runopts.c
@@ -28,6 +28,7 @@
 #include "buffer.h"
 #include "dbutil.h"
 #include "algo.h"
+#include "tcpfwd.h"
 
 cli_runopts cli_opts; /* GLOBAL */
 
@@ -36,6 +37,9 @@ static void parsehostname(char* userhostarg);
 #ifdef DROPBEAR_PUBKEY_AUTH
 static void loadidentityfile(const char* filename);
 #endif
+#ifdef ENABLE_CLI_ANYTCPFWD
+static void addforward(char* str, struct TCPFwdList** fwdlist);
+#endif
 
 static void printhelp() {
 
@@ -48,10 +52,10 @@ static void printhelp() {
 #ifdef DROPBEAR_PUBKEY_AUTH
 					"-i <identityfile>   (multiple allowed)\n"
 #endif
-#ifndef DISABLE_REMOTETCPFWD
+#ifdef ENABLE_CLI_LOCALTCPFWD
 					"-L <listenport:remotehsot:reportport> Local port forwarding\n"
 #endif
-#ifndef DISABLE_TCPFWD_DIRECT
+#ifdef ENABLE_CLI_REMOTETCPFWD
 					"-R <listenport:remotehost:remoteport> Remote port forwarding\n"
 #endif
 					,DROPBEAR_VERSION, cli_opts.progname);
@@ -65,15 +69,13 @@ void cli_getopts(int argc, char ** argv) {
 #ifdef DROPBEAR_PUBKEY_AUTH
 	int nextiskey = 0; /* A flag if the next argument is a keyfile */
 #endif
-#ifdef DROPBEAR_CLI_LOCALTCP
+#ifdef ENABLE_CLI_LOCALTCPFWD
 	int nextislocal = 0;
 #endif
-#ifdef DROPBEAR_CLI_REMOTETCP
+#ifdef ENABLE_CLI_REMOTETCPFWD
 	int nextisremote = 0;
 #endif
 
-
-
 	/* see printhelp() for options */
 	cli_opts.progname = argv[0];
 	cli_opts.remotehost = NULL;
@@ -84,11 +86,11 @@ void cli_getopts(int argc, char ** argv) {
 #ifdef DROPBEAR_PUBKEY_AUTH
 	cli_opts.pubkeys = NULL;
 #endif
-#ifdef DROPBEAR_CLI_LOCALTCP
-	cli_opts.localports = NULL;
+#ifdef ENABLE_CLI_LOCALTCPFWD
+	cli_opts.localfwds = NULL;
 #endif
-#ifdef DROPBEAR_CLI_REMOTETCP
-	cli_opts.remoteports = NULL;
+#ifdef ENABLE_CLI_REMOTETCPFWD
+	cli_opts.remotefwds = NULL;
 #endif
 	opts.nolocaltcp = 0;
 	opts.noremotetcp = 0;
@@ -106,6 +108,22 @@ void cli_getopts(int argc, char ** argv) {
 			nextiskey = 0;
 			continue;
 		}
+#endif
+#ifdef ENABLE_CLI_REMOTETCPFWD
+		if (nextisremote) {
+			TRACE(("nextisremote true"));
+			addforward(argv[i], &cli_opts.remotefwds);
+			nextisremote = 0;
+			continue;
+		}
+#endif
+#ifdef ENABLE_CLI_LOCALTCPFWD
+		if (nextislocal) {
+			TRACE(("nextislocal true"));
+			addforward(argv[i], &cli_opts.localfwds);
+			nextislocal = 0;
+			continue;
+		}
 #endif
 		if (next) {
 			/* The previous flag set a value to assign */
@@ -135,6 +153,16 @@ void cli_getopts(int argc, char ** argv) {
 				case 'T': /* don't want a pty */
 					cli_opts.wantpty = 0;
 					break;
+#ifdef ENABLE_CLI_LOCALTCPFWD
+				case 'L':
+					nextislocal = 1;
+					break;
+#endif
+#ifdef ENABLE_CLI_REMOTETCPFWD
+				case 'R':
+					nextisremote = 1;
+					break;
+#endif
 				default:
 					fprintf(stderr, "Unknown argument '%s'\n", argv[i]);
 					printhelp();
@@ -145,7 +173,7 @@ void cli_getopts(int argc, char ** argv) {
 			continue; /* next argument */
 
 		} else {
-			TRACE(("non-flag arg"));
+			TRACE(("non-flag arg: '%s'", argv[i]));
 
 			/* Either the hostname or commands */
 
@@ -226,10 +254,14 @@ static void loadidentityfile(const char* filename) {
 
 /* Parses a [user@]hostname argument. userhostarg is the argv[i] corresponding
  * - note that it will be modified */
-static void parsehostname(char* userhostarg) {
+static void parsehostname(char* orighostarg) {
 
 	uid_t uid;
 	struct passwd *pw = NULL; 
+	char *userhostarg = NULL;
+
+	/* We probably don't want to be editing argvs */
+	userhostarg = m_strdup(orighostarg);
 
 	cli_opts.remotehost = strchr(userhostarg, '@');
 	if (cli_opts.remotehost == NULL) {
@@ -257,3 +289,81 @@ static void parsehostname(char* userhostarg) {
 		dropbear_exit("Bad hostname");
 	}
 }
+
+#ifdef ENABLE_CLI_ANYTCPFWD
+/* Turn a "listenport:remoteaddr:remoteport" string into into a forwarding
+ * set, and add it to the forwarding list */
+static void addforward(char* origstr, struct TCPFwdList** fwdlist) {
+
+	char * listenport = NULL;
+	char * connectport = NULL;
+	char * connectaddr = NULL;
+	struct TCPFwdList* newfwd = NULL;
+	char * str = NULL;
+
+	TRACE(("enter addforward"));
+
+	/* We probably don't want to be editing argvs */
+	str = m_strdup(origstr);
+
+	listenport = str;
+
+	connectaddr = strchr(str, ':');
+	if (connectaddr == NULL) {
+		TRACE(("connectaddr == NULL"));
+		goto fail;
+	}
+
+	connectaddr[0] = '\0';
+	connectaddr++;
+
+	connectport = strchr(connectaddr, ':');
+	if (connectport == NULL) {
+		TRACE(("connectport == NULL"));
+		goto fail;
+	}
+
+	connectport[0] = '\0';
+	connectport++;
+
+	newfwd = (struct TCPFwdList*)m_malloc(sizeof(struct TCPFwdList));
+
+	/* Now we check the ports - note that the port ints are unsigned,
+	 * the check later only checks for >= MAX_PORT */
+	newfwd->listenport = strtol(listenport, NULL, 10);
+	if (errno != 0) {
+		TRACE(("bad listenport strtol"));
+		goto fail;
+	}
+
+	newfwd->connectport = strtol(connectport, NULL, 10);
+	if (errno != 0) {
+		TRACE(("bad connectport strtol"));
+		goto fail;
+	}
+
+	newfwd->connectaddr = connectaddr;
+
+	if (newfwd->listenport > 65535) {
+		TRACE(("listenport > 65535"));
+		goto badport;
+	}
+		
+	if (newfwd->connectport > 65535) {
+		TRACE(("connectport > 65535"));
+		goto badport;
+	}
+
+	newfwd->next = *fwdlist;
+	*fwdlist = newfwd;
+
+	TRACE(("leave addforward: done"));
+	return;
+
+fail:
+	dropbear_exit("Bad TCP forward '%s'", origstr);
+
+badport:
+	dropbear_exit("Bad TCP port in '%s'", origstr);
+}
+#endif
diff --git a/cli-session.c b/cli-session.c
index 22f7001bd33a59bca7586deaa3f836297248a098..318bc64e2afbd9de0ee594606956a6f27ad4a9e6 100644
--- a/cli-session.c
+++ b/cli-session.c
@@ -4,8 +4,7 @@
 #include "kex.h"
 #include "ssh.h"
 #include "packet.h"
-#include "tcp-accept.h"
-#include "tcp-connect.h"
+#include "tcpfwd.h"
 #include "channel.h"
 #include "random.h"
 #include "service.h"
@@ -45,8 +44,9 @@ static const packettype cli_packettypes[] = {
 };
 
 static const struct ChanType *cli_chantypes[] = {
-	/* &chan_tcpdirect etc, though need to only allow if we've requested
-	 * that forwarding */
+#ifdef ENABLE_CLI_REMOTETCPFWD
+	&cli_chan_tcpremote,
+#endif
 	NULL /* Null termination */
 };
 
@@ -178,6 +178,10 @@ static void cli_sessionloop() {
 			*/
 
 		case USERAUTH_SUCCESS_RCVD:
+#ifdef ENABLE_CLI_LOCALTCPFWD
+			TRACE(("recvd USERAUTH_SUCCESS_RCVD"));
+			setup_localtcp();
+#endif
 			cli_send_chansess_request();
 			TRACE(("leave cli_sessionloop: cli_send_chansess_request"));
 			cli_ses.state = SESSION_RUNNING;
@@ -223,7 +227,6 @@ static void cli_finished() {
 }
 
 
-
 /* called when the remote side closes the connection */
 static void cli_remoteclosed() {
 
diff --git a/cli-tcpfwd.c b/cli-tcpfwd.c
index 955ffab07d5a2aa7e8e66199333f8c089d795cc4..f32a53f6122ce30f4b6db495bfbc3b8ed75b231f 100644
--- a/cli-tcpfwd.c
+++ b/cli-tcpfwd.c
@@ -1,20 +1,54 @@
 #include "includes.h"
 #include "options.h"
-#include "tcp-accept.h"
-#include "tcp-connect.h"
+#include "dbutil.h"
+#include "tcpfwd.h"
 #include "channel.h"
+#include "runopts.h"
+#include "session.h"
+#include "ssh.h"
 
+static int cli_localtcp(unsigned int listenport, const char* remoteaddr,
+		unsigned int remoteport);
+static int newtcpforwarded(struct Channel * channel);
+
+const struct ChanType cli_chan_tcpremote = {
+	1, /* sepfds */
+	"forwarded-tcpip",
+	newtcpforwarded,
+	NULL,
+	NULL,
+	NULL
+};
 static const struct ChanType cli_chan_tcplocal = {
 	1, /* sepfds */
 	"direct-tcpip",
 	NULL,
 	NULL,
+	NULL,
 	NULL
 };
 
 void setup_localtcp() {
 
-	qv
+	int ret;
+
+	if (cli_opts.localfwds == NULL) {
+		TRACE(("cli_opts.localfwds == NULL"));
+	}
+
+	while (cli_opts.localfwds != NULL) {
+		ret = cli_localtcp(cli_opts.localfwds->listenport,
+				cli_opts.localfwds->connectaddr,
+				cli_opts.localfwds->connectport);
+		if (ret == DROPBEAR_FAILURE) {
+			dropbear_log(LOG_WARNING, "Failed local port forward %d:%s:%d",
+					cli_opts.localfwds->listenport,
+					cli_opts.localfwds->connectaddr,
+					cli_opts.localfwds->connectport);
+		}
+
+		cli_opts.localfwds = cli_opts.localfwds->next;
+	}
 
 }
 
@@ -22,6 +56,10 @@ static int cli_localtcp(unsigned int listenport, const char* remoteaddr,
 		unsigned int remoteport) {
 
 	struct TCPListener* tcpinfo = NULL;
+	int ret;
+
+	TRACE(("enter cli_localtcp: %d %s %d", listenport, remoteaddr,
+				remoteport));
 
 	tcpinfo = (struct TCPListener*)m_malloc(sizeof(struct TCPListener*));
 	tcpinfo->sendaddr = remoteaddr;
@@ -34,5 +72,91 @@ static int cli_localtcp(unsigned int listenport, const char* remoteaddr,
 	if (ret == DROPBEAR_FAILURE) {
 		m_free(tcpinfo);
 	}
+	TRACE(("leave cli_localtcp: %d", ret));
+	return ret;
+}
+
+static void send_msg_global_request_remotetcp(int port) {
+
+	TRACE(("enter send_msg_global_request_remotetcp"));
+
+	CHECKCLEARTOWRITE();
+	buf_putbyte(ses.writepayload, SSH_MSG_GLOBAL_REQUEST);
+	buf_putstring(ses.writepayload, "tcpip-forward", 13);
+	buf_putbyte(ses.writepayload, 0);
+	buf_putstring(ses.writepayload, "0.0.0.0", 7); /* TODO: IPv6? */
+	buf_putint(ses.writepayload, port);
+
+	encrypt_packet();
+
+	TRACE(("leave send_msg_global_request_remotetcp"));
+}
+
+void setup_remotetcp() {
+
+	struct TCPFwdList * iter = NULL;
+
+	if (cli_opts.remotefwds == NULL) {
+		TRACE(("cli_opts.remotefwds == NULL"));
+	}
+
+	iter = cli_opts.remotefwds;
+
+	while (iter != NULL) {
+		send_msg_global_request_remotetcp(iter->listenport);
+		iter = iter->next;
+	}
+}
+
+static int newtcpforwarded(struct Channel * channel) {
+
+	unsigned int origport;
+	struct TCPFwdList * iter = NULL;
+	char portstring[NI_MAXSERV];
+	int sock;
+	int ret = DROPBEAR_FAILURE;
+
+	/* We don't care what address they connected to */
+	buf_eatstring(ses.payload);
+
+	origport = buf_getint(ses.payload);
+
+	/* Find which port corresponds */
+	iter = cli_opts.remotefwds;
+
+	while (iter != NULL) {
+		if (origport == iter->listenport) {
+			break;
+		}
+		iter = iter->next;
+	}
+
+	if (iter == NULL) {
+		/* We didn't request forwarding on that port */
+		dropbear_log(LOG_INFO, "Server send unrequested port, from port %d", 
+										origport);
+		goto out;
+	}
+	
+	snprintf(portstring, sizeof(portstring), "%d", iter->connectport);
+	sock = connect_remote(iter->connectaddr, portstring, 1, NULL);
+	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:
+	TRACE(("leave newtcpdirect: ret %d", ret));
 	return ret;
 }
diff --git a/dbutil.c b/dbutil.c
index 72d25e70398877793415e5fa0010c1121fc0b763..b5cd2b0c418af80b2917ebf6a80a48e60ca84718 100644
--- a/dbutil.c
+++ b/dbutil.c
@@ -185,7 +185,7 @@ int dropbear_listen(const char* address, const char* port,
 		if (bind(sock, res->ai_addr, res->ai_addrlen) < 0) {
 			err = errno;
 			close(sock);
-			TRACE(("bind() failed"));
+			TRACE(("bind(%s) failed", port));
 			continue;
 		}
 
@@ -206,7 +206,7 @@ int dropbear_listen(const char* address, const char* port,
 			int len;
 			len = 20 + strlen(strerror(err));
 			*errstring = (char*)m_malloc(len);
-			snprintf(*errstring, len, "Error connecting: %s", strerror(err));
+			snprintf(*errstring, len, "Error listening: %s", strerror(err));
 			TRACE(("leave dropbear_listen: failure, %s", strerror(err)));
 			return -1;
 		}
diff --git a/fake-rfc2553.c b/fake-rfc2553.c
new file mode 100644
index 0000000000000000000000000000000000000000..0186b53006127da4d3b1d2b214a0bdfffa0b1904
--- /dev/null
+++ b/fake-rfc2553.c
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2000-2003 Damien Miller.  All rights reserved.
+ * Copyright (C) 1999 WIDE Project.  All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Pseudo-implementation of RFC2553 name / address resolution functions
+ *
+ * But these functions are not implemented correctly. The minimum subset
+ * is implemented for ssh use only. For example, this routine assumes
+ * that ai_family is AF_INET. Don't use it for another purpose.
+ */
+
+#include "includes.h"
+
+RCSID("$Id: fake-rfc2553.c,v 1.5 2003/09/22 02:08:23 dtucker Exp $");
+
+#ifndef HAVE_GETNAMEINFO
+int getnameinfo(const struct sockaddr *sa, size_t salen, char *host, 
+                size_t hostlen, char *serv, size_t servlen, int flags)
+{
+	struct sockaddr_in *sin = (struct sockaddr_in *)sa;
+	struct hostent *hp;
+	char tmpserv[16];
+
+	if (serv != NULL) {
+		snprintf(tmpserv, sizeof(tmpserv), "%d", ntohs(sin->sin_port));
+		if (strlcpy(serv, tmpserv, servlen) >= servlen)
+			return (EAI_MEMORY);
+	}
+
+	if (host != NULL) {
+		if (flags & NI_NUMERICHOST) {
+			if (strlcpy(host, inet_ntoa(sin->sin_addr),
+			    hostlen) >= hostlen)
+				return (EAI_MEMORY);
+			else
+				return (0);
+		} else {
+			hp = gethostbyaddr((char *)&sin->sin_addr, 
+			    sizeof(struct in_addr), AF_INET);
+			if (hp == NULL)
+				return (EAI_NODATA);
+			
+			if (strlcpy(host, hp->h_name, hostlen) >= hostlen)
+				return (EAI_MEMORY);
+			else
+				return (0);
+		}
+	}
+	return (0);
+}
+#endif /* !HAVE_GETNAMEINFO */
+
+#ifndef HAVE_GAI_STRERROR
+#ifdef HAVE_CONST_GAI_STRERROR_PROTO
+const char *
+#else
+char *
+#endif
+gai_strerror(int err)
+{
+	switch (err) {
+	case EAI_NODATA:
+		return ("no address associated with name");
+	case EAI_MEMORY:
+		return ("memory allocation failure.");
+	case EAI_NONAME:
+		return ("nodename nor servname provided, or not known");
+	default:
+		return ("unknown/invalid error.");
+	}
+}    
+#endif /* !HAVE_GAI_STRERROR */
+
+#ifndef HAVE_FREEADDRINFO
+void
+freeaddrinfo(struct addrinfo *ai)
+{
+	struct addrinfo *next;
+
+	for(; ai != NULL;) {
+		next = ai->ai_next;
+		free(ai);
+		ai = next;
+	}
+}
+#endif /* !HAVE_FREEADDRINFO */
+
+#ifndef HAVE_GETADDRINFO
+static struct
+addrinfo *malloc_ai(int port, u_long addr, const struct addrinfo *hints)
+{
+	struct addrinfo *ai;
+
+	ai = malloc(sizeof(*ai) + sizeof(struct sockaddr_in));
+	if (ai == NULL)
+		return (NULL);
+	
+	memset(ai, '\0', sizeof(*ai) + sizeof(struct sockaddr_in));
+	
+	ai->ai_addr = (struct sockaddr *)(ai + 1);
+	/* XXX -- ssh doesn't use sa_len */
+	ai->ai_addrlen = sizeof(struct sockaddr_in);
+	ai->ai_addr->sa_family = ai->ai_family = AF_INET;
+
+	((struct sockaddr_in *)(ai)->ai_addr)->sin_port = port;
+	((struct sockaddr_in *)(ai)->ai_addr)->sin_addr.s_addr = addr;
+	
+	/* XXX: the following is not generally correct, but does what we want */
+	if (hints->ai_socktype)
+		ai->ai_socktype = hints->ai_socktype;
+	else
+		ai->ai_socktype = SOCK_STREAM;
+
+	if (hints->ai_protocol)
+		ai->ai_protocol = hints->ai_protocol;
+
+	return (ai);
+}
+
+int
+getaddrinfo(const char *hostname, const char *servname, 
+    const struct addrinfo *hints, struct addrinfo **res)
+{
+	struct hostent *hp;
+	struct servent *sp;
+	struct in_addr in;
+	int i;
+	long int port;
+	u_long addr;
+
+	port = 0;
+	if (servname != NULL) {
+		char *cp;
+
+		port = strtol(servname, &cp, 10);
+		if (port > 0 && port <= 65535 && *cp == '\0')
+			port = htons(port);
+		else if ((sp = getservbyname(servname, NULL)) != NULL)
+			port = sp->s_port;
+		else
+			port = 0;
+	}
+
+	if (hints && hints->ai_flags & AI_PASSIVE) {
+		addr = htonl(0x00000000);
+		if (hostname && inet_aton(hostname, &in) != 0)
+			addr = in.s_addr;
+		*res = malloc_ai(port, addr, hints);
+		if (*res == NULL) 
+			return (EAI_MEMORY);
+		return (0);
+	}
+		
+	if (!hostname) {
+		*res = malloc_ai(port, htonl(0x7f000001), hints);
+		if (*res == NULL) 
+			return (EAI_MEMORY);
+		return (0);
+	}
+	
+	if (inet_aton(hostname, &in)) {
+		*res = malloc_ai(port, in.s_addr, hints);
+		if (*res == NULL) 
+			return (EAI_MEMORY);
+		return (0);
+	}
+	
+	/* Don't try DNS if AI_NUMERICHOST is set */
+	if (hints && hints->ai_flags & AI_NUMERICHOST)
+		return (EAI_NONAME);
+	
+	hp = gethostbyname(hostname);
+	if (hp && hp->h_name && hp->h_name[0] && hp->h_addr_list[0]) {
+		struct addrinfo *cur, *prev;
+
+		cur = prev = *res = NULL;
+		for (i = 0; hp->h_addr_list[i]; i++) {
+			struct in_addr *in = (struct in_addr *)hp->h_addr_list[i];
+
+			cur = malloc_ai(port, in->s_addr, hints);
+			if (cur == NULL) {
+				if (*res != NULL)
+					freeaddrinfo(*res);
+				return (EAI_MEMORY);
+			}
+			if (prev)
+				prev->ai_next = cur;
+			else
+				*res = cur;
+
+			prev = cur;
+		}
+		return (0);
+	}
+	
+	return (EAI_NODATA);
+}
+#endif /* !HAVE_GETADDRINFO */
diff --git a/fake-rfc2553.h b/fake-rfc2553.h
new file mode 100644
index 0000000000000000000000000000000000000000..baea07038001a9820b40ded5e01414617bbf9a8f
--- /dev/null
+++ b/fake-rfc2553.h
@@ -0,0 +1,161 @@
+/* $Id: fake-rfc2553.h,v 1.9 2004/03/10 10:06:33 dtucker Exp $ */
+
+/*
+ * Copyright (C) 2000-2003 Damien Miller.  All rights reserved.
+ * Copyright (C) 1999 WIDE Project.  All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Pseudo-implementation of RFC2553 name / address resolution functions
+ *
+ * But these functions are not implemented correctly. The minimum subset
+ * is implemented for ssh use only. For example, this routine assumes
+ * that ai_family is AF_INET. Don't use it for another purpose.
+ */
+
+#ifndef _FAKE_RFC2553_H
+#define _FAKE_RFC2553_H
+
+#include "includes.h"
+#include "sys/types.h"
+
+/*
+ * First, socket and INET6 related definitions 
+ */
+#ifndef HAVE_STRUCT_SOCKADDR_STORAGE
+# define	_SS_MAXSIZE	128	/* Implementation specific max size */
+# define       _SS_PADSIZE     (_SS_MAXSIZE - sizeof (struct sockaddr))
+struct sockaddr_storage {
+	struct sockaddr	ss_sa;
+	char		__ss_pad2[_SS_PADSIZE];
+};
+# define ss_family ss_sa.sa_family
+#endif /* !HAVE_STRUCT_SOCKADDR_STORAGE */
+
+#ifndef IN6_IS_ADDR_LOOPBACK
+# define IN6_IS_ADDR_LOOPBACK(a) \
+	(((u_int32_t *)(a))[0] == 0 && ((u_int32_t *)(a))[1] == 0 && \
+	 ((u_int32_t *)(a))[2] == 0 && ((u_int32_t *)(a))[3] == htonl(1))
+#endif /* !IN6_IS_ADDR_LOOPBACK */
+
+#ifndef HAVE_STRUCT_IN6_ADDR
+struct in6_addr {
+	u_int8_t	s6_addr[16];
+};
+#endif /* !HAVE_STRUCT_IN6_ADDR */
+
+#ifndef HAVE_STRUCT_SOCKADDR_IN6
+struct sockaddr_in6 {
+	unsigned short	sin6_family;
+	u_int16_t	sin6_port;
+	u_int32_t	sin6_flowinfo;
+	struct in6_addr	sin6_addr;
+};
+#endif /* !HAVE_STRUCT_SOCKADDR_IN6 */
+
+#ifndef AF_INET6
+/* Define it to something that should never appear */
+#define AF_INET6 AF_MAX
+#endif
+
+/*
+ * Next, RFC2553 name / address resolution API
+ */
+
+#ifndef NI_NUMERICHOST
+# define NI_NUMERICHOST    (1)
+#endif
+#ifndef NI_NAMEREQD
+# define NI_NAMEREQD       (1<<1)
+#endif
+#ifndef NI_NUMERICSERV
+# define NI_NUMERICSERV    (1<<2)
+#endif
+
+#ifndef AI_PASSIVE
+# define AI_PASSIVE		(1)
+#endif
+#ifndef AI_CANONNAME
+# define AI_CANONNAME		(1<<1)
+#endif
+#ifndef AI_NUMERICHOST
+# define AI_NUMERICHOST		(1<<2)
+#endif
+
+#ifndef NI_MAXSERV
+# define NI_MAXSERV 32
+#endif /* !NI_MAXSERV */
+#ifndef NI_MAXHOST
+# define NI_MAXHOST 1025
+#endif /* !NI_MAXHOST */
+
+#ifndef EAI_NODATA
+# define EAI_NODATA	1
+# define EAI_MEMORY	2
+# define EAI_NONAME	3
+#endif
+
+#ifndef HAVE_STRUCT_ADDRINFO
+struct addrinfo {
+	int	ai_flags;	/* AI_PASSIVE, AI_CANONNAME */
+	int	ai_family;	/* PF_xxx */
+	int	ai_socktype;	/* SOCK_xxx */
+	int	ai_protocol;	/* 0 or IPPROTO_xxx for IPv4 and IPv6 */
+	size_t	ai_addrlen;	/* length of ai_addr */
+	char	*ai_canonname;	/* canonical name for hostname */
+	struct sockaddr *ai_addr;	/* binary address */
+	struct addrinfo *ai_next;	/* next structure in linked list */
+};
+#endif /* !HAVE_STRUCT_ADDRINFO */
+
+#ifndef HAVE_GETADDRINFO
+#ifdef getaddrinfo
+# undef getaddrinfo
+#endif
+#define getaddrinfo(a,b,c,d)	(ssh_getaddrinfo(a,b,c,d))
+int getaddrinfo(const char *, const char *, 
+    const struct addrinfo *, struct addrinfo **);
+#endif /* !HAVE_GETADDRINFO */
+
+#if !defined(HAVE_GAI_STRERROR) && !defined(HAVE_CONST_GAI_STRERROR_PROTO)
+#define gai_strerror(a)		(ssh_gai_strerror(a))
+char *gai_strerror(int);
+#endif /* !HAVE_GAI_STRERROR */
+
+#ifndef HAVE_FREEADDRINFO
+#define freeaddrinfo(a)		(ssh_freeaddrinfo(a))
+void freeaddrinfo(struct addrinfo *);
+#endif /* !HAVE_FREEADDRINFO */
+
+#ifndef HAVE_GETNAMEINFO
+#define getnameinfo(a,b,c,d,e,f,g) (ssh_getnameinfo(a,b,c,d,e,f,g))
+int getnameinfo(const struct sockaddr *, size_t, char *, size_t, 
+    char *, size_t, int);
+#endif /* !HAVE_GETNAMEINFO */
+
+#endif /* !_FAKE_RFC2553_H */
+
diff --git a/options.h b/options.h
index 99115db51b12c30dffc060ee07bd9eda88a2f54f..c841332fb8da1a9d56949fedaaad133db079bcf8 100644
--- a/options.h
+++ b/options.h
@@ -51,10 +51,13 @@
 #define ENABLE_X11FWD
 
 /* Enable TCP Fowarding */
-/* OpenSSH's "-L" style forwarding (client port forwarded via server) */
-#define ENABLE_LOCALTCPFWD
-/* OpenSSH's "-R" style forwarding (server port forwarded via client) */
-#define ENABLE_REMOTETCPFWD
+/* "-L" style forwarding (client listening port forwarded via server) */
+#define ENABLE_CLI_LOCALTCPFWD
+/* "-R" style forwarding (server listening port forwarded via client) */
+#define ENABLE_CLI_REMOTETCPFWD
+
+#define ENABLE_SVR_LOCALTCPFWD
+#define ENABLE_SVR_REMOTETCPFWD
 
 /* Enable Authentication Agent Forwarding */
 #define ENABLE_AGENTFWD
@@ -299,6 +302,10 @@
 #define DISABLE_REMOTETCPFWD
 #endif
 
+#if defined(ENABLE_CLI_REMOTETCPFWD) || defined(ENABLE_CLI_LOCALTCPFWD)
+#define ENABLE_CLI_ANYTCPFWD 
+#endif
+
 #if defined(ENABLE_REMOTETCPFWD) || defined(ENABLE_LOCALTCPFWD) || \
 	defined(ENABLE_AGENTFWD) || defined(ENABLE_X11FWD)
 #define USING_LISTENERS
diff --git a/runopts.h b/runopts.h
index c74dab5dd9995466d6d1a8de1810e429fa671ae2..a160a29b523ea882e2465fb1706842f110a2e201 100644
--- a/runopts.h
+++ b/runopts.h
@@ -29,6 +29,7 @@
 #include "signkey.h"
 #include "buffer.h"
 #include "auth.h"
+#include "tcpfwd.h"
 
 typedef struct runopts {
 
@@ -90,8 +91,14 @@ typedef struct cli_runopts {
 
 	char *cmd;
 	int wantpty;
-	struct PubkeyList *pubkeys; /* Keys to use for public-key auth */
 #ifdef DROPBEAR_PUBKEY_AUTH
+	struct PubkeyList *pubkeys; /* Keys to use for public-key auth */
+#endif
+#ifdef ENABLE_CLI_REMOTETCPFWD
+	struct TCPFwdList * remotefwds;
+#endif
+#ifdef ENABLE_CLI_LOCALTCPFWD
+	struct TCPFwdList * localfwds;
 #endif
 	/* XXX TODO */
 
diff --git a/session.h b/session.h
index 4a9d4ed4030238d75716a5b9251420e8079a4ccb..6a6ed0753c6dbab2654e9507dc74a4a0b1b4c70e 100644
--- a/session.h
+++ b/session.h
@@ -35,6 +35,7 @@
 #include "queue.h"
 #include "listener.h"
 #include "packet.h"
+#include "tcpfwd.h"
 
 extern int sessinitdone; /* Is set to 0 somewhere */
 extern int exitflag;
diff --git a/svr-tcpfwd.c b/svr-tcpfwd.c
index 46c7129a9156e9803a9d0fc441dd9ca9f5813884..0eccae4c25aee5dc09680d908019ba742f9ba941 100644
--- a/svr-tcpfwd.c
+++ b/svr-tcpfwd.c
@@ -1,7 +1,6 @@
 #include "includes.h"
 #include "ssh.h"
-#include "tcp-accept.h"
-#include "tcp-connect.h"
+#include "tcpfwd.h"
 #include "dbutil.h"
 #include "session.h"
 #include "buffer.h"
@@ -15,6 +14,7 @@ static void send_msg_request_success();
 static void send_msg_request_failure();
 static int svr_cancelremotetcp();
 static int svr_remotetcpreq();
+static int newtcpdirect(struct Channel * channel);
 
 
 const struct ChanType svr_chan_tcpdirect = {
@@ -178,8 +178,8 @@ static int svr_remotetcpreq() {
 
 	tcpinfo = (struct TCPListener*)m_malloc(sizeof(struct TCPListener));
 	tcpinfo->sendaddr = bindaddr;
-	TRACE(("sendport = %d", port));
 	tcpinfo->sendport = port;
+	tcpinfo->listenport = port;
 	tcpinfo->chantype = &svr_chan_tcpremote;
 
 	/* Note: bindaddr is actually ignored by listen_tcpfwd, since
@@ -196,4 +196,70 @@ out:
 	TRACE(("leave remotetcpreq"));
 	return ret;
 }
+
+/* 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;
+	char portstring[NI_MAXSERV];
+	int sock;
+	int len;
+	int ret = DROPBEAR_FAILURE;
+
+	if (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;
+	}
+
+	snprintf(portstring, sizeof(portstring), "%d", destport);
+	sock = connect_remote(desthost, portstring, 1, NULL);
+	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;
+}
+
 #endif
diff --git a/tcp-accept.c b/tcp-accept.c
index 8f8b5c0fb38ba386443a56a58100e247ec77e6ad..6b82914b982ed0b700be09b09ff24fdced324e8b 100644
--- a/tcp-accept.c
+++ b/tcp-accept.c
@@ -1,6 +1,6 @@
 #include "includes.h"
 #include "ssh.h"
-#include "tcp-accept.h"
+#include "tcpfwd.h"
 #include "dbutil.h"
 #include "session.h"
 #include "buffer.h"
@@ -67,7 +67,7 @@ int listen_tcpfwd(struct TCPListener* tcpinfo) {
 	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", tcpinfo->sendport);
+	snprintf(portstring, sizeof(portstring), "%d", tcpinfo->listenport);
 
 	/* XXX Note: we're just listening on localhost, no matter what they tell
 	 * us. If someone wants to make it listen otherways, then change
diff --git a/tcp-accept.h b/tcp-accept.h
deleted file mode 100644
index 8c795dcc78aeb19889c2645b2a5c35aa1602f36f..0000000000000000000000000000000000000000
--- a/tcp-accept.h
+++ /dev/null
@@ -1,25 +0,0 @@
-#ifndef _REMOTETCPFWD_H
-#define _REMOTETCPFWD_H
-
-struct TCPListener {
-
-	/* sendaddr/sendport are what we send in the channel init request. For a 
-	 * forwarded-tcpip request, it's the addr/port we were binding to.
-	 * For a direct-tcpip request, it's the addr/port we want the other
-	 * end to connect to */
-	
-	unsigned char *sendaddr;
-	unsigned int sendport;
-
-	/* This is for direct-tcpip (ie the client listening), and specifies the
-	 * port to listen on. Is unspecified for the server */
-	unsigned int listenport;
-
-	const struct ChanType *chantype;
-
-};
-
-void recv_msg_global_request_remotetcp();
-int listen_tcpfwd(struct TCPListener* tcpinfo);
-
-#endif /* _REMOTETCPFWD_H */
diff --git a/tcp-connect.c b/tcp-connect.c
deleted file mode 100644
index d24f32d80ffb6ea7d51101a0eaa6100b6320543e..0000000000000000000000000000000000000000
--- a/tcp-connect.c
+++ /dev/null
@@ -1,75 +0,0 @@
-#include "includes.h"
-#include "session.h"
-#include "dbutil.h"
-#include "channel.h"
-#include "tcp-connect.h"
-#include "runopts.h"
-
-#ifndef DISABLE_TCP_CONNECT
-
-/* Called upon creating a new direct tcp channel (ie we connect out to an
- * address */
-int newtcpdirect(struct Channel * channel) {
-
-	unsigned char* desthost = NULL;
-	unsigned int destport;
-	unsigned char* orighost = NULL;
-	unsigned int origport;
-	char portstring[NI_MAXSERV];
-	int sock;
-	int len;
-	int ret = DROPBEAR_FAILURE;
-
-	if (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;
-	}
-
-	snprintf(portstring, sizeof(portstring), "%d", destport);
-	sock = connect_remote(desthost, portstring, 1, NULL);
-	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;
-}
-
-#endif /* DISABLE_TCPFWD_DIRECT */
diff --git a/tcp-connect.h b/tcpfwd.h
similarity index 58%
rename from tcp-connect.h
rename to tcpfwd.h
index 40ce22bbac9daba779960f39b3dc9970070d5fb9..569d59dc0664dc1dd3bc13f714cafab55f783245 100644
--- a/tcp-connect.h
+++ b/tcpfwd.h
@@ -21,15 +21,49 @@
  * 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
+#ifndef _TCPFWD_H
+#define _TCPFWD_H
 
-#include "includes.h"
 #include "channel.h"
 
+struct TCPListener {
+
+	/* sendaddr/sendport are what we send in the channel init request. For a 
+	 * forwarded-tcpip request, it's the addr/port we were binding to.
+	 * For a direct-tcpip request, it's the addr/port we want the other
+	 * end to connect to */
+	
+	unsigned char *sendaddr;
+	unsigned int sendport;
+
+	/* This is for direct-tcpip (ie the client listening), and specifies the
+	 * port to listen on. Is unspecified for the server */
+	unsigned int listenport;
+
+	const struct ChanType *chantype;
+
+};
+
+/* A link in a list of forwards */
+struct TCPFwdList {
+
+	char* connectaddr;
+	unsigned int connectport;
+	unsigned int listenport;
+	struct TCPFwdList * next;
+
+};
+
+/* Server */
+void recv_msg_global_request_remotetcp();
 extern const struct ChanType svr_chan_tcpdirect;
-int newtcpdirect(struct Channel * channel);
 
-#endif
+/* Client */
+void setup_localtcp();
+extern const struct ChanType cli_chan_tcpremote;
+
+/* Common */
+int listen_tcpfwd(struct TCPListener* tcpinfo);
+
+
 #endif