changeset 118:5312ca05ed48 private-rez

propagate of 717950f4061f1123659ee87c7c168805af920ab7 and 839f98f136788cc1466e4641bf796f96040a085d from branch 'matt.dbclient.authpam' to 'matt.dbclient.rez'
author Matt Johnston <matt@ucc.asn.au>
date Sun, 12 Sep 2004 04:56:50 +0000
parents 3b2a5a1c4347 (current diff) e0acad552a92 (diff)
children 3394a7cb30cd
files Makefile.in authpasswd.h authpubkey.h configure.in debian/compat debian/conffiles debian/dirs debian/docs debian/postinst debian/postrm options.h svr-auth.c tcpfwd-direct.c tcpfwd-direct.h tcpfwd-remote.c tcpfwd-remote.h
diffstat 96 files changed, 3875 insertions(+), 1685 deletions(-) [+]
line wrap: on
line diff
--- a/CHANGES	Sun Aug 08 16:57:37 2004 +0000
+++ b/CHANGES	Sun Sep 12 04:56:50 2004 +0000
@@ -1,3 +1,133 @@
+0.44test3 - Fri Aug 27 22:20:54 +0800
+
+- Fixed a bunch of warnings.
+
+- scp works correctly when passed a username (fix for the dbclient program
+  itself as well, "-lmatt" works as well as "-l matt").
+
+- Remove unrequired debian files
+
+- Exit with the remote process's return code for dbclient
+
+- Display stderr messages from the server in the client
+
+- Add circular buffering to the channel code. This should dramatically reduce
+  the amount of backtraffic sent in response to traffic incoming to the
+  Dropbear end - improves high-latency performance (ie dialup).
+
+- Various other related channel-handling fixups.
+
+- Allow leading lines in the banner when connecting to servers
+
+- Fixed printing out errors onto the network socket with stderr (for inetd
+  mode when using xinetd)
+
+- Remove obselete documentation
+
+- Fix a null-pointer exception when trying to free non-existant listeners
+  at cleanup.
+
+- DEBUG_TRACE now only works if you add "-v" to the program commandline
+
+- Don't leave stdin non-blocking on exit - this caused the parent shell
+  of dbclient to close when dbclient exited, for some shells in BusyBox
+
+- Server connections no longer timeout after 5 minutes
+
+- Fixed stupid DSS hostkey typo (server couldn't load host keys)
+
+0.44test2 - Tues Aug 17 2004 17:43:54 +0800
+
+- Fix up dropbearmulti targets in the Makefile - symlinks are now created
+
+- Compile fake-rfc2553 even with dropbearconvert/dropbearkey - this 
+  allows them to work on platforms without a native getaddrinfo()
+
+- Create ~/.ssh/known_hosts properly if it doesn't exist
+
+- Fix basename() function prototype
+
+- Backport some local changes (more #ifdefs for termcodes.c, a fix for missing
+  defines on AIX).
+
+- Let dbclient be run as "ssh"
+
+- Initialise mp_ints by default
+
+0.44test1 - Sun Aug 16 2005 17:43:54 +0800
+
+- TESTING RELEASE - this is the first public release of the client codebase,
+  so there are sure to be bugs to be found. In addition, if you're just using
+  the server portion, the final binary size probably will increase - I'll
+  be trying to get it back down in future releases.
+
+- Dropbear client added - lots of changes to the server code as well to 
+  generalise things
+
+- IPv6 support added for client, server, and forwarding
+
+- New makefile with more generic support for multiple-program binaries
+
+0.43 - Fri Jul 16 2004 17:44:54 +0800
+
+- SECURITY: Don't try to free() uninitialised variables in DSS verification
+  code. Thanks to Arne Bernin for pointing out this bug. This is possibly
+  exploitable, all users with DSS and pubkey-auth compiled in are advised to
+  upgrade.
+
+- Clean up agent forwarding socket files correctly, patch from Gerrit Pape.
+
+- Don't go into an infinite loop when portforwarding to servers which don't
+  send any initial data/banner. Patch from Nikola Vladov
+
+- Fix for network vs. host byte order in logging remote TCP ports, also
+  from Gerrit Pape.
+
+- Initialise many pointers to NULL, for general safety. Also checked cleanup
+  code for mp_ints (related to security issues above).
+
+0.42 - Wed Jun 16 2004 12:44:54 +0800
+
+- Updated to Gerrit Pape's official Debian subdirectory
+
+- Fixed bad check when opening /dev/urandom - thanks to Danny Sung.
+
+- Added -i inetd mode flag, and associated options in options.h . Dropbear
+  can be compiled with either normal mode, inetd, or both modes. Thanks
+  to Gerrit Pape for basic patch and motivation.
+
+- Use <dirent.h> rather than <sys/dir.h> for POSIX compliance. Thanks to Bill
+  Sommerfield.
+
+- Fixed a TCP forwarding (client-local, -L style) bug which caused the whole
+  session to close if the TCP connection failed. Thanks to Andrew Braund for
+  reporting it and helping track it down.
+
+- Re-enable sigpipe for child processes. Thanks to Gerrit Pape for some
+  suggestions, and BSD manpages for a clearer explanation of the behaviour.
+
+- Added manpages, thanks to Gerrit Pape.
+
+- Changed license text for LibTomCrypt and LibTomMath.
+
+- Added strip-static target
+
+- Fixed a bug in agent-forwarding cleanup handler - would segfault
+  (dereferencing a null pointer) if agent forwarding had failed.
+
+- Fix behaviour of authorized_keys parsing, so larger (>1024 bit) DSA keys will
+  work. Thanks to Dr. Markus Waldeck for the report. 
+
+- Fixed local port forwarding code so that the "-j" option will make forwarding
+  attempts fail more gracefully.
+
+- Allow repeated requests in a single session if previous ones fail - this fixes  PuTTY and some other SCP clients, which try SFTP, then fall-back to SCP if it
+  isn't available. Thanks to Stirling Westrup for the report.
+
+- Updated to LibTomCrypt 0.96 and LibTomMath 0.30. The AES code now uses
+  smaller non-precomputed tables if DROPBEAR_SMALL_CODE is defined in
+  options.h, leading to a significant reduction in the binary size.
+
 0.41 - Mon Jan 19 2004 22:40:19 +0800
 
 - Fix in configure so that cross-compiling works, thanks to numerous people for
--- a/INSTALL	Sun Aug 08 16:57:37 2004 +0000
+++ b/INSTALL	Sun Sep 12 04:56:50 2004 +0000
@@ -1,45 +1,30 @@
 Basic Dropbear build instructions:
 
-- First, edit options.h to choose user-defined features to choose, such as
-  which ciphers/hashes you want, which forwarding you want, etc.
+- Edit options.h to set which features you want.
+- Edit debug.h if you want any debug options (not usually required).
 
-- Edit debug.h if you want any debug options
-
-- Now configure Dropbear's host-specific options
-  (if you are using a cvs copy, "autoconf; autoheader" first)
+(If using a non-tarball copy, "autoconf; autoheader")
 
 ./configure      (optionally with --disable-zlib or --disable-syslog,
                   or --help for other options)
 
-- Then compile and optionally install Dropbear:
+Now compile:
 
-(the Makefile requires GNU make, if you want to make it portable, send me
- some patches)
+make PROGRAMS="dropbear dbclient dropbearkey dropbearconvert scp"
 
-make
-make install     (installs to /usr/local/sbin, /usr/local/bin by default)
+And install (/usr/local/bin is usual default):
 
-You need to generate server keys, this is one-off:
-./dropbearkey -t rsa -f dropbear_rsa_host_key
-./dropbearkey -t dss -f dropbear_dss_host_key
-
-or alternatively convert OpenSSH keys to Dropbear:
-./dropbearconvert openssh dropbear /etc/ssh/ssh_host_dsa_key dropbear_dss_host_key
+make PROGRAMS="dropbear dbclient dropbearkey dropbearconvert scp" install
 
-And you can now run the server.
-./dropbear
-
-or './dropbear -h' to get options.
+(you can leave items out of the PROGRAMS list to avoid compiling them. If you
+recompile after changing the PROGRAMS list, you *MUST* "make clean" before
+recompiling - bad things will happen otherwise)
 
-If the server is run as non-root, you most likely won't be able to allocate a
-pty, and you cannot login as any user other than that running the daemon
-(obviously). Shadow passwords will also be unusable as non-root.
+See MULTI for instructions on making all-in-one binaries.
 
-The Dropbear distribution includes a standalone version of OpenSSH's scp
-program. You can compile it with "make scp", you may want to change the path
-of the ssh binary, specified near the top of the scp.c file. By default
-the progress meter isn't compiled in to save space, you can enable it with
-"make scp-progress".
+If you want to compile statically, add "STATIC=1" to the make command-line.
+
+Binaries can be strippd with "make strip"
 
 ============================================================================
 
@@ -50,18 +35,11 @@
 headers don't match the library you are running with, ie the headers might
 say that shadow password support exists, but the libraries don't have it.
 
-To compile for uClibc the following should work:
-
-rm config.cache
-CC=i386-uclib-gcc ./configure --disable-zlib
-make clean
-make
-make strip
-
-... and that should be it. You can use "make static" to make statically linked 
-binaries, and it is advisable to strip the binaries too. If you're looking
-to make a small binary, you should remove unneeded ciphers and MD5, by 
-editing options.h
+Compiling for uClibc should be the same as normal, just set CC to the magic
+uClibc toolchain compiler (ie export CC=i386-uclibc-gcc or whatever).
+You can use "make STATIC=1" to make statically linked binaries, and it is
+advisable to strip the binaries too. If you're looking to make a small binary,
+you should remove unneeded ciphers and MD5, by editing options.h
 
 It is possible to compile zlib in, by copying zlib.h and zconf.h into a
 subdirectory (ie zlibincludes), and 
@@ -78,7 +56,11 @@
 You may want to manually disable lastlog recording when using uClibc, configure
 with --disable-lastlog.
 
-One common problem is pty allocation. There are a number of types of pty allocation which can be used -- if they work properly, the end result is the same for each type. Running configure should detect the best type to use automatically, however for some embedded systems, this may be incorrect. Some things to note:
+One common problem is pty allocation. There are a number of types of pty
+allocation which can be used -- if they work properly, the end result is the
+same for each type. Running configure should detect the best type to use
+automatically, however for some systems, this may be incorrect. Some
+things to note:
 
     If your system expects /dev/pts to be mounted (this is a uClibc option),
 	make sure that it is.
@@ -90,19 +72,3 @@
 	to create all the /dev/pty?? and /dev/tty?? devices, which can be
 	problematic for devfs. In general, openpty() is the best way to allocate
 	PTYs, so it's best to try and get it working.
-
-
-============================================================================
-
-Public key auth:
-
-You can use ~/.ssh/authorized_keys in the same way as with OpenSSH, just put
-the key entries in that file. They should be of the form:
-
-ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEAwVa6M6cGVmUcLl2cFzkxEoJd06Ub4bVDsYrWvXhvUV+ZAM9uGuewZBDoAqNKJxoIn0Hyd0Nk/yU99UVv6NWV/5YSHtnf35LKds56j7cuzoQpFIdjNwdxAN0PCET/MG8qyskG/2IE2DPNIaJ3Wy+Ws4IZEgdJgPlTYUBWWtCWOGc= someone@hostname
-
-You must make sure that ~/.ssh, and the key file, are only writable by the
-user.
-
-NOTE: Dropbear ignores authorized_keys options such as those described in the
-OpenSSH sshd manpage, and will not allow a login for these keys. 
--- a/LICENSE	Sun Aug 08 16:57:37 2004 +0000
+++ b/LICENSE	Sun Sep 12 04:56:50 2004 +0000
@@ -1,6 +1,15 @@
-The majority of code is written by Matt Johnston, under the following license:
+Dropbear contains a number of components from different sources, hence there
+are a few licenses and authors involved. All licenses are fairly 
+non-restrictive.
+
 
-Copyright (c) 2002,2003 Matt Johnston
+The majority of code is written by Matt Johnston, under the license below.
+
+Portions of the client-mode work are (c) 2004 Mihnea Stoenescu, under the
+same license:
+
+Copyright (c) 2002-2004 Matt Johnston
+Portions copyright (c) 2004 Mihnea Stoenescu
 All rights reserved.
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -23,9 +32,7 @@
 
 =====
 
-LibTomCrypt and LibTomMath are (c) Tom St Denis, under TDCAL (Tom Doesn't Care
-About Licenses) some files are from public domain sources, see
-libtomcrypt/legal.txt
+LibTomCrypt and LibTomMath are written by Tom St Denis, and are Public Domain.
 
 =====
 
--- a/MULTI	Sun Aug 08 16:57:37 2004 +0000
+++ b/MULTI	Sun Sep 12 04:56:50 2004 +0000
@@ -3,29 +3,24 @@
 
 To compile for systems without much space (floppy distributions etc), you
 can create a single binary. This will save disk space by avoiding repeated
-code between the three components (dropbear, dropbearkey, dropbearconvert).
+code between the various parts.
 If you are familiar with "busybox", it's the same principle.
 
-To use the multi-purpose binary, firstly enable the "#define DROPBEAR_MULTI"
-line in options.h
-
-Then enable which of the binaries you want to compile, also in options.h
-(by default these are all enabled).
+To compile the multi-binary, first "make clean" (if you've compiled
+previously), then
 
-You should then "make clean" (if you compiled previously), then
-
-"make dropbearmulti"
-
-("make dropbearmultistatic" will make a static binary).
+make PROGRAMS="programs you want here" MULTI=1
 
 To use the binary, symlink it from the desired executable:
 
 ln -s dropbearmulti dropbear
+ln -s dropbearmulti dbclient
+etc
 
 then execute as normal:
 
 ./dropbear <options here>
 
-"make install" doesn't currently work for multi-binary configuration, however
+"make install" doesn't currently work for multi-binary configuration, though
 in most situations where it is being used, the target and build systems will
 differ.
--- a/Makefile.in	Sun Aug 08 16:57:37 2004 +0000
+++ b/Makefile.in	Sun Sep 12 04:56:50 2004 +0000
@@ -3,14 +3,13 @@
 # invocation:
 # make PROGRAMS="dropbear dbclient scp" MULTI=1 STATIC=1 SCPPROGRESS=1
 #
-# to make a single multiple statically linked binary "staticdropbearmulti",
-# which includes dropbear, scp and dbclient functionality, and includes the
-# progress-bar functionality in scp. Hopefully that seems intuitive.
-
-# This makefile is quite evil.
+# to make a multiple-program statically linked binary "staticdropbearmulti".
+# This example will include dropbear, scp, dropbearkey, dropbearconvert, and
+# dbclient functionality, and includes the progress-bar functionality in scp.
+# Hopefully that seems intuitive.
 
 ifndef PROGRAMS
-	PROGRAMS=dropbear dbclient dropbearkey dropbearkey
+	PROGRAMS=dropbear dbclient dropbearkey dropbearconvert
 endif
 
 LTC=libtomcrypt/libtomcrypt.a
@@ -20,21 +19,22 @@
 		dss.o bignum.o \
 		signkey.o rsa.o random.o \
 		queue.o \
-		atomicio.o compat.o 
+		atomicio.o compat.o  fake-rfc2553.o
 
 SVROBJS=svr-kex.o svr-algo.o svr-auth.o sshpty.o \
 		svr-authpasswd.o svr-authpubkey.o svr-session.o svr-service.o \
+		svr-authpam.o
 		svr-chansession.o svr-runopts.o svr-agentfwd.o svr-main.o svr-x11fwd.o\
-		svr-authpam.o
+		svr-tcpfwd.o
 
 CLIOBJS=cli-algo.o cli-main.o cli-auth.o cli-authpasswd.o cli-kex.o \
 		cli-session.o cli-service.o cli-runopts.o cli-chansession.o \
-		cli-authpubkey.o
+		cli-authpubkey.o cli-tcpfwd.o cli-channel.o
 
 CLISVROBJS=common-session.o packet.o common-algo.o common-kex.o \
 			common-channel.o common-chansession.o termcodes.o loginrec.o \
-			tcpfwd-direct.o tcpfwd-remote.o listener.o process-packet.o \
-			common-runopts.o
+			tcp-accept.o listener.o process-packet.o \
+			common-runopts.o circbuffer.o
 
 KEYOBJS=dropbearkey.o gendss.o genrsa.o
 
@@ -43,11 +43,11 @@
 SCPOBJS=scp.o progressmeter.o atomicio.o scpmisc.o
 
 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 \
+		dss.h bignum.h signkey.h rsa.h random.h service.h auth.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 tcpfwd-direct.h compat.h \
-		tcpfwd-remote.h listener.h
+		termcodes.h gendss.h genrsa.h runopts.h includes.h \
+		loginrec.h atomicio.h x11fwd.h agentfwd.h tcpfwd.h compat.h \
+		listener.h fake-rfc2553.h
 
 dropbearobjs=$(COMMONOBJS) $(CLISVROBJS) $(SVROBJS) 
 dbclientobjs=$(COMMONOBJS) $(CLISVROBJS) $(CLIOBJS)
@@ -115,25 +115,36 @@
 strip: $(TARGETS)
 	$(STRIP) $(addsuffix $(EXEEXT), $(addprefix $(SPREFIX), $(TARGETS)))
 
-install: $(addprefix install, $(TARGETS))
+install: $(addprefix inst, $(TARGETS))
+
+installdropbearmulti: insdbmulti $(addprefix insmulti, $(PROGRAMS)) 
+
+insdbmulti: dropbearmulti
+	$(INSTALL) -d -m 755 $(DESTDIR)$(bindir)
+	$(INSTALL) -m 755 $(SPREFIX)dropbearmulti$(EXEEXT) $(DESTDIR)$(bindir)
+	-chown root $(DESTDIR)$(bindir)/$(SPREFIX)dropbearmulti$(EXEEXT)
+	-chgrp 0 $(DESTDIR)$(bindir)/$(SPREFIX)dropbearmulti$(EXEEXT)
+
+insmultidropbear: dropbearmulti
+	-rm -f $(DESTDIR)$(sbindir)/$(SPREFIX)dropbear$(EXEEXT)
+	-ln -s $(DESTDIR)$(bindir)/$(SPREFIX)dropbearmulti$(EXEEXT) $(DESTDIR)$(sbindir)/$(SPREFIX)dropbear$(EXEEXT) 
+
+insmulti%: dropbearmulti
+	-rm -f $(DESTDIR)$(bindir)/$(SPREFIX)$*$(EXEEXT) 
+	-ln -s $(DESTDIR)$(bindir)/$(SPREFIX)dropbearmulti$(EXEEXT) $(DESTDIR)$(bindir)/$(SPREFIX)$*$(EXEEXT) 
 
 # dropbear should go in sbin, so it needs a seperate rule
-installdropbear: dropbear
+instdropbear: dropbear
 	$(INSTALL) -d -m 755 $(DESTDIR)$(sbindir)
 	$(INSTALL) -m 755 $(SPREFIX)dropbear$(EXEEXT) $(DESTDIR)$(sbindir)
 	-chown root $(DESTDIR)$(sbindir)/$(SPREFIX)dropbear$(EXEEXT)
 	-chgrp 0 $(DESTDIR)$(sbindir)/$(SPREFIX)dropbear$(EXEEXT)
 
-install%: $*
+inst%: $*
 	$(INSTALL) -d -m 755 $(DESTDIR)$(bindir)
 	$(INSTALL) -m 755 $(SPREFIX)$*$(EXEEXT) $(DESTDIR)$(bindir)
-	-chown root $(DESTDIR)$(sbindir)/$(SPREFIX)$*$(EXEEXT)
-	-chgrp 0 $(DESTDIR)$(sbindir)/$(SPREFIX)$*$(EXEEXT)
-	ifeq ($(MULTI), 1)
-		@echo 
-		@echo "You must manually create links for $*"
-	endif
-
+	-chown root $(DESTDIR)$(bindir)/$(SPREFIX)$*$(EXEEXT)
+	-chgrp 0 $(DESTDIR)$(bindir)/$(SPREFIX)$*$(EXEEXT)
 
 
 # for some reason the rule further down doesn't like $($@objs) as a prereq.
@@ -158,11 +169,16 @@
 	CFLAGS+=$(addprefix -DDBMULTI_, $(PROGRAMS)) -DDROPBEAR_MULTI
 endif
 
-dropbearmulti: $(HEADERS) $(MULTIOBJS) $(LTC) $(LTM) Makefile
-	$(LD) $(LDFLAGS) -o $(SPREFIX)$@$(EXEEXT) $(MULTIOBJS) $(LIBS)
-	@echo
-	@echo "You should now create symlinks to the programs you have included"
-	@echo "ie 'ln -s dropbearmulti dropbear'"
+dropbearmulti: multilink 
+
+multibinary: $(HEADERS) $(MULTIOBJS) $(LTC) $(LTM) Makefile
+	$(LD) $(LDFLAGS) -o $(SPREFIX)dropbearmulti$(EXEEXT) $(MULTIOBJS) $(LIBS)
+
+multilink: multibinary $(addprefix link, $(PROGRAMS))
+
+link%:
+	-rm -f $(SPREFIX)$*$(EXEEXT)
+	-ln -s $(SPREFIX)dropbearmulti$(EXEEXT) $(SPREFIX)$*$(EXEEXT)
 
 $(LTC): options.h
 	cd libtomcrypt && $(MAKE) clean && $(MAKE)
@@ -179,7 +195,9 @@
 sizes: dropbear
 	objdump -t dropbear|grep ".text"|cut -d "." -f 2|sort -rn
 
-clean: ltc-clean ltm-clean
+clean: ltc-clean ltm-clean thisclean
+
+thisclean:
 	-rm -f dropbear dbclient dropbearkey dropbearconvert scp scp-progress
 	-rm -f staticdropbear staticdropbearkey staticdropbearconvert staticscp
 	-rm -f dropbearmulti staticdropbearmulti
--- a/README	Sun Aug 08 16:57:37 2004 +0000
+++ b/README	Sun Sep 12 04:56:50 2004 +0000
@@ -1,4 +1,4 @@
-This is Dropbear, a smallish SSH 2 server.
+This is Dropbear, a smallish SSH 2 server and client.
 
 INSTALL has compilation instructions.
 
@@ -12,3 +12,63 @@
 
 Matt Johnston
 [email protected]
+
+
+In the absence of detailed documentation, some notes follow:
+============================================================================
+
+Server public key auth:
+
+You can use ~/.ssh/authorized_keys in the same way as with OpenSSH, just put
+the key entries in that file. They should be of the form:
+
+ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEAwVa6M6cGVmUcLl2cFzkxEoJd06Ub4bVDsYrWvXhvUV+ZAM9uGuewZBDoAqNKJxoIn0Hyd0Nk/yU99UVv6NWV/5YSHtnf35LKds56j7cuzoQpFIdjNwdxAN0PCET/MG8qyskG/2IE2DPNIaJ3Wy+Ws4IZEgdJgPlTYUBWWtCWOGc= someone@hostname
+
+You must make sure that ~/.ssh, and the key file, are only writable by the
+user.
+
+NOTE: Dropbear ignores authorized_keys options such as those described in the
+OpenSSH sshd manpage, and will not allow a login for these keys. 
+
+============================================================================
+
+Client public key auth:
+
+Dropbear can do public key auth as a client, but you will have to convert
+OpenSSH style keys to Dropbear format, or use dropbearkey to create them.
+
+If you have an OpenSSH-style private key ~/.ssh/id_rsa, you need to do:
+
+dropbearconvert openssh dropbear ~/.ssh/id_rsa  ~/.ssh/id_rsa.db
+dbclient -i ~/.ssh/id_rsa.db <hostname>
+
+Currently encrypted keys aren't supported, neither is agent forwarding. At some
+stage both hopefully will be.
+
+============================================================================
+
+If you want to get the public-key portion of a Dropbear private key, look at
+dropbearkey's '-y' option.
+
+============================================================================
+
+To run the server, you need to generate server keys, this is one-off:
+./dropbearkey -t rsa -f dropbear_rsa_host_key
+./dropbearkey -t dss -f dropbear_dss_host_key
+
+or alternatively convert OpenSSH keys to Dropbear:
+./dropbearconvert openssh dropbear /etc/ssh/ssh_host_dsa_key dropbear_dss_host_key
+
+============================================================================
+
+If the server is run as non-root, you most likely won't be able to allocate a
+pty, and you cannot login as any user other than that running the daemon
+(obviously). Shadow passwords will also be unusable as non-root.
+
+============================================================================
+
+The Dropbear distribution includes a standalone version of OpenSSH's scp
+program. You can compile it with "make scp", you may want to change the path
+of the ssh binary, specified near the top of the scp.c file. By default
+the progress meter isn't compiled in to save space, you can enable it by 
+adding 'SCPPROGRESS=1' to the make commandline.
--- a/TODO	Sun Aug 08 16:57:37 2004 +0000
+++ b/TODO	Sun Sep 12 04:56:50 2004 +0000
@@ -1,29 +1,29 @@
 Current:
 
-Things which need doing:
+Things which might need doing:
 
 - Make options.h generated from configure perhaps?
 
-- investigate self-pipe?
-- fix agent fwd problems
-- improve channel window adjustment algorithm (circular buffering)
+- Improved queueing of unauthed connections
 
-- Don't use pregenerated AES tables
+- fix agent fwd problems
 
-- check PRNG
+- handle /etc/environment in AIX
+
 - check that there aren't timing issues with valid/invalid user authentication
   feedback.
 
-- IP6 (binding to :: takes over ipv4 as well, sigh. If anyone wants to suggest
-  a clean way (ie no V4MAPPED or setsockopt things) please let me know :)
-- Binding to different interfaces (see ipv6 probably)
+- Binding to different interfaces
 
-- PAM ??
-- inetd
 - possible RSA blinding? need to check whether this is vuln to timing attacks
+- check PRNG
 - CTR mode, SSH_MSG_IGNORE sending to improve CBC security
-- DH Group Exchange possibly
+- DH Group Exchange possibly, or just add group14 (whatever it's called today)
 
 - Use m_burn for clearing sensitive items in LTM/LTC
 
 - fix scp.c for IRIX
+
+- Be able to use OpenSSH keys for the client? or at least have some form of 
+  encrypted keys.
+- Client agent forwarding
--- a/authpasswd.h	Sun Aug 08 16:57:37 2004 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,33 +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 _AUTH_PASSWD_
-#define _AUTH_PASSWD_
-
-#ifdef DROPBEAR_PASSWORD_AUTH
-
-void passwordauth();
-
-#endif /* DROPBEAR_PASSWORD_AUTH */
-#endif /* _AUTH_PASSWD_ */
--- a/authpubkey.h	Sun Aug 08 16:57:37 2004 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,33 +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 _PUBKEY_AUTH_
-#define _PUBKEY_AUTH_
-
-#ifdef DROPBEAR_PUBKEY_AUTH
-
-void pubkeyauth();
-
-#endif /* DROPBEAR_PUBKEY_AUTH */
-#endif /* _PUBKEY_AUTH_ */
--- a/buffer.c	Sun Aug 08 16:57:37 2004 +0000
+++ b/buffer.c	Sun Sep 12 04:56:50 2004 +0000
@@ -1,5 +1,5 @@
 /*
- * Dropbear - a SSH2 server
+ * Dropbear SSH
  * 
  * Copyright (c) 2002,2003 Matt Johnston
  * All rights reserved.
@@ -34,8 +34,8 @@
 #define BUF_MAX_INCR 1000000000
 #define BUF_MAX_SIZE 1000000000
 
-/* avoid excessively large numbers, > 5000 bit */
-#define BUF_MAX_MPINT (5000 / 8)
+/* avoid excessively large numbers, > ~8192 bits */
+#define BUF_MAX_MPINT (8240 / 8)
 
 /* Create (malloc) a new buffer of size */
 buffer* buf_new(unsigned int size) {
@@ -76,7 +76,8 @@
 
 }
 
-/* resize a buffer, pos and len will be repositioned if required */
+/* resize a buffer, pos and len will be repositioned if required when
+ * downsizing */
 void buf_resize(buffer *buf, unsigned int newsize) {
 
 	if (newsize > BUF_MAX_SIZE) {
@@ -151,6 +152,8 @@
 /* Get a byte from the buffer and increment the pos */
 unsigned char buf_getbyte(buffer* buf) {
 
+	/* This check is really just ==, but the >= allows us to check for the
+	 * assert()able case of pos > len, which should _never_ happen. */
 	if (buf->pos >= buf->len) {
 		dropbear_exit("bad buf_getbyte");
 	}
--- a/channel.h	Sun Aug 08 16:57:37 2004 +0000
+++ b/channel.h	Sun Sep 12 04:56:50 2004 +0000
@@ -27,6 +27,7 @@
 
 #include "includes.h"
 #include "buffer.h"
+#include "circbuffer.h"
 
 /* channel->type values */
 #define CHANNEL_ID_NONE 0
@@ -41,14 +42,18 @@
 #define SSH_OPEN_UNKNOWN_CHANNEL_TYPE           3
 #define SSH_OPEN_RESOURCE_SHORTAGE              4
 
-#define MAX_CHANNELS 60 /* simple mem restriction, includes each tcp/x11
+/* Not a real type */
+#define SSH_OPEN_IN_PROGRESS					99
+
+#define MAX_CHANNELS 100 /* simple mem restriction, includes each tcp/x11
 							connection, so can't be _too_ small */
 
 #define CHAN_EXTEND_SIZE 3 /* how many extra slots to add when we need more */
 
-#define RECV_MAXWINDOW 6000 /* tweak */
-#define RECV_MAXPACKET 1400 /* tweak */
-#define RECV_MINWINDOW 19000 /* when we get below this, we send a windowadjust */
+#define RECV_MAXWINDOW 4000 /* tweak */
+#define RECV_WINDOWEXTEND 500 /* We send a "window extend" every
+								RECV_WINDOWEXTEND bytes */
+#define RECV_MAXPACKET RECV_MAXWINDOW /* tweak */
 
 struct ChanType;
 
@@ -57,15 +62,16 @@
 	unsigned int index; /* the local channel index */
 	unsigned int remotechan;
 	unsigned int recvwindow, transwindow;
+	unsigned int recvdonelen;
 	unsigned int recvmaxpacket, transmaxpacket;
 	void* typedata; /* a pointer to type specific data */
-	int infd; /* stdin for the program, we write to this */
-	int outfd; /* stdout for the program, we read from this */
-	int errfd; /* stdout for a program. This doesn't really fit here,
-				  but makes the code a lot tidyer without being too bad. This
-				  is -1 for channels which don't requre it. Currently only
-				  a 'session' without a pty will use it */
-	buffer *writebuf; /* data for the program */
+	int infd; /* data to send over the wire */
+	int outfd; /* data for consumption, what was in writebuf */
+	int errfd; /* used like infd or errfd, depending if it's client or server.
+				  Doesn't exactly belong here, but is cleaner here */
+	circbuffer *writebuf; /* data from the wire, for local consumption */
+	circbuffer *extrabuf; /* extended-data for the program - used like writebuf
+					     but for stderr */
 
 	int sentclosed, recvclosed;
 
@@ -94,6 +100,7 @@
 void chancleanup();
 void setchannelfds(fd_set *readfd, fd_set *writefd);
 void channelio(fd_set *readfd, fd_set *writefd);
+struct Channel* getchannel(unsigned int chan);
 struct Channel* newchannel(unsigned int remotechan, 
 		const struct ChanType *type, 
 		unsigned int transwindow, unsigned int transmaxpacket);
@@ -103,10 +110,16 @@
 void send_msg_channel_failure(struct Channel *channel);
 void send_msg_channel_success(struct Channel *channel);
 void recv_msg_channel_data();
+void recv_msg_channel_extended_data();
 void recv_msg_channel_window_adjust();
 void recv_msg_channel_close();
 void recv_msg_channel_eof();
 
+void common_recv_msg_channel_data(struct Channel *channel, int fd, 
+		circbuffer * buf);
+
+const struct ChanType clichansess;
+
 #ifdef USING_LISTENERS
 int send_msg_channel_open_init(int fd, const struct ChanType *type);
 void recv_msg_channel_open_confirmation();
--- a/chansession.h	Sun Aug 08 16:57:37 2004 +0000
+++ b/chansession.h	Sun Sep 12 04:56:50 2004 +0000
@@ -68,11 +68,6 @@
 };
 
 
-void chansessionrequest(struct Channel * channel);
-void send_msg_chansess_exitstatus(struct Channel * channel,
-		struct ChanSess * chansess);
-void send_msg_chansess_exitsignal(struct Channel * channel,
-		struct ChanSess * chansess);
 void addnewvar(const char* param, const char* var);
 
 void cli_send_chansess_request();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/circbuffer.c	Sun Sep 12 04:56:50 2004 +0000
@@ -0,0 +1,138 @@
+/*
+ * Dropbear SSH
+ * 
+ * Copyright (c) 2002-2004 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. */
+
+#include "includes.h"
+#include "dbutil.h"
+#include "circbuffer.h"
+
+#define MAX_CBUF_SIZE 100000000
+
+circbuffer * cbuf_new(unsigned int size) {
+
+	circbuffer *cbuf = NULL;
+
+	if (size > MAX_CBUF_SIZE) {
+		dropbear_exit("bad cbuf size");
+	}
+
+	cbuf = (circbuffer*)m_malloc(sizeof(circbuffer));
+	cbuf->data = (unsigned char*)m_malloc(size);
+	cbuf->used = 0;
+	cbuf->readpos = 0;
+	cbuf->writepos = 0;
+	cbuf->size = size;
+
+	return cbuf;
+}
+
+void cbuf_free(circbuffer * cbuf) {
+
+	m_free(cbuf->data);
+	m_free(cbuf);
+}
+
+unsigned int cbuf_getused(circbuffer * cbuf) {
+
+	return cbuf->used;
+
+}
+
+unsigned int cbuf_getavail(circbuffer * cbuf) {
+
+	return cbuf->size - cbuf->used;
+
+}
+
+unsigned int cbuf_readlen(circbuffer *cbuf) {
+
+	assert(((2*cbuf->size)+cbuf->writepos-cbuf->readpos)%cbuf->size == cbuf->used%cbuf->size);
+	assert(((2*cbuf->size)+cbuf->readpos-cbuf->writepos)%cbuf->size == (cbuf->size-cbuf->used)%cbuf->size);
+
+	if (cbuf->used == 0) {
+		TRACE(("cbuf_readlen: unused buffer"));
+		return 0;
+	}
+
+	if (cbuf->readpos < cbuf->writepos) {
+		return cbuf->writepos - cbuf->readpos;
+	}
+
+	return cbuf->size - cbuf->readpos;
+}
+
+unsigned int cbuf_writelen(circbuffer *cbuf) {
+
+	assert(cbuf->used <= cbuf->size);
+	assert(((2*cbuf->size)+cbuf->writepos-cbuf->readpos)%cbuf->size == cbuf->used%cbuf->size);
+	assert(((2*cbuf->size)+cbuf->readpos-cbuf->writepos)%cbuf->size == (cbuf->size-cbuf->used)%cbuf->size);
+
+	if (cbuf->used == cbuf->size) {
+		TRACE(("cbuf_writelen: full buffer"));
+		return 0; /* full */
+	}
+	
+	if (cbuf->writepos < cbuf->readpos) {
+		return cbuf->readpos - cbuf->writepos;
+	}
+
+	return cbuf->size - cbuf->writepos;
+}
+
+unsigned char* cbuf_readptr(circbuffer *cbuf, unsigned int len) {
+	if (len > cbuf_readlen(cbuf)) {
+		dropbear_exit("bad cbuf read");
+	}
+
+	return &cbuf->data[cbuf->readpos];
+}
+
+unsigned char* cbuf_writeptr(circbuffer *cbuf, unsigned int len) {
+
+	if (len > cbuf_writelen(cbuf)) {
+		dropbear_exit("bad cbuf write");
+	}
+
+	return &cbuf->data[cbuf->writepos];
+}
+
+void cbuf_incrwrite(circbuffer *cbuf, unsigned int len) {
+	if (len > cbuf_writelen(cbuf)) {
+		dropbear_exit("bad cbuf write");
+	}
+
+	cbuf->used += len;
+	assert(cbuf->used <= cbuf->size);
+	cbuf->writepos = (cbuf->writepos + len) % cbuf->size;
+}
+
+
+void cbuf_incrread(circbuffer *cbuf, unsigned int len) {
+	if (len > cbuf_readlen(cbuf)) {
+		dropbear_exit("bad cbuf read");
+	}
+
+	assert(cbuf->used >= len);
+	cbuf->used -= len;
+	cbuf->readpos = (cbuf->readpos + len) % cbuf->size;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/circbuffer.h	Sun Sep 12 04:56:50 2004 +0000
@@ -0,0 +1,50 @@
+/*
+ * Dropbear SSH
+ * 
+ * Copyright (c) 2002-2004 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 _CIRCBUFFER_H_
+#define _CIRCBUFFER_H_
+struct circbuf {
+
+	unsigned int size;
+	unsigned int readpos;
+	unsigned int writepos;
+	unsigned int used;
+	unsigned char* data;
+};
+
+typedef struct circbuf circbuffer;
+
+circbuffer * cbuf_new(unsigned int size);
+void cbuf_free(circbuffer * cbuf);
+
+unsigned int cbuf_getused(circbuffer * cbuf); /* how much data stored */
+unsigned int cbuf_getavail(circbuffer * cbuf); /* how much we can write */
+unsigned int cbuf_readlen(circbuffer *cbuf); /* max linear read len */
+unsigned int cbuf_writelen(circbuffer *cbuf); /* max linear write len */
+
+unsigned char* cbuf_readptr(circbuffer *cbuf, unsigned int len);
+unsigned char* cbuf_writeptr(circbuffer *cbuf, unsigned int len);
+void cbuf_incrwrite(circbuffer *cbuf, unsigned int len);
+void cbuf_incrread(circbuffer *cbuf, unsigned int len);
+#endif
--- a/cli-auth.c	Sun Aug 08 16:57:37 2004 +0000
+++ b/cli-auth.c	Sun Sep 12 04:56:50 2004 +0000
@@ -1,3 +1,28 @@
+/*
+ * Dropbear SSH
+ * 
+ * Copyright (c) 2002,2003 Matt Johnston
+ * Copyright (c) 2004 by Mihnea Stoenescu
+ * 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. */
+
 #include "includes.h"
 #include "session.h"
 #include "auth.h"
@@ -92,7 +117,7 @@
 		return;
 	}
 
-#ifdef DROPBEAR_PUBKEY_AUTH
+#ifdef ENABLE_CLI_PUBKEY_AUTH
 	/* If it was a pubkey auth request, we should cross that key 
 	 * off the list. */
 	if (cli_ses.lastauthtype == AUTH_TYPE_PUBKEY) {
@@ -126,13 +151,13 @@
 	for (i = 0; i <= methlen; i++) {
 		if (methods[i] == '\0') {
 			TRACE(("auth method '%s'", tok));
-#ifdef DROPBEAR_PUBKEY_AUTH
+#ifdef ENABLE_CLI_PUBKEY_AUTH
 			if (strncmp(AUTH_METHOD_PUBKEY, tok,
 				AUTH_METHOD_PUBKEY_LEN) == 0) {
 				ses.authstate.authtypes |= AUTH_TYPE_PUBKEY;
 			}
 #endif
-#ifdef DROPBEAR_PASSWORD_AUTH
+#ifdef ENABLE_CLI_PASSWORD_AUTH
 			if (strncmp(AUTH_METHOD_PASSWORD, tok,
 				AUTH_METHOD_PASSWORD_LEN) == 0) {
 				ses.authstate.authtypes |= AUTH_TYPE_PASSWORD;
@@ -144,6 +169,8 @@
 		}
 	}
 
+	m_free(methods);
+
 	cli_ses.state = USERAUTH_FAIL_RCVD;
 		
 	TRACE(("leave recv_msg_userauth_failure"));
@@ -163,14 +190,14 @@
 	CHECKCLEARTOWRITE();
 	
 	/* XXX We hardcode that we try a pubkey first */
-#ifdef DROPBEAR_PUBKEY_AUTH
+#ifdef ENABLE_CLI_PUBKEY_AUTH
 	if (ses.authstate.authtypes & AUTH_TYPE_PUBKEY) {
 		finished = cli_auth_pubkey();
 		cli_ses.lastauthtype = AUTH_TYPE_PUBKEY;
 	}
 #endif
 
-#ifdef DROPBEAR_PASSWORD_AUTH
+#ifdef ENABLE_CLI_PASSWORD_AUTH
 	if (!finished && ses.authstate.authtypes & AUTH_TYPE_PASSWORD) {
 		finished = cli_auth_password();
 		cli_ses.lastauthtype = AUTH_TYPE_PASSWORD;
--- a/cli-authpasswd.c	Sun Aug 08 16:57:37 2004 +0000
+++ b/cli-authpasswd.c	Sun Sep 12 04:56:50 2004 +0000
@@ -1,3 +1,27 @@
+/*
+ * Dropbear SSH
+ * 
+ * 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. */
+
 #include "includes.h"
 #include "buffer.h"
 #include "dbutil.h"
@@ -5,6 +29,7 @@
 #include "ssh.h"
 #include "runopts.h"
 
+#ifdef ENABLE_CLI_PASSWORD_AUTH
 int cli_auth_password() {
 
 	char* password = NULL;
@@ -35,3 +60,4 @@
 	return 1; /* Password auth can always be tried */
 
 }
+#endif
--- a/cli-authpubkey.c	Sun Aug 08 16:57:37 2004 +0000
+++ b/cli-authpubkey.c	Sun Sep 12 04:56:50 2004 +0000
@@ -1,3 +1,28 @@
+/*
+ * Dropbear SSH
+ * 
+ * Copyright (c) 2002,2003 Matt Johnston
+ * Copyright (c) 2004 by Mihnea Stoenescu
+ * 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. */
+
 #include "includes.h"
 #include "buffer.h"
 #include "dbutil.h"
@@ -6,6 +31,7 @@
 #include "runopts.h"
 #include "auth.h"
 
+#ifdef ENABLE_CLI_PUBKEY_AUTH
 static void send_msg_userauth_pubkey(sign_key *key, int type, int realsign);
 
 /* Called when we receive a SSH_MSG_USERAUTH_FAILURE for a pubkey request.
@@ -13,17 +39,22 @@
 void cli_pubkeyfail() {
 
 	struct PubkeyList *keyitem;
+	struct PubkeyList **previtem;
 
 	TRACE(("enter cli_pubkeyfail"));
+	previtem = &cli_opts.pubkeys;
+
 	/* Find the key we failed with, and remove it */
 	for (keyitem = cli_opts.pubkeys; keyitem != NULL; keyitem = keyitem->next) {
-		if (keyitem->next == cli_ses.lastpubkey) {
-			keyitem->next = cli_ses.lastpubkey->next;
+		if (keyitem == cli_ses.lastpubkey) {
+			*previtem = keyitem->next;
 		}
+		previtem = &keyitem;
 	}
 
 	sign_key_free(cli_ses.lastpubkey->key); /* It won't be used again */
 	m_free(cli_ses.lastpubkey);
+
 	TRACE(("leave cli_pubkeyfail"));
 }
 
@@ -145,6 +176,7 @@
 		/* Send a trial request */
 		send_msg_userauth_pubkey(cli_opts.pubkeys->key,
 				cli_opts.pubkeys->type, 0);
+		cli_ses.lastpubkey = cli_opts.pubkeys;
 		TRACE(("leave cli_auth_pubkey-success"));
 		return 1;
 	} else {
@@ -152,3 +184,4 @@
 		return 0;
 	}
 }
+#endif /* Pubkey auth */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cli-channel.c	Sun Sep 12 04:56:50 2004 +0000
@@ -0,0 +1,65 @@
+/*
+ * Dropbear SSH
+ * 
+ * Copyright (c) 2002-2004 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. */
+
+#include "includes.h"
+#include "channel.h"
+#include "buffer.h"
+#include "circbuffer.h"
+#include "dbutil.h"
+#include "session.h"
+#include "ssh.h"
+
+/* We receive channel data - only used by the client chansession code*/
+void recv_msg_channel_extended_data() {
+
+	unsigned int chan;
+	struct Channel *channel;
+	unsigned int datatype;
+
+	TRACE(("enter recv_msg_channel_extended_data"));
+
+	chan = buf_getint(ses.payload);
+	channel = getchannel(chan);
+
+	if (channel == NULL) {
+		dropbear_exit("Unknown channel");
+	}
+
+	if (channel->type != &clichansess) {
+		TRACE(("leave recv_msg_channel_extended_data: chantype is wrong"));
+		return; /* we just ignore it */
+	}
+
+	datatype = buf_getint(ses.payload);
+	
+	if (datatype != SSH_EXTENDED_DATA_STDERR) {
+		TRACE(("leave recv_msg_channel_extended_data: wrong datatype: %d",
+					datatype));
+		return;	
+	}
+
+	common_recv_msg_channel_data(channel, channel->errfd, channel->extrabuf);
+
+	TRACE(("leave recv_msg_channel_extended_data"));
+}
--- a/cli-chansession.c	Sun Aug 08 16:57:37 2004 +0000
+++ b/cli-chansession.c	Sun Sep 12 04:56:50 2004 +0000
@@ -1,3 +1,28 @@
+/*
+ * Dropbear SSH
+ * 
+ * Copyright (c) 2002,2003 Matt Johnston
+ * Copyright (c) 2004 by Mihnea Stoenescu
+ * 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. */
+
 #include "includes.h"
 #include "packet.h"
 #include "buffer.h"
@@ -7,9 +32,11 @@
 #include "ssh.h"
 #include "runopts.h"
 #include "termcodes.h"
+#include "chansession.h"
 
 static void cli_closechansess(struct Channel *channel);
 static int cli_initchansess(struct Channel *channel);
+static void cli_chansessreq(struct Channel *channel);
 
 static void start_channel_request(struct Channel *channel, unsigned char *type);
 
@@ -17,19 +44,43 @@
 static void send_chansess_shell_req(struct Channel *channel);
 
 static void cli_tty_setup();
-void cli_tty_cleanup();
 
-static const struct ChanType clichansess = {
+const struct ChanType clichansess = {
 	0, /* sepfds */
 	"session", /* name */
 	cli_initchansess, /* inithandler */
 	NULL, /* checkclosehandler */
-	NULL, /* reqhandler */
+	cli_chansessreq, /* reqhandler */
 	cli_closechansess, /* closehandler */
 };
 
+static void cli_chansessreq(struct Channel *channel) {
+
+	unsigned char* type = NULL;
+	int wantreply;
+
+	TRACE(("enter cli_chansessreq"));
+
+	type = buf_getstring(ses.payload, NULL);
+	wantreply = buf_getbyte(ses.payload);
+
+	if (strcmp(type, "exit-status") != 0) {
+		TRACE(("unknown request '%s'", type));
+		send_msg_channel_failure(channel);
+		goto out;
+	}
+		
+	/* We'll just trust what they tell us */
+	cli_ses.retval = buf_getint(ses.payload);
+	TRACE(("got exit-status of '%d'", cli_ses.retval));
+
+out:
+	m_free(type);
+}
+	
+
 /* If the main session goes, we close it up */
-static void cli_closechansess(struct Channel *channel) {
+static void cli_closechansess(struct Channel *UNUSED(channel)) {
 
 	/* This channel hasn't gone yet, so we have > 1 */
 	if (ses.chancount > 1) {
@@ -178,7 +229,7 @@
 	bufpos2 = ses.writepayload->pos;
 
 	buf_setpos(ses.writepayload, bufpos1); /* Jump back */
-	buf_putint(ses.writepayload, bufpos2 - bufpos1); /* len(termcodes) */
+	buf_putint(ses.writepayload, bufpos2 - bufpos1 - 4); /* len(termcodes) */
 	buf_setpos(ses.writepayload, bufpos2); /* Back where we were */
 
 	TRACE(("leave put_termcodes"));
@@ -203,7 +254,7 @@
 
 }
 
-static void sigwinch_handler(int dummy) {
+static void sigwinch_handler(int UNUSED(unused)) {
 
 	cli_ses.winchange = 1;
 
@@ -288,9 +339,17 @@
 
 static int cli_initchansess(struct Channel *channel) {
 
+
 	channel->infd = STDOUT_FILENO;
-	//channel->outfd = STDIN_FILENO;
-	//channel->errfd = STDERR_FILENO;
+	setnonblocking(STDOUT_FILENO);
+
+	channel->outfd = STDIN_FILENO;
+	setnonblocking(STDIN_FILENO);
+
+	channel->errfd = STDERR_FILENO;
+	setnonblocking(STDERR_FILENO);
+
+	channel->extrabuf = cbuf_new(RECV_MAXWINDOW);
 
 	if (cli_opts.wantpty) {
 		send_chansess_pty_req(channel);
--- a/cli-kex.c	Sun Aug 08 16:57:37 2004 +0000
+++ b/cli-kex.c	Sun Sep 12 04:56:50 2004 +0000
@@ -1,7 +1,8 @@
 /*
  * Dropbear - a SSH2 server
  * 
- * Copyright (c) 2002,2003 Matt Johnston
+ * Copyright (c) 2002-2004 Matt Johnston
+ * Copyright (c) 2004 by Mihnea Stoenescu
  * All rights reserved.
  * 
  * Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -44,8 +45,8 @@
 
 	cli_ses.dh_e = (mp_int*)m_malloc(sizeof(mp_int));
 	cli_ses.dh_x = (mp_int*)m_malloc(sizeof(mp_int));
+	m_mp_init_multi(cli_ses.dh_e, cli_ses.dh_x, NULL);
 
-	m_mp_init_multi(cli_ses.dh_e, cli_ses.dh_x, NULL);
 	gen_kexdh_vals(cli_ses.dh_e, cli_ses.dh_x);
 
 	CHECKCLEARTOWRITE();
@@ -58,13 +59,18 @@
 /* Handle a diffie-hellman key exchange reply. */
 void recv_msg_kexdh_reply() {
 
-	mp_int dh_f;
+	DEF_MP_INT(dh_f);
 	sign_key *hostkey = NULL;
 	unsigned int type, keybloblen;
 	unsigned char* keyblob = NULL;
 
 
 	TRACE(("enter recv_msg_kexdh_reply"));
+
+	if (cli_ses.kex_state != KEXDH_INIT_SENT) {
+		dropbear_exit("Received out-of-order kexdhreply");
+	}
+	m_mp_init(&dh_f);
 	type = ses.newkeys->algo_hostkey;
 	TRACE(("type is %d", type));
 
@@ -82,7 +88,6 @@
 		dropbear_exit("Bad KEX packet");
 	}
 
-	m_mp_init(&dh_f);
 	if (buf_getmpint(ses.payload, &dh_f) != DROPBEAR_SUCCESS) {
 		TRACE(("failed getting mpint"));
 		dropbear_exit("Bad KEX packet");
@@ -90,6 +95,9 @@
 
 	kexdh_comb_key(cli_ses.dh_e, cli_ses.dh_x, &dh_f, hostkey);
 	mp_clear(&dh_f);
+	mp_clear_multi(cli_ses.dh_e, cli_ses.dh_x, NULL);
+	m_free(cli_ses.dh_e);
+	m_free(cli_ses.dh_x);
 
 	if (buf_verify(ses.payload, hostkey, ses.hash, SHA1_HASH_SIZE) 
 			!= DROPBEAR_SUCCESS) {
@@ -125,8 +133,10 @@
 
 	char * filename = NULL;
 	FILE *hostsfile = NULL;
+	int readonly = 0;
 	struct passwd *pw = NULL;
-	unsigned int len, hostlen;
+	unsigned int hostlen, algolen;
+	unsigned long len;
 	const char *algoname = NULL;
 	buffer * line = NULL;
 	int ret;
@@ -144,20 +154,37 @@
 	/* Check that ~/.ssh exists - easiest way is just to mkdir */
 	if (mkdir(filename, S_IRWXU) != 0) {
 		if (errno != EEXIST) {
+			dropbear_log(LOG_INFO, "Warning: failed creating ~/.ssh: %s",
+					strerror(errno));
+			TRACE(("mkdir didn't work: %s", strerror(errno)));
 			ask_to_confirm(keyblob, keybloblen);
 			goto out; /* only get here on success */
 		}
 	}
 
 	snprintf(filename, len+18, "%s/.ssh/known_hosts", pw->pw_dir);
-	hostsfile = fopen(filename, "r+");
+	hostsfile = fopen(filename, "a+");
+	
+	if (hostsfile != NULL) {
+		fseek(hostsfile, 0, SEEK_SET);
+	} else {
+		/* We mightn't have been able to open it if it was read-only */
+		if (errno == EACCES || errno == EROFS) {
+				TRACE(("trying readonly: %s", strerror(errno)));
+				readonly = 1;
+				hostsfile = fopen(filename, "r");
+		}
+	}
+
 	if (hostsfile == NULL) {
+		TRACE(("hostsfile didn't open: %s", strerror(errno)));
 		ask_to_confirm(keyblob, keybloblen);
 		goto out; /* We only get here on success */
 	}
 
 	line = buf_new(MAX_KNOWNHOSTS_LINE);
 	hostlen = strlen(cli_opts.remotehost);
+	algoname = signkey_name_from_type(ses.newkeys->algo_hostkey, &algolen);
 
 	do {
 		if (buf_getline(line, hostsfile) == DROPBEAR_FAILURE) {
@@ -188,20 +215,19 @@
 			continue;
 		}
 
-		algoname = signkey_name_from_type(ses.newkeys->algo_hostkey, &len);
-		if ( strncmp(buf_getptr(line, len), algoname, len) != 0) {
+		if ( strncmp(buf_getptr(line, algolen), algoname, algolen) != 0) {
 			TRACE(("algo doesn't match"));
 			continue;
 		}
 
-		buf_incrpos(line, len);
+		buf_incrpos(line, algolen);
 		if (buf_getbyte(line) != ' ') {
 			TRACE(("missing space after algo"));
 			continue;
 		}
 
 		/* Now we're at the interesting hostkey */
-		ret = cmp_base64_key(keyblob, keybloblen, algoname, len, line);
+		ret = cmp_base64_key(keyblob, keybloblen, algoname, algolen, line);
 
 		if (ret == DROPBEAR_SUCCESS) {
 			/* Good matching key */
@@ -214,12 +240,39 @@
 
 	/* Key doesn't exist yet */
 	ask_to_confirm(keyblob, keybloblen);
+
 	/* If we get here, they said yes */
 
+	if (readonly) {
+		TRACE(("readonly"));
+		goto out;
+	}
+
+	/* put the new entry in the file */
+	fseek(hostsfile, 0, SEEK_END); /* In case it wasn't opened append */
+	buf_setpos(line, 0);
+	buf_setlen(line, 0);
+	buf_putbytes(line, ses.remotehost, hostlen);
+	buf_putbyte(line, ' ');
+	buf_putbytes(line, algoname, algolen);
+	buf_putbyte(line, ' ');
+	len = line->size - line->pos;
+	TRACE(("keybloblen %d, len %d", keybloblen, len));
+	/* The only failure with base64 is buffer_overflow, but buf_getwriteptr
+	 * will die horribly in the case anyway */
+	base64_encode(keyblob, keybloblen, buf_getwriteptr(line, len), &len);
+	buf_incrwritepos(line, len);
+	buf_putbyte(line, '\n');
+	buf_setpos(line, 0);
+	fwrite(buf_getptr(line, line->len), line->len, 1, hostsfile);
+	/* We ignore errors, since there's not much we can do about them */
+
 out:
 	if (hostsfile != NULL) {
 		fclose(hostsfile);
 	}
 	m_free(filename);
-	buf_free(line);
+	if (line != NULL) {
+		buf_free(line);
+	}
 }
--- a/cli-main.c	Sun Aug 08 16:57:37 2004 +0000
+++ b/cli-main.c	Sun Sep 12 04:56:50 2004 +0000
@@ -1,3 +1,29 @@
+/*
+ * Dropbear - a SSH2 server
+ * SSH client implementation
+ * 
+ * Copyright (c) 2002,2003 Matt Johnston
+ * Copyright (c) 2004 by Mihnea Stoenescu
+ * 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. */
+
 #include "includes.h"
 #include "dbutil.h"
 #include "runopts.h"
@@ -26,6 +52,10 @@
 	TRACE(("user='%s' host='%s' port='%s'", cli_opts.username,
 				cli_opts.remotehost, cli_opts.remoteport));
 
+	if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) {
+		dropbear_exit("signal() error");
+	}
+
 	sock = connect_remote(cli_opts.remotehost, cli_opts.remoteport, 
 			0, &error);
 
@@ -70,7 +100,8 @@
 	exit(exitcode);
 }
 
-static void cli_dropbear_log(int priority, const char* format, va_list param) {
+static void cli_dropbear_log(int UNUSED(priority), 
+		const char* format, va_list param) {
 
 	char printbuf[1024];
 
--- a/cli-runopts.c	Sun Aug 08 16:57:37 2004 +0000
+++ b/cli-runopts.c	Sun Sep 12 04:56:50 2004 +0000
@@ -28,26 +28,40 @@
 #include "buffer.h"
 #include "dbutil.h"
 #include "algo.h"
+#include "tcpfwd.h"
 
 cli_runopts cli_opts; /* GLOBAL */
 
 static void printhelp();
 static void parsehostname(char* userhostarg);
-#ifdef DROPBEAR_PUBKEY_AUTH
+#ifdef ENABLE_CLI_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() {
 
 	fprintf(stderr, "Dropbear client v%s\n"
-					"Usage: %s [options] user@host\n"
+					"Usage: %s [options] [user@]host\n"
 					"Options are:\n"
 					"-p <remoteport>\n"
 					"-t    Allocate a pty\n"
 					"-T    Don't allocate a pty\n"
-#ifdef DROPBEAR_PUBKEY_AUTH
+#ifdef ENABLE_CLI_PUBKEY_AUTH
 					"-i <identityfile>   (multiple allowed)\n"
 #endif
+#ifdef ENABLE_CLI_LOCALTCPFWD
+					"-L <listenport:remotehsot:reportport> Local port forwarding\n"
+#endif
+#ifdef ENABLE_CLI_REMOTETCPFWD
+					"-R <listenport:remotehost:remoteport> Remote port forwarding\n"
+#endif
+					"-l <username>\n"
+#ifdef DEBUG_TRACE
+					"-v    verbose\n"
+#endif
 					,DROPBEAR_VERSION, cli_opts.progname);
 }
 
@@ -56,11 +70,16 @@
 	unsigned int i, j;
 	char ** next = 0;
 	unsigned int cmdlen;
-#ifdef DROPBEAR_PUBKEY_AUTH
+#ifdef ENABLE_CLI_PUBKEY_AUTH
 	int nextiskey = 0; /* A flag if the next argument is a keyfile */
 #endif
-
-
+#ifdef ENABLE_CLI_LOCALTCPFWD
+	int nextislocal = 0;
+#endif
+#ifdef ENABLE_CLI_REMOTETCPFWD
+	int nextisremote = 0;
+#endif
+	char* dummy = NULL; /* Not used for anything real */
 
 	/* see printhelp() for options */
 	cli_opts.progname = argv[0];
@@ -69,9 +88,15 @@
 	cli_opts.username = NULL;
 	cli_opts.cmd = NULL;
 	cli_opts.wantpty = 9; /* 9 means "it hasn't been touched", gets set later */
-#ifdef DROPBEAR_PUBKEY_AUTH
+#ifdef ENABLE_CLI_PUBKEY_AUTH
 	cli_opts.pubkeys = NULL;
 #endif
+#ifdef ENABLE_CLI_LOCALTCPFWD
+	cli_opts.localfwds = NULL;
+#endif
+#ifdef ENABLE_CLI_REMOTETCPFWD
+	cli_opts.remotefwds = NULL;
+#endif
 	opts.nolocaltcp = 0;
 	opts.noremotetcp = 0;
 	/* not yet
@@ -81,7 +106,7 @@
 
 	/* Iterate all the arguments */
 	for (i = 1; i < (unsigned int)argc; i++) {
-#ifdef DROPBEAR_PUBKEY_AUTH
+#ifdef ENABLE_CLI_PUBKEY_AUTH
 		if (nextiskey) {
 			/* Load a hostkey since the previous argument was "-i" */
 			loadidentityfile(argv[i]);
@@ -89,6 +114,22 @@
 			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 */
 			*next = argv[i];
@@ -106,7 +147,7 @@
 				case 'p': /* remoteport */
 					next = &cli_opts.remoteport;
 					break;
-#ifdef DROPBEAR_PUBKEY_AUTH
+#ifdef ENABLE_CLI_PUBKEY_AUTH
 				case 'i': /* an identityfile */
 					nextiskey = 1;
 					break;
@@ -117,17 +158,58 @@
 				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
+				case 'l':
+					next = &cli_opts.username;
+					break;
+				case 'h':
+					printhelp();
+					exit(EXIT_SUCCESS);
+					break;
+#ifdef DEBUG_TRACE
+				case 'v':
+					debug_trace = 1;
+					break;
+#endif
+				case 'F':
+				case 'e':
+				case 'c':
+				case 'm':
+				case 'D':
+#ifndef ENABLE_CLI_REMOTETCPFWD
+				case 'R':
+#endif
+#ifndef ENABLE_CLI_LOCALTCPFWD
+				case 'L':
+#endif
+				case 'o':
+				case 'b':
+					next = &dummy;
 				default:
-					fprintf(stderr, "Unknown argument '%s'\n", argv[i]);
-					printhelp();
-					exit(EXIT_FAILURE);
+					fprintf(stderr, 
+						"WARNING: Ignoring unknown argument '%s'\n", argv[i]);
 					break;
 			} /* Switch */
+			
+			/* Now we handle args where they might be "-luser" (no spaces)*/
+			if (next && strlen(argv[i]) > 2) {
+				*next = &argv[i][2];
+				next = NULL;
+			}
 
 			continue; /* next argument */
 
 		} else {
-			TRACE(("non-flag arg"));
+			TRACE(("non-flag arg: '%s'", argv[i]));
 
 			/* Either the hostname or commands */
 
@@ -162,7 +244,8 @@
 	}
 
 	if (cli_opts.remotehost == NULL) {
-		dropbear_exit("Bad syntax");
+		printhelp();
+		exit(EXIT_FAILURE);
 	}
 
 	if (cli_opts.remoteport == NULL) {
@@ -180,7 +263,7 @@
 	}
 }
 
-#ifdef DROPBEAR_PUBKEY_AUTH
+#ifdef ENABLE_CLI_PUBKEY_AUTH
 static void loadidentityfile(const char* filename) {
 
 	struct PubkeyList * nextkey;
@@ -208,10 +291,14 @@
 
 /* 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) {
@@ -239,3 +326,81 @@
 		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
--- a/cli-service.c	Sun Aug 08 16:57:37 2004 +0000
+++ b/cli-service.c	Sun Sep 12 04:56:50 2004 +0000
@@ -1,3 +1,28 @@
+/*
+ * Dropbear SSH
+ * 
+ * Copyright (c) 2002,2003 Matt Johnston
+ * Copyright (c) 2004 by Mihnea Stoenescu
+ * 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. */
+
 #include "includes.h"
 #include "service.h"
 #include "dbutil.h"
--- a/cli-session.c	Sun Aug 08 16:57:37 2004 +0000
+++ b/cli-session.c	Sun Sep 12 04:56:50 2004 +0000
@@ -1,11 +1,35 @@
+/*
+ * Dropbear SSH
+ * 
+ * Copyright (c) 2002,2003 Matt Johnston
+ * Copyright (c) 2004 by Mihnea Stoenescu
+ * 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. */
+
 #include "includes.h"
 #include "session.h"
 #include "dbutil.h"
 #include "kex.h"
 #include "ssh.h"
 #include "packet.h"
-#include "tcpfwd-direct.h"
-#include "tcpfwd-remote.h"
+#include "tcpfwd.h"
 #include "channel.h"
 #include "random.h"
 #include "service.h"
@@ -22,8 +46,9 @@
 /* Sorted in decreasing frequency will be more efficient - data and window
  * should be first */
 static const packettype cli_packettypes[] = {
-	/* TYPE, AUTHREQUIRED, FUNCTION */
+	/* TYPE, FUNCTION */
 	{SSH_MSG_CHANNEL_DATA, recv_msg_channel_data},
+	{SSH_MSG_CHANNEL_EXTENDED_DATA, recv_msg_channel_extended_data},
 	{SSH_MSG_CHANNEL_WINDOW_ADJUST, recv_msg_channel_window_adjust},
 	{SSH_MSG_USERAUTH_FAILURE, recv_msg_userauth_failure}, /* client */
 	{SSH_MSG_USERAUTH_SUCCESS, recv_msg_userauth_success}, /* client */
@@ -31,7 +56,6 @@
 	{SSH_MSG_KEXDH_REPLY, recv_msg_kexdh_reply}, /* client */
 	{SSH_MSG_NEWKEYS, recv_msg_newkeys},
 	{SSH_MSG_SERVICE_ACCEPT, recv_msg_service_accept}, /* client */
-	{SSH_MSG_GLOBAL_REQUEST, recv_msg_global_request_remotetcp},
 	{SSH_MSG_CHANNEL_REQUEST, recv_msg_channel_request},
 	{SSH_MSG_CHANNEL_OPEN, recv_msg_channel_open},
 	{SSH_MSG_CHANNEL_EOF, recv_msg_channel_eof},
@@ -39,15 +63,16 @@
 	{SSH_MSG_CHANNEL_OPEN_CONFIRMATION, recv_msg_channel_open_confirmation},
 	{SSH_MSG_CHANNEL_OPEN_FAILURE, recv_msg_channel_open_failure},
 	{SSH_MSG_USERAUTH_BANNER, recv_msg_userauth_banner}, /* client */
-#ifdef DROPBEAR_PUBKEY_AUTH
+#ifdef ENABLE_CLI_PUBKEY_AUTH
 	{SSH_MSG_USERAUTH_PK_OK, recv_msg_userauth_pk_ok}, /* client */
 #endif
 	{0, 0} /* End */
 };
 
 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 */
 };
 
@@ -88,6 +113,14 @@
 	cli_ses.tty_raw_mode = 0;
 	cli_ses.winchange = 0;
 
+	/* We store stdin's flags, so we can set them back on exit (otherwise
+	 * busybox's ash isn't happy */
+	cli_ses.stdincopy = dup(STDIN_FILENO);
+	cli_ses.stdinflags = fcntl(STDIN_FILENO, F_GETFL, 0);
+
+	cli_ses.retval = EXIT_SUCCESS; /* Assume it's clean if we don't get a
+									  specific exit status */
+
 	/* Auth */
 	cli_ses.lastpubkey = NULL;
 	cli_ses.lastauthtype = NULL;
@@ -179,6 +212,12 @@
 			*/
 
 		case USERAUTH_SUCCESS_RCVD:
+#ifdef ENABLE_CLI_LOCALTCPFWD
+			setup_localtcp();
+#endif
+#ifdef ENABLE_CLI_REMOTETCPFWD
+			setup_remotetcp();
+#endif
 			cli_send_chansess_request();
 			TRACE(("leave cli_sessionloop: cli_send_chansess_request"));
 			cli_ses.state = SESSION_RUNNING;
@@ -210,6 +249,11 @@
 	if (!sessinitdone) {
 		return;
 	}
+
+	/* Set stdin back to non-blocking - busybox ash dies nastily
+	 * if we don't revert the flags */
+	fcntl(cli_ses.stdincopy, F_SETFL, cli_ses.stdinflags);
+
 	cli_tty_cleanup();
 
 }
@@ -220,11 +264,10 @@
 	common_session_cleanup();
 	fprintf(stderr, "Connection to %s@%s:%s closed.\n", cli_opts.username,
 			cli_opts.remotehost, cli_opts.remoteport);
-	exit(EXIT_SUCCESS);
+	exit(cli_ses.retval);
 }
 
 
-
 /* called when the remote side closes the connection */
 static void cli_remoteclosed() {
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cli-tcpfwd.c	Sun Sep 12 04:56:50 2004 +0000
@@ -0,0 +1,193 @@
+/*
+ * Dropbear SSH
+ * 
+ * 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. */
+
+#include "includes.h"
+#include "options.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() {
+
+	int ret;
+
+	TRACE(("enter setup_localtcp"));
+
+	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;
+	}
+	TRACE(("leave setup_localtcp"));
+
+}
+
+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 = m_strdup(remoteaddr);
+	tcpinfo->sendport = remoteport;
+	tcpinfo->listenport = listenport;
+	tcpinfo->chantype = &cli_chan_tcplocal;
+
+	ret = listen_tcpfwd(tcpinfo);
+
+	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;
+
+	TRACE(("enter setup_remotetcp"));
+
+	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;
+	}
+	TRACE(("leave setup_remotetcp"));
+}
+
+static int newtcpforwarded(struct Channel * channel) {
+
+	unsigned int origport;
+	struct TCPFwdList * iter = NULL;
+	char portstring[NI_MAXSERV];
+	int sock;
+	int err = SSH_OPEN_ADMINISTRATIVELY_PROHIBITED;
+
+	/* 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"));
+		err = SSH_OPEN_CONNECT_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;
+	
+	err = SSH_OPEN_IN_PROGRESS;
+
+out:
+	TRACE(("leave newtcpdirect: err %d", err));
+	return err;
+}
--- a/common-algo.c	Sun Aug 08 16:57:37 2004 +0000
+++ b/common-algo.c	Sun Sep 12 04:56:50 2004 +0000
@@ -1,7 +1,8 @@
 /*
- * Dropbear - a SSH2 server
+ * Dropbear SSH
  * 
  * Copyright (c) 2002,2003 Matt Johnston
+ * Copyright (c) 2004 by Mihnea Stoenescu
  * All rights reserved.
  * 
  * Permission is hereby granted, free of charge, to any person obtaining a copy
--- a/common-channel.c	Sun Aug 08 16:57:37 2004 +0000
+++ b/common-channel.c	Sun Sep 12 04:56:50 2004 +0000
@@ -1,7 +1,7 @@
 /*
- * Dropbear - a SSH2 server
+ * Dropbear SSH
  * 
- * Copyright (c) 2002,2003 Matt Johnston
+ * Copyright (c) 2002-2004 Matt Johnston
  * All rights reserved.
  * 
  * Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -29,11 +29,10 @@
 #include "packet.h"
 #include "ssh.h"
 #include "buffer.h"
+#include "circbuffer.h"
 #include "dbutil.h"
 #include "channel.h"
 #include "ssh.h"
-#include "tcpfwd-direct.h"
-#include "tcpfwd-remote.h"
 #include "listener.h"
 
 static void send_msg_channel_open_failure(unsigned int remotechan, int reason,
@@ -41,7 +40,7 @@
 static void send_msg_channel_open_confirmation(struct Channel* channel,
 		unsigned int recvwindow, 
 		unsigned int recvmaxpacket);
-static void writechannel(struct Channel *channel);
+static void writechannel(struct Channel* channel, int fd, circbuffer *cbuf);
 static void send_msg_channel_window_adjust(struct Channel *channel, 
 		unsigned int incr);
 static void send_msg_channel_data(struct Channel *channel, int isextended,
@@ -149,8 +148,10 @@
 	newchan->errfd = FD_CLOSED; /* this isn't always set to start with */
 	newchan->initconn = 0;
 
-	newchan->writebuf = buf_new(RECV_MAXWINDOW);
+	newchan->writebuf = cbuf_new(RECV_MAXWINDOW);
+	newchan->extrabuf = NULL; /* The user code can set it up */
 	newchan->recvwindow = RECV_MAXWINDOW;
+	newchan->recvdonelen = 0;
 	newchan->recvmaxpacket = RECV_MAXPACKET;
 
 	ses.channels[i] = newchan;
@@ -162,7 +163,7 @@
 }
 
 /* Get the channel structure corresponding to a channel number */
-static struct Channel* getchannel(unsigned int chan) {
+struct Channel* getchannel(unsigned int chan) {
 	if (chan >= ses.chansize || ses.channels[chan] == NULL) {
 		return NULL;
 	}
@@ -174,6 +175,7 @@
 
 	struct Channel *channel;
 	unsigned int i;
+	int ret;
 
 	/* iterate through all the possible channels */
 	for (i = 0; i < ses.chansize; i++) {
@@ -190,7 +192,8 @@
 		}
 
 		/* read from program/pipe stderr */
-		if (channel->errfd >= 0 && FD_ISSET(channel->errfd, readfd)) {
+		if (channel->extrabuf == NULL &&
+				channel->errfd >= 0 && FD_ISSET(channel->errfd, readfd)) {
 				send_msg_channel_data(channel, 1, SSH_EXTENDED_DATA_STDERR);
 		}
 
@@ -198,8 +201,15 @@
 		 * see if it has errors */
 		if (channel->infd >= 0 && channel->infd != channel->outfd
 				&& FD_ISSET(channel->infd, readfd)) {
-			int ret;
-			ret = write(channel->infd, NULL, 0);
+			if (channel->initconn) {
+				/* Handling for "in progress" connection - this is needed
+				 * to avoid spinning 100% CPU when we connect to a server
+				 * which doesn't send anything (tcpfwding) */
+				checkinitdone(channel);
+				continue; /* Important not to use the channel after 
+							 checkinitdone(), as it may be NULL */
+			}
+			ret = write(channel->infd, NULL, 0); /* Fake write */
 			if (ret < 0 && errno != EINTR && errno != EAGAIN) {
 				closeinfd(channel);
 			}
@@ -211,9 +221,14 @@
 				checkinitdone(channel);
 				continue; /* Important not to use the channel after
 							 checkinitdone(), as it may be NULL */
-			} else {
-				writechannel(channel);
 			}
+			writechannel(channel, channel->infd, channel->writebuf);
+		}