diff --git a/CHANGES b/CHANGES
new file mode 100644
index 0000000000000000000000000000000000000000..ffe9ff23415ab61568b3f4cb176e4e0a9120e6fc
--- /dev/null
+++ b/CHANGES
@@ -0,0 +1,519 @@
+0.47 - Thurs Dec 8 2005
+
+- SECURITY: fix for buffer allocation error in server code, could potentially
+  allow authenticated users to gain elevated privileges. All multi-user systems
+  running the server should upgrade (or apply the patch available on the
+  Dropbear webpage).
+
+- Fix channel handling code so that redirecting to /dev/null doesn't use
+  100% CPU.
+
+- Turn on zlib compression for dbclient.
+
+- Set "low delay" TOS bit, can significantly improve interactivity
+  over some links.
+
+- Added client keyboard-interactive mode support, allows operation with
+  newer OpenSSH servers in default config.
+
+- Log when pubkey auth fails because of bad ~/.ssh/authorized_keys permissions
+
+- Improve logging of assertions
+
+- Added aes-256 cipher and sha1-96 hmac.
+
+- Fix twofish so that it actually works.
+
+- Improve PAM prompt comparison.
+
+- Added -g (dbclient) and -a (dropbear server) options to allow
+  connections to listening forwarded ports from remote machines.
+
+- Various other minor fixes
+
+- Compile fixes for glibc 2.1 (ss_family vs __ss_family) and NetBSD
+  (netinet/in_systm.h needs to be included).
+
+0.46 - Sat July 9 2005
+
+- Fix long-standing bug which caused connections to be closed if an ssh-agent
+  socket was no longer available
+
+- Print a warning if we seem to be blocking on /dev/random 
+  (suggested by Paul Fox)
+
+- Fixed a memory leak in DSS code (thanks to Boris Berezovsky for the patch)
+
+- dbclient -L no longer segfaults, allocate correct buffer size (thanks
+  to David Cook for reporting it, and Christopher Faylor for independently
+  sending in a patch)
+
+- Added RSA blinding to signing code (suggested by Dan Kaminsky)
+
+- Rearranged bignum reading/random generation code
+
+- Reset the non-blocking status on stderr and stdout as well as stdin,
+  fixes a problem where the shell running dbclient will exit (thanks to 
+  Brent Roman for reporting it)
+
+- Fix so that all file descriptors are closed so the child shell doesn't
+  inherit descriptors (thanks to Linden May for the patch)
+
+- Change signkey.c to avoid gcc 4 generating incorrect code
+
+- After both sides of a file descriptor have been shutdown(), close()
+  it to avoid leaking descriptors (thanks to Ari Hyttinen for a patch)
+
+- Update to LibTomCrypt 1.05 and LibTomMath 0.35
+
+0.45 - Mon March 7 2005
+
+- Makefile no longer appends 'static' to statically linked binaries
+
+- Add optional SSH_ASKPASS support to the client
+
+- Respect HOST_LOOKUP option
+
+- Fix accidentally removed "return;" statement which was removed in 0.44
+  (causing clients which sent an empty terminal-modes string to fail to
+  connect - including pssh, ssh.com, danger hiptop). (patches
+  independently from Paul Fox, David Horwitt and Sven-Ola Tuecke)
+
+- Read "y/n" response for fingerprints from /dev/tty directly so that dbclient
+  will work with scp.
+
+0.44 - Mon Jan 3 2005
+
+- SECURITY: Fix for PAM auth so that usernames are logged and conversation
+  function responses are allocated correctly - all 0.44test4 users with PAM
+  compiled in (not default) are advised to upgrade.
+
+- Fix calls to getnameinfo() for compatibility with Solaris
+
+- Pristine compilation works (run 'configure' from a fresh dir and make it
+  there)
+
+- Fixes for compiling with most options disabled.
+
+- Upgraded to LibTomCrypt 0.99 and LibTomMath 0.32
+
+- Make sure that zeroing out of values in LTM and LTC won't get optimised away
+
+- Removed unused functions from loginrec.c
+
+- /dev/random is now the default entropy source rather than /dev/urandom
+
+- Logging of IPs in auth success/failure messages for improved greppability
+
+- Fix dbclient so that "scp -i keyfile" works. (It can handle "-ikeyfile
+  properly)
+
+- Avoid a race in server shell-handling code which prevents the exit-code
+  from being returned to the client in some circumstances.
+
+- Makefile modified so that install target works correctly (doesn't try
+  to install "all" binary) - patch from Juergen Daubert
+
+- Various minor fixes and compile warnings.
+
+0.44test4 - Tue Sept 14 2004 21:15:54 +0800
+
+- Fix inetd mode so it actually loads the hostkeys (oops)
+
+- Changed DROPBEAR_DEFPORT properly everywhere
+
+- Fix a small memory leak in the auth code
+
+- WCOREDUMP is only used on systems which support it (ie not cygwin or AIX)
+
+- Check (and fail for) cases when we can't negotiate algorithms with the
+  remote side successfully (rather than bombing out ungracefully)
+
+- Handle authorized_keys files without a terminating newline
+
+- Fiddle the channel receive window size for possibly better performance
+
+- Added in the PAM authentication code (finally! thanks to Martin Carlsson)
+
+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
+  reporting and testing
+
+- Terminal mode parsing now handles empty terminal mode strings (sent by
+  Windows ssh.com clients), thanks to Ricardo Derbes for the report
+
+- Handling is improved for users with no shell specified in /etc/passwd,
+  thanks again to Ricardo Derbes
+
+- Fix for compiling with --disable-syslog, thanks to gordonfh
+
+- Various minor fixes allow scp to work with irix, thanks to Paul Marinceu for
+  fixing it up
+
+- Use <stropts.h> not <sys/stropts.h>, since the former seems more common
+
+0.40 - Tue Jan 13 2004 21:05:19 +0800
+
+- Remote TCP forwarding (-R) style implemented
+
+- Local and remote TCP forwarding can each be disabled at runtime (-k and -j
+  switches)
+
+- Fix for problems detecting openpty() with uClibc - many thanks to various
+  people for reporting and testing fixes, including (in random order) Cristian
+  Ionescu-Idbohrn, James Ewing, Steve Dover, Thomas Lundquist and Frederic
+  Lavernhe
+
+- Improved portability for IRIX, thanks to Paul Marinceu
+
+- AIX and HPUX portability fixes, thanks to Darren Tucker for patches
+
+- prngd should now work correctly, thanks to Darren Tucker for the patch
+
+- scp compilation on systems without strlcpy() is fixed, thanks to Peter
+  Jannesen and David Muse for reporting it (independently and simultaneously :)
+
+- Merged in new LibTomCrypt 0.92 and LibTomMath 0.28
+
+0.39 - Tue Dec 16 2003 15:19:19 +0800
+
+- Better checking of key lengths and parameters for DSS and RSA auth
+
+- Print fingerprint of keys used for pubkey auth
+
+- More consistent logging of usernames and IPs
+
+- Added option to disable password auth (or just for root) at runtime
+
+- Avoid including bignum functions which don't give much speed benefit but
+  take up binary size
+
+- Added a stripped down version of OpenSSH's scp binary
+
+- Added additional supporting functions for Irix, thanks to Paul Marinceu
+
+- Don't check for unused libraries in configure script
+
+- Removed trailing comma in algorithm lists (thanks to Mihnea Stoenescu)
+
+- Fixed up channel close handling, always send close packet in response
+  (also thanks to Mihnea Stoenescu)
+
+- Various makefile improvements for cross-compiling, thanks to Friedrich
+  Lobenstock and Mihnea Stoenescu
+
+- Use daemon() function if available (or our own copy) rather than separate
+  code (thanks to Frédéric Lavernhe for the report and debugging, and Bernard
+  Blackham for his suggestion on what to look at)
+
+- Fixed up support for first_kex_packet_follows, required to talk to ssh.com
+  clients. Thanks to Marian Stagarescu for the bug report.
+
+- Avoid using MAXPATHLEN, pointer from Ian Morris
+
+- Improved input sanity checking
+
+0.38 - Sat Oct 11 2003 16:28:13 +0800
+
+- Default hostkey path changed to /etc/dropbear/dropbear_{rsa,dss}_host_key
+  rather than /etc/dropbear_{rsa,dss}_host_key
+
+- Added SMALL and MULTI text files which have info on compiling for multiple
+  binaries or small binaries
+
+- Allow for commandline definition of some options.h settings
+  (without warnings)
+
+- Be more careful handling EINTR
+
+- More fixes for channel closing
+
+- Added multi-binary support
+
+- Improved logging of IPs, now get logged in all cases
+
+- Don't chew cpu when waiting for version identification string, also
+  make sure that we kick off people if they don't auth within 5 minutes.
+
+- Various small fixes, warnings etc
+
+- Display MOTD if requested - suggested by
+  Trent Lloyd <lathiat at sixlabs.org> and
+  Zach White <zwhite at darkstar.frop.org>
+
+- sftp support works (relies on OpenSSH sftp binary or similar)
+
+- Added --disable-shadow option (requested by the floppyfw guys)
+
+0.37 - Wed Sept 24 2003 19:42:12 +0800
+
+- Various portability fixes, fixes for Solaris 9, Tru64 5.1, Mac OS X 10.2,
+  AIX, BSDs
+
+- Updated LibTomMath to 0.27 and LibTomCrypt to 0.90
+
+- Renamed util.{c,h} to dbutil.{c,h} to avoid conflicts with system util.h
+
+- Added some small changes so it'll work with AIX (plus Linux Affinity).
+  Thanks to Shig for them.
+
+- Improved the closing messages, so a clean exit is "Exited normally"
+
+- Added some more robust integer/size checking in buffer.c as a backstop for
+  integer overflows
+
+- X11 forwarding fixed for OSX, path for xauth changed to /usr/X11R6/bin/xauth
+
+- Channel code handles closing more nicely, doesn't sit waiting for an extra
+  keystroke on BSD/OSX platforms, and data is flushed fully before closing
+  child processes (thanks to 
+  Cristian Ionescu-Idbohrn <cristian.ionescu-idbohrn at axis.com> for
+  pointing that out).
+
+- Changed "DISABLE_TCPFWD" to "ENABLE_TCPFWD" (and for x11/auth) so
+  "disable DISABLE_TCPWD" isn't so confusing.
+
+- Fix authorized_keys handling (don't crash on too-long keys, and
+  use fgetc not getc to avoid strange macro-related issues), thanks to
+  Cristian Ionescu-Idbohrn <cristian.ionescu-idbohrn at axis.com> 
+  and Steve Rodgers <hwstar at cox.net> for reporting and testing.
+
+- Fixes to the README with regard to uClibc systems, thanks to 
+  Cristian Ionescu-Idbohrn <cristian.ionescu-idbohrn at axis.com>,
+  as well as general improvements to documentation (split README/INSTALL)
+
+- Fixed up some compilation problems with dropbearconvert/dropbearkey if
+  DSS or RSA were disabled, reported by Patrik Karlsson <patrik at cqure.net>
+
+- Fix double-free bug for hostkeys, reported by
+  Vincent Sanders <vince at kyllikki.org>
+
+- Fix up missing \ns from dropbearconvert help message,
+  thanks to Mordy Ovits <movits at bloomberg.com> for the patch
+
+0.36 - Tue August 19 2003 12:16:23 +0800
+
+- Fix uninitialised temporary variable in DSS signing code
+  (thanks to Matthew Franz <mdfranz at io.com> for reporting, and the authors
+  of Valgrind for making it easy to track down)
+- Fix remote version-string parsing error
+  (thanks to Bernard Blackham <bernard at blackham.com.au> for noticing)
+- Improved host-algorithm-matching algorithm in algo.c
+- Decreased MAX_STRING_LEN to a more realistic value
+- Fix incorrect version (0.34) in this CHANGES file for the previous release.
+
+0.35 - Sun August 17 2003 05:37:47 +0800
+
+- Fix for remotely exploitable format string buffer overflow.
+  (thanks to Joel Eriksson <je at bitnux.com>)
+
+0.34 - Fri August 15 2003 15:10:00 +0800
+
+- Made syslog optional, both at compile time and as a compile option
+  (suggested by Laurent Bercot <ska at skarnet.org>)
+- Fixup for bad base64 parsing in authorized_keys
+  (noticed by Davyd Madeley <davyd at zdlcomputing.com>)
+- Added initial tcp forwarding code, only -L (local) at this stage
+- Improved "make install" with DESTDIR and changing ownership seperately,
+  don't check for setpgrp on Linux for crosscompiling.
+  (from Erik Andersen <andersen at codepoet.org>)
+- More commenting, fix minor compile warnings, make return values more
+  consistent etc
+- Various signedness fixes
+- Can listen on multiple ports
+- added option to disable openpty with configure script,
+  (from K.-P. Kirchdörfer <kapeka at epost.de>)
+- Various cleanups to bignum code
+  (thanks to Tom St Denis <tomstdenis at iahu.ca>)
+- Fix compile error when disabling RSA
+  (from Marc Kleine-Budde <kleine-budde at gmx.de>)
+- Other cleanups, splitting large functions for packet and kex handling etc
+
+0.33 - Sun June 22 2003 22:24:12 +0800
+
+- Fixed some invalid assertions in the channel code, fixing the server dying
+  when forwarding X11 connections.
+- Add dropbearconvert to convert to/from OpenSSH host keys and Dropbear keys
+- RSA keys now keep p and q parameters for compatibility -- old Dropbear keys
+  still work, but can't be converted to OpenSSH etc.
+- Debian packaging directory added, thanks to 
+  Grahame (grahame at angrygoats.net)
+- 'install' target added to the makefile
+- general tidying, improve consistency of functions etc
+- If RSA or DSS hostkeys don't exist, that algorithm won't be used.
+- Improved RSA and DSS key generation, more efficient and fixed some minor bugs
+  (thanks to Tom St Denis for the advice)
+- Merged new versions of LibTomCrypt (0.86) and LibTomMath (0.21)
+
+0.32 - Sat May 24 2003 12:44:11 +0800
+
+- Don't compile unused code from libtomcrypt (test vectors etc)
+- Updated to libtommath 0.17 and libtomcrypt 0.83. New libtommath results
+  in smaller binary size, due to not linking unrequired code
+- X11 forwarding added
+- Agent forwarding added (for OpenSSH.com ssh client/agent)
+- Fix incorrect buffer freeing when banners are used
+- Hostname resolution works
+- Various minor bugfixes/code size improvements etc
+
+0.31 - Fri May 9 2003 17:57:16 +0800
+
+- Improved syslog messages - IP logging etc
+- Strip control characters from log messages (specified username currently)
+- Login recording (utmp/wtmp) support, so last/w/who work - taken from OpenSSH
+- Shell is started as a proper login shell, so /etc/profile etc is sourced
+- Ptys work on Solaris (2.8 x86 tested) now
+- Fixed bug in specifying the rsa hostkey
+- Fixed bug in compression code, could trigger if compression resulted in
+  larger output than input (uncommon but possible).
+
+0.30 - Thu Apr 17 2003 18:46:15 +0800
+
+- SECURITY: buffer.c had bad checking for buffer increment length - fixed
+- channel code now closes properly on EOF - scp processes don't hang around
+- syslog support added - improved auth/login/failure messages
+- general code tidying, made return codes more consistent
+- Makefile fixed for dependencies and makes libtomcrypt as well
+- Implemented sending SSH_MSG_UNIMPLEMENTED :)
+
+0.29 - Wed Apr 9 2003
+
+- Fixed a stupid bug in 0.28 release, 'newstr = strdup(oldstr)',
+  not 'newstr=oldstr'
+
+0.28 - Sun Apr 6 2003
+
+- Initial public release
+
+Development was started in October 2002
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 0000000000000000000000000000000000000000..1bf444eeebb4d07f430c28e2b719fd3231878597
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,79 @@
+Basic Dropbear build instructions:
+
+- Edit options.h to set which features you want.
+- Edit debug.h if you want any debug options (not usually required).
+
+(If using a non-tarball copy, "autoconf; autoheader")
+
+./configure      (optionally with --disable-zlib or --disable-syslog,
+                  or --help for other options)
+
+Now compile:
+
+make PROGRAMS="dropbear dbclient dropbearkey dropbearconvert scp"
+
+And install (/usr/local/bin is usual default):
+
+make PROGRAMS="dropbear dbclient dropbearkey dropbearconvert scp" install
+
+(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)
+
+See MULTI for instructions on making all-in-one binaries.
+
+If you want to compile statically, add "STATIC=1" to the make command-line.
+
+Binaries can be strippd with "make strip"
+
+============================================================================
+
+If you're compiling for a 386-class CPU, you will probably need to add
+CFLAGS=-DLTC_NO_BSWAP so that libtomcrypt doesn't use 486+ instructions.
+
+============================================================================
+
+Compiling with uClibc:
+
+Firstly, make sure you have at least uclibc 0.9.17, as getusershell() in prior
+versions is broken. Also note that you may get strange issues if your uClibc
+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.
+
+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 
+
+export CFLAGS="-Izlibincludes -I../zlibincludes"
+export LDFLAGS=/usr/lib/libz.a
+
+before ./configure and make.
+
+If you disable zlib, you must explicitly disable compression for the client -
+OpenSSH is possibly buggy in this regard, it seems you need to disable it
+globally in ~/.ssh/config, not just in the host entry in that file.
+
+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 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.
+
+	Make sure that your libc headers match the library version you are using.
+
+	If openpty() is being used (HAVE_OPENPTY defined in config.h) and it fails,
+	you can try compiling with --disable-openpty. You will probably then need
+	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.
diff --git a/LICENSE b/LICENSE
index 5baa792a6502f536835dcf76a003594b80d47ef7..e0a11acad44549e8832010c244970882dc81c463 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,89 @@
-LibTomMath is hereby released into the Public Domain.  
+Dropbear contains a number of components from different sources, hence there
+are a few licenses and authors involved. All licenses are fairly 
+non-restrictive.
 
--- Tom St Denis
 
+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
+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.
+
+=====
+
+LibTomCrypt and LibTomMath are written by Tom St Denis, and are Public Domain.
+
+=====
+
+sshpty.c is taken from OpenSSH 3.5p1, 
+  Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+                     All rights reserved
+ "As far as I am concerned, the code I have written for this software
+  can be used freely for any purpose.  Any derived versions of this
+  software must be clearly marked as such, and if the derived work is
+  incompatible with the protocol description in the RFC file, it must be
+  called by a name other than "ssh" or "Secure Shell". "
+
+=====
+
+loginrec.c
+loginrec.h
+atomicio.h
+atomicio.c
+and strlcat() (included in util.c) are from OpenSSH 3.6.1p2, and are licensed
+under the 2 point BSD license.
+
+loginrec is written primarily by Andre Lucas, atomicio.c by Theo de Raadt.
+
+strlcat() is (c) Todd C. Miller
+
+=====
+
+Import code in keyimport.c is modified from PuTTY's import.c, licensed as
+follows:
+
+PuTTY is copyright 1997-2003 Simon Tatham.
+
+Portions copyright Robert de Bath, Joris van Rantwijk, Delian
+Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas Barry,
+Justin Bradford, and CORE SDI S.A.
+
+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 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.
diff --git a/MULTI b/MULTI
new file mode 100644
index 0000000000000000000000000000000000000000..a50e30eb17f3f7ece30f71bd76841e8550798c9e
--- /dev/null
+++ b/MULTI
@@ -0,0 +1,26 @@
+Multi-binary compilation
+========================
+
+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 various parts.
+If you are familiar with "busybox", it's the same principle.
+
+To compile the multi-binary, first "make clean" (if you've compiled
+previously), then
+
+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, though
+in most situations where it is being used, the target and build systems will
+differ.
diff --git a/Makefile.in b/Makefile.in
index e96173a404f78180c93af5f12ab73fea53d11ac7..fc17c1ff9fab67365a869b9cb9bbcd93975e614e 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -1,165 +1,209 @@
-#Makefile for GCC
+# This Makefile is for Dropbear SSH Server and Client
+# @configure_input@
+
+# invocation:
+# make PROGRAMS="dropbear dbclient scp" MULTI=1 STATIC=1 SCPPROGRESS=1
 #
-#Tom St Denis
+# 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 dropbearconvert
+endif
+
+LTC=libtomcrypt/libtomcrypt.a
+LTM=libtommath/libtommath.a
+
+COMMONOBJS=dbutil.o buffer.o \
+		dss.o bignum.o \
+		signkey.o rsa.o random.o \
+		queue.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-chansession.o svr-runopts.o svr-agentfwd.o svr-main.o svr-x11fwd.o\
+		svr-tcpfwd.o svr-authpam.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-tcpfwd.o cli-channel.o cli-authinteract.o
 
-#version of library 
-VERSION=0.35
+CLISVROBJS=common-session.o packet.o common-algo.o common-kex.o \
+			common-channel.o common-chansession.o termcodes.o loginrec.o \
+			tcp-accept.o listener.o process-packet.o \
+			common-runopts.o circbuffer.o
+
+KEYOBJS=dropbearkey.o gendss.o genrsa.o
+
+CONVERTOBJS=dropbearconvert.o keyimport.o
+
+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 \
+		debug.h channel.h chansession.h config.h queue.h sshpty.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)
+dropbearkeyobjs=$(COMMONOBJS) $(KEYOBJS)
+dropbearconvertobjs=$(COMMONOBJS) $(CONVERTOBJS)
+scpobjs=$(SCPOBJS)
 
 VPATH=@srcdir@
 srcdir=@srcdir@
 
-# Dropbear takes flags from the toplevel makefile
-CFLAGS += -I$(srcdir)
-
-#CFLAGS  +=  -I./ -Wall -W -Wshadow -Wsign-compare
-
-#for speed 
-#CFLAGS += -O3 -funroll-all-loops
-
-#for size 
-#CFLAGS += -Os
-
-#x86 optimizations [should be valid for any GCC install though]
-#CFLAGS  += -fomit-frame-pointer
-
-#debug
-#CFLAGS += -g3
-
-#install as this user
-USER=root
-GROUP=root
-
-default: libtommath.a
-
-#default files to install
-LIBNAME=libtommath.a
-HEADERS=tommath.h tommath_class.h tommath_superclass.h
-
-#LIBPATH-The directory for libtommath to be installed to.
-#INCPATH-The directory to install the header files for libtommath.
-#DATAPATH-The directory to install the pdf docs.
-DESTDIR=
-LIBPATH=/usr/lib
-INCPATH=/usr/include
-DATAPATH=/usr/share/doc/libtommath/pdf
-
-OBJECTS=bncore.o bn_mp_init.o bn_mp_clear.o bn_mp_exch.o bn_mp_grow.o bn_mp_shrink.o \
-bn_mp_clamp.o bn_mp_zero.o  bn_mp_set.o bn_mp_set_int.o bn_mp_init_size.o bn_mp_copy.o \
-bn_mp_init_copy.o bn_mp_abs.o bn_mp_neg.o bn_mp_cmp_mag.o bn_mp_cmp.o bn_mp_cmp_d.o \
-bn_mp_rshd.o bn_mp_lshd.o bn_mp_mod_2d.o bn_mp_div_2d.o bn_mp_mul_2d.o bn_mp_div_2.o \
-bn_mp_mul_2.o bn_s_mp_add.o bn_s_mp_sub.o bn_fast_s_mp_mul_digs.o bn_s_mp_mul_digs.o \
-bn_fast_s_mp_mul_high_digs.o bn_s_mp_mul_high_digs.o bn_fast_s_mp_sqr.o bn_s_mp_sqr.o \
-bn_mp_add.o bn_mp_sub.o bn_mp_karatsuba_mul.o bn_mp_mul.o bn_mp_karatsuba_sqr.o \
-bn_mp_sqr.o bn_mp_div.o bn_mp_mod.o bn_mp_add_d.o bn_mp_sub_d.o bn_mp_mul_d.o \
-bn_mp_div_d.o bn_mp_mod_d.o bn_mp_expt_d.o bn_mp_addmod.o bn_mp_submod.o \
-bn_mp_mulmod.o bn_mp_sqrmod.o bn_mp_gcd.o bn_mp_lcm.o bn_fast_mp_invmod.o bn_mp_invmod.o \
-bn_mp_reduce.o bn_mp_montgomery_setup.o bn_fast_mp_montgomery_reduce.o bn_mp_montgomery_reduce.o \
-bn_mp_exptmod_fast.o bn_mp_exptmod.o bn_mp_2expt.o bn_mp_n_root.o bn_mp_jacobi.o bn_reverse.o \
-bn_mp_count_bits.o bn_mp_read_unsigned_bin.o bn_mp_read_signed_bin.o bn_mp_to_unsigned_bin.o \
-bn_mp_to_signed_bin.o bn_mp_unsigned_bin_size.o bn_mp_signed_bin_size.o  \
-bn_mp_xor.o bn_mp_and.o bn_mp_or.o bn_mp_rand.o bn_mp_montgomery_calc_normalization.o \
-bn_mp_prime_is_divisible.o bn_prime_tab.o bn_mp_prime_fermat.o bn_mp_prime_miller_rabin.o \
-bn_mp_prime_is_prime.o bn_mp_prime_next_prime.o bn_mp_dr_reduce.o \
-bn_mp_dr_is_modulus.o bn_mp_dr_setup.o bn_mp_reduce_setup.o \
-bn_mp_toom_mul.o bn_mp_toom_sqr.o bn_mp_div_3.o bn_s_mp_exptmod.o \
-bn_mp_reduce_2k.o bn_mp_reduce_is_2k.o bn_mp_reduce_2k_setup.o \
-bn_mp_reduce_2k_l.o bn_mp_reduce_is_2k_l.o bn_mp_reduce_2k_setup_l.o \
-bn_mp_radix_smap.o bn_mp_read_radix.o bn_mp_toradix.o bn_mp_radix_size.o \
-bn_mp_fread.o bn_mp_fwrite.o bn_mp_cnt_lsb.o bn_error.o \
-bn_mp_init_multi.o bn_mp_clear_multi.o bn_mp_exteuclid.o bn_mp_toradix_n.o \
-bn_mp_prime_random_ex.o bn_mp_get_int.o bn_mp_sqrt.o bn_mp_is_square.o bn_mp_init_set.o \
-bn_mp_init_set_int.o bn_mp_invmod_slow.o bn_mp_prime_rabin_miller_trials.o \
-bn_mp_to_signed_bin_n.o bn_mp_to_unsigned_bin_n.o
-
-libtommath.a:  $(OBJECTS)
-	$(AR) $(ARFLAGS) libtommath.a $(OBJECTS)
-	$(RANLIB) libtommath.a
-
-#make a profiled library (takes a while!!!)
-#
-# This will build the library with profile generation
-# then run the test demo and rebuild the library.
-# 
-# So far I've seen improvements in the MP math
-profiled:
-	make CFLAGS="$(CFLAGS) -fprofile-arcs -DTESTING" timing
-	./ltmtest
-	rm -f *.a *.o ltmtest
-	make CFLAGS="$(CFLAGS) -fbranch-probabilities"
-
-#make a single object profiled library 
-profiled_single:
-	perl gen.pl
-	$(CC) $(CFLAGS) -fprofile-arcs -DTESTING -c mpi.c -o mpi.o
-	$(CC) $(CFLAGS) -DTESTING -DTIMER demo/timing.c mpi.o -o ltmtest
-	./ltmtest
-	rm -f *.o ltmtest
-	$(CC) $(CFLAGS) -fbranch-probabilities -DTESTING -c mpi.c -o mpi.o
-	$(AR) $(ARFLAGS) libtommath.a mpi.o
-	ranlib libtommath.a	
-
-install: libtommath.a
-	install -d -g $(GROUP) -o $(USER) $(DESTDIR)$(LIBPATH)
-	install -d -g $(GROUP) -o $(USER) $(DESTDIR)$(INCPATH)
-	install -g $(GROUP) -o $(USER) $(LIBNAME) $(DESTDIR)$(LIBPATH)
-	install -g $(GROUP) -o $(USER) $(HEADERS) $(DESTDIR)$(INCPATH)
-
-test: libtommath.a demo/demo.o
-	$(CC) $(CFLAGS) demo/demo.o libtommath.a -o test
-	
-mtest: test	
-	cd mtest ; $(CC) $(CFLAGS) mtest.c -o mtest
-        
-timing: libtommath.a
-	$(CC) $(CFLAGS) -DTIMER demo/timing.c libtommath.a -o ltmtest
-
-# makes the LTM book DVI file, requires tetex, perl and makeindex [part of tetex I think]
-docdvi: tommath.src
-	cd pics ; make 
-	echo "hello" > tommath.ind
-	perl booker.pl
-	latex tommath > /dev/null
-	latex tommath > /dev/null
-	makeindex tommath
-	latex tommath > /dev/null
-
-# poster, makes the single page PDF poster
-poster: poster.tex
-	pdflatex poster
-	rm -f poster.aux poster.log 
-
-# makes the LTM book PDF file, requires tetex, cleans up the LaTeX temp files
-docs:   docdvi
-	dvipdf tommath
-	rm -f tommath.log tommath.aux tommath.dvi tommath.idx tommath.toc tommath.lof tommath.ind tommath.ilg
-	cd pics ; make clean
-	
-#LTM user manual
-mandvi: bn.tex
-	echo "hello" > bn.ind
-	latex bn > /dev/null
-	latex bn > /dev/null
-	makeindex bn
-	latex bn > /dev/null
-
-#LTM user manual [pdf]
-manual:	mandvi
-	pdflatex bn >/dev/null
-	rm -f bn.aux bn.dvi bn.log bn.idx bn.lof bn.out bn.toc
-
-pretty: 
-	perl pretty.build
-
-clean:
-	rm -f *.bat *.pdf *.o *.a *.obj *.lib *.exe *.dll etclib/*.o demo/demo.o test ltmtest mpitest mtest/mtest mtest/mtest.exe \
-        *.idx *.toc *.log *.aux *.dvi *.lof *.ind *.ilg *.ps *.log *.s mpi.c *.da *.dyn *.dpi tommath.tex *.lo *.la
-	rm -rf .libs
-	cd etc && make clean
-	cd pics && make clean
-
-zipup: clean manual poster docs
-	perl gen.pl ; mv mpi.c pre_gen/ ; \
-	cd .. ; rm -rf ltm* libtommath-$(VERSION) ; mkdir libtommath-$(VERSION) ; \
-	cp -R ./libtommath/* ./libtommath-$(VERSION)/ ; \
-	tar -c libtommath-$(VERSION)/* | bzip2 -9vvc > ltm-$(VERSION).tar.bz2 ; \
-	zip -9 -r ltm-$(VERSION).zip libtommath-$(VERSION)/*
+prefix=@prefix@
+exec_prefix=${prefix}
+bindir=${exec_prefix}/bin
+sbindir=${exec_prefix}/sbin
+
+CC=@CC@
+LD=@LD@
+AR=@AR@
+RANLIB=@RANLIB@
+STRIP=@STRIP@
+INSTALL=@INSTALL@
+CFLAGS=-I. -I$(srcdir)/libtomcrypt/src/headers/ @CFLAGS@
+LIBS=$(LTC) $(LTM) @LIBS@
+LDFLAGS=@LDFLAGS@
+
+EXEEXT=@EXEEXT@
+
+# whether we're building client, server, or both for the common objects.
+# evilness so we detect 'dropbear' by itself as a word
+space:= $(empty) $(empty)
+ifneq (,$(strip $(foreach prog, $(PROGRAMS), $(findstring ZdropbearZ, Z$(prog)Z))))
+	CFLAGS+= -DDROPBEAR_SERVER
+endif
+ifneq (,$(strip $(foreach prog, $(PROGRAMS), $(findstring ZdbclientZ, Z$(prog)Z))))
+	CFLAGS+= -DDROPBEAR_CLIENT
+endif
+
+
+# these are exported so that libtomcrypt's makefile will use them
+export CC
+export CFLAGS
+export RANLIB AR STRIP
+
+ifeq ($(STATIC), 1)
+	LDFLAGS+=-static
+endif
+
+ifeq ($(MULTI), 1)
+	TARGETS=dropbearmulti
+else
+	TARGETS=$(PROGRAMS)
+endif
+
+# for the scp progress meter. The -D doesn't affect anything else.
+ifeq ($(SCPPROGRESS), 1)
+	CFLAGS+=-DPROGRESS_METER
+endif
+
+#%: $(HEADERS)
+#%: $(HEADERS) Makefile
+# TODO
+
+all: $(TARGETS)
+
+strip: $(TARGETS)
+	$(STRIP) $(addsuffix $(EXEEXT), $(TARGETS))
+
+install: $(addprefix inst_, $(TARGETS))
+
+installdropbearmulti: insdbmulti $(addprefix insmulti, $(PROGRAMS)) 
+
+insdbmulti: dropbearmulti
+	$(INSTALL) -d -m 755 $(DESTDIR)$(bindir)
+	$(INSTALL) -m 755 dropbearmulti$(EXEEXT) $(DESTDIR)$(bindir)
+	-chown root $(DESTDIR)$(bindir)/dropbearmulti$(EXEEXT)
+	-chgrp 0 $(DESTDIR)$(bindir)/dropbearmulti$(EXEEXT)
+
+insmultidropbear: dropbearmulti
+	-rm -f $(DESTDIR)$(sbindir)/dropbear$(EXEEXT)
+	-ln -s $(DESTDIR)$(bindir)/dropbearmulti$(EXEEXT) $(DESTDIR)$(sbindir)/dropbear$(EXEEXT) 
+
+insmulti%: dropbearmulti
+	-rm -f $(DESTDIR)$(bindir)/$*$(EXEEXT) 
+	-ln -s $(DESTDIR)$(bindir)/dropbearmulti$(EXEEXT) $(DESTDIR)$(bindir)/$*$(EXEEXT) 
+
+# dropbear should go in sbin, so it needs a seperate rule
+inst_dropbear: dropbear
+	$(INSTALL) -d -m 755 $(DESTDIR)$(sbindir)
+	$(INSTALL) -m 755 dropbear$(EXEEXT) $(DESTDIR)$(sbindir)
+	-chown root $(DESTDIR)$(sbindir)/dropbear$(EXEEXT)
+	-chgrp 0 $(DESTDIR)$(sbindir)/dropbear$(EXEEXT)
+
+inst_%: $*
+	$(INSTALL) -d -m 755 $(DESTDIR)$(bindir)
+	$(INSTALL) -m 755 $*$(EXEEXT) $(DESTDIR)$(bindir)
+	-chown root $(DESTDIR)$(bindir)/$*$(EXEEXT)
+	-chgrp 0 $(DESTDIR)$(bindir)/$*$(EXEEXT)
+
+
+# for some reason the rule further down doesn't like $($@objs) as a prereq.
+dropbear: $(dropbearobjs)
+dbclient: $(dbclientobjs)
+dropbearkey: $(dropbearkeyobjs)
+dropbearconvert: $(dropbearconvertobjs)
+
+dropbear dbclient dropbearkey dropbearconvert: $(HEADERS)  $(LTC) $(LTM) \
+													Makefile
+	$(LD) $(LDFLAGS) -o $@$(EXEEXT) $($@objs) $(LIBS)
+
+# scp doesn't use the libs so is special.
+scp: $(SCPOBJS)  $(HEADERS) Makefile
+	$(LD) $(LDFLAGS) -o $@$(EXEEXT) $(SCPOBJS)
+
+
+# multi-binary compilation.
+MULTIOBJS=
+ifeq ($(MULTI),1)
+	MULTIOBJS=dbmulti.o $(sort $(foreach prog, $(PROGRAMS), $($(prog)objs)))
+	CFLAGS+=$(addprefix -DDBMULTI_, $(PROGRAMS)) -DDROPBEAR_MULTI
+endif
+
+dropbearmulti: multilink 
+
+multibinary: $(HEADERS) $(MULTIOBJS) $(LTC) $(LTM) Makefile
+	$(LD) $(LDFLAGS) -o dropbearmulti$(EXEEXT) $(MULTIOBJS) $(LIBS)
+
+multilink: multibinary $(addprefix link, $(PROGRAMS))
+
+link%:
+	-rm -f $*$(EXEEXT)
+	-ln -s dropbearmulti$(EXEEXT) $*$(EXEEXT)
+
+$(LTC): options.h
+	cd libtomcrypt && $(MAKE) clean && $(MAKE)
+
+$(LTM): options.h
+	cd libtommath && $(MAKE)
+
+ltc-clean:
+	cd libtomcrypt && $(MAKE) clean
+
+ltm-clean:
+	cd libtommath && $(MAKE) clean
+
+sizes: dropbear
+	objdump -t dropbear|grep ".text"|cut -d "." -f 2|sort -rn
+
+clean: ltc-clean ltm-clean thisclean
+
+thisclean:
+	-rm -f dropbear dbclient dropbearkey dropbearconvert scp scp-progress \
+			dropbearmulti *.o *.da *.bb *.bbg *.prof 
+
+distclean: clean tidy
+	-rm -f config.h
+	-rm -f Makefile
+
+tidy:
+	-rm -f *~ *.gcov */*~
diff --git a/README b/README
new file mode 100644
index 0000000000000000000000000000000000000000..43dd1f2b762b50b95dafa8795ca718bb2a1fe9a4
--- /dev/null
+++ b/README
@@ -0,0 +1,74 @@
+This is Dropbear, a smallish SSH 2 server and client.
+
+INSTALL has compilation instructions.
+
+MULTI has instructions on making a multi-purpose binary (ie a single binary
+which performs multiple tasks, to save disk space)
+
+SMALL has some tips on creating small binaries.
+
+See TODO for a few of the things I know need looking at, and please contact
+me if you have any questions/bugs found/features/ideas/comments etc :)
+
+Matt Johnston
+matt@ucc.asn.au
+
+
+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 by _PATH_SSH_PROGRAM in options.h . By default
+the progress meter isn't compiled in to save space, you can enable it by 
+adding 'SCPPROGRESS=1' to the make commandline.
diff --git a/SMALL b/SMALL
new file mode 100644
index 0000000000000000000000000000000000000000..babd671a33eb7b267baab1da70a65747741c883c
--- /dev/null
+++ b/SMALL
@@ -0,0 +1,53 @@
+Tips for a small system:
+
+If you only want server functionality (for example), compile with
+	make PROGRAMS=dropbear
+rather than just
+	make dropbear
+so that client functionality in shared portions of Dropbear won't be included.
+The same applies if you are compiling just a client.
+
+---
+
+The following are set in options.h:
+
+	- You can safely disable blowfish and twofish ciphers, and MD5 hmac, without
+	  affecting interoperability
+
+	- If you're compiling statically, you can turn off host lookups
+
+	- You can disable either password or public-key authentication, though note
+	  that the IETF draft states that pubkey authentication is required.
+
+	- Similarly with DSS and RSA, you can disable one of these if you know that
+	  all clients will be able to support a particular one. The IETF draft
+	  states that DSS is required, however you may prefer to use RSA. 
+	  DON'T disable either of these on systems where you aren't 100% sure about
+	  who will be connecting and what clients they will be using.
+
+	- Disabling the MOTD code and SFTP-SERVER may save a small amount of codesize
+
+	- You can disable x11, tcp and agent forwarding as desired. None of these are
+	  essential, although agent-forwarding is often useful even on firewall boxes.
+
+---
+
+If you are compiling statically, you may want to disable zlib, as it will use
+a few tens of kB of binary-size (./configure --disable-zlib).
+
+You can create a combined binary, see the file MULTI, which will put all
+the functions into one binary, avoiding repeated code.
+
+If you're compiling with gcc, you might want to look at gcc's options for
+stripping unused code. The relevant vars to set before configure are:
+
+LDFLAGS=-Wl,--gc-sections
+CFLAGS="-ffunction-sections -fdata-sections"
+
+You can also experiment with optimisation flags such as -Os, note that in some
+cases these flags actually seem to increase size, so experiment before
+deciding.
+
+Of course using small C libraries such as uClibc and dietlibc can also help.
+
+If you have any queries, mail me and I'll see if I can help.
diff --git a/TODO b/TODO
index deffba1333190a7e0bac0cb4a3e7fbfe7a6fff01..9807f599b45903ebb37acbc4ab959e348a557781 100644
--- a/TODO
+++ b/TODO
@@ -1,16 +1,30 @@
-things for book in order of importance...
+Current:
 
-- Fix up pseudo-code [only] for combas that are not consistent with source
-- Start in chapter 3 [basics] and work up...
-   - re-write to prose [less abrupt]
-   - clean up pseudo code [spacing]
-   - more examples where appropriate and figures
+Things which might need doing:
 
-Goal:
-   - Get sync done by mid January [roughly 8-12 hours work]
-   - Finish ch3-6 by end of January [roughly 12-16 hours of work]
-   - Finish ch7-end by mid Feb [roughly 20-24 hours of work].
+- default private dbclient keys
 
-Goal isn't "first edition" but merely cleaner to read.
+- Make options.h generated from configure perhaps?
 
+- Improved queueing of unauthed connections
 
+- handle /etc/environment in AIX
+
+- check that there aren't timing issues with valid/invalid user authentication
+  feedback.
+
+- Binding to different interfaces
+
+- check PRNG
+- CTR mode
+- SSH_MSG_IGNORE sending to improve CBC security
+- DH Group Exchange possibly, or just add group14 (whatever it's called today)
+
+- 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
+
+- Handle restrictions in ~/.ssh/authorized_keys ?
diff --git a/agentfwd.h b/agentfwd.h
new file mode 100644
index 0000000000000000000000000000000000000000..a0f675d02a0b9247811fcbb30192d41f7af7e86c
--- /dev/null
+++ b/agentfwd.h
@@ -0,0 +1,43 @@
+/*
+ * 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 _AGENTFWD_H_
+#define _AGENTFWD_H_
+#ifndef DISABLE_AGENTFWD
+
+#include "includes.h"
+#include "chansession.h"
+#include "channel.h"
+
+int agentreq(struct ChanSess * chansess);
+void agentsetauth(struct ChanSess *chansess);
+void agentcleanup(struct ChanSess * chansess);
+void agentset(struct ChanSess *chansess);
+
+#ifdef __hpux
+#define seteuid(a)       setresuid(-1, (a), -1)
+#define setegid(a)       setresgid(-1, (a), -1)
+#endif
+
+#endif /* DROPBEAR_AGENTFWD */
+#endif /* _AGENTFWD_H_ */
diff --git a/algo.h b/algo.h
new file mode 100644
index 0000000000000000000000000000000000000000..5ed01cc7ea6a6576e9e52ed6106eb2193f89dbc1
--- /dev/null
+++ b/algo.h
@@ -0,0 +1,74 @@
+/*
+ * 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 _ALGO_H_
+
+#define _ALGO_H_
+
+#include "includes.h"
+#include "buffer.h"
+
+struct Algo_Type {
+
+	unsigned char *name; /* identifying name */
+	char val; /* a value for this cipher, or -1 for invalid */
+	void *data; /* algorithm specific data */
+	unsigned usable : 1; /* whether we can use this algorithm */
+
+};
+
+typedef struct Algo_Type algo_type;
+
+/* lists mapping ssh types of algorithms to internal values */
+extern algo_type sshkex[];
+extern algo_type sshhostkey[];
+extern algo_type sshciphers[];
+extern algo_type sshhashes[];
+extern algo_type sshcompress[];
+
+extern const struct dropbear_cipher dropbear_nocipher;
+extern const struct dropbear_hash dropbear_nohash;
+
+struct dropbear_cipher {
+	const struct ltc_cipher_descriptor *cipherdesc;
+	unsigned long keysize;
+	unsigned char blocksize;
+};
+
+struct dropbear_hash {
+	const struct ltc_hash_descriptor *hashdesc;
+	unsigned long keysize;
+	unsigned char hashsize;
+};
+
+void crypto_init();
+int have_algo(char* algo, size_t algolen, algo_type algos[]);
+void buf_put_algolist(buffer * buf, algo_type localalgos[]);
+
+algo_type * svr_buf_match_algo(buffer* buf, algo_type localalgos[],
+		int *goodguess);
+algo_type * cli_buf_match_algo(buffer* buf, algo_type localalgos[],
+		int *goodguess);
+
+#endif /* _ALGO_H_ */
diff --git a/atomicio.c b/atomicio.c
new file mode 100644
index 0000000000000000000000000000000000000000..1915a7bf0ce151abcae22b65caa99fe4daaf18a0
--- /dev/null
+++ b/atomicio.c
@@ -0,0 +1,63 @@
+/*
+ * Copied from OpenSSH 3.6.1p2.
+ * 
+ * Copyright (c) 1995,1999 Theo de Raadt.  All rights reserved.
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+
+/* RCSID("OpenBSD: atomicio.c,v 1.10 2001/05/08 22:48:07 markus Exp "); */
+
+#include "atomicio.h"
+
+/*
+ * ensure all of data on socket comes through. f==read || f==write
+ */
+ssize_t
+atomicio(f, fd, _s, n)
+	ssize_t (*f) ();
+	int fd;
+	void *_s;
+	size_t n;
+{
+	char *s = _s;
+	ssize_t res;
+	size_t pos = 0;
+
+	while (n > pos) {
+		res = (f) (fd, s + pos, n - pos);
+		switch (res) {
+		case -1:
+#ifdef EWOULDBLOCK
+			if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)
+#else
+			if (errno == EINTR || errno == EAGAIN)
+#endif
+				continue;
+		case 0:
+			return (res);
+		default:
+			pos += res;
+		}
+	}
+	return (pos);
+}
diff --git a/atomicio.h b/atomicio.h
new file mode 100644
index 0000000000000000000000000000000000000000..6c1f3accbdb85fd1c889fd360f7a9cc39b2c591b
--- /dev/null
+++ b/atomicio.h
@@ -0,0 +1,36 @@
+
+/*
+ * Copied from OpenSSH 3.6.1p2, required for loginrec.c
+ *
+ * $OpenBSD: atomicio.h,v 1.4 2001/06/26 06:32:46 itojun Exp $
+ *
+ * Copyright (c) 1995,1999 Theo de Raadt.  All rights reserved.
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+
+#include "includes.h"
+
+/*
+ * Ensure all of data on socket comes through. f==read || f==write
+ */
+ssize_t	atomicio(ssize_t (*)(), int, void *, size_t);
diff --git a/auth.h b/auth.h
new file mode 100644
index 0000000000000000000000000000000000000000..c407ad559e7a0fba1272172ea8ace0dfc00ea04c
--- /dev/null
+++ b/auth.h
@@ -0,0 +1,111 @@
+/*
+ * 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_H_
+#define _AUTH_H_
+
+#include "includes.h"
+
+void svr_authinitialise();
+void cli_authinitialise();
+
+/* Server functions */
+void recv_msg_userauth_request();
+void send_msg_userauth_failure(int partial, int incrfail);
+void send_msg_userauth_success();
+void svr_auth_password();
+void svr_auth_pubkey();
+void svr_auth_pam();
+
+/* Client functions */
+void recv_msg_userauth_failure();
+void recv_msg_userauth_success();
+void recv_msg_userauth_specific_60();
+void recv_msg_userauth_pk_ok();
+void recv_msg_userauth_info_request();
+void cli_get_user();
+void cli_auth_getmethods();
+void cli_auth_try();
+void recv_msg_userauth_banner();
+void cli_pubkeyfail();
+void cli_auth_password();
+int cli_auth_pubkey();
+void cli_auth_interactive();
+char* getpass_or_cancel();
+
+
+#define MAX_USERNAME_LEN 25 /* arbitrary for the moment */
+
+#define AUTH_TYPE_NONE      1
+#define AUTH_TYPE_PUBKEY    1 << 1
+#define AUTH_TYPE_PASSWORD  1 << 2
+#define AUTH_TYPE_INTERACT  1 << 3
+
+#define AUTH_METHOD_NONE "none"
+#define AUTH_METHOD_NONE_LEN 4
+#define AUTH_METHOD_PUBKEY "publickey"
+#define AUTH_METHOD_PUBKEY_LEN 9
+#define AUTH_METHOD_PASSWORD "password"
+#define AUTH_METHOD_PASSWORD_LEN 8
+#define AUTH_METHOD_INTERACT "keyboard-interactive"
+#define AUTH_METHOD_INTERACT_LEN 20
+
+
+
+/* This structure is shared between server and client - it contains
+ * relatively little extraneous bits when used for the client rather than the
+ * server */
+struct AuthState {
+
+	char *username; /* This is the username the client presents to check. It
+					   is updated each run through, used for auth checking */
+	unsigned char authtypes; /* Flags indicating which auth types are still 
+								valid */
+	unsigned int failcount; /* Number of (failed) authentication attempts.*/
+	unsigned authdone : 1; /* 0 if we haven't authed, 1 if we have. Applies for
+							  client and server (though has differing [obvious]
+							  meanings). */
+	unsigned perm_warn : 1; /* Server only, set if bad permissions on 
+							   ~/.ssh/authorized_keys have already been
+							   logged. */
+
+	/* These are only used for the server */
+	char *printableuser; /* stripped of control chars, used for logs etc */
+	struct passwd * pw;
+
+};
+
+struct SignKeyList;
+/* A singly linked list of signing keys */
+struct SignKeyList {
+
+	sign_key *key;
+	int type; /* The type of key */
+	struct SignKeyList *next;
+	/* filename? or the buffer? for encrypted keys, so we can later get
+	 * the private key portion */
+
+};
+
+#endif /* _AUTH_H_ */
diff --git a/bignum.c b/bignum.c
new file mode 100644
index 0000000000000000000000000000000000000000..60b52207b27a12f562ce569db7ee050cd2be7498
--- /dev/null
+++ b/bignum.c
@@ -0,0 +1,75 @@
+/*
+ * 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. */
+
+/* Contains helper functions for mp_int handling */
+
+#include "includes.h"
+#include "dbutil.h"
+
+/* wrapper for mp_init, failing fatally on errors (memory allocation) */
+void m_mp_init(mp_int *mp) {
+
+	if (mp_init(mp) != MP_OKAY) {
+		dropbear_exit("mem alloc error");
+	}
+}
+
+/* simplified duplication of bn_mp_multi's mp_init_multi, but die fatally
+ * on error */
+void m_mp_init_multi(mp_int *mp, ...) 
+{
+    mp_int* cur_arg = mp;
+    va_list args;
+
+    va_start(args, mp);        /* init args to next argument from caller */
+    while (cur_arg != NULL) {
+        if (mp_init(cur_arg) != MP_OKAY) {
+			dropbear_exit("mem alloc error");
+        }
+        cur_arg = va_arg(args, mp_int*);
+    }
+    va_end(args);
+}
+
+void bytes_to_mp(mp_int *mp, const unsigned char* bytes, unsigned int len) {
+
+	if (mp_read_unsigned_bin(mp, (unsigned char*)bytes, len) != MP_OKAY) {
+		dropbear_exit("mem alloc error");
+	}
+}
+
+/* hash the ssh representation of the mp_int mp */
+void sha1_process_mp(hash_state *hs, mp_int *mp) {
+
+	int i;
+	buffer * buf;
+
+	buf = buf_new(512 + 20); /* max buffer is a 4096 bit key, 
+								plus header + some leeway*/
+	buf_putmpint(buf, mp);
+	i = buf->pos;
+	buf_setpos(buf, 0);
+	sha1_process(hs, buf_getptr(buf, i), i);
+	buf_free(buf);
+}
diff --git a/bignum.h b/bignum.h
new file mode 100644
index 0000000000000000000000000000000000000000..042f81151f9760b520d2fb8a24505d0df7e8cf6f
--- /dev/null
+++ b/bignum.h
@@ -0,0 +1,35 @@
+/*
+ * 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 _BIGNUM_H_
+#define _BIGNUM_H_
+
+#include "includes.h"
+
+void m_mp_init(mp_int *mp);
+void m_mp_init_multi(mp_int *mp, ...);
+void bytes_to_mp(mp_int *mp, const unsigned char* bytes, unsigned int len);
+void sha1_process_mp(hash_state *hs, mp_int *mp);
+
+#endif /* _BIGNUM_H_ */
diff --git a/buffer.c b/buffer.c
new file mode 100644
index 0000000000000000000000000000000000000000..579fa6f98d6d5727e4094bfe1ab49d351e4dcf0f
--- /dev/null
+++ b/buffer.c
@@ -0,0 +1,338 @@
+/*
+ * 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. */
+
+/* Buffer handling routines, designed to avoid overflows/using invalid data */
+
+#include "includes.h"
+#include "dbutil.h"
+#include "buffer.h"
+
+/* Prevent integer overflows when incrementing buffer position/length.
+ * Calling functions should check arguments first, but this provides a
+ * backstop */
+#define BUF_MAX_INCR 1000000000
+#define BUF_MAX_SIZE 1000000000
+
+/* 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) {
+
+	buffer* buf;
+	
+	if (size > BUF_MAX_SIZE) {
+		dropbear_exit("buf->size too big");
+	}
+
+	buf = (buffer*)m_malloc(sizeof(buffer));
+
+	if (size > 0) {
+		buf->data = (unsigned char*)m_malloc(size);
+	} else {
+		buf->data = NULL;
+	}
+
+	buf->size = size;
+	buf->pos = 0;
+	buf->len = 0;
+
+	return buf;
+
+}
+
+/* free the buffer's data and the buffer itself */
+void buf_free(buffer* buf) {
+
+	m_free(buf->data)
+	m_free(buf);
+}
+
+/* overwrite the contents of the buffer to clear it */
+void buf_burn(buffer* buf) {
+	
+	m_burn(buf->data, buf->size);
+
+}
+
+/* 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) {
+		dropbear_exit("buf->size too big");
+	}
+
+	buf->data = m_realloc(buf->data, newsize);
+	buf->size = newsize;
+	buf->len = MIN(newsize, buf->len);
+	buf->pos = MIN(newsize, buf->pos);
+
+}
+
+/* Create a copy of buf, allocating required memory etc. */
+/* The new buffer is sized the same as the length of the source buffer. */
+buffer* buf_newcopy(buffer* buf) {
+	
+	buffer* ret;
+
+	ret = buf_new(buf->len);
+	ret->len = buf->len;
+	memcpy(ret->data, buf->data, buf->len);
+	return ret;
+}
+
+/* Set the length of the buffer */
+void buf_setlen(buffer* buf, unsigned int len) {
+	if (len > buf->size) {
+		dropbear_exit("bad buf_setlen");
+	}
+	buf->len = len;
+}
+
+/* Increment the length of the buffer */
+void buf_incrlen(buffer* buf, unsigned int incr) {
+	if (incr > BUF_MAX_INCR || buf->len + incr > buf->size) {
+		dropbear_exit("bad buf_incrlen");
+	}
+	buf->len += incr;
+}
+/* Set the position of the buffer */
+void buf_setpos(buffer* buf, unsigned int pos) {
+
+	if (pos > buf->len) {
+		dropbear_exit("bad buf_setpos");
+	}
+	buf->pos = pos;
+}
+
+/* increment the postion by incr, increasing the buffer length if required */
+void buf_incrwritepos(buffer* buf, unsigned int incr) {
+	if (incr > BUF_MAX_INCR || buf->pos + incr > buf->size) {
+		dropbear_exit("bad buf_incrwritepos");
+	}
+	buf->pos += incr;
+	if (buf->pos > buf->len) {
+		buf->len = buf->pos;
+	}
+}
+
+/* increment the position by incr, negative values are allowed, to
+ * decrement the pos*/
+void buf_incrpos(buffer* buf,  int incr) {
+	if (incr > BUF_MAX_INCR ||
+			(unsigned int)((int)buf->pos + incr) > buf->len 
+			|| ((int)buf->pos + incr) < 0) {
+		dropbear_exit("bad buf_incrpos");
+	}
+	buf->pos += incr;
+}
+
+/* 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
+	 * bad case of pos > len, which should _never_ happen. */
+	if (buf->pos >= buf->len) {
+		dropbear_exit("bad buf_getbyte");
+	}
+	return buf->data[buf->pos++];
+}
+
+/* Get a bool from the buffer and increment the pos */
+unsigned char buf_getbool(buffer* buf) {
+
+	unsigned char b;
+	b = buf_getbyte(buf);
+	if (b != 0)
+		b = 1;
+	return b;
+}
+
+/* put a byte, incrementing the length if required */
+void buf_putbyte(buffer* buf, unsigned char val) {
+
+	if (buf->pos >= buf->len) {
+		buf_incrlen(buf, 1);
+	}
+	buf->data[buf->pos] = val;
+	buf->pos++;
+}
+
+/* returns an in-place pointer to the buffer, checking that
+ * the next len bytes from that position can be used */
+unsigned char* buf_getptr(buffer* buf, unsigned int len) {
+
+	if (buf->pos + len > buf->len) {
+		dropbear_exit("bad buf_getptr");
+	}
+	return &buf->data[buf->pos];
+}
+
+/* like buf_getptr, but checks against total size, not used length.
+ * This allows writing past the used length, but not past the size */
+unsigned char* buf_getwriteptr(buffer* buf, unsigned int len) {
+
+	if (buf->pos + len > buf->size) {
+		dropbear_exit("bad buf_getwriteptr");
+	}
+	return &buf->data[buf->pos];
+}
+
+/* Return a null-terminated string, it is malloced, so must be free()ed
+ * Note that the string isn't checked for null bytes, hence the retlen
+ * may be longer than what is returned by strlen */
+unsigned char* buf_getstring(buffer* buf, unsigned int *retlen) {
+
+	unsigned int len;
+	unsigned char* ret;
+	len = buf_getint(buf);
+	if (len > MAX_STRING_LEN) {
+		dropbear_exit("string too long");
+	}
+
+	if (retlen != NULL) {
+		*retlen = len;
+	}
+	ret = m_malloc(len+1);
+	memcpy(ret, buf_getptr(buf, len), len);
+	buf_incrpos(buf, len);
+	ret[len] = '\0';
+
+	return ret;
+}
+
+/* Just increment the buffer position the same as if we'd used buf_getstring,
+ * but don't bother copying/malloc()ing for it */
+void buf_eatstring(buffer *buf) {
+
+	buf_incrpos( buf, buf_getint(buf) );
+}
+
+/* Get an uint32 from the buffer and increment the pos */
+unsigned int buf_getint(buffer* buf) {
+	unsigned int ret;
+
+	LOAD32H(ret, buf_getptr(buf, 4));
+	buf_incrpos(buf, 4);
+	return ret;
+}
+
+/* put a 32bit uint into the buffer, incr bufferlen & pos if required */
+void buf_putint(buffer* buf, int unsigned val) {
+
+	STORE32H(val, buf_getwriteptr(buf, 4));
+	buf_incrwritepos(buf, 4);
+
+}
+
+/* put a SSH style string into the buffer, increasing buffer len if required */
+void buf_putstring(buffer* buf, const unsigned char* str, unsigned int len) {
+	
+	buf_putint(buf, len);
+	buf_putbytes(buf, str, len);
+
+}
+
+/* put the set of len bytes into the buffer, incrementing the pos, increasing
+ * len if required */
+void buf_putbytes(buffer *buf, const unsigned char *bytes, unsigned int len) {
+	memcpy(buf_getwriteptr(buf, len), bytes, len);
+	buf_incrwritepos(buf, len);
+}
+	
+
+/* for our purposes we only need positive (or 0) numbers, so will
+ * fail if we get negative numbers */
+void buf_putmpint(buffer* buf, mp_int * mp) {
+
+	unsigned int len, pad = 0;
+	TRACE(("enter buf_putmpint"))
+
+	dropbear_assert(mp != NULL);
+
+	if (SIGN(mp) == MP_NEG) {
+		dropbear_exit("negative bignum");
+	}
+
+	/* zero check */
+	if (USED(mp) == 1 && DIGIT(mp, 0) == 0) {
+		len = 0;
+	} else {
+		/* SSH spec requires padding for mpints with the MSB set, this code
+		 * implements it */
+		len = mp_count_bits(mp);
+		/* if the top bit of MSB is set, we need to pad */
+		pad = (len%8 == 0) ? 1 : 0;
+		len = len / 8 + 1; /* don't worry about rounding, we need it for
+							  padding anyway when len%8 == 0 */
+
+	}
+
+	/* store the length */
+	buf_putint(buf, len);
+	
+	/* store the actual value */
+	if (len > 0) {
+		if (pad) {
+			buf_putbyte(buf, 0x00);
+		}
+		if (mp_to_unsigned_bin(mp, buf_getwriteptr(buf, len-pad)) != MP_OKAY) {
+			dropbear_exit("mpint error");
+		}
+		buf_incrwritepos(buf, len-pad);
+	}
+
+	TRACE(("leave buf_putmpint"))
+}
+
+/* Retrieve an mp_int from the buffer.
+ * Will fail for -ve since they shouldn't be required here.
+ * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
+int buf_getmpint(buffer* buf, mp_int* mp) {
+
+	unsigned int len;
+	len = buf_getint(buf);
+	
+	if (len == 0) {
+		mp_zero(mp);
+		return DROPBEAR_SUCCESS;
+	}
+
+	if (len > BUF_MAX_MPINT) {
+		return DROPBEAR_FAILURE;
+	}
+
+	/* check for negative */
+	if (*buf_getptr(buf, 1) & (1 << (CHAR_BIT-1))) {
+		return DROPBEAR_FAILURE;
+	}
+
+	if (mp_read_unsigned_bin(mp, buf_getptr(buf, len), len) != MP_OKAY) {
+		return DROPBEAR_FAILURE;
+	}
+
+	buf_incrpos(buf, len);
+	return DROPBEAR_SUCCESS;
+}
diff --git a/buffer.h b/buffer.h
new file mode 100644
index 0000000000000000000000000000000000000000..f9aa6fa28dbeeb395c6cab7a790e8c31afacb001
--- /dev/null
+++ b/buffer.h
@@ -0,0 +1,66 @@
+/*
+ * 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 _BUFFER_H_
+
+#define _BUFFER_H_
+
+#include "includes.h"
+
+struct buf {
+
+	unsigned char * data;
+	unsigned int len; /* the used size */
+	unsigned int pos;
+	unsigned int size; /* the memory size */
+
+};
+
+typedef struct buf buffer;
+
+buffer * buf_new(unsigned int size);
+void buf_resize(buffer *buf, unsigned int newsize);
+void buf_free(buffer* buf);
+void buf_burn(buffer* buf);
+buffer* buf_newcopy(buffer* buf);
+void buf_setlen(buffer* buf, unsigned int len);
+void buf_incrlen(buffer* buf, unsigned int incr);
+void buf_setpos(buffer* buf, unsigned int pos);
+void buf_incrpos(buffer* buf, int incr); /* -ve is ok, to go backwards */
+void buf_incrwritepos(buffer* buf, unsigned int incr);
+unsigned char buf_getbyte(buffer* buf);
+unsigned char buf_getbool(buffer* buf);
+void buf_putbyte(buffer* buf, unsigned char val);
+unsigned char* buf_getptr(buffer* buf, unsigned int len);
+unsigned char* buf_getwriteptr(buffer* buf, unsigned int len);
+unsigned char* buf_getstring(buffer* buf, unsigned int *retlen);
+void buf_eatstring(buffer *buf);
+void buf_putint(buffer* buf, unsigned int val);
+void buf_putstring(buffer* buf, const unsigned char* str, unsigned int len);
+void buf_putbytes(buffer *buf, const unsigned char *bytes, unsigned int len);
+void buf_putmpint(buffer* buf, mp_int * mp);
+int buf_getmpint(buffer* buf, mp_int* mp);
+unsigned int buf_getint(buffer* buf);
+
+#endif /* _BUFFER_H_ */
diff --git a/channel.h b/channel.h
new file mode 100644
index 0000000000000000000000000000000000000000..7030a0f214f6df61fc2a04de7a1d924b12712a9c
--- /dev/null
+++ b/channel.h
@@ -0,0 +1,135 @@
+/*
+ * 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 _CHANNEL_H_
+#define _CHANNEL_H_
+
+#include "includes.h"
+#include "buffer.h"
+#include "circbuffer.h"
+
+/* channel->type values */
+#define CHANNEL_ID_NONE 0
+#define CHANNEL_ID_SESSION 1
+#define CHANNEL_ID_X11 2
+#define CHANNEL_ID_AGENT 3
+#define CHANNEL_ID_TCPDIRECT 4
+#define CHANNEL_ID_TCPFORWARDED 5
+
+#define SSH_OPEN_ADMINISTRATIVELY_PROHIBITED    1
+#define SSH_OPEN_CONNECT_FAILED                 2
+#define SSH_OPEN_UNKNOWN_CHANNEL_TYPE           3
+#define SSH_OPEN_RESOURCE_SHORTAGE              4
+
+/* 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 8000 /* tweak */
+#define RECV_WINDOWEXTEND 1000 /* We send a "window extend" every
+								RECV_WINDOWEXTEND bytes */
+#define RECV_MAXPACKET RECV_MAXWINDOW /* tweak */
+
+struct ChanType;
+
+struct Channel {
+
+	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 writefd; /* read from wire, written to insecure side */
+	int readfd; /* read from insecure size, written to wire */
+	int errfd; /* used like writefd or readfd, 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;
+
+	/* this is set when we receive/send a channel eof packet */
+	int recveof, senteof;
+
+	int initconn; /* used for TCP forwarding, whether the channel has been
+					 fully initialised */
+
+	int await_open; /* flag indicating whether we've sent an open request
+					   for this channel (and are awaiting a confirmation
+					   or failure). */
+
+	const struct ChanType* type;
+
+};
+
+struct ChanType {
+
+	int sepfds; /* Whether this channel has seperate pipes for in/out or not */
+	char *name;
+	int (*inithandler)(struct Channel*);
+	int (*checkclose)(struct Channel*);
+	void (*reqhandler)(struct Channel*);
+	void (*closehandler)(struct Channel*);
+
+};
+
+void chaninitialise(const struct ChanType *chantypes[]);
+void chancleanup();
+void setchannelfds(fd_set *readfd, fd_set *writefd);
+void channelio(fd_set *readfd, fd_set *writefd);
+struct Channel* getchannel();
+struct Channel* newchannel(unsigned int remotechan, 
+		const struct ChanType *type, 
+		unsigned int transwindow, unsigned int transmaxpacket);
+
+void recv_msg_channel_open();
+void recv_msg_channel_request();
+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);
+
+#ifdef DROPBEAR_CLIENT
+extern const struct ChanType clichansess;
+#endif
+
+#if defined(USING_LISTENERS) || defined(DROPBEAR_CLIENT)
+int send_msg_channel_open_init(int fd, const struct ChanType *type);
+void recv_msg_channel_open_confirmation();
+void recv_msg_channel_open_failure();
+#endif
+
+#endif /* _CHANNEL_H_ */
diff --git a/chansession.h b/chansession.h
new file mode 100644
index 0000000000000000000000000000000000000000..213c285cf5d3caddf12c8b0a1f9fb847e10f9db7
--- /dev/null
+++ b/chansession.h
@@ -0,0 +1,92 @@
+/*
+ * 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 _CHANSESSION_H_
+#define _CHANSESSION_H_
+
+#include "loginrec.h"
+#include "channel.h"
+#include "listener.h"
+
+struct exitinfo {
+
+	int exitpid; /* -1 if not exited */
+	int exitstatus;
+	int exitsignal;
+	int exitcore;
+};
+
+struct ChanSess {
+
+	unsigned char * cmd; /* command to exec */
+	pid_t pid; /* child process pid */
+
+	/* pty details */
+	int master; /* the master terminal fd*/
+	int slave;
+	unsigned char * tty;
+	unsigned char * term;
+
+	/* exit details */
+	struct exitinfo exit;
+	
+#ifndef DISABLE_X11FWD
+	struct Listener * x11listener;
+	int x11port;
+	char * x11authprot;
+	char * x11authcookie;
+	unsigned int x11screennum;
+	unsigned char x11singleconn;
+#endif
+
+#ifndef DISABLE_AGENTFWD
+	struct Listener * agentlistener;
+	char * agentfile;
+	char * agentdir;
+#endif
+};
+
+struct ChildPid {
+	pid_t pid;
+	struct ChanSess * chansess;
+};
+
+
+void addnewvar(const char* param, const char* var);
+
+void cli_send_chansess_request();
+void cli_tty_cleanup();
+void cli_chansess_winchange();
+
+void svr_chansessinitialise();
+extern const struct ChanType svrchansess;
+
+struct SigMap {
+	int signal;
+	char* name;
+};
+
+extern const struct SigMap signames[];
+
+#endif /* _CHANSESSION_H_ */
diff --git a/circbuffer.c b/circbuffer.c
new file mode 100644
index 0000000000000000000000000000000000000000..e70087ad72e29666a5772f3d139f9da3ad57bf48
--- /dev/null
+++ b/circbuffer.c
@@ -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) {
+
+	dropbear_assert(((2*cbuf->size)+cbuf->writepos-cbuf->readpos)%cbuf->size == cbuf->used%cbuf->size);
+	dropbear_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) {
+
+	dropbear_assert(cbuf->used <= cbuf->size);
+	dropbear_assert(((2*cbuf->size)+cbuf->writepos-cbuf->readpos)%cbuf->size == cbuf->used%cbuf->size);
+	dropbear_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;
+	dropbear_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");
+	}
+
+	dropbear_assert(cbuf->used >= len);
+	cbuf->used -= len;
+	cbuf->readpos = (cbuf->readpos + len) % cbuf->size;
+}
diff --git a/circbuffer.h b/circbuffer.h
new file mode 100644
index 0000000000000000000000000000000000000000..21c5134b74b9a91d1c87718063783a1524310c86
--- /dev/null
+++ b/circbuffer.h
@@ -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
diff --git a/cli-algo.c b/cli-algo.c
new file mode 100644
index 0000000000000000000000000000000000000000..ec3a1ff0b8d274f91169cbfa5d7ae82e61e84725
--- /dev/null
+++ b/cli-algo.c
@@ -0,0 +1,99 @@
+/*
+ * 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 "algo.h"
+#include "dbutil.h"
+
+
+/*
+ * The chosen [encryption | MAC | compression] algorithm to each 
+ * direction MUST be the first algorithm  on the client's list
+ * that is also on the server's list.
+ */
+algo_type * cli_buf_match_algo(buffer* buf, algo_type localalgos[],
+		int *goodguess) {
+
+	unsigned char * algolist = NULL;
+	unsigned char * remotealgos[MAX_PROPOSED_ALGO];
+	unsigned int len;
+	unsigned int count, i, j;
+	algo_type * ret = NULL;
+
+	*goodguess = 0;
+
+	/* get the comma-separated list from the buffer ie "algo1,algo2,algo3" */
+	algolist = buf_getstring(buf, &len);
+	TRACE(("cli_buf_match_algo: %s", algolist))
+	if (len > MAX_PROPOSED_ALGO*(MAX_NAME_LEN+1)) {
+		goto out; /* just a sanity check, no other use */
+	}
+
+	/* remotealgos will contain a list of the strings parsed out */
+	/* We will have at least one string (even if it's just "") */
+	remotealgos[0] = algolist;
+	count = 1;
+	/* Iterate through, replacing ','s with NULs, to split it into
+	 * words. */
+	for (i = 0; i < len; i++) {
+		if (algolist[i] == '\0') {
+			/* someone is trying something strange */
+			goto out;
+		}
+		if (algolist[i] == ',') {
+			algolist[i] = '\0';
+			remotealgos[count] = &algolist[i+1];
+			count++;
+		}
+		if (count == MAX_PROPOSED_ALGO) {
+			break;
+		}
+	}
+
+	/* iterate and find the first match */
+
+	for (j = 0; localalgos[j].name != NULL; j++) {
+		if (localalgos[j].usable) {
+		len = strlen(localalgos[j].name);
+			for (i = 0; i < count; i++) {
+				if (len == strlen(remotealgos[i]) 
+						&& strncmp(localalgos[j].name, 
+							remotealgos[i], len) == 0) {
+					if (i == 0 && j == 0) {
+						/* was a good guess */
+						*goodguess = 1;
+					}
+					ret = &localalgos[j];
+					goto out;
+				}
+			}
+		}
+	}
+
+out:
+	m_free(algolist);
+	return ret;
+}
+
diff --git a/cli-auth.c b/cli-auth.c
new file mode 100644
index 0000000000000000000000000000000000000000..d08de9a33bbbd3e861a1735606cf54179fc37fa7
--- /dev/null
+++ b/cli-auth.c
@@ -0,0 +1,295 @@
+/*
+ * 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"
+#include "dbutil.h"
+#include "buffer.h"
+#include "ssh.h"
+#include "packet.h"
+#include "runopts.h"
+
+void cli_authinitialise() {
+
+	memset(&ses.authstate, 0, sizeof(ses.authstate));
+}
+
+
+/* Send a "none" auth request to get available methods */
+void cli_auth_getmethods() {
+
+	TRACE(("enter cli_auth_getmethods"))
+
+	CHECKCLEARTOWRITE();
+
+	buf_putbyte(ses.writepayload, SSH_MSG_USERAUTH_REQUEST);
+	buf_putstring(ses.writepayload, cli_opts.username, 
+			strlen(cli_opts.username));
+	buf_putstring(ses.writepayload, SSH_SERVICE_CONNECTION, 
+			SSH_SERVICE_CONNECTION_LEN);
+	buf_putstring(ses.writepayload, "none", 4); /* 'none' method */
+
+	encrypt_packet();
+	TRACE(("leave cli_auth_getmethods"))
+
+}
+
+void recv_msg_userauth_banner() {
+
+	unsigned char* banner = NULL;
+	unsigned int bannerlen;
+	unsigned int i, linecount;
+
+	TRACE(("enter recv_msg_userauth_banner"))
+	if (ses.authstate.authdone) {
+		TRACE(("leave recv_msg_userauth_banner: banner after auth done"))
+		return;
+	}
+
+	banner = buf_getstring(ses.payload, &bannerlen);
+	buf_eatstring(ses.payload); /* The language string */
+
+	if (bannerlen > MAX_BANNER_SIZE) {
+		TRACE(("recv_msg_userauth_banner: bannerlen too long: %d", bannerlen))
+		goto out;
+	}
+
+	cleantext(banner);
+
+	/* Limit to 25 lines */
+	linecount = 1;
+	for (i = 0; i < bannerlen; i++) {
+		if (banner[i] == '\n') {
+			if (linecount >= MAX_BANNER_LINES) {
+				banner[i] = '\0';
+				break;
+			}
+			linecount++;
+		}
+	}
+
+	printf("%s\n", banner);
+
+out:
+	m_free(banner);
+	TRACE(("leave recv_msg_userauth_banner"))
+}
+
+/* This handles the message-specific types which
+ * all have a value of 60. These are
+ * SSH_MSG_USERAUTH_PASSWD_CHANGEREQ,
+ * SSH_MSG_USERAUTH_PK_OK, &
+ * SSH_MSG_USERAUTH_INFO_REQUEST. */
+void recv_msg_userauth_specific_60() {
+
+#ifdef ENABLE_CLI_PUBKEY_AUTH
+	if (cli_ses.lastauthtype == AUTH_TYPE_PUBKEY) {
+		recv_msg_userauth_pk_ok();
+		return;
+	}
+#endif
+
+#ifdef ENABLE_CLI_INTERACT_AUTH
+	if (cli_ses.lastauthtype == AUTH_TYPE_INTERACT) {
+		recv_msg_userauth_info_request();
+		return;
+	}
+#endif
+
+#ifdef ENABLE_CLI_PASSWORD_AUTH
+	if (cli_ses.lastauthtype == AUTH_TYPE_PASSWORD) {
+		/* Eventually there could be proper password-changing
+		 * support. However currently few servers seem to
+		 * implement it, and password auth is last-resort
+		 * regardless - keyboard-interactive is more likely
+		 * to be used anyway. */
+		dropbear_close("Your password has expired.");
+	}
+#endif
+
+	dropbear_exit("Unexpected userauth packet");
+}
+
+void recv_msg_userauth_failure() {
+
+	unsigned char * methods = NULL;
+	unsigned char * tok = NULL;
+	unsigned int methlen = 0;
+	unsigned int partial = 0;
+	unsigned int i = 0;
+
+	TRACE(("<- MSG_USERAUTH_FAILURE"))
+	TRACE(("enter recv_msg_userauth_failure"))
+
+	if (cli_ses.state != USERAUTH_REQ_SENT) {
+		/* Perhaps we should be more fatal? */
+		dropbear_exit("Unexpected userauth failure");
+	}
+
+#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) {
+		cli_pubkeyfail();
+	}
+#endif
+
+#ifdef ENABLE_CLI_INTERACT_AUTH
+	/* If we get a failure message for keyboard interactive without
+	 * receiving any request info packet, then we don't bother trying
+	 * keyboard interactive again */
+	if (cli_ses.lastauthtype == AUTH_TYPE_INTERACT
+			&& !cli_ses.interact_request_received) {
+		TRACE(("setting auth_interact_failed = 1"))
+		cli_ses.auth_interact_failed = 1;
+	}
+#endif
+
+	cli_ses.lastauthtype = AUTH_TYPE_NONE;
+
+	methods = buf_getstring(ses.payload, &methlen);
+
+	partial = buf_getbool(ses.payload);
+
+	if (partial) {
+		dropbear_log(LOG_INFO, "Authentication partially succeeded, more attempts required");
+	} else {
+		ses.authstate.failcount++;
+	}
+
+	TRACE(("Methods (len %d): '%s'", methlen, methods))
+
+	ses.authstate.authdone=0;
+	ses.authstate.authtypes=0;
+
+	/* Split with nulls rather than commas */
+	for (i = 0; i < methlen; i++) {
+		if (methods[i] == ',') {
+			methods[i] = '\0';
+		}
+	}
+
+	tok = methods; /* tok stores the next method we'll compare */
+	for (i = 0; i <= methlen; i++) {
+		if (methods[i] == '\0') {
+			TRACE(("auth method '%s'", tok))
+#ifdef ENABLE_CLI_PUBKEY_AUTH
+			if (strncmp(AUTH_METHOD_PUBKEY, tok,
+				AUTH_METHOD_PUBKEY_LEN) == 0) {
+				ses.authstate.authtypes |= AUTH_TYPE_PUBKEY;
+			}
+#endif
+#ifdef ENABLE_CLI_INTERACT_AUTH
+			if (strncmp(AUTH_METHOD_INTERACT, tok,
+				AUTH_METHOD_INTERACT_LEN) == 0) {
+				ses.authstate.authtypes |= AUTH_TYPE_INTERACT;
+			}
+#endif
+#ifdef ENABLE_CLI_PASSWORD_AUTH
+			if (strncmp(AUTH_METHOD_PASSWORD, tok,
+				AUTH_METHOD_PASSWORD_LEN) == 0) {
+				ses.authstate.authtypes |= AUTH_TYPE_PASSWORD;
+			}
+#endif
+			tok = &methods[i+1]; /* Must make sure we don't use it after the
+									last loop, since it'll point to something
+									undefined */
+		}
+	}
+
+	m_free(methods);
+
+	cli_ses.state = USERAUTH_FAIL_RCVD;
+		
+	TRACE(("leave recv_msg_userauth_failure"))
+}
+
+void recv_msg_userauth_success() {
+	TRACE(("received msg_userauth_success"))
+	ses.authstate.authdone = 1;
+	cli_ses.state = USERAUTH_SUCCESS_RCVD;
+	cli_ses.lastauthtype = AUTH_TYPE_NONE;
+}
+
+void cli_auth_try() {
+
+	TRACE(("enter cli_auth_try"))
+	int finished = 0;
+
+	CHECKCLEARTOWRITE();
+	
+	/* Order to try is pubkey, interactive, password.
+	 * As soon as "finished" is set for one, we don't do any more. */
+#ifdef ENABLE_CLI_PUBKEY_AUTH
+	if (ses.authstate.authtypes & AUTH_TYPE_PUBKEY) {
+		finished = cli_auth_pubkey();
+		cli_ses.lastauthtype = AUTH_TYPE_PUBKEY;
+	}
+#endif
+
+#ifdef ENABLE_CLI_INTERACT_AUTH
+	if (!finished && ses.authstate.authtypes & AUTH_TYPE_INTERACT) {
+		if (cli_ses.auth_interact_failed) {
+			finished = 0;
+		} else {
+			cli_auth_interactive();
+			cli_ses.lastauthtype = AUTH_TYPE_INTERACT;
+			finished = 1;
+		}
+	}
+#endif
+
+#ifdef ENABLE_CLI_PASSWORD_AUTH
+	if (!finished && ses.authstate.authtypes & AUTH_TYPE_PASSWORD) {
+		cli_auth_password();
+		finished = 1;
+		cli_ses.lastauthtype = AUTH_TYPE_PASSWORD;
+	}
+#endif
+
+	TRACE(("cli_auth_try lastauthtype %d", cli_ses.lastauthtype))
+
+	if (!finished) {
+		dropbear_exit("No auth methods could be used.");
+	}
+
+	TRACE(("leave cli_auth_try"))
+}
+
+/* A helper for getpass() that exits if the user cancels. The returned
+ * password is statically allocated by getpass() */
+char* getpass_or_cancel()
+{
+	char* password = NULL;
+
+	password = getpass("Password: ");
+
+	/* 0x03 is a ctrl-c character in the buffer. */
+	if (password == NULL || strchr(password, '\3') != NULL) {
+		dropbear_close("Interrupted.");
+	}
+	return password;
+}
diff --git a/cli-authinteract.c b/cli-authinteract.c
new file mode 100644
index 0000000000000000000000000000000000000000..5a169cb2e90895a5c5dcb1af544abf9e4e75a90c
--- /dev/null
+++ b/cli-authinteract.c
@@ -0,0 +1,168 @@
+/*
+ * Dropbear SSH
+ * 
+ * Copyright (c) 2005 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"
+#include "session.h"
+#include "ssh.h"
+#include "runopts.h"
+
+#ifdef ENABLE_CLI_INTERACT_AUTH
+
+static unsigned char* get_response(unsigned char* prompt)
+{
+	FILE* tty = NULL;
+	unsigned char* response = NULL;
+	/* not a password, but a reasonable limit */
+	char buf[DROPBEAR_MAX_CLI_PASS];
+	char* ret = NULL;
+
+	fprintf(stderr, "%s", prompt);
+
+	tty = fopen(_PATH_TTY, "r");
+	if (tty) {
+		ret = fgets(buf, sizeof(buf), tty);
+		fclose(tty);
+	} else {
+		ret = fgets(buf, sizeof(buf), stdin);
+	}
+
+	if (ret == NULL) {
+		response = (unsigned char*)m_strdup("");
+	} else {
+		unsigned int buflen = strlen(buf);
+		/* fgets includes newlines */
+		if (buflen > 0 && buf[buflen-1] == '\n')
+			buf[buflen-1] = '\0';
+		response = (unsigned char*)m_strdup(buf);
+	}
+
+	m_burn(buf, sizeof(buf));
+
+	return response;
+}
+
+void recv_msg_userauth_info_request() {
+
+	unsigned char *name = NULL;
+	unsigned char *instruction = NULL;
+	unsigned int num_prompts = 0;
+	unsigned int i;
+
+	unsigned char *prompt = NULL;
+	unsigned int echo = 0;
+	unsigned char *response = NULL;
+
+	TRACE(("enter recv_msg_recv_userauth_info_request"))
+
+	cli_ses.interact_request_received = 1;
+
+	name = buf_getstring(ses.payload, NULL);
+	instruction = buf_getstring(ses.payload, NULL);
+
+	/* language tag */
+	buf_eatstring(ses.payload);
+
+	num_prompts = buf_getint(ses.payload);
+	
+	if (num_prompts >= DROPBEAR_MAX_CLI_INTERACT_PROMPTS) {
+		dropbear_exit("Too many prompts received for keyboard-interactive");
+	}
+
+	/* we'll build the response as we go */
+	CHECKCLEARTOWRITE();
+	buf_putbyte(ses.writepayload, SSH_MSG_USERAUTH_INFO_RESPONSE);
+	buf_putint(ses.writepayload, num_prompts);
+
+	if (strlen(name) > 0) {
+		cleantext(name);
+		fprintf(stderr, "%s", name);
+		m_free(name);
+	}
+	if (strlen(instruction) > 0) {
+		cleantext(instruction);
+		fprintf(stderr, "%s", instruction);
+		m_free(instruction);
+	}
+
+	for (i = 0; i < num_prompts; i++) {
+		unsigned int response_len = 0;
+		prompt = buf_getstring(ses.payload, NULL);
+		cleantext(prompt);
+
+		echo = buf_getbool(ses.payload);
+
+		if (!echo) {
+			unsigned char* p = getpass_or_cancel(prompt);
+			response = m_strdup(p);
+			m_burn(p, strlen(p));
+		} else {
+			response = get_response(prompt);
+		}
+
+		response_len = strlen(response);
+		buf_putstring(ses.writepayload, response, response_len);
+		m_burn(response, response_len);
+		m_free(response);
+	}
+
+	encrypt_packet();
+
+
+	TRACE(("leave recv_msg_recv_userauth_info_request"))
+}
+
+void cli_auth_interactive() {
+
+	TRACE(("enter cli_auth_interactive"))
+	CHECKCLEARTOWRITE();
+
+	buf_putbyte(ses.writepayload, SSH_MSG_USERAUTH_REQUEST);
+
+	/* username */
+	buf_putstring(ses.writepayload, cli_opts.username,
+			strlen(cli_opts.username));
+
+	/* service name */
+	buf_putstring(ses.writepayload, SSH_SERVICE_CONNECTION, 
+			SSH_SERVICE_CONNECTION_LEN);
+
+	/* method */
+	buf_putstring(ses.writepayload, AUTH_METHOD_INTERACT,
+			AUTH_METHOD_INTERACT_LEN);
+
+	/* empty language tag */
+	buf_putstring(ses.writepayload, "", 0);
+
+	/* empty submethods */
+	buf_putstring(ses.writepayload, "", 0);
+
+	encrypt_packet();
+	cli_ses.interact_request_received = 0;
+
+	TRACE(("leave cli_auth_interactive"))
+
+}
+#endif	/* ENABLE_CLI_INTERACT_AUTH */
diff --git a/cli-authpasswd.c b/cli-authpasswd.c
new file mode 100644
index 0000000000000000000000000000000000000000..5dffac4b15928e108e657b560a07c1ba4694d17e
--- /dev/null
+++ b/cli-authpasswd.c
@@ -0,0 +1,150 @@
+/*
+ * 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"
+#include "session.h"
+#include "ssh.h"
+#include "runopts.h"
+
+#ifdef ENABLE_CLI_PASSWORD_AUTH
+
+#ifdef ENABLE_CLI_ASKPASS_HELPER
+/* Returns 1 if we want to use the askpass program, 0 otherwise */
+static int want_askpass()
+{
+	char* askpass_prog = NULL;
+
+	askpass_prog = getenv("SSH_ASKPASS");
+	return askpass_prog && !isatty(STDIN_FILENO) && getenv("DISPLAY");
+}
+
+/* returns a statically allocated password from a helper app, or NULL
+ * on failure */
+static char *gui_getpass(const char *prompt) {
+
+	pid_t pid;
+	int p[2], maxlen, len, status;
+	static char buf[DROPBEAR_MAX_CLI_PASS + 1];
+	char* helper = NULL;
+
+	TRACE(("enter gui_getpass"))
+
+	helper = getenv("SSH_ASKPASS");
+	if (!helper)
+	{
+		TRACE(("leave gui_getpass: no askpass program"))
+		return NULL;
+	}
+
+	if (pipe(p) < 0) {
+		TRACE(("error creating child pipe"))
+		return NULL;
+	}
+
+	pid = fork();
+
+	if (pid < 0) {
+		TRACE(("fork error"))
+		return NULL;
+	}
+
+	if (!pid) {
+		/* child */
+		close(p[0]);
+		if (dup2(p[1], STDOUT_FILENO) < 0) {
+			TRACE(("error redirecting stdout"))
+			exit(1);
+		}
+		close(p[1]);
+		execlp(helper, helper, prompt, (char *)0);
+		TRACE(("execlp error"))
+		exit(1);
+	}
+
+	close(p[1]);
+	maxlen = sizeof(buf);
+	while (maxlen > 0) {
+		len = read(p[0], buf + sizeof(buf) - maxlen, maxlen);
+		if (len > 0) {
+			maxlen -= len;
+		} else {
+			if (errno != EINTR)
+				break;
+		}
+	}
+
+	close(p[0]);
+
+	while (waitpid(pid, &status, 0) < 0 && errno == EINTR)
+		;
+	if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
+		return(NULL);
+
+	len = sizeof(buf) - maxlen;
+	buf[len] = '\0';
+	if (len > 0 && buf[len - 1] == '\n')
+		buf[len - 1] = '\0';
+
+	TRACE(("leave gui_getpass"))
+	return(buf);
+}
+#endif /* ENABLE_CLI_ASKPASS_HELPER */
+
+void cli_auth_password() {
+
+	char* password = NULL;
+
+	TRACE(("enter cli_auth_password"))
+	CHECKCLEARTOWRITE();
+
+#ifdef ENABLE_CLI_ASKPASS_HELPER
+	if (want_askpass())
+		password = gui_getpass("Password: ");
+	else
+#endif
+		password = getpass_or_cancel("Password: ");
+
+	buf_putbyte(ses.writepayload, SSH_MSG_USERAUTH_REQUEST);
+
+	buf_putstring(ses.writepayload, cli_opts.username,
+			strlen(cli_opts.username));
+
+	buf_putstring(ses.writepayload, SSH_SERVICE_CONNECTION, 
+			SSH_SERVICE_CONNECTION_LEN);
+
+	buf_putstring(ses.writepayload, AUTH_METHOD_PASSWORD, 
+			AUTH_METHOD_PASSWORD_LEN);
+
+	buf_putbyte(ses.writepayload, 0); /* FALSE - so says the spec */
+
+	buf_putstring(ses.writepayload, password, strlen(password));
+
+	encrypt_packet();
+	m_burn(password, strlen(password));
+
+	TRACE(("leave cli_auth_password"))
+}
+#endif	/* ENABLE_CLI_PASSWORD_AUTH */
diff --git a/cli-authpubkey.c b/cli-authpubkey.c
new file mode 100644
index 0000000000000000000000000000000000000000..9d36bc38c475ef0b846b196362593305519e5da8
--- /dev/null
+++ b/cli-authpubkey.c
@@ -0,0 +1,187 @@
+/*
+ * 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"
+#include "session.h"
+#include "ssh.h"
+#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.
+ * We use it to remove the key we tried from the list */
+void cli_pubkeyfail() {
+
+	struct SignKeyList *keyitem;
+	struct SignKeyList **previtem;
+
+	TRACE(("enter cli_pubkeyfail"))
+	previtem = &cli_opts.privkeys;
+
+	/* Find the key we failed with, and remove it */
+	for (keyitem = cli_opts.privkeys; keyitem != NULL; keyitem = keyitem->next) {
+		if (keyitem == cli_ses.lastprivkey) {
+			*previtem = keyitem->next;
+		}
+		previtem = &keyitem;
+	}
+
+	sign_key_free(cli_ses.lastprivkey->key); /* It won't be used again */
+	m_free(cli_ses.lastprivkey);
+
+	TRACE(("leave cli_pubkeyfail"))
+}
+
+void recv_msg_userauth_pk_ok() {
+
+	struct SignKeyList *keyitem;
+	buffer* keybuf;
+	char* algotype = NULL;
+	unsigned int algolen;
+	int keytype;
+	unsigned int remotelen;
+
+	TRACE(("enter recv_msg_userauth_pk_ok"))
+
+	algotype = buf_getstring(ses.payload, &algolen);
+	keytype = signkey_type_from_name(algotype, algolen);
+	TRACE(("recv_msg_userauth_pk_ok: type %d", keytype))
+	m_free(algotype);
+
+	keybuf = buf_new(MAX_PUBKEY_SIZE);
+
+	remotelen = buf_getint(ses.payload);
+
+	/* Iterate through our keys, find which one it was that matched, and
+	 * send a real request with that key */
+	for (keyitem = cli_opts.privkeys; keyitem != NULL; keyitem = keyitem->next) {
+
+		if (keyitem->type != keytype) {
+			/* Types differed */
+			TRACE(("types differed"))
+			continue;
+		}
+
+		/* Now we compare the contents of the key */
+		keybuf->pos = keybuf->len = 0;
+		buf_put_pub_key(keybuf, keyitem->key, keytype);
+		buf_setpos(keybuf, 0);
+		buf_incrpos(keybuf, 4); /* first int is the length of the remainder (ie
+								   remotelen) which has already been taken from
+								   the remote buffer */
+
+
+		if (keybuf->len-4 != remotelen) {
+			TRACE(("lengths differed: localh %d remote %d", keybuf->len, remotelen))
+			/* Lengths differed */
+			continue;
+		}
+		if (memcmp(buf_getptr(keybuf, remotelen),
+					buf_getptr(ses.payload, remotelen), remotelen) != 0) {
+			/* Data didn't match this key */
+			TRACE(("data differed"))
+			continue;
+		}
+
+		/* Success */
+		break;
+	}
+
+	if (keyitem != NULL) {
+		TRACE(("matching key"))
+		/* XXX TODO: if it's an encrypted key, here we ask for their
+		 * password */
+		send_msg_userauth_pubkey(keyitem->key, keytype, 1);
+	} else {
+		TRACE(("That was whacky. We got told that a key was valid, but it didn't match our list. Sounds like dodgy code on Dropbear's part"))
+	}
+
+	TRACE(("leave recv_msg_userauth_pk_ok"))
+}
+
+/* TODO: make it take an agent reference to use as well */
+static void send_msg_userauth_pubkey(sign_key *key, int type, int realsign) {
+
+	const char *algoname = NULL;
+	int algolen;
+	buffer* sigbuf = NULL;
+
+	TRACE(("enter send_msg_userauth_pubkey"))
+	CHECKCLEARTOWRITE();
+
+	buf_putbyte(ses.writepayload, SSH_MSG_USERAUTH_REQUEST);
+
+	buf_putstring(ses.writepayload, cli_opts.username,
+			strlen(cli_opts.username));
+
+	buf_putstring(ses.writepayload, SSH_SERVICE_CONNECTION, 
+			SSH_SERVICE_CONNECTION_LEN);
+
+	buf_putstring(ses.writepayload, AUTH_METHOD_PUBKEY, 
+			AUTH_METHOD_PUBKEY_LEN);
+
+	buf_putbyte(ses.writepayload, realsign);
+
+	algoname = signkey_name_from_type(type, &algolen);
+
+	buf_putstring(ses.writepayload, algoname, algolen);
+	buf_put_pub_key(ses.writepayload, key, type);
+
+	if (realsign) {
+		TRACE(("realsign"))
+		/* We put the signature as well - this contains string(session id), then
+		 * the contents of the write payload to this point */
+		sigbuf = buf_new(4 + SHA1_HASH_SIZE + ses.writepayload->len);
+		buf_putstring(sigbuf, ses.session_id, SHA1_HASH_SIZE);
+		buf_putbytes(sigbuf, ses.writepayload->data, ses.writepayload->len);
+		buf_put_sign(ses.writepayload, key, type, sigbuf->data, sigbuf->len);
+		buf_free(sigbuf); /* Nothing confidential in the buffer */
+	}
+
+	encrypt_packet();
+	TRACE(("leave send_msg_userauth_pubkey"))
+}
+
+int cli_auth_pubkey() {
+
+	TRACE(("enter cli_auth_pubkey"))
+
+	if (cli_opts.privkeys != NULL) {
+		/* Send a trial request */
+		send_msg_userauth_pubkey(cli_opts.privkeys->key,
+				cli_opts.privkeys->type, 0);
+		cli_ses.lastprivkey = cli_opts.privkeys;
+		TRACE(("leave cli_auth_pubkey-success"))
+		return 1;
+	} else {
+		TRACE(("leave cli_auth_pubkey-failure"))
+		return 0;
+	}
+}
+#endif /* Pubkey auth */
diff --git a/cli-channel.c b/cli-channel.c
new file mode 100644
index 0000000000000000000000000000000000000000..1bd49abcf79bd9a67db158d7ebbdb42741e3f436
--- /dev/null
+++ b/cli-channel.c
@@ -0,0 +1,62 @@
+/*
+ * 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() {
+
+	struct Channel *channel;
+	unsigned int datatype;
+
+	TRACE(("enter recv_msg_channel_extended_data"))
+
+	channel = getchannel();
+	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"))
+}
diff --git a/cli-chansession.c b/cli-chansession.c
new file mode 100644
index 0000000000000000000000000000000000000000..6d358b7f174b0bddcf0ec6fa27dc3a30a71a0580
--- /dev/null
+++ b/cli-chansession.c
@@ -0,0 +1,380 @@
+/*
+ * 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"
+#include "session.h"
+#include "dbutil.h"
+#include "channel.h"
+#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);
+
+static void send_chansess_pty_req(struct Channel *channel);
+static void send_chansess_shell_req(struct Channel *channel);
+
+static void cli_tty_setup();
+
+const struct ChanType clichansess = {
+	0, /* sepfds */
+	"session", /* name */
+	cli_initchansess, /* inithandler */
+	NULL, /* checkclosehandler */
+	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_getbool(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 *UNUSED(channel)) {
+
+	/* This channel hasn't gone yet, so we have > 1 */
+	if (ses.chancount > 1) {
+		dropbear_log(LOG_INFO, "Waiting for other channels to close...");
+	}
+
+	cli_tty_cleanup(); /* Restore tty modes etc */
+
+}
+
+static void start_channel_request(struct Channel *channel, 
+		unsigned char *type) {
+
+	CHECKCLEARTOWRITE();
+	buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_REQUEST);
+	buf_putint(ses.writepayload, channel->remotechan);
+
+	buf_putstring(ses.writepayload, type, strlen(type));
+
+}
+
+/* Taken from OpenSSH's sshtty.c:
+ * RCSID("OpenBSD: sshtty.c,v 1.5 2003/09/19 17:43:35 markus Exp "); */
+static void cli_tty_setup() {
+
+	struct termios tio;
+
+	TRACE(("enter cli_pty_setup"))
+
+	if (cli_ses.tty_raw_mode == 1) {
+		TRACE(("leave cli_tty_setup: already in raw mode!"))
+		return;
+	}
+
+	if (tcgetattr(STDIN_FILENO, &tio) == -1) {
+		dropbear_exit("Failed to set raw TTY mode");
+	}
+
+	/* make a copy */
+	cli_ses.saved_tio = tio;
+
+	tio.c_iflag |= IGNPAR;
+	tio.c_iflag &= ~(ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXANY | IXOFF);
+#ifdef IUCLC
+	tio.c_iflag &= ~IUCLC;
+#endif
+	tio.c_lflag &= ~(ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHONL);
+#ifdef IEXTEN
+	tio.c_lflag &= ~IEXTEN;
+#endif
+	tio.c_oflag &= ~OPOST;
+	tio.c_cc[VMIN] = 1;
+	tio.c_cc[VTIME] = 0;
+	if (tcsetattr(STDIN_FILENO, TCSADRAIN, &tio) == -1) {
+		dropbear_exit("Failed to set raw TTY mode");
+	}
+
+	cli_ses.tty_raw_mode = 1;
+	TRACE(("leave cli_tty_setup"))
+}
+
+void cli_tty_cleanup() {
+
+	TRACE(("enter cli_tty_cleanup"))
+
+	if (cli_ses.tty_raw_mode == 0) {
+		TRACE(("leave cli_tty_cleanup: not in raw mode"))
+		return;
+	}
+
+	if (tcsetattr(STDIN_FILENO, TCSADRAIN, &cli_ses.saved_tio) == -1) {
+		dropbear_log(LOG_WARNING, "Failed restoring TTY");
+	} else {
+		cli_ses.tty_raw_mode = 0; 
+	}
+
+	TRACE(("leave cli_tty_cleanup"))
+}
+
+static void put_termcodes() {
+
+	TRACE(("enter put_termcodes"))
+
+	struct termios tio;
+	unsigned int sshcode;
+	const struct TermCode *termcode;
+	unsigned int value;
+	unsigned int mapcode;
+
+	unsigned int bufpos1, bufpos2;
+
+	if (tcgetattr(STDIN_FILENO, &tio) == -1) {
+		dropbear_log(LOG_WARNING, "Failed reading termmodes");
+		buf_putint(ses.writepayload, 1); /* Just the terminator */
+		buf_putbyte(ses.writepayload, 0); /* TTY_OP_END */
+		return;
+	}
+
+	bufpos1 = ses.writepayload->pos;
+	buf_putint(ses.writepayload, 0); /* A placeholder for the final length */
+
+	/* As with Dropbear server, we ignore baud rates for now */
+	for (sshcode = 1; sshcode < MAX_TERMCODE; sshcode++) {
+
+		termcode = &termcodes[sshcode];
+		mapcode = termcode->mapcode;
+
+		switch (termcode->type) {
+
+			case TERMCODE_NONE:
+				continue;
+
+			case TERMCODE_CONTROLCHAR:
+				value = tio.c_cc[mapcode];
+				break;
+
+			case TERMCODE_INPUT:
+				value = tio.c_iflag & mapcode;
+				break;
+
+			case TERMCODE_OUTPUT:
+				value = tio.c_oflag & mapcode;
+				break;
+
+			case TERMCODE_LOCAL:
+				value = tio.c_lflag & mapcode;
+				break;
+
+			case TERMCODE_CONTROL:
+				value = tio.c_cflag & mapcode;
+				break;
+
+			default:
+				continue;
+
+		}
+
+		/* If we reach here, we have something to say */
+		buf_putbyte(ses.writepayload, sshcode);
+		buf_putint(ses.writepayload, value);
+	}
+
+	buf_putbyte(ses.writepayload, 0); /* THE END, aka TTY_OP_END */
+
+	/* Put the string length at the start of the buffer */
+	bufpos2 = ses.writepayload->pos;
+
+	buf_setpos(ses.writepayload, bufpos1); /* Jump back */
+	buf_putint(ses.writepayload, bufpos2 - bufpos1 - 4); /* len(termcodes) */
+	buf_setpos(ses.writepayload, bufpos2); /* Back where we were */
+
+	TRACE(("leave put_termcodes"))
+}
+
+static void put_winsize() {
+
+	struct winsize ws;
+
+	if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) < 0) {
+		/* Some sane defaults */
+		ws.ws_row = 25;
+		ws.ws_col = 80;
+		ws.ws_xpixel = 0;
+		ws.ws_ypixel = 0;
+	}
+
+	buf_putint(ses.writepayload, ws.ws_col); /* Cols */
+	buf_putint(ses.writepayload, ws.ws_row); /* Rows */
+	buf_putint(ses.writepayload, ws.ws_xpixel); /* Width */
+	buf_putint(ses.writepayload, ws.ws_ypixel); /* Height */
+
+}
+
+static void sigwinch_handler(int UNUSED(unused)) {
+
+	cli_ses.winchange = 1;
+
+}
+
+void cli_chansess_winchange() {
+
+	unsigned int i;
+	struct Channel *channel = NULL;
+
+	for (i = 0; i < ses.chansize; i++) {
+		channel = ses.channels[i];
+		if (channel != NULL && channel->type == &clichansess) {
+			CHECKCLEARTOWRITE();
+			buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_REQUEST);
+			buf_putint(ses.writepayload, channel->remotechan);
+			buf_putstring(ses.writepayload, "window-change", 13);
+			buf_putbyte(ses.writepayload, 0); /* FALSE says the spec */
+			put_winsize();
+			encrypt_packet();
+		}
+	}
+	cli_ses.winchange = 0;
+}
+
+static void send_chansess_pty_req(struct Channel *channel) {
+
+	unsigned char* term = NULL;
+
+	TRACE(("enter send_chansess_pty_req"))
+
+	start_channel_request(channel, "pty-req");
+
+	/* Don't want replies */
+	buf_putbyte(ses.writepayload, 0);
+
+	/* Get the terminal */
+	term = getenv("TERM");
+	if (term == NULL) {
+		term = "vt100"; /* Seems a safe default */
+	}
+	buf_putstring(ses.writepayload, term, strlen(term));
+
+	/* Window size */
+	put_winsize();
+
+	/* Terminal mode encoding */
+	put_termcodes();
+
+	encrypt_packet();
+
+	/* Set up a window-change handler */
+	if (signal(SIGWINCH, sigwinch_handler) == SIG_ERR) {
+		dropbear_exit("signal error");
+	}
+	TRACE(("leave send_chansess_pty_req"))
+}
+
+static void send_chansess_shell_req(struct Channel *channel) {
+
+	unsigned char* reqtype = NULL;
+
+	TRACE(("enter send_chansess_shell_req"))
+
+	if (cli_opts.cmd) {
+		reqtype = "exec";
+	} else {
+		reqtype = "shell";
+	}
+
+	start_channel_request(channel, reqtype);
+
+	/* XXX TODO */
+	buf_putbyte(ses.writepayload, 0); /* Don't want replies */
+	if (cli_opts.cmd) {
+		buf_putstring(ses.writepayload, cli_opts.cmd, strlen(cli_opts.cmd));
+	}
+
+	encrypt_packet();
+	TRACE(("leave send_chansess_shell_req"))
+}
+
+static int cli_initchansess(struct Channel *channel) {
+
+
+	channel->writefd = STDOUT_FILENO;
+	setnonblocking(STDOUT_FILENO);
+
+	channel->readfd = 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);
+	}
+
+	send_chansess_shell_req(channel);
+
+	if (cli_opts.wantpty) {
+		cli_tty_setup();
+	}
+
+	return 0; /* Success */
+
+}
+
+void cli_send_chansess_request() {
+
+	TRACE(("enter cli_send_chansess_request"))
+	if (send_msg_channel_open_init(STDIN_FILENO, &clichansess) 
+			== DROPBEAR_FAILURE) {
+		dropbear_exit("Couldn't open initial channel");
+	}
+
+	/* No special channel request data */
+	encrypt_packet();
+	TRACE(("leave cli_send_chansess_request"))
+
+}
diff --git a/cli-kex.c b/cli-kex.c
new file mode 100644
index 0000000000000000000000000000000000000000..40d4e957078016c6e3ca4b48d58b9480dc729ce4
--- /dev/null
+++ b/cli-kex.c
@@ -0,0 +1,288 @@
+/*
+ * Dropbear - a SSH2 server
+ * 
+ * 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
+ * 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 "algo.h"
+#include "buffer.h"
+#include "session.h"
+#include "kex.h"
+#include "ssh.h"
+#include "packet.h"
+#include "bignum.h"
+#include "random.h"
+#include "runopts.h"
+#include "signkey.h"
+
+
+static void checkhostkey(unsigned char* keyblob, unsigned int keybloblen);
+#define MAX_KNOWNHOSTS_LINE 4500
+
+void send_msg_kexdh_init() {
+
+	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);
+
+	gen_kexdh_vals(cli_ses.dh_e, cli_ses.dh_x);
+
+	CHECKCLEARTOWRITE();
+	buf_putbyte(ses.writepayload, SSH_MSG_KEXDH_INIT);
+	buf_putmpint(ses.writepayload, cli_ses.dh_e);
+	encrypt_packet();
+	ses.requirenext = SSH_MSG_KEXDH_REPLY;
+}
+
+/* Handle a diffie-hellman key exchange reply. */
+void recv_msg_kexdh_reply() {
+
+	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))
+
+	hostkey = new_sign_key();
+	keybloblen = buf_getint(ses.payload);
+
+	keyblob = buf_getptr(ses.payload, keybloblen);
+	if (!ses.kexstate.donefirstkex) {
+		/* Only makes sense the first time */
+		checkhostkey(keyblob, keybloblen);
+	}
+
+	if (buf_get_pub_key(ses.payload, hostkey, &type) != DROPBEAR_SUCCESS) {
+		TRACE(("failed getting pubkey"))
+		dropbear_exit("Bad KEX packet");
+	}
+
+	if (buf_getmpint(ses.payload, &dh_f) != DROPBEAR_SUCCESS) {
+		TRACE(("failed getting mpint"))
+		dropbear_exit("Bad KEX packet");
+	}
+
+	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) {
+		dropbear_exit("Bad hostkey signature");
+	}
+
+	sign_key_free(hostkey);
+	hostkey = NULL;
+
+	send_msg_newkeys();
+	ses.requirenext = SSH_MSG_NEWKEYS;
+	TRACE(("leave recv_msg_kexdh_init"))
+}
+
+static void ask_to_confirm(unsigned char* keyblob, unsigned int keybloblen) {
+
+	char* fp = NULL;
+	FILE *tty = NULL;
+	char response = 'z';
+
+	fp = sign_key_fingerprint(keyblob, keybloblen);
+	fprintf(stderr, "\nHost '%s' is not in the trusted hosts file.\n(fingerprint %s)\nDo you want to continue connecting? (y/n)\n", 
+			cli_opts.remotehost, 
+			fp);
+
+	tty = fopen(_PATH_TTY, "r");
+	if (tty) {
+		response = getc(tty);
+		fclose(tty);
+	} else {
+		response = getc(stdin);
+	}
+
+	if (response == 'y') {
+		m_free(fp);
+		return;
+	}
+
+	dropbear_exit("Didn't validate host key");
+}
+
+static void checkhostkey(unsigned char* keyblob, unsigned int keybloblen) {
+
+	char * filename = NULL;
+	FILE *hostsfile = NULL;
+	int readonly = 0;
+	struct passwd *pw = NULL;
+	unsigned int hostlen, algolen;
+	unsigned long len;
+	const char *algoname = NULL;
+	buffer * line = NULL;
+	int ret;
+	
+	pw = getpwuid(getuid());
+
+	if (pw == NULL) {
+		dropbear_exit("Failed to get homedir");
+	}
+
+	len = strlen(pw->pw_dir);
+	filename = m_malloc(len + 18); /* "/.ssh/known_hosts" and null-terminator*/
+
+	snprintf(filename, len+18, "%s/.ssh", pw->pw_dir);
+	/* 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, "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) {
+			TRACE(("failed reading line: prob EOF"))
+			break;
+		}
+
+		/* The line is too short to be sensible */
+		/* "30" is 'enough to hold ssh-dss plus the spaces, ie so we don't
+		 * buf_getfoo() past the end and die horribly - the base64 parsing
+		 * code is what tiptoes up to the end nicely */
+		if (line->len < (hostlen+30) ) {
+			TRACE(("line is too short to be sensible"))
+			continue;
+		}
+
+		/* Compare hostnames */
+		if (strncmp(cli_opts.remotehost, buf_getptr(line, hostlen),
+					hostlen) != 0) {
+			TRACE(("hosts don't match"))
+			continue;
+		}
+
+		buf_incrpos(line, hostlen);
+		if (buf_getbyte(line) != ' ') {
+			/* there wasn't a space after the hostname, something dodgy */
+			TRACE(("missing space afte matching hostname"))
+			continue;
+		}
+
+		if ( strncmp(buf_getptr(line, algolen), algoname, algolen) != 0) {
+			TRACE(("algo doesn't match"))
+			continue;
+		}
+
+		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, algolen, line);
+
+		if (ret == DROPBEAR_SUCCESS) {
+			/* Good matching key */
+			TRACE(("good matching key"))
+			goto out;
+		}
+
+		/* The keys didn't match. eep. */
+	} while (1); /* keep going 'til something happens */
+
+	/* 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);
+	if (line != NULL) {
+		buf_free(line);
+	}
+}
diff --git a/cli-main.c b/cli-main.c
new file mode 100644
index 0000000000000000000000000000000000000000..3f767c916c0d60bd9144b2c3f4f483d26e831012
--- /dev/null
+++ b/cli-main.c
@@ -0,0 +1,112 @@
+/*
+ * 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"
+#include "session.h"
+
+static void cli_dropbear_exit(int exitcode, const char* format, va_list param);
+static void cli_dropbear_log(int priority, const char* format, va_list param);
+
+#if defined(DBMULTI_dbclient) || !defined(DROPBEAR_MULTI)
+#if defined(DBMULTI_dbclient) && defined(DROPBEAR_MULTI)
+int cli_main(int argc, char ** argv) {
+#else
+int main(int argc, char ** argv) {
+#endif
+
+	int sock;
+	char* error = NULL;
+	char* hostandport;
+	int len;
+
+	_dropbear_exit = cli_dropbear_exit;
+	_dropbear_log = cli_dropbear_log;
+
+	cli_getopts(argc, argv);
+
+	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);
+
+	if (sock < 0) {
+		dropbear_exit("%s", error);
+	}
+
+	/* Set up the host:port log */
+	len = strlen(cli_opts.remotehost);
+	len += 10; /* 16 bit port and leeway*/
+	hostandport = (char*)m_malloc(len);
+	snprintf(hostandport, len, "%s:%s", 
+			cli_opts.remotehost, cli_opts.remoteport);
+
+	cli_session(sock, hostandport);
+
+	/* not reached */
+	return -1;
+}
+#endif /* DBMULTI stuff */
+
+static void cli_dropbear_exit(int exitcode, const char* format, va_list param) {
+
+	char fmtbuf[300];
+
+	if (!sessinitdone) {
+		snprintf(fmtbuf, sizeof(fmtbuf), "exited: %s",
+				format);
+	} else {
+		snprintf(fmtbuf, sizeof(fmtbuf), 
+				"connection to %s@%s:%s exited: %s", 
+				cli_opts.username, cli_opts.remotehost, 
+				cli_opts.remoteport, format);
+	}
+
+	/* Do the cleanup first, since then the terminal will be reset */
+	cli_session_cleanup();
+	common_session_cleanup();
+
+	_dropbear_log(LOG_INFO, fmtbuf, param);
+
+	exit(exitcode);
+}
+
+static void cli_dropbear_log(int UNUSED(priority), 
+		const char* format, va_list param) {
+
+	char printbuf[1024];
+
+	vsnprintf(printbuf, sizeof(printbuf), format, param);
+
+	fprintf(stderr, "%s: %s\n", cli_opts.progname, printbuf);
+
+}
diff --git a/cli-runopts.c b/cli-runopts.c
new file mode 100644
index 0000000000000000000000000000000000000000..54d4875c1a51bf54eac25e26ff2ad520ac18170f
--- /dev/null
+++ b/cli-runopts.c
@@ -0,0 +1,414 @@
+/*
+ * 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. */
+
+#include "includes.h"
+#include "runopts.h"
+#include "signkey.h"
+#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 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"
+					"Options are:\n"
+					"-p <remoteport>\n"
+					"-l <username>\n"
+					"-t    Allocate a pty\n"
+					"-T    Don't allocate a pty\n"
+#ifdef ENABLE_CLI_PUBKEY_AUTH
+					"-i <identityfile>   (multiple allowed)\n"
+#endif
+#ifdef ENABLE_CLI_LOCALTCPFWD
+					"-L <listenport:remotehost:remoteport> Local port forwarding\n"
+					"-g    Allow remote hosts to connect to forwarded ports\n"
+#endif
+#ifdef ENABLE_CLI_REMOTETCPFWD
+					"-R <listenport:remotehost:remoteport> Remote port forwarding\n"
+#endif
+#ifdef DEBUG_TRACE
+					"-v    verbose\n"
+#endif
+					,DROPBEAR_VERSION, cli_opts.progname);
+}
+
+void cli_getopts(int argc, char ** argv) {
+
+	unsigned int i, j;
+	char ** next = 0;
+	unsigned int cmdlen;
+#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];
+	cli_opts.remotehost = NULL;
+	cli_opts.remoteport = NULL;
+	cli_opts.username = NULL;
+	cli_opts.cmd = NULL;
+	cli_opts.wantpty = 9; /* 9 means "it hasn't been touched", gets set later */
+#ifdef ENABLE_CLI_PUBKEY_AUTH
+	cli_opts.privkeys = NULL;
+#endif
+#ifdef ENABLE_CLI_LOCALTCPFWD
+	cli_opts.localfwds = NULL;
+	opts.listen_fwd_all = 0;
+#endif
+#ifdef ENABLE_CLI_REMOTETCPFWD
+	cli_opts.remotefwds = NULL;
+#endif
+	/* not yet
+	opts.ipv4 = 1;
+	opts.ipv6 = 1;
+	*/
+
+	/* Iterate all the arguments */
+	for (i = 1; i < (unsigned int)argc; i++) {
+#ifdef ENABLE_CLI_PUBKEY_AUTH
+		if (nextiskey) {
+			/* Load a hostkey since the previous argument was "-i" */
+			loadidentityfile(argv[i]);
+			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 */
+			*next = argv[i];
+			if (*next == NULL) {
+				dropbear_exit("Invalid null argument");
+			}
+			next = NULL;
+			continue;
+		}
+
+		if (argv[i][0] == '-') {
+			/* A flag *waves* */
+
+			switch (argv[i][1]) {
+				case 'p': /* remoteport */
+					next = &cli_opts.remoteport;
+					break;
+#ifdef ENABLE_CLI_PUBKEY_AUTH
+				case 'i': /* an identityfile */
+					/* Keep scp happy when it changes "-i file" to "-ifile" */
+					if (strlen(argv[i]) > 2) {
+						loadidentityfile(&argv[i][2]);
+					} else  {
+						nextiskey = 1;
+					}
+					break;
+#endif
+				case 't': /* we want a pty */
+					cli_opts.wantpty = 1;
+					break;
+				case 'T': /* don't want a pty */
+					cli_opts.wantpty = 0;
+					break;
+#ifdef ENABLE_CLI_LOCALTCPFWD
+				case 'L':
+					nextislocal = 1;
+					break;
+				case 'g':
+					opts.listen_fwd_all = 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, 
+						"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: '%s'", argv[i]))
+
+			/* Either the hostname or commands */
+
+			if (cli_opts.remotehost == NULL) {
+
+				parsehostname(argv[i]);
+
+			} else {
+
+				/* this is part of the commands to send - after this we
+				 * don't parse any more options, and flags are sent as the
+				 * command */
+				cmdlen = 0;
+				for (j = i; j < (unsigned int)argc; j++) {
+					cmdlen += strlen(argv[j]) + 1; /* +1 for spaces */
+				}
+				/* Allocate the space */
+				cli_opts.cmd = (char*)m_malloc(cmdlen);
+				cli_opts.cmd[0] = '\0';
+
+				/* Append all the bits */
+				for (j = i; j < (unsigned int)argc; j++) {
+					strlcat(cli_opts.cmd, argv[j], cmdlen);
+					strlcat(cli_opts.cmd, " ", cmdlen);
+				}
+				/* It'll be null-terminated here */
+
+				/* We've eaten all the options and flags */
+				break;
+			}
+		}
+	}
+
+	if (cli_opts.remotehost == NULL) {
+		printhelp();
+		exit(EXIT_FAILURE);
+	}
+
+	if (cli_opts.remoteport == NULL) {
+		cli_opts.remoteport = "22";
+	}
+
+	/* If not explicitly specified with -t or -T, we don't want a pty if
+	 * there's a command, but we do otherwise */
+	if (cli_opts.wantpty == 9) {
+		if (cli_opts.cmd == NULL) {
+			cli_opts.wantpty = 1;
+		} else {
+			cli_opts.wantpty = 0;
+		}
+	}
+}
+
+#ifdef ENABLE_CLI_PUBKEY_AUTH
+static void loadidentityfile(const char* filename) {
+
+	struct SignKeyList * nextkey;
+	sign_key *key;
+	int keytype;
+
+	key = new_sign_key();
+	keytype = DROPBEAR_SIGNKEY_ANY;
+	if ( readhostkey(filename, key, &keytype) != DROPBEAR_SUCCESS ) {
+
+		fprintf(stderr, "Failed loading keyfile '%s'\n", filename);
+		sign_key_free(key);
+
+	} else {
+
+		nextkey = (struct SignKeyList*)m_malloc(sizeof(struct SignKeyList));
+		nextkey->key = key;
+		nextkey->next = cli_opts.privkeys;
+		nextkey->type = keytype;
+		cli_opts.privkeys = nextkey;
+	}
+}
+#endif
+
+
+/* Parses a [user@]hostname argument. userhostarg is the argv[i] corresponding
+ * - note that it will be modified */
+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) {
+		/* no username portion, the cli-auth.c code can figure the
+		 * local user's name */
+		cli_opts.remotehost = userhostarg;
+	} else {
+		cli_opts.remotehost[0] = '\0'; /* Split the user/host */
+		cli_opts.remotehost++;
+		cli_opts.username = userhostarg;
+	}
+
+	if (cli_opts.username == NULL) {
+		uid = getuid();
+		
+		pw = getpwuid(uid);
+		if (pw == NULL || pw->pw_name == NULL) {
+			dropbear_exit("Unknown own user");
+		}
+
+		cli_opts.username = m_strdup(pw->pw_name);
+	}
+
+	if (cli_opts.remotehost[0] == '\0') {
+		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-service.c b/cli-service.c
new file mode 100644
index 0000000000000000000000000000000000000000..87b6ed22433176bc48e38dd681f858b83cec4374
--- /dev/null
+++ b/cli-service.c
@@ -0,0 +1,87 @@
+/*
+ * 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"
+#include "packet.h"
+#include "buffer.h"
+#include "session.h"
+#include "ssh.h"
+
+void send_msg_service_request(char* servicename) {
+
+	TRACE(("enter send_msg_service_request: servicename='%s'", servicename))
+
+	CHECKCLEARTOWRITE();
+
+	buf_putbyte(ses.writepayload, SSH_MSG_SERVICE_REQUEST);
+	buf_putstring(ses.writepayload, servicename, strlen(servicename));
+
+	encrypt_packet();
+	TRACE(("leave send_msg_service_request"))
+}
+
+/* This just sets up the state variables right for the main client session loop
+ * to deal with */
+void recv_msg_service_accept() {
+
+	unsigned char* servicename;
+	unsigned int len;
+
+	TRACE(("enter recv_msg_service_accept"))
+
+	servicename = buf_getstring(ses.payload, &len);
+
+	/* ssh-userauth */
+	if (cli_ses.state == SERVICE_AUTH_REQ_SENT
+			&& len == SSH_SERVICE_USERAUTH_LEN
+			&& strncmp(SSH_SERVICE_USERAUTH, servicename, len) == 0) {
+
+		cli_ses.state = SERVICE_AUTH_ACCEPT_RCVD;
+		m_free(servicename);
+		TRACE(("leave recv_msg_service_accept: done ssh-userauth"))
+		return;
+	}
+
+	/* ssh-connection */
+	if (cli_ses.state == SERVICE_CONN_REQ_SENT
+			&& len == SSH_SERVICE_CONNECTION_LEN 
+			&& strncmp(SSH_SERVICE_CONNECTION, servicename, len) == 0) {
+
+		if (ses.authstate.authdone != 1) {
+			dropbear_exit("request for connection before auth");
+		}
+
+		cli_ses.state = SERVICE_CONN_ACCEPT_RCVD;
+		m_free(servicename);
+		TRACE(("leave recv_msg_service_accept: done ssh-connection"))
+		return;
+	}
+
+	dropbear_exit("unrecognised service accept");
+	/* m_free(servicename); not reached */
+
+}
diff --git a/cli-session.c b/cli-session.c
new file mode 100644
index 0000000000000000000000000000000000000000..35510fa9797e586da3aa93a9e758f203b5fedb7b
--- /dev/null
+++ b/cli-session.c
@@ -0,0 +1,303 @@
+/*
+ * 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.h"
+#include "channel.h"
+#include "random.h"
+#include "service.h"
+#include "runopts.h"
+#include "chansession.h"
+
+static void cli_remoteclosed();
+static void cli_sessionloop();
+static void cli_session_init();
+static void cli_finished();
+
+struct clientsession cli_ses; /* GLOBAL */
+
+/* Sorted in decreasing frequency will be more efficient - data and window
+ * should be first */
+static const packettype cli_packettypes[] = {
+	/* 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 */
+	{SSH_MSG_KEXINIT, recv_msg_kexinit},
+	{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_CHANNEL_REQUEST, recv_msg_channel_request},
+	{SSH_MSG_CHANNEL_OPEN, recv_msg_channel_open},
+	{SSH_MSG_CHANNEL_EOF, recv_msg_channel_eof},
+	{SSH_MSG_CHANNEL_CLOSE, recv_msg_channel_close},
+	{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 */
+	{SSH_MSG_USERAUTH_SPECIFIC_60, recv_msg_userauth_specific_60}, /* client */
+	{0, 0} /* End */
+};
+
+static const struct ChanType *cli_chantypes[] = {
+#ifdef ENABLE_CLI_REMOTETCPFWD
+	&cli_chan_tcpremote,
+#endif
+	NULL /* Null termination */
+};
+
+void cli_session(int sock, char* remotehost) {
+
+	seedrandom();
+
+	crypto_init();
+
+	common_session_init(sock, remotehost);
+
+	chaninitialise(cli_chantypes);
+
+	/* Set up cli_ses vars */
+	cli_session_init();
+
+	/* Ready to go */
+	sessinitdone = 1;
+
+	/* Exchange identification */
+	session_identification();
+
+	send_msg_kexinit();
+
+	session_loop(cli_sessionloop);
+
+	/* Not reached */
+
+}
+
+static void cli_session_init() {
+
+	cli_ses.state = STATE_NOTHING;
+	cli_ses.kex_state = KEX_NOTHING;
+
+	cli_ses.tty_raw_mode = 0;
+	cli_ses.winchange = 0;
+
+	/* We store std{in,out,err}'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.stdoutcopy = dup(STDOUT_FILENO);
+	cli_ses.stdoutflags = fcntl(STDOUT_FILENO, F_GETFL, 0);
+	cli_ses.stderrcopy = dup(STDERR_FILENO);
+	cli_ses.stderrflags = fcntl(STDERR_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.lastprivkey = NULL;
+	cli_ses.lastauthtype = 0;
+
+	/* For printing "remote host closed" for the user */
+	ses.remoteclosed = cli_remoteclosed;
+	ses.buf_match_algo = cli_buf_match_algo;
+
+	/* packet handlers */
+	ses.packettypes = cli_packettypes;
+
+	ses.isserver = 0;
+}
+
+/* This function drives the progress of the session - it initiates KEX,
+ * service, userauth and channel requests */
+static void cli_sessionloop() {
+
+	TRACE(("enter cli_sessionloop"))
+
+	if (ses.lastpacket == SSH_MSG_KEXINIT && cli_ses.kex_state == KEX_NOTHING) {
+		cli_ses.kex_state = KEXINIT_RCVD;
+	}
+
+	if (cli_ses.kex_state == KEXINIT_RCVD) {
+
+		/* We initiate the KEXDH. If DH wasn't the correct type, the KEXINIT
+		 * negotiation would have failed. */
+		send_msg_kexdh_init();
+		cli_ses.kex_state = KEXDH_INIT_SENT;
+		TRACE(("leave cli_sessionloop: done with KEXINIT_RCVD"))
+		return;
+	}
+
+	/* A KEX has finished, so we should go back to our KEX_NOTHING state */
+	if (cli_ses.kex_state != KEX_NOTHING && ses.kexstate.recvkexinit == 0
+			&& ses.kexstate.sentkexinit == 0) {
+		cli_ses.kex_state = KEX_NOTHING;
+	}
+
+	/* We shouldn't do anything else if a KEX is in progress */
+	if (cli_ses.kex_state != KEX_NOTHING) {
+		TRACE(("leave cli_sessionloop: kex_state != KEX_NOTHING"))
+		return;
+	}
+
+	/* We should exit if we haven't donefirstkex: we shouldn't reach here
+	 * in normal operation */
+	if (ses.kexstate.donefirstkex == 0) {
+		TRACE(("XXX XXX might be bad! leave cli_sessionloop: haven't donefirstkex"))
+		return;
+	}
+
+	switch (cli_ses.state) {
+
+		case STATE_NOTHING:
+			/* We've got the transport layer sorted, we now need to request
+			 * userauth */
+			send_msg_service_request(SSH_SERVICE_USERAUTH);
+			cli_ses.state = SERVICE_AUTH_REQ_SENT;
+			TRACE(("leave cli_sessionloop: sent userauth service req"))
+			return;
+
+		/* userauth code */
+		case SERVICE_AUTH_ACCEPT_RCVD:
+			cli_auth_getmethods();
+			cli_ses.state = USERAUTH_REQ_SENT;
+			TRACE(("leave cli_sessionloop: sent userauth methods req"))
+			return;
+			
+		case USERAUTH_FAIL_RCVD:
+			cli_auth_try();
+			cli_ses.state = USERAUTH_REQ_SENT;
+			TRACE(("leave cli_sessionloop: cli_auth_try"))
+			return;
+
+			/*
+		case USERAUTH_SUCCESS_RCVD:
+			send_msg_service_request(SSH_SERVICE_CONNECTION);
+			cli_ses.state = SERVICE_CONN_REQ_SENT;
+			TRACE(("leave cli_sessionloop: sent ssh-connection service req"))
+			return;
+
+		case SERVICE_CONN_ACCEPT_RCVD:
+			cli_send_chansess_request();
+			TRACE(("leave cli_sessionloop: cli_send_chansess_request"))
+			cli_ses.state = SESSION_RUNNING;
+			return;
+			*/
+
+		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;
+			return;
+
+		case SESSION_RUNNING:
+			if (ses.chancount < 1) {
+				cli_finished();
+			}
+
+			if (cli_ses.winchange) {
+				cli_chansess_winchange();
+			}
+			return;
+
+		/* XXX more here needed */
+
+
+	default:
+		break;
+	}
+
+	TRACE(("leave cli_sessionloop: fell out"))
+
+}
+
+void cli_session_cleanup() {
+
+	if (!sessinitdone) {
+		return;
+	}
+
+	/* Set std{in,out,err} back to non-blocking - busybox ash dies nastily if
+	 * we don't revert the flags */
+	fcntl(cli_ses.stdincopy, F_SETFL, cli_ses.stdinflags);
+	fcntl(cli_ses.stdoutcopy, F_SETFL, cli_ses.stdoutflags);
+	fcntl(cli_ses.stderrcopy, F_SETFL, cli_ses.stderrflags);
+
+	cli_tty_cleanup();
+
+}
+
+static void cli_finished() {
+
+	cli_session_cleanup();
+	common_session_cleanup();
+	fprintf(stderr, "Connection to %s@%s:%s closed.\n", cli_opts.username,
+			cli_opts.remotehost, cli_opts.remoteport);
+	exit(cli_ses.retval);
+}
+
+
+/* called when the remote side closes the connection */
+static void cli_remoteclosed() {
+
+	/* XXX TODO perhaps print a friendlier message if we get this but have
+	 * already sent/received disconnect message(s) ??? */
+	close(ses.sock);
+	ses.sock = -1;
+	dropbear_exit("remote closed the connection");
+}
+
+/* Operates in-place turning dirty (untrusted potentially containing control
+ * characters) text into clean text. 
+ * Note: this is safe only with ascii - other charsets could have problems. */
+void cleantext(unsigned char* dirtytext) {
+
+	unsigned int i, j;
+	unsigned char c;
+
+	j = 0;
+	for (i = 0; dirtytext[i] != '\0'; i++) {
+
+		c = dirtytext[i];
+		/* We can ignore '\r's */
+		if ( (c >= ' ' && c <= '~') || c == '\n' || c == '\t') {
+			dirtytext[j] = c;
+			j++;
+		}
+	}
+	/* Null terminate */
+	dirtytext[j] = '\0';
+}
diff --git a/cli-tcpfwd.c b/cli-tcpfwd.c
new file mode 100644
index 0000000000000000000000000000000000000000..c3bfd4dc4a88540f4b3d2739578df97c28838bdd
--- /dev/null
+++ b/cli-tcpfwd.c
@@ -0,0 +1,216 @@
+/*
+ * 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"
+
+#ifdef ENABLE_CLI_REMOTETCPFWD
+static int newtcpforwarded(struct Channel * channel);
+
+const struct ChanType cli_chan_tcpremote = {
+	1, /* sepfds */
+	"forwarded-tcpip",
+	newtcpforwarded,
+	NULL,
+	NULL,
+	NULL
+};
+#endif
+
+#ifdef ENABLE_CLI_LOCALTCPFWD
+static int cli_localtcp(unsigned int listenport, const char* remoteaddr,
+		unsigned int remoteport);
+static const struct ChanType cli_chan_tcplocal = {
+	1, /* sepfds */
+	"direct-tcpip",
+	NULL,
+	NULL,
+	NULL,
+	NULL
+};
+#endif
+
+#ifdef ENABLE_CLI_LOCALTCPFWD
+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;
+
+	if (opts.listen_fwd_all) {
+		tcpinfo->listenaddr = m_strdup("");
+	} else {
+		tcpinfo->listenaddr = m_strdup("localhost");
+	}
+	tcpinfo->listenport = listenport;
+
+	tcpinfo->chantype = &cli_chan_tcplocal;
+	tcpinfo->tcp_type = direct;
+
+	ret = listen_tcpfwd(tcpinfo);
+
+	if (ret == DROPBEAR_FAILURE) {
+		m_free(tcpinfo);
+	}
+	TRACE(("leave cli_localtcp: %d", ret))
+	return ret;
+}
+#endif /* ENABLE_CLI_LOCALTCPFWD */
+
+#ifdef  ENABLE_CLI_REMOTETCPFWD
+static void send_msg_global_request_remotetcp(int port) {
+
+	char* listenspec = NULL;
+	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);
+	if (opts.listen_fwd_all) {
+		listenspec = "";
+	} else {
+		listenspec = "localhost";
+	}
+	/* TODO: IPv6? */;
+	buf_putstring(ses.writepayload, listenspec, strlen(listenspec));
+	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);
+
+	/* We don't set readfd, that will get set after the connection's
+	 * progress succeeds */
+	channel->writefd = sock;
+	channel->initconn = 1;
+	
+	err = SSH_OPEN_IN_PROGRESS;
+
+out:
+	TRACE(("leave newtcpdirect: err %d", err))
+	return err;
+}
+#endif /* ENABLE_CLI_REMOTETCPFWD */
diff --git a/common-algo.c b/common-algo.c
new file mode 100644
index 0000000000000000000000000000000000000000..ae2102a109d6bf9864d83632fbe1029cc752410d
--- /dev/null
+++ b/common-algo.c
@@ -0,0 +1,228 @@
+/*
+ * 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 "algo.h"
+#include "dbutil.h"
+
+/* This file (algo.c) organises the ciphers which can be used, and is used to
+ * decide which ciphers/hashes/compression/signing to use during key exchange*/
+
+/* Mappings for ciphers, parameters are
+   {&cipher_desc, keysize, blocksize} */
+
+#ifdef DROPBEAR_AES256_CBC
+static const struct dropbear_cipher dropbear_aes256 = 
+	{&aes_desc, 32, 16};
+#endif
+#ifdef DROPBEAR_AES128_CBC
+static const struct dropbear_cipher dropbear_aes128 = 
+	{&aes_desc, 16, 16};
+#endif
+#ifdef DROPBEAR_BLOWFISH_CBC
+static const struct dropbear_cipher dropbear_blowfish = 
+	{&blowfish_desc, 16, 8};
+#endif
+#ifdef DROPBEAR_TWOFISH256_CBC
+static const struct dropbear_cipher dropbear_twofish256 = 
+	{&twofish_desc, 32, 16};
+#endif
+#ifdef DROPBEAR_TWOFISH128_CBC
+static const struct dropbear_cipher dropbear_twofish128 = 
+	{&twofish_desc, 16, 16};
+#endif
+#ifdef DROPBEAR_3DES_CBC
+static const struct dropbear_cipher dropbear_3des = 
+	{&des3_desc, 24, 8};
+#endif
+
+/* used to indicate no encryption, as defined in rfc2410 */
+const struct dropbear_cipher dropbear_nocipher =
+	{NULL, 16, 8}; 
+
+/* Mapping of ssh hashes to libtomcrypt hashes, including keysize etc.
+   {&hash_desc, keysize, hashsize} */
+
+#ifdef DROPBEAR_SHA1_HMAC
+static const struct dropbear_hash dropbear_sha1 = 
+	{&sha1_desc, 20, 20};
+#endif
+#ifdef DROPBEAR_SHA1_96_HMAC
+static const struct dropbear_hash dropbear_sha1_96 = 
+	{&sha1_desc, 20, 12};
+#endif
+#ifdef DROPBEAR_MD5_HMAC
+static const struct dropbear_hash dropbear_md5 = 
+	{&md5_desc, 16, 16};
+#endif
+
+const struct dropbear_hash dropbear_nohash =
+	{NULL, 16, 0}; /* used initially */
+	
+
+/* The following map ssh names to internal values */
+
+algo_type sshciphers[] = {
+#ifdef DROPBEAR_AES128_CBC
+	{"aes128-cbc", 0, (void*)&dropbear_aes128, 1},
+#endif
+#ifdef DROPBEAR_3DES_CBC
+	{"3des-cbc", 0, (void*)&dropbear_3des, 1},
+#endif
+#ifdef DROPBEAR_AES256_CBC
+	{"aes256-cbc", 0, (void*)&dropbear_aes256, 1},
+#endif
+#ifdef DROPBEAR_TWOFISH256_CBC
+	{"twofish256-cbc", 0, (void*)&dropbear_twofish256, 1},
+	{"twofish-cbc", 0, (void*)&dropbear_twofish256, 1},
+#endif
+#ifdef DROPBEAR_TWOFISH128_CBC
+	{"twofish128-cbc", 0, (void*)&dropbear_twofish128, 1},
+#endif
+#ifdef DROPBEAR_BLOWFISH_CBC
+	{"blowfish-cbc", 0, (void*)&dropbear_blowfish, 1},
+#endif
+	{NULL, 0, NULL, 0}
+};
+
+algo_type sshhashes[] = {
+#ifdef DROPBEAR_SHA1_96_HMAC
+	{"hmac-sha1-96", 0, (void*)&dropbear_sha1_96, 1},
+#endif
+#ifdef DROPBEAR_SHA1_HMAC
+	{"hmac-sha1", 0, (void*)&dropbear_sha1, 1},
+#endif
+#ifdef DROPBEAR_MD5_HMAC
+	{"hmac-md5", 0, (void*)&dropbear_md5, 1},
+#endif
+	{NULL, 0, NULL, 0}
+};
+
+algo_type sshcompress[] = {
+#ifndef DISABLE_ZLIB
+	{"zlib", DROPBEAR_COMP_ZLIB, NULL, 1},
+#endif
+	{"none", DROPBEAR_COMP_NONE, NULL, 1},
+	{NULL, 0, NULL, 0}
+};
+
+algo_type sshhostkey[] = {
+#ifdef DROPBEAR_RSA
+	{"ssh-rsa", DROPBEAR_SIGNKEY_RSA, NULL, 1},
+#endif
+#ifdef DROPBEAR_DSS
+	{"ssh-dss", DROPBEAR_SIGNKEY_DSS, NULL, 1},
+#endif
+	{NULL, 0, NULL, 0}
+};
+
+algo_type sshkex[] = {
+	{"diffie-hellman-group1-sha1", DROPBEAR_KEX_DH_GROUP1, NULL, 1},
+	{NULL, 0, NULL, 0}
+};
+
+
+/* Register the compiled in ciphers.
+ * This should be run before using any of the ciphers/hashes */
+void crypto_init() {
+
+	const struct ltc_cipher_descriptor *regciphers[] = {
+#ifdef DROPBEAR_AES_CBC
+		&aes_desc,
+#endif
+#ifdef DROPBEAR_BLOWFISH_CBC
+		&blowfish_desc,
+#endif
+#ifdef DROPBEAR_TWOFISH_CBC
+		&twofish_desc,
+#endif
+#ifdef DROPBEAR_3DES_CBC
+		&des3_desc,
+#endif
+		NULL
+	};
+
+	const struct ltc_hash_descriptor *reghashes[] = {
+		/* we need sha1 for hostkey stuff regardless */
+		&sha1_desc,
+#ifdef DROPBEAR_MD5_HMAC
+		&md5_desc,
+#endif
+		NULL
+	};	
+	int i;
+	
+	for (i = 0; regciphers[i] != NULL; i++) {
+		if (register_cipher(regciphers[i]) == -1) {
+			dropbear_exit("error registering crypto");
+		}
+	}
+
+	for (i = 0; reghashes[i] != NULL; i++) {
+		if (register_hash(reghashes[i]) == -1) {
+			dropbear_exit("error registering crypto");
+		}
+	}
+}
+
+/* algolen specifies the length of algo, algos is our local list to match
+ * against.
+ * Returns DROPBEAR_SUCCESS if we have a match for algo, DROPBEAR_FAILURE
+ * otherwise */
+int have_algo(char* algo, size_t algolen, algo_type algos[]) {
+
+	int i;
+
+	for (i = 0; algos[i].name != NULL; i++) {
+		if (strlen(algos[i].name) == algolen
+				&& (strncmp(algos[i].name, algo, algolen) == 0)) {
+			return DROPBEAR_SUCCESS;
+		}
+	}
+
+	return DROPBEAR_FAILURE;
+}
+
+
+
+/* Output a comma separated list of algorithms to a buffer */
+void buf_put_algolist(buffer * buf, algo_type localalgos[]) {
+
+	unsigned int i, len;
+	unsigned int donefirst = 0;
+	buffer *algolist = NULL;
+
+	algolist = buf_new(100);
+	for (i = 0; localalgos[i].name != NULL; i++) {
+		if (localalgos[i].usable) {
+			if (donefirst)
+				buf_putbyte(algolist, ',');
+			donefirst = 1;
+			len = strlen(localalgos[i].name);
+			buf_putbytes(algolist, localalgos[i].name, len);
+		}
+	}
+	buf_putstring(buf, algolist->data, algolist->len);
+	buf_free(algolist);
+}
diff --git a/common-channel.c b/common-channel.c
new file mode 100644
index 0000000000000000000000000000000000000000..68d2b48887af2800ba4c0bed2493d3c2510ae67e
--- /dev/null
+++ b/common-channel.c
@@ -0,0 +1,1038 @@
+/*
+ * 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. */
+
+/* Handle the multiplexed channels, such as sessions, x11, agent connections */
+
+#include "includes.h"
+#include "session.h"
+#include "packet.h"
+#include "ssh.h"
+#include "buffer.h"
+#include "circbuffer.h"
+#include "dbutil.h"
+#include "channel.h"
+#include "ssh.h"
+#include "listener.h"
+
+static void send_msg_channel_open_failure(unsigned int remotechan, int reason,
+		const unsigned char *text, const unsigned char *lang);
+static void send_msg_channel_open_confirmation(struct Channel* channel,
+		unsigned int recvwindow, 
+		unsigned int recvmaxpacket);
+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,
+		unsigned int exttype);
+static void send_msg_channel_eof(struct Channel *channel);
+static void send_msg_channel_close(struct Channel *channel);
+static void removechannel(struct Channel *channel);
+static void deletechannel(struct Channel *channel);
+static void checkinitdone(struct Channel *channel);
+static void checkclose(struct Channel *channel);
+
+static void closewritefd(struct Channel * channel);
+static void closereadfd(struct Channel * channel, int fd);
+static void closechanfd(struct Channel *channel, int fd, int how);
+
+#define FD_UNINIT (-2)
+#define FD_CLOSED (-1)
+
+/* Initialise all the channels */
+void chaninitialise(const struct ChanType *chantypes[]) {
+
+	/* may as well create space for a single channel */
+	ses.channels = (struct Channel**)m_malloc(sizeof(struct Channel*));
+	ses.chansize = 1;
+	ses.channels[0] = NULL;
+	ses.chancount = 0;
+
+	ses.chantypes = chantypes;
+
+#ifdef USING_LISTENERS
+	listeners_initialise();
+#endif
+
+}
+
+/* Clean up channels, freeing allocated memory */
+void chancleanup() {
+
+	unsigned int i;
+
+	TRACE(("enter chancleanup"))
+	for (i = 0; i < ses.chansize; i++) {
+		if (ses.channels[i] != NULL) {
+			TRACE(("channel %d closing", i))
+			removechannel(ses.channels[i]);
+		}
+	}
+	m_free(ses.channels);
+	TRACE(("leave chancleanup"))
+}
+
+/* Create a new channel entry, send a reply confirm or failure */
+/* If remotechan, transwindow and transmaxpacket are not know (for a new
+ * outgoing connection, with them to be filled on confirmation), they should
+ * all be set to 0 */
+struct Channel* newchannel(unsigned int remotechan, 
+		const struct ChanType *type, 
+		unsigned int transwindow, unsigned int transmaxpacket) {
+
+	struct Channel * newchan;
+	unsigned int i, j;
+
+	TRACE(("enter newchannel"))
+	
+	/* first see if we can use existing channels */
+	for (i = 0; i < ses.chansize; i++) {
+		if (ses.channels[i] == NULL) {
+			break;
+		}
+	}
+
+	/* otherwise extend the list */
+	if (i == ses.chansize) {
+		if (ses.chansize >= MAX_CHANNELS) {
+			TRACE(("leave newchannel: max chans reached"))
+			return NULL;
+		}
+
+		/* extend the channels */
+		ses.channels = (struct Channel**)m_realloc(ses.channels,
+				(ses.chansize+CHAN_EXTEND_SIZE)*sizeof(struct Channel*));
+
+		ses.chansize += CHAN_EXTEND_SIZE;
+
+		/* set the new channels to null */
+		for (j = i; j < ses.chansize; j++) {
+			ses.channels[j] = NULL;
+		}
+
+	}
+	
+	newchan = (struct Channel*)m_malloc(sizeof(struct Channel));
+	newchan->type = type;
+	newchan->index = i;
+	newchan->sentclosed = newchan->recvclosed = 0;
+	newchan->senteof = newchan->recveof = 0;
+
+	newchan->remotechan = remotechan;
+	newchan->transwindow = transwindow;
+	newchan->transmaxpacket = transmaxpacket;
+	
+	newchan->typedata = NULL;
+	newchan->writefd = FD_UNINIT;
+	newchan->readfd = FD_UNINIT;
+	newchan->errfd = FD_CLOSED; /* this isn't always set to start with */
+	newchan->initconn = 0;
+	newchan->await_open = 0;
+
+	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;
+	ses.chancount++;
+
+	TRACE(("leave newchannel"))
+
+	return newchan;
+}
+
+/* Returns the channel structure corresponding to the channel in the current
+ * data packet (ses.payload must be positioned appropriately) */
+struct Channel* getchannel() {
+
+	unsigned int chan;
+
+	chan = buf_getint(ses.payload);
+	if (chan >= ses.chansize || ses.channels[chan] == NULL) {
+		return NULL;
+	}
+	return ses.channels[chan];
+}
+
+/* Iterate through the channels, performing IO if available */
+void channelio(fd_set *readfds, fd_set *writefds) {
+
+	struct Channel *channel;
+	unsigned int i;
+
+	/* iterate through all the possible channels */
+	for (i = 0; i < ses.chansize; i++) {
+
+		channel = ses.channels[i];
+		if (channel == NULL) {
+			/* only process in-use channels */
+			continue;
+		}
+
+		/* read data and send it over the wire */
+		if (channel->readfd >= 0 && FD_ISSET(channel->readfd, readfds)) {
+			send_msg_channel_data(channel, 0, 0);
+		}
+
+		/* read stderr data and send it over the wire */
+		if (channel->extrabuf == NULL &&
+				channel->errfd >= 0 && FD_ISSET(channel->errfd, readfds)) {
+				send_msg_channel_data(channel, 1, SSH_EXTENDED_DATA_STDERR);
+		}
+
+		/* write to program/pipe stdin */
+		if (channel->writefd >= 0 && FD_ISSET(channel->writefd, writefds)) {
+			if (channel->initconn) {
+				checkinitdone(channel);
+				continue; /* Important not to use the channel after
+							 checkinitdone(), as it may be NULL */
+			}
+			writechannel(channel, channel->writefd, channel->writebuf);
+		}
+		
+		/* stderr for client mode */
+		if (channel->extrabuf != NULL 
+				&& channel->errfd >= 0 && FD_ISSET(channel->errfd, writefds)) {
+			writechannel(channel, channel->errfd, channel->extrabuf);
+		}
+	
+		/* now handle any of the channel-closing type stuff */
+		checkclose(channel);
+
+	} /* foreach channel */
+
+	/* Listeners such as TCP, X11, agent-auth */
+#ifdef USING_LISTENERS
+	handle_listeners(readfds);
+#endif
+}
+
+
+/* do all the EOF/close type stuff checking for a channel */
+static void checkclose(struct Channel *channel) {
+
+	TRACE(("checkclose: writefd %d, readfd %d, errfd %d, sentclosed %d, recvclosed %d",
+				channel->writefd, channel->readfd,
+				channel->errfd, channel->sentclosed, channel->recvclosed))
+	TRACE(("writebuf %d extrabuf %s extrabuf %d",
+				cbuf_getused(channel->writebuf),
+				channel->writebuf,
+				channel->writebuf ? 0 : cbuf_getused(channel->extrabuf)))
+
+	if (!channel->sentclosed) {
+
+		/* check for exited - currently only used for server sessions,
+		 * if the shell has exited etc */
+		if (channel->type->checkclose) {
+			if (channel->type->checkclose(channel)) {
+				closewritefd(channel);
+			}
+		}
+
+		if (!channel->senteof
+			&& channel->readfd == FD_CLOSED 
+			&& (channel->extrabuf != NULL || channel->errfd == FD_CLOSED)) {
+			send_msg_channel_eof(channel);
+		}
+
+		if (channel->writefd == FD_CLOSED
+			&& channel->readfd == FD_CLOSED
+			&& (channel->extrabuf != NULL || channel->errfd == FD_CLOSED)) {
+			send_msg_channel_close(channel);
+		}
+	}
+
+	/* When either party wishes to terminate the channel, it sends
+	 * SSH_MSG_CHANNEL_CLOSE.  Upon receiving this message, a party MUST
+	 * send back a SSH_MSG_CHANNEL_CLOSE unless it has already sent this
+	 * message for the channel.  The channel is considered closed for a
+	 * party when it has both sent and received SSH_MSG_CHANNEL_CLOSE, and
+	 * the party may then reuse the channel number.  A party MAY send
+	 * SSH_MSG_CHANNEL_CLOSE without having sent or received
+	 * SSH_MSG_CHANNEL_EOF. 
+	 * (from draft-ietf-secsh-connect)
+	 */
+	if (channel->recvclosed) {
+		if (! channel->sentclosed) {
+			TRACE(("Sending MSG_CHANNEL_CLOSE in response to same."))
+			send_msg_channel_close(channel);
+		}
+		removechannel(channel);
+	}
+}
+
+
+/* Check whether a deferred (EINPROGRESS) connect() was successful, and
+ * if so, set up the channel properly. Otherwise, the channel is cleaned up, so
+ * it is important that the channel reference isn't used after a call to this
+ * function */
+static void checkinitdone(struct Channel *channel) {
+
+	int val;
+	socklen_t vallen = sizeof(val);
+
+	TRACE(("enter checkinitdone"))
+
+	if (getsockopt(channel->writefd, SOL_SOCKET, SO_ERROR, &val, &vallen)
+			|| val != 0) {
+		send_msg_channel_open_failure(channel->remotechan,
+				SSH_OPEN_CONNECT_FAILED, "", "");
+		close(channel->writefd);
+		deletechannel(channel);
+		TRACE(("leave checkinitdone: fail"))
+	} else {
+		send_msg_channel_open_confirmation(channel, channel->recvwindow,
+				channel->recvmaxpacket);
+		channel->readfd = channel->writefd;
+		channel->initconn = 0;
+		TRACE(("leave checkinitdone: success"))
+	}
+}
+
+
+
+/* Send the close message and set the channel as closed */
+static void send_msg_channel_close(struct Channel *channel) {
+
+	TRACE(("enter send_msg_channel_close"))
+	/* XXX server */
+	if (channel->type->closehandler) {
+		channel->type->closehandler(channel);
+	}
+	
+	CHECKCLEARTOWRITE();
+
+	buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_CLOSE);
+	buf_putint(ses.writepayload, channel->remotechan);
+
+	encrypt_packet();
+
+	channel->senteof = 1;
+	channel->sentclosed = 1;
+	TRACE(("leave send_msg_channel_close"))
+}
+
+/* call this when trans/eof channels are closed */
+static void send_msg_channel_eof(struct Channel *channel) {
+
+	TRACE(("enter send_msg_channel_eof"))
+	CHECKCLEARTOWRITE();
+
+	buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_EOF);
+	buf_putint(ses.writepayload, channel->remotechan);
+
+	encrypt_packet();
+
+	channel->senteof = 1;
+
+	TRACE(("leave send_msg_channel_eof"))
+}
+
+/* Called to write data out to the local side of the channel. 
+ * Only called when we know we can write to a channel, writes as much as
+ * possible */
+static void writechannel(struct Channel* channel, int fd, circbuffer *cbuf) {
+
+	int len, maxlen;
+
+	TRACE(("enter writechannel"))
+
+	maxlen = cbuf_readlen(cbuf);
+
+	/* Write the data out */
+	len = write(fd, cbuf_readptr(cbuf, maxlen), maxlen);
+	if (len <= 0) {
+		if (len < 0 && errno != EINTR) {
+			/* no more to write - we close it even if the fd was stderr, since
+			 * that's a nasty failure too */
+			closewritefd(channel);
+		}
+		TRACE(("leave writechannel: len <= 0"))
+		return;
+	}
+
+	cbuf_incrread(cbuf, len);
+	channel->recvdonelen += len;
+
+	if (fd == channel->writefd && cbuf_getused(cbuf) == 0 && channel->recveof) { 
+		/* Check if we're closing up */
+		closewritefd(channel);
+		TRACE(("leave writechannel: recveof set"))
+		return;
+	}
+
+	/* Window adjust handling */
+	if (channel->recvdonelen >= RECV_WINDOWEXTEND) {
+		/* Set it back to max window */
+		send_msg_channel_window_adjust(channel, channel->recvdonelen);
+		channel->recvwindow += channel->recvdonelen;
+		channel->recvdonelen = 0;
+	}
+
+	dropbear_assert(channel->recvwindow <= RECV_MAXWINDOW);
+	dropbear_assert(channel->recvwindow <= cbuf_getavail(channel->writebuf));
+	dropbear_assert(channel->extrabuf == NULL ||
+			channel->recvwindow <= cbuf_getavail(channel->extrabuf));
+	
+	
+	TRACE(("leave writechannel"))
+}
+
+/* Set the file descriptors for the main select in session.c
+ * This avoid channels which don't have any window available, are closed, etc*/
+void setchannelfds(fd_set *readfds, fd_set *writefds) {
+	
+	unsigned int i;
+	struct Channel * channel;
+	
+	for (i = 0; i < ses.chansize; i++) {
+
+		channel = ses.channels[i];
+		if (channel == NULL) {
+			continue;
+		}
+
+		/* Stuff to put over the wire */
+		if (channel->transwindow > 0) {
+
+			if (channel->readfd >= 0) {
+				FD_SET(channel->readfd, readfds);
+			}
+			
+			if (channel->extrabuf == NULL && channel->errfd >= 0) {
+					FD_SET(channel->errfd, readfds);
+			}
+		}
+
+		/* Stuff from the wire */
+		if ((channel->writefd >= 0 && cbuf_getused(channel->writebuf) > 0 )
+				|| channel->initconn) {
+
+				FD_SET(channel->writefd, writefds);
+		}
+
+		if (channel->extrabuf != NULL && channel->errfd >= 0 
+				&& cbuf_getused(channel->extrabuf) > 0 ) {
+				FD_SET(channel->errfd, writefds);
+		}
+
+	} /* foreach channel */
+
+#ifdef USING_LISTENERS
+	set_listener_fds(readfds);
+#endif
+
+}
+
+/* handle the channel EOF event, by closing the channel filedescriptor. The
+ * channel isn't closed yet, it is left until the incoming (from the program
+ * etc) FD is also EOF */
+void recv_msg_channel_eof() {
+
+	struct Channel * channel;
+
+	TRACE(("enter recv_msg_channel_eof"))
+
+	channel = getchannel();
+	if (channel == NULL) {
+		dropbear_exit("EOF for unknown channel");
+	}
+
+	channel->recveof = 1;
+	if (cbuf_getused(channel->writebuf) == 0
+			&& (channel->extrabuf == NULL 
+					|| cbuf_getused(channel->extrabuf) == 0)) {
+		closewritefd(channel);
+	}
+
+	TRACE(("leave recv_msg_channel_eof"))
+}
+
+
+/* Handle channel closure(), respond in kind and close the channels */
+void recv_msg_channel_close() {
+
+	struct Channel * channel;
+
+	TRACE(("enter recv_msg_channel_close"))
+
+	channel = getchannel();
+	if (channel == NULL) {
+		/* disconnect ? */
+		dropbear_exit("Close for unknown channel");
+	}
+
+	channel->recveof = 1;
+	channel->recvclosed = 1;
+
+	if (channel->sentclosed) {
+		removechannel(channel);
+	}
+
+	TRACE(("leave recv_msg_channel_close"))
+}
+
+/* Remove a channel entry, this is only executed after both sides have sent
+ * channel close */
+static void removechannel(struct Channel * channel) {
+
+	TRACE(("enter removechannel"))
+	TRACE(("channel index is %d", channel->index))
+
+	cbuf_free(channel->writebuf);
+	channel->writebuf = NULL;
+
+	if (channel->extrabuf) {
+		cbuf_free(channel->extrabuf);
+		channel->extrabuf = NULL;
+	}
+
+
+	/* close the FDs in case they haven't been done
+	 * yet (ie they were shutdown etc */
+	close(channel->writefd);
+	close(channel->readfd);
+	close(channel->errfd);
+
+	channel->typedata = NULL;
+
+	deletechannel(channel);
+
+	TRACE(("leave removechannel"))
+}
+
+/* Remove a channel entry */
+static void deletechannel(struct Channel *channel) {
+
+	ses.channels[channel->index] = NULL;
+	m_free(channel);
+	ses.chancount--;
+	
+}
+
+
+/* Handle channel specific requests, passing off to corresponding handlers
+ * such as chansession or x11fwd */
+void recv_msg_channel_request() {
+
+	struct Channel *channel;
+
+	TRACE(("enter recv_msg_channel_request"))
+	
+	channel = getchannel();
+	if (channel == NULL) {
+		/* disconnect ? */
+		dropbear_exit("Unknown channel");
+	}
+
+	if (channel->type->reqhandler) {
+		channel->type->reqhandler(channel);
+	} else {
+		send_msg_channel_failure(channel);
+	}
+
+	TRACE(("leave recv_msg_channel_request"))
+
+}
+
+/* Reads data from the server's program/shell/etc, and puts it in a
+ * channel_data packet to send.
+ * chan is the remote channel, isextended is 0 if it is normal data, 1
+ * if it is extended data. if it is extended, then the type is in
+ * exttype */
+static void send_msg_channel_data(struct Channel *channel, int isextended,
+		unsigned int exttype) {
+
+	buffer *buf;
+	int len;
+	unsigned int maxlen;
+	int fd;
+
+/*	TRACE(("enter send_msg_channel_data"))
+	TRACE(("extended = %d type = %d", isextended, exttype))*/
+
+	CHECKCLEARTOWRITE();
+
+	dropbear_assert(!channel->sentclosed);
+
+	if (isextended) {
+		fd = channel->errfd;
+	} else {
+		fd = channel->readfd;
+	}
+	dropbear_assert(fd >= 0);
+
+	maxlen = MIN(channel->transwindow, channel->transmaxpacket);
+	/* -(1+4+4) is SSH_MSG_CHANNEL_DATA, channel number, string length, and 
+	 * exttype if is extended */
+	maxlen = MIN(maxlen, 
+			ses.writepayload->size - 1 - 4 - 4 - (isextended ? 4 : 0));
+	if (maxlen == 0) {
+		TRACE(("leave send_msg_channel_data: no window"))
+		return; /* the data will get written later */
+	}
+
+	/* read the data */
+	TRACE(("maxlen %d", maxlen))
+	buf = buf_new(maxlen);
+	TRACE(("buf pos %d data %x", buf->pos, buf->data))
+	len = read(fd, buf_getwriteptr(buf, maxlen), maxlen);
+	if (len <= 0) {
+		/* on error/eof, send eof */
+		if (len == 0 || errno != EINTR) {
+			closereadfd(channel, fd);
+		}
+		buf_free(buf);
+		buf = NULL;
+		TRACE(("leave send_msg_channel_data: read err or EOF for fd %d", 
+					channel->index));
+		return;
+	}
+	buf_incrlen(buf, len);
+
+	buf_putbyte(ses.writepayload, 
+			isextended ? SSH_MSG_CHANNEL_EXTENDED_DATA : SSH_MSG_CHANNEL_DATA);
+	buf_putint(ses.writepayload, channel->remotechan);
+
+	if (isextended) {
+		buf_putint(ses.writepayload, exttype);
+	}
+
+	buf_putstring(ses.writepayload, buf_getptr(buf, len), len);
+	buf_free(buf);
+	buf = NULL;
+
+	channel->transwindow -= len;
+
+	encrypt_packet();
+	TRACE(("leave send_msg_channel_data"))
+}
+
+/* We receive channel data */
+void recv_msg_channel_data() {
+
+	struct Channel *channel;
+
+	channel = getchannel();
+	if (channel == NULL) {
+		dropbear_exit("Unknown channel");
+	}
+
+	common_recv_msg_channel_data(channel, channel->writefd, channel->writebuf);
+}
+
+/* Shared for data and stderr data - when we receive data, put it in a buffer
+ * for writing to the local file descriptor */
+void common_recv_msg_channel_data(struct Channel *channel, int fd, 
+		circbuffer * cbuf) {
+
+	unsigned int datalen;
+	unsigned int maxdata;
+	unsigned int buflen;
+	unsigned int len;
+
+	TRACE(("enter recv_msg_channel_data"))
+
+	if (channel->recveof) {
+		dropbear_exit("received data after eof");
+	}
+
+ 	if (fd < 0) {
+		dropbear_exit("received data with bad writefd");
+	}
+
+	datalen = buf_getint(ses.payload);
+
+
+	maxdata = cbuf_getavail(cbuf);
+
+	/* Whilst the spec says we "MAY ignore data past the end" this could
+	 * lead to corrupted file transfers etc (chunks missed etc). It's better to
+	 * just die horribly */
+	if (datalen > maxdata) {
+		dropbear_exit("Oversized packet");
+	}
+
+	/* We may have to run throught twice, if the buffer wraps around. Can't
+	 * just "leave it for next time" like with writechannel, since this
+	 * is payload data */
+	len = datalen;
+	while (len > 0) {
+		buflen = cbuf_writelen(cbuf);
+		buflen = MIN(buflen, len);
+
+		memcpy(cbuf_writeptr(cbuf, buflen), 
+				buf_getptr(ses.payload, buflen), buflen);
+		cbuf_incrwrite(cbuf, buflen);
+		buf_incrpos(ses.payload, buflen);
+		len -= buflen;
+	}
+
+	dropbear_assert(channel->recvwindow >= datalen);
+	channel->recvwindow -= datalen;
+	dropbear_assert(channel->recvwindow <= RECV_MAXWINDOW);
+
+	TRACE(("leave recv_msg_channel_data"))
+}
+
+/* Increment the outgoing data window for a channel - the remote end limits
+ * the amount of data which may be transmitted, this window is decremented
+ * as data is sent, and incremented upon receiving window-adjust messages */
+void recv_msg_channel_window_adjust() {
+
+	struct Channel * channel;
+	unsigned int incr;
+	
+	channel = getchannel();
+	if (channel == NULL) {
+		dropbear_exit("Unknown channel");
+	}
+	
+	incr = buf_getint(ses.payload);
+	TRACE(("received window increment %d", incr))
+	incr = MIN(incr, MAX_TRANS_WIN_INCR);
+	
+	channel->transwindow += incr;
+	channel->transwindow = MIN(channel->transwindow, MAX_TRANS_WINDOW);
+
+}
+
+/* Increment the incoming data window for a channel, and let the remote
+ * end know */
+static void send_msg_channel_window_adjust(struct Channel* channel, 
+		unsigned int incr) {
+
+	TRACE(("sending window adjust %d", incr))
+	CHECKCLEARTOWRITE();
+
+	buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_WINDOW_ADJUST);
+	buf_putint(ses.writepayload, channel->remotechan);
+	buf_putint(ses.writepayload, incr);
+
+	encrypt_packet();
+}
+	
+/* Handle a new channel request, performing any channel-type-specific setup */
+/* XXX server */
+void recv_msg_channel_open() {
+
+	unsigned char *type;
+	unsigned int typelen;
+	unsigned int remotechan, transwindow, transmaxpacket;
+	struct Channel *channel;
+	const struct ChanType **cp;
+	const struct ChanType *chantype;
+	unsigned int errtype = SSH_OPEN_UNKNOWN_CHANNEL_TYPE;
+	int ret;
+
+
+	TRACE(("enter recv_msg_channel_open"))
+
+	/* get the packet contents */
+	type = buf_getstring(ses.payload, &typelen);
+
+	remotechan = buf_getint(ses.payload);
+	transwindow = buf_getint(ses.payload);
+	transwindow = MIN(transwindow, MAX_TRANS_WINDOW);
+	transmaxpacket = buf_getint(ses.payload);
+	transmaxpacket = MIN(transmaxpacket, MAX_TRANS_PAYLOAD_LEN);
+
+	/* figure what type of packet it is */
+	if (typelen > MAX_NAME_LEN) {
+		goto failure;
+	}
+
+	/* Get the channel type. Client and server style invokation will set up a
+	 * different list for ses.chantypes at startup. We just iterate through
+	 * this list and find the matching name */
+	for (cp = &ses.chantypes[0], chantype = (*cp); 
+			chantype != NULL;
+			cp++, chantype = (*cp)) {
+		if (strcmp(type, chantype->name) == 0) {
+			break;
+		}
+	}
+
+	if (chantype == NULL) {
+		TRACE(("No matching type for '%s'", type))
+		goto failure;
+	}
+
+	TRACE(("matched type '%s'", type))
+
+	/* create the channel */
+	channel = newchannel(remotechan, chantype, transwindow, transmaxpacket);
+
+	if (channel == NULL) {
+		TRACE(("newchannel returned NULL"))
+		goto failure;
+	}
+
+	if (channel->type->inithandler) {
+		ret = channel->type->inithandler(channel);
+		if (ret > 0) {
+			if (ret == SSH_OPEN_IN_PROGRESS) {
+				/* We'll send the confirmation later */
+				goto cleanup;
+			}
+			errtype = ret;
+			deletechannel(channel);
+			TRACE(("inithandler returned failure %d", ret))
+			goto failure;
+		}
+	}
+
+	/* success */
+	send_msg_channel_open_confirmation(channel, channel->recvwindow,
+			channel->recvmaxpacket);
+	goto cleanup;
+
+failure:
+	TRACE(("recv_msg_channel_open failure"))
+	send_msg_channel_open_failure(remotechan, errtype, "", "");
+
+cleanup:
+	m_free(type);
+
+	TRACE(("leave recv_msg_channel_open"))
+}
+
+/* Send a failure message */
+void send_msg_channel_failure(struct Channel *channel) {
+
+	TRACE(("enter send_msg_channel_failure"))
+	CHECKCLEARTOWRITE();
+
+	buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_FAILURE);
+	buf_putint(ses.writepayload, channel->remotechan);
+
+	encrypt_packet();
+	TRACE(("leave send_msg_channel_failure"))
+}
+
+/* Send a success message */
+void send_msg_channel_success(struct Channel *channel) {
+
+	TRACE(("enter send_msg_channel_success"))
+	CHECKCLEARTOWRITE();
+
+	buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_SUCCESS);
+	buf_putint(ses.writepayload, channel->remotechan);
+
+	encrypt_packet();
+	TRACE(("leave send_msg_channel_success"))
+}
+
+/* Send a channel open failure message, with a corresponding reason
+ * code (usually resource shortage or unknown chan type) */
+static void send_msg_channel_open_failure(unsigned int remotechan, 
+		int reason, const unsigned char *text, const unsigned char *lang) {
+
+	TRACE(("enter send_msg_channel_open_failure"))
+	CHECKCLEARTOWRITE();
+	
+	buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_OPEN_FAILURE);
+	buf_putint(ses.writepayload, remotechan);
+	buf_putint(ses.writepayload, reason);
+	buf_putstring(ses.writepayload, text, strlen((char*)text));
+	buf_putstring(ses.writepayload, lang, strlen((char*)lang));
+
+	encrypt_packet();
+	TRACE(("leave send_msg_channel_open_failure"))
+}
+
+/* Confirm a channel open, and let the remote end know what number we've
+ * allocated and the receive parameters */
+static void send_msg_channel_open_confirmation(struct Channel* channel,
+		unsigned int recvwindow, 
+		unsigned int recvmaxpacket) {
+
+	TRACE(("enter send_msg_channel_open_confirmation"))
+	CHECKCLEARTOWRITE();
+
+	buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_OPEN_CONFIRMATION);
+	buf_putint(ses.writepayload, channel->remotechan);
+	buf_putint(ses.writepayload, channel->index);
+	buf_putint(ses.writepayload, recvwindow);
+	buf_putint(ses.writepayload, recvmaxpacket);
+
+	encrypt_packet();
+	TRACE(("leave send_msg_channel_open_confirmation"))
+}
+
+#if defined(USING_LISTENERS) || defined(DROPBEAR_CLIENT)
+/* Create a new channel, and start the open request. This is intended
+ * for X11, agent, tcp forwarding, and should be filled with channel-specific
+ * options, with the calling function calling encrypt_packet() after
+ * completion. It is mandatory for the caller to encrypt_packet() if
+ * DROPBEAR_SUCCESS is returned */
+int send_msg_channel_open_init(int fd, const struct ChanType *type) {
+
+	struct Channel* chan;
+
+	TRACE(("enter send_msg_channel_open_init()"))
+	chan = newchannel(0, type, 0, 0);
+	if (!chan) {
+		TRACE(("leave send_msg_channel_open_init() - FAILED in newchannel()"))
+		return DROPBEAR_FAILURE;
+	}
+
+	/* set fd non-blocking */
+	setnonblocking(fd);
+
+	chan->writefd = chan->readfd = fd;
+	ses.maxfd = MAX(ses.maxfd, fd);
+
+	chan->await_open = 1;
+
+	/* now open the channel connection */
+	CHECKCLEARTOWRITE();
+
+	buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_OPEN);
+	buf_putstring(ses.writepayload, type->name, strlen(type->name));
+	buf_putint(ses.writepayload, chan->index);
+	buf_putint(ses.writepayload, RECV_MAXWINDOW);
+	buf_putint(ses.writepayload, RECV_MAXPACKET);
+
+	TRACE(("leave send_msg_channel_open_init()"))
+	return DROPBEAR_SUCCESS;
+}
+
+/* Confirmation that our channel open request (for forwardings) was 
+ * successful*/
+void recv_msg_channel_open_confirmation() {
+
+	struct Channel * channel;
+	int ret;
+
+	TRACE(("enter recv_msg_channel_open_confirmation"))
+
+	channel = getchannel();
+	if (channel == NULL) {
+		dropbear_exit("Unknown channel");
+	}
+
+	if (!channel->await_open) {
+		dropbear_exit("unexpected channel reply");
+	}
+	channel->await_open = 0;
+
+	channel->remotechan =  buf_getint(ses.payload);
+	channel->transwindow = buf_getint(ses.payload);
+	channel->transmaxpacket = buf_getint(ses.payload);
+	
+	TRACE(("new chan remote %d local %d", 
+				channel->remotechan, channel->index))
+
+	/* Run the inithandler callback */
+	if (channel->type->inithandler) {
+		ret = channel->type->inithandler(channel);
+		if (ret > 0) {
+			removechannel(channel);
+			TRACE(("inithandler returned failure %d", ret))
+		}
+	}
+
+	
+	TRACE(("leave recv_msg_channel_open_confirmation"))
+}
+
+/* Notification that our channel open request failed */
+void recv_msg_channel_open_failure() {
+
+	struct Channel * channel;
+
+	channel = getchannel();
+	if (channel == NULL) {
+		dropbear_exit("Unknown channel");
+	}
+
+	if (!channel->await_open) {
+		dropbear_exit("unexpected channel reply");
+	}
+	channel->await_open = 0;
+
+	removechannel(channel);
+}
+#endif /* USING_LISTENERS */
+
+/* close a stdout/stderr fd */
+static void closereadfd(struct Channel * channel, int fd) {
+
+	/* don't close it if it is the same as writefd,
+	 * unless writefd is already set -1 */
+	TRACE(("enter closereadfd"))
+	closechanfd(channel, fd, 0);
+	TRACE(("leave closereadfd"))
+}
+
+/* close a stdin fd */
+static void closewritefd(struct Channel * channel) {
+
+	TRACE(("enter closewritefd"))
+	closechanfd(channel, channel->writefd, 1);
+	TRACE(("leave closewritefd"))
+}
+
+/* close a fd, how is 0 for stdout/stderr, 1 for stdin */
+static void closechanfd(struct Channel *channel, int fd, int how) {
+
+	int closein = 0, closeout = 0;
+
+	/* XXX server */
+	if (channel->type->sepfds) {
+		TRACE(("shutdown((%d), %d)", fd, how))
+		shutdown(fd, how);
+		if (how == 0) {
+			closeout = 1;
+		} else {
+			closein = 1;
+		}
+	} else {
+		close(fd);
+		closein = closeout = 1;
+	}
+
+	if (closeout && fd == channel->readfd) {
+		channel->readfd = FD_CLOSED;
+	}
+	if (closeout && (channel->extrabuf == NULL) && (fd == channel->errfd)) {
+		channel->errfd = FD_CLOSED;
+	}
+
+	if (closein && fd == channel->writefd) {
+		channel->writefd = FD_CLOSED;
+	}
+	if (closein && (channel->extrabuf != NULL) && (fd == channel->errfd)) {
+		channel->errfd = FD_CLOSED;
+	}
+
+	/* if we called shutdown on it and all references are gone, then we 
+	 * need to close() it to stop it lingering */
+	if (channel->type->sepfds && channel->readfd == FD_CLOSED 
+		&& channel->writefd == FD_CLOSED && channel->errfd == FD_CLOSED) {
+		close(fd);
+	}
+}
diff --git a/common-chansession.c b/common-chansession.c
new file mode 100644
index 0000000000000000000000000000000000000000..b350c6ce4de9f6ee019e4aca65c92cce4e0069aa
--- /dev/null
+++ b/common-chansession.c
@@ -0,0 +1,43 @@
+/*
+ * 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. */
+
+#include "chansession.h"
+
+/* Mapping of signal values to ssh signal strings */
+const struct SigMap signames[] = {
+	{SIGABRT, "ABRT"},
+	{SIGALRM, "ALRM"},
+	{SIGFPE, "FPE"},
+	{SIGHUP, "HUP"},
+	{SIGILL, "ILL"},
+	{SIGINT, "INT"},
+	{SIGKILL, "KILL"},
+	{SIGPIPE, "PIPE"},
+	{SIGQUIT, "QUIT"},
+	{SIGSEGV, "SEGV"},
+	{SIGTERM, "TERM"},
+	{SIGUSR1, "USR1"},
+	{SIGUSR2, "USR2"},
+	{0, NULL}
+};
diff --git a/common-kex.c b/common-kex.c
new file mode 100644
index 0000000000000000000000000000000000000000..5db8e52e7794e9123218b83eca7dcaa174784993
--- /dev/null
+++ b/common-kex.c
@@ -0,0 +1,718 @@
+/*
+ * Dropbear SSH
+ * 
+ * Copyright (c) 2002-2004 Matt Johnston
+ * Portions 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 "algo.h"
+#include "buffer.h"
+#include "session.h"
+#include "kex.h"
+#include "ssh.h"
+#include "packet.h"
+#include "bignum.h"
+#include "random.h"
+
+/* diffie-hellman-group1-sha1 value for p */
+static const unsigned char dh_p_val[] = {
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2,
+    0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
+	0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6,
+	0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
+	0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D,
+	0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
+	0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9,
+	0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
+	0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11,
+	0x7C, 0x4B, 0x1F, 0xE6, 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE6, 0x53, 0x81,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+#define DH_P_LEN sizeof(dh_p_val)
+
+static const int DH_G_VAL = 2;
+
+static void kexinitialise();
+void gen_new_keys();
+#ifndef DISABLE_ZLIB
+static void gen_new_zstreams();
+#endif
+static void read_kex_algos();
+/* helper function for gen_new_keys */
+static void hashkeys(unsigned char *out, int outlen, 
+		const hash_state * hs, unsigned const char X);
+
+
+/* Send our list of algorithms we can use */
+void send_msg_kexinit() {
+
+	CHECKCLEARTOWRITE();
+	buf_putbyte(ses.writepayload, SSH_MSG_KEXINIT);
+
+	/* cookie */
+	genrandom(buf_getwriteptr(ses.writepayload, 16), 16);
+	buf_incrwritepos(ses.writepayload, 16);
+
+	/* kex algos */
+	buf_put_algolist(ses.writepayload, sshkex);
+
+	/* server_host_key_algorithms */
+	buf_put_algolist(ses.writepayload, sshhostkey);
+
+	/* encryption_algorithms_client_to_server */
+	buf_put_algolist(ses.writepayload, sshciphers);
+
+	/* encryption_algorithms_server_to_client */
+	buf_put_algolist(ses.writepayload, sshciphers);
+
+	/* mac_algorithms_client_to_server */
+	buf_put_algolist(ses.writepayload, sshhashes);
+
+	/* mac_algorithms_server_to_client */
+	buf_put_algolist(ses.writepayload, sshhashes);
+
+	/* compression_algorithms_client_to_server */
+	buf_put_algolist(ses.writepayload, sshcompress);
+
+	/* compression_algorithms_server_to_client */
+	buf_put_algolist(ses.writepayload, sshcompress);
+
+	/* languages_client_to_server */
+	buf_putstring(ses.writepayload, "", 0);
+
+	/* languages_server_to_client */
+	buf_putstring(ses.writepayload, "", 0);
+
+	/* first_kex_packet_follows - unimplemented for now */
+	buf_putbyte(ses.writepayload, 0x00);
+
+	/* reserved unit32 */
+	buf_putint(ses.writepayload, 0);
+
+	/* set up transmitted kex packet buffer for hashing. 
+	 * This is freed after the end of the kex */
+	ses.transkexinit = buf_newcopy(ses.writepayload);
+
+	encrypt_packet();
+	ses.dataallowed = 0; /* don't send other packets during kex */
+
+	TRACE(("DATAALLOWED=0"))
+	TRACE(("-> KEXINIT"))
+	ses.kexstate.sentkexinit = 1;
+}
+
+/* *** NOTE regarding (send|recv)_msg_newkeys *** 
+ * Changed by mihnea from the original kex.c to set dataallowed after a 
+ * completed key exchange, no matter the order in which it was performed.
+ * This enables client mode without affecting server functionality.
+ */
+
+/* Bring new keys into use after a key exchange, and let the client know*/
+void send_msg_newkeys() {
+
+	TRACE(("enter send_msg_newkeys"))
+
+	/* generate the kexinit request */
+	CHECKCLEARTOWRITE();
+	buf_putbyte(ses.writepayload, SSH_MSG_NEWKEYS);
+	encrypt_packet();
+	
+
+	/* set up our state */
+	if (ses.kexstate.recvnewkeys) {
+		TRACE(("while RECVNEWKEYS=1"))
+		gen_new_keys();
+		kexinitialise(); /* we've finished with this kex */
+		TRACE((" -> DATAALLOWED=1"))
+		ses.dataallowed = 1; /* we can send other packets again now */
+		ses.kexstate.donefirstkex = 1;
+	} else {
+		ses.kexstate.sentnewkeys = 1;
+		TRACE(("SENTNEWKEYS=1"))
+	}
+
+	TRACE(("-> MSG_NEWKEYS"))
+	TRACE(("leave send_msg_newkeys"))
+}
+
+/* Bring the new keys into use after a key exchange */
+void recv_msg_newkeys() {
+
+	TRACE(("<- MSG_NEWKEYS"))
+	TRACE(("enter recv_msg_newkeys"))
+
+	/* simply check if we've sent SSH_MSG_NEWKEYS, and if so,
+	 * switch to the new keys */
+	if (ses.kexstate.sentnewkeys) {
+		TRACE(("while SENTNEWKEYS=1"))
+		gen_new_keys();
+		kexinitialise(); /* we've finished with this kex */
+	    TRACE((" -> DATAALLOWED=1"))
+	    ses.dataallowed = 1; /* we can send other packets again now */
+		ses.kexstate.donefirstkex = 1;
+	} else {
+		TRACE(("RECVNEWKEYS=1"))
+		ses.kexstate.recvnewkeys = 1;
+	}
+	
+	TRACE(("leave recv_msg_newkeys"))
+}
+
+
+/* Set up the kex for the first time */
+void kexfirstinitialise() {
+
+	ses.kexstate.donefirstkex = 0;
+	kexinitialise();
+}
+
+/* Reset the kex state, ready for a new negotiation */
+static void kexinitialise() {
+
+	struct timeval tv;
+
+	TRACE(("kexinitialise()"))
+
+	/* sent/recv'd MSG_KEXINIT */
+	ses.kexstate.sentkexinit = 0;
+	ses.kexstate.recvkexinit = 0;
+
+	/* sent/recv'd MSG_NEWKEYS */
+	ses.kexstate.recvnewkeys = 0;
+	ses.kexstate.sentnewkeys = 0;
+
+	/* first_packet_follows */
+	ses.kexstate.firstfollows = 0;
+
+	ses.kexstate.datatrans = 0;
+	ses.kexstate.datarecv = 0;
+
+	if (gettimeofday(&tv, 0) < 0) {
+		dropbear_exit("Error getting time");
+	}
+	ses.kexstate.lastkextime = tv.tv_sec;
+
+}
+
+/* Helper function for gen_new_keys, creates a hash. It makes a copy of the
+ * already initialised hash_state hs, which should already have processed
+ * the dh_K and hash, since these are common. X is the letter 'A', 'B' etc.
+ * out must have at least min(SHA1_HASH_SIZE, outlen) bytes allocated.
+ * The output will only be expanded once, since that is all that is required
+ * (for 3DES and SHA, with 24 and 20 bytes respectively). 
+ *
+ * See Section 5.2 of the IETF secsh Transport Draft for details */
+
+/* Duplicated verbatim from kex.c --mihnea */
+static void hashkeys(unsigned char *out, int outlen, 
+		const hash_state * hs, const unsigned char X) {
+
+	hash_state hs2;
+	unsigned char k2[SHA1_HASH_SIZE]; /* used to extending */
+
+	memcpy(&hs2, hs, sizeof(hash_state));
+	sha1_process(&hs2, &X, 1);
+	sha1_process(&hs2, ses.session_id, SHA1_HASH_SIZE);
+	sha1_done(&hs2, out);
+	if (SHA1_HASH_SIZE < outlen) {
+		/* need to extend */
+		memcpy(&hs2, hs, sizeof(hash_state));
+		sha1_process(&hs2, out, SHA1_HASH_SIZE);
+		sha1_done(&hs2, k2);
+		memcpy(&out[SHA1_HASH_SIZE], k2, outlen - SHA1_HASH_SIZE);
+	}
+}
+
+/* Generate the actual encryption/integrity keys, using the results of the
+ * key exchange, as specified in section 5.2 of the IETF secsh-transport
+ * draft. This occurs after the DH key-exchange.
+ *
+ * ses.newkeys is the new set of keys which are generated, these are only
+ * taken into use after both sides have sent a newkeys message */
+
+/* Originally from kex.c, generalized for cli/svr mode --mihnea */
+void gen_new_keys() {
+
+	unsigned char C2S_IV[MAX_IV_LEN];
+	unsigned char C2S_key[MAX_KEY_LEN];
+	unsigned char S2C_IV[MAX_IV_LEN];
+	unsigned char S2C_key[MAX_KEY_LEN];
+	/* unsigned char key[MAX_KEY_LEN]; */
+	unsigned char *trans_IV, *trans_key, *recv_IV, *recv_key;
+
+	hash_state hs;
+	unsigned int C2S_keysize, S2C_keysize;
+	char mactransletter, macrecvletter; /* Client or server specific */
+
+	TRACE(("enter gen_new_keys"))
+	/* the dh_K and hash are the start of all hashes, we make use of that */
+
+	sha1_init(&hs);
+	sha1_process_mp(&hs, ses.dh_K);
+	mp_clear(ses.dh_K);
+	m_free(ses.dh_K);
+	sha1_process(&hs, ses.hash, SHA1_HASH_SIZE);
+	m_burn(ses.hash, SHA1_HASH_SIZE);
+
+	if (IS_DROPBEAR_CLIENT) {
+	    trans_IV	= C2S_IV;
+	    recv_IV		= S2C_IV;
+	    trans_key	= C2S_key;
+	    recv_key	= S2C_key;
+	    C2S_keysize = ses.newkeys->trans_algo_crypt->keysize;
+	    S2C_keysize = ses.newkeys->recv_algo_crypt->keysize;
+		mactransletter = 'E';
+		macrecvletter = 'F';
+	} else {
+	    trans_IV	= S2C_IV;
+	    recv_IV		= C2S_IV;
+	    trans_key	= S2C_key;
+	    recv_key	= C2S_key;
+	    C2S_keysize = ses.newkeys->recv_algo_crypt->keysize;
+	    S2C_keysize = ses.newkeys->trans_algo_crypt->keysize;
+		mactransletter = 'F';
+		macrecvletter = 'E';
+	}
+
+	hashkeys(C2S_IV, SHA1_HASH_SIZE, &hs, 'A');
+	hashkeys(S2C_IV, SHA1_HASH_SIZE, &hs, 'B');
+	hashkeys(C2S_key, C2S_keysize, &hs, 'C');
+	hashkeys(S2C_key, S2C_keysize, &hs, 'D');
+
+	if (cbc_start(
+		find_cipher(ses.newkeys->recv_algo_crypt->cipherdesc->name),
+			recv_IV, recv_key, 
+			ses.newkeys->recv_algo_crypt->keysize, 0, 
+			&ses.newkeys->recv_symmetric_struct) != CRYPT_OK) {
+		dropbear_exit("crypto error");
+	}
+
+	if (cbc_start(
+		find_cipher(ses.newkeys->trans_algo_crypt->cipherdesc->name),
+			trans_IV, trans_key, 
+			ses.newkeys->trans_algo_crypt->keysize, 0, 
+			&ses.newkeys->trans_symmetric_struct) != CRYPT_OK) {
+		dropbear_exit("crypto error");
+	}
+	
+	/* MAC keys */
+	hashkeys(ses.newkeys->transmackey, 
+			ses.newkeys->trans_algo_mac->keysize, &hs, mactransletter);
+	hashkeys(ses.newkeys->recvmackey, 
+			ses.newkeys->recv_algo_mac->keysize, &hs, macrecvletter);
+
+#ifndef DISABLE_ZLIB
+	gen_new_zstreams();
+#endif
+	
+	/* Switch over to the new keys */
+	m_burn(ses.keys, sizeof(struct key_context));
+	m_free(ses.keys);
+	ses.keys = ses.newkeys;
+	ses.newkeys = NULL;
+
+	TRACE(("leave gen_new_keys"))
+}
+
+#ifndef DISABLE_ZLIB
+/* Set up new zlib compression streams, close the old ones. Only
+ * called from gen_new_keys() */
+static void gen_new_zstreams() {
+
+	/* create new zstreams */
+	if (ses.newkeys->recv_algo_comp == DROPBEAR_COMP_ZLIB) {
+		ses.newkeys->recv_zstream = (z_streamp)m_malloc(sizeof(z_stream));
+		ses.newkeys->recv_zstream->zalloc = Z_NULL;
+		ses.newkeys->recv_zstream->zfree = Z_NULL;
+		
+		if (inflateInit(ses.newkeys->recv_zstream) != Z_OK) {
+			dropbear_exit("zlib error");
+		}
+	} else {
+		ses.newkeys->recv_zstream = NULL;
+	}
+
+	if (ses.newkeys->trans_algo_comp == DROPBEAR_COMP_ZLIB) {
+		ses.newkeys->trans_zstream = (z_streamp)m_malloc(sizeof(z_stream));
+		ses.newkeys->trans_zstream->zalloc = Z_NULL;
+		ses.newkeys->trans_zstream->zfree = Z_NULL;
+	
+		if (deflateInit(ses.newkeys->trans_zstream, Z_DEFAULT_COMPRESSION) 
+				!= Z_OK) {
+			dropbear_exit("zlib error");
+		}
+	} else {
+		ses.newkeys->trans_zstream = NULL;
+	}
+	
+	/* clean up old keys */
+	if (ses.keys->recv_zstream != NULL) {
+		if (inflateEnd(ses.keys->recv_zstream) == Z_STREAM_ERROR) {
+			/* Z_DATA_ERROR is ok, just means that stream isn't ended */
+			dropbear_exit("crypto error");
+		}
+		m_free(ses.keys->recv_zstream);
+	}
+	if (ses.keys->trans_zstream != NULL) {
+		if (deflateEnd(ses.keys->trans_zstream) == Z_STREAM_ERROR) {
+			/* Z_DATA_ERROR is ok, just means that stream isn't ended */
+			dropbear_exit("crypto error");
+		}
+		m_free(ses.keys->trans_zstream);
+	}
+}
+#endif
+
+
+/* Executed upon receiving a kexinit message from the client to initiate
+ * key exchange. If we haven't already done so, we send the list of our
+ * preferred algorithms. The client's requested algorithms are processed,
+ * and we calculate the first portion of the key-exchange-hash for used
+ * later in the key exchange. No response is sent, as the client should
+ * initiate the diffie-hellman key exchange */
+
+/* Originally from kex.c, generalized for cli/svr mode --mihnea  */
+/* Belongs in common_kex.c where it should be moved after review */
+void recv_msg_kexinit() {
+	
+	unsigned int kexhashbuf_len = 0;
+	unsigned int remote_ident_len = 0;
+	unsigned int local_ident_len = 0;
+
+	TRACE(("<- KEXINIT"))
+	TRACE(("enter recv_msg_kexinit"))
+	
+	if (!ses.kexstate.sentkexinit) {
+		/* we need to send a kex packet */
+		send_msg_kexinit();
+		TRACE(("continue recv_msg_kexinit: sent kexinit"))
+	}
+
+	/* start the kex hash */
+	local_ident_len = strlen(LOCAL_IDENT);
+	remote_ident_len = strlen((char*)ses.remoteident);
+
+	kexhashbuf_len = local_ident_len + remote_ident_len
+		+ ses.transkexinit->len + ses.payload->len
+		+ KEXHASHBUF_MAX_INTS;
+
+	ses.kexhashbuf = buf_new(kexhashbuf_len);
+
+	if (IS_DROPBEAR_CLIENT) {
+
+		/* read the peer's choice of algos */
+		read_kex_algos();
+
+		/* V_C, the client's version string (CR and NL excluded) */
+	    buf_putstring(ses.kexhashbuf,
+			(unsigned char*)LOCAL_IDENT, local_ident_len);
+		/* V_S, the server's version string (CR and NL excluded) */
+	    buf_putstring(ses.kexhashbuf, ses.remoteident, remote_ident_len);
+
+		/* I_C, the payload of the client's SSH_MSG_KEXINIT */
+	    buf_putstring(ses.kexhashbuf,
+			ses.transkexinit->data, ses.transkexinit->len);
+		/* I_S, the payload of the server's SSH_MSG_KEXINIT */
+	    buf_setpos(ses.payload, 0);
+	    buf_putstring(ses.kexhashbuf, ses.payload->data, ses.payload->len);
+
+	} else {
+		/* SERVER */
+
+		/* read the peer's choice of algos */
+		read_kex_algos();
+		/* V_C, the client's version string (CR and NL excluded) */
+	    buf_putstring(ses.kexhashbuf, ses.remoteident, remote_ident_len);
+		/* V_S, the server's version string (CR and NL excluded) */
+	    buf_putstring(ses.kexhashbuf, 
+				(unsigned char*)LOCAL_IDENT, local_ident_len);
+
+		/* I_C, the payload of the client's SSH_MSG_KEXINIT */
+	    buf_setpos(ses.payload, 0);
+	    buf_putstring(ses.kexhashbuf, ses.payload->data, ses.payload->len);
+
+		/* I_S, the payload of the server's SSH_MSG_KEXINIT */
+	    buf_putstring(ses.kexhashbuf,
+			ses.transkexinit->data, ses.transkexinit->len);
+
+		ses.requirenext = SSH_MSG_KEXDH_INIT;
+	}
+
+	buf_free(ses.transkexinit);
+	ses.transkexinit = NULL;
+	/* the rest of ses.kexhashbuf will be done after DH exchange */
+
+	ses.kexstate.recvkexinit = 1;
+
+	TRACE(("leave recv_msg_kexinit"))
+}
+
+/* Initialises and generate one side of the diffie-hellman key exchange values.
+ * See the ietf-secsh-transport draft, section 6, for details */
+/* dh_pub and dh_priv MUST be already initialised */
+void gen_kexdh_vals(mp_int *dh_pub, mp_int *dh_priv) {
+
+	DEF_MP_INT(dh_p);
+	DEF_MP_INT(dh_q);
+	DEF_MP_INT(dh_g);
+
+	TRACE(("enter send_msg_kexdh_reply"))
+	
+	m_mp_init_multi(&dh_g, &dh_p, &dh_q, NULL);
+
+	/* read the prime and generator*/
+	bytes_to_mp(&dh_p, (unsigned char*)dh_p_val, DH_P_LEN);
+	
+	if (mp_set_int(&dh_g, DH_G_VAL) != MP_OKAY) {
+		dropbear_exit("Diffie-Hellman error");
+	}
+
+	/* calculate q = (p-1)/2 */
+	/* dh_priv is just a temp var here */
+	if (mp_sub_d(&dh_p, 1, dh_priv) != MP_OKAY) { 
+		dropbear_exit("Diffie-Hellman error");
+	}
+	if (mp_div_2(dh_priv, &dh_q) != MP_OKAY) {
+		dropbear_exit("Diffie-Hellman error");
+	}
+
+	/* Generate a private portion 0 < dh_priv < dh_q */
+	gen_random_mpint(&dh_q, dh_priv);
+
+	/* f = g^y mod p */
+	if (mp_exptmod(&dh_g, dh_priv, &dh_p, dh_pub) != MP_OKAY) {
+		dropbear_exit("Diffie-Hellman error");
+	}
+	mp_clear_multi(&dh_g, &dh_p, &dh_q, NULL);
+}
+
+/* This function is fairly common between client/server, with some substitution
+ * of dh_e/dh_f etc. Hence these arguments:
+ * dh_pub_us is 'e' for the client, 'f' for the server. dh_pub_them is 
+ * vice-versa. dh_priv is the x/y value corresponding to dh_pub_us */
+void kexdh_comb_key(mp_int *dh_pub_us, mp_int *dh_priv, mp_int *dh_pub_them,
+		sign_key *hostkey) {
+
+	mp_int dh_p;
+	mp_int *dh_e = NULL, *dh_f = NULL;
+	hash_state hs;
+
+	/* read the prime and generator*/
+	mp_init(&dh_p);
+	bytes_to_mp(&dh_p, dh_p_val, DH_P_LEN);
+
+	/* Check that dh_pub_them (dh_e or dh_f) is in the range [1, p-1] */
+	if (mp_cmp(dh_pub_them, &dh_p) != MP_LT 
+			|| mp_cmp_d(dh_pub_them, 0) != MP_GT) {
+		dropbear_exit("Diffie-Hellman error");
+	}
+	
+	/* K = e^y mod p = f^x mod p */
+	ses.dh_K = (mp_int*)m_malloc(sizeof(mp_int));
+	m_mp_init(ses.dh_K);
+	if (mp_exptmod(dh_pub_them, dh_priv, &dh_p, ses.dh_K) != MP_OKAY) {
+		dropbear_exit("Diffie-Hellman error");
+	}
+
+	/* clear no longer needed vars */
+	mp_clear_multi(&dh_p, NULL);
+
+	/* From here on, the code needs to work with the _same_ vars on each side,
+	 * not vice-versaing for client/server */
+	if (IS_DROPBEAR_CLIENT) {
+		dh_e = dh_pub_us;
+		dh_f = dh_pub_them;
+	} else {
+		dh_e = dh_pub_them;
+		dh_f = dh_pub_us;
+	} 
+
+	/* Create the remainder of the hash buffer, to generate the exchange hash */
+	/* K_S, the host key */
+	buf_put_pub_key(ses.kexhashbuf, hostkey, ses.newkeys->algo_hostkey);
+	/* e, exchange value sent by the client */
+	buf_putmpint(ses.kexhashbuf, dh_e);
+	/* f, exchange value sent by the server */
+	buf_putmpint(ses.kexhashbuf, dh_f);
+	/* K, the shared secret */
+	buf_putmpint(ses.kexhashbuf, ses.dh_K);
+
+	/* calculate the hash H to sign */
+	sha1_init(&hs);
+	buf_setpos(ses.kexhashbuf, 0);
+	sha1_process(&hs, buf_getptr(ses.kexhashbuf, ses.kexhashbuf->len),
+			ses.kexhashbuf->len);
+	sha1_done(&hs, ses.hash);
+
+	buf_burn(ses.kexhashbuf);
+	buf_free(ses.kexhashbuf);
+	ses.kexhashbuf = NULL;
+	
+	/* first time around, we set the session_id to H */
+	if (ses.session_id == NULL) {
+		/* create the session_id, this never needs freeing */
+		ses.session_id = (unsigned char*)m_malloc(SHA1_HASH_SIZE);
+		memcpy(ses.session_id, ses.hash, SHA1_HASH_SIZE);
+	}
+}
+
+/* read the other side's algo list. buf_match_algo is a callback to match
+ * algos for the client or server. */
+static void read_kex_algos() {
+
+	/* for asymmetry */
+	algo_type * c2s_hash_algo = NULL;
+	algo_type * s2c_hash_algo = NULL;
+	algo_type * c2s_cipher_algo = NULL;
+	algo_type * s2c_cipher_algo = NULL;
+	algo_type * c2s_comp_algo = NULL;
+	algo_type * s2c_comp_algo = NULL;
+	/* the generic one */
+	algo_type * algo = NULL;
+
+	/* which algo couldn't match */
+	char * erralgo = NULL;
+
+	int goodguess = 0;
+	int allgood = 1; /* we AND this with each goodguess and see if its still
+						true after */
+
+	buf_incrpos(ses.payload, 16); /* start after the cookie */
+
+	ses.newkeys = (struct key_context*)m_malloc(sizeof(struct key_context));
+
+	/* kex_algorithms */
+	algo = ses.buf_match_algo(ses.payload, sshkex, &goodguess);
+	allgood &= goodguess;
+	if (algo == NULL) {
+		erralgo = "kex";
+		goto error;
+	}
+	TRACE(("kex algo %s", algo->name))
+	ses.newkeys->algo_kex = algo->val;
+
+	/* server_host_key_algorithms */
+	algo = ses.buf_match_algo(ses.payload, sshhostkey, &goodguess);
+	allgood &= goodguess;
+	if (algo == NULL) {
+		erralgo = "hostkey";
+		goto error;
+	}
+	TRACE(("hostkey algo %s", algo->name))
+	ses.newkeys->algo_hostkey = algo->val;
+
+	/* encryption_algorithms_client_to_server */
+	c2s_cipher_algo = ses.buf_match_algo(ses.payload, sshciphers, &goodguess);
+	if (c2s_cipher_algo == NULL) {
+		erralgo = "enc c->s";
+		goto error;
+	}
+	TRACE(("enc c2s is  %s", c2s_cipher_algo->name))
+
+	/* encryption_algorithms_server_to_client */
+	s2c_cipher_algo = ses.buf_match_algo(ses.payload, sshciphers, &goodguess);
+	if (s2c_cipher_algo == NULL) {
+		erralgo = "enc s->c";
+		goto error;
+	}
+	TRACE(("enc s2c is  %s", s2c_cipher_algo->name))
+
+	/* mac_algorithms_client_to_server */
+	c2s_hash_algo = ses.buf_match_algo(ses.payload, sshhashes, &goodguess);
+	if (c2s_hash_algo == NULL) {
+		erralgo = "mac c->s";
+		goto error;
+	}
+	TRACE(("hash c2s is  %s", c2s_hash_algo->name))
+
+	/* mac_algorithms_server_to_client */
+	s2c_hash_algo = ses.buf_match_algo(ses.payload, sshhashes, &goodguess);
+	if (s2c_hash_algo == NULL) {
+		erralgo = "mac s->c";
+		goto error;
+	}
+	TRACE(("hash s2c is  %s", s2c_hash_algo->name))
+
+	/* compression_algorithms_client_to_server */
+	c2s_comp_algo = ses.buf_match_algo(ses.payload, sshcompress, &goodguess);
+	if (c2s_comp_algo == NULL) {
+		erralgo = "comp c->s";
+		goto error;
+	}
+	TRACE(("hash c2s is  %s", c2s_comp_algo->name))
+
+	/* compression_algorithms_server_to_client */
+	s2c_comp_algo = ses.buf_match_algo(ses.payload, sshcompress, &goodguess);
+	if (s2c_comp_algo == NULL) {
+		erralgo = "comp s->c";
+		goto error;
+	}
+	TRACE(("hash s2c is  %s", s2c_comp_algo->name))
+
+	/* languages_client_to_server */
+	buf_eatstring(ses.payload);
+
+	/* languages_server_to_client */
+	buf_eatstring(ses.payload);
+
+	/* first_kex_packet_follows */
+	if (buf_getbool(ses.payload)) {
+		ses.kexstate.firstfollows = 1;
+		/* if the guess wasn't good, we ignore the packet sent */
+		if (!allgood) {
+			ses.ignorenext = 1;
+		}
+	}
+
+	/* Handle the asymmetry */
+	if (IS_DROPBEAR_CLIENT) {
+		ses.newkeys->recv_algo_crypt = 
+			(struct dropbear_cipher*)s2c_cipher_algo->data;
+		ses.newkeys->trans_algo_crypt = 
+			(struct dropbear_cipher*)c2s_cipher_algo->data;
+		ses.newkeys->recv_algo_mac = 
+			(struct dropbear_hash*)s2c_hash_algo->data;
+		ses.newkeys->trans_algo_mac = 
+			(struct dropbear_hash*)c2s_hash_algo->data;
+		ses.newkeys->recv_algo_comp = s2c_comp_algo->val;
+		ses.newkeys->trans_algo_comp = c2s_comp_algo->val;
+	} else {
+		/* SERVER */
+		ses.newkeys->recv_algo_crypt = 
+			(struct dropbear_cipher*)c2s_cipher_algo->data;
+		ses.newkeys->trans_algo_crypt = 
+			(struct dropbear_cipher*)s2c_cipher_algo->data;
+		ses.newkeys->recv_algo_mac = 
+			(struct dropbear_hash*)c2s_hash_algo->data;
+		ses.newkeys->trans_algo_mac = 
+			(struct dropbear_hash*)s2c_hash_algo->data;
+		ses.newkeys->recv_algo_comp = c2s_comp_algo->val;
+		ses.newkeys->trans_algo_comp = s2c_comp_algo->val;
+	}
+
+	/* reserved for future extensions */
+	buf_getint(ses.payload);
+	return;
+
+error:
+	dropbear_exit("no matching algo %s", erralgo);
+}
diff --git a/common-runopts.c b/common-runopts.c
new file mode 100644
index 0000000000000000000000000000000000000000..2de036e33badab97b6b81072d6ee8c695991e377
--- /dev/null
+++ b/common-runopts.c
@@ -0,0 +1,57 @@
+/*
+ * 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. */
+
+#include "includes.h"
+#include "runopts.h"
+#include "signkey.h"
+#include "buffer.h"
+#include "dbutil.h"
+#include "auth.h"
+
+runopts opts; /* GLOBAL */
+
+/* returns success or failure, and the keytype in *type. If we want
+ * to restrict the type, type can contain a type to return */
+int readhostkey(const char * filename, sign_key * hostkey, int *type) {
+
+	int ret = DROPBEAR_FAILURE;
+	buffer *buf;
+
+	buf = buf_new(MAX_PRIVKEY_SIZE);
+
+	if (buf_readfile(buf, filename) == DROPBEAR_FAILURE) {
+		goto out;
+	}
+	buf_setpos(buf, 0);
+	if (buf_get_priv_key(buf, hostkey, type) == DROPBEAR_FAILURE) {
+		goto out;
+	}
+
+	ret = DROPBEAR_SUCCESS;
+out:
+
+	buf_burn(buf);
+	buf_free(buf);
+	return ret;
+}
diff --git a/common-session.c b/common-session.c
new file mode 100644
index 0000000000000000000000000000000000000000..4c15391e3599738eff26554ceb30c0361b189fc3
--- /dev/null
+++ b/common-session.c
@@ -0,0 +1,373 @@
+/*
+ * 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. */
+
+#include "includes.h"
+#include "session.h"
+#include "dbutil.h"
+#include "packet.h"
+#include "algo.h"
+#include "buffer.h"
+#include "dss.h"
+#include "ssh.h"
+#include "random.h"
+#include "kex.h"
+#include "channel.h"
+#include "atomicio.h"
+
+static void checktimeouts();
+static int ident_readln(int fd, char* buf, int count);
+
+struct sshsession ses; /* GLOBAL */
+
+/* need to know if the session struct has been initialised, this way isn't the
+ * cleanest, but works OK */
+int sessinitdone = 0; /* GLOBAL */
+
+/* this is set when we get SIGINT or SIGTERM, the handler is in main.c */
+int exitflag = 0; /* GLOBAL */
+
+
+
+/* called only at the start of a session, set up initial state */
+void common_session_init(int sock, char* remotehost) {
+
+	TRACE(("enter session_init"))
+
+	ses.remotehost = remotehost;
+
+	ses.sock = sock;
+	ses.maxfd = sock;
+
+	ses.connecttimeout = 0;
+	
+	kexfirstinitialise(); /* initialise the kex state */
+
+	ses.writepayload = buf_new(MAX_TRANS_PAYLOAD_LEN);
+	ses.transseq = 0;
+
+	ses.readbuf = NULL;
+	ses.decryptreadbuf = NULL;
+	ses.payload = NULL;
+	ses.recvseq = 0;
+
+	initqueue(&ses.writequeue);
+
+	ses.requirenext = SSH_MSG_KEXINIT;
+	ses.dataallowed = 0; /* don't send data yet, we'll wait until after kex */
+	ses.ignorenext = 0;
+	ses.lastpacket = 0;
+
+	/* set all the algos to none */
+	ses.keys = (struct key_context*)m_malloc(sizeof(struct key_context));
+	ses.newkeys = NULL;
+	ses.keys->recv_algo_crypt = &dropbear_nocipher;
+	ses.keys->trans_algo_crypt = &dropbear_nocipher;
+	
+	ses.keys->recv_algo_mac = &dropbear_nohash;
+	ses.keys->trans_algo_mac = &dropbear_nohash;
+
+	ses.keys->algo_kex = -1;
+	ses.keys->algo_hostkey = -1;
+	ses.keys->recv_algo_comp = DROPBEAR_COMP_NONE;
+	ses.keys->trans_algo_comp = DROPBEAR_COMP_NONE;
+
+#ifndef DISABLE_ZLIB
+	ses.keys->recv_zstream = NULL;
+	ses.keys->trans_zstream = NULL;
+#endif
+
+	/* key exchange buffers */
+	ses.session_id = NULL;
+	ses.kexhashbuf = NULL;
+	ses.transkexinit = NULL;
+	ses.dh_K = NULL;
+	ses.remoteident = NULL;
+
+	ses.chantypes = NULL;
+
+	ses.allowprivport = 0;
+
+
+	TRACE(("leave session_init"))
+}
+
+void session_loop(void(*loophandler)()) {
+
+	fd_set readfd, writefd;
+	struct timeval timeout;
+	int val;
+
+	/* main loop, select()s for all sockets in use */
+	for(;;) {
+
+		timeout.tv_sec = SELECT_TIMEOUT;
+		timeout.tv_usec = 0;
+		FD_ZERO(&writefd);
+		FD_ZERO(&readfd);
+		dropbear_assert(ses.payload == NULL);
+		if (ses.sock != -1) {
+			FD_SET(ses.sock, &readfd);
+			if (!isempty(&ses.writequeue)) {
+				FD_SET(ses.sock, &writefd);
+			}
+		}
+
+		/* set up for channels which require reading/writing */
+		if (ses.dataallowed) {
+			setchannelfds(&readfd, &writefd);
+		}
+		val = select(ses.maxfd+1, &readfd, &writefd, NULL, &timeout);
+
+		if (exitflag) {
+			dropbear_exit("Terminated by signal");
+		}
+		
+		if (val < 0) {
+			if (errno == EINTR) {
+				/* This must happen even if we've been interrupted, so that
+				 * changed signal-handler vars can take effect etc */
+				if (loophandler) {
+					loophandler();
+				}
+				continue;
+			} else {
+				dropbear_exit("Error in select");
+			}
+		}
+
+		/* check for auth timeout, rekeying required etc */
+		checktimeouts();
+		
+		if (val == 0) {
+			/* timeout */
+			TRACE(("select timeout"))
+			continue;
+		}
+
+		/* process session socket's incoming/outgoing data */
+		if (ses.sock != -1) {
+			if (FD_ISSET(ses.sock, &writefd) && !isempty(&ses.writequeue)) {
+				write_packet();
+			}
+
+			if (FD_ISSET(ses.sock, &readfd)) {
+				read_packet();
+			}
+			
+			/* Process the decrypted packet. After this, the read buffer
+			 * will be ready for a new packet */
+			if (ses.payload != NULL) {
+				process_packet();
+			}
+		}
+
+		/* process pipes etc for the channels, ses.dataallowed == 0
+		 * during rekeying ) */
+		if (ses.dataallowed) {
+			channelio(&readfd, &writefd);
+		}
+
+		if (loophandler) {
+			loophandler();
+		}
+
+	} /* for(;;) */
+	
+	/* Not reached */
+}
+
+/* clean up a session on exit */
+void common_session_cleanup() {
+	
+	TRACE(("enter session_cleanup"))
+	
+	/* we can't cleanup if we don't know the session state */
+	if (!sessinitdone) {
+		TRACE(("leave session_cleanup: !sessinitdone"))
+		return;
+	}
+	
+	m_free(ses.session_id);
+	m_burn(ses.keys, sizeof(struct key_context));
+	m_free(ses.keys);
+
+	chancleanup();
+
+	TRACE(("leave session_cleanup"))
+}
+
+
+void session_identification() {
+
+	/* max length of 255 chars */
+	char linebuf[256];
+	int len = 0;
+	char done = 0;
+	int i;
+
+	/* write our version string, this blocks */
+	if (atomicio(write, ses.sock, LOCAL_IDENT "\r\n",
+				strlen(LOCAL_IDENT "\r\n")) == DROPBEAR_FAILURE) {
+		dropbear_exit("Error writing ident string");
+	}
+
+    /* If they send more than 50 lines, something is wrong */
+	for (i = 0; i < 50; i++) {
+		len = ident_readln(ses.sock, linebuf, sizeof(linebuf));
+
+		if (len < 0 && errno != EINTR) {
+			/* It failed */
+			break;
+		}
+
+		if (len >= 4 && memcmp(linebuf, "SSH-", 4) == 0) {
+			/* start of line matches */
+			done = 1;
+			break;
+		}
+	}
+
+	if (!done) {
+		TRACE(("err: %s for '%s'\n", strerror(errno), linebuf))
+		dropbear_exit("Failed to get remote version");
+	} else {
+		/* linebuf is already null terminated */
+		ses.remoteident = m_malloc(len);
+		memcpy(ses.remoteident, linebuf, len);
+	}
+
+    /* Shall assume that 2.x will be backwards compatible. */
+    if (strncmp(ses.remoteident, "SSH-2.", 6) != 0
+            && strncmp(ses.remoteident, "SSH-1.99-", 9) != 0) {
+        dropbear_exit("Incompatible remote version '%s'", ses.remoteident);
+    }
+
+	TRACE(("remoteident: %s", ses.remoteident))
+
+}
+
+/* returns the length including null-terminating zero on success,
+ * or -1 on failure */
+static int ident_readln(int fd, char* buf, int count) {
+	
+	char in;
+	int pos = 0;
+	int num = 0;
+	fd_set fds;
+	struct timeval timeout;
+
+	TRACE(("enter ident_readln"))
+
+	if (count < 1) {
+		return -1;
+	}
+
+	FD_ZERO(&fds);
+
+	/* select since it's a non-blocking fd */
+	
+	/* leave space to null-terminate */
+	while (pos < count-1) {
+
+		FD_SET(fd, &fds);
+
+		timeout.tv_sec = 1;
+		timeout.tv_usec = 0;
+		if (select(fd+1, &fds, NULL, NULL, &timeout) < 0) {
+			if (errno == EINTR) {
+				continue;
+			}
+			TRACE(("leave ident_readln: select error"))
+			return -1;
+		}
+
+		checktimeouts();
+		
+		/* Have to go one byte at a time, since we don't want to read past
+		 * the end, and have to somehow shove bytes back into the normal
+		 * packet reader */
+		if (FD_ISSET(fd, &fds)) {
+			num = read(fd, &in, 1);
+			/* a "\n" is a newline, "\r" we want to read in and keep going
+			 * so that it won't be read as part of the next line */
+			if (num < 0) {
+				/* error */
+				if (errno == EINTR) {
+					continue; /* not a real error */
+				}
+				TRACE(("leave ident_readln: read error"))
+				return -1;
+			}
+			if (num == 0) {
+				/* EOF */
+				TRACE(("leave ident_readln: EOF"))
+				return -1;
+			}
+			if (in == '\n') {
+				/* end of ident string */
+				break;
+			}
+			/* we don't want to include '\r's */
+			if (in != '\r') {
+				buf[pos] = in;
+				pos++;
+			}
+		}
+	}
+
+	buf[pos] = '\0';
+	TRACE(("leave ident_readln: return %d", pos+1))
+	return pos+1;
+}
+
+/* Check all timeouts which are required. Currently these are the time for
+ * user authentication, and the automatic rekeying. */
+static void checktimeouts() {
+
+	struct timeval tv;
+	long secs;
+
+	if (gettimeofday(&tv, 0) < 0) {
+		dropbear_exit("Error getting time");
+	}
+
+	secs = tv.tv_sec;
+	
+	if (ses.connecttimeout != 0 && secs > ses.connecttimeout) {
+			dropbear_close("Timeout before auth");
+	}
+
+	/* we can't rekey if we haven't done remote ident exchange yet */
+	if (ses.remoteident == NULL) {
+		return;
+	}
+
+	if (!ses.kexstate.sentkexinit
+			&& (secs - ses.kexstate.lastkextime >= KEX_REKEY_TIMEOUT
+			|| ses.kexstate.datarecv+ses.kexstate.datatrans >= KEX_REKEY_DATA)){
+		TRACE(("rekeying after timeout or max data reached"))
+		send_msg_kexinit();
+	}
+}
+
diff --git a/compat.c b/compat.c
new file mode 100644
index 0000000000000000000000000000000000000000..7e0c1ac765383d115ad1cbe6627d4c5d407b4802
--- /dev/null
+++ b/compat.c
@@ -0,0 +1,281 @@
+/*
+ * 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.
+ *
+ * strlcat() is copyright as follows:
+ * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+ * 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. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``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 AUTHOR 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.
+ *
+ * daemon() and getusershell() is copyright as follows:
+ *
+ * Copyright (c) 1990, 1993
+ *      The Regents of the University of California.  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 University 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 REGENTS 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 REGENTS 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.
+*
+* Modifications for Dropbear to getusershell() are by Paul Marinceu
+*/
+
+#include "includes.h"
+
+#ifndef HAVE_GETUSERSHELL
+static char **curshell, **shells, *strings;
+static char **initshells();
+#endif
+
+#ifndef HAVE_STRLCPY
+/* Implemented by matt as specified in freebsd 4.7 manpage.
+ * We don't require great speed, is simply for use with sshpty code */
+size_t strlcpy(char *dst, const char *src, size_t size) {
+
+	size_t i;
+
+	/* this is undefined, though size==0 -> return 0 */
+	if (size < 1) {
+		return 0;
+	}
+
+	for (i = 0; i < size-1; i++) {
+		if (src[i] == '\0') {
+			break;
+		} else {
+			dst[i] = src[i];
+		}
+	}
+
+	dst[i] = '\0';
+	return strlen(src);
+
+}
+#endif /* HAVE_STRLCPY */
+
+#ifndef HAVE_STRLCAT
+/* taken from openbsd-compat for OpenSSH 3.6.1p1 */
+/* "$OpenBSD: strlcat.c,v 1.8 2001/05/13 15:40:15 deraadt Exp $"
+ *
+ * Appends src to string dst of size siz (unlike strncat, siz is the
+ * full size of dst, not space left).  At most siz-1 characters
+ * will be copied.  Always NUL terminates (unless siz <= strlen(dst)).
+ * Returns strlen(src) + MIN(siz, strlen(initial dst)).
+ * If retval >= siz, truncation occurred.
+ */
+	size_t
+strlcat(dst, src, siz)
+	char *dst;
+	const char *src;
+	size_t siz;
+{
+	register char *d = dst;
+	register const char *s = src;
+	register size_t n = siz;
+	size_t dlen;
+
+	/* Find the end of dst and adjust bytes left but don't go past end */
+	while (n-- != 0 && *d != '\0')
+		d++;
+	dlen = d - dst;
+	n = siz - dlen;
+
+	if (n == 0)
+		return(dlen + strlen(s));
+	while (*s != '\0') {
+		if (n != 1) {
+			*d++ = *s;
+			n--;
+		}
+		s++;
+	}
+	*d = '\0';
+
+	return(dlen + (s - src));	/* count does not include NUL */
+}
+#endif /* HAVE_STRLCAT */
+
+#ifndef HAVE_DAEMON
+/* From NetBSD - daemonise a process */
+
+int daemon(int nochdir, int noclose) {
+
+	int fd;
+
+	switch (fork()) {
+		case -1:
+			return (-1);
+		case 0:
+			break;
+		default:
+			_exit(0);
+	}
+
+	if (setsid() == -1)
+		return -1;
+
+	if (!nochdir)
+		(void)chdir("/");
+
+	if (!noclose && (fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) {
+		(void)dup2(fd, STDIN_FILENO);
+		(void)dup2(fd, STDOUT_FILENO);
+		(void)dup2(fd, STDERR_FILENO);
+		if (fd > STDERR_FILENO)
+			(void)close(fd);
+	}
+	return 0;
+}
+#endif /* HAVE_DAEMON */
+
+#ifndef HAVE_BASENAME
+
+char *basename(const char *path) {
+
+	char *foo = strrchr(path, '/');
+	return ++foo;
+}
+
+#endif /* HAVE_BASENAME */
+
+#ifndef HAVE_GETUSERSHELL
+
+/*
+ * Get a list of shells from /etc/shells, if it exists.
+ */
+char * getusershell() {
+	char *ret;
+
+	if (curshell == NULL)
+		curshell = initshells();
+	ret = *curshell;
+	if (ret != NULL)
+		curshell++;
+	return (ret);
+}
+
+void endusershell() {
+
+	if (shells != NULL)
+		free(shells);
+	shells = NULL;
+	if (strings != NULL)
+		free(strings);
+	strings = NULL;
+	curshell = NULL;
+}
+
+void setusershell() {
+	curshell = initshells();
+}
+
+static char **initshells() {
+	/* don't touch this list. */
+	const char *okshells[] = { "/bin/sh", "/bin/csh", NULL };
+	register char **sp, *cp;
+	register FILE *fp;
+	struct stat statb;
+	int flen;
+
+	if (shells != NULL)
+		free(shells);
+	shells = NULL;
+	if (strings != NULL)
+		free(strings);
+	strings = NULL;
+	if ((fp = fopen("/etc/shells", "rc")) == NULL)
+		return (char **) okshells;
+	if (fstat(fileno(fp), &statb) == -1) {
+		(void)fclose(fp);
+		return (char **) okshells;
+	}
+	if ((strings = malloc((u_int)statb.st_size + 1)) == NULL) {
+		(void)fclose(fp);
+		return (char **) okshells;
+	}
+	shells = calloc((unsigned)statb.st_size / 3, sizeof (char *));
+	if (shells == NULL) {
+		(void)fclose(fp);
+		free(strings);
+		strings = NULL;
+		return (char **) okshells;
+	}
+	sp = shells;
+	cp = strings;
+	flen = statb.st_size;
+	while (fgets(cp, flen - (cp - strings), fp) != NULL) {
+		while (*cp != '#' && *cp != '/' && *cp != '\0')
+			cp++;
+		if (*cp == '#' || *cp == '\0')
+			continue;
+		*sp++ = cp;
+		while (!isspace(*cp) && *cp != '#' && *cp != '\0')
+			cp++;
+		*cp++ = '\0';
+	}
+	*sp = NULL;
+	(void)fclose(fp);
+	return (shells);
+}
+
+#endif /* HAVE_GETUSERSHELL */
diff --git a/compat.h b/compat.h
new file mode 100644
index 0000000000000000000000000000000000000000..1ab344fe7e20a18b581333290855bd2097cf381c
--- /dev/null
+++ b/compat.h
@@ -0,0 +1,56 @@
+/*
+ * 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. */
+
+#ifndef _COMPAT_H_
+#define _COMPAT_H_
+
+#include "includes.h"
+
+#ifndef HAVE_STRLCPY
+size_t strlcpy(char *dst, const char *src, size_t size);
+#endif
+
+#ifndef HAVE_STRLCAT
+size_t strlcat(char *dst, const char *src, size_t siz);
+#endif
+
+#ifndef HAVE_DAEMON
+int daemon(int nochdir, int noclose);
+#endif
+
+#ifndef HAVE_BASENAME
+char *basename(const char* path);
+#endif
+
+#ifndef HAVE_GETUSERSHELL
+char *getusershell();
+void setusershell();
+void endusershell();
+#endif
+
+#ifndef _PATH_DEVNULL
+#define _PATH_DEVNULL "/dev/null"
+#endif
+
+#endif /* _COMPAT_H_ */
diff --git a/config.guess b/config.guess
new file mode 100644
index 0000000000000000000000000000000000000000..9e55e1a073b3e3f20cd976a468a8249df92520ba
--- /dev/null
+++ b/config.guess
@@ -0,0 +1,1391 @@
+#! /bin/sh
+# Attempt to guess a canonical system name.
+#   Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+#   2000, 2001, 2002, 2003 Free Software Foundation, Inc.
+
+timestamp='2003-05-19'
+
+# This file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# Originally written by Per Bothner <per@bothner.com>.
+# Please send patches to <config-patches@gnu.org>.  Submit a context
+# diff and a properly formatted ChangeLog entry.
+#
+# This script attempts to guess a canonical system name similar to
+# config.sub.  If it succeeds, it prints the system name on stdout, and
+# exits with 0.  Otherwise, it exits with 1.
+#
+# The plan is that this can be called by configure scripts if you
+# don't specify an explicit build system type.
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION]
+
+Output the configuration name of the system \`$me' is run on.
+
+Operation modes:
+  -h, --help         print this help, then exit
+  -t, --time-stamp   print date of last modification, then exit
+  -v, --version      print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.guess ($timestamp)
+
+Originally written by Per Bothner.
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
+Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions.  There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+  case $1 in
+    --time-stamp | --time* | -t )
+       echo "$timestamp" ; exit 0 ;;
+    --version | -v )
+       echo "$version" ; exit 0 ;;
+    --help | --h* | -h )
+       echo "$usage"; exit 0 ;;
+    -- )     # Stop option processing
+       shift; break ;;
+    - )	# Use stdin as input.
+       break ;;
+    -* )
+       echo "$me: invalid option $1$help" >&2
+       exit 1 ;;
+    * )
+       break ;;
+  esac
+done
+
+if test $# != 0; then
+  echo "$me: too many arguments$help" >&2
+  exit 1
+fi
+
+trap 'exit 1' 1 2 15
+
+# CC_FOR_BUILD -- compiler used by this script. Note that the use of a
+# compiler to aid in system detection is discouraged as it requires
+# temporary files to be created and, as you can see below, it is a
+# headache to deal with in a portable fashion.
+
+# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still
+# use `HOST_CC' if defined, but it is deprecated.
+
+# Portable tmp directory creation inspired by the Autoconf team.
+
+set_cc_for_build='
+trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ;
+trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ;
+: ${TMPDIR=/tmp} ;
+ { tmp=`(umask 077 && mktemp -d -q "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } ||
+ { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } ||
+ { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ;
+dummy=$tmp/dummy ;
+tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ;
+case $CC_FOR_BUILD,$HOST_CC,$CC in
+ ,,)    echo "int x;" > $dummy.c ;
+	for c in cc gcc c89 c99 ; do
+	  if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then
+	     CC_FOR_BUILD="$c"; break ;
+	  fi ;
+	done ;
+	if test x"$CC_FOR_BUILD" = x ; then
+	  CC_FOR_BUILD=no_compiler_found ;
+	fi
+	;;
+ ,,*)   CC_FOR_BUILD=$CC ;;
+ ,*,*)  CC_FOR_BUILD=$HOST_CC ;;
+esac ;'
+
+# This is needed to find uname on a Pyramid OSx when run in the BSD universe.
+# (ghazi@noc.rutgers.edu 1994-08-24)
+if (test -f /.attbin/uname) >/dev/null 2>&1 ; then
+	PATH=$PATH:/.attbin ; export PATH
+fi
+
+UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown
+UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
+UNAME_SYSTEM=`(uname -s) 2>/dev/null`  || UNAME_SYSTEM=unknown
+UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
+
+# Note: order is significant - the case branches are not exclusive.
+
+case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
+    *:NetBSD:*:*)
+	# NetBSD (nbsd) targets should (where applicable) match one or
+	# more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*,
+	# *-*-netbsdecoff* and *-*-netbsd*.  For targets that recently
+	# switched to ELF, *-*-netbsd* would select the old
+	# object file format.  This provides both forward
+	# compatibility and a consistent mechanism for selecting the
+	# object file format.
+	#
+	# Note: NetBSD doesn't particularly care about the vendor
+	# portion of the name.  We always set it to "unknown".
+	sysctl="sysctl -n hw.machine_arch"
+	UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \
+	    /usr/sbin/$sysctl 2>/dev/null || echo unknown)`
+	case "${UNAME_MACHINE_ARCH}" in
+	    armeb) machine=armeb-unknown ;;
+	    arm*) machine=arm-unknown ;;
+	    sh3el) machine=shl-unknown ;;
+	    sh3eb) machine=sh-unknown ;;
+	    *) machine=${UNAME_MACHINE_ARCH}-unknown ;;
+	esac
+	# The Operating System including object format, if it has switched
+	# to ELF recently, or will in the future.
+	case "${UNAME_MACHINE_ARCH}" in
+	    arm*|i386|m68k|ns32k|sh3*|sparc|vax)
+		eval $set_cc_for_build
+		if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
+			| grep __ELF__ >/dev/null
+		then
+		    # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout).
+		    # Return netbsd for either.  FIX?
+		    os=netbsd
+		else
+		    os=netbsdelf
+		fi
+		;;
+	    *)
+	        os=netbsd
+		;;
+	esac
+	# The OS release
+	# Debian GNU/NetBSD machines have a different userland, and
+	# thus, need a distinct triplet. However, they do not need
+	# kernel version information, so it can be replaced with a
+	# suitable tag, in the style of linux-gnu.
+	case "${UNAME_VERSION}" in
+	    Debian*)
+		release='-gnu'
+		;;
+	    *)
+		release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
+		;;
+	esac
+	# Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
+	# contains redundant information, the shorter form:
+	# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
+	echo "${machine}-${os}${release}"
+	exit 0 ;;
+    amiga:OpenBSD:*:*)
+	echo m68k-unknown-openbsd${UNAME_RELEASE}
+	exit 0 ;;
+    arc:OpenBSD:*:*)
+	echo mipsel-unknown-openbsd${UNAME_RELEASE}
+	exit 0 ;;
+    hp300:OpenBSD:*:*)
+	echo m68k-unknown-openbsd${UNAME_RELEASE}
+	exit 0 ;;
+    mac68k:OpenBSD:*:*)
+	echo m68k-unknown-openbsd${UNAME_RELEASE}
+	exit 0 ;;
+    macppc:OpenBSD:*:*)
+	echo powerpc-unknown-openbsd${UNAME_RELEASE}
+	exit 0 ;;
+    mvme68k:OpenBSD:*:*)
+	echo m68k-unknown-openbsd${UNAME_RELEASE}
+	exit 0 ;;
+    mvme88k:OpenBSD:*:*)
+	echo m88k-unknown-openbsd${UNAME_RELEASE}
+	exit 0 ;;
+    mvmeppc:OpenBSD:*:*)
+	echo powerpc-unknown-openbsd${UNAME_RELEASE}
+	exit 0 ;;
+    pmax:OpenBSD:*:*)
+	echo mipsel-unknown-openbsd${UNAME_RELEASE}
+	exit 0 ;;
+    sgi:OpenBSD:*:*)
+	echo mipseb-unknown-openbsd${UNAME_RELEASE}
+	exit 0 ;;
+    sun3:OpenBSD:*:*)
+	echo m68k-unknown-openbsd${UNAME_RELEASE}
+	exit 0 ;;
+    wgrisc:OpenBSD:*:*)
+	echo mipsel-unknown-openbsd${UNAME_RELEASE}
+	exit 0 ;;
+    *:OpenBSD:*:*)
+	echo ${UNAME_MACHINE}-unknown-openbsd${UNAME_RELEASE}
+	exit 0 ;;
+    alpha:OSF1:*:*)
+	if test $UNAME_RELEASE = "V4.0"; then
+		UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
+	fi
+	# According to Compaq, /usr/sbin/psrinfo has been available on
+	# OSF/1 and Tru64 systems produced since 1995.  I hope that
+	# covers most systems running today.  This code pipes the CPU
+	# types through head -n 1, so we only detect the type of CPU 0.
+	ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^  The alpha \(.*\) processor.*$/\1/p' | head -n 1`
+	case "$ALPHA_CPU_TYPE" in
+	    "EV4 (21064)")
+		UNAME_MACHINE="alpha" ;;
+	    "EV4.5 (21064)")
+		UNAME_MACHINE="alpha" ;;
+	    "LCA4 (21066/21068)")
+		UNAME_MACHINE="alpha" ;;
+	    "EV5 (21164)")
+		UNAME_MACHINE="alphaev5" ;;
+	    "EV5.6 (21164A)")
+		UNAME_MACHINE="alphaev56" ;;
+	    "EV5.6 (21164PC)")
+		UNAME_MACHINE="alphapca56" ;;
+	    "EV5.7 (21164PC)")
+		UNAME_MACHINE="alphapca57" ;;
+	    "EV6 (21264)")
+		UNAME_MACHINE="alphaev6" ;;
+	    "EV6.7 (21264A)")
+		UNAME_MACHINE="alphaev67" ;;
+	    "EV6.8CB (21264C)")
+		UNAME_MACHINE="alphaev68" ;;
+	    "EV6.8AL (21264B)")
+		UNAME_MACHINE="alphaev68" ;;
+	    "EV6.8CX (21264D)")
+		UNAME_MACHINE="alphaev68" ;;
+	    "EV6.9A (21264/EV69A)")
+		UNAME_MACHINE="alphaev69" ;;
+	    "EV7 (21364)")
+		UNAME_MACHINE="alphaev7" ;;
+	    "EV7.9 (21364A)")
+		UNAME_MACHINE="alphaev79" ;;
+	esac
+	# A Vn.n version is a released version.
+	# A Tn.n version is a released field test version.
+	# A Xn.n version is an unreleased experimental baselevel.
+	# 1.2 uses "1.2" for uname -r.
+	echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[VTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+	exit 0 ;;
+    Alpha\ *:Windows_NT*:*)
+	# How do we know it's Interix rather than the generic POSIX subsystem?
+	# Should we change UNAME_MACHINE based on the output of uname instead
+	# of the specific Alpha model?
+	echo alpha-pc-interix
+	exit 0 ;;
+    21064:Windows_NT:50:3)
+	echo alpha-dec-winnt3.5
+	exit 0 ;;
+    Amiga*:UNIX_System_V:4.0:*)
+	echo m68k-unknown-sysv4
+	exit 0;;
+    *:[Aa]miga[Oo][Ss]:*:*)
+	echo ${UNAME_MACHINE}-unknown-amigaos
+	exit 0 ;;
+    *:[Mm]orph[Oo][Ss]:*:*)
+	echo ${UNAME_MACHINE}-unknown-morphos
+	exit 0 ;;
+    *:OS/390:*:*)
+	echo i370-ibm-openedition
+	exit 0 ;;
+    arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
+	echo arm-acorn-riscix${UNAME_RELEASE}
+	exit 0;;
+    SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)
+	echo hppa1.1-hitachi-hiuxmpp
+	exit 0;;
+    Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*)
+	# akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE.
+	if test "`(/bin/universe) 2>/dev/null`" = att ; then
+		echo pyramid-pyramid-sysv3
+	else
+		echo pyramid-pyramid-bsd
+	fi
+	exit 0 ;;
+    NILE*:*:*:dcosx)
+	echo pyramid-pyramid-svr4
+	exit 0 ;;
+    DRS?6000:UNIX_SV:4.2*:7*)
+	case `/usr/bin/uname -p` in
+	    sparc) echo sparc-icl-nx7 && exit 0 ;;
+	esac ;;
+    sun4H:SunOS:5.*:*)
+	echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+	exit 0 ;;
+    sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
+	echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+	exit 0 ;;
+    i86pc:SunOS:5.*:*)
+	echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+	exit 0 ;;
+    sun4*:SunOS:6*:*)
+	# According to config.sub, this is the proper way to canonicalize
+	# SunOS6.  Hard to guess exactly what SunOS6 will be like, but
+	# it's likely to be more like Solaris than SunOS4.
+	echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+	exit 0 ;;
+    sun4*:SunOS:*:*)
+	case "`/usr/bin/arch -k`" in
+	    Series*|S4*)
+		UNAME_RELEASE=`uname -v`
+		;;
+	esac
+	# Japanese Language versions have a version number like `4.1.3-JL'.
+	echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'`
+	exit 0 ;;
+    sun3*:SunOS:*:*)
+	echo m68k-sun-sunos${UNAME_RELEASE}
+	exit 0 ;;
+    sun*:*:4.2BSD:*)
+	UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`
+	test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3
+	case "`/bin/arch`" in
+	    sun3)
+		echo m68k-sun-sunos${UNAME_RELEASE}
+		;;
+	    sun4)
+		echo sparc-sun-sunos${UNAME_RELEASE}
+		;;
+	esac
+	exit 0 ;;
+    aushp:SunOS:*:*)
+	echo sparc-auspex-sunos${UNAME_RELEASE}
+	exit 0 ;;
+    # The situation for MiNT is a little confusing.  The machine name
+    # can be virtually everything (everything which is not
+    # "atarist" or "atariste" at least should have a processor
+    # > m68000).  The system name ranges from "MiNT" over "FreeMiNT"
+    # to the lowercase version "mint" (or "freemint").  Finally
+    # the system name "TOS" denotes a system which is actually not
+    # MiNT.  But MiNT is downward compatible to TOS, so this should
+    # be no problem.
+    atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)
+        echo m68k-atari-mint${UNAME_RELEASE}
+	exit 0 ;;
+    atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
+	echo m68k-atari-mint${UNAME_RELEASE}
+        exit 0 ;;
+    *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
+        echo m68k-atari-mint${UNAME_RELEASE}
+	exit 0 ;;
+    milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
+        echo m68k-milan-mint${UNAME_RELEASE}
+        exit 0 ;;
+    hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
+        echo m68k-hades-mint${UNAME_RELEASE}
+        exit 0 ;;
+    *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
+        echo m68k-unknown-mint${UNAME_RELEASE}
+        exit 0 ;;
+    powerpc:machten:*:*)
+	echo powerpc-apple-machten${UNAME_RELEASE}
+	exit 0 ;;
+    RISC*:Mach:*:*)
+	echo mips-dec-mach_bsd4.3
+	exit 0 ;;
+    RISC*:ULTRIX:*:*)
+	echo mips-dec-ultrix${UNAME_RELEASE}
+	exit 0 ;;
+    VAX*:ULTRIX*:*:*)
+	echo vax-dec-ultrix${UNAME_RELEASE}
+	exit 0 ;;
+    2020:CLIX:*:* | 2430:CLIX:*:*)
+	echo clipper-intergraph-clix${UNAME_RELEASE}
+	exit 0 ;;
+    mips:*:*:UMIPS | mips:*:*:RISCos)
+	eval $set_cc_for_build
+	sed 's/^	//' << EOF >$dummy.c
+#ifdef __cplusplus
+#include <stdio.h>  /* for printf() prototype */
+	int main (int argc, char *argv[]) {
+#else
+	int main (argc, argv) int argc; char *argv[]; {
+#endif
+	#if defined (host_mips) && defined (MIPSEB)
+	#if defined (SYSTYPE_SYSV)
+	  printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0);
+	#endif
+	#if defined (SYSTYPE_SVR4)
+	  printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0);
+	#endif
+	#if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD)
+	  printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0);
+	#endif
+	#endif
+	  exit (-1);
+	}
+EOF
+	$CC_FOR_BUILD -o $dummy $dummy.c \
+	  && $dummy `echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` \
+	  && exit 0
+	echo mips-mips-riscos${UNAME_RELEASE}
+	exit 0 ;;
+    Motorola:PowerMAX_OS:*:*)
+	echo powerpc-motorola-powermax
+	exit 0 ;;
+    Motorola:*:4.3:PL8-*)
+	echo powerpc-harris-powermax
+	exit 0 ;;
+    Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*)
+	echo powerpc-harris-powermax
+	exit 0 ;;
+    Night_Hawk:Power_UNIX:*:*)
+	echo powerpc-harris-powerunix
+	exit 0 ;;
+    m88k:CX/UX:7*:*)
+	echo m88k-harris-cxux7
+	exit 0 ;;
+    m88k:*:4*:R4*)
+	echo m88k-motorola-sysv4
+	exit 0 ;;
+    m88k:*:3*:R3*)
+	echo m88k-motorola-sysv3
+	exit 0 ;;
+    AViiON:dgux:*:*)
+        # DG/UX returns AViiON for all architectures
+        UNAME_PROCESSOR=`/usr/bin/uname -p`
+	if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ]
+	then
+	    if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \
+	       [ ${TARGET_BINARY_INTERFACE}x = x ]
+	    then
+		echo m88k-dg-dgux${UNAME_RELEASE}
+	    else
+		echo m88k-dg-dguxbcs${UNAME_RELEASE}
+	    fi
+	else
+	    echo i586-dg-dgux${UNAME_RELEASE}
+	fi
+ 	exit 0 ;;
+    M88*:DolphinOS:*:*)	# DolphinOS (SVR3)
+	echo m88k-dolphin-sysv3
+	exit 0 ;;
+    M88*:*:R3*:*)
+	# Delta 88k system running SVR3
+	echo m88k-motorola-sysv3
+	exit 0 ;;
+    XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3)
+	echo m88k-tektronix-sysv3
+	exit 0 ;;
+    Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD)
+	echo m68k-tektronix-bsd
+	exit 0 ;;
+    *:IRIX*:*:*)
+	echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'`
+	exit 0 ;;
+    ????????:AIX?:[12].1:2)   # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.
+	echo romp-ibm-aix      # uname -m gives an 8 hex-code CPU id
+	exit 0 ;;              # Note that: echo "'`uname -s`'" gives 'AIX '
+    i*86:AIX:*:*)
+	echo i386-ibm-aix
+	exit 0 ;;
+    ia64:AIX:*:*)
+	if [ -x /usr/bin/oslevel ] ; then
+		IBM_REV=`/usr/bin/oslevel`
+	else
+		IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+	fi
+	echo ${UNAME_MACHINE}-ibm-aix${IBM_REV}
+	exit 0 ;;
+    *:AIX:2:3)
+	if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
+		eval $set_cc_for_build
+		sed 's/^		//' << EOF >$dummy.c
+		#include <sys/systemcfg.h>
+
+		main()
+			{
+			if (!__power_pc())
+				exit(1);
+			puts("powerpc-ibm-aix3.2.5");
+			exit(0);
+			}
+EOF
+		$CC_FOR_BUILD -o $dummy $dummy.c && $dummy && exit 0
+		echo rs6000-ibm-aix3.2.5
+	elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then
+		echo rs6000-ibm-aix3.2.4
+	else
+		echo rs6000-ibm-aix3.2
+	fi
+	exit 0 ;;
+    *:AIX:*:[45])
+	IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'`
+	if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then
+		IBM_ARCH=rs6000
+	else
+		IBM_ARCH=powerpc
+	fi
+	if [ -x /usr/bin/oslevel ] ; then
+		IBM_REV=`/usr/bin/oslevel`
+	else
+		IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+	fi
+	echo ${IBM_ARCH}-ibm-aix${IBM_REV}
+	exit 0 ;;
+    *:AIX:*:*)
+	echo rs6000-ibm-aix
+	exit 0 ;;
+    ibmrt:4.4BSD:*|romp-ibm:BSD:*)
+	echo romp-ibm-bsd4.4
+	exit 0 ;;
+    ibmrt:*BSD:*|romp-ibm:BSD:*)            # covers RT/PC BSD and
+	echo romp-ibm-bsd${UNAME_RELEASE}   # 4.3 with uname added to
+	exit 0 ;;                           # report: romp-ibm BSD 4.3
+    *:BOSX:*:*)
+	echo rs6000-bull-bosx
+	exit 0 ;;
+    DPX/2?00:B.O.S.:*:*)
+	echo m68k-bull-sysv3
+	exit 0 ;;
+    9000/[34]??:4.3bsd:1.*:*)
+	echo m68k-hp-bsd
+	exit 0 ;;
+    hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*)
+	echo m68k-hp-bsd4.4
+	exit 0 ;;
+    9000/[34678]??:HP-UX:*:*)
+	HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+	case "${UNAME_MACHINE}" in
+	    9000/31? )            HP_ARCH=m68000 ;;
+	    9000/[34]?? )         HP_ARCH=m68k ;;
+	    9000/[678][0-9][0-9])
+		if [ -x /usr/bin/getconf ]; then
+		    sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null`
+                    sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`
+                    case "${sc_cpu_version}" in
+                      523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0
+                      528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1
+                      532)                      # CPU_PA_RISC2_0
+                        case "${sc_kernel_bits}" in
+                          32) HP_ARCH="hppa2.0n" ;;
+                          64) HP_ARCH="hppa2.0w" ;;
+			  '') HP_ARCH="hppa2.0" ;;   # HP-UX 10.20
+                        esac ;;
+                    esac
+		fi
+		if [ "${HP_ARCH}" = "" ]; then
+		    eval $set_cc_for_build
+		    sed 's/^              //' << EOF >$dummy.c
+
+              #define _HPUX_SOURCE
+              #include <stdlib.h>
+              #include <unistd.h>
+
+              int main ()
+              {
+              #if defined(_SC_KERNEL_BITS)
+                  long bits = sysconf(_SC_KERNEL_BITS);
+              #endif
+                  long cpu  = sysconf (_SC_CPU_VERSION);
+
+                  switch (cpu)
+              	{
+              	case CPU_PA_RISC1_0: puts ("hppa1.0"); break;
+              	case CPU_PA_RISC1_1: puts ("hppa1.1"); break;
+              	case CPU_PA_RISC2_0:
+              #if defined(_SC_KERNEL_BITS)
+              	    switch (bits)
+              		{
+              		case 64: puts ("hppa2.0w"); break;
+              		case 32: puts ("hppa2.0n"); break;
+              		default: puts ("hppa2.0"); break;
+              		} break;
+              #else  /* !defined(_SC_KERNEL_BITS) */
+              	    puts ("hppa2.0"); break;
+              #endif
+              	default: puts ("hppa1.0"); break;
+              	}
+                  exit (0);
+              }
+EOF
+		    (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy`
+		    test -z "$HP_ARCH" && HP_ARCH=hppa
+		fi ;;
+	esac
+	if [ ${HP_ARCH} = "hppa2.0w" ]
+	then
+	    # avoid double evaluation of $set_cc_for_build
+	    test -n "$CC_FOR_BUILD" || eval $set_cc_for_build
+	    if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E -) | grep __LP64__ >/dev/null
+	    then
+		HP_ARCH="hppa2.0w"
+	    else
+		HP_ARCH="hppa64"
+	    fi
+	fi
+	echo ${HP_ARCH}-hp-hpux${HPUX_REV}
+	exit 0 ;;
+    ia64:HP-UX:*:*)
+	HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+	echo ia64-hp-hpux${HPUX_REV}
+	exit 0 ;;
+    3050*:HI-UX:*:*)
+	eval $set_cc_for_build
+	sed 's/^	//' << EOF >$dummy.c
+	#include <unistd.h>
+	int
+	main ()
+	{
+	  long cpu = sysconf (_SC_CPU_VERSION);
+	  /* The order matters, because CPU_IS_HP_MC68K erroneously returns
+	     true for CPU_PA_RISC1_0.  CPU_IS_PA_RISC returns correct
+	     results, however.  */
+	  if (CPU_IS_PA_RISC (cpu))
+	    {
+	      switch (cpu)
+		{
+		  case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break;
+		  case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break;
+		  case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break;
+		  default: puts ("hppa-hitachi-hiuxwe2"); break;
+		}
+	    }
+	  else if (CPU_IS_HP_MC68K (cpu))
+	    puts ("m68k-hitachi-hiuxwe2");
+	  else puts ("unknown-hitachi-hiuxwe2");
+	  exit (0);
+	}
+EOF
+	$CC_FOR_BUILD -o $dummy $dummy.c && $dummy && exit 0
+	echo unknown-hitachi-hiuxwe2
+	exit 0 ;;
+    9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* )
+	echo hppa1.1-hp-bsd
+	exit 0 ;;
+    9000/8??:4.3bsd:*:*)
+	echo hppa1.0-hp-bsd
+	exit 0 ;;
+    *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*)
+	echo hppa1.0-hp-mpeix
+	exit 0 ;;
+    hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* )
+	echo hppa1.1-hp-osf
+	exit 0 ;;
+    hp8??:OSF1:*:*)
+	echo hppa1.0-hp-osf
+	exit 0 ;;
+    i*86:OSF1:*:*)
+	if [ -x /usr/sbin/sysversion ] ; then
+	    echo ${UNAME_MACHINE}-unknown-osf1mk
+	else
+	    echo ${UNAME_MACHINE}-unknown-osf1
+	fi
+	exit 0 ;;
+    parisc*:Lites*:*:*)
+	echo hppa1.1-hp-lites
+	exit 0 ;;
+    C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
+	echo c1-convex-bsd
+        exit 0 ;;
+    C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
+	if getsysinfo -f scalar_acc
+	then echo c32-convex-bsd
+	else echo c2-convex-bsd
+	fi
+        exit 0 ;;
+    C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
+	echo c34-convex-bsd
+        exit 0 ;;
+    C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
+	echo c38-convex-bsd
+        exit 0 ;;
+    C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
+	echo c4-convex-bsd
+        exit 0 ;;
+    CRAY*Y-MP:*:*:*)
+	echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+	exit 0 ;;
+    CRAY*[A-Z]90:*:*:*)
+	echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \
+	| sed -e 's/CRAY.*\([A-Z]90\)/\1/' \
+	      -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \
+	      -e 's/\.[^.]*$/.X/'
+	exit 0 ;;
+    CRAY*TS:*:*:*)
+	echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+	exit 0 ;;
+    CRAY*T3E:*:*:*)
+	echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+	exit 0 ;;
+    CRAY*SV1:*:*:*)
+	echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+	exit 0 ;;
+    *:UNICOS/mp:*:*)
+	echo nv1-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' 
+	exit 0 ;;
+    F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)
+	FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+        FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
+        FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'`
+        echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+        exit 0 ;;
+    i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*)
+	echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE}
+	exit 0 ;;
+    sparc*:BSD/OS:*:*)
+	echo sparc-unknown-bsdi${UNAME_RELEASE}
+	exit 0 ;;
+    *:BSD/OS:*:*)
+	echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE}
+	exit 0 ;;
+    *:FreeBSD:*:*|*:GNU/FreeBSD:*:*)
+	# Determine whether the default compiler uses glibc.
+	eval $set_cc_for_build
+	sed 's/^	//' << EOF >$dummy.c
+	#include <features.h>
+	#if __GLIBC__ >= 2
+	LIBC=gnu
+	#else
+	LIBC=
+	#endif
+EOF
+	eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^LIBC=`
+	echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`${LIBC:+-$LIBC}
+	exit 0 ;;
+    i*:CYGWIN*:*)
+	echo ${UNAME_MACHINE}-pc-cygwin
+	exit 0 ;;
+    i*:MINGW*:*)
+	echo ${UNAME_MACHINE}-pc-mingw32
+	exit 0 ;;
+    i*:PW*:*)
+	echo ${UNAME_MACHINE}-pc-pw32
+	exit 0 ;;
+    x86:Interix*:3*)
+	echo i586-pc-interix3
+	exit 0 ;;
+    [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*)
+	echo i${UNAME_MACHINE}-pc-mks
+	exit 0 ;;
+    i*:Windows_NT*:* | Pentium*:Windows_NT*:*)
+	# How do we know it's Interix rather than the generic POSIX subsystem?
+	# It also conflicts with pre-2.0 versions of AT&T UWIN. Should we
+	# UNAME_MACHINE based on the output of uname instead of i386?
+	echo i586-pc-interix
+	exit 0 ;;
+    i*:UWIN*:*)
+	echo ${UNAME_MACHINE}-pc-uwin
+	exit 0 ;;
+    p*:CYGWIN*:*)
+	echo powerpcle-unknown-cygwin
+	exit 0 ;;
+    prep*:SunOS:5.*:*)
+	echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+	exit 0 ;;
+    *:GNU:*:*)
+	echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'`
+	exit 0 ;;
+    i*86:Minix:*:*)
+	echo ${UNAME_MACHINE}-pc-minix
+	exit 0 ;;
+    arm*:Linux:*:*)
+	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	exit 0 ;;
+    cris:Linux:*:*)
+	echo cris-axis-linux-gnu
+	exit 0 ;;
+    ia64:Linux:*:*)
+	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	exit 0 ;;
+    m68*:Linux:*:*)
+	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	exit 0 ;;
+    mips:Linux:*:*)
+	eval $set_cc_for_build
+	sed 's/^	//' << EOF >$dummy.c
+	#undef CPU
+	#undef mips
+	#undef mipsel
+	#if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
+	CPU=mipsel
+	#else
+	#if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
+	CPU=mips
+	#else
+	CPU=
+	#endif
+	#endif
+EOF
+	eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^CPU=`
+	test x"${CPU}" != x && echo "${CPU}-unknown-linux-gnu" && exit 0
+	;;
+    mips64:Linux:*:*)
+	eval $set_cc_for_build
+	sed 's/^	//' << EOF >$dummy.c
+	#undef CPU
+	#undef mips64
+	#undef mips64el
+	#if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
+	CPU=mips64el
+	#else
+	#if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
+	CPU=mips64
+	#else
+	CPU=
+	#endif
+	#endif
+EOF
+	eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^CPU=`
+	test x"${CPU}" != x && echo "${CPU}-unknown-linux-gnu" && exit 0
+	;;
+    ppc:Linux:*:*)
+	echo powerpc-unknown-linux-gnu
+	exit 0 ;;
+    ppc64:Linux:*:*)
+	echo powerpc64-unknown-linux-gnu
+	exit 0 ;;
+    alpha:Linux:*:*)
+	case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in
+	  EV5)   UNAME_MACHINE=alphaev5 ;;
+	  EV56)  UNAME_MACHINE=alphaev56 ;;
+	  PCA56) UNAME_MACHINE=alphapca56 ;;
+	  PCA57) UNAME_MACHINE=alphapca56 ;;
+	  EV6)   UNAME_MACHINE=alphaev6 ;;
+	  EV67)  UNAME_MACHINE=alphaev67 ;;
+	  EV68*) UNAME_MACHINE=alphaev68 ;;
+        esac
+	objdump --private-headers /bin/sh | grep ld.so.1 >/dev/null
+	if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi
+	echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC}
+	exit 0 ;;
+    parisc:Linux:*:* | hppa:Linux:*:*)
+	# Look for CPU level
+	case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in
+	  PA7*) echo hppa1.1-unknown-linux-gnu ;;
+	  PA8*) echo hppa2.0-unknown-linux-gnu ;;
+	  *)    echo hppa-unknown-linux-gnu ;;
+	esac
+	exit 0 ;;
+    parisc64:Linux:*:* | hppa64:Linux:*:*)
+	echo hppa64-unknown-linux-gnu
+	exit 0 ;;
+    s390:Linux:*:* | s390x:Linux:*:*)
+	echo ${UNAME_MACHINE}-ibm-linux
+	exit 0 ;;
+    sh*:Linux:*:*)
+	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	exit 0 ;;
+    sparc:Linux:*:* | sparc64:Linux:*:*)
+	echo ${UNAME_MACHINE}-unknown-linux-gnu
+	exit 0 ;;
+    x86_64:Linux:*:*)
+	echo x86_64-unknown-linux-gnu
+	exit 0 ;;
+    i*86:Linux:*:*)
+	# The BFD linker knows what the default object file format is, so
+	# first see if it will tell us. cd to the root directory to prevent
+	# problems with other programs or directories called `ld' in the path.
+	# Set LC_ALL=C to ensure ld outputs messages in English.
+	ld_supported_targets=`cd /; LC_ALL=C ld --help 2>&1 \
+			 | sed -ne '/supported targets:/!d
+				    s/[ 	][ 	]*/ /g
+				    s/.*supported targets: *//
+				    s/ .*//
+				    p'`
+        case "$ld_supported_targets" in
+	  elf32-i386)
+		TENTATIVE="${UNAME_MACHINE}-pc-linux-gnu"
+		;;
+	  a.out-i386-linux)
+		echo "${UNAME_MACHINE}-pc-linux-gnuaout"
+		exit 0 ;;
+	  coff-i386)
+		echo "${UNAME_MACHINE}-pc-linux-gnucoff"
+		exit 0 ;;
+	  "")
+		# Either a pre-BFD a.out linker (linux-gnuoldld) or
+		# one that does not give us useful --help.
+		echo "${UNAME_MACHINE}-pc-linux-gnuoldld"
+		exit 0 ;;
+	esac
+	# Determine whether the default compiler is a.out or elf
+	eval $set_cc_for_build
+	sed 's/^	//' << EOF >$dummy.c
+	#include <features.h>
+	#ifdef __ELF__
+	# ifdef __GLIBC__
+	#  if __GLIBC__ >= 2
+	LIBC=gnu
+	#  else
+	LIBC=gnulibc1
+	#  endif
+	# else
+	LIBC=gnulibc1
+	# endif
+	#else
+	#ifdef __INTEL_COMPILER
+	LIBC=gnu
+	#else
+	LIBC=gnuaout
+	#endif
+	#endif
+EOF
+	eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^LIBC=`
+	test x"${LIBC}" != x && echo "${UNAME_MACHINE}-pc-linux-${LIBC}" && exit 0
+	test x"${TENTATIVE}" != x && echo "${TENTATIVE}" && exit 0
+	;;
+    i*86:DYNIX/ptx:4*:*)
+	# ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.
+	# earlier versions are messed up and put the nodename in both
+	# sysname and nodename.
+	echo i386-sequent-sysv4
+	exit 0 ;;
+    i*86:UNIX_SV:4.2MP:2.*)
+        # Unixware is an offshoot of SVR4, but it has its own version
+        # number series starting with 2...
+        # I am not positive that other SVR4 systems won't match this,
+	# I just have to hope.  -- rms.
+        # Use sysv4.2uw... so that sysv4* matches it.
+	echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION}
+	exit 0 ;;
+    i*86:OS/2:*:*)
+	# If we were able to find `uname', then EMX Unix compatibility
+	# is probably installed.
+	echo ${UNAME_MACHINE}-pc-os2-emx
+	exit 0 ;;
+    i*86:XTS-300:*:STOP)
+	echo ${UNAME_MACHINE}-unknown-stop
+	exit 0 ;;
+    i*86:atheos:*:*)
+	echo ${UNAME_MACHINE}-unknown-atheos
+	exit 0 ;;
+    i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*)
+	echo i386-unknown-lynxos${UNAME_RELEASE}
+	exit 0 ;;
+    i*86:*DOS:*:*)
+	echo ${UNAME_MACHINE}-pc-msdosdjgpp
+	exit 0 ;;
+    i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*)
+	UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'`
+	if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
+		echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL}
+	else
+		echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL}
+	fi
+	exit 0 ;;
+    i*86:*:5:[78]*)
+	case `/bin/uname -X | grep "^Machine"` in
+	    *486*)	     UNAME_MACHINE=i486 ;;
+	    *Pentium)	     UNAME_MACHINE=i586 ;;
+	    *Pent*|*Celeron) UNAME_MACHINE=i686 ;;
+	esac
+	echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION}
+	exit 0 ;;
+    i*86:*:3.2:*)
+	if test -f /usr/options/cb.name; then
+		UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name`
+		echo ${UNAME_MACHINE}-pc-isc$UNAME_REL
+	elif /bin/uname -X 2>/dev/null >/dev/null ; then
+		UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')`
+		(/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486
+		(/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \
+			&& UNAME_MACHINE=i586
+		(/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \
+			&& UNAME_MACHINE=i686
+		(/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \
+			&& UNAME_MACHINE=i686
+		echo ${UNAME_MACHINE}-pc-sco$UNAME_REL
+	else
+		echo ${UNAME_MACHINE}-pc-sysv32
+	fi
+	exit 0 ;;
+    pc:*:*:*)
+	# Left here for compatibility:
+        # uname -m prints for DJGPP always 'pc', but it prints nothing about
+        # the processor, so we play safe by assuming i386.
+	echo i386-pc-msdosdjgpp
+        exit 0 ;;
+    Intel:Mach:3*:*)
+	echo i386-pc-mach3
+	exit 0 ;;
+    paragon:*:*:*)
+	echo i860-intel-osf1
+	exit 0 ;;
+    i860:*:4.*:*) # i860-SVR4
+	if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then
+	  echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4
+	else # Add other i860-SVR4 vendors below as they are discovered.
+	  echo i860-unknown-sysv${UNAME_RELEASE}  # Unknown i860-SVR4
+	fi
+	exit 0 ;;
+    mini*:CTIX:SYS*5:*)
+	# "miniframe"
+	echo m68010-convergent-sysv
+	exit 0 ;;
+    mc68k:UNIX:SYSTEM5:3.51m)
+	echo m68k-convergent-sysv
+	exit 0 ;;
+    M680?0:D-NIX:5.3:*)
+	echo m68k-diab-dnix
+	exit 0 ;;
+    M68*:*:R3V[567]*:*)
+	test -r /sysV68 && echo 'm68k-motorola-sysv' && exit 0 ;;
+    3[34]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0)
+	OS_REL=''
+	test -r /etc/.relid \
+	&& OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+	/bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+	  && echo i486-ncr-sysv4.3${OS_REL} && exit 0
+	/bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+	  && echo i586-ncr-sysv4.3${OS_REL} && exit 0 ;;
+    3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
+        /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+          && echo i486-ncr-sysv4 && exit 0 ;;
+    m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)
+	echo m68k-unknown-lynxos${UNAME_RELEASE}
+	exit 0 ;;
+    mc68030:UNIX_System_V:4.*:*)
+	echo m68k-atari-sysv4
+	exit 0 ;;
+    TSUNAMI:LynxOS:2.*:*)
+	echo sparc-unknown-lynxos${UNAME_RELEASE}
+	exit 0 ;;
+    rs6000:LynxOS:2.*:*)
+	echo rs6000-unknown-lynxos${UNAME_RELEASE}
+	exit 0 ;;
+    PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*)
+	echo powerpc-unknown-lynxos${UNAME_RELEASE}
+	exit 0 ;;
+    SM[BE]S:UNIX_SV:*:*)
+	echo mips-dde-sysv${UNAME_RELEASE}
+	exit 0 ;;
+    RM*:ReliantUNIX-*:*:*)
+	echo mips-sni-sysv4
+	exit 0 ;;
+    RM*:SINIX-*:*:*)
+	echo mips-sni-sysv4
+	exit 0 ;;
+    *:SINIX-*:*:*)
+	if uname -p 2>/dev/null >/dev/null ; then
+		UNAME_MACHINE=`(uname -p) 2>/dev/null`
+		echo ${UNAME_MACHINE}-sni-sysv4
+	else
+		echo ns32k-sni-sysv
+	fi
+	exit 0 ;;
+    PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
+                      # says <Richard.M.Bartel@ccMail.Census.GOV>
+        echo i586-unisys-sysv4
+        exit 0 ;;
+    *:UNIX_System_V:4*:FTX*)
+	# From Gerald Hewes <hewes@openmarket.com>.
+	# How about differentiating between stratus architectures? -djm
+	echo hppa1.1-stratus-sysv4
+	exit 0 ;;
+    *:*:*:FTX*)
+	# From seanf@swdc.stratus.com.
+	echo i860-stratus-sysv4
+	exit 0 ;;
+    *:VOS:*:*)
+	# From Paul.Green@stratus.com.
+	echo hppa1.1-stratus-vos
+	exit 0 ;;
+    mc68*:A/UX:*:*)
+	echo m68k-apple-aux${UNAME_RELEASE}
+	exit 0 ;;
+    news*:NEWS-OS:6*:*)
+	echo mips-sony-newsos6
+	exit 0 ;;
+    R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
+	if [ -d /usr/nec ]; then
+	        echo mips-nec-sysv${UNAME_RELEASE}
+	else
+	        echo mips-unknown-sysv${UNAME_RELEASE}
+	fi
+        exit 0 ;;
+    BeBox:BeOS:*:*)	# BeOS running on hardware made by Be, PPC only.
+	echo powerpc-be-beos
+	exit 0 ;;
+    BeMac:BeOS:*:*)	# BeOS running on Mac or Mac clone, PPC only.
+	echo powerpc-apple-beos
+	exit 0 ;;
+    BePC:BeOS:*:*)	# BeOS running on Intel PC compatible.
+	echo i586-pc-beos
+	exit 0 ;;
+    SX-4:SUPER-UX:*:*)
+	echo sx4-nec-superux${UNAME_RELEASE}
+	exit 0 ;;
+    SX-5:SUPER-UX:*:*)
+	echo sx5-nec-superux${UNAME_RELEASE}
+	exit 0 ;;
+    SX-6:SUPER-UX:*:*)
+	echo sx6-nec-superux${UNAME_RELEASE}
+	exit 0 ;;
+    Power*:Rhapsody:*:*)
+	echo powerpc-apple-rhapsody${UNAME_RELEASE}
+	exit 0 ;;
+    *:Rhapsody:*:*)
+	echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE}
+	exit 0 ;;
+    *:Darwin:*:*)
+	case `uname -p` in
+	    *86) UNAME_PROCESSOR=i686 ;;
+	    powerpc) UNAME_PROCESSOR=powerpc ;;
+	esac
+	echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE}
+	exit 0 ;;
+    *:procnto*:*:* | *:QNX:[0123456789]*:*)
+	UNAME_PROCESSOR=`uname -p`
+	if test "$UNAME_PROCESSOR" = "x86"; then
+		UNAME_PROCESSOR=i386
+		UNAME_MACHINE=pc
+	fi
+	echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE}
+	exit 0 ;;
+    *:QNX:*:4*)
+	echo i386-pc-qnx
+	exit 0 ;;
+    NSR-[DGKLNPTVW]:NONSTOP_KERNEL:*:*)
+	echo nsr-tandem-nsk${UNAME_RELEASE}
+	exit 0 ;;
+    *:NonStop-UX:*:*)
+	echo mips-compaq-nonstopux
+	exit 0 ;;
+    BS2000:POSIX*:*:*)
+	echo bs2000-siemens-sysv
+	exit 0 ;;
+    DS/*:UNIX_System_V:*:*)
+	echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE}
+	exit 0 ;;
+    *:Plan9:*:*)
+	# "uname -m" is not consistent, so use $cputype instead. 386
+	# is converted to i386 for consistency with other x86
+	# operating systems.
+	if test "$cputype" = "386"; then
+	    UNAME_MACHINE=i386
+	else
+	    UNAME_MACHINE="$cputype"
+	fi
+	echo ${UNAME_MACHINE}-unknown-plan9
+	exit 0 ;;
+    *:TOPS-10:*:*)
+	echo pdp10-unknown-tops10
+	exit 0 ;;
+    *:TENEX:*:*)
+	echo pdp10-unknown-tenex
+	exit 0 ;;
+    KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*)
+	echo pdp10-dec-tops20
+	exit 0 ;;
+    XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*)
+	echo pdp10-xkl-tops20
+	exit 0 ;;
+    *:TOPS-20:*:*)
+	echo pdp10-unknown-tops20
+	exit 0 ;;
+    *:ITS:*:*)
+	echo pdp10-unknown-its
+	exit 0 ;;
+esac
+
+#echo '(No uname command or uname output not recognized.)' 1>&2
+#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2
+
+eval $set_cc_for_build
+cat >$dummy.c <<EOF
+#ifdef _SEQUENT_
+# include <sys/types.h>
+# include <sys/utsname.h>
+#endif
+main ()
+{
+#if defined (sony)
+#if defined (MIPSEB)
+  /* BFD wants "bsd" instead of "newsos".  Perhaps BFD should be changed,
+     I don't know....  */
+  printf ("mips-sony-bsd\n"); exit (0);
+#else
+#include <sys/param.h>
+  printf ("m68k-sony-newsos%s\n",
+#ifdef NEWSOS4
+          "4"
+#else
+	  ""
+#endif
+         ); exit (0);
+#endif
+#endif
+
+#if defined (__arm) && defined (__acorn) && defined (__unix)
+  printf ("arm-acorn-riscix"); exit (0);
+#endif
+
+#if defined (hp300) && !defined (hpux)
+  printf ("m68k-hp-bsd\n"); exit (0);
+#endif
+
+#if defined (NeXT)
+#if !defined (__ARCHITECTURE__)
+#define __ARCHITECTURE__ "m68k"
+#endif
+  int version;
+  version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`;
+  if (version < 4)
+    printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version);
+  else
+    printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version);
+  exit (0);
+#endif
+
+#if defined (MULTIMAX) || defined (n16)
+#if defined (UMAXV)
+  printf ("ns32k-encore-sysv\n"); exit (0);
+#else
+#if defined (CMU)
+  printf ("ns32k-encore-mach\n"); exit (0);
+#else
+  printf ("ns32k-encore-bsd\n"); exit (0);
+#endif
+#endif
+#endif
+
+#if defined (__386BSD__)
+  printf ("i386-pc-bsd\n"); exit (0);
+#endif
+
+#if defined (sequent)
+#if defined (i386)
+  printf ("i386-sequent-dynix\n"); exit (0);
+#endif
+#if defined (ns32000)
+  printf ("ns32k-sequent-dynix\n"); exit (0);
+#endif
+#endif
+
+#if defined (_SEQUENT_)
+    struct utsname un;
+
+    uname(&un);
+
+    if (strncmp(un.version, "V2", 2) == 0) {
+	printf ("i386-sequent-ptx2\n"); exit (0);
+    }
+    if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */
+	printf ("i386-sequent-ptx1\n"); exit (0);
+    }
+    printf ("i386-sequent-ptx\n"); exit (0);
+
+#endif
+
+#if defined (vax)
+# if !defined (ultrix)
+#  include <sys/param.h>
+#  if defined (BSD)
+#   if BSD == 43
+      printf ("vax-dec-bsd4.3\n"); exit (0);
+#   else
+#    if BSD == 199006
+      printf ("vax-dec-bsd4.3reno\n"); exit (0);
+#    else
+      printf ("vax-dec-bsd\n"); exit (0);
+#    endif
+#   endif
+#  else
+    printf ("vax-dec-bsd\n"); exit (0);
+#  endif
+# else
+    printf ("vax-dec-ultrix\n"); exit (0);
+# endif
+#endif
+
+#if defined (alliant) && defined (i860)
+  printf ("i860-alliant-bsd\n"); exit (0);
+#endif
+
+  exit (1);
+}
+EOF
+
+$CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && $dummy && exit 0
+
+# Apollos put the system type in the environment.
+
+test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit 0; }
+
+# Convex versions that predate uname can use getsysinfo(1)
+
+if [ -x /usr/convex/getsysinfo ]
+then
+    case `getsysinfo -f cpu_type` in
+    c1*)
+	echo c1-convex-bsd
+	exit 0 ;;
+    c2*)
+	if getsysinfo -f scalar_acc
+	then echo c32-convex-bsd
+	else echo c2-convex-bsd
+	fi
+	exit 0 ;;
+    c34*)
+	echo c34-convex-bsd
+	exit 0 ;;
+    c38*)
+	echo c38-convex-bsd
+	exit 0 ;;
+    c4*)
+	echo c4-convex-bsd
+	exit 0 ;;
+    esac
+fi
+
+cat >&2 <<EOF
+$0: unable to guess system type
+
+This script, last modified $timestamp, has failed to recognize
+the operating system you are using. It is advised that you
+download the most up to date version of the config scripts from
+
+    ftp://ftp.gnu.org/pub/gnu/config/
+
+If the version you run ($0) is already up to date, please
+send the following data and any information you think might be
+pertinent to <config-patches@gnu.org> in order to provide the needed
+information to handle your system.
+
+config.guess timestamp = $timestamp
+
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null`
+/bin/uname -X     = `(/bin/uname -X) 2>/dev/null`
+
+hostinfo               = `(hostinfo) 2>/dev/null`
+/bin/universe          = `(/bin/universe) 2>/dev/null`
+/usr/bin/arch -k       = `(/usr/bin/arch -k) 2>/dev/null`
+/bin/arch              = `(/bin/arch) 2>/dev/null`
+/usr/bin/oslevel       = `(/usr/bin/oslevel) 2>/dev/null`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null`
+
+UNAME_MACHINE = ${UNAME_MACHINE}
+UNAME_RELEASE = ${UNAME_RELEASE}
+UNAME_SYSTEM  = ${UNAME_SYSTEM}
+UNAME_VERSION = ${UNAME_VERSION}
+EOF
+
+exit 1
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/config.sub b/config.sub
new file mode 100644
index 0000000000000000000000000000000000000000..fe4f1edf3c0d2ff94ac211a8523f02b481c45810
--- /dev/null
+++ b/config.sub
@@ -0,0 +1,1492 @@
+#! /bin/sh
+# Configuration validation subroutine script.
+#   Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+#   2000, 2001, 2002, 2003 Free Software Foundation, Inc.
+
+timestamp='2003-05-09'
+
+# This file is (in principle) common to ALL GNU software.
+# The presence of a machine in this file suggests that SOME GNU software
+# can handle that machine.  It does not imply ALL GNU software can.
+#
+# This file is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# Please send patches to <config-patches@gnu.org>.  Submit a context
+# diff and a properly formatted ChangeLog entry.
+#
+# Configuration subroutine to validate and canonicalize a configuration type.
+# Supply the specified configuration type as an argument.
+# If it is invalid, we print an error message on stderr and exit with code 1.
+# Otherwise, we print the canonical config type on stdout and succeed.
+
+# This file is supposed to be the same for all GNU packages
+# and recognize all the CPU types, system types and aliases
+# that are meaningful with *any* GNU software.
+# Each package is responsible for reporting which valid configurations
+# it does not support.  The user should be able to distinguish
+# a failure to support a valid configuration from a meaningless
+# configuration.
+
+# The goal of this file is to map all the various variations of a given
+# machine specification into a single specification in the form:
+#	CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
+# or in some cases, the newer four-part form:
+#	CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
+# It is wrong to echo any other type of specification.
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION] CPU-MFR-OPSYS
+       $0 [OPTION] ALIAS
+
+Canonicalize a configuration name.
+
+Operation modes:
+  -h, --help         print this help, then exit
+  -t, --time-stamp   print date of last modification, then exit
+  -v, --version      print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.sub ($timestamp)
+
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
+Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions.  There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+  case $1 in
+    --time-stamp | --time* | -t )
+       echo "$timestamp" ; exit 0 ;;
+    --version | -v )
+       echo "$version" ; exit 0 ;;
+    --help | --h* | -h )
+       echo "$usage"; exit 0 ;;
+    -- )     # Stop option processing
+       shift; break ;;
+    - )	# Use stdin as input.
+       break ;;
+    -* )
+       echo "$me: invalid option $1$help"
+       exit 1 ;;
+
+    *local*)
+       # First pass through any local machine types.
+       echo $1
+       exit 0;;
+
+    * )
+       break ;;
+  esac
+done
+
+case $# in
+ 0) echo "$me: missing argument$help" >&2
+    exit 1;;
+ 1) ;;
+ *) echo "$me: too many arguments$help" >&2
+    exit 1;;
+esac
+
+# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any).
+# Here we must recognize all the valid KERNEL-OS combinations.
+maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
+case $maybe_os in
+  nto-qnx* | linux-gnu* | freebsd*-gnu* | netbsd*-gnu* | storm-chaos* | os2-emx* | rtmk-nova*)
+    os=-$maybe_os
+    basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
+    ;;
+  *)
+    basic_machine=`echo $1 | sed 's/-[^-]*$//'`
+    if [ $basic_machine != $1 ]
+    then os=`echo $1 | sed 's/.*-/-/'`
+    else os=; fi
+    ;;
+esac
+
+### Let's recognize common machines as not being operating systems so
+### that things like config.sub decstation-3100 work.  We also
+### recognize some manufacturers as not being operating systems, so we
+### can provide default operating systems below.
+case $os in
+	-sun*os*)
+		# Prevent following clause from handling this invalid input.
+		;;
+	-dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \
+	-att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \
+	-unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \
+	-convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\
+	-c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \
+	-harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \
+	-apple | -axis)
+		os=
+		basic_machine=$1
+		;;
+	-sim | -cisco | -oki | -wec | -winbond)
+		os=
+		basic_machine=$1
+		;;
+	-scout)
+		;;
+	-wrs)
+		os=-vxworks
+		basic_machine=$1
+		;;
+	-chorusos*)
+		os=-chorusos
+		basic_machine=$1
+		;;
+ 	-chorusrdb)
+ 		os=-chorusrdb
+		basic_machine=$1
+ 		;;
+	-hiux*)
+		os=-hiuxwe2
+		;;
+	-sco5)
+		os=-sco3.2v5
+		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+		;;
+	-sco4)
+		os=-sco3.2v4
+		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+		;;
+	-sco3.2.[4-9]*)
+		os=`echo $os | sed -e 's/sco3.2./sco3.2v/'`
+		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+		;;
+	-sco3.2v[4-9]*)
+		# Don't forget version if it is 3.2v4 or newer.
+		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+		;;
+	-sco*)
+		os=-sco3.2v2
+		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+		;;
+	-udk*)
+		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+		;;
+	-isc)
+		os=-isc2.2
+		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+		;;
+	-clix*)
+		basic_machine=clipper-intergraph
+		;;
+	-isc*)
+		basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+		;;
+	-lynx*)
+		os=-lynxos
+		;;
+	-ptx*)
+		basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'`
+		;;
+	-windowsnt*)
+		os=`echo $os | sed -e 's/windowsnt/winnt/'`
+		;;
+	-psos*)
+		os=-psos
+		;;
+	-mint | -mint[0-9]*)
+		basic_machine=m68k-atari
+		os=-mint
+		;;
+esac
+
+# Decode aliases for certain CPU-COMPANY combinations.
+case $basic_machine in
+	# Recognize the basic CPU types without company name.
+	# Some are omitted here because they have special meanings below.
+	1750a | 580 \
+	| a29k \
+	| alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \
+	| alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \
+	| arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr \
+	| clipper \
+	| d10v | d30v | dlx | dsp16xx \
+	| fr30 | frv \
+	| h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
+	| i370 | i860 | i960 | ia64 \
+	| ip2k \
+	| m32r | m68000 | m68k | m88k | mcore \
+	| mips | mipsbe | mipseb | mipsel | mipsle \
+	| mips16 \
+	| mips64 | mips64el \
+	| mips64vr | mips64vrel \
+	| mips64orion | mips64orionel \
+	| mips64vr4100 | mips64vr4100el \
+	| mips64vr4300 | mips64vr4300el \
+	| mips64vr5000 | mips64vr5000el \
+	| mipsisa32 | mipsisa32el \
+	| mipsisa32r2 | mipsisa32r2el \
+	| mipsisa64 | mipsisa64el \
+	| mipsisa64sb1 | mipsisa64sb1el \
+	| mipsisa64sr71k | mipsisa64sr71kel \
+	| mipstx39 | mipstx39el \
+	| mn10200 | mn10300 \
+	| msp430 \
+	| ns16k | ns32k \
+	| openrisc | or32 \
+	| pdp10 | pdp11 | pj | pjl \
+	| powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \
+	| pyramid \
+	| sh | sh[1234] | sh[23]e | sh[34]eb | shbe | shle | sh[1234]le | sh3ele \
+	| sh64 | sh64le \
+	| sparc | sparc64 | sparc86x | sparclet | sparclite | sparcv9 | sparcv9b \
+	| strongarm \
+	| tahoe | thumb | tic80 | tron \
+	| v850 | v850e \
+	| we32k \
+	| x86 | xscale | xstormy16 | xtensa \
+	| z8k)
+		basic_machine=$basic_machine-unknown
+		;;
+	m6811 | m68hc11 | m6812 | m68hc12)
+		# Motorola 68HC11/12.
+		basic_machine=$basic_machine-unknown
+		os=-none
+		;;
+	m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k)
+		;;
+
+	# We use `pc' rather than `unknown'
+	# because (1) that's what they normally are, and
+	# (2) the word "unknown" tends to confuse beginning users.
+	i*86 | x86_64)
+	  basic_machine=$basic_machine-pc
+	  ;;
+	# Object if more than one company name word.
+	*-*-*)
+		echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+		exit 1
+		;;
+	# Recognize the basic CPU types with company name.
+	580-* \
+	| a29k-* \
+	| alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \
+	| alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \
+	| alphapca5[67]-* | alpha64pca5[67]-* | arc-* \
+	| arm-*  | armbe-* | armle-* | armeb-* | armv*-* \
+	| avr-* \
+	| bs2000-* \
+	| c[123]* | c30-* | [cjt]90-* | c4x-* | c54x-* | c55x-* | c6x-* \
+	| clipper-* | cydra-* \
+	| d10v-* | d30v-* | dlx-* \
+	| elxsi-* \
+	| f30[01]-* | f700-* | fr30-* | frv-* | fx80-* \
+	| h8300-* | h8500-* \
+	| hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \
+	| i*86-* | i860-* | i960-* | ia64-* \
+	| ip2k-* \
+	| m32r-* \
+	| m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \
+	| m88110-* | m88k-* | mcore-* \
+	| mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \
+	| mips16-* \
+	| mips64-* | mips64el-* \
+	| mips64vr-* | mips64vrel-* \
+	| mips64orion-* | mips64orionel-* \
+	| mips64vr4100-* | mips64vr4100el-* \
+	| mips64vr4300-* | mips64vr4300el-* \
+	| mips64vr5000-* | mips64vr5000el-* \
+	| mipsisa32-* | mipsisa32el-* \
+	| mipsisa32r2-* | mipsisa32r2el-* \
+	| mipsisa64-* | mipsisa64el-* \
+	| mipsisa64sb1-* | mipsisa64sb1el-* \
+	| mipsisa64sr71k-* | mipsisa64sr71kel-* \
+	| mipstx39-* | mipstx39el-* \
+	| msp430-* \
+	| none-* | np1-* | nv1-* | ns16k-* | ns32k-* \
+	| orion-* \
+	| pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
+	| powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \
+	| pyramid-* \
+	| romp-* | rs6000-* \
+	| sh-* | sh[1234]-* | sh[23]e-* | sh[34]eb-* | shbe-* \
+	| shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \
+	| sparc-* | sparc64-* | sparc86x-* | sparclet-* | sparclite-* \
+	| sparcv9-* | sparcv9b-* | strongarm-* | sv1-* | sx?-* \
+	| tahoe-* | thumb-* \
+	| tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \
+	| tron-* \
+	| v850-* | v850e-* | vax-* \
+	| we32k-* \
+	| x86-* | x86_64-* | xps100-* | xscale-* | xstormy16-* \
+	| xtensa-* \
+	| ymp-* \
+	| z8k-*)
+		;;
+	# Recognize the various machine names and aliases which stand
+	# for a CPU type and a company and sometimes even an OS.
+	386bsd)
+		basic_machine=i386-unknown
+		os=-bsd
+		;;
+	3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc)
+		basic_machine=m68000-att
+		;;
+	3b*)
+		basic_machine=we32k-att
+		;;
+	a29khif)
+		basic_machine=a29k-amd
+		os=-udi
+		;;
+	adobe68k)
+		basic_machine=m68010-adobe
+		os=-scout
+		;;
+	alliant | fx80)
+		basic_machine=fx80-alliant
+		;;
+	altos | altos3068)
+		basic_machine=m68k-altos
+		;;
+	am29k)
+		basic_machine=a29k-none
+		os=-bsd
+		;;
+	amd64)
+		basic_machine=x86_64-pc
+		;;
+	amdahl)
+		basic_machine=580-amdahl
+		os=-sysv
+		;;
+	amiga | amiga-*)
+		basic_machine=m68k-unknown
+		;;
+	amigaos | amigados)
+		basic_machine=m68k-unknown
+		os=-amigaos
+		;;
+	amigaunix | amix)
+		basic_machine=m68k-unknown
+		os=-sysv4
+		;;
+	apollo68)
+		basic_machine=m68k-apollo
+		os=-sysv
+		;;
+	apollo68bsd)
+		basic_machine=m68k-apollo
+		os=-bsd
+		;;
+	aux)
+		basic_machine=m68k-apple
+		os=-aux
+		;;
+	balance)
+		basic_machine=ns32k-sequent
+		os=-dynix
+		;;
+	c90)
+		basic_machine=c90-cray
+		os=-unicos
+		;;
+	convex-c1)
+		basic_machine=c1-convex
+		os=-bsd
+		;;
+	convex-c2)
+		basic_machine=c2-convex
+		os=-bsd
+		;;
+	convex-c32)
+		basic_machine=c32-convex
+		os=-bsd
+		;;
+	convex-c34)
+		basic_machine=c34-convex
+		os=-bsd
+		;;
+	convex-c38)
+		basic_machine=c38-convex
+		os=-bsd
+		;;
+	cray | j90)
+		basic_machine=j90-cray
+		os=-unicos
+		;;
+	crds | unos)
+		basic_machine=m68k-crds
+		;;
+	cris | cris-* | etrax*)
+		basic_machine=cris-axis
+		;;
+	da30 | da30-*)
+		basic_machine=m68k-da30
+		;;
+	decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn)
+		basic_machine=mips-dec
+		;;
+	decsystem10* | dec10*)
+		basic_machine=pdp10-dec
+		os=-tops10
+		;;
+	decsystem20* | dec20*)
+		basic_machine=pdp10-dec
+		os=-tops20
+		;;
+	delta | 3300 | motorola-3300 | motorola-delta \
+	      | 3300-motorola | delta-motorola)
+		basic_machine=m68k-motorola
+		;;
+	delta88)
+		basic_machine=m88k-motorola
+		os=-sysv3
+		;;
+	dpx20 | dpx20-*)
+		basic_machine=rs6000-bull
+		os=-bosx
+		;;
+	dpx2* | dpx2*-bull)
+		basic_machine=m68k-bull
+		os=-sysv3
+		;;
+	ebmon29k)
+		basic_machine=a29k-amd
+		os=-ebmon
+		;;
+	elxsi)
+		basic_machine=elxsi-elxsi
+		os=-bsd
+		;;
+	encore | umax | mmax)
+		basic_machine=ns32k-encore
+		;;
+	es1800 | OSE68k | ose68k | ose | OSE)
+		basic_machine=m68k-ericsson
+		os=-ose
+		;;
+	fx2800)
+		basic_machine=i860-alliant
+		;;
+	genix)
+		basic_machine=ns32k-ns
+		;;
+	gmicro)
+		basic_machine=tron-gmicro
+		os=-sysv
+		;;
+	go32)
+		basic_machine=i386-pc
+		os=-go32
+		;;
+	h3050r* | hiux*)
+		basic_machine=hppa1.1-hitachi
+		os=-hiuxwe2
+		;;
+	h8300hms)
+		basic_machine=h8300-hitachi
+		os=-hms
+		;;
+	h8300xray)
+		basic_machine=h8300-hitachi
+		os=-xray
+		;;
+	h8500hms)
+		basic_machine=h8500-hitachi
+		os=-hms
+		;;
+	harris)
+		basic_machine=m88k-harris
+		os=-sysv3
+		;;
+	hp300-*)
+		basic_machine=m68k-hp
+		;;
+	hp300bsd)
+		basic_machine=m68k-hp
+		os=-bsd
+		;;
+	hp300hpux)
+		basic_machine=m68k-hp
+		os=-hpux
+		;;
+	hp3k9[0-9][0-9] | hp9[0-9][0-9])
+		basic_machine=hppa1.0-hp
+		;;
+	hp9k2[0-9][0-9] | hp9k31[0-9])
+		basic_machine=m68000-hp
+		;;
+	hp9k3[2-9][0-9])
+		basic_machine=m68k-hp
+		;;
+	hp9k6[0-9][0-9] | hp6[0-9][0-9])
+		basic_machine=hppa1.0-hp
+		;;
+	hp9k7[0-79][0-9] | hp7[0-79][0-9])
+		basic_machine=hppa1.1-hp
+		;;
+	hp9k78[0-9] | hp78[0-9])
+		# FIXME: really hppa2.0-hp
+		basic_machine=hppa1.1-hp
+		;;
+	hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893)
+		# FIXME: really hppa2.0-hp
+		basic_machine=hppa1.1-hp
+		;;
+	hp9k8[0-9][13679] | hp8[0-9][13679])
+		basic_machine=hppa1.1-hp
+		;;
+	hp9k8[0-9][0-9] | hp8[0-9][0-9])
+		basic_machine=hppa1.0-hp
+		;;
+	hppa-next)
+		os=-nextstep3
+		;;
+	hppaosf)
+		basic_machine=hppa1.1-hp
+		os=-osf
+		;;
+	hppro)
+		basic_machine=hppa1.1-hp
+		os=-proelf
+		;;
+	i370-ibm* | ibm*)
+		basic_machine=i370-ibm
+		;;
+# I'm not sure what "Sysv32" means.  Should this be sysv3.2?
+	i*86v32)
+		basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+		os=-sysv32
+		;;
+	i*86v4*)
+		basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+		os=-sysv4
+		;;
+	i*86v)
+		basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+		os=-sysv
+		;;
+	i*86sol2)
+		basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+		os=-solaris2
+		;;
+	i386mach)
+		basic_machine=i386-mach
+		os=-mach
+		;;
+	i386-vsta | vsta)
+		basic_machine=i386-unknown
+		os=-vsta
+		;;
+	iris | iris4d)
+		basic_machine=mips-sgi
+		case $os in
+		    -irix*)
+			;;
+		    *)
+			os=-irix4
+			;;
+		esac
+		;;
+	isi68 | isi)
+		basic_machine=m68k-isi
+		os=-sysv
+		;;
+	m88k-omron*)
+		basic_machine=m88k-omron
+		;;
+	magnum | m3230)
+		basic_machine=mips-mips
+		os=-sysv
+		;;
+	merlin)
+		basic_machine=ns32k-utek
+		os=-sysv
+		;;
+	mingw32)
+		basic_machine=i386-pc
+		os=-mingw32
+		;;
+	miniframe)
+		basic_machine=m68000-convergent
+		;;
+	*mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*)
+		basic_machine=m68k-atari
+		os=-mint
+		;;
+	mips3*-*)
+		basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`
+		;;
+	mips3*)
+		basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown
+		;;
+	mmix*)
+		basic_machine=mmix-knuth
+		os=-mmixware
+		;;
+	monitor)
+		basic_machine=m68k-rom68k
+		os=-coff
+		;;
+	morphos)
+		basic_machine=powerpc-unknown
+		os=-morphos
+		;;
+	msdos)
+		basic_machine=i386-pc
+		os=-msdos
+		;;
+	mvs)
+		basic_machine=i370-ibm
+		os=-mvs
+		;;
+	ncr3000)
+		basic_machine=i486-ncr
+		os=-sysv4
+		;;
+	netbsd386)
+		basic_machine=i386-unknown
+		os=-netbsd
+		;;
+	netwinder)
+		basic_machine=armv4l-rebel
+		os=-linux
+		;;
+	news | news700 | news800 | news900)
+		basic_machine=m68k-sony
+		os=-newsos
+		;;
+	news1000)
+		basic_machine=m68030-sony
+		os=-newsos
+		;;
+	news-3600 | risc-news)
+		basic_machine=mips-sony
+		os=-newsos
+		;;
+	necv70)
+		basic_machine=v70-nec
+		os=-sysv
+		;;
+	next | m*-next )
+		basic_machine=m68k-next
+		case $os in
+		    -nextstep* )
+			;;
+		    -ns2*)
+		      os=-nextstep2
+			;;
+		    *)
+		      os=-nextstep3
+			;;
+		esac
+		;;
+	nh3000)
+		basic_machine=m68k-harris
+		os=-cxux
+		;;
+	nh[45]000)
+		basic_machine=m88k-harris
+		os=-cxux
+		;;
+	nindy960)
+		basic_machine=i960-intel
+		os=-nindy
+		;;
+	mon960)
+		basic_machine=i960-intel
+		os=-mon960
+		;;
+	nonstopux)
+		basic_machine=mips-compaq
+		os=-nonstopux
+		;;
+	np1)
+		basic_machine=np1-gould
+		;;
+	nv1)
+		basic_machine=nv1-cray
+		os=-unicosmp
+		;;
+	nsr-tandem)
+		basic_machine=nsr-tandem
+		;;
+	op50n-* | op60c-*)
+		basic_machine=hppa1.1-oki
+		os=-proelf
+		;;
+	or32 | or32-*)
+		basic_machine=or32-unknown
+		os=-coff
+		;;
+	OSE68000 | ose68000)
+		basic_machine=m68000-ericsson
+		os=-ose
+		;;
+	os68k)
+		basic_machine=m68k-none
+		os=-os68k
+		;;
+	pa-hitachi)
+		basic_machine=hppa1.1-hitachi
+		os=-hiuxwe2
+		;;
+	paragon)
+		basic_machine=i860-intel
+		os=-osf
+		;;
+	pbd)
+		basic_machine=sparc-tti
+		;;
+	pbb)
+		basic_machine=m68k-tti
+		;;
+	pc532 | pc532-*)
+		basic_machine=ns32k-pc532
+		;;
+	pentium | p5 | k5 | k6 | nexgen | viac3)
+		basic_machine=i586-pc
+		;;
+	pentiumpro | p6 | 6x86 | athlon | athlon_*)
+		basic_machine=i686-pc
+		;;
+	pentiumii | pentium2)
+		basic_machine=i686-pc
+		;;
+	pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*)
+		basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'`
+		;;
+	pentiumpro-* | p6-* | 6x86-* | athlon-*)
+		basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
+		;;
+	pentiumii-* | pentium2-*)
+		basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
+		;;
+	pn)
+		basic_machine=pn-gould
+		;;
+	power)	basic_machine=power-ibm
+		;;
+	ppc)	basic_machine=powerpc-unknown
+		;;
+	ppc-*)	basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
+		;;
+	ppcle | powerpclittle | ppc-le | powerpc-little)
+		basic_machine=powerpcle-unknown
+		;;
+	ppcle-* | powerpclittle-*)
+		basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'`
+		;;
+	ppc64)	basic_machine=powerpc64-unknown
+		;;
+	ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'`
+		;;
+	ppc64le | powerpc64little | ppc64-le | powerpc64-little)
+		basic_machine=powerpc64le-unknown
+		;;
+	ppc64le-* | powerpc64little-*)
+		basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'`
+		;;
+	ps2)
+		basic_machine=i386-ibm
+		;;
+	pw32)
+		basic_machine=i586-unknown
+		os=-pw32
+		;;
+	rom68k)
+		basic_machine=m68k-rom68k
+		os=-coff
+		;;
+	rm[46]00)
+		basic_machine=mips-siemens
+		;;
+	rtpc | rtpc-*)
+		basic_machine=romp-ibm
+		;;
+	s390 | s390-*)
+		basic_machine=s390-ibm
+		;;
+	s390x | s390x-*)
+		basic_machine=s390x-ibm
+		;;
+	sa29200)
+		basic_machine=a29k-amd
+		os=-udi
+		;;
+	sb1)
+		basic_machine=mipsisa64sb1-unknown
+		;;
+	sb1el)
+		basic_machine=mipsisa64sb1el-unknown
+		;;
+	sequent)
+		basic_machine=i386-sequent
+		;;
+	sh)
+		basic_machine=sh-hitachi
+		os=-hms
+		;;
+	sparclite-wrs | simso-wrs)
+		basic_machine=sparclite-wrs
+		os=-vxworks
+		;;
+	sps7)
+		basic_machine=m68k-bull
+		os=-sysv2
+		;;
+	spur)
+		basic_machine=spur-unknown
+		;;
+	st2000)
+		basic_machine=m68k-tandem
+		;;
+	stratus)
+		basic_machine=i860-stratus
+		os=-sysv4
+		;;
+	sun2)
+		basic_machine=m68000-sun
+		;;
+	sun2os3)
+		basic_machine=m68000-sun
+		os=-sunos3
+		;;
+	sun2os4)
+		basic_machine=m68000-sun
+		os=-sunos4
+		;;
+	sun3os3)
+		basic_machine=m68k-sun
+		os=-sunos3
+		;;
+	sun3os4)
+		basic_machine=m68k-sun
+		os=-sunos4
+		;;
+	sun4os3)
+		basic_machine=sparc-sun
+		os=-sunos3
+		;;
+	sun4os4)
+		basic_machine=sparc-sun
+		os=-sunos4
+		;;
+	sun4sol2)
+		basic_machine=sparc-sun
+		os=-solaris2
+		;;
+	sun3 | sun3-*)
+		basic_machine=m68k-sun
+		;;
+	sun4)
+		basic_machine=sparc-sun
+		;;
+	sun386 | sun386i | roadrunner)
+		basic_machine=i386-sun
+		;;
+	sv1)
+		basic_machine=sv1-cray
+		os=-unicos
+		;;
+	symmetry)
+		basic_machine=i386-sequent
+		os=-dynix
+		;;
+	t3e)
+		basic_machine=alphaev5-cray
+		os=-unicos
+		;;
+	t90)
+		basic_machine=t90-cray
+		os=-unicos
+		;;
+        tic4x | c4x*)
+		basic_machine=tic4x-unknown
+		os=-coff
+		;;
+	tic54x | c54x*)
+		basic_machine=tic54x-unknown
+		os=-coff
+		;;
+	tic55x | c55x*)
+		basic_machine=tic55x-unknown
+		os=-coff
+		;;
+	tic6x | c6x*)
+		basic_machine=tic6x-unknown
+		os=-coff
+		;;
+	tx39)
+		basic_machine=mipstx39-unknown
+		;;
+	tx39el)
+		basic_machine=mipstx39el-unknown
+		;;
+	toad1)
+		basic_machine=pdp10-xkl
+		os=-tops20
+		;;
+	tower | tower-32)
+		basic_machine=m68k-ncr
+		;;
+	udi29k)
+		basic_machine=a29k-amd
+		os=-udi
+		;;
+	ultra3)
+		basic_machine=a29k-nyu
+		os=-sym1
+		;;
+	v810 | necv810)
+		basic_machine=v810-nec
+		os=-none
+		;;
+	vaxv)
+		basic_machine=vax-dec
+		os=-sysv
+		;;
+	vms)
+		basic_machine=vax-dec
+		os=-vms
+		;;
+	vpp*|vx|vx-*)
+		basic_machine=f301-fujitsu
+		;;
+	vxworks960)
+		basic_machine=i960-wrs
+		os=-vxworks
+		;;
+	vxworks68)
+		basic_machine=m68k-wrs
+		os=-vxworks
+		;;
+	vxworks29k)
+		basic_machine=a29k-wrs
+		os=-vxworks
+		;;
+	w65*)
+		basic_machine=w65-wdc
+		os=-none
+		;;
+	w89k-*)
+		basic_machine=hppa1.1-winbond
+		os=-proelf
+		;;
+	xps | xps100)
+		basic_machine=xps100-honeywell
+		;;
+	ymp)
+		basic_machine=ymp-cray
+		os=-unicos
+		;;
+	z8k-*-coff)
+		basic_machine=z8k-unknown
+		os=-sim
+		;;
+	none)
+		basic_machine=none-none
+		os=-none
+		;;
+
+# Here we handle the default manufacturer of certain CPU types.  It is in
+# some cases the only manufacturer, in others, it is the most popular.
+	w89k)
+		basic_machine=hppa1.1-winbond
+		;;
+	op50n)
+		basic_machine=hppa1.1-oki
+		;;
+	op60c)
+		basic_machine=hppa1.1-oki
+		;;
+	romp)
+		basic_machine=romp-ibm
+		;;
+	rs6000)
+		basic_machine=rs6000-ibm
+		;;
+	vax)
+		basic_machine=vax-dec
+		;;
+	pdp10)
+		# there are many clones, so DEC is not a safe bet
+		basic_machine=pdp10-unknown
+		;;
+	pdp11)
+		basic_machine=pdp11-dec
+		;;
+	we32k)
+		basic_machine=we32k-att
+		;;
+	sh3 | sh4 | sh[34]eb | sh[1234]le | sh[23]ele)
+		basic_machine=sh-unknown
+		;;
+	sh64)
+		basic_machine=sh64-unknown
+		;;
+	sparc | sparcv9 | sparcv9b)
+		basic_machine=sparc-sun
+		;;
+	cydra)
+		basic_machine=cydra-cydrome
+		;;
+	orion)
+		basic_machine=orion-highlevel
+		;;
+	orion105)
+		basic_machine=clipper-highlevel
+		;;
+	mac | mpw | mac-mpw)
+		basic_machine=m68k-apple
+		;;
+	pmac | pmac-mpw)
+		basic_machine=powerpc-apple
+		;;
+	*-unknown)
+		# Make sure to match an already-canonicalized machine name.
+		;;
+	*)
+		echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+		exit 1
+		;;
+esac
+
+# Here we canonicalize certain aliases for manufacturers.
+case $basic_machine in
+	*-digital*)
+		basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'`
+		;;
+	*-commodore*)
+		basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'`
+		;;
+	*)
+		;;
+esac
+
+# Decode manufacturer-specific aliases for certain operating systems.
+
+if [ x"$os" != x"" ]
+then
+case $os in
+        # First match some system type aliases
+        # that might get confused with valid system types.
+	# -solaris* is a basic system type, with this one exception.
+	-solaris1 | -solaris1.*)
+		os=`echo $os | sed -e 's|solaris1|sunos4|'`
+		;;
+	-solaris)
+		os=-solaris2
+		;;
+	-svr4*)
+		os=-sysv4
+		;;
+	-unixware*)
+		os=-sysv4.2uw
+		;;
+	-gnu/linux*)
+		os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'`
+		;;
+	# First accept the basic system types.
+	# The portable systems comes first.
+	# Each alternative MUST END IN A *, to match a version number.
+	# -sysv* is not here because it comes later, after sysvr4.
+	-gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \
+	      | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\
+	      | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \
+	      | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
+	      | -aos* \
+	      | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
+	      | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
+	      | -hiux* | -386bsd* | -netbsd* | -openbsd* | -freebsd* | -riscix* \
+	      | -lynxos* | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
+	      | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
+	      | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
+	      | -chorusos* | -chorusrdb* \
+	      | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
+	      | -mingw32* | -linux-gnu* | -uxpv* | -beos* | -mpeix* | -udk* \
+	      | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \
+	      | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
+	      | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \
+	      | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \
+	      | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \
+	      | -powermax* | -dnix*)
+	# Remember, each alternative MUST END IN *, to match a version number.
+		;;
+	-qnx*)
+		case $basic_machine in
+		    x86-* | i*86-*)
+			;;
+		    *)
+			os=-nto$os
+			;;
+		esac
+		;;
+	-nto-qnx*)
+		;;
+	-nto*)
+		os=`echo $os | sed -e 's|nto|nto-qnx|'`
+		;;
+	-sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \
+	      | -windows* | -osx | -abug | -netware* | -os9* | -beos* \
+	      | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*)
+		;;
+	-mac*)
+		os=`echo $os | sed -e 's|mac|macos|'`
+		;;
+	-linux*)
+		os=`echo $os | sed -e 's|linux|linux-gnu|'`
+		;;
+	-sunos5*)
+		os=`echo $os | sed -e 's|sunos5|solaris2|'`
+		;;
+	-sunos6*)
+		os=`echo $os | sed -e 's|sunos6|solaris3|'`
+		;;
+	-opened*)
+		os=-openedition
+		;;
+	-wince*)
+		os=-wince
+		;;
+	-osfrose*)
+		os=-osfrose
+		;;
+	-osf*)
+		os=-osf
+		;;
+	-utek*)
+		os=-bsd
+		;;
+	-dynix*)
+		os=-bsd
+		;;
+	-acis*)
+		os=-aos
+		;;
+	-atheos*)
+		os=-atheos
+		;;
+	-386bsd)
+		os=-bsd
+		;;
+	-ctix* | -uts*)
+		os=-sysv
+		;;
+	-nova*)
+		os=-rtmk-nova
+		;;
+	-ns2 )
+		os=-nextstep2
+		;;
+	-nsk*)
+		os=-nsk
+		;;
+	# Preserve the version number of sinix5.
+	-sinix5.*)
+		os=`echo $os | sed -e 's|sinix|sysv|'`
+		;;
+	-sinix*)
+		os=-sysv4
+		;;
+	-triton*)
+		os=-sysv3
+		;;
+	-oss*)
+		os=-sysv3
+		;;
+	-svr4)
+		os=-sysv4
+		;;
+	-svr3)
+		os=-sysv3
+		;;
+	-sysvr4)
+		os=-sysv4
+		;;
+	# This must come after -sysvr4.
+	-sysv*)
+		;;
+	-ose*)
+		os=-ose
+		;;
+	-es1800*)
+		os=-ose
+		;;
+	-xenix)
+		os=-xenix
+		;;
+	-*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+		os=-mint
+		;;
+	-aros*)
+		os=-aros
+		;;
+	-kaos*)
+		os=-kaos
+		;;
+	-none)
+		;;
+	*)
+		# Get rid of the `-' at the beginning of $os.
+		os=`echo $os | sed 's/[^-]*-//'`
+		echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2
+		exit 1
+		;;
+esac
+else
+
+# Here we handle the default operating systems that come with various machines.
+# The value should be what the vendor currently ships out the door with their
+# machine or put another way, the most popular os provided with the machine.
+
+# Note that if you're going to try to match "-MANUFACTURER" here (say,
+# "-sun"), then you have to tell the case statement up towards the top
+# that MANUFACTURER isn't an operating system.  Otherwise, code above
+# will signal an error saying that MANUFACTURER isn't an operating
+# system, and we'll never get to this point.
+
+case $basic_machine in
+	*-acorn)
+		os=-riscix1.2
+		;;
+	arm*-rebel)
+		os=-linux
+		;;
+	arm*-semi)
+		os=-aout
+		;;
+	# This must come before the *-dec entry.
+	pdp10-*)
+		os=-tops20
+		;;
+	pdp11-*)
+		os=-none
+		;;
+	*-dec | vax-*)
+		os=-ultrix4.2
+		;;
+	m68*-apollo)
+		os=-domain
+		;;
+	i386-sun)
+		os=-sunos4.0.2
+		;;
+	m68000-sun)
+		os=-sunos3
+		# This also exists in the configure program, but was not the
+		# default.
+		# os=-sunos4
+		;;
+	m68*-cisco)
+		os=-aout
+		;;
+	mips*-cisco)
+		os=-elf
+		;;
+	mips*-*)
+		os=-elf
+		;;
+	or32-*)
+		os=-coff
+		;;
+	*-tti)	# must be before sparc entry or we get the wrong os.
+		os=-sysv3
+		;;
+	sparc-* | *-sun)
+		os=-sunos4.1.1
+		;;
+	*-be)
+		os=-beos
+		;;
+	*-ibm)
+		os=-aix
+		;;
+	*-wec)
+		os=-proelf
+		;;
+	*-winbond)
+		os=-proelf
+		;;
+	*-oki)
+		os=-proelf
+		;;
+	*-hp)
+		os=-hpux
+		;;
+	*-hitachi)
+		os=-hiux
+		;;
+	i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent)
+		os=-sysv
+		;;
+	*-cbm)
+		os=-amigaos
+		;;
+	*-dg)
+		os=-dgux
+		;;
+	*-dolphin)
+		os=-sysv3
+		;;
+	m68k-ccur)
+		os=-rtu
+		;;
+	m88k-omron*)
+		os=-luna
+		;;
+	*-next )
+		os=-nextstep
+		;;
+	*-sequent)
+		os=-ptx
+		;;
+	*-crds)
+		os=-unos
+		;;
+	*-ns)
+		os=-genix
+		;;
+	i370-*)
+		os=-mvs
+		;;
+	*-next)
+		os=-nextstep3
+		;;
+	*-gould)
+		os=-sysv
+		;;
+	*-highlevel)
+		os=-bsd
+		;;
+	*-encore)
+		os=-bsd
+		;;
+	*-sgi)
+		os=-irix
+		;;
+	*-siemens)
+		os=-sysv4
+		;;
+	*-masscomp)
+		os=-rtu
+		;;
+	f30[01]-fujitsu | f700-fujitsu)
+		os=-uxpv
+		;;
+	*-rom68k)
+		os=-coff
+		;;
+	*-*bug)
+		os=-coff
+		;;
+	*-apple)
+		os=-macos
+		;;
+	*-atari*)
+		os=-mint
+		;;
+	*)
+		os=-none
+		;;
+esac
+fi
+
+# Here we handle the case where we know the os, and the CPU type, but not the
+# manufacturer.  We pick the logical manufacturer.
+vendor=unknown
+case $basic_machine in
+	*-unknown)
+		case $os in
+			-riscix*)
+				vendor=acorn
+				;;
+			-sunos*)
+				vendor=sun
+				;;
+			-aix*)
+				vendor=ibm
+				;;
+			-beos*)
+				vendor=be
+				;;
+			-hpux*)
+				vendor=hp
+				;;
+			-mpeix*)
+				vendor=hp
+				;;
+			-hiux*)
+				vendor=hitachi
+				;;
+			-unos*)
+				vendor=crds
+				;;
+			-dgux*)
+				vendor=dg
+				;;
+			-luna*)
+				vendor=omron
+				;;
+			-genix*)
+				vendor=ns
+				;;
+			-mvs* | -opened*)
+				vendor=ibm
+				;;
+			-ptx*)
+				vendor=sequent
+				;;
+			-vxsim* | -vxworks* | -windiss*)
+				vendor=wrs
+				;;
+			-aux*)
+				vendor=apple
+				;;
+			-hms*)
+				vendor=hitachi
+				;;
+			-mpw* | -macos*)
+				vendor=apple
+				;;
+			-*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+				vendor=atari
+				;;
+			-vos*)
+				vendor=stratus
+				;;
+		esac
+		basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"`
+		;;
+esac
+
+echo $basic_machine$os
+exit 0
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/configure.in b/configure.in
new file mode 100644
index 0000000000000000000000000000000000000000..e8608311148d619578fc24d57a728d590808804e
--- /dev/null
+++ b/configure.in
@@ -0,0 +1,617 @@
+#                                               -*- Autoconf -*-
+# Process this file with autoconf and autoheader to produce a configure script.
+
+# This Autoconf file was cobbled from various locations. In particular, a bunch
+# of the platform checks have been taken straight from OpenSSH's configure.ac
+# Huge thanks to them for dealing with the horrible platform-specifics :)
+
+AC_PREREQ(2.50)
+AC_INIT(buffer.c)
+
+OLDCFLAGS=$CFLAGS
+# Checks for programs.
+AC_PROG_CC
+AC_PROG_MAKE_SET
+
+if test -z "$LD" ; then
+	LD=$CC
+fi
+AC_SUBST(LD)	
+
+if test -z "$OLDCFLAGS" && test "$GCC" = "yes"; then
+	AC_MSG_RESULT(No \$CFLAGS set... using "-Os -W -Wall" for GCC)
+	CFLAGS="-Os -W -Wall"
+fi
+
+# Host specific options
+# this isn't a definitive list of hosts, they are just added as required
+AC_CANONICAL_HOST
+
+case "$host" in
+
+*-*-linux*)
+	no_ptmx_check=1
+	;;
+
+*-*-solaris*)
+	CFLAGS="$CFLAGS -I/usr/local/include"
+	LDFLAGS="$LDFLAGS -L/usr/local/lib -R/usr/local/lib"
+	conf_lastlog_location="/var/adm/lastlog"
+	AC_MSG_CHECKING(for obsolete utmp and wtmp in solaris2.x)
+	sol2ver=`echo "$host"| sed -e 's/.*[[0-9]]\.//'`
+	if test "$sol2ver" -ge 8; then
+		AC_MSG_RESULT(yes)
+		AC_DEFINE(DISABLE_UTMP,,Disable utmp)
+		AC_DEFINE(DISABLE_WTMP,,Disable wtmp)
+	else
+		AC_MSG_RESULT(no)
+	fi
+	AC_CHECK_LIB(socket, socket, LIBS="$LIBS -lsocket")
+	AC_CHECK_LIB(nsl, yp_match, LIBS="$LIBS -lnsl")
+	;;
+
+*-*-aix*)
+	AC_DEFINE(AIX,,Using AIX)
+	# OpenSSH thinks it's broken. If it isn't, let me know.
+	AC_DEFINE(BROKEN_GETADDRINFO,,Broken getaddrinfo)
+	;;
+	
+*-*-hpux*)
+	LIBS="$LIBS -lsec"
+	# It's probably broken.
+	AC_DEFINE(BROKEN_GETADDRINFO,,Broken getaddrinfo)
+	;;
+*-dec-osf*)
+	AC_DEFINE(BROKEN_GETADDRINFO,,Broken getaddrinfo)
+	;;
+esac
+
+AC_CHECK_TOOL(AR, ar, :)
+AC_CHECK_TOOL(RANLIB, ranlib, :)
+AC_CHECK_TOOL(STRIP, strip, :)
+AC_CHECK_TOOL(INSTALL, install, :)
+
+dnl Can't use login() or logout() with uclibc
+AC_CHECK_DECL(__UCLIBC__, 
+	[
+	no_loginfunc_check=1
+	AC_MSG_RESULT(Using uClibc - login() and logout() probably don't work, so we won't use them.)
+	],,,)
+
+# Checks for libraries.
+AC_CHECK_LIB(crypt, crypt, LIBS="$LIBS -lcrypt")
+
+# Check if zlib is needed
+AC_ARG_WITH(zlib,
+	[  --with-zlib=PATH        Use zlib in PATH],
+	[
+		# option is given
+		if test -d "$withval/lib"; then
+			LDFLAGS="-L${withval}/lib ${LDFLAGS}"
+		else
+			LDFLAGS="-L${withval} ${LDFLAGS}"
+		fi
+		if test -d "$withval/include"; then
+			CPPFLAGS="-I${withval}/include ${CPPFLAGS}"
+		else
+			CPPFLAGS="-I${withval} ${CPPFLAGS}"
+		fi
+	]
+)
+
+AC_ARG_ENABLE(zlib,
+	[  --disable-zlib          Don't include zlib support],
+	[
+		if test "x$enableval" = "xno"; then
+			AC_DEFINE(DISABLE_ZLIB,, Use zlib)
+			AC_MSG_RESULT(Disabling zlib)
+		else
+			AC_CHECK_LIB(z, deflate, , AC_MSG_ERROR([*** zlib missing - install first or check config.log ***]))
+			AC_MSG_RESULT(Enabling zlib)
+		fi
+	],
+	[
+		# if not disabled, check for zlib
+		AC_CHECK_LIB(z, deflate, , AC_MSG_ERROR([*** zlib missing - install first or check config.log ***]))
+		AC_MSG_RESULT(Enabling zlib)
+	]
+)
+
+# Check if pam is needed
+AC_ARG_WITH(pam,
+	[  --with-pam=PATH        Use pam in PATH],
+	[
+		# option is given
+		if test -d "$withval/lib"; then
+			LDFLAGS="-L${withval}/lib ${LDFLAGS}"
+		else
+			LDFLAGS="-L${withval} ${LDFLAGS}"
+		fi
+		if test -d "$withval/include"; then
+			CPPFLAGS="-I${withval}/include ${CPPFLAGS}"
+		else
+			CPPFLAGS="-I${withval} ${CPPFLAGS}"
+		fi
+	]
+)
+
+
+AC_ARG_ENABLE(pam,
+	[  --enable-pam          Try to include PAM support],
+	[
+		if test "x$enableval" = "xyes"; then
+			AC_CHECK_LIB(pam, pam_authenticate, , AC_MSG_ERROR([*** PAM missing - install first or check config.log ***]))
+			AC_MSG_RESULT(Enabling PAM)
+		else
+			AC_DEFINE(DISABLE_PAM,, Use PAM)
+			AC_MSG_RESULT(Disabling PAM)
+		fi
+	],
+	[
+		# disable it by default
+		AC_DEFINE(DISABLE_PAM,, Use PAM)
+		AC_MSG_RESULT(Disabling PAM)
+	]
+)
+
+AC_ARG_ENABLE(openpty,
+	[  --disable-openpty       Don't use openpty, use alternative method],
+	[
+		if test "x$enableval" = "xno"; then
+			AC_MSG_RESULT(Not using openpty)
+		else
+			AC_MSG_RESULT(Using openpty if available)
+			AC_SEARCH_LIBS(openpty, util, [AC_DEFINE(HAVE_OPENPTY,,Have openpty() function)])
+		fi
+	],
+	[
+		AC_MSG_RESULT(Using openpty if available)
+		AC_SEARCH_LIBS(openpty, util, [AC_DEFINE(HAVE_OPENPTY)])
+	]
+)
+		
+
+AC_ARG_ENABLE(syslog,
+	[  --disable-syslog        Don't include syslog support],
+	[
+		if test "x$enableval" = "xno"; then
+			AC_DEFINE(DISABLE_SYSLOG,, Using syslog)
+			AC_MSG_RESULT(Disabling syslog)
+		else
+			AC_MSG_RESULT(Enabling syslog)
+		fi
+	],
+	[
+		AC_MSG_RESULT(Enabling syslog)
+	]
+)
+
+AC_ARG_ENABLE(shadow,
+	[  --disable-shadow        Don't use shadow passwords (if available)],
+	[
+		if test "x$enableval" = "xno"; then
+			AC_MSG_RESULT(Not using shadow passwords)
+		else
+			AC_CHECK_HEADERS([shadow.h])
+			AC_MSG_RESULT(Using shadow passwords if available)
+		fi
+	],
+	[
+		AC_CHECK_HEADERS([shadow.h])
+		AC_MSG_RESULT(Using shadow passwords if available)
+	]
+)
+			
+
+# Checks for header files.
+AC_HEADER_STDC
+AC_HEADER_SYS_WAIT
+AC_CHECK_HEADERS([fcntl.h limits.h netinet/in.h netinet/tcp.h stdlib.h string.h sys/socket.h sys/time.h termios.h unistd.h crypt.h pty.h ioctl.h libutil.h libgen.h inttypes.h stropts.h utmp.h utmpx.h lastlog.h paths.h util.h netdb.h security/pam_appl.h pam/pam_appl.h netinet/in_systm.h])
+
+# Checks for typedefs, structures, and compiler characteristics.
+AC_C_CONST
+AC_TYPE_UID_T
+AC_TYPE_MODE_T
+AC_TYPE_PID_T
+AC_TYPE_SIZE_T
+AC_HEADER_TIME
+
+AC_CHECK_TYPES([uint16_t, u_int16_t, struct sockaddr_storage])
+AC_CHECK_TYPE([socklen_t], ,[
+	AC_MSG_CHECKING([for socklen_t equivalent])
+	AC_CACHE_VAL([curl_cv_socklen_t_equiv],
+	[
+	# Systems have either "struct sockaddr *" or
+	# "void *" as the second argument to getpeername
+	curl_cv_socklen_t_equiv=
+	for arg2 in "struct sockaddr" void; do
+		for t in int size_t unsigned long "unsigned long"; do
+		AC_TRY_COMPILE([
+			#include <sys/types.h>
+			#include <sys/socket.h>
+
+			int getpeername (int, $arg2 *, $t *);
+		],[
+			$t len;
+			getpeername(0,0,&len);
+		],[
+			curl_cv_socklen_t_equiv="$t"
+			break
+		])
+		done
+	done
+
+	if test "x$curl_cv_socklen_t_equiv" = x; then
+		AC_MSG_ERROR([Cannot find a type to use in place of socklen_t])
+	fi
+	])
+	AC_MSG_RESULT($curl_cv_socklen_t_equiv)
+	AC_DEFINE_UNQUOTED(socklen_t, $curl_cv_socklen_t_equiv,
+			[type to use in place of socklen_t if not defined])],
+	[#include <sys/types.h>
+	#include <sys/socket.h>])
+
+# for the fake-rfc2553 stuff - straight from OpenSSH
+
+AC_CACHE_CHECK([for struct sockaddr_storage], ac_cv_have_struct_sockaddr_storage, [
+	AC_TRY_COMPILE(
+		[
+#include <sys/types.h>
+#include <sys/socket.h>
+		],
+		[ struct sockaddr_storage s; ],
+		[ ac_cv_have_struct_sockaddr_storage="yes" ],
+		[ ac_cv_have_struct_sockaddr_storage="no" ]
+	)
+])
+if test "x$ac_cv_have_struct_sockaddr_storage" = "xyes" ; then
+	AC_DEFINE(HAVE_STRUCT_SOCKADDR_STORAGE)
+fi
+
+AC_CACHE_CHECK([for struct sockaddr_in6], ac_cv_have_struct_sockaddr_in6, [
+	AC_TRY_COMPILE(
+		[
+#include <sys/types.h>
+#include <netinet/in.h>
+		],
+		[ struct sockaddr_in6 s; s.sin6_family = 0; ],
+		[ ac_cv_have_struct_sockaddr_in6="yes" ],
+		[ ac_cv_have_struct_sockaddr_in6="no" ]
+	)
+])
+if test "x$ac_cv_have_struct_sockaddr_in6" = "xyes" ; then
+	AC_DEFINE(HAVE_STRUCT_SOCKADDR_IN6,,Have struct sockaddr_in6)
+fi
+
+AC_CACHE_CHECK([for struct in6_addr], ac_cv_have_struct_in6_addr, [
+	AC_TRY_COMPILE(
+		[
+#include <sys/types.h>
+#include <netinet/in.h>
+		],
+		[ struct in6_addr s; s.s6_addr[0] = 0; ],
+		[ ac_cv_have_struct_in6_addr="yes" ],
+		[ ac_cv_have_struct_in6_addr="no" ]
+	)
+])
+if test "x$ac_cv_have_struct_in6_addr" = "xyes" ; then
+	AC_DEFINE(HAVE_STRUCT_IN6_ADDR,,Have struct in6_addr)
+fi
+
+AC_CACHE_CHECK([for struct addrinfo], ac_cv_have_struct_addrinfo, [
+	AC_TRY_COMPILE(
+		[
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+		],
+		[ struct addrinfo s; s.ai_flags = AI_PASSIVE; ],
+		[ ac_cv_have_struct_addrinfo="yes" ],
+		[ ac_cv_have_struct_addrinfo="no" ]
+	)
+])
+if test "x$ac_cv_have_struct_addrinfo" = "xyes" ; then
+	AC_DEFINE(HAVE_STRUCT_ADDRINFO,,Have struct addrinfo)
+fi
+
+
+# IRIX has a const char return value for gai_strerror()
+AC_CHECK_FUNCS(gai_strerror,[
+	AC_DEFINE(HAVE_GAI_STRERROR)
+	AC_TRY_COMPILE([
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+
+const char *gai_strerror(int);],[
+char *str;
+
+str = gai_strerror(0);],[
+		AC_DEFINE(HAVE_CONST_GAI_STRERROR_PROTO, 1,
+		[Define if gai_strerror() returns const char *])])])
+
+# for loginrec.c
+
+AC_CHECK_MEMBERS([struct utmp.ut_host, struct utmp.ut_pid, struct utmp.ut_type, struct utmp.ut_tv, struct utmp.ut_id, struct utmp.ut_addr, struct utmp.ut_addr_v6, struct utmp.ut_exit, struct utmp.ut_time],,,[
+#include <sys/types.h>
+#if HAVE_UTMP_H
+#include <utmp.h>
+#endif
+])
+
+AC_CHECK_MEMBERS([struct utmpx.ut_host, struct utmpx.ut_syslen, struct utmpx.ut_type, struct utmpx.ut_id, struct utmpx.ut_addr, struct utmpx.ut_addr_v6, struct utmpx.ut_time, struct utmpx.ut_tv, struct sockaddr_storage.ss_family, struct sockadd_storage.__family],,,[
+#include <sys/types.h>
+#include <sys/socket.h>
+#if HAVE_UTMPX_H
+#include <utmpx.h>
+#endif
+])
+
+AC_CHECK_FUNCS(endutent getutent getutid getutline pututline setutent)
+AC_CHECK_FUNCS(utmpname)
+AC_CHECK_FUNCS(endutxent getutxent getutxid getutxline pututxline )
+AC_CHECK_FUNCS(setutxent utmpxname)
+AC_CHECK_FUNCS(logout updwtmp logwtmp)
+
+dnl Added from OpenSSH 3.6.1p2's configure.ac
+
+dnl allow user to disable some login recording features
+AC_ARG_ENABLE(lastlog,
+	[  --disable-lastlog       Disable use of lastlog even if detected [no]],
+	[ AC_DEFINE(DISABLE_LASTLOG,,Disable use of lastlog()) ]
+)
+AC_ARG_ENABLE(utmp,
+	[  --disable-utmp          Disable use of utmp even if detected [no]],
+	[ AC_DEFINE(DISABLE_UTMP,,Disable use of utmp) ]
+)
+AC_ARG_ENABLE(utmpx,
+	[  --disable-utmpx         Disable use of utmpx even if detected [no]],
+	[ AC_DEFINE(DISABLE_UTMPX,,Disable use of utmpx) ]
+)
+AC_ARG_ENABLE(wtmp,
+	[  --disable-wtmp          Disable use of wtmp even if detected [no]],
+	[ AC_DEFINE(DISABLE_WTMP,,Disable use of wtmp) ]
+)
+AC_ARG_ENABLE(wtmpx,
+	[  --disable-wtmpx         Disable use of wtmpx even if detected [no]],
+	[ AC_DEFINE(DISABLE_WTMPX,,Disable use of wtmpx) ]
+)
+AC_ARG_ENABLE(loginfunc,
+	[  --disable-loginfunc     Disable use of login() etc. [no]],
+	[ no_loginfunc_check=1
+	AC_MSG_RESULT(Not using login() etc) ]
+)
+AC_ARG_ENABLE(pututline,
+	[  --disable-pututline     Disable use of pututline() etc. ([uw]tmp) [no]],
+	[ AC_DEFINE(DISABLE_PUTUTLINE,,Disable use of pututline()) ]
+)
+AC_ARG_ENABLE(pututxline,
+	[  --disable-pututxline    Disable use of pututxline() etc. ([uw]tmpx) [no]],
+	[ AC_DEFINE(DISABLE_PUTUTXLINE,,Disable use of pututxline()) ]
+)
+AC_ARG_WITH(lastlog,
+  [  --with-lastlog=FILE|DIR specify lastlog location [common locations]],
+	[
+		if test "x$withval" = "xno" ; then	
+			AC_DEFINE(DISABLE_LASTLOG)
+		else
+			conf_lastlog_location=$withval
+		fi
+	]
+)
+
+if test -z "$no_loginfunc_check"; then
+	dnl    Checks for libutil functions (login(), logout() etc, not openpty() )
+	AC_SEARCH_LIBS(login, util bsd, [AC_DEFINE(HAVE_LOGIN,,Have login() function)])
+	AC_CHECK_FUNCS(logout updwtmp logwtmp)
+fi
+
+dnl lastlog, [uw]tmpx? detection
+dnl  NOTE: set the paths in the platform section to avoid the
+dnl   need for command-line parameters
+dnl lastlog and [uw]tmp are subject to a file search if all else fails
+
+dnl lastlog detection
+dnl  NOTE: the code itself will detect if lastlog is a directory
+AC_MSG_CHECKING([if your system defines LASTLOG_FILE])
+AC_TRY_COMPILE([
+#include <sys/types.h>
+#include <utmp.h>
+#ifdef HAVE_LASTLOG_H
+#  include <lastlog.h>
+#endif
+#ifdef HAVE_PATHS_H
+#  include <paths.h>
+#endif
+#ifdef HAVE_LOGIN_H
+# include <login.h>
+#endif
+	],
+	[ char *lastlog = LASTLOG_FILE; ],
+	[ AC_MSG_RESULT(yes) ],
+	[
+		AC_MSG_RESULT(no)
+		AC_MSG_CHECKING([if your system defines _PATH_LASTLOG])
+		AC_TRY_COMPILE([
+#include <sys/types.h>
+#include <utmp.h>
+#ifdef HAVE_LASTLOG_H
+#  include <lastlog.h>
+#endif
+#ifdef HAVE_PATHS_H
+#  include <paths.h>
+#endif
+		],
+		[ char *lastlog = _PATH_LASTLOG; ],
+		[ AC_MSG_RESULT(yes) ],
+		[
+			AC_MSG_RESULT(no)
+			system_lastlog_path=no
+		])
+	]
+)
+
+if test -z "$conf_lastlog_location"; then
+	if test x"$system_lastlog_path" = x"no" ; then
+		for f in /var/log/lastlog /usr/adm/lastlog /var/adm/lastlog /etc/security/lastlog ; do
+				if (test -d "$f" || test -f "$f") ; then
+					conf_lastlog_location=$f
+				fi
+		done
+		if test -z "$conf_lastlog_location"; then
+			AC_MSG_WARN([** Cannot find lastlog **])
+			dnl Don't define DISABLE_LASTLOG - that means we don't try wtmp/wtmpx
+		fi
+	fi
+fi
+
+if test -n "$conf_lastlog_location"; then
+	AC_DEFINE_UNQUOTED(CONF_LASTLOG_FILE, "$conf_lastlog_location", lastlog file location)
+fi	
+
+dnl utmp detection
+AC_MSG_CHECKING([if your system defines UTMP_FILE])
+AC_TRY_COMPILE([
+#include <sys/types.h>
+#include <utmp.h>
+#ifdef HAVE_PATHS_H
+#  include <paths.h>
+#endif
+	],
+	[ char *utmp = UTMP_FILE; ],
+	[ AC_MSG_RESULT(yes) ],
+	[ AC_MSG_RESULT(no)
+	  system_utmp_path=no ]
+)
+if test -z "$conf_utmp_location"; then
+	if test x"$system_utmp_path" = x"no" ; then
+		for f in /etc/utmp /usr/adm/utmp /var/run/utmp; do
+			if test -f $f ; then
+				conf_utmp_location=$f
+			fi
+		done
+		if test -z "$conf_utmp_location"; then
+			AC_DEFINE(DISABLE_UTMP)
+		fi
+	fi
+fi
+if test -n "$conf_utmp_location"; then
+	AC_DEFINE_UNQUOTED(CONF_UTMP_FILE, "$conf_utmp_location", utmp file location)
+fi	
+
+dnl wtmp detection
+AC_MSG_CHECKING([if your system defines WTMP_FILE])
+AC_TRY_COMPILE([
+#include <sys/types.h>
+#include <utmp.h>
+#ifdef HAVE_PATHS_H
+#  include <paths.h>
+#endif
+	],
+	[ char *wtmp = WTMP_FILE; ],
+	[ AC_MSG_RESULT(yes) ],
+	[ AC_MSG_RESULT(no)
+	  system_wtmp_path=no ]
+)
+if test -z "$conf_wtmp_location"; then
+	if test x"$system_wtmp_path" = x"no" ; then
+		for f in /usr/adm/wtmp /var/log/wtmp; do
+			if test -f $f ; then
+				conf_wtmp_location=$f
+			fi
+		done
+		if test -z "$conf_wtmp_location"; then
+			AC_DEFINE(DISABLE_WTMP)
+		fi
+	fi
+fi
+if test -n "$conf_wtmp_location"; then
+	AC_DEFINE_UNQUOTED(CONF_WTMP_FILE, "$conf_wtmp_location", wtmp file location)
+fi	
+
+
+dnl utmpx detection - I don't know any system so perverse as to require
+dnl  utmpx, but not define UTMPX_FILE (ditto wtmpx.) No doubt it's out
+dnl  there, though.
+AC_MSG_CHECKING([if your system defines UTMPX_FILE])
+AC_TRY_COMPILE([
+#include <sys/types.h>
+#include <utmp.h>
+#ifdef HAVE_UTMPX_H
+#include <utmpx.h>
+#endif
+#ifdef HAVE_PATHS_H
+#  include <paths.h>
+#endif
+	],
+	[ char *utmpx = UTMPX_FILE; ],
+	[ AC_MSG_RESULT(yes) ],
+	[ AC_MSG_RESULT(no)
+	  system_utmpx_path=no ]
+)
+if test -z "$conf_utmpx_location"; then
+	if test x"$system_utmpx_path" = x"no" ; then
+		AC_DEFINE(DISABLE_UTMPX)
+	fi
+else
+	AC_DEFINE_UNQUOTED(CONF_UTMPX_FILE, "$conf_utmpx_location", utmpx file location)
+fi	
+
+dnl wtmpx detection
+AC_MSG_CHECKING([if your system defines WTMPX_FILE])
+AC_TRY_COMPILE([
+#include <sys/types.h>
+#include <utmp.h>
+#ifdef HAVE_UTMPX_H
+#include <utmpx.h>
+#endif
+#ifdef HAVE_PATHS_H
+#  include <paths.h>
+#endif
+	],
+	[ char *wtmpx = WTMPX_FILE; ],
+	[ AC_MSG_RESULT(yes) ],
+	[ AC_MSG_RESULT(no)
+	  system_wtmpx_path=no ]
+)
+if test -z "$conf_wtmpx_location"; then
+	if test x"$system_wtmpx_path" = x"no" ; then
+		AC_DEFINE(DISABLE_WTMPX)
+	fi
+else
+	AC_DEFINE_UNQUOTED(CONF_WTMPX_FILE, "$conf_wtmpx_location", wtmpx file location)
+fi	
+
+# Checks for library functions.
+AC_PROG_GCC_TRADITIONAL
+AC_FUNC_MEMCMP
+AC_FUNC_SELECT_ARGTYPES
+AC_TYPE_SIGNAL
+AC_CHECK_FUNCS([dup2 getspnam getusershell memset putenv select socket strdup clearenv strlcpy strlcat daemon basename _getpty getaddrinfo freeaddrinfo getnameinfo])
+
+AC_SEARCH_LIBS(basename, gen, AC_DEFINE(HAVE_BASENAME))
+
+# Solaris needs ptmx
+if test -z "$no_ptmx_check" ; then
+	if test x"$cross_compiling" = x"no" ; then
+		AC_CHECK_FILE("/dev/ptmx", AC_DEFINE(USE_DEV_PTMX,,Use /dev/ptmx))
+	else
+		AC_MSG_RESULT(Not checking for /dev/ptmx, we're cross-compiling)
+	fi
+fi
+
+if test -z "$no_ptc_check" ; then
+	if test x"$cross_compiling" = x"no" ; then
+		AC_CHECK_FILE("/dev/ptc", AC_DEFINE(HAVE_DEV_PTS_AND_PTC,,Use /dev/ptc & /dev/pts))
+	else
+		AC_MSG_RESULT(Not checking for /dev/ptc & /dev/pts\, we're cross-compiling)
+	fi
+fi
+
+AC_EXEEXT
+AC_CONFIG_HEADER(config.h)
+AC_OUTPUT(Makefile)
+AC_OUTPUT(libtomcrypt/Makefile)
+AC_OUTPUT(libtommath/Makefile)
+AC_MSG_RESULT()
+AC_MSG_RESULT(Now edit options.h to choose features.)
diff --git a/dbclient.1 b/dbclient.1
new file mode 100644
index 0000000000000000000000000000000000000000..4d7cc3c33eaf34873de0c843244baded269c445c
--- /dev/null
+++ b/dbclient.1
@@ -0,0 +1,74 @@
+.TH dbclient 1
+.SH NAME
+dbclient \- lightweight SSH2 client
+.SH SYNOPSIS
+.B dbclient
+[\-Tt] [\-p
+.I port\fR] [\-i
+.I id\fR] [\-L
+.I l\fR:\fIh\fR:\fIr\fR] [\-R
+.I l\fR:\fIh\fR:\fIr\fR] [\-l
+.IR user ]
+.I host
+.SH DESCRIPTION
+.B dbclient
+is a SSH 2 client designed to be small enough to be used in small memory
+environments, while still being functional and secure enough for general use.
+.SH OPTIONS
+.TP
+.B \-p \fIport
+Remote port.
+Connect to port
+.I port
+on the remote host.
+Default is 22.
+.TP
+.B \-i \fIidfile
+Identity file.
+Read the identity from file
+.I idfile
+(multiple allowed).
+.TP
+.B \-L \fIlocalport\fR:\fIremotehost\fR:\fIremoteport\fR
+Local port forwarding.
+Forward the port
+.I localport
+on the local host to port
+.I remoteport
+on the remote host
+.IR remotehost .
+.TP
+.B \-R \fIlocalport\fR:\fIremotehost\fR:\fIremoteport\fR
+Remote port forwarding.
+Forward the port
+.I remoteport
+on the remote host
+.I remotehost
+to port
+.I localport
+on the local host.
+.TP
+.B \-l \fIuser
+Username.
+Login as
+.I user
+on the remote host.
+.TP
+.B \-t
+Allocate a pty.
+.TP
+.B \-T
+Don't allocate a pty.
+.TP
+.B \-g
+Allow non-local hosts to connect to forwarded ports. Applies to -L and -R
+forwarded ports, though remote connections to -R forwarded ports may be limited
+by the ssh server.
+.SH AUTHOR
+Matt Johnston (matt@ucc.asn.au).
+.br
+Gerrit Pape (pape@smarden.org) wrote this manual page.
+.SH SEE ALSO
+dropbear(8), dropbearkey(8)
+.P
+http://matt.ucc.asn.au/dropbear/dropbear.html
diff --git a/dbmulti.c b/dbmulti.c
new file mode 100644
index 0000000000000000000000000000000000000000..2b8df3f1fb8408d89550e6e9fcaeb2432ce8cfe4
--- /dev/null
+++ b/dbmulti.c
@@ -0,0 +1,90 @@
+/*
+ * 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"
+
+/* definitions are cleanest if we just put them here */
+int dropbear_main(int argc, char ** argv);
+int dropbearkey_main(int argc, char ** argv);
+int dropbearconvert_main(int argc, char ** argv);
+int scp_main(int argc, char ** argv);
+
+int main(int argc, char ** argv) {
+
+	char * progname;
+
+	if (argc > 0) {
+		/* figure which form we're being called as */
+		progname = basename(argv[0]);
+
+#ifdef DBMULTI_dropbear
+		if (strcmp(progname, "dropbear") == 0) {
+			return dropbear_main(argc, argv);
+		}
+#endif
+#ifdef DBMULTI_dbclient
+		if (strcmp(progname, "dbclient") == 0
+				|| strcmp(progname, "ssh") == 0) {
+			return cli_main(argc, argv);
+		}
+#endif
+#ifdef DBMULTI_dropbearkey
+		if (strcmp(progname, "dropbearkey") == 0) {
+			return dropbearkey_main(argc, argv);
+		}
+#endif
+#ifdef DBMULTI_dropbearconvert
+		if (strcmp(progname, "dropbearconvert") == 0) {
+			return dropbearconvert_main(argc, argv);
+		}
+#endif
+#ifdef DBMULTI_scp
+		if (strcmp(progname, "scp") == 0) {
+			return scp_main(argc, argv);
+		}
+#endif
+	}
+
+	fprintf(stderr, "Dropbear multi-purpose version %s\n"
+			"Make a symlink pointing at this binary with one of the following names:\n"
+#ifdef DBMULTI_dropbear
+			"'dropbear' - the Dropbear server\n"
+#endif
+#ifdef DBMULTI_dbclient
+			"'dbclient' or 'ssh' - the Dropbear client\n"
+#endif
+#ifdef DBMULTI_dropbearkey
+			"'dropbearkey' - the key generator\n"
+#endif
+#ifdef DBMULTI_dropbearconvert
+			"'dropbearconvert' - the key converter\n"
+#endif
+#ifdef DBMULTI_scp
+			"'scp' - secure copy\n"
+#endif
+			,
+			DROPBEAR_VERSION);
+	exit(1);
+
+}
diff --git a/dbutil.c b/dbutil.c
new file mode 100644
index 0000000000000000000000000000000000000000..15f51ba5e0086351eef27d149877bbcceab22fe8
--- /dev/null
+++ b/dbutil.c
@@ -0,0 +1,679 @@
+/*
+ * 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.
+ *
+ * strlcat() is copyright as follows:
+ * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+ * 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. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``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 AUTHOR 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. */
+
+#include "includes.h"
+#include "dbutil.h"
+#include "buffer.h"
+#include "session.h"
+#include "atomicio.h"
+
+#define MAX_FMT 100
+
+static void generic_dropbear_exit(int exitcode, const char* format, 
+		va_list param);
+static void generic_dropbear_log(int priority, const char* format, 
+		va_list param);
+
+void (*_dropbear_exit)(int exitcode, const char* format, va_list param) 
+						= generic_dropbear_exit;
+void (*_dropbear_log)(int priority, const char* format, va_list param)
+						= generic_dropbear_log;
+
+#ifdef DEBUG_TRACE
+int debug_trace = 0;
+#endif
+
+#ifndef DISABLE_SYSLOG
+void startsyslog() {
+
+	openlog(PROGNAME, LOG_PID, LOG_AUTHPRIV);
+
+}
+#endif /* DISABLE_SYSLOG */
+
+/* the "format" string must be <= 100 characters */
+void dropbear_close(const char* format, ...) {
+
+	va_list param;
+
+	va_start(param, format);
+	_dropbear_exit(EXIT_SUCCESS, format, param);
+	va_end(param);
+
+}
+
+void dropbear_exit(const char* format, ...) {
+
+	va_list param;
+
+	va_start(param, format);
+	_dropbear_exit(EXIT_FAILURE, format, param);
+	va_end(param);
+}
+
+static void generic_dropbear_exit(int exitcode, const char* format, 
+		va_list param) {
+
+	char fmtbuf[300];
+
+	snprintf(fmtbuf, sizeof(fmtbuf), "Exited: %s", format);
+
+	_dropbear_log(LOG_INFO, fmtbuf, param);
+
+	exit(exitcode);
+}
+
+void fail_assert(const char* expr, const char* file, int line) {
+	dropbear_exit("failed assertion (%s:%d): `%s'", file, line, expr);
+}
+
+static void generic_dropbear_log(int UNUSED(priority), const char* format, 
+		va_list param) {
+
+	char printbuf[1024];
+
+	vsnprintf(printbuf, sizeof(printbuf), format, param);
+
+	fprintf(stderr, "%s\n", printbuf);
+
+}
+
+/* this is what can be called to write arbitrary log messages */
+void dropbear_log(int priority, const char* format, ...) {
+
+	va_list param;
+
+	va_start(param, format);
+	_dropbear_log(priority, format, param);
+	va_end(param);
+}
+
+
+#ifdef DEBUG_TRACE
+void dropbear_trace(const char* format, ...) {
+
+	va_list param;
+
+	if (!debug_trace) {
+		return;
+	}
+
+	va_start(param, format);
+	fprintf(stderr, "TRACE: ");
+	vfprintf(stderr, format, param);
+	fprintf(stderr, "\n");
+	va_end(param);
+}
+#endif /* DEBUG_TRACE */
+
+static void set_sock_priority(int sock) {
+
+	int val;
+
+	/* disable nagle */
+	val = 1;
+	setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void*)&val, sizeof(val));
+
+	/* set the TOS bit. note that this will fail for ipv6, I can't find any
+	 * equivalent. */
+#ifdef IPTOS_LOWDELAY
+	val = IPTOS_LOWDELAY;
+	setsockopt(sock, IPPROTO_IP, IP_TOS, (void*)&val, sizeof(val));
+#endif
+
+#ifdef SO_PRIORITY
+	/* linux specific, sets QoS class.
+	 * 6 looks to be optimal for interactive traffic (see tc-prio(8) ). */
+	val = 6;
+	setsockopt(sock, SOL_SOCKET, SO_PRIORITY, (void*) &val, sizeof(val));
+#endif
+
+}
+
+/* Listen on address:port. 
+ * Special cases are address of "" listening on everything,
+ * and address of NULL listening on localhost only.
+ * Returns the number of sockets bound on success, or -1 on failure. On
+ * failure, if errstring wasn't NULL, it'll be a newly malloced error
+ * string.*/
+int dropbear_listen(const char* address, const char* port,
+		int *socks, unsigned int sockcount, char **errstring, int *maxfd) {
+
+	struct addrinfo hints, *res = NULL, *res0 = NULL;
+	int err;
+	unsigned int nsock;
+	struct linger linger;
+	int val;
+	int sock;
+
+	TRACE(("enter dropbear_listen"))
+	
+	memset(&hints, 0, sizeof(hints));
+	hints.ai_family = AF_UNSPEC; /* TODO: let them flag v4 only etc */
+	hints.ai_socktype = SOCK_STREAM;
+
+	// for calling getaddrinfo:
+	// address == NULL and !AI_PASSIVE: local loopback
+	// address == NULL and AI_PASSIVE: all interfaces
+	// address != NULL: whatever the address says
+	if (!address) {
+		TRACE(("dropbear_listen: local loopback"))
+	} else {
+		if (address[0] == '\0') {
+			TRACE(("dropbear_listen: all interfaces"))
+			address = NULL;
+		}
+		hints.ai_flags = AI_PASSIVE;
+	}
+	err = getaddrinfo(address, port, &hints, &res0);
+
+	if (err) {
+		if (errstring != NULL && *errstring == NULL) {
+			int len;
+			len = 20 + strlen(gai_strerror(err));
+			*errstring = (char*)m_malloc(len);
+			snprintf(*errstring, len, "Error resolving: %s", gai_strerror(err));
+		}
+		if (res0) {
+			freeaddrinfo(res0);
+			res0 = NULL;
+		}
+		TRACE(("leave dropbear_listen: failed resolving"))
+		return -1;
+	}
+
+
+	nsock = 0;
+	for (res = res0; res != NULL && nsock < sockcount;
+			res = res->ai_next) {
+
+		/* Get a socket */
+		socks[nsock] = socket(res->ai_family, res->ai_socktype,
+				res->ai_protocol);
+
+		sock = socks[nsock]; /* For clarity */
+
+		if (sock < 0) {
+			err = errno;
+			TRACE(("socket() failed"))
+			continue;
+		}
+
+		/* Various useful socket options */
+		val = 1;
+		/* set to reuse, quick timeout */
+		setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void*) &val, sizeof(val));
+		linger.l_onoff = 1;
+		linger.l_linger = 5;
+		setsockopt(sock, SOL_SOCKET, SO_LINGER, (void*)&linger, sizeof(linger));
+
+		set_sock_priority(sock);
+
+		if (bind(sock, res->ai_addr, res->ai_addrlen) < 0) {
+			err = errno;
+			close(sock);
+			TRACE(("bind(%s) failed", port))
+			continue;
+		}
+
+		if (listen(sock, 20) < 0) {
+			err = errno;
+			close(sock);
+			TRACE(("listen() failed"))
+			continue;
+		}
+
+		*maxfd = MAX(*maxfd, sock);
+
+		nsock++;
+	}
+
+	if (res0) {
+		freeaddrinfo(res0);
+		res0 = NULL;
+	}
+
+	if (nsock == 0) {
+		if (errstring != NULL && *errstring == NULL) {
+			int len;
+			len = 20 + strlen(strerror(err));
+			*errstring = (char*)m_malloc(len);
+			snprintf(*errstring, len, "Error listening: %s", strerror(err));
+			TRACE(("leave dropbear_listen: failure, %s", strerror(err)))
+			return -1;
+		}
+	}
+
+	TRACE(("leave dropbear_listen: success, %d socks bound", nsock))
+	return nsock;
+}
+
+/* Connect via TCP to a host. Connection will try ipv4 or ipv6, will
+ * return immediately if nonblocking is set. On failure, if errstring
+ * wasn't null, it will be a newly malloced error message */
+
+/* TODO: maxfd */
+int connect_remote(const char* remotehost, const char* remoteport,
+		int nonblocking, char ** errstring) {
+
+	struct addrinfo *res0 = NULL, *res = NULL, hints;
+	int sock;
+	int err;
+
+	TRACE(("enter connect_remote"))
+
+	if (errstring != NULL) {
+		*errstring = NULL;
+	}
+
+	memset(&hints, 0, sizeof(hints));
+	hints.ai_socktype = SOCK_STREAM;
+	hints.ai_family = PF_UNSPEC;
+
+	err = getaddrinfo(remotehost, remoteport, &hints, &res0);
+	if (err) {
+		if (errstring != NULL && *errstring == NULL) {
+			int len;
+			len = 20 + strlen(gai_strerror(err));
+			*errstring = (char*)m_malloc(len);
+			snprintf(*errstring, len, "Error resolving: %s", gai_strerror(err));
+		}
+		TRACE(("Error resolving: %s", gai_strerror(err)))
+		return -1;
+	}
+
+	sock = -1;
+	err = EADDRNOTAVAIL;
+	for (res = res0; res; res = res->ai_next) {
+
+		sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
+		if (sock < 0) {
+			err = errno;
+			continue;
+		}
+
+		if (nonblocking) {
+			if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) {
+				close(sock);
+				sock = -1;
+				if (errstring != NULL && *errstring == NULL) {
+					*errstring = m_strdup("Failed non-blocking");
+				}
+				TRACE(("Failed non-blocking: %s", strerror(errno)))
+				continue;
+			}
+		}
+
+		if (connect(sock, res->ai_addr, res->ai_addrlen) < 0) {
+			if (errno == EINPROGRESS && nonblocking) {
+				TRACE(("Connect in progress"))
+				break;
+			} else {
+				err = errno;
+				close(sock);
+				sock = -1;
+				continue;
+			}
+		}
+
+		break; /* Success */
+	}
+
+	if (sock < 0 && !(errno == EINPROGRESS && nonblocking)) {
+		/* Failed */
+		if (errstring != NULL && *errstring == NULL) {
+			int len;
+			len = 20 + strlen(strerror(err));
+			*errstring = (char*)m_malloc(len);
+			snprintf(*errstring, len, "Error connecting: %s", strerror(err));
+		}
+		TRACE(("Error connecting: %s", strerror(err)))
+	} else {
+		/* Success */
+		set_sock_priority(sock);
+	}
+
+	freeaddrinfo(res0);
+	if (sock > 0 && errstring != NULL && *errstring != NULL) {
+		m_free(*errstring);
+	}
+
+	TRACE(("leave connect_remote: sock %d\n", sock))
+	return sock;
+}
+
+/* Return a string representation of the socket address passed. The return
+ * value is allocated with malloc() */
+unsigned char * getaddrstring(struct sockaddr_storage* addr, int withport) {
+
+	char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
+	char *retstring = NULL;
+	int ret;
+	unsigned int len;
+
+	len = sizeof(struct sockaddr_storage);
+	/* Some platforms such as Solaris 8 require that len is the length
+	 * of the specific structure. */
+	if (addr->ss_family == AF_INET) {
+		len = sizeof(struct sockaddr_in);
+	}
+#ifdef AF_INET6
+	if (addr->ss_family == AF_INET6) {
+		len = sizeof(struct sockaddr_in6);
+	}
+#endif
+
+	ret = getnameinfo((struct sockaddr*)addr, len, hbuf, sizeof(hbuf), 
+			sbuf, sizeof(sbuf), NI_NUMERICSERV | NI_NUMERICHOST);
+
+	if (ret != 0) {
+		/* This is a fairly bad failure - it'll fallback to IP if it
+		 * just can't resolve */
+		dropbear_exit("failed lookup (%d, %d)", ret, errno);
+	}
+
+	if (withport) {
+		len = strlen(hbuf) + 2 + strlen(sbuf);
+		retstring = (char*)m_malloc(len);
+		snprintf(retstring, len, "%s:%s", hbuf, sbuf);
+	} else {
+		retstring = m_strdup(hbuf);
+	}
+
+	return retstring;
+
+}
+
+/* Get the hostname corresponding to the address addr. On failure, the IP
+ * address is returned. The return value is allocated with strdup() */
+char* getaddrhostname(struct sockaddr_storage * addr) {
+
+	char hbuf[NI_MAXHOST];
+	char sbuf[NI_MAXSERV];
+	int ret;
+	unsigned int len;
+#ifdef DO_HOST_LOOKUP
+	const int flags = NI_NUMERICSERV;
+#else
+	const int flags = NI_NUMERICHOST | NI_NUMERICSERV;
+#endif
+
+	len = sizeof(struct sockaddr_storage);
+	/* Some platforms such as Solaris 8 require that len is the length
+	 * of the specific structure. */
+	if (addr->ss_family == AF_INET) {
+		len = sizeof(struct sockaddr_in);
+	}
+#ifdef AF_INET6
+	if (addr->ss_family == AF_INET6) {
+		len = sizeof(struct sockaddr_in6);
+	}
+#endif
+
+
+	ret = getnameinfo((struct sockaddr*)addr, len, hbuf, sizeof(hbuf),
+			sbuf, sizeof(sbuf), flags);
+
+	if (ret != 0) {
+		/* On some systems (Darwin does it) we get EINTR from getnameinfo
+		 * somehow. Eew. So we'll just return the IP, since that doesn't seem
+		 * to exhibit that behaviour. */
+		return getaddrstring(addr, 0);
+	}
+
+	return m_strdup(hbuf);
+}
+
+#ifdef DEBUG_TRACE
+void printhex(const char * label, const unsigned char * buf, int len) {
+
+	int i;
+
+	fprintf(stderr, "%s\n", label);
+	for (i = 0; i < len; i++) {
+		fprintf(stderr, "%02x", buf[i]);
+		if (i % 16 == 15) {
+			fprintf(stderr, "\n");
+		}
+		else if (i % 2 == 1) {
+			fprintf(stderr, " ");
+		}
+	}
+	fprintf(stderr, "\n");
+}
+#endif
+
+/* Strip all control characters from text (a null-terminated string), except
+ * for '\n', '\r' and '\t'.
+ * The result returned is a newly allocated string, this must be free()d after
+ * use */
+char * stripcontrol(const char * text) {
+
+	char * ret;
+	int len, pos;
+	int i;
+	
+	len = strlen(text);
+	ret = m_malloc(len+1);
+
+	pos = 0;
+	for (i = 0; i < len; i++) {
+		if ((text[i] <= '~' && text[i] >= ' ') /* normal printable range */
+				|| text[i] == '\n' || text[i] == '\r' || text[i] == '\t') {
+			ret[pos] = text[i];
+			pos++;
+		}
+	}
+	ret[pos] = 0x0;
+	return ret;
+}
+			
+
+/* reads the contents of filename into the buffer buf, from the current
+ * position, either to the end of the file, or the buffer being full.
+ * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
+int buf_readfile(buffer* buf, const char* filename) {
+
+	int fd;
+	int len;
+	int maxlen;
+
+	fd = open(filename, O_RDONLY);
+
+	if (fd < 0) {
+		close(fd);
+		return DROPBEAR_FAILURE;
+	}
+	
+	do {
+		maxlen = buf->size - buf->pos;
+		len = read(fd, buf_getwriteptr(buf, maxlen),
+				maxlen);
+		buf_incrwritepos(buf, len);
+	} while (len < maxlen && len > 0);
+
+	close(fd);
+	return DROPBEAR_SUCCESS;
+}
+
+/* get a line from the file into buffer in the style expected for an
+ * authkeys file.
+ * Will return DROPBEAR_SUCCESS if data is read, or DROPBEAR_FAILURE on EOF.*/
+/* Only used for ~/.ssh/known_hosts and ~/.ssh/authorized_keys */
+#if defined(DROPBEAR_CLIENT) || defined(ENABLE_SVR_PUBKEY_AUTH)
+int buf_getline(buffer * line, FILE * authfile) {
+
+	int c = EOF;
+
+	TRACE(("enter buf_getline"))
+
+	buf_setpos(line, 0);
+	buf_setlen(line, 0);
+
+	while (line->pos < line->size) {
+
+		c = fgetc(authfile); /*getc() is weird with some uClibc systems*/
+		if (c == EOF || c == '\n' || c == '\r') {
+			goto out;
+		}
+
+		buf_putbyte(line, (unsigned char)c);
+	}
+
+	TRACE(("leave getauthline: line too long"))
+	/* We return success, but the line length will be zeroed - ie we just
+	 * ignore that line */
+	buf_setlen(line, 0);
+
+out:
+
+
+	/* if we didn't read anything before EOF or error, exit */
+	if (c == EOF && line->pos == 0) {
+		TRACE(("leave buf_getline: failure"))
+		return DROPBEAR_FAILURE;
+	} else {
+		TRACE(("leave buf_getline: success"))
+		buf_setpos(line, 0);
+		return DROPBEAR_SUCCESS;
+	}
+
+}	
+#endif
+
+/* make sure that the socket closes */
+void m_close(int fd) {
+
+	int val;
+	do {
+		val = close(fd);
+	} while (val < 0 && errno == EINTR);
+
+	if (val < 0 && errno != EBADF) {
+		/* Linux says EIO can happen */
+		dropbear_exit("Error closing fd %d, %s", fd, strerror(errno));
+	}
+}
+	
+void * m_malloc(size_t size) {
+
+	void* ret;
+
+	if (size == 0) {
+		dropbear_exit("m_malloc failed");
+	}
+	ret = calloc(1, size);
+	if (ret == NULL) {
+		dropbear_exit("m_malloc failed");
+	}
+	return ret;
+
+}
+
+void * m_strdup(const char * str) {
+	char* ret;
+
+	ret = strdup(str);
+	if (ret == NULL) {
+		dropbear_exit("m_strdup failed");
+	}
+	return ret;
+}
+
+void __m_free(void* ptr) {
+	if (ptr != NULL) {
+		free(ptr);
+	}
+}
+
+void * m_realloc(void* ptr, size_t size) {
+
+	void *ret;
+
+	if (size == 0) {
+		dropbear_exit("m_realloc failed");
+	}
+	ret = realloc(ptr, size);
+	if (ret == NULL) {
+		dropbear_exit("m_realloc failed");
+	}
+	return ret;
+}
+
+/* Clear the data, based on the method in David Wheeler's
+ * "Secure Programming for Linux and Unix HOWTO" */
+/* Beware of calling this from within dbutil.c - things might get
+ * optimised away */
+void m_burn(void *data, unsigned int len) {
+	volatile char *p = data;
+
+	if (data == NULL)
+		return;
+	while (len--) {
+		*p++ = 0x66;
+	}
+}
+
+
+void setnonblocking(int fd) {
+
+	TRACE(("setnonblocking: %d", fd))
+
+	if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) {
+		if (errno == ENODEV) {
+			/* Some devices (like /dev/null redirected in)
+			 * can't be set to non-blocking */
+			TRACE(("ignoring ENODEV for setnonblocking"))
+		} else {
+			dropbear_exit("Couldn't set nonblocking");
+		}
+	}
+	TRACE(("leave setnonblocking"))
+}
diff --git a/dbutil.h b/dbutil.h
new file mode 100644
index 0000000000000000000000000000000000000000..d74e17e260512252237bc1551f482cd44a59df2a
--- /dev/null
+++ b/dbutil.h
@@ -0,0 +1,73 @@
+/*
+ * 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 _DBUTIL_H_
+
+#define _DBUTIL_H_
+
+#include "includes.h"
+#include "buffer.h"
+
+#ifndef DISABLE_SYSLOG
+void startsyslog();
+#endif
+
+extern void (*_dropbear_exit)(int exitcode, const char* format, va_list param);
+extern void (*_dropbear_log)(int priority, const char* format, va_list param);
+
+void dropbear_exit(const char* format, ...);
+void dropbear_close(const char* format, ...);
+void dropbear_log(int priority, const char* format, ...);
+void fail_assert(const char* expr, const char* file, int line);
+#ifdef DEBUG_TRACE
+void dropbear_trace(const char* format, ...);
+void printhex(const char * label, const unsigned char * buf, int len);
+extern int debug_trace;
+#endif
+char * stripcontrol(const char * text);
+unsigned char * getaddrstring(struct sockaddr_storage* addr, int withport);
+int dropbear_listen(const char* address, const char* port,
+		int *socks, unsigned int sockcount, char **errstring, int *maxfd);
+int connect_remote(const char* remotehost, const char* remoteport,
+		int nonblocking, char ** errstring);
+char* getaddrhostname(struct sockaddr_storage * addr);
+int buf_readfile(buffer* buf, const char* filename);
+int buf_getline(buffer * line, FILE * authfile);
+
+void m_close(int fd);
+void * m_malloc(size_t size);
+void * m_strdup(const char * str);
+void * m_realloc(void* ptr, size_t size);
+#define m_free(X) __m_free(X); (X) = NULL;
+void __m_free(void* ptr);
+void m_burn(void* data, unsigned int len);
+void setnonblocking(int fd);
+
+/* Used to force mp_ints to be initialised */
+#define DEF_MP_INT(X) mp_int X = {0, 0, 0, NULL}
+
+/* Dropbear assertion */
+#define dropbear_assert(X) do { if (!(X)) { fail_assert(#X, __FILE__, __LINE__); } } while (0)
+
+#endif /* _DBUTIL_H_ */
diff --git a/debian/README.Debian b/debian/README.Debian
new file mode 100644
index 0000000000000000000000000000000000000000..8cdac38218e5bad6f11e242716d17e307c9709c4
--- /dev/null
+++ b/debian/README.Debian
@@ -0,0 +1,41 @@
+Dropbear for Debian
+-------------------
+
+This package will attempt to listen on port 22. If the OpenSSH 
+package ("ssh") is installed, the file /etc/default/dropbear 
+will be set up so that the server does not start by default.
+
+You can run Dropbear concurrently with OpenSSH 'sshd' by 
+modifying /etc/default/dropbear so that "NO_START" is set to 
+"0" and changing the port number that Dropbear runs on. Follow 
+the instructions in the file.
+
+This package suggests you install the "ssh" package. This package 
+provides the "ssh" client program, as well as the "/usr/bin/scp" 
+binary you will need to be able to retrieve files from a server 
+running Dropbear via SCP.
+
+Replacing OpenSSH "sshd" with Dropbear
+--------------------------------------
+
+You will still want to have the "ssh" package installed, as it 
+provides the "ssh" and "scp" binaries. When you install this 
+package, it checks for existing OpenSSH host keys and if found, 
+converts them to the Dropbear format.
+
+If this appears to have worked, you should be able to change over 
+by following these steps:
+
+1. Stop the OpenSSH server
+   % /etc/init.d/ssh stop
+2. Prevent the OpenSSH server from starting in the future
+   % touch /etc/ssh/sshd_not_to_be_run
+3. Modify the Dropbear defaults file, set NO_START to 0 and 
+   ensure DROPBEAR_PORT is set to 22.
+   % editor /etc/default/dropbear
+4. Restart the Dropbear server.
+   % /etc/init.d/dropbear restart
+
+See the Dropbear homepage for more information:
+  http://matt.ucc.asn.au/dropbear/dropbear.html
+
diff --git a/debian/README.Debian.diet b/debian/README.Debian.diet
new file mode 100644
index 0000000000000000000000000000000000000000..bd0cb5cc8021acd8a40e8328f1f83e66c729fb58
--- /dev/null
+++ b/debian/README.Debian.diet
@@ -0,0 +1,15 @@
+Building with the diet libc
+---------------------------
+
+This package optionally can be built with the diet libc instead of the
+glibc to provide small statically linked programs.  The resulting package
+has no dependency on any other package.
+
+To use the diet libc, make sure the latest versions of the dietlibc-dev
+package is installed, and set DEB_BUILD_OPTIONS=diet in the environment
+when building the package, e.g.:
+
+ # apt-get install dietlibc-dev
+ $ DEB_BUILD_OPTIONS=diet fakeroot apt-get source -b dropbear
+
+ -- Gerrit Pape <pape@smarden.org>, Sat, 17 Jul 2004 19:09:34 +0000
diff --git a/debian/README.runit b/debian/README.runit
new file mode 100644
index 0000000000000000000000000000000000000000..4ac2814ac22b3904d36f8e768a1ab38eae7bccb0
--- /dev/null
+++ b/debian/README.runit
@@ -0,0 +1,46 @@
+Using the dropbear SSH server with runit's services supervision
+---------------------------------------------------------------
+
+The dropbear SSH server is perfectly suited to be run under runit's
+service supervision, and this package already has prepared an adequate
+service directory.  Follow these steps to enable the dropbear service
+using the runit package.
+
+If not yet installed on your system, install the runit package, and make
+sure its service supervision is enabled (it's by default)
+
+ # apt-get install runit
+
+Make sure the dropbear service normally handled through the sysv init
+script is stopped
+
+ # /etc/init.d/dropbear stop
+
+Create the system user ``dropbearlog'' which will run the logger service,
+and own the logs
+
+ # adduser --system --home /var/log/dropbear --no-create-home dropbearlog
+
+Create the log directory and make the newly created system user the owner
+of this directory
+
+ # mkdir -p /var/log/dropbear && chown dropbearlog /var/log/dropbear
+
+Optionally adjust the configuration of the dropbear service by editing the
+run script
+
+ # vi /etc/dropbear/run
+
+Finally enable the service by linking dropbear's service directory to
+/var/service/.  The service will be started within five seconds, and
+automatically at boot time.  The sysv init script is disabled; see the
+runsvctrl(8) program for information on how to control services handled by
+runit.  See the svlogd(8) program on how to configure the log service.
+
+ # ln -s /etc/dropbear /var/service/
+
+Optionally check the status of the service a few seconds later
+
+ # runsvstat -l /var/service/dropbear
+
+ -- Gerrit Pape <pape@smarden.org>, Sun, 16 May 2004 15:52:34 +0000
diff --git a/debian/changelog b/debian/changelog
new file mode 100644
index 0000000000000000000000000000000000000000..89c5875f8055f06c6cd80fb21630a8064170429c
--- /dev/null
+++ b/debian/changelog
@@ -0,0 +1,194 @@
+dropbear (0.47-0.1) unstable; urgency=high
+
+  * New upstream release.
+  * SECURITY: Fix incorrect buffer sizing.
+
+ -- Matt Johnston <matt@ucc.asn.au>  Thu, 8 Dec 2005 19:20:21 +0800
+
+dropbear (0.46-2) unstable; urgency=low
+
+  * debian/control: Standards-Version: 3.6.2.1; update descriptions to
+    mention included server and client (thx Tino Keitel).
+  * debian/dropbear.init: allow '/etc/init.d/dropbear stop' even though
+    'NO_START is not set to zero.' (closes: #336723).
+
+ -- Gerrit Pape <pape@smarden.org>  Tue,  6 Dec 2005 13:30:49 +0000
+
+dropbear (0.46-1) unstable; urgency=medium
+
+  * New upstream release, various fixes.
+  * debian/diff/dbclient-usage-typo.diff, debian/diff/manpages.diff: remove;
+    obsolete.
+  * debian/dbclient.1: move to ./dbclient.1.
+
+ -- Matt Johnston <matt@ucc.asn.au>  Fri, 8 July 2005 21:32:55 +0800
+
+dropbear (0.45-3) unstable; urgency=low
+
+  * debian/dropbear.init: init script prints human readable message in case
+    it's disabled (closes: #309099).
+  * debian/dropbear.postinst: configure: restart service through init script
+    instead of start.
+  * debian/dropbear.prerm: set -u -> set -e.
+
+ -- Gerrit Pape <pape@smarden.org>  Wed, 25 May 2005 22:38:17 +0000
+
+dropbear (0.45-2) unstable; urgency=low
+
+  * Matt Johnston:
+    * New upstream release, various fixes.
+
+ -- Gerrit Pape <pape@smarden.org>  Sat, 12 Mar 2005 15:17:55 +0000
+
+dropbear (0.44-1) unstable; urgency=low
+
+  * New upstream release.
+  * debian/rules: install /usr/bin/dbclient; handle possible patches more
+    gracefully; install debian/dbclient.1 man page; enable target patch;
+    minor.
+  * debian/implicit: update to revision 1.10.
+  * debian/dbclient.1: new; man page.
+  * debian/diff/dbclient-usage-typo.diff: new; fix typo.
+  * debian/diff/manpages.diff: new; add references to dbclient man page.
+
+ -- Gerrit Pape <pape@smarden.org>  Sat,  8 Jan 2005 22:50:43 +0000
+
+dropbear (0.43-2) unstable; urgency=high
+
+  * Matt Johnston:
+    * New upstream release 0.43
+    * SECURITY: Don't attempt to free uninitialised buffers in DSS verification
+      code
+    * Handle portforwarding to servers which don't send any initial data
+      (Closes: #258426)
+  * debian/dropbear.postinst: remove code causing bothersome warning on
+    package install (closes: #256752).
+  * debian/README.Debian.diet: new; how to build with the diet libc.
+  * debian/dropbear.docs: add debian/README.Debian.diet.
+  * debian/rules: support "diet" in DEB_BUILD_OPTIONS; minor cleanup.
+
+ -- Gerrit Pape <pape@smarden.org>  Sat, 17 Jul 2004 19:31:19 +0000
+
+dropbear (0.42-1) unstable; urgency=low
+
+  * New upstream release 0.42.
+  * debian/diff/cvs-20040520.diff: remove; obsolete.
+  * debian/rules: disable target patch.
+
+ -- Matt Johnston <matt@ucc.asn.au>  Wed, 16 June 2004 12:44:54 +0800
+
+dropbear (0.41-3) unstable; urgency=low
+
+  * 1st upload to the Debian archive (closes: #216553).
+  * debian/diff/cvs-20040520.diff: new; stable cvs snapshot.
+  * debian/rules: new target patch: apply diffs in debian/diff/, reverse
+    apply in target clean; install man pages.
+  * debian/control: Priority: optional.
+
+ -- Gerrit Pape <pape@smarden.org>  Sun, 23 May 2004 08:32:37 +0000
+
+dropbear (0.41-2) unstable; urgency=low
+
+  * new maintainer.
+  * debian/control: no longer Build-Depends: debhelper; Build-Depends:
+    libz-dev; Standards-Version: 3.6.1.0; Suggests: runit; update
+    descriptions.
+  * debian/rules: stop using debhelper, use implicit rules; cleanup;
+    install dropbearconvert into /usr/lib/dropbear/.
+  * debian/impicit: new; implicit rules.
+  * debian/copyright.in: adapt.
+  * debian/dropbear.init: minor adaptions; test for dropbear service
+    directory.
+  * debian/README.runit: new; how to use dropbear with runit.
+  * debian/README.Debian, debian/docs: rename to debian/dropbear.*.
+  * debian/dropbear.docs: add debian/README.runit
+  * debian/conffiles: rename to debian/dropbear.conffiles; add init
+    script, and run scripts.
+  * debian/postinst: rename to debian/dropbear.postinst; adapt; use
+    invloke-rc.d dropbear start.
+  * debian/dropbear.prerm: new; invoke-rc.d dropbear stop.
+  * debian/postrm: rename to debian/dropbear.postrm; adapt; clean up
+    service directories.
+  * debian/compat, debian/dirs, dropbear.default: remove; obsolete.
+
+ -- Gerrit Pape <pape@smarden.org>  Sun, 16 May 2004 16:50:55 +0000
+
+dropbear (0.41-1) unstable; urgency=low
+
+  * Updated to 0.41 release.
+  * Various minor fixes
+
+ -- Matt Johnston <matt@ucc.asn.au>  Mon, 19 Jan 2004 23:20:54 +0800
+
+dropbear (0.39-1) unstable; urgency=low
+
+  * updated to 0.39 release. Some new features, some bugfixes.
+
+ -- Matt Johnston <matt@ucc.asn.au>  Tue, 16 Dec 2003 16:20:54 +0800
+
+dropbear (0.38-1) unstable; urgency=medium
+
+  * updated to 0.38 release - various important bugfixes
+
+ -- Matt Johnston <matt@ucc.asn.au>  Sat, 11 Oct 2003 16:28:54 +0800
+
+dropbear (0.37-1) unstable; urgency=medium
+
+  * updated to 0.37 release - various important bugfixes
+
+ -- Matt Johnston <matt@ucc.asn.au>  Wed, 24 Sept 2003 19:43:54 +0800
+
+dropbear (0.36-1) unstable; urgency=high
+
+  * updated to 0.36 release - various important bugfixes
+
+ -- Matt Johnston <matt@ucc.asn.au>  Tues, 19 Aug 2003 12:20:54 +0800
+
+dropbear (0.35-1) unstable; urgency=high
+
+  * updated to 0.35 release - contains fix for remotely exploitable
+    vulnerability.
+
+ -- Matt Johnston <matt@ucc.asn.au>  Sun, 17 Aug 2003 05:37:47 +0800
+
+dropbear (0.34-1) unstable; urgency=medium
+
+  * updated to 0.34 release
+
+ -- Matt Johnston <matt@ucc.asn.au>  Fri, 15 Aug 2003 15:10:00 +0800
+
+dropbear (0.33-1) unstable; urgency=medium
+
+  * updated to 0.33 release
+
+ -- Matt Johnston <matt@ucc.asn.au>  Sun, 22 Jun 2003 22:22:00 +0800
+
+dropbear (0.32cvs-1) unstable; urgency=medium
+
+  * now maintained in UCC CVS
+  * debian/copyright.in file added, generated from LICENSE
+
+ -- Grahame Bowland <grahame@angrygoats.net>  Tue, 21 Jun 2003 17:57:02 +0800
+
+dropbear (0.32cvs-1) unstable; urgency=medium
+
+  * sync with CVS
+  * fixes X crash bug
+
+ -- Grahame Bowland <grahame@angrygoats.net>  Tue, 20 Jun 2003 15:04:47 +0800
+
+dropbear (0.32-2) unstable; urgency=low
+
+  * fix creation of host keys to use correct names in /etc/dropbear
+  * init script "restart" function fixed
+  * purging this package now deletes the host keys and /etc/dropbear
+  * change priority in debian/control to 'standard'
+
+ -- Grahame Bowland <grahame@angrygoats.net>  Tue, 17 Jun 2003 15:04:47 +0800
+
+dropbear (0.32-1) unstable; urgency=low
+
+  * Initial Release.
+
+ -- Grahame Bowland <grahame@angrygoats.net>  Tue, 17 Jun 2003 15:04:47 +0800
+
diff --git a/debian/control b/debian/control
new file mode 100644
index 0000000000000000000000000000000000000000..81835b3ae665a535c0c39bbc482ffe8b6c8be944
--- /dev/null
+++ b/debian/control
@@ -0,0 +1,20 @@
+Source: dropbear
+Section: net
+Priority: optional
+Maintainer: Gerrit Pape <pape@smarden.org>
+Build-Depends: libz-dev
+Standards-Version: 3.6.2.1
+
+Package: dropbear
+Architecture: any
+Depends: ${shlibs:Depends}
+Suggests: ssh, runit
+Description: lightweight SSH2 server and client
+ dropbear is a SSH 2 server and client designed to be small enough to
+ be used in small memory environments, while still being functional and
+ secure enough for general use.
+ .
+ It implements most required features of the SSH 2 protocol, and other
+ features such as X11 and authentication agent forwarding.
+ .
+ See http://matt.ucc.asn.au/dropbear/dropbear.html
diff --git a/debian/copyright.in b/debian/copyright.in
new file mode 100644
index 0000000000000000000000000000000000000000..79526d3faa96ccf732942780aec44fe3fb08e409
--- /dev/null
+++ b/debian/copyright.in
@@ -0,0 +1,11 @@
+This package was debianized by Grahame Bowland <grahame.angrygoats.net> on
+Tue, 17 Jun 2003 15:04:47 +0800, maintained temporarily by Matt Johnston
+<matt@ucc.asn.au>, and was adopted by Gerrit Pape <pape@smarden.org> on
+Sun, 16 May 2004 14:38:33 +0000.
+
+It was downloaded from http://matt.ucc.asn.au/dropbear/
+
+Upstream Author: Matt Johnston <matt@ucc.asn.au>
+
+Copyright: 
+
diff --git a/debian/dropbear.README.Debian b/debian/dropbear.README.Debian
new file mode 100644
index 0000000000000000000000000000000000000000..8cdac38218e5bad6f11e242716d17e307c9709c4
--- /dev/null
+++ b/debian/dropbear.README.Debian
@@ -0,0 +1,41 @@
+Dropbear for Debian
+-------------------
+
+This package will attempt to listen on port 22. If the OpenSSH 
+package ("ssh") is installed, the file /etc/default/dropbear 
+will be set up so that the server does not start by default.
+
+You can run Dropbear concurrently with OpenSSH 'sshd' by 
+modifying /etc/default/dropbear so that "NO_START" is set to 
+"0" and changing the port number that Dropbear runs on. Follow 
+the instructions in the file.
+
+This package suggests you install the "ssh" package. This package 
+provides the "ssh" client program, as well as the "/usr/bin/scp" 
+binary you will need to be able to retrieve files from a server 
+running Dropbear via SCP.
+
+Replacing OpenSSH "sshd" with Dropbear
+--------------------------------------
+
+You will still want to have the "ssh" package installed, as it 
+provides the "ssh" and "scp" binaries. When you install this 
+package, it checks for existing OpenSSH host keys and if found, 
+converts them to the Dropbear format.
+
+If this appears to have worked, you should be able to change over 
+by following these steps:
+
+1. Stop the OpenSSH server
+   % /etc/init.d/ssh stop
+2. Prevent the OpenSSH server from starting in the future
+   % touch /etc/ssh/sshd_not_to_be_run
+3. Modify the Dropbear defaults file, set NO_START to 0 and 
+   ensure DROPBEAR_PORT is set to 22.
+   % editor /etc/default/dropbear
+4. Restart the Dropbear server.
+   % /etc/init.d/dropbear restart
+
+See the Dropbear homepage for more information:
+  http://matt.ucc.asn.au/dropbear/dropbear.html
+
diff --git a/debian/dropbear.conffiles b/debian/dropbear.conffiles
new file mode 100644
index 0000000000000000000000000000000000000000..6919006992724e15c132c79d2a4eb0e0f884a2cc
--- /dev/null
+++ b/debian/dropbear.conffiles
@@ -0,0 +1,3 @@
+/etc/init.d/dropbear
+/etc/dropbear/run
+/etc/dropbear/log/run
diff --git a/logs/invmod.log b/debian/dropbear.default
similarity index 100%
rename from logs/invmod.log
rename to debian/dropbear.default
diff --git a/debian/dropbear.docs b/debian/dropbear.docs
new file mode 100644
index 0000000000000000000000000000000000000000..94fec74a8fc6d73494440395f487d52142358ec6
--- /dev/null
+++ b/debian/dropbear.docs
@@ -0,0 +1,4 @@
+README
+TODO
+debian/README.runit
+debian/README.Debian.diet
diff --git a/debian/dropbear.init b/debian/dropbear.init
new file mode 100644
index 0000000000000000000000000000000000000000..7979c8d8c25c08065c3f150aa3b9f90f61421c1f
--- /dev/null
+++ b/debian/dropbear.init
@@ -0,0 +1,61 @@
+#!/bin/sh
+#
+# Do not configure this file. Edit /etc/default/dropbear instead!
+#
+
+PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
+DAEMON=/usr/sbin/dropbear
+NAME=dropbear
+DESC="Dropbear SSH server"
+
+DROPBEAR_PORT=22
+DROPBEAR_EXTRA_ARGS=
+NO_START=0
+
+set -e
+
+cancel() { echo "$1" >&2; exit 0; };
+test ! -r /etc/default/dropbear || . /etc/default/dropbear
+test -x "$DAEMON" || cancel "$DAEMON does not exist or is not executable."
+test ! -h /var/service/dropbear || \
+  cancel '/var/service/dropbear exists, service is controlled through runit.'
+
+test -z "$DROPBEAR_BANNER" || \
+  DROPBEAR_EXTRA_ARGS="$DROPBEAR_EXTRA_ARGS -b $DROPBEAR_BANNER"
+test -n "$DROPBEAR_RSAKEY" || \
+  DROPBEAR_RSAKEY="/etc/dropbear/dropbear_rsa_host_key"
+test -n "$DROPBEAR_DSSKEY" || \
+  DROPBEAR_DSSKEY="/etc/dropbear/dropbear_dss_host_key"
+
+case "$1" in
+  start)
+	test "$NO_START" = "0" || cancel 'NO_START is not set to zero.'
+	echo -n "Starting $DESC: "
+	start-stop-daemon --start --quiet --pidfile /var/run/"$NAME".pid \
+	  --exec "$DAEMON" -- -d "$DROPBEAR_DSSKEY" -r "$DROPBEAR_RSAKEY" \
+	    -p "$DROPBEAR_PORT" $DROPBEAR_EXTRA_ARGS
+	echo "$NAME."
+	;;
+  stop)
+	echo -n "Stopping $DESC: "
+	start-stop-daemon --stop --quiet --oknodo --pidfile /var/run/"$NAME".pid
+	echo "$NAME."
+	;;
+  restart|force-reload)
+	test "$NO_START" = "0" || cancel 'NO_START is not set to zero.'
+	echo -n "Restarting $DESC: "
+	start-stop-daemon --stop --quiet --oknodo --pidfile /var/run/"$NAME".pid
+	sleep 1
+	start-stop-daemon --start --quiet --pidfile /var/run/"$NAME".pid \
+	  --exec "$DAEMON" -- -d "$DROPBEAR_DSSKEY" -r "$DROPBEAR_RSAKEY" \
+	    -p "$DROPBEAR_PORT" $DROPBEAR_EXTRA_ARGS
+	echo "$NAME."
+	;;
+  *)
+	N=/etc/init.d/$NAME
+	echo "Usage: $N {start|stop|restart|force-reload}" >&2
+	exit 1
+	;;
+esac
+
+exit 0
diff --git a/debian/dropbear.postinst b/debian/dropbear.postinst
new file mode 100644
index 0000000000000000000000000000000000000000..312eb0510dd51fa6309e9b9744e96197fdb203df
--- /dev/null
+++ b/debian/dropbear.postinst
@@ -0,0 +1,67 @@
+#!/bin/sh
+set -e
+
+test "$1" = 'configure' || exit 0
+
+if test ! -e /etc/dropbear/dropbear_rsa_host_key; then
+  if test -f /etc/ssh/ssh_host_rsa_key; then
+    echo "Converting existing OpenSSH RSA host key to Dropbear format."
+    /usr/lib/dropbear/dropbearconvert openssh dropbear \
+      /etc/ssh/ssh_host_rsa_key /etc/dropbear/dropbear_rsa_host_key
+  else
+    echo "Generating Dropbear RSA key. Please wait."
+    dropbearkey -t rsa -f /etc/dropbear/dropbear_rsa_host_key
+  fi
+fi
+if test ! -e /etc/dropbear/dropbear_dss_host_key; then
+  if test -f /etc/ssh/ssh_host_dsa_key; then
+    echo "Converting existing OpenSSH RSA host key to Dropbear format."
+    /usr/lib/dropbear/dropbearconvert openssh dropbear \
+      /etc/ssh/ssh_host_dsa_key /etc/dropbear/dropbear_dss_host_key
+  else
+    echo "Generating Dropbear DSS key. Please wait."
+    dropbearkey -t dss -f /etc/dropbear/dropbear_dss_host_key
+  fi
+fi
+if test ! -s /etc/default/dropbear; then 
+  # check whether OpenSSH seems to be installed.
+  if test -x /usr/sbin/sshd; then
+    cat <<EOT
+OpenSSH appears to be installed.  Setting /etc/default/dropbear so that
+Dropbear will not start by default.  Edit this file to change this behaviour.
+
+EOT
+    cat >>/etc/default/dropbear <<EOT
+# disabled because OpenSSH is installed
+# change to NO_START=0 to enable Dropbear
+NO_START=1
+
+EOT
+  fi
+  cat >>/etc/default/dropbear <<EOT
+# the TCP port that Dropbear listens on
+DROPBEAR_PORT=22
+
+# any additional arguments for Dropbear
+DROPBEAR_EXTRA_ARGS=
+
+# specify an optional banner file containing a message to be
+# sent to clients before they connect, such as "/etc/issue.net"
+DROPBEAR_BANNER=""
+
+# RSA hostkey file (default: /etc/dropbear/dropbear_rsa_host_key)
+#DROPBEAR_RSAKEY="/etc/dropbear/dropbear_rsa_host_key"
+
+# DSS hostkey file (default: /etc/dropbear/dropbear_dss_host_key)
+#DROPBEAR_DSSKEY="/etc/dropbear/dropbear_dss_host_key"
+EOT
+fi
+
+if test -x /etc/init.d/dropbear; then
+  update-rc.d dropbear defaults >/dev/null
+  if test -x /usr/sbin/invoke-rc.d; then
+    invoke-rc.d dropbear restart
+  else
+    /etc/init.d/dropbear restart
+  fi
+fi
diff --git a/debian/dropbear.postrm b/debian/dropbear.postrm
new file mode 100644
index 0000000000000000000000000000000000000000..d09dab0712cc99e4d372c3908f3cdde4df7107bb
--- /dev/null
+++ b/debian/dropbear.postrm
@@ -0,0 +1,12 @@
+#! /bin/sh
+set -e
+
+test "$1" = 'purge' || exit 0
+if test -e /etc/dropbear; then
+  rm -f /etc/dropbear/dropbear_rsa_host_key
+  rm -f /etc/dropbear/dropbear_dss_host_key
+  rmdir --ignore-fail-on-non-empty /etc/dropbear
+fi
+update-rc.d dropbear remove >/dev/null
+rm -f /etc/default/dropbear
+rm -rf /etc/dropbear/supervise /etc/dropbear/log/supervise
diff --git a/debian/dropbear.prerm b/debian/dropbear.prerm
new file mode 100644
index 0000000000000000000000000000000000000000..e63cdb8980ddb76c71097b300b11f6ce813ceeba
--- /dev/null
+++ b/debian/dropbear.prerm
@@ -0,0 +1,11 @@
+#!/bin/sh
+set -e
+
+test "$1" = 'remove' || test "$1" = 'deconfigure' || exit 0
+if test -x /etc/init.d/dropbear; then
+  if test -x /usr/sbin/invoke-rc.d; then
+    invoke-rc.d dropbear stop
+  else
+    /etc/init.d/dropbear stop
+  fi
+fi
diff --git a/debian/implicit b/debian/implicit
new file mode 100644
index 0000000000000000000000000000000000000000..57a444a29d5080dab15f7d4fc7696bd2d410f717
--- /dev/null
+++ b/debian/implicit
@@ -0,0 +1,85 @@
+# $Id: implicit,v 1.10 2004/07/03 15:20:00 pape Exp $
+
+.PHONY: deb-checkdir deb-checkuid
+
+deb-checkdir:
+	@test -e debian/control || sh -cx '! : wrong directory'
+deb-checkuid:
+	@test "`id -u`" -eq 0 || sh -cx '! : need root privileges'
+
+%.deb: %.deb-docs %.deb-DEBIAN
+	@rm -f $*.deb $*.deb-checkdir $*.deb-docs $*.deb-docs-base \
+	  $*.deb-docs-docs $*.deb-docs-examples $*.deb-DEBIAN \
+	  $*.deb-DEBIAN-dir $*.deb-DEBIAN-scripts $*.deb-DEBIAN-md5sums
+
+%.udeb: %.deb-DEBIAN
+	@rm -f $*.deb $*.deb-checkdir $*.deb-DEBIAN $*.deb-DEBIAN-dir \
+	  $*.deb-DEBIAN-scripts $*.deb-DEBIAN-md5sums
+
+%.deb-checkdir:
+	@test -d debian/$* || sh -cx '! : directory debian/$* missing'
+	@test "`id -u`" -eq 0 || sh -cx '! : need root privileges'
+
+%.deb-docs-base:
+	: implicit
+	@rm -f debian/$*/usr/share/doc/$*/* || :
+	@install -d -m0755 debian/$*/usr/share/doc/$*
+	: debian/$*/usr/share/doc/$*/
+	@sh -cx 'install -m0644 debian/copyright debian/$*/usr/share/doc/$*/'
+	@sh -cx 'install -m0644 debian/changelog \
+	  debian/$*/usr/share/doc/$*/changelog.Debian'
+	@test ! -r changelog || \
+	  sh -cx 'install -m0644 changelog debian/$*/usr/share/doc/$*/'
+	@test -r debian/$*/usr/share/doc/$*/changelog || \
+	  sh -cx 'mv debian/$*/usr/share/doc/$*/changelog.Debian \
+	    debian/$*/usr/share/doc/$*/changelog'
+	@test -s debian/$*/usr/share/doc/$*/changelog || \
+	  sh -cx 'rm -f debian/$*/usr/share/doc/$*/changelog'
+	@gzip -9 debian/$*/usr/share/doc/$*/changelog*
+%.deb-docs-docs:
+	@for i in `cat debian/$*.docs 2>/dev/null || :`; do \
+	  sh -cx "install -m0644 $$i debian/$*/usr/share/doc/$*/" || exit 1; \
+	done
+	@test ! -r debian/$*.README.Debian || \
+	  sh -cx 'install -m0644 debian/$*.README.Debian \
+	    debian/$*/usr/share/doc/$*/README.Debian'
+	@if test -r debian/$*.NEWS.Debian; then \
+	  sh -cx 'install -m0644 debian/$*.NEWS.Debian \
+	    debian/$*/usr/share/doc/$*/NEWS.Debian && \
+	      gzip -9 debian/$*/usr/share/doc/$*/NEWS.Debian'; \
+	fi
+%.deb-docs-examples:
+	@rm -rf debian/$*/usr/share/doc/$*/examples
+	: debian/$*/usr/share/doc/$*/examples/
+	@test ! -r debian/$*.examples || \
+	  install -d -m0755 debian/$*/usr/share/doc/$*/examples
+	@for i in `cat debian/$*.examples 2>/dev/null || :`; do \
+	  sh -cx "install -m0644 $$i debian/$*/usr/share/doc/$*/examples/" \
+	    || exit 1; \
+	done
+%.deb-docs: %.deb-checkdir %.deb-docs-base %.deb-docs-docs %.deb-docs-examples
+	: debian/$*/usr/share/doc/$*/ ok
+
+%.deb-DEBIAN-base:
+	@rm -rf debian/$*/DEBIAN
+	: debian/$*/DEBIAN/
+	@install -d -m0755 debian/$*/DEBIAN
+	@for i in conffiles shlibs templates; do \
+	  test ! -r debian/$*.$$i || \
+	    sh -cx "install -m0644 debian/$*.$$i debian/$*/DEBIAN/$$i" \
+	      || exit 1; \
+	done
+%.deb-DEBIAN-scripts:
+	@for i in preinst prerm postinst postrm config; do \
+	  test ! -r debian/$*.$$i || \
+	    sh -cx "install -m0755 debian/$*.$$i debian/$*/DEBIAN/$$i" \
+	      || exit 1; \
+	done
+%.deb-DEBIAN-md5sums:
+	: debian/$*/DEBIAN/md5sums
+	@rm -f debian/$*/DEBIAN/md5sums
+	@cd debian/$* && find * -path 'DEBIAN' -prune -o \
+	  -type f -exec md5sum {} >>DEBIAN/md5sums \;
+%.deb-DEBIAN: %.deb-checkdir %.deb-DEBIAN-base %.deb-DEBIAN-scripts \
+	  %.deb-DEBIAN-md5sums
+	: debian/$*/DEBIAN/ ok
diff --git a/debian/rules b/debian/rules
new file mode 100755
index 0000000000000000000000000000000000000000..52c3ea81da035dee7d490a3ab782997569543bf7
--- /dev/null
+++ b/debian/rules
@@ -0,0 +1,107 @@
+#!/usr/bin/make -f
+
+#export DH_OPTIONS
+DEB_HOST_GNU_TYPE ?=$(shell dpkg-architecture -qDEB_HOST_GNU_TYPE)
+DEB_BUILD_GNU_TYPE ?=$(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE)
+
+STRIP =strip
+ifneq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS)))
+  STRIP =: nostrip
+endif
+
+CFLAGS =-Wall -g
+ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS)))
+  CFLAGS +=-O0
+else
+  CFLAGS +=-O2
+endif
+
+CONFFLAGS =
+CC =gcc
+ifneq (,$(findstring diet,$(DEB_BUILD_OPTIONS)))
+  CONFFLAGS =--disable-zlib
+  CC =diet -v -Os gcc -nostdinc
+endif
+
+DIR =$(shell pwd)/debian/dropbear
+
+patch: deb-checkdir patch-stamp
+patch-stamp:
+	for i in `ls -1 debian/diff/*.diff || :`; do \
+	  patch -p0 <$$i || exit 1; \
+	done
+	touch patch-stamp
+
+config.status: patch-stamp configure
+	CC='$(CC)' \
+	CFLAGS='$(CFLAGS)'' -DSFTPSERVER_PATH="\"/usr/lib/sftp-server\""' \
+	  ./configure --host='$(DEB_HOST_GNU_TYPE)' \
+	    --build='$(DEB_BUILD_GNU_TYPE)' --prefix=/usr \
+	    --mandir=\$${prefix}/share/man --infodir=\$${prefix}/share/info \
+	    $(CONFFLAGS)
+
+build: deb-checkdir build-stamp
+build-stamp: config.status
+	$(MAKE) CC='$(CC)' LD='$(CC)'
+	touch build-stamp
+
+clean: deb-checkdir deb-checkuid
+	-$(MAKE) distclean
+	test ! -e patch-stamp || \
+	  for i in `ls -1r debian/diff/*.diff || :`; do \
+	    patch -p0 -R <$$i; \
+	  done
+	rm -f patch-stamp build-stamp config.log config.status
+	rm -rf '$(DIR)'
+	rm -f debian/files debian/substvars debian/copyright changelog
+
+install: deb-checkdir deb-checkuid build-stamp
+	rm -rf '$(DIR)'
+	install -d -m0755 '$(DIR)'/etc/dropbear
+	# programs
+	install -d -m0755 '$(DIR)'/usr/sbin
+	install -m0755 dropbear '$(DIR)'/usr/sbin/dropbear
+	install -d -m0755 '$(DIR)'/usr/bin
+	install -m0755 dbclient '$(DIR)'/usr/bin/dbclient
+	install -m0755 dropbearkey '$(DIR)'/usr/bin/dropbearkey
+	install -d -m0755 '$(DIR)'/usr/lib/dropbear
+	install -m0755 dropbearconvert \
+	  '$(DIR)'/usr/lib/dropbear/dropbearconvert
+	$(STRIP) -R .comment -R .note '$(DIR)'/usr/sbin/* \
+	  '$(DIR)'/usr/bin/* '$(DIR)'/usr/lib/dropbear/*
+	# init and run scripts
+	install -d -m0755 '$(DIR)'/etc/init.d
+	install -m0755 debian/dropbear.init '$(DIR)'/etc/init.d/dropbear
+	install -m0755 debian/service/run '$(DIR)'/etc/dropbear/run
+	install -d -m0755 '$(DIR)'/etc/dropbear/log
+	install -m0755 debian/service/log '$(DIR)'/etc/dropbear/log/run
+	ln -s /var/log/dropbear '$(DIR)'/etc/dropbear/log/main
+	ln -s /var/run/dropbear '$(DIR)'/etc/dropbear/supervise
+	ln -s /var/run/dropbear.log '$(DIR)'/etc/dropbear/log/supervise
+	# man pages
+	install -d -m0755 '$(DIR)'/usr/share/man/man8
+	for i in dropbear.8 dropbearkey.8; do \
+	  install -m644 $$i '$(DIR)'/usr/share/man/man8/ || exit 1; \
+	done
+	gzip -9 '$(DIR)'/usr/share/man/man8/*.8
+	install -d -m0755 '$(DIR)'/usr/share/man/man1
+	install -m644 dbclient.1 '$(DIR)'/usr/share/man/man1/
+	gzip -9 '$(DIR)'/usr/share/man/man1/*.1
+	# copyright, changelog
+	cat debian/copyright.in LICENSE >debian/copyright
+	test -r changelog || ln -s CHANGES changelog
+
+binary-indep:
+
+binary-arch: install dropbear.deb
+	test '$(CC)' != 'gcc' || \
+	  dpkg-shlibdeps '$(DIR)'/usr/sbin/* '$(DIR)'/usr/bin/* \
+	    '$(DIR)'/usr/lib/dropbear/*
+	dpkg-gencontrol -isp -pdropbear -P'$(DIR)'
+	dpkg -b '$(DIR)' ..
+
+binary: binary-arch binary-indep
+
+.PHONY: patch build clean install binary-indep binary-arch binary
+
+include debian/implicit
diff --git a/debian/service/log b/debian/service/log
new file mode 100644
index 0000000000000000000000000000000000000000..2ffb13d455e9cbb32cce847fa91063b5d3145cab
--- /dev/null
+++ b/debian/service/log
@@ -0,0 +1,2 @@
+#!/bin/sh
+exec chpst -udropbearlog svlogd -tt ./main
diff --git a/debian/service/run b/debian/service/run
new file mode 100644
index 0000000000000000000000000000000000000000..f208085bda88be6f7b86eba9a3e9a11bfcec194f
--- /dev/null
+++ b/debian/service/run
@@ -0,0 +1,3 @@
+#!/bin/sh
+exec 2>&1
+exec dropbear -d ./dropbear_dss_host_key -r ./dropbear_rsa_host_key -F -E -p 22
diff --git a/debug.h b/debug.h
new file mode 100644
index 0000000000000000000000000000000000000000..93cb89178b6be47fe612a1e32a7083215aaebfd3
--- /dev/null
+++ b/debug.h
@@ -0,0 +1,74 @@
+/*
+ * 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 _DEBUG_H_
+#define _DEBUG_H_
+
+#include "includes.h"
+
+/* Debugging */
+
+/* Work well for valgrind - don't clear environment, be nicer with signals
+ * etc. Don't use this normally, it might cause problems */
+/* #define DEBUG_VALGRIND */
+
+/* Define this to compile in trace debugging printf()s. 
+ * You'll need to run programs with "-v" to turn this on.
+ *
+ * Caution: Don't use this in an unfriendly environment (ie unfirewalled),
+ * since the printing may not sanitise strings etc. This will add a reasonable
+ * amount to your executable size. */
+/*#define DEBUG_TRACE */
+
+/* All functions writing to the cleartext payload buffer call
+ * CHECKCLEARTOWRITE() before writing. This is only really useful if you're
+ * attempting to track down a problem */
+#define CHECKCLEARTOWRITE() assert(ses.writepayload->len == 0 && \
+		ses.writepayload->pos == 0)
+
+/* Define this, compile with -pg and set GMON_OUT_PREFIX=gmon to get gmon
+ * output when Dropbear forks. This will allow it gprof to be used.
+ * It's useful to run dropbear -F, so you don't fork as much */
+/* (This is Linux specific) */
+/*#define DEBUG_FORKGPROF*/
+
+/* A couple of flags, not usually useful, and mightn't do anything */
+
+/*#define DEBUG_KEXHASH*/
+/*#define DEBUG_RSA*/
+
+/* you don't need to touch this block */
+#ifdef DEBUG_TRACE
+#define TRACE(X) dropbear_trace X;
+#else /*DEBUG_TRACE*/
+#define TRACE(X)
+#endif /*DEBUG_TRACE*/
+
+/* For testing as non-root on shadowed systems, include the crypt of a password
+ * here. You can then log in as any user with this password. Ensure that you
+ * make your own password, and are careful about using this. This will also
+ * disable some of the chown pty code etc*/
+/* #define DEBUG_HACKCRYPT "hL8nrFDt0aJ3E" */ /* this is crypt("password") */
+
+#endif
diff --git a/dropbear.8 b/dropbear.8
new file mode 100644
index 0000000000000000000000000000000000000000..38cf7e22ad4d09034f811379d127139a62f27d61
--- /dev/null
+++ b/dropbear.8
@@ -0,0 +1,84 @@
+.TH dropbear 8
+.SH NAME
+dropbear \- lightweight SSH2 server
+.SH SYNOPSIS
+.B dropbear
+[\-FEmwsgjki] [\-b
+.I banner\fR] [\-d
+.I dsskey\fR] [\-r
+.I rsakey\fR] [\-p
+.IR port ]
+.SH DESCRIPTION
+.B dropbear
+is a SSH 2 server designed to be small enough to be used in small memory
+environments, while still being functional and secure enough for general use.
+.SH OPTIONS
+.TP
+.B \-b \fIbanner
+bannerfile.
+Display the contents of the file
+.I banner
+before user login (default: none).
+.TP
+.B \-d \fIdsskey
+dsskeyfile.
+Use the contents of the file
+.I dsskey
+for the dss host key (default: /etc/dropbear/dropbear_dss_host_key).
+This file is generated with
+.BR dropbearkey (8).
+.TP
+.B \-r \fIrsakey
+rsakeyfile.
+Use the contents of the file
+.I rsakey
+for the rsa host key (default: /etc/dropbear/dropbear_rsa_host_key).
+This file is generated with
+.BR dropbearkey (8).
+.TP
+.B \-F
+Don't fork into background.
+.TP
+.B \-E
+Log to standard error rather than syslog.
+.TP
+.B \-m
+Don't display the message of the day on login.
+.TP
+.B \-w
+Disallow root logins.
+.TP
+.B \-s
+Disable password logins.
+.TP
+.B \-g
+Disable password logins for root.
+.TP
+.B \-j
+Disable local port forwarding.
+.TP
+.B \-k
+Disable remote port forwarding.
+.TP
+.B \-p \fIport
+Listen on specified tcp port
+.IR port ;
+up to 10 can be specified (default 22 if none specified).
+.TP
+.B \-i
+Service program mode.
+Use this option to run
+.B dropbear
+under TCP/IP servers like inetd, tcpsvd, or tcpserver.
+In program mode the \-F option is implied, and \-p options are ignored.
+.TP
+.B \-a
+Allow remote hosts to connect to forwarded ports.
+.SH AUTHOR
+Matt Johnston (matt@ucc.asn.au).
+.br
+Gerrit Pape (pape@smarden.org) wrote this manual page.
+.SH SEE ALSO
+dropbearkey(8), dbclient(1)
+.P
+http://matt.ucc.asn.au/dropbear/dropbear.html
diff --git a/dropbearconvert.c b/dropbearconvert.c
new file mode 100644
index 0000000000000000000000000000000000000000..9e16fe72f63ec45404d4c7f70e8b65c04137478f
--- /dev/null
+++ b/dropbearconvert.c
@@ -0,0 +1,149 @@
+/*
+ * 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. */
+
+/* This program converts to/from Dropbear and OpenSSH private-key formats */
+#include "includes.h"
+#include "signkey.h"
+#include "buffer.h"
+#include "dbutil.h"
+#include "keyimport.h"
+
+
+static int do_convert(int intype, const char* infile, int outtype,
+		const char* outfile);
+
+static void printhelp(char * progname);
+
+static void printhelp(char * progname) {
+
+	fprintf(stderr, "Usage: %s <inputtype> <outputtype> <inputfile> <outputfile>\n\n"
+					"CAUTION: This program is for convenience only, and is not secure if used on\n"
+					"untrusted input files, ie it could allow arbitrary code execution.\n"
+					"All parameters must be specified in order.\n"
+					"\n"
+					"The input and output types are one of:\n"
+					"openssh\n"
+					"dropbear\n"
+					"\n"
+					"Example:\n"
+					"dropbearconvert openssh dropbear /etc/ssh/ssh_host_rsa_key /etc/dropbear_rsa_host_key\n",
+					progname);
+}
+
+#if defined(DBMULTI_dropbearconvert) || !defined(DROPBEAR_MULTI)
+#if defined(DBMULTI_dropbearconvert) && defined(DROPBEAR_MULTI)
+int dropbearconvert_main(int argc, char ** argv) {
+#else 
+int main(int argc, char ** argv) {
+#endif
+
+	int intype, outtype;
+	const char* infile;
+	const char* outfile;
+
+#ifdef DEBUG_TRACE
+	/* It's hard for it to get in the way _too_ much */
+	debug_trace = 1;
+#endif
+
+	/* get the commandline options */
+	if (argc != 5) {
+		fprintf(stderr, "All arguments must be specified\n");
+		goto usage;
+	}
+
+	/* input type */
+	if (argv[1][0] == 'd') {
+		intype = KEYFILE_DROPBEAR;
+	} else if (argv[1][0] == 'o') {
+		intype = KEYFILE_OPENSSH;
+	} else {
+		fprintf(stderr, "Invalid input key type\n");
+		goto usage;
+	}
+
+	/* output type */
+	if (argv[2][0] == 'd') {
+		outtype = KEYFILE_DROPBEAR;
+	} else if (argv[2][0] == 'o') {
+		outtype = KEYFILE_OPENSSH;
+	} else {
+		fprintf(stderr, "Invalid output key type\n");
+		goto usage;
+	}
+
+	/* we don't want output readable by others */
+	umask(077);
+
+	infile = argv[3];
+	outfile = argv[4];
+
+	return do_convert(intype, infile, outtype, outfile);
+
+usage:
+	printhelp(argv[0]);
+	return 1;
+}
+#endif
+
+static int do_convert(int intype, const char* infile, int outtype,
+		const char* outfile) {
+
+	sign_key * key = NULL;
+	char * keytype = NULL;
+	int ret = 1;
+
+	key = import_read(infile, NULL, intype);
+	if (!key) {
+		fprintf(stderr, "Error reading key from '%s'\n",
+				infile);
+		goto out;
+	}
+
+#ifdef DROPBEAR_RSA
+	if (key->rsakey != NULL) {
+		keytype = "RSA";
+	}
+#endif
+#ifdef DROPBEAR_DSS
+	if (key->dsskey != NULL) {
+		keytype = "DSS";
+	}
+#endif
+
+	fprintf(stderr, "Key is a %s key\n", keytype);
+
+	if (import_write(outfile, key, NULL, outtype) != 1) {
+		fprintf(stderr, "Error writing key to '%s'\n", outfile);
+	} else {
+		fprintf(stderr, "Wrote key to '%s'\n", outfile);
+		ret = 0;
+	}
+
+out:
+	if (key) {
+		sign_key_free(key);
+	}
+	return ret;
+}
diff --git a/dropbearkey.8 b/dropbearkey.8
new file mode 100644
index 0000000000000000000000000000000000000000..a093d85ea7f6b317a294e1950d5839f1139f80a0
--- /dev/null
+++ b/dropbearkey.8
@@ -0,0 +1,47 @@
+.TH dropbearkey 8
+.SH NAME
+dropbearkey \- create private keys for the use with dropbear(8)
+.SH SYNOPSIS
+.B dropbearkey
+\-t
+.I type
+\-f
+.I file
+[\-s
+.IR bits ]
+.SH DESCRIPTION
+.B dropbearkey
+generates a type
+.I rsa
+or
+.I dss
+SSH private key, and saves it to a file for the use with the
+.BR dropbear (8)
+SSH 2 server.
+.SH OPTIONS
+.TP
+.B \-t \fItype
+Type of key to generate.
+Must be one of
+.I rsa
+or
+.IR dss .
+.TP
+.B \-f \fIfile
+Write the secret key to the file
+.IR file .
+.TP
+.B \-s \fIbits
+Set the key size to
+.I bits
+bits, should be multiple of 8 (optional).
+.SH EXAMPLE
+ # dropbearkey -t rsa -f /etc/dropbear/dropbear_rsa_host_key
+.SH AUTHOR
+Matt Johnston (matt@ucc.asn.au).
+.br
+Gerrit Pape (pape@smarden.org) wrote this manual page.
+.SH SEE ALSO
+dropbear(8), dbclient(1)
+.P
+http://matt.ucc.asn.au/dropbear/dropbear.html
diff --git a/dropbearkey.c b/dropbearkey.c
new file mode 100644
index 0000000000000000000000000000000000000000..8ceefdc83ea9fda4f2cd129ee6cddda386bcdd0d
--- /dev/null
+++ b/dropbearkey.c
@@ -0,0 +1,355 @@
+/*
+ * 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. */
+
+/* The format of the keyfiles is basically a raw dump of the buffer. Data types
+ * are specified in the transport draft - string is a 32-bit len then the
+ * non-null-terminated string, mp_int is a 32-bit len then the bignum data.
+ * The actual functions are buf_put_rsa_priv_key() and buf_put_dss_priv_key()
+
+ * RSA:
+ * string	"ssh-rsa"
+ * mp_int	e
+ * mp_int	n
+ * mp_int	d
+ * mp_int	p (newer versions only)
+ * mp_int	q (newer versions only) 
+ *
+ * DSS:
+ * string	"ssh-dss"
+ * mp_int	p
+ * mp_int	q
+ * mp_int	g
+ * mp_int	y
+ * mp_int	x
+ *
+ */
+#include "includes.h"
+#include "signkey.h"
+#include "buffer.h"
+#include "dbutil.h"
+
+#include "genrsa.h"
+#include "gendss.h"
+
+static void printhelp(char * progname);
+
+#define RSA_SIZE (1024/8) /* 1024 bit */
+#define DSS_SIZE (1024/8) /* 1024 bit */
+
+static void buf_writefile(buffer * buf, const char * filename);
+static void printpubkey(sign_key * key, int keytype);
+static void justprintpub(const char* filename);
+
+/* Print a help message */
+static void printhelp(char * progname) {
+
+	fprintf(stderr, "Usage: %s -t <type> -f <filename> [-s bits]\n"
+					"Options are:\n"
+					"-t type	Type of key to generate. One of:\n"
+#ifdef DROPBEAR_RSA
+					"		rsa\n"
+#endif
+#ifdef DROPBEAR_DSS
+					"		dss\n"
+#endif
+					"-f filename	Use filename for the secret key\n"
+					"-s bits	Key size in bits, should be a multiple of 8 (optional)\n"
+					"-y		Just print the publickey and fingerprint for the\n		private key in <filename>.\n"
+#ifdef DEBUG_TRACE
+					"-v		verbose\n"
+#endif
+					,progname);
+}
+
+#if defined(DBMULTI_dropbearkey) || !defined(DROPBEAR_MULTI)
+#if defined(DBMULTI_dropbearkey) && defined(DROPBEAR_MULTI)
+int dropbearkey_main(int argc, char ** argv) {
+#else
+int main(int argc, char ** argv) {
+#endif
+
+	int i;
+	char ** next = 0;
+	sign_key *key = NULL;
+	buffer *buf = NULL;
+	char * filename = NULL;
+	int keytype = -1;
+	char * typetext = NULL;
+	char * sizetext = NULL;
+	unsigned int bits;
+	unsigned int keysize;
+	int printpub = 0;
+
+	/* get the commandline options */
+	for (i = 1; i < argc; i++) {
+		if (argv[i] == NULL) {
+			continue; /* Whack */
+		} 
+		if (next) {
+			*next = argv[i];
+			next = NULL;
+			continue;
+		}
+
+		if (argv[i][0] == '-') {
+			switch (argv[i][1]) {
+				case 'f':
+					next = &filename;
+					break;
+				case 't':
+					next = &typetext;
+					break;
+				case 's':
+					next = &sizetext;
+					break;
+				case 'y':
+					printpub = 1;
+					break;
+				case 'h':
+					printhelp(argv[0]);
+					exit(EXIT_SUCCESS);
+					break;
+#ifdef DEBUG_TRACE
+				case 'v':
+					debug_trace = 1;
+					break;
+#endif
+				default:
+					fprintf(stderr, "Unknown argument %s\n", argv[i]);
+					printhelp(argv[0]);
+					exit(EXIT_FAILURE);
+					break;
+			}
+		}
+	}
+
+	if (!filename) {
+		fprintf(stderr, "Must specify a key filename\n");
+		printhelp(argv[0]);
+		exit(EXIT_FAILURE);
+	}
+
+	if (printpub) {
+		justprintpub(filename);
+		/* Not reached */
+	}
+
+	/* check/parse args */
+	if (!typetext) {
+		fprintf(stderr, "Must specify key type\n");
+		printhelp(argv[0]);
+		exit(EXIT_FAILURE);
+	}
+
+	if (strlen(typetext) == 3) {
+#ifdef DROPBEAR_RSA
+		if (strncmp(typetext, "rsa", 3) == 0) {
+			keytype = DROPBEAR_SIGNKEY_RSA;
+			TRACE(("type is rsa"))
+		}
+#endif
+#ifdef DROPBEAR_DSS
+		if (strncmp(typetext, "dss", 3) == 0) {
+			keytype = DROPBEAR_SIGNKEY_DSS;
+			TRACE(("type is dss"))
+		}
+#endif
+	}
+	if (keytype == -1) {
+		fprintf(stderr, "Unknown key type '%s'\n", typetext);
+		printhelp(argv[0]);
+		exit(EXIT_FAILURE);
+	}
+
+	if (sizetext) {
+		if (sscanf(sizetext, "%u", &bits) != 1) {
+			fprintf(stderr, "Bits must be an integer\n");
+			exit(EXIT_FAILURE);
+		}
+	
+		if (bits < 512 || bits > 4096 || (bits % 8 != 0)) {
+			fprintf(stderr, "Bits must satisfy 512 <= bits <= 4096, and be a"
+					" multiple of 8\n");
+			exit(EXIT_FAILURE);
+		}
+
+		keysize = bits / 8;
+	} else {
+		if (keytype == DROPBEAR_SIGNKEY_DSS) {
+			keysize = DSS_SIZE;
+		} else if (keytype == DROPBEAR_SIGNKEY_RSA) {
+			keysize = RSA_SIZE;
+		} else {
+			exit(EXIT_FAILURE); /* not reached */
+		}
+	}
+
+
+	fprintf(stderr, "Will output %d bit %s secret key to '%s'\n", keysize*8,
+			typetext, filename);
+
+	/* don't want the file readable by others */
+	umask(077);
+
+	/* now we can generate the key */
+	key = new_sign_key();
+	
+	fprintf(stderr, "Generating key, this may take a while...\n");
+	switch(keytype) {
+#ifdef DROPBEAR_RSA
+		case DROPBEAR_SIGNKEY_RSA:
+			key->rsakey = gen_rsa_priv_key(keysize); /* 128 bytes = 1024 bit */
+			break;
+#endif
+#ifdef DROPBEAR_DSS
+		case DROPBEAR_SIGNKEY_DSS:
+			key->dsskey = gen_dss_priv_key(keysize); /* 128 bytes = 1024 bit */
+			break;
+#endif
+		default:
+			fprintf(stderr, "Internal error, bad key type\n");
+			exit(EXIT_FAILURE);
+	}
+
+	buf = buf_new(MAX_PRIVKEY_SIZE); 
+
+	buf_put_priv_key(buf, key, keytype);
+	buf_setpos(buf, 0);
+	buf_writefile(buf, filename);
+
+	buf_burn(buf);
+	buf_free(buf);
+
+	printpubkey(key, keytype);
+
+	sign_key_free(key);
+
+	return EXIT_SUCCESS;
+}
+#endif
+
+static void justprintpub(const char* filename) {
+
+	buffer *buf = NULL;
+	sign_key *key = NULL;
+	int keytype;
+	int ret;
+	int err = DROPBEAR_FAILURE;
+
+	buf = buf_new(MAX_PRIVKEY_SIZE);
+	ret = buf_readfile(buf, filename);
+
+	if (ret != DROPBEAR_SUCCESS) {
+		fprintf(stderr, "Failed reading '%s'\n", filename);
+		goto out;
+	}
+
+	key = new_sign_key();
+	keytype = DROPBEAR_SIGNKEY_ANY;
+
+	buf_setpos(buf, 0);
+	ret = buf_get_priv_key(buf, key, &keytype);
+	if (ret == DROPBEAR_FAILURE) {
+		fprintf(stderr, "Bad key in '%s'\n", filename);
+		goto out;
+	}
+
+	printpubkey(key, keytype);
+
+	err = DROPBEAR_SUCCESS;
+
+out:
+	buf_burn(buf);
+	buf_free(buf);
+	buf = NULL;
+	sign_key_free(key);
+	key = NULL;
+	exit(err);
+}
+
+static void printpubkey(sign_key * key, int keytype) {
+
+	buffer * buf = NULL;
+	unsigned char base64key[MAX_PUBKEY_SIZE*2];
+	unsigned long base64len;
+	int err;
+	const char * typestring = NULL;
+	char *fp = NULL;
+	int len;
+
+	buf = buf_new(MAX_PUBKEY_SIZE);
+	buf_put_pub_key(buf, key, keytype);
+	buf_setpos(buf, 4);
+
+	len = buf->len - buf->pos;
+
+	base64len = sizeof(base64key);
+	err = base64_encode(buf_getptr(buf, len), len, base64key, &base64len);
+
+	if (err != CRYPT_OK) {
+		fprintf(stderr, "base64 failed");
+	}
+
+	typestring = signkey_name_from_type(keytype, &err);
+
+	fp = sign_key_fingerprint(buf_getptr(buf, len), len);
+
+	printf("Public key portion is:\n%s %s\nFingerprint: %s\n",
+			typestring, base64key, fp);
+
+	m_free(fp);
+	buf_free(buf);
+}
+
+/* Write a buffer to a file specified, failing if the file exists */
+static void buf_writefile(buffer * buf, const char * filename) {
+
+	int fd;
+	int len;
+
+	fd = open(filename, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
+	if (fd < 0) {
+		fprintf(stderr, "Couldn't create new file %s\n", filename);
+		perror("Reason");
+		buf_burn(buf);
+		exit(EXIT_FAILURE);
+	}
+
+	/* write the file now */
+	while (buf->pos != buf->len) {
+		len = write(fd, buf_getptr(buf, buf->len - buf->pos),
+				buf->len - buf->pos);
+		if (errno == EINTR) {
+			continue;
+		}
+		if (len <= 0) {
+			fprintf(stderr, "Failed writing file '%s'\n",filename);
+			perror("Reason");
+			exit(EXIT_FAILURE);
+		}
+		buf_incrpos(buf, len);
+	}
+
+	close(fd);
+}
diff --git a/dss.c b/dss.c
new file mode 100644
index 0000000000000000000000000000000000000000..84a093ce98bedcefdc69314f707a0ef1e6694213
--- /dev/null
+++ b/dss.c
@@ -0,0 +1,416 @@
+/*
+ * 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. */
+
+#include "includes.h"
+#include "dbutil.h"
+#include "bignum.h"
+#include "dss.h"
+#include "buffer.h"
+#include "ssh.h"
+#include "random.h"
+
+/* Handle DSS (Digital Signature Standard), aka DSA (D.S. Algorithm),
+ * operations, such as key reading, signing, verification. Key generation
+ * is in gendss.c, since it isn't required in the server itself.
+ *
+ * See FIPS186 or the Handbook of Applied Cryptography for details of the
+ * algorithm */
+
+#ifdef DROPBEAR_DSS 
+
+/* Load a dss key from a buffer, initialising the values.
+ * The key will have the same format as buf_put_dss_key.
+ * These should be freed with dss_key_free.
+ * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
+int buf_get_dss_pub_key(buffer* buf, dss_key *key) {
+
+	TRACE(("enter buf_get_dss_pub_key"))
+	dropbear_assert(key != NULL);
+	key->p = m_malloc(sizeof(mp_int));
+	key->q = m_malloc(sizeof(mp_int));
+	key->g = m_malloc(sizeof(mp_int));
+	key->y = m_malloc(sizeof(mp_int));
+	m_mp_init_multi(key->p, key->q, key->g, key->y, NULL);
+	key->x = NULL;
+
+	buf_incrpos(buf, 4+SSH_SIGNKEY_DSS_LEN); /* int + "ssh-dss" */
+	if (buf_getmpint(buf, key->p) == DROPBEAR_FAILURE
+	 || buf_getmpint(buf, key->q) == DROPBEAR_FAILURE
+	 || buf_getmpint(buf, key->g) == DROPBEAR_FAILURE
+	 || buf_getmpint(buf, key->y) == DROPBEAR_FAILURE) {
+		TRACE(("leave buf_get_dss_pub_key: failed reading mpints"))
+		return DROPBEAR_FAILURE;
+	}
+
+	if (mp_count_bits(key->p) < MIN_DSS_KEYLEN) {
+		dropbear_log(LOG_WARNING, "DSS key too short");
+		TRACE(("leave buf_get_dss_pub_key: short key"))
+		return DROPBEAR_FAILURE;
+	}
+
+	TRACE(("leave buf_get_dss_pub_key: success"))
+	return DROPBEAR_SUCCESS;
+}
+
+/* Same as buf_get_dss_pub_key, but reads a private "x" key at the end.
+ * Loads a private dss key from a buffer
+ * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
+int buf_get_dss_priv_key(buffer* buf, dss_key *key) {
+
+	int ret = DROPBEAR_FAILURE;
+
+	dropbear_assert(key != NULL);
+
+	ret = buf_get_dss_pub_key(buf, key);
+	if (ret == DROPBEAR_FAILURE) {
+		return DROPBEAR_FAILURE;
+	}
+
+	key->x = m_malloc(sizeof(mp_int));
+	m_mp_init(key->x);
+	ret = buf_getmpint(buf, key->x);
+
+	return ret;
+}
+	
+
+/* Clear and free the memory used by a public or private key */
+void dss_key_free(dss_key *key) {
+
+	TRACE(("enter dsa_key_free"))
+	if (key == NULL) {
+		TRACE(("enter dsa_key_free: key == NULL"))
+		return;
+	}
+	if (key->p) {
+		mp_clear(key->p);
+		m_free(key->p);
+	}
+	if (key->q) {
+		mp_clear(key->q);
+		m_free(key->q);
+	}
+	if (key->g) {
+		mp_clear(key->g);
+		m_free(key->g);
+	}
+	if (key->y) {
+		mp_clear(key->y);
+		m_free(key->y);
+	}
+	if (key->x) {
+		mp_clear(key->x);
+		m_free(key->x);
+	}
+	m_free(key);
+	TRACE(("leave dsa_key_free"))
+}
+
+/* put the dss public key into the buffer in the required format:
+ *
+ * string	"ssh-dss"
+ * mpint	p
+ * mpint	q
+ * mpint	g
+ * mpint	y
+ */
+void buf_put_dss_pub_key(buffer* buf, dss_key *key) {
+
+	dropbear_assert(key != NULL);
+	buf_putstring(buf, SSH_SIGNKEY_DSS, SSH_SIGNKEY_DSS_LEN);
+	buf_putmpint(buf, key->p);
+	buf_putmpint(buf, key->q);
+	buf_putmpint(buf, key->g);
+	buf_putmpint(buf, key->y);
+
+}
+
+/* Same as buf_put_dss_pub_key, but with the private "x" key appended */
+void buf_put_dss_priv_key(buffer* buf, dss_key *key) {
+
+	dropbear_assert(key != NULL);
+	buf_put_dss_pub_key(buf, key);
+	buf_putmpint(buf, key->x);
+
+}
+
+#ifdef DROPBEAR_SIGNKEY_VERIFY
+/* Verify a DSS signature (in buf) made on data by the key given. 
+ * returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
+int buf_dss_verify(buffer* buf, dss_key *key, const unsigned char* data,
+		unsigned int len) {
+
+	unsigned char msghash[SHA1_HASH_SIZE];
+	hash_state hs;
+	int ret = DROPBEAR_FAILURE;
+	DEF_MP_INT(val1);
+	DEF_MP_INT(val2);
+	DEF_MP_INT(val3);
+	DEF_MP_INT(val4);
+	char * string = NULL;
+	int stringlen;
+
+	TRACE(("enter buf_dss_verify"))
+	dropbear_assert(key != NULL);
+
+	m_mp_init_multi(&val1, &val2, &val3, &val4, NULL);
+
+	/* get blob, check length */
+	string = buf_getstring(buf, &stringlen);
+	if (stringlen != 2*SHA1_HASH_SIZE) {
+		goto out;
+	}
+
+	/* hash the data */
+	sha1_init(&hs);
+	sha1_process(&hs, data, len);
+	sha1_done(&hs, msghash);
+
+	/* create the signature - s' and r' are the received signatures in buf */
+	/* w = (s')-1 mod q */
+	/* let val1 = s' */
+	bytes_to_mp(&val1, &string[SHA1_HASH_SIZE], SHA1_HASH_SIZE);
+
+	if (mp_cmp(&val1, key->q) != MP_LT) {
+		TRACE(("verify failed, s' >= q"))
+		goto out;
+	}
+	/* let val2 = w = (s')^-1 mod q*/
+	if (mp_invmod(&val1, key->q, &val2) != MP_OKAY) {
+		goto out;
+	}
+
+	/* u1 = ((SHA(M')w) mod q */
+	/* let val1 = SHA(M') = msghash */
+	bytes_to_mp(&val1, msghash, SHA1_HASH_SIZE);
+
+	/* let val3 = u1 = ((SHA(M')w) mod q */
+	if (mp_mulmod(&val1, &val2, key->q, &val3) != MP_OKAY) {
+		goto out;
+	}
+
+	/* u2 = ((r')w) mod q */
+	/* let val1 = r' */
+	bytes_to_mp(&val1, &string[0], SHA1_HASH_SIZE);
+	if (mp_cmp(&val1, key->q) != MP_LT) {
+		TRACE(("verify failed, r' >= q"))
+		goto out;
+	}
+	/* let val4 = u2 = ((r')w) mod q */
+	if (mp_mulmod(&val1, &val2, key->q, &val4) != MP_OKAY) {
+		goto out;
+	}
+
+	/* v = (((g)^u1 (y)^u2) mod p) mod q */
+	/* val2 = g^u1 mod p */
+	if (mp_exptmod(key->g, &val3, key->p, &val2) != MP_OKAY) {
+		goto out;
+	}
+	/* val3 = y^u2 mod p */
+	if (mp_exptmod(key->y, &val4, key->p, &val3) != MP_OKAY) {
+		goto out;
+	}
+	/* val4 = ((g)^u1 (y)^u2) mod p */
+	if (mp_mulmod(&val2, &val3, key->p, &val4) != MP_OKAY) {
+		goto out;
+	}
+	/* val2 = v = (((g)^u1 (y)^u2) mod p) mod q */
+	if (mp_mod(&val4, key->q, &val2) != MP_OKAY) {
+		goto out;
+	}
+	
+	/* check whether signatures verify */
+	if (mp_cmp(&val2, &val1) == MP_EQ) {
+		/* good sig */
+		ret = DROPBEAR_SUCCESS;
+	}
+
+out:
+	mp_clear_multi(&val1, &val2, &val3, &val4, NULL);
+	m_free(string);
+
+	return ret;
+
+}
+#endif /* DROPBEAR_SIGNKEY_VERIFY */
+
+#ifdef DSS_PROTOK	
+/* convert an unsigned mp into an array of bytes, malloced.
+ * This array must be freed after use, len contains the length of the array,
+ * if len != NULL */
+static unsigned char* mptobytes(mp_int *mp, int *len) {
+	
+	unsigned char* ret;
+	int size;
+
+	size = mp_unsigned_bin_size(mp);
+	ret = m_malloc(size);
+	if (mp_to_unsigned_bin(mp, ret) != MP_OKAY) {
+		dropbear_exit("mem alloc error");
+	}
+	if (len != NULL) {
+		*len = size;
+	}
+	return ret;
+}
+#endif
+
+/* Sign the data presented with key, writing the signature contents
+ * to the buffer
+ *
+ * When DSS_PROTOK is #defined:
+ * The alternate k generation method is based on the method used in PuTTY. 
+ * In particular to avoid being vulnerable to attacks using flaws in random
+ * generation of k, we use the following:
+ *
+ * proto_k = SHA512 ( SHA512(x) || SHA160(message) )
+ * k = proto_k mod q
+ *
+ * Now we aren't relying on the random number generation to protect the private
+ * key x, which is a long term secret */
+void buf_put_dss_sign(buffer* buf, dss_key *key, const unsigned char* data,
+		unsigned int len) {
+
+	unsigned char msghash[SHA1_HASH_SIZE];
+	unsigned int writelen;
+	unsigned int i;
+#ifdef DSS_PROTOK
+	unsigned char privkeyhash[SHA512_HASH_SIZE];
+	unsigned char *privkeytmp;
+	unsigned char proto_k[SHA512_HASH_SIZE];
+	DEF_MP_INT(dss_protok);
+#endif
+	DEF_MP_INT(dss_k);
+	DEF_MP_INT(dss_m);
+	DEF_MP_INT(dss_temp1);
+	DEF_MP_INT(dss_temp2);
+	DEF_MP_INT(dss_r);
+	DEF_MP_INT(dss_s);
+	hash_state hs;
+	
+	TRACE(("enter buf_put_dss_sign"))
+	dropbear_assert(key != NULL);
+	
+	/* hash the data */
+	sha1_init(&hs);
+	sha1_process(&hs, data, len);
+	sha1_done(&hs, msghash);
+
+	m_mp_init_multi(&dss_k, &dss_temp1, &dss_temp2, &dss_r, &dss_s,
+			&dss_m, NULL);
+#ifdef DSS_PROTOK	
+	/* hash the privkey */
+	privkeytmp = mptobytes(key->x, &i);
+	sha512_init(&hs);
+	sha512_process(&hs, "the quick brown fox jumped over the lazy dog", 44);
+	sha512_process(&hs, privkeytmp, i);
+	sha512_done(&hs, privkeyhash);
+	m_burn(privkeytmp, i);
+	m_free(privkeytmp);
+
+	/* calculate proto_k */
+	sha512_init(&hs);
+	sha512_process(&hs, privkeyhash, SHA512_HASH_SIZE);
+	sha512_process(&hs, msghash, SHA1_HASH_SIZE);
+	sha512_done(&hs, proto_k);
+
+	/* generate k */
+	m_mp_init(&dss_protok);
+	bytes_to_mp(&dss_protok, proto_k, SHA512_HASH_SIZE);
+	mp_mod(&dss_protok, key->q, &dss_k);
+	mp_clear(&dss_protok);
+	m_burn(proto_k, SHA512_HASH_SIZE);
+#else /* DSS_PROTOK not defined*/
+	gen_random_mpint(key->q, &dss_k);
+#endif
+
+	/* now generate the actual signature */
+	bytes_to_mp(&dss_m, msghash, SHA1_HASH_SIZE);
+
+	/* g^k mod p */
+	if (mp_exptmod(key->g, &dss_k, key->p, &dss_temp1) !=  MP_OKAY) {
+		dropbear_exit("dss error");
+	}
+	/* r = (g^k mod p) mod q */
+	if (mp_mod(&dss_temp1, key->q, &dss_r) != MP_OKAY) {
+		dropbear_exit("dss error");
+	}
+
+	/* x*r mod q */
+	if (mp_mulmod(&dss_r, key->x, key->q, &dss_temp1) != MP_OKAY) {
+		dropbear_exit("dss error");
+	}
+	/* (SHA1(M) + xr) mod q) */
+	if (mp_addmod(&dss_m, &dss_temp1, key->q, &dss_temp2) != MP_OKAY) {
+		dropbear_exit("dss error");
+	}
+	
+	/* (k^-1) mod q */
+	if (mp_invmod(&dss_k, key->q, &dss_temp1) != MP_OKAY) {
+		dropbear_exit("dss error");
+	}
+
+	/* s = (k^-1(SHA1(M) + xr)) mod q */
+	if (mp_mulmod(&dss_temp1, &dss_temp2, key->q, &dss_s) != MP_OKAY) {
+		dropbear_exit("dss error");
+	}
+
+	buf_putstring(buf, SSH_SIGNKEY_DSS, SSH_SIGNKEY_DSS_LEN);
+	buf_putint(buf, 2*SHA1_HASH_SIZE);
+
+	writelen = mp_unsigned_bin_size(&dss_r);
+	dropbear_assert(writelen <= SHA1_HASH_SIZE);
+	/* need to pad to 160 bits with leading zeros */
+	for (i = 0; i < SHA1_HASH_SIZE - writelen; i++) {
+		buf_putbyte(buf, 0);
+	}
+	if (mp_to_unsigned_bin(&dss_r, buf_getwriteptr(buf, writelen)) 
+			!= MP_OKAY) {
+		dropbear_exit("dss error");
+	}
+	mp_clear(&dss_r);
+	buf_incrwritepos(buf, writelen);
+
+	writelen = mp_unsigned_bin_size(&dss_s);
+	dropbear_assert(writelen <= SHA1_HASH_SIZE);
+	/* need to pad to 160 bits with leading zeros */
+	for (i = 0; i < SHA1_HASH_SIZE - writelen; i++) {
+		buf_putbyte(buf, 0);
+	}
+	if (mp_to_unsigned_bin(&dss_s, buf_getwriteptr(buf, writelen)) 
+			!= MP_OKAY) {
+		dropbear_exit("dss error");
+	}
+	mp_clear(&dss_s);
+	buf_incrwritepos(buf, writelen);
+
+	mp_clear_multi(&dss_k, &dss_temp1, &dss_temp2, &dss_r, &dss_s,
+			&dss_m, NULL);
+	
+	/* create the signature to return */
+
+	TRACE(("leave buf_put_dss_sign"))
+}
+
+#endif /* DROPBEAR_DSS */
diff --git a/dss.h b/dss.h
new file mode 100644
index 0000000000000000000000000000000000000000..0b6925646700bc3767751886509f6e9c90a28777
--- /dev/null
+++ b/dss.h
@@ -0,0 +1,61 @@
+/*
+ * 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 _DSS_H_
+#define _DSS_H_
+
+#include "includes.h"
+#include "buffer.h"
+
+#ifdef DROPBEAR_DSS 
+
+#define DSS_SIGNATURE_SIZE 4+SSH_SIGNKEY_DSS_LEN+4+2*SHA1_HASH_SIZE
+
+struct DSS_key {
+
+	mp_int* p;
+	mp_int* q;
+	mp_int* g;
+	mp_int* y;
+	mp_int* x;
+
+};
+
+typedef struct DSS_key dss_key;
+
+void buf_put_dss_sign(buffer* buf, dss_key *key, const unsigned char* data,
+		unsigned int len);
+#ifdef DROPBEAR_SIGNKEY_VERIFY
+int buf_dss_verify(buffer* buf, dss_key *key, const unsigned char* data,
+		unsigned int len);
+#endif
+int buf_get_dss_pub_key(buffer* buf, dss_key *key);
+int buf_get_dss_priv_key(buffer* buf, dss_key *key);
+void buf_put_dss_pub_key(buffer* buf, dss_key *key);
+void buf_put_dss_priv_key(buffer* buf, dss_key *key);
+void dss_key_free(dss_key *key);
+
+#endif /* DROPBEAR_DSS */
+
+#endif /* _DSS_H_ */
diff --git a/fake-rfc2553.c b/fake-rfc2553.c
new file mode 100644
index 0000000000000000000000000000000000000000..afbea88777d7612caf83b5a742b72f3179f45ec5
--- /dev/null
+++ b/fake-rfc2553.c
@@ -0,0 +1,227 @@
+/*
+ *
+ * Taken from OpenSSH 3.8.1p1
+ * 
+ * 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..053e6a6a33cb4316afd9eda69beddad0bc304d7b
--- /dev/null
+++ b/fake-rfc2553.h
@@ -0,0 +1,162 @@
+/* Taken from OpenSSH 3.8.1p1 */
+
+/* $.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"
+
+/*
+ * 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/filelist.txt b/filelist.txt
new file mode 100644
index 0000000000000000000000000000000000000000..8281c140295dfc60fbca8104c92d23e38b60f0c5
--- /dev/null
+++ b/filelist.txt
@@ -0,0 +1,117 @@
+This file is out of date - it remains here in case it is still of use.
+The basic naming convention is svr- and cli-  for seperate parts,
+then common- for common parts. Some files have no prefix.
+
+A brief rundown on which files do what, and their corresponding sections
+in the IETF drafts. The .c files usually have corresponding .h files.
+
+Transport layer  draft-ietf-secsh-transport-16.txt
+===============
+
+session.c		Contains the main select() loop, and handles setting
+			up/closing down ssh connections
+
+algo.c			Framework for handling various ciphers/hashes/algos,
+			and choosing between the lists of client/server
+			preferred ones
+
+kex.c			Key exchange routines, used at startup to negotiate
+			which algorithms to use, and also to obtain session
+			keys. This also runs when rekeying during the
+			connection.
+
+packet.c		Handles the basic packet encryption/decryption,
+			and switching to the appropriate packet handlers.
+			Called from session.c's main select loop.
+
+service.c		Handles service requests (userauth or connection)
+
+
+Authentication  draft-ietf-secsh-userauth-17.txt
+==============
+
+auth.c			General auth handling, including user checking etc,
+			passes different auth types to auth{passwd,pubkey}
+
+authpasswd.c		Handles /etc/passwd or /etc/shadow auth
+
+authpubkey.c		Handles ~/.ssh/authorized_keys auth
+
+
+Connection  draft-ietf-secsh-connect-17.txt
+==========
+
+channel.c		Channel handling routines - each shell/tcp conn/agent
+			etc is a channel.
+
+chansession.c		Handles shell/exec requests
+
+sshpty.c		From OpenSSH, allocates PTYs etc
+
+termcodes.c		Mapping of POSIX terminal codes to SSH terminal codes
+
+loginrec.c		From OpenSSH, handles utmp/wtmp logging
+
+x11fwd.c		Handles X11 forwarding
+
+agentfwd.c		Handles auth-agent forwarding requests
+
+localtcpfwd.c		Handles -L style tcp forwarding requests, setting
+			up the listening port and also handling connections
+			to that port (and subsequent channels)
+
+
+Program-related
+===============
+
+dbmulti.c		Combination binary chooser main() function
+
+dbutil.c		Various utility functions, incl logging, memory etc
+
+dropbearconvert.c	Conversion from dropbear<->openssh keys, uses
+			keyimport.c to do most of the work
+
+dropbearkey.c		Generates keys, calling gen{dss,rsa}
+
+keyimport.c		Modified from PuTTY, converts between key types
+
+main.c			dropbear's main(), handles listening, forking for
+			new connections, child-process limits
+
+runopts.c		Parses commandline options
+
+options.h		Compile-time feature selection
+
+config.h		Features selected from configure
+
+debug.h			Compile-time selection of debug features
+
+includes.h		Included system headers etc
+
+
+Generic Routines
+================
+
+signkey.c		A generic handler for pubkeys, switches to dss or rsa
+			depending on the key type
+
+rsa.c			RSA asymmetric crypto routines
+
+dss.c			DSS asymmetric crypto routines
+
+gendss.c		DSS key generation
+
+genrsa.c		RSA key generation
+
+bignum.c		Some bignum helper functions
+
+queue.c			A queue, used to enqueue encrypted packets to send
+
+random.c		PRNG, based on /dev/urandom or prngd
+
+atomicio.c		From OpenSSH, does `blocking' IO on non-blocking fds
+
+buffer.c		Buffer-usage routines, with size checking etc
+
+
+vim:set ts=8:
diff --git a/gendss.c b/gendss.c
new file mode 100644
index 0000000000000000000000000000000000000000..bf46d3d0095e6d3b8f3e6ccb8a0baad06052708f
--- /dev/null
+++ b/gendss.c
@@ -0,0 +1,198 @@
+/*
+ * 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. */
+
+#include "includes.h"
+#include "dbutil.h"
+#include "signkey.h"
+#include "bignum.h"
+#include "random.h"
+#include "buffer.h"
+#include "gendss.h"
+#include "dss.h"
+
+#define QSIZE 20 /* 160 bit */
+
+/* This is just a test */
+
+#ifdef DROPBEAR_DSS
+
+static void getq(dss_key *key);
+static void getp(dss_key *key, unsigned int size);
+static void getg(dss_key *key);
+static void getx(dss_key *key);
+static void gety(dss_key *key);
+
+dss_key * gen_dss_priv_key(unsigned int size) {
+
+	dss_key *key;
+
+	key = (dss_key*)m_malloc(sizeof(dss_key));
+
+	key->p = (mp_int*)m_malloc(sizeof(mp_int));
+	key->q = (mp_int*)m_malloc(sizeof(mp_int));
+	key->g = (mp_int*)m_malloc(sizeof(mp_int));
+	key->y = (mp_int*)m_malloc(sizeof(mp_int));
+	key->x = (mp_int*)m_malloc(sizeof(mp_int));
+	m_mp_init_multi(key->p, key->q, key->g, key->y, key->x, NULL);
+	
+	seedrandom();
+	
+	getq(key);
+	getp(key, size);
+	getg(key);
+	getx(key);
+	gety(key);
+
+	return key;
+	
+}
+
+static void getq(dss_key *key) {
+
+	char buf[QSIZE];
+
+	/* 160 bit prime */
+	genrandom(buf, QSIZE);
+	buf[0] |= 0x80; /* top bit high */
+	buf[QSIZE-1] |= 0x01; /* bottom bit high */
+
+	bytes_to_mp(key->q, buf, QSIZE);
+
+	/* 18 rounds are required according to HAC */
+	if (mp_prime_next_prime(key->q, 18, 0) != MP_OKAY) {
+		fprintf(stderr, "dss key generation failed\n");
+		exit(1);
+	}
+}
+
+static void getp(dss_key *key, unsigned int size) {
+
+	DEF_MP_INT(tempX);
+	DEF_MP_INT(tempC);
+	DEF_MP_INT(tempP);
+	DEF_MP_INT(temp2q);
+	int result;
+	unsigned char *buf;
+
+	m_mp_init_multi(&tempX, &tempC, &tempP, &temp2q, NULL);
+
+
+	/* 2*q */
+	if (mp_mul_d(key->q, 2, &temp2q) != MP_OKAY) {
+		fprintf(stderr, "dss key generation failed\n");
+		exit(1);
+	}
+	
+	buf = (unsigned char*)m_malloc(size);
+
+	result = 0;
+	do {
+		
+		genrandom(buf, size);
+		buf[0] |= 0x80; /* set the top bit high */
+
+		/* X is a random mp_int */
+		bytes_to_mp(&tempX, buf, size);
+
+		/* C = X mod 2q */
+		if (mp_mod(&tempX, &temp2q, &tempC) != MP_OKAY) {
+			fprintf(stderr, "dss key generation failed\n");
+			exit(1);
+		}
+
+		/* P = X - (C - 1) = X - C + 1*/
+		if (mp_sub(&tempX, &tempC, &tempP) != MP_OKAY) {
+			fprintf(stderr, "dss key generation failed\n");
+			exit(1);
+		}
+		
+		if (mp_add_d(&tempP, 1, key->p) != MP_OKAY) {
+			fprintf(stderr, "dss key generation failed\n");
+			exit(1);
+		}
+
+		/* now check for prime, 5 rounds is enough according to HAC */
+		/* result == 1  =>  p is prime */
+		if (mp_prime_is_prime(key->p, 5, &result) != MP_OKAY) {
+			fprintf(stderr, "dss key generation failed\n");
+			exit(1);
+		}
+	} while (!result);
+
+	mp_clear_multi(&tempX, &tempC, &tempP, &temp2q, NULL);
+	m_burn(buf, size);
+	m_free(buf);
+}
+
+static void getg(dss_key * key) {
+
+	DEF_MP_INT(div);
+	DEF_MP_INT(h);
+	DEF_MP_INT(val);
+
+	m_mp_init_multi(&div, &h, &val, NULL);
+
+	/* get div=(p-1)/q */
+	if (mp_sub_d(key->p, 1, &val) != MP_OKAY) {
+		fprintf(stderr, "dss key generation failed\n");
+		exit(1);
+	}
+	if (mp_div(&val, key->q, &div, NULL) != MP_OKAY) {
+		fprintf(stderr, "dss key generation failed\n");
+		exit(1);
+	}
+
+	/* initialise h=1 */
+	mp_set(&h, 1);
+	do {
+		/* now keep going with g=h^div mod p, until g > 1 */
+		if (mp_exptmod(&h, &div, key->p, key->g) != MP_OKAY) {
+			fprintf(stderr, "dss key generation failed\n");
+			exit(1);
+		}
+
+		if (mp_add_d(&h, 1, &h) != MP_OKAY) {
+			fprintf(stderr, "dss key generation failed\n");
+			exit(1);
+		}
+	
+	} while (mp_cmp_d(key->g, 1) != MP_GT);
+
+	mp_clear_multi(&div, &h, &val, NULL);
+}
+
+static void getx(dss_key *key) {
+
+	gen_random_mpint(key->q, key->x);
+}
+
+static void gety(dss_key *key) {
+
+	if (mp_exptmod(key->g, key->x, key->p, key->y) != MP_OKAY) {
+		fprintf(stderr, "dss key generation failed\n");
+		exit(1);
+	}
+}
+
+#endif /* DROPBEAR_DSS */
diff --git a/gendss.h b/gendss.h
new file mode 100644
index 0000000000000000000000000000000000000000..246dae351663a9ba5c38569347abfad3b2d13d61
--- /dev/null
+++ b/gendss.h
@@ -0,0 +1,36 @@
+/*
+ * 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 _GENDSS_H_
+#define _GENDSS_H_
+
+#include "dss.h"
+
+#ifdef DROPBEAR_DSS
+
+dss_key * gen_dss_priv_key(unsigned int size);
+
+#endif /* DROPBEAR_DSS */
+
+#endif /* _GENDSS_H_ */
diff --git a/genrsa.c b/genrsa.c
new file mode 100644
index 0000000000000000000000000000000000000000..73a7984ec92869c505fad4b56142830c0e9b0606
--- /dev/null
+++ b/genrsa.c
@@ -0,0 +1,137 @@
+/*
+ * 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. */
+
+#include "includes.h"
+#include "dbutil.h"
+#include "bignum.h"
+#include "random.h"
+#include "rsa.h"
+#include "genrsa.h"
+
+#define RSA_E 65537
+
+#ifdef DROPBEAR_RSA
+
+static void getrsaprime(mp_int* prime, mp_int *primeminus, 
+		mp_int* rsa_e, unsigned int size);
+
+/* mostly taken from libtomcrypt's rsa key generation routine */
+rsa_key * gen_rsa_priv_key(unsigned int size) {
+
+	rsa_key * key;
+	DEF_MP_INT(pminus);
+	DEF_MP_INT(qminus);
+	DEF_MP_INT(lcm);
+
+	key = (rsa_key*)m_malloc(sizeof(rsa_key));
+
+	key->e = (mp_int*)m_malloc(sizeof(mp_int));
+	key->n = (mp_int*)m_malloc(sizeof(mp_int));
+	key->d = (mp_int*)m_malloc(sizeof(mp_int));
+	key->p = (mp_int*)m_malloc(sizeof(mp_int));
+	key->q = (mp_int*)m_malloc(sizeof(mp_int));
+
+	m_mp_init_multi(key->e, key->n, key->d, key->p, key->q,
+			&pminus, &lcm, &qminus, NULL);
+
+	seedrandom();
+
+	if (mp_set_int(key->e, RSA_E) != MP_OKAY) {
+		fprintf(stderr, "rsa generation failed\n");
+		exit(1);
+	}
+
+	/* PuTTY doesn't like it if the modulus isn't a multiple of 8 bits,
+	 * so we just generate them until we get one which is OK */
+	getrsaprime(key->p, &pminus, key->e, size/2);
+	do {
+		getrsaprime(key->q, &qminus, key->e, size/2);
+
+		if (mp_mul(key->p, key->q, key->n) != MP_OKAY) {
+			fprintf(stderr, "rsa generation failed\n");
+			exit(1);
+		}
+	} while (mp_count_bits(key->n) % 8 != 0);
+
+	/* lcm(p-1, q-1) */
+	if (mp_lcm(&pminus, &qminus, &lcm) != MP_OKAY) {
+		fprintf(stderr, "rsa generation failed\n");
+		exit(1);
+	}
+
+	/* de = 1 mod lcm(p-1,q-1) */
+	/* therefore d = (e^-1) mod lcm(p-1,q-1) */
+	if (mp_invmod(key->e, &lcm, key->d) != MP_OKAY) {
+		fprintf(stderr, "rsa generation failed\n");
+		exit(1);
+	}
+
+	mp_clear_multi(&pminus, &qminus, &lcm, NULL);
+
+	return key;
+}	
+
+/* return a prime suitable for p or q */
+static void getrsaprime(mp_int* prime, mp_int *primeminus, 
+		mp_int* rsa_e, unsigned int size) {
+
+	unsigned char *buf;
+	DEF_MP_INT(temp_gcd);
+
+	buf = (unsigned char*)m_malloc(size+1);
+
+	m_mp_init(&temp_gcd);
+	do {
+		/* generate a random odd number with MSB set, then find the
+		   the next prime above it */
+		genrandom(buf, size+1);
+		buf[0] |= 0x80; /* MSB set */
+
+		bytes_to_mp(prime, buf, size+1);
+
+		/* find the next integer which is prime, 8 round of miller-rabin */
+		if (mp_prime_next_prime(prime, 8, 0) != MP_OKAY) {
+			fprintf(stderr, "rsa generation failed\n");
+			exit(1);
+		}
+
+		/* subtract one to get p-1 */
+		if (mp_sub_d(prime, 1, primeminus) != MP_OKAY) {
+			fprintf(stderr, "rsa generation failed\n");
+			exit(1);
+		}
+		/* check relative primality to e */
+		if (mp_gcd(primeminus, rsa_e, &temp_gcd) != MP_OKAY) {
+			fprintf(stderr, "rsa generation failed\n");
+			exit(1);
+		}
+	} while (mp_cmp_d(&temp_gcd, 1) != MP_EQ); /* while gcd(p-1, e) != 1 */
+
+	/* now we have a good value for result */
+	mp_clear(&temp_gcd);
+	m_burn(buf, size+1);
+	m_free(buf);
+}
+
+#endif /* DROPBEAR_RSA */
diff --git a/genrsa.h b/genrsa.h
new file mode 100644
index 0000000000000000000000000000000000000000..ef9f579a7530d1910f4ce1a81badabeac2842631
--- /dev/null
+++ b/genrsa.h
@@ -0,0 +1,36 @@
+/*
+ * 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 _GENRSA_H_
+#define _GENRSA_H_
+
+#include "rsa.h"
+
+#ifdef DROPBEAR_RSA
+
+rsa_key * gen_rsa_priv_key(unsigned int size);
+
+#endif /* DROPBEAR_RSA */
+
+#endif /* _GENRSA_H_ */
diff --git a/includes.h b/includes.h
new file mode 100644
index 0000000000000000000000000000000000000000..06c969224a33dccb3a71324a28856aea2d8d3303
--- /dev/null
+++ b/includes.h
@@ -0,0 +1,155 @@
+/*
+ * 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 _INCLUDES_H_
+#define _INCLUDES_H_
+
+
+#include "config.h"
+#include "options.h"
+#include "debug.h"
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/param.h> /* required for BSD4_4 define */
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/un.h>
+#include <sys/wait.h>
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <grp.h>
+#include <limits.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <dirent.h>
+
+#ifdef HAVE_UTMP_H
+#include <utmp.h>
+#endif
+
+#ifdef HAVE_UTMPX_H
+#include <utmpx.h>
+#endif
+
+#ifdef HAVE_PATHS_H
+#include <paths.h>
+#endif
+
+#ifdef HAVE_LASTLOG_H
+#include <lastlog.h>
+#endif
+
+#include <arpa/inet.h>
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+
+/* netbsd 1.6 needs this to be included before netinet/ip.h for some
+ * undocumented reason */
+#ifdef HAVE_NETINET_IN_SYSTM_H
+#include <netinet/in_systm.h>
+#endif
+
+#include <netinet/ip.h>
+
+#ifdef HAVE_NETINET_TCP_H
+#include <netinet/tcp.h>
+#endif
+
+#ifdef HAVE_INTTYPES_H
+#include <inttypes.h>
+#endif
+
+#ifdef HAVE_LIBUTIL_H
+#include <libutil.h>
+#endif
+
+#ifdef HAVE_CRYPT_H
+#include <crypt.h>
+#endif
+
+#ifndef DISABLE_ZLIB
+#include <zlib.h>
+#endif
+
+#ifdef HAVE_UTIL_H
+#include <util.h>
+#endif
+
+#ifdef HAVE_SHADOW_H
+#include <shadow.h>
+#endif
+
+#ifdef HAVE_LIBGEN_H
+#include <libgen.h>
+#endif
+
+#include "libtomcrypt/src/headers/tomcrypt.h"
+#include "libtommath/tommath.h"
+
+#include "compat.h"
+#include "fake-rfc2553.h"
+
+#ifndef HAVE_UINT16_T
+#ifndef HAVE_U_INT16_T
+typedef unsigned short u_int16_t;
+#endif /* HAVE_U_INT16_T */
+typedef u_int16_t uint16_t;
+#endif /* HAVE_UINT16_T */
+
+#ifndef LOG_AUTHPRIV
+#define LOG_AUTHPRIV LOG_AUTH
+#endif
+
+/* glibc 2.1.3 systems have sockaddr_storage.__ss_family rather than
+ * sockaddr_storage.ss_family */
+#if !defined(HAVE_STRUCT_SOCKADDR_STORAGE_SS_FAMILY) \
+    && defined(HAVE_STRUCT_SOCKADDR_STORAGE___SS_FAMILY)
+#define ss_family __ss_family
+#endif
+
+/* so we can avoid warnings about unused params (ie in signal handlers etc) */
+#ifdef UNUSED 
+#elif defined(__GNUC__) 
+# define UNUSED(x) UNUSED_ ## x __attribute__((unused)) 
+#elif defined(__LCLINT__) 
+# define UNUSED(x) /*@unused@*/ x 
+#else 
+# define UNUSED(x) x 
+#endif
+
+#endif /* _INCLUDES_H_ */
diff --git a/install-sh b/install-sh
new file mode 100644
index 0000000000000000000000000000000000000000..e9de23842dcd44d2953129c866b1ad25f7e1f1d9
--- /dev/null
+++ b/install-sh
@@ -0,0 +1,251 @@
+#!/bin/sh
+#
+# install - install a program, script, or datafile
+# This comes from X11R5 (mit/util/scripts/install.sh).
+#
+# Copyright 1991 by the Massachusetts Institute of Technology
+#
+# Permission to use, copy, modify, distribute, and sell this software and its
+# documentation for any purpose is hereby granted without fee, provided that
+# the above copyright notice appear in all copies and that both that
+# copyright notice and this permission notice appear in supporting
+# documentation, and that the name of M.I.T. not be used in advertising or
+# publicity pertaining to distribution of the software without specific,
+# written prior permission.  M.I.T. makes no representations about the
+# suitability of this software for any purpose.  It is provided "as is"
+# without express or implied warranty.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# `make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch.  It can only install one file at a time, a restriction
+# shared with many OS's install programs.
+
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit="${DOITPROG-}"
+
+
+# put in absolute paths if you don't have them in your path; or use env. vars.
+
+mvprog="${MVPROG-mv}"
+cpprog="${CPPROG-cp}"
+chmodprog="${CHMODPROG-chmod}"
+chownprog="${CHOWNPROG-chown}"
+chgrpprog="${CHGRPPROG-chgrp}"
+stripprog="${STRIPPROG-strip}"
+rmprog="${RMPROG-rm}"
+mkdirprog="${MKDIRPROG-mkdir}"
+
+transformbasename=""
+transform_arg=""
+instcmd="$mvprog"
+chmodcmd="$chmodprog 0755"
+chowncmd=""
+chgrpcmd=""
+stripcmd=""
+rmcmd="$rmprog -f"
+mvcmd="$mvprog"
+src=""
+dst=""
+dir_arg=""
+
+while [ x"$1" != x ]; do
+    case $1 in
+	-c) instcmd="$cpprog"
+	    shift
+	    continue;;
+
+	-d) dir_arg=true
+	    shift
+	    continue;;
+
+	-m) chmodcmd="$chmodprog $2"
+	    shift
+	    shift
+	    continue;;
+
+	-o) chowncmd="$chownprog $2"
+	    shift
+	    shift
+	    continue;;
+
+	-g) chgrpcmd="$chgrpprog $2"
+	    shift
+	    shift
+	    continue;;
+
+	-s) stripcmd="$stripprog"
+	    shift
+	    continue;;
+
+	-t=*) transformarg=`echo $1 | sed 's/-t=//'`
+	    shift
+	    continue;;
+
+	-b=*) transformbasename=`echo $1 | sed 's/-b=//'`
+	    shift
+	    continue;;
+
+	*)  if [ x"$src" = x ]
+	    then
+		src=$1
+	    else
+		# this colon is to work around a 386BSD /bin/sh bug
+		:
+		dst=$1
+	    fi
+	    shift
+	    continue;;
+    esac
+done
+
+if [ x"$src" = x ]
+then
+	echo "install:	no input file specified"
+	exit 1
+else
+	true
+fi
+
+if [ x"$dir_arg" != x ]; then
+	dst=$src
+	src=""
+	
+	if [ -d $dst ]; then
+		instcmd=:
+		chmodcmd=""
+	else
+		instcmd=mkdir
+	fi
+else
+
+# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
+# might cause directories to be created, which would be especially bad 
+# if $src (and thus $dsttmp) contains '*'.
+
+	if [ -f $src -o -d $src ]
+	then
+		true
+	else
+		echo "install:  $src does not exist"
+		exit 1
+	fi
+	
+	if [ x"$dst" = x ]
+	then
+		echo "install:	no destination specified"
+		exit 1
+	else
+		true
+	fi
+
+# If destination is a directory, append the input filename; if your system
+# does not like double slashes in filenames, you may need to add some logic
+
+	if [ -d $dst ]
+	then
+		dst="$dst"/`basename $src`
+	else
+		true
+	fi
+fi
+
+## this sed command emulates the dirname command
+dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
+
+# Make sure that the destination directory exists.
+#  this part is taken from Noah Friedman's mkinstalldirs script
+
+# Skip lots of stat calls in the usual case.
+if [ ! -d "$dstdir" ]; then
+defaultIFS='	
+'
+IFS="${IFS-${defaultIFS}}"
+
+oIFS="${IFS}"
+# Some sh's can't handle IFS=/ for some reason.
+IFS='%'
+set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
+IFS="${oIFS}"
+
+pathcomp=''
+
+while [ $# -ne 0 ] ; do
+	pathcomp="${pathcomp}${1}"
+	shift
+
+	if [ ! -d "${pathcomp}" ] ;
+        then
+		$mkdirprog "${pathcomp}"
+	else
+		true
+	fi
+
+	pathcomp="${pathcomp}/"
+done
+fi
+
+if [ x"$dir_arg" != x ]
+then
+	$doit $instcmd $dst &&
+
+	if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
+	if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
+	if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
+	if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi
+else
+
+# If we're going to rename the final executable, determine the name now.
+
+	if [ x"$transformarg" = x ] 
+	then
+		dstfile=`basename $dst`
+	else
+		dstfile=`basename $dst $transformbasename | 
+			sed $transformarg`$transformbasename
+	fi
+
+# don't allow the sed command to completely eliminate the filename
+
+	if [ x"$dstfile" = x ] 
+	then
+		dstfile=`basename $dst`
+	else
+		true
+	fi
+
+# Make a temp file name in the proper directory.
+
+	dsttmp=$dstdir/#inst.$$#
+
+# Move or copy the file name to the temp name
+
+	$doit $instcmd $src $dsttmp &&
+
+	trap "rm -f ${dsttmp}" 0 &&
+
+# and set any options; do chmod last to preserve setuid bits
+
+# If any of these fail, we abort the whole thing.  If we want to
+# ignore errors from any of these, just make sure not to ignore
+# errors from the above "$doit $instcmd $src $dsttmp" command.
+
+	if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&
+	if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
+	if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
+	if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&
+
+# Now rename the file to the real destination.
+
+	$doit $rmcmd -f $dstdir/$dstfile &&
+	$doit $mvcmd $dsttmp $dstdir/$dstfile 
+
+fi &&
+
+
+exit 0
diff --git a/kex.h b/kex.h
new file mode 100644
index 0000000000000000000000000000000000000000..448ad1b03246d19782f75970e38a2221c616a223
--- /dev/null
+++ b/kex.h
@@ -0,0 +1,64 @@
+/*
+ * 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 _KEX_H_
+#define _KEX_H_
+
+#include "includes.h"
+#include "algo.h"
+
+void send_msg_kexinit();
+void recv_msg_kexinit();
+void send_msg_newkeys();
+void recv_msg_newkeys();
+void kexfirstinitialise();
+void gen_kexdh_vals(mp_int *dh_pub, mp_int *dh_priv);
+void kexdh_comb_key(mp_int *dh_pub_us, mp_int *dh_priv, mp_int *dh_pub_them,
+		sign_key *hostkey);
+
+void recv_msg_kexdh_init(); /* server */
+
+void send_msg_kexdh_init(); /* client */
+void recv_msg_kexdh_reply(); /* client */
+
+struct KEXState {
+
+	unsigned sentkexinit : 1; /*set when we've sent/recv kexinit packet */
+	unsigned recvkexinit : 1;
+	unsigned firstfollows : 1; /* true when first_kex_packet_follows is set */
+	unsigned sentnewkeys : 1; /* set once we've send/recv'ed MSG_NEWKEYS*/
+	unsigned recvnewkeys : 1;
+
+	unsigned donefirstkex : 1; /* Set to 1 after the first kex has completed,
+								  ie the transport layer has been set up */
+
+	long lastkextime; /* time of the last kex */
+	unsigned int datatrans; /* data transmitted since last kex */
+	unsigned int datarecv; /* data received since last kex */
+
+};
+
+#define MAX_KEXHASHBUF 2000
+
+#endif /* _KEX_H_ */
diff --git a/keyimport.c b/keyimport.c
new file mode 100644
index 0000000000000000000000000000000000000000..a0474f350e7897f31c3de372707f4c1455d5b690
--- /dev/null
+++ b/keyimport.c
@@ -0,0 +1,1730 @@
+/*
+ * Based on PuTTY's import.c for importing/exporting OpenSSH and SSH.com
+ * keyfiles.
+ *
+ * The horribleness of the code is probably mine (matt).
+ *
+ * Modifications copyright 2003 Matt Johnston
+ *
+ * PuTTY is copyright 1997-2003 Simon Tatham.
+ * 
+ * Portions copyright Robert de Bath, Joris van Rantwijk, Delian
+ * Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas Barry,
+ * Justin Bradford, and CORE SDI S.A.
+ * 
+ * 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 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 "keyimport.h"
+#include "bignum.h"
+#include "buffer.h"
+#include "dbutil.h"
+
+#define PUT_32BIT(cp, value) do { \
+  (cp)[3] = (unsigned char)(value); \
+  (cp)[2] = (unsigned char)((value) >> 8); \
+  (cp)[1] = (unsigned char)((value) >> 16); \
+  (cp)[0] = (unsigned char)((value) >> 24); } while (0)
+
+#define GET_32BIT(cp) \
+	(((unsigned long)(unsigned char)(cp)[0] << 24) | \
+	((unsigned long)(unsigned char)(cp)[1] << 16) | \
+	((unsigned long)(unsigned char)(cp)[2] << 8) | \
+	((unsigned long)(unsigned char)(cp)[3]))
+
+static int openssh_encrypted(const char *filename);
+static sign_key *openssh_read(const char *filename, char *passphrase);
+static int openssh_write(const char *filename, sign_key *key,
+				  char *passphrase);
+
+static int dropbear_write(const char*filename, sign_key * key);
+static sign_key *dropbear_read(const char* filename);
+
+#if 0
+static int sshcom_encrypted(const char *filename, char **comment);
+static struct ssh2_userkey *sshcom_read(const char *filename, char *passphrase);
+static int sshcom_write(const char *filename, struct ssh2_userkey *key,
+				 char *passphrase);
+#endif
+
+int import_encrypted(const char* filename, int filetype) {
+
+	if (filetype == KEYFILE_OPENSSH) {
+		return openssh_encrypted(filename);
+#if 0
+	} else if (filetype == KEYFILE_SSHCOM) {
+		return sshcom_encrypted(filename, NULL);
+#endif
+	}
+	return 0;
+}
+
+sign_key *import_read(const char *filename, char *passphrase, int filetype) {
+
+	if (filetype == KEYFILE_OPENSSH) {
+		return openssh_read(filename, passphrase);
+	} else if (filetype == KEYFILE_DROPBEAR) {
+		return dropbear_read(filename);
+#if 0
+	} else if (filetype == KEYFILE_SSHCOM) {
+		return sshcom_read(filename, passphrase);
+#endif
+	}
+	return NULL;
+}
+
+int import_write(const char *filename, sign_key *key, char *passphrase,
+		int filetype) {
+
+	if (filetype == KEYFILE_OPENSSH) {
+		return openssh_write(filename, key, passphrase);
+	} else if (filetype == KEYFILE_DROPBEAR) {
+		return dropbear_write(filename, key);
+#if 0
+	} else if (filetype == KEYFILE_SSHCOM) {
+		return sshcom_write(filename, key, passphrase);
+#endif
+	}
+	return 0;
+}
+
+static sign_key *dropbear_read(const char* filename) {
+
+	buffer * buf = NULL;
+	sign_key *ret = NULL;
+	int type;
+
+	buf = buf_new(MAX_PRIVKEY_SIZE);
+	if (buf_readfile(buf, filename) == DROPBEAR_FAILURE) {
+		goto error;
+	}
+
+	buf_setpos(buf, 0);
+	ret = new_sign_key();
+
+	type = DROPBEAR_SIGNKEY_ANY;
+	if (buf_get_priv_key(buf, ret, &type) == DROPBEAR_FAILURE){
+		goto error;
+	}
+	buf_free(buf);
+
+	return ret;
+
+error:
+	if (buf) {
+		buf_free(buf);
+	}
+	if (ret) {
+		sign_key_free(ret);
+	}
+	return NULL;
+}
+
+/* returns 0 on fail, 1 on success */
+static int dropbear_write(const char*filename, sign_key * key) {
+
+	int keytype = -1;
+	buffer * buf;
+	FILE*fp;
+	int len;
+	int ret;
+
+#ifdef DROPBEAR_RSA
+	if (key->rsakey != NULL) {
+		keytype = DROPBEAR_SIGNKEY_RSA;
+	}
+#endif
+#ifdef DROPBEAR_DSS
+	if (key->dsskey != NULL) {
+		keytype = DROPBEAR_SIGNKEY_DSS;
+	}
+#endif
+
+	buf = buf_new(MAX_PRIVKEY_SIZE);
+	buf_put_priv_key(buf, key, keytype);
+
+	fp = fopen(filename, "w");
+	if (!fp) {
+		ret = 0;
+		goto out;
+	}
+
+	buf_setpos(buf, 0);
+	do {
+		len = fwrite(buf_getptr(buf, buf->len - buf->pos),
+				1, buf->len - buf->pos, fp);
+		buf_incrpos(buf, len);
+	} while (len > 0 && buf->len != buf->pos);
+
+	fclose(fp);
+
+	if (buf->pos != buf->len) {
+		ret = 0;
+	} else {
+		ret = 1;
+	}
+out:
+	buf_free(buf);
+	return ret;
+}
+
+
+/* ----------------------------------------------------------------------
+ * Helper routines. (The base64 ones are defined in sshpubk.c.)
+ */
+
+#define isbase64(c) (	((c) >= 'A' && (c) <= 'Z') || \
+						 ((c) >= 'a' && (c) <= 'z') || \
+						 ((c) >= '0' && (c) <= '9') || \
+						 (c) == '+' || (c) == '/' || (c) == '=' \
+						 )
+
+/* cpl has to be less than 100 */
+static void base64_encode_fp(FILE * fp, unsigned char *data,
+		int datalen, int cpl)
+{
+    char out[100];
+    int n;
+	unsigned long outlen;
+	int rawcpl;
+	rawcpl = cpl * 3 / 4;
+	dropbear_assert((unsigned int)cpl < sizeof(out));
+
+    while (datalen > 0) {
+		n = (datalen < rawcpl ? datalen : rawcpl);
+		outlen = sizeof(out);
+		base64_encode(data, n, out, &outlen);
+		data += n;
+		datalen -= n;
+		fwrite(out, 1, outlen, fp);
+		fputc('\n', fp);
+    }
+}
+/*
+ * Read an ASN.1/BER identifier and length pair.
+ * 
+ * Flags are a combination of the #defines listed below.
+ * 
+ * Returns -1 if unsuccessful; otherwise returns the number of
+ * bytes used out of the source data.
+ */
+
+/* ASN.1 tag classes. */
+#define ASN1_CLASS_UNIVERSAL		(0 << 6)
+#define ASN1_CLASS_APPLICATION	  (1 << 6)
+#define ASN1_CLASS_CONTEXT_SPECIFIC (2 << 6)
+#define ASN1_CLASS_PRIVATE		  (3 << 6)
+#define ASN1_CLASS_MASK			 (3 << 6)
+
+/* Primitive versus constructed bit. */
+#define ASN1_CONSTRUCTED			(1 << 5)
+
+static int ber_read_id_len(void *source, int sourcelen,
+						   int *id, int *length, int *flags)
+{
+	unsigned char *p = (unsigned char *) source;
+
+	if (sourcelen == 0)
+		return -1;
+
+	*flags = (*p & 0xE0);
+	if ((*p & 0x1F) == 0x1F) {
+		*id = 0;
+		while (*p & 0x80) {
+			*id = (*id << 7) | (*p & 0x7F);
+			p++, sourcelen--;
+			if (sourcelen == 0)
+				return -1;
+		}
+		*id = (*id << 7) | (*p & 0x7F);
+		p++, sourcelen--;
+	} else {
+		*id = *p & 0x1F;
+		p++, sourcelen--;
+	}
+
+	if (sourcelen == 0)
+		return -1;
+
+	if (*p & 0x80) {
+		int n = *p & 0x7F;
+		p++, sourcelen--;
+		if (sourcelen < n)
+			return -1;
+		*length = 0;
+		while (n--)
+			*length = (*length << 8) | (*p++);
+		sourcelen -= n;
+	} else {
+		*length = *p;
+		p++, sourcelen--;
+	}
+
+	return p - (unsigned char *) source;
+}
+
+/*
+ * Write an ASN.1/BER identifier and length pair. Returns the
+ * number of bytes consumed. Assumes dest contains enough space.
+ * Will avoid writing anything if dest is NULL, but still return
+ * amount of space required.
+ */
+static int ber_write_id_len(void *dest, int id, int length, int flags)
+{
+	unsigned char *d = (unsigned char *)dest;
+	int len = 0;
+
+	if (id <= 30) {
+		/*
+		 * Identifier is one byte.
+		 */
+		len++;
+		if (d) *d++ = id | flags;
+	} else {
+		int n;
+		/*
+		 * Identifier is multiple bytes: the first byte is 11111
+		 * plus the flags, and subsequent bytes encode the value of
+		 * the identifier, 7 bits at a time, with the top bit of
+		 * each byte 1 except the last one which is 0.
+		 */
+		len++;
+		if (d) *d++ = 0x1F | flags;
+		for (n = 1; (id >> (7*n)) > 0; n++)
+			continue;					   /* count the bytes */
+		while (n--) {
+			len++;
+			if (d) *d++ = (n ? 0x80 : 0) | ((id >> (7*n)) & 0x7F);
+		}
+	}
+
+	if (length < 128) {
+		/*
+		 * Length is one byte.
+		 */
+		len++;
+		if (d) *d++ = length;
+	} else {
+		int n;
+		/*
+		 * Length is multiple bytes. The first is 0x80 plus the
+		 * number of subsequent bytes, and the subsequent bytes
+		 * encode the actual length.
+		 */
+		for (n = 1; (length >> (8*n)) > 0; n++)
+			continue;					   /* count the bytes */
+		len++;
+		if (d) *d++ = 0x80 | n;
+		while (n--) {
+			len++;
+			if (d) *d++ = (length >> (8*n)) & 0xFF;
+		}
+	}
+
+	return len;
+}
+
+
+/* Simple structure to point to an mp-int within a blob. */
+struct mpint_pos { void *start; int bytes; };
+
+/* ----------------------------------------------------------------------
+ * Code to read and write OpenSSH private keys.
+ */
+
+enum { OSSH_DSA, OSSH_RSA };
+struct openssh_key {
+	int type;
+	int encrypted;
+	char iv[32];
+	unsigned char *keyblob;
+	unsigned int keyblob_len, keyblob_size;
+};
+
+static struct openssh_key *load_openssh_key(const char *filename)
+{
+	struct openssh_key *ret;
+	FILE *fp;
+	char buffer[256];
+	char *errmsg = NULL, *p = NULL;
+	int headers_done;
+	unsigned long len, outlen;
+
+	ret = (struct openssh_key*)m_malloc(sizeof(struct openssh_key));
+	ret->keyblob = NULL;
+	ret->keyblob_len = ret->keyblob_size = 0;
+	ret->encrypted = 0;
+	memset(ret->iv, 0, sizeof(ret->iv));
+
+	if (strlen(filename) == 1 && filename[0] == '-') {
+		fp = stdin;
+	} else {
+		fp = fopen(filename, "r");
+	}
+	if (!fp) {
+		errmsg = "Unable to open key file";
+		goto error;
+	}
+	if (!fgets(buffer, sizeof(buffer), fp) ||
+		0 != strncmp(buffer, "-----BEGIN ", 11) ||
+		0 != strcmp(buffer+strlen(buffer)-17, "PRIVATE KEY-----\n")) {
+		errmsg = "File does not begin with OpenSSH key header";
+		goto error;
+	}
+	if (!strcmp(buffer, "-----BEGIN RSA PRIVATE KEY-----\n"))
+		ret->type = OSSH_RSA;
+	else if (!strcmp(buffer, "-----BEGIN DSA PRIVATE KEY-----\n"))
+		ret->type = OSSH_DSA;
+	else {
+		errmsg = "Unrecognised key type";
+		goto error;
+	}
+
+	headers_done = 0;
+	while (1) {
+		if (!fgets(buffer, sizeof(buffer), fp)) {
+			errmsg = "Unexpected end of file";
+			goto error;
+		}
+		if (0 == strncmp(buffer, "-----END ", 9) &&
+			0 == strcmp(buffer+strlen(buffer)-17, "PRIVATE KEY-----\n"))
+			break;					   /* done */
+		if ((p = strchr(buffer, ':')) != NULL) {
+			if (headers_done) {
+				errmsg = "Header found in body of key data";
+				goto error;
+			}
+			*p++ = '\0';
+			while (*p && isspace((unsigned char)*p)) p++;
+			if (!strcmp(buffer, "Proc-Type")) {
+				if (p[0] != '4' || p[1] != ',') {
+					errmsg = "Proc-Type is not 4 (only 4 is supported)";
+					goto error;
+				}
+				p += 2;
+				if (!strcmp(p, "ENCRYPTED\n"))
+					ret->encrypted = 1;
+			} else if (!strcmp(buffer, "DEK-Info")) {
+				int i, j;
+
+				if (strncmp(p, "DES-EDE3-CBC,", 13)) {
+					errmsg = "Ciphers other than DES-EDE3-CBC not supported";
+					goto error;
+				}
+				p += 13;
+				for (i = 0; i < 8; i++) {
+					if (1 != sscanf(p, "%2x", &j))
+						break;
+					ret->iv[i] = j;
+					p += 2;
+				}
+				if (i < 8) {
+					errmsg = "Expected 16-digit iv in DEK-Info";
+					goto error;
+				}
+			}
+		} else {
+			headers_done = 1;
+			len = strlen(buffer);
+			outlen = len*4/3;
+			if (ret->keyblob_len + outlen > ret->keyblob_size) {
+				ret->keyblob_size = ret->keyblob_len + outlen + 256;
+				ret->keyblob = (unsigned char*)m_realloc(ret->keyblob,
+						ret->keyblob_size);
+			}
+			outlen = ret->keyblob_size - ret->keyblob_len;
+			if (base64_decode(buffer, len, 
+						ret->keyblob + ret->keyblob_len, &outlen) != CRYPT_OK){
+				errmsg = "Error decoding base64";
+				goto error;
+			}
+			ret->keyblob_len += outlen;
+		}
+	}
+
+	if (ret->keyblob_len == 0 || !ret->keyblob) {
+		errmsg = "Key body not present";
+		goto error;
+	}
+
+	if (ret->encrypted && ret->keyblob_len % 8 != 0) {
+		errmsg = "Encrypted key blob is not a multiple of cipher block size";
+		goto error;
+	}
+
+	memset(buffer, 0, sizeof(buffer));
+	return ret;
+
+	error:
+	memset(buffer, 0, sizeof(buffer));
+	if (ret) {
+		if (ret->keyblob) {
+			memset(ret->keyblob, 0, ret->keyblob_size);
+			m_free(ret->keyblob);
+		}
+		memset(&ret, 0, sizeof(ret));
+		m_free(ret);
+	}
+	if (errmsg) {
+		fprintf(stderr, "Error: %s\n", errmsg);
+	}
+	return NULL;
+}
+
+static int openssh_encrypted(const char *filename)
+{
+	struct openssh_key *key = load_openssh_key(filename);
+	int ret;
+
+	if (!key)
+		return 0;
+	ret = key->encrypted;
+	memset(key->keyblob, 0, key->keyblob_size);
+	m_free(key->keyblob);
+	memset(&key, 0, sizeof(key));
+	m_free(key);
+	return ret;
+}
+
+static sign_key *openssh_read(const char *filename, char *passphrase)
+{
+	struct openssh_key *key;
+	unsigned char *p;
+	int ret, id, len, flags;
+	int i, num_integers = 0;
+	sign_key *retval = NULL;
+	char *errmsg;
+	char *modptr = NULL;
+	int modlen = -9999;
+	int type;
+
+	sign_key *retkey;
+	buffer * blobbuf = NULL;
+
+	key = load_openssh_key(filename);
+
+	if (!key)
+		return NULL;
+
+	if (key->encrypted) {
+		errmsg = "encrypted keys not supported currently";
+		goto error;
+#if 0
+		/* matt TODO */
+		/*
+		 * Derive encryption key from passphrase and iv/salt:
+		 * 
+		 *  - let block A equal MD5(passphrase || iv)
+		 *  - let block B equal MD5(A || passphrase || iv)
+		 *  - block C would be MD5(B || passphrase || iv) and so on
+		 *  - encryption key is the first N bytes of A || B
+		 */
+		struct MD5Context md5c;
+		unsigned char keybuf[32];
+
+		MD5Init(&md5c);
+		MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase));
+		MD5Update(&md5c, (unsigned char *)key->iv, 8);
+		MD5Final(keybuf, &md5c);
+
+		MD5Init(&md5c);
+		MD5Update(&md5c, keybuf, 16);
+		MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase));
+		MD5Update(&md5c, (unsigned char *)key->iv, 8);
+		MD5Final(keybuf+16, &md5c);
+
+		/*
+		 * Now decrypt the key blob.
+		 */
+		des3_decrypt_pubkey_ossh(keybuf, (unsigned char *)key->iv,
+								 key->keyblob, key->keyblob_len);
+
+		memset(&md5c, 0, sizeof(md5c));
+		memset(keybuf, 0, sizeof(keybuf));
+#endif 
+	}
+
+	/*
+	 * Now we have a decrypted key blob, which contains an ASN.1
+	 * encoded private key. We must now untangle the ASN.1.
+	 *
+	 * We expect the whole key blob to be formatted as a SEQUENCE
+	 * (0x30 followed by a length code indicating that the rest of
+	 * the blob is part of the sequence). Within that SEQUENCE we
+	 * expect to see a bunch of INTEGERs. What those integers mean
+	 * depends on the key type:
+	 *
+	 *  - For RSA, we expect the integers to be 0, n, e, d, p, q,
+	 *	dmp1, dmq1, iqmp in that order. (The last three are d mod
+	 *	(p-1), d mod (q-1), inverse of q mod p respectively.)
+	 *
+	 *  - For DSA, we expect them to be 0, p, q, g, y, x in that
+	 *	order.
+	 */
+	
+	p = key->keyblob;
+
+	/* Expect the SEQUENCE header. Take its absence as a failure to decrypt. */
+	ret = ber_read_id_len(p, key->keyblob_len, &id, &len, &flags);
+	p += ret;
+	if (ret < 0 || id != 16) {
+		errmsg = "ASN.1 decoding failure - wrong password?";
+		goto error;
+	}
+
+	/* Expect a load of INTEGERs. */
+	if (key->type == OSSH_RSA)
+		num_integers = 9;
+	else if (key->type == OSSH_DSA)
+		num_integers = 6;
+
+	/*
+	 * Space to create key blob in.
+	 */
+	blobbuf = buf_new(3000);
+
+	if (key->type == OSSH_DSA) {
+		buf_putstring(blobbuf, "ssh-dss", 7);
+	} else if (key->type == OSSH_RSA) {
+		buf_putstring(blobbuf, "ssh-rsa", 7);
+	}
+
+	for (i = 0; i < num_integers; i++) {
+		ret = ber_read_id_len(p, key->keyblob+key->keyblob_len-p,
+							  &id, &len, &flags);
+		p += ret;
+		if (ret < 0 || id != 2 ||
+			key->keyblob+key->keyblob_len-p < len) {
+			errmsg = "ASN.1 decoding failure";
+			goto error;
+		}
+
+		if (i == 0) {
+			/*
+			 * The first integer should be zero always (I think
+			 * this is some sort of version indication).
+			 */
+			if (len != 1 || p[0] != 0) {
+				errmsg = "Version number mismatch";
+				goto error;
+			}
+		} else if (key->type == OSSH_RSA) {
+			/*
+			 * OpenSSH key order is n, e, d, p, q, dmp1, dmq1, iqmp
+			 * but we want e, n, d, p, q
+			 */
+			if (i == 1) {
+				/* Save the details for after we deal with number 2. */
+				modptr = (char *)p;
+				modlen = len;
+			} else if (i >= 2 && i <= 5) {
+				buf_putstring(blobbuf, p, len);
+				if (i == 2) {
+					buf_putstring(blobbuf, modptr, modlen);
+				}
+			}
+		} else if (key->type == OSSH_DSA) {
+			/*
+			 * OpenSSH key order is p, q, g, y, x,
+			 * we want the same.
+			 */
+			buf_putstring(blobbuf, p, len);
+		}
+
+		/* Skip past the number. */
+		p += len;
+	}
+
+	/*
+	 * Now put together the actual key. Simplest way to do this is
+	 * to assemble our own key blobs and feed them to the createkey
+	 * functions; this is a bit faffy but it does mean we get all
+	 * the sanity checks for free.
+	 */
+	retkey = new_sign_key();
+	buf_setpos(blobbuf, 0);
+	type = DROPBEAR_SIGNKEY_ANY;
+	if (buf_get_priv_key(blobbuf, retkey, &type)
+			!= DROPBEAR_SUCCESS) {
+		errmsg = "unable to create key structure";
+		sign_key_free(retkey);
+		retkey = NULL;
+		goto error;
+	}
+
+	errmsg = NULL;					 /* no error */
+	retval = retkey;
+
+	error:
+	if (blobbuf) {
+		buf_burn(blobbuf);
+		buf_free(blobbuf);
+	}
+	m_burn(key->keyblob, key->keyblob_size);
+	m_free(key->keyblob);
+	m_burn(key, sizeof(key));
+	m_free(key);
+	if (errmsg) {
+		fprintf(stderr, "Error: %s\n", errmsg);
+	}
+	return retval;
+}
+
+static int openssh_write(const char *filename, sign_key *key,
+				  char *passphrase)
+{
+	buffer * keyblob = NULL;
+	buffer * extrablob = NULL; /* used for calculated values to write */
+	unsigned char *outblob = NULL;
+	int outlen = -9999;
+	struct mpint_pos numbers[9];
+	int nnumbers = -1, pos, len, seqlen, i;
+	char *header = NULL, *footer = NULL;
+	char zero[1];
+	unsigned char iv[8];
+	int ret = 0;
+	FILE *fp;
+	int keytype = -1;
+
+#ifdef DROPBEAR_RSA
+	mp_int dmp1, dmq1, iqmp, tmpval; /* for rsa */
+
+	if (key->rsakey != NULL) {
+		keytype = DROPBEAR_SIGNKEY_RSA;
+	}
+#endif
+#ifdef DROPBEAR_DSS
+	if (key->dsskey != NULL) {
+		keytype = DROPBEAR_SIGNKEY_DSS;
+	}
+#endif
+
+	dropbear_assert(keytype != -1);
+
+	/*
+	 * Fetch the key blobs.
+	 */
+	keyblob = buf_new(3000);
+	buf_put_priv_key(keyblob, key, keytype);
+
+	buf_setpos(keyblob, 0);
+	/* skip the "ssh-rsa" or "ssh-dss" header */
+	buf_incrpos(keyblob, buf_getint(keyblob));
+
+	/*
+	 * Find the sequence of integers to be encoded into the OpenSSH
+	 * key blob, and also decide on the header line.
+	 */
+	numbers[0].start = zero; numbers[0].bytes = 1; zero[0] = '\0';
+
+#ifdef DROPBEAR_RSA
+	if (keytype == DROPBEAR_SIGNKEY_RSA) {
+
+		if (key->rsakey->p == NULL || key->rsakey->q == NULL) {
+			fprintf(stderr, "Pre-0.33 Dropbear keys cannot be converted to OpenSSH keys.\n");
+			goto error;
+		}
+
+		/* e */
+		numbers[2].bytes = buf_getint(keyblob);
+		numbers[2].start = buf_getptr(keyblob, numbers[2].bytes);
+		buf_incrpos(keyblob, numbers[2].bytes);
+		
+		/* n */
+		numbers[1].bytes = buf_getint(keyblob);
+		numbers[1].start = buf_getptr(keyblob, numbers[1].bytes);
+		buf_incrpos(keyblob, numbers[1].bytes);
+		
+		/* d */
+		numbers[3].bytes = buf_getint(keyblob);
+		numbers[3].start = buf_getptr(keyblob, numbers[3].bytes);
+		buf_incrpos(keyblob, numbers[3].bytes);
+		
+		/* p */
+		numbers[4].bytes = buf_getint(keyblob);
+		numbers[4].start = buf_getptr(keyblob, numbers[4].bytes);
+		buf_incrpos(keyblob, numbers[4].bytes);
+		
+		/* q */
+		numbers[5].bytes = buf_getint(keyblob);
+		numbers[5].start = buf_getptr(keyblob, numbers[5].bytes);
+		buf_incrpos(keyblob, numbers[5].bytes);
+
+		/* now calculate some extra parameters: */
+		m_mp_init(&tmpval);
+		m_mp_init(&dmp1);
+		m_mp_init(&dmq1);
+		m_mp_init(&iqmp);
+
+		/* dmp1 = d mod (p-1) */
+		if (mp_sub_d(key->rsakey->p, 1, &tmpval) != MP_OKAY) {
+			fprintf(stderr, "Bignum error for p-1\n");
+			goto error;
+		}
+		if (mp_mod(key->rsakey->d, &tmpval, &dmp1) != MP_OKAY) {
+			fprintf(stderr, "Bignum error for dmp1\n");
+			goto error;
+		}
+
+		/* dmq1 = d mod (q-1) */
+		if (mp_sub_d(key->rsakey->q, 1, &tmpval) != MP_OKAY) {
+			fprintf(stderr, "Bignum error for q-1\n");
+			goto error;
+		}
+		if (mp_mod(key->rsakey->d, &tmpval, &dmq1) != MP_OKAY) {
+			fprintf(stderr, "Bignum error for dmq1\n");
+			goto error;
+		}
+
+		/* iqmp = (q^-1) mod p */
+		if (mp_invmod(key->rsakey->q, key->rsakey->p, &iqmp) != MP_OKAY) {
+			fprintf(stderr, "Bignum error for iqmp\n");
+			goto error;
+		}
+
+		extrablob = buf_new(2000);
+		buf_putmpint(extrablob, &dmp1);
+		buf_putmpint(extrablob, &dmq1);
+		buf_putmpint(extrablob, &iqmp);
+		buf_setpos(extrablob, 0);
+		mp_clear(&dmp1);
+		mp_clear(&dmq1);
+		mp_clear(&iqmp);
+		mp_clear(&tmpval);
+		
+		/* dmp1 */
+		numbers[6].bytes = buf_getint(extrablob);
+		numbers[6].start = buf_getptr(extrablob, numbers[6].bytes);
+		buf_incrpos(extrablob, numbers[6].bytes);
+		
+		/* dmq1 */
+		numbers[7].bytes = buf_getint(extrablob);
+		numbers[7].start = buf_getptr(extrablob, numbers[7].bytes);
+		buf_incrpos(extrablob, numbers[7].bytes);
+		
+		/* iqmp */
+		numbers[8].bytes = buf_getint(extrablob);
+		numbers[8].start = buf_getptr(extrablob, numbers[8].bytes);
+		buf_incrpos(extrablob, numbers[8].bytes);
+
+		nnumbers = 9;
+		header = "-----BEGIN RSA PRIVATE KEY-----\n";
+		footer = "-----END RSA PRIVATE KEY-----\n";
+	}
+#endif /* DROPBEAR_RSA */
+
+#ifdef DROPBEAR_DSS
+	if (keytype == DROPBEAR_SIGNKEY_DSS) {
+
+		/* p */
+		numbers[1].bytes = buf_getint(keyblob);
+		numbers[1].start = buf_getptr(keyblob, numbers[1].bytes);
+		buf_incrpos(keyblob, numbers[1].bytes);
+
+		/* q */
+		numbers[2].bytes = buf_getint(keyblob);
+		numbers[2].start = buf_getptr(keyblob, numbers[2].bytes);
+		buf_incrpos(keyblob, numbers[2].bytes);
+
+		/* g */
+		numbers[3].bytes = buf_getint(keyblob);
+		numbers[3].start = buf_getptr(keyblob, numbers[3].bytes);
+		buf_incrpos(keyblob, numbers[3].bytes);
+
+		/* y */
+		numbers[4].bytes = buf_getint(keyblob);
+		numbers[4].start = buf_getptr(keyblob, numbers[4].bytes);
+		buf_incrpos(keyblob, numbers[4].bytes);
+
+		/* x */
+		numbers[5].bytes = buf_getint(keyblob);
+		numbers[5].start = buf_getptr(keyblob, numbers[5].bytes);
+		buf_incrpos(keyblob, numbers[5].bytes);
+
+		nnumbers = 6;
+		header = "-----BEGIN DSA PRIVATE KEY-----\n";
+		footer = "-----END DSA PRIVATE KEY-----\n";
+	}
+#endif /* DROPBEAR_DSS */
+
+	/*
+	 * Now count up the total size of the ASN.1 encoded integers,
+	 * so as to determine the length of the containing SEQUENCE.
+	 */
+	len = 0;
+	for (i = 0; i < nnumbers; i++) {
+		len += ber_write_id_len(NULL, 2, numbers[i].bytes, 0);
+		len += numbers[i].bytes;
+	}
+	seqlen = len;
+	/* Now add on the SEQUENCE header. */
+	len += ber_write_id_len(NULL, 16, seqlen, ASN1_CONSTRUCTED);
+	/* Round up to the cipher block size, ensuring we have at least one
+	 * byte of padding (see below). */
+	outlen = len;
+	if (passphrase)
+		outlen = (outlen+8) &~ 7;
+
+	/*
+	 * Now we know how big outblob needs to be. Allocate it.
+	 */
+	outblob = (unsigned char*)m_malloc(outlen);
+
+	/*
+	 * And write the data into it.
+	 */
+	pos = 0;
+	pos += ber_write_id_len(outblob+pos, 16, seqlen, ASN1_CONSTRUCTED);
+	for (i = 0; i < nnumbers; i++) {
+		pos += ber_write_id_len(outblob+pos, 2, numbers[i].bytes, 0);
+		memcpy(outblob+pos, numbers[i].start, numbers[i].bytes);
+		pos += numbers[i].bytes;
+	}
+
+	/*
+	 * Padding on OpenSSH keys is deterministic. The number of
+	 * padding bytes is always more than zero, and always at most
+	 * the cipher block length. The value of each padding byte is
+	 * equal to the number of padding bytes. So a plaintext that's
+	 * an exact multiple of the block size will be padded with 08
+	 * 08 08 08 08 08 08 08 (assuming a 64-bit block cipher); a
+	 * plaintext one byte less than a multiple of the block size
+	 * will be padded with just 01.
+	 * 
+	 * This enables the OpenSSL key decryption function to strip
+	 * off the padding algorithmically and return the unpadded
+	 * plaintext to the next layer: it looks at the final byte, and
+	 * then expects to find that many bytes at the end of the data
+	 * with the same value. Those are all removed and the rest is
+	 * returned.
+	 */
+	dropbear_assert(pos == len);
+	while (pos < outlen) {
+		outblob[pos++] = outlen - len;
+	}
+
+	/*
+	 * Encrypt the key.
+	 */
+	if (passphrase) {
+		fprintf(stderr, "Encrypted keys aren't supported currently\n");
+		goto error;
+#if 0
+		/*
+		 * Invent an iv. Then derive encryption key from passphrase
+		 * and iv/salt:
+		 * 
+		 *  - let block A equal MD5(passphrase || iv)
+		 *  - let block B equal MD5(A || passphrase || iv)
+		 *  - block C would be MD5(B || passphrase || iv) and so on
+		 *  - encryption key is the first N bytes of A || B
+		 */
+		struct MD5Context md5c;
+		unsigned char keybuf[32];
+
+		for (i = 0; i < 8; i++) iv[i] = random_byte();
+
+		MD5Init(&md5c);
+		MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase));
+		MD5Update(&md5c, iv, 8);
+		MD5Final(keybuf, &md5c);
+
+		MD5Init(&md5c);
+		MD5Update(&md5c, keybuf, 16);
+		MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase));
+		MD5Update(&md5c, iv, 8);
+		MD5Final(keybuf+16, &md5c);
+
+		/*
+		 * Now encrypt the key blob.
+		 */
+		des3_encrypt_pubkey_ossh(keybuf, iv, outblob, outlen);
+
+		memset(&md5c, 0, sizeof(md5c));
+		memset(keybuf, 0, sizeof(keybuf));
+#endif
+	}
+
+	/*
+	 * And save it. We'll use Unix line endings just in case it's
+	 * subsequently transferred in binary mode.
+	 */
+	if (strlen(filename) == 1 && filename[0] == '-') {
+		fp = stdout;
+	} else {
+		fp = fopen(filename, "wb");	  /* ensure Unix line endings */
+	}
+	if (!fp) {
+		fprintf(stderr, "Failed opening output file\n");
+		goto error;
+	}
+	fputs(header, fp);
+	if (passphrase) {
+		fprintf(fp, "Proc-Type: 4,ENCRYPTED\nDEK-Info: DES-EDE3-CBC,");
+		for (i = 0; i < 8; i++)
+			fprintf(fp, "%02X", iv[i]);
+		fprintf(fp, "\n\n");
+	}
+	base64_encode_fp(fp, outblob, outlen, 64);
+	fputs(footer, fp);
+	fclose(fp);
+	ret = 1;
+
+	error:
+	if (outblob) {
+		memset(outblob, 0, outlen);
+		m_free(outblob);
+	}
+	if (keyblob) {
+		buf_burn(keyblob);
+		buf_free(keyblob);
+	}
+	if (extrablob) {
+		buf_burn(extrablob);
+		buf_free(extrablob);
+	}
+	return ret;
+}
+
+#if 0
+/* XXX TODO ssh.com stuff isn't going yet */
+
+/* ----------------------------------------------------------------------
+ * Code to read ssh.com private keys.
+ */
+
+/*
+ * The format of the base64 blob is largely ssh2-packet-formatted,
+ * except that mpints are a bit different: they're more like the
+ * old ssh1 mpint. You have a 32-bit bit count N, followed by
+ * (N+7)/8 bytes of data.
+ * 
+ * So. The blob contains:
+ * 
+ *  - uint32 0x3f6ff9eb	   (magic number)
+ *  - uint32 size			 (total blob size)
+ *  - string key-type		 (see below)
+ *  - string cipher-type	  (tells you if key is encrypted)
+ *  - string encrypted-blob
+ * 
+ * (The first size field includes the size field itself and the
+ * magic number before it. All other size fields are ordinary ssh2
+ * strings, so the size field indicates how much data is to
+ * _follow_.)
+ * 
+ * The encrypted blob, once decrypted, contains a single string
+ * which in turn contains the payload. (This allows padding to be
+ * added after that string while still making it clear where the
+ * real payload ends. Also it probably makes for a reasonable
+ * decryption check.)
+ * 
+ * The payload blob, for an RSA key, contains:
+ *  - mpint e
+ *  - mpint d
+ *  - mpint n  (yes, the public and private stuff is intermixed)
+ *  - mpint u  (presumably inverse of p mod q)
+ *  - mpint p  (p is the smaller prime)
+ *  - mpint q  (q is the larger)
+ * 
+ * For a DSA key, the payload blob contains:
+ *  - uint32 0
+ *  - mpint p
+ *  - mpint g
+ *  - mpint q
+ *  - mpint y
+ *  - mpint x
+ * 
+ * Alternatively, if the parameters are `predefined', that
+ * (0,p,g,q) sequence can be replaced by a uint32 1 and a string
+ * containing some predefined parameter specification. *shudder*,
+ * but I doubt we'll encounter this in real life.
+ * 
+ * The key type strings are ghastly. The RSA key I looked at had a
+ * type string of
+ * 
+ *   `if-modn{sign{rsa-pkcs1-sha1},encrypt{rsa-pkcs1v2-oaep}}'
+ * 
+ * and the DSA key wasn't much better:
+ * 
+ *   `dl-modp{sign{dsa-nist-sha1},dh{plain}}'
+ * 
+ * It isn't clear that these will always be the same. I think it
+ * might be wise just to look at the `if-modn{sign{rsa' and
+ * `dl-modp{sign{dsa' prefixes.
+ * 
+ * Finally, the encryption. The cipher-type string appears to be
+ * either `none' or `3des-cbc'. Looks as if this is SSH2-style
+ * 3des-cbc (i.e. outer cbc rather than inner). The key is created
+ * from the passphrase by means of yet another hashing faff:
+ * 
+ *  - first 16 bytes are MD5(passphrase)
+ *  - next 16 bytes are MD5(passphrase || first 16 bytes)
+ *  - if there were more, they'd be MD5(passphrase || first 32),
+ *	and so on.
+ */
+
+#define SSHCOM_MAGIC_NUMBER 0x3f6ff9eb
+
+struct sshcom_key {
+	char comment[256];				 /* allowing any length is overkill */
+	unsigned char *keyblob;
+	int keyblob_len, keyblob_size;
+};
+
+static struct sshcom_key *load_sshcom_key(const char *filename)
+{
+	struct sshcom_key *ret;
+	FILE *fp;
+	char buffer[256];
+	int len;
+	char *errmsg, *p;
+	int headers_done;
+	char base64_bit[4];
+	int base64_chars = 0;
+
+	ret = snew(struct sshcom_key);
+	ret->comment[0] = '\0';
+	ret->keyblob = NULL;
+	ret->keyblob_len = ret->keyblob_size = 0;
+
+	fp = fopen(filename, "r");
+	if (!fp) {
+		errmsg = "Unable to open key file";
+		goto error;
+	}
+	if (!fgets(buffer, sizeof(buffer), fp) ||
+		0 != strcmp(buffer, "---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ----\n")) {
+		errmsg = "File does not begin with ssh.com key header";
+		goto error;
+	}
+
+	headers_done = 0;
+	while (1) {
+		if (!fgets(buffer, sizeof(buffer), fp)) {
+			errmsg = "Unexpected end of file";
+			goto error;
+		}
+		if (!strcmp(buffer, "---- END SSH2 ENCRYPTED PRIVATE KEY ----\n"))
+			break;					 /* done */
+		if ((p = strchr(buffer, ':')) != NULL) {
+			if (headers_done) {
+				errmsg = "Header found in body of key data";
+				goto error;
+			}
+			*p++ = '\0';
+			while (*p && isspace((unsigned char)*p)) p++;
+			/*
+			 * Header lines can end in a trailing backslash for
+			 * continuation.
+			 */
+			while ((len = strlen(p)) > (int)(sizeof(buffer) - (p-buffer) -1) ||
+				   p[len-1] != '\n' || p[len-2] == '\\') {
+				if (len > (int)((p-buffer) + sizeof(buffer)-2)) {
+					errmsg = "Header line too long to deal with";
+					goto error;
+				}
+				if (!fgets(p+len-2, sizeof(buffer)-(p-buffer)-(len-2), fp)) {
+					errmsg = "Unexpected end of file";
+					goto error;
+				}
+			}
+			p[strcspn(p, "\n")] = '\0';
+			if (!strcmp(buffer, "Comment")) {
+				/* Strip quotes in comment if present. */
+				if (p[0] == '"' && p[strlen(p)-1] == '"') {
+					p++;
+					p[strlen(p)-1] = '\0';
+				}
+				strncpy(ret->comment, p, sizeof(ret->comment));
+				ret->comment[sizeof(ret->comment)-1] = '\0';
+			}
+		} else {
+			headers_done = 1;
+
+			p = buffer;
+			while (isbase64(*p)) {
+				base64_bit[base64_chars++] = *p;
+				if (base64_chars == 4) {
+					unsigned char out[3];
+
+					base64_chars = 0;
+
+					len = base64_decode_atom(base64_bit, out);
+
+					if (len <= 0) {
+						errmsg = "Invalid base64 encoding";
+						goto error;
+					}
+
+					if (ret->keyblob_len + len > ret->keyblob_size) {
+						ret->keyblob_size = ret->keyblob_len + len + 256;
+						ret->keyblob = sresize(ret->keyblob, ret->keyblob_size,
+											   unsigned char);
+					}
+
+					memcpy(ret->keyblob + ret->keyblob_len, out, len);
+					ret->keyblob_len += len;
+				}
+
+				p++;
+			}
+		}
+	}
+
+	if (ret->keyblob_len == 0 || !ret->keyblob) {
+		errmsg = "Key body not present";
+		goto error;
+	}
+
+	return ret;
+
+	error:
+	if (ret) {
+		if (ret->keyblob) {
+			memset(ret->keyblob, 0, ret->keyblob_size);
+			m_free(ret->keyblob);
+		}
+		memset(&ret, 0, sizeof(ret));
+		m_free(ret);
+	}
+	return NULL;
+}
+
+int sshcom_encrypted(const char *filename, char **comment)
+{
+	struct sshcom_key *key = load_sshcom_key(filename);
+	int pos, len, answer;
+
+	*comment = NULL;
+	if (!key)
+		return 0;
+
+	/*
+	 * Check magic number.
+	 */
+	if (GET_32BIT(key->keyblob) != 0x3f6ff9eb)
+		return 0;					  /* key is invalid */
+
+	/*
+	 * Find the cipher-type string.
+	 */
+	answer = 0;
+	pos = 8;
+	if (key->keyblob_len < pos+4)
+		goto done;					 /* key is far too short */
+	pos += 4 + GET_32BIT(key->keyblob + pos);   /* skip key type */
+	if (key->keyblob_len < pos+4)
+		goto done;					 /* key is far too short */
+	len = GET_32BIT(key->keyblob + pos);   /* find cipher-type length */
+	if (key->keyblob_len < pos+4+len)
+		goto done;					 /* cipher type string is incomplete */
+	if (len != 4 || 0 != memcmp(key->keyblob + pos + 4, "none", 4))
+		answer = 1;
+
+	done:
+	*comment = dupstr(key->comment);
+	memset(key->keyblob, 0, key->keyblob_size);
+	m_free(key->keyblob);
+	memset(&key, 0, sizeof(key));
+	m_free(key);
+	return answer;
+}
+
+static int sshcom_read_mpint(void *data, int len, struct mpint_pos *ret)
+{
+	int bits;
+	int bytes;
+	unsigned char *d = (unsigned char *) data;
+
+	if (len < 4)
+		goto error;
+	bits = GET_32BIT(d);
+
+	bytes = (bits + 7) / 8;
+	if (len < 4+bytes)
+		goto error;
+
+	ret->start = d + 4;
+	ret->bytes = bytes;
+	return bytes+4;
+
+	error:
+	ret->start = NULL;
+	ret->bytes = -1;
+	return len;						/* ensure further calls fail as well */
+}
+
+static int sshcom_put_mpint(void *target, void *data, int len)
+{
+	unsigned char *d = (unsigned char *)target;
+	unsigned char *i = (unsigned char *)data;
+	int bits = len * 8 - 1;
+
+	while (bits > 0) {
+		if (*i & (1 << (bits & 7)))
+			break;
+		if (!(bits-- & 7))
+			i++, len--;
+	}
+
+	PUT_32BIT(d, bits+1);
+	memcpy(d+4, i, len);
+	return len+4;
+}
+
+sign_key *sshcom_read(const char *filename, char *passphrase)
+{
+	struct sshcom_key *key = load_sshcom_key(filename);
+	char *errmsg;
+	int pos, len;
+	const char prefix_rsa[] = "if-modn{sign{rsa";
+	const char prefix_dsa[] = "dl-modp{sign{dsa";
+	enum { RSA, DSA } type;
+	int encrypted;
+	char *ciphertext;
+	int cipherlen;
+	struct ssh2_userkey *ret = NULL, *retkey;
+	const struct ssh_signkey *alg;
+	unsigned char *blob = NULL;
+	int blobsize, publen, privlen;
+
+	if (!key)
+		return NULL;
+
+	/*
+	 * Check magic number.
+	 */
+	if (GET_32BIT(key->keyblob) != SSHCOM_MAGIC_NUMBER) {
+		errmsg = "Key does not begin with magic number";
+		goto error;
+	}
+
+	/*
+	 * Determine the key type.
+	 */
+	pos = 8;
+	if (key->keyblob_len < pos+4 ||
+		(len = GET_32BIT(key->keyblob + pos)) > key->keyblob_len - pos - 4) {
+		errmsg = "Key blob does not contain a key type string";
+		goto error;
+	}
+	if (len > sizeof(prefix_rsa) - 1 &&
+		!memcmp(key->keyblob+pos+4, prefix_rsa, sizeof(prefix_rsa) - 1)) {
+		type = RSA;
+	} else if (len > sizeof(prefix_dsa) - 1 &&
+		!memcmp(key->keyblob+pos+4, prefix_dsa, sizeof(prefix_dsa) - 1)) {
+		type = DSA;
+	} else {
+		errmsg = "Key is of unknown type";
+		goto error;
+	}
+	pos += 4+len;
+
+	/*
+	 * Determine the cipher type.
+	 */
+	if (key->keyblob_len < pos+4 ||
+		(len = GET_32BIT(key->keyblob + pos)) > key->keyblob_len - pos - 4) {
+		errmsg = "Key blob does not contain a cipher type string";
+		goto error;
+	}
+	if (len == 4 && !memcmp(key->keyblob+pos+4, "none", 4))
+		encrypted = 0;
+	else if (len == 8 && !memcmp(key->keyblob+pos+4, "3des-cbc", 8))
+		encrypted = 1;
+	else {
+		errmsg = "Key encryption is of unknown type";
+		goto error;
+	}
+	pos += 4+len;
+
+	/*
+	 * Get hold of the encrypted part of the key.
+	 */
+	if (key->keyblob_len < pos+4 ||
+		(len = GET_32BIT(key->keyblob + pos)) > key->keyblob_len - pos - 4) {
+		errmsg = "Key blob does not contain actual key data";
+		goto error;
+	}
+	ciphertext = (char *)key->keyblob + pos + 4;
+	cipherlen = len;
+	if (cipherlen == 0) {
+		errmsg = "Length of key data is zero";
+		goto error;
+	}
+
+	/*
+	 * Decrypt it if necessary.
+	 */
+	if (encrypted) {
+		/*
+		 * Derive encryption key from passphrase and iv/salt:
+		 * 
+		 *  - let block A equal MD5(passphrase)
+		 *  - let block B equal MD5(passphrase || A)
+		 *  - block C would be MD5(passphrase || A || B) and so on
+		 *  - encryption key is the first N bytes of A || B
+		 */
+		struct MD5Context md5c;
+		unsigned char keybuf[32], iv[8];
+
+		if (cipherlen % 8 != 0) {
+			errmsg = "Encrypted part of key is not a multiple of cipher block"
+				" size";
+			goto error;
+		}
+
+		MD5Init(&md5c);
+		MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase));
+		MD5Final(keybuf, &md5c);
+
+		MD5Init(&md5c);
+		MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase));
+		MD5Update(&md5c, keybuf, 16);
+		MD5Final(keybuf+16, &md5c);
+
+		/*
+		 * Now decrypt the key blob.
+		 */
+		memset(iv, 0, sizeof(iv));
+		des3_decrypt_pubkey_ossh(keybuf, iv, (unsigned char *)ciphertext,
+								 cipherlen);
+
+		memset(&md5c, 0, sizeof(md5c));
+		memset(keybuf, 0, sizeof(keybuf));
+
+		/*
+		 * Hereafter we return WRONG_PASSPHRASE for any parsing
+		 * error. (But only if we've just tried to decrypt it!
+		 * Returning WRONG_PASSPHRASE for an unencrypted key is
+		 * automatic doom.)
+		 */
+		if (encrypted)
+			ret = SSH2_WRONG_PASSPHRASE;
+	}
+
+	/*
+	 * Strip away the containing string to get to the real meat.
+	 */
+	len = GET_32BIT(ciphertext);
+	if (len > cipherlen-4) {
+		errmsg = "containing string was ill-formed";
+		goto error;
+	}
+	ciphertext += 4;
+	cipherlen = len;
+
+	/*
+	 * Now we break down into RSA versus DSA. In either case we'll
+	 * construct public and private blobs in our own format, and
+	 * end up feeding them to alg->createkey().
+	 */
+	blobsize = cipherlen + 256;
+	blob = snewn(blobsize, unsigned char);
+	privlen = 0;
+	if (type == RSA) {
+		struct mpint_pos n, e, d, u, p, q;
+		int pos = 0;
+		pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &e);
+		pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &d);
+		pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &n);
+		pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &u);
+		pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &p);
+		pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &q);
+		if (!q.start) {
+			errmsg = "key data did not contain six integers";
+			goto error;
+		}
+
+		alg = &ssh_rsa;
+		pos = 0;
+		pos += put_string(blob+pos, "ssh-rsa", 7);
+		pos += put_mp(blob+pos, e.start, e.bytes);
+		pos += put_mp(blob+pos, n.start, n.bytes);
+		publen = pos;
+		pos += put_string(blob+pos, d.start, d.bytes);
+		pos += put_mp(blob+pos, q.start, q.bytes);
+		pos += put_mp(blob+pos, p.start, p.bytes);
+		pos += put_mp(blob+pos, u.start, u.bytes);
+		privlen = pos - publen;
+	} else if (type == DSA) {
+		struct mpint_pos p, q, g, x, y;
+		int pos = 4;
+		if (GET_32BIT(ciphertext) != 0) {
+			errmsg = "predefined DSA parameters not supported";
+			goto error;
+		}
+		pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &p);
+		pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &g);
+		pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &q);
+		pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &y);
+		pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &x);
+		if (!x.start) {
+			errmsg = "key data did not contain five integers";
+			goto error;
+		}
+
+		alg = &ssh_dss;
+		pos = 0;
+		pos += put_string(blob+pos, "ssh-dss", 7);
+		pos += put_mp(blob+pos, p.start, p.bytes);
+		pos += put_mp(blob+pos, q.start, q.bytes);
+		pos += put_mp(blob+pos, g.start, g.bytes);
+		pos += put_mp(blob+pos, y.start, y.bytes);
+		publen = pos;
+		pos += put_mp(blob+pos, x.start, x.bytes);
+		privlen = pos - publen;
+	}
+
+	dropbear_assert(privlen > 0);			   /* should have bombed by now if not */
+
+	retkey = snew(struct ssh2_userkey);
+	retkey->alg = alg;
+	retkey->data = alg->createkey(blob, publen, blob+publen, privlen);
+	if (!retkey->data) {
+		m_free(retkey);
+		errmsg = "unable to create key data structure";
+		goto error;
+	}
+	retkey->comment = dupstr(key->comment);
+
+	errmsg = NULL; /* no error */
+	ret = retkey;
+
+	error:
+	if (blob) {
+		memset(blob, 0, blobsize);
+		m_free(blob);
+	}
+	memset(key->keyblob, 0, key->keyblob_size);
+	m_free(key->keyblob);
+	memset(&key, 0, sizeof(key));
+	m_free(key);
+	return ret;
+}
+
+int sshcom_write(const char *filename, sign_key *key,
+				 char *passphrase)
+{
+	unsigned char *pubblob, *privblob;
+	int publen, privlen;
+	unsigned char *outblob;
+	int outlen;
+	struct mpint_pos numbers[6];
+	int nnumbers, initial_zero, pos, lenpos, i;
+	char *type;
+	char *ciphertext;
+	int cipherlen;
+	int ret = 0;
+	FILE *fp;
+
+	/*
+	 * Fetch the key blobs.
+	 */
+	pubblob = key->alg->public_blob(key->data, &publen);
+	privblob = key->alg->private_blob(key->data, &privlen);
+	outblob = NULL;
+
+	/*
+	 * Find the sequence of integers to be encoded into the OpenSSH
+	 * key blob, and also decide on the header line.
+	 */
+	if (key->alg == &ssh_rsa) {
+		int pos;
+		struct mpint_pos n, e, d, p, q, iqmp;
+
+		pos = 4 + GET_32BIT(pubblob);
+		pos += ssh2_read_mpint(pubblob+pos, publen-pos, &e);
+		pos += ssh2_read_mpint(pubblob+pos, publen-pos, &n);
+		pos = 0;
+		pos += ssh2_read_mpint(privblob+pos, privlen-pos, &d);
+		pos += ssh2_read_mpint(privblob+pos, privlen-pos, &p);
+		pos += ssh2_read_mpint(privblob+pos, privlen-pos, &q);
+		pos += ssh2_read_mpint(privblob+pos, privlen-pos, &iqmp);
+
+		dropbear_assert(e.start && iqmp.start); /* can't go wrong */
+
+		numbers[0] = e;
+		numbers[1] = d;
+		numbers[2] = n;
+		numbers[3] = iqmp;
+		numbers[4] = q;
+		numbers[5] = p;
+
+		nnumbers = 6;
+		initial_zero = 0;
+		type = "if-modn{sign{rsa-pkcs1-sha1},encrypt{rsa-pkcs1v2-oaep}}";
+	} else if (key->alg == &ssh_dss) {
+		int pos;
+		struct mpint_pos p, q, g, y, x;
+
+		pos = 4 + GET_32BIT(pubblob);
+		pos += ssh2_read_mpint(pubblob+pos, publen-pos, &p);
+		pos += ssh2_read_mpint(pubblob+pos, publen-pos, &q);
+		pos += ssh2_read_mpint(pubblob+pos, publen-pos, &g);
+		pos += ssh2_read_mpint(pubblob+pos, publen-pos, &y);
+		pos = 0;
+		pos += ssh2_read_mpint(privblob+pos, privlen-pos, &x);
+
+		dropbear_assert(y.start && x.start); /* can't go wrong */
+
+		numbers[0] = p;
+		numbers[1] = g;
+		numbers[2] = q;
+		numbers[3] = y;
+		numbers[4] = x;
+
+		nnumbers = 5;
+		initial_zero = 1;
+		type = "dl-modp{sign{dsa-nist-sha1},dh{plain}}";
+	} else {
+		dropbear_assert(0);					 /* zoinks! */
+	}
+
+	/*
+	 * Total size of key blob will be somewhere under 512 plus
+	 * combined length of integers. We'll calculate the more
+	 * precise size as we construct the blob.
+	 */
+	outlen = 512;
+	for (i = 0; i < nnumbers; i++)
+		outlen += 4 + numbers[i].bytes;
+	outblob = snewn(outlen, unsigned char);
+
+	/*
+	 * Create the unencrypted key blob.
+	 */
+	pos = 0;
+	PUT_32BIT(outblob+pos, SSHCOM_MAGIC_NUMBER); pos += 4;
+	pos += 4;							   /* length field, fill in later */
+	pos += put_string(outblob+pos, type, strlen(type));
+	{
+		char *ciphertype = passphrase ? "3des-cbc" : "none";
+		pos += put_string(outblob+pos, ciphertype, strlen(ciphertype));
+	}
+	lenpos = pos;					   /* remember this position */
+	pos += 4;							   /* encrypted-blob size */
+	pos += 4;							   /* encrypted-payload size */
+	if (initial_zero) {
+		PUT_32BIT(outblob+pos, 0);
+		pos += 4;
+	}
+	for (i = 0; i < nnumbers; i++)
+		pos += sshcom_put_mpint(outblob+pos,
+								numbers[i].start, numbers[i].bytes);
+	/* Now wrap up the encrypted payload. */
+	PUT_32BIT(outblob+lenpos+4, pos - (lenpos+8));
+	/* Pad encrypted blob to a multiple of cipher block size. */
+	if (passphrase) {
+		int padding = -(pos - (lenpos+4)) & 7;
+		while (padding--)
+			outblob[pos++] = random_byte();
+	}
+	ciphertext = (char *)outblob+lenpos+4;
+	cipherlen = pos - (lenpos+4);
+	dropbear_assert(!passphrase || cipherlen % 8 == 0);
+	/* Wrap up the encrypted blob string. */
+	PUT_32BIT(outblob+lenpos, cipherlen);
+	/* And finally fill in the total length field. */
+	PUT_32BIT(outblob+4, pos);
+
+	dropbear_assert(pos < outlen);
+
+	/*
+	 * Encrypt the key.
+	 */
+	if (passphrase) {
+		/*
+		 * Derive encryption key from passphrase and iv/salt:
+		 * 
+		 *  - let block A equal MD5(passphrase)
+		 *  - let block B equal MD5(passphrase || A)
+		 *  - block C would be MD5(passphrase || A || B) and so on
+		 *  - encryption key is the first N bytes of A || B
+		 */
+		struct MD5Context md5c;
+		unsigned char keybuf[32], iv[8];
+
+		MD5Init(&md5c);
+		MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase));
+		MD5Final(keybuf, &md5c);
+
+		MD5Init(&md5c);
+		MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase));
+		MD5Update(&md5c, keybuf, 16);
+		MD5Final(keybuf+16, &md5c);
+
+		/*
+		 * Now decrypt the key blob.
+		 */
+		memset(iv, 0, sizeof(iv));
+		des3_encrypt_pubkey_ossh(keybuf, iv, (unsigned char *)ciphertext,
+								 cipherlen);
+
+		memset(&md5c, 0, sizeof(md5c));
+		memset(keybuf, 0, sizeof(keybuf));
+	}
+
+	/*
+	 * And save it. We'll use Unix line endings just in case it's
+	 * subsequently transferred in binary mode.
+	 */
+	fp = fopen(filename, "wb");	  /* ensure Unix line endings */
+	if (!fp)
+		goto error;
+	fputs("---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ----\n", fp);
+	fprintf(fp, "Comment: \"");
+	/*
+	 * Comment header is broken with backslash-newline if it goes
+	 * over 70 chars. Although it's surrounded by quotes, it
+	 * _doesn't_ escape backslashes or quotes within the string.
+	 * Don't ask me, I didn't design it.
+	 */
+	{
+		int slen = 60;					   /* starts at 60 due to "Comment: " */
+		char *c = key->comment;
+		while ((int)strlen(c) > slen) {
+			fprintf(fp, "%.*s\\\n", slen, c);
+			c += slen;
+			slen = 70;					   /* allow 70 chars on subsequent lines */
+		}
+		fprintf(fp, "%s\"\n", c);
+	}
+	base64_encode_fp(fp, outblob, pos, 70);
+	fputs("---- END SSH2 ENCRYPTED PRIVATE KEY ----\n", fp);
+	fclose(fp);
+	ret = 1;
+
+	error:
+	if (outblob) {
+		memset(outblob, 0, outlen);
+		m_free(outblob);
+	}
+	if (privblob) {
+		memset(privblob, 0, privlen);
+		m_free(privblob);
+	}
+	if (pubblob) {
+		memset(pubblob, 0, publen);
+		m_free(pubblob);
+	}
+	return ret;
+}
+#endif /* ssh.com stuff disabled */
diff --git a/keyimport.h b/keyimport.h
new file mode 100644
index 0000000000000000000000000000000000000000..19f212f6334977527b293df0da7d15930a753a41
--- /dev/null
+++ b/keyimport.h
@@ -0,0 +1,42 @@
+/*
+ * 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. */
+
+#ifndef _KEYIMPORT_H_
+#define _KEYIMPORT_H_
+
+#include "includes.h"
+#include "signkey.h"
+
+enum {
+	KEYFILE_DROPBEAR,
+	KEYFILE_OPENSSH,
+	KEYFILE_SSHCOM
+};
+
+int import_write(const char *filename, sign_key *key, char *passphrase,
+		int filetype);
+sign_key *import_read(const char *filename, char *passphrase, int filetype);
+int import_encrypted(const char* filename, int filetype);
+
+#endif /* _KEYIMPORT_H_ */
diff --git a/libtommath/LICENSE b/libtommath/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..5baa792a6502f536835dcf76a003594b80d47ef7
--- /dev/null
+++ b/libtommath/LICENSE
@@ -0,0 +1,4 @@
+LibTomMath is hereby released into the Public Domain.  
+
+-- Tom St Denis
+
diff --git a/libtommath/Makefile.in b/libtommath/Makefile.in
new file mode 100644
index 0000000000000000000000000000000000000000..e96173a404f78180c93af5f12ab73fea53d11ac7
--- /dev/null
+++ b/libtommath/Makefile.in
@@ -0,0 +1,165 @@
+#Makefile for GCC
+#
+#Tom St Denis
+
+#version of library 
+VERSION=0.35
+
+VPATH=@srcdir@
+srcdir=@srcdir@
+
+# Dropbear takes flags from the toplevel makefile
+CFLAGS += -I$(srcdir)
+
+#CFLAGS  +=  -I./ -Wall -W -Wshadow -Wsign-compare
+
+#for speed 
+#CFLAGS += -O3 -funroll-all-loops
+
+#for size 
+#CFLAGS += -Os
+
+#x86 optimizations [should be valid for any GCC install though]
+#CFLAGS  += -fomit-frame-pointer
+
+#debug
+#CFLAGS += -g3
+
+#install as this user
+USER=root
+GROUP=root
+
+default: libtommath.a
+
+#default files to install
+LIBNAME=libtommath.a
+HEADERS=tommath.h tommath_class.h tommath_superclass.h
+
+#LIBPATH-The directory for libtommath to be installed to.
+#INCPATH-The directory to install the header files for libtommath.
+#DATAPATH-The directory to install the pdf docs.
+DESTDIR=
+LIBPATH=/usr/lib
+INCPATH=/usr/include
+DATAPATH=/usr/share/doc/libtommath/pdf
+
+OBJECTS=bncore.o bn_mp_init.o bn_mp_clear.o bn_mp_exch.o bn_mp_grow.o bn_mp_shrink.o \
+bn_mp_clamp.o bn_mp_zero.o  bn_mp_set.o bn_mp_set_int.o bn_mp_init_size.o bn_mp_copy.o \
+bn_mp_init_copy.o bn_mp_abs.o bn_mp_neg.o bn_mp_cmp_mag.o bn_mp_cmp.o bn_mp_cmp_d.o \
+bn_mp_rshd.o bn_mp_lshd.o bn_mp_mod_2d.o bn_mp_div_2d.o bn_mp_mul_2d.o bn_mp_div_2.o \
+bn_mp_mul_2.o bn_s_mp_add.o bn_s_mp_sub.o bn_fast_s_mp_mul_digs.o bn_s_mp_mul_digs.o \
+bn_fast_s_mp_mul_high_digs.o bn_s_mp_mul_high_digs.o bn_fast_s_mp_sqr.o bn_s_mp_sqr.o \
+bn_mp_add.o bn_mp_sub.o bn_mp_karatsuba_mul.o bn_mp_mul.o bn_mp_karatsuba_sqr.o \
+bn_mp_sqr.o bn_mp_div.o bn_mp_mod.o bn_mp_add_d.o bn_mp_sub_d.o bn_mp_mul_d.o \
+bn_mp_div_d.o bn_mp_mod_d.o bn_mp_expt_d.o bn_mp_addmod.o bn_mp_submod.o \
+bn_mp_mulmod.o bn_mp_sqrmod.o bn_mp_gcd.o bn_mp_lcm.o bn_fast_mp_invmod.o bn_mp_invmod.o \
+bn_mp_reduce.o bn_mp_montgomery_setup.o bn_fast_mp_montgomery_reduce.o bn_mp_montgomery_reduce.o \
+bn_mp_exptmod_fast.o bn_mp_exptmod.o bn_mp_2expt.o bn_mp_n_root.o bn_mp_jacobi.o bn_reverse.o \
+bn_mp_count_bits.o bn_mp_read_unsigned_bin.o bn_mp_read_signed_bin.o bn_mp_to_unsigned_bin.o \
+bn_mp_to_signed_bin.o bn_mp_unsigned_bin_size.o bn_mp_signed_bin_size.o  \
+bn_mp_xor.o bn_mp_and.o bn_mp_or.o bn_mp_rand.o bn_mp_montgomery_calc_normalization.o \
+bn_mp_prime_is_divisible.o bn_prime_tab.o bn_mp_prime_fermat.o bn_mp_prime_miller_rabin.o \
+bn_mp_prime_is_prime.o bn_mp_prime_next_prime.o bn_mp_dr_reduce.o \
+bn_mp_dr_is_modulus.o bn_mp_dr_setup.o bn_mp_reduce_setup.o \
+bn_mp_toom_mul.o bn_mp_toom_sqr.o bn_mp_div_3.o bn_s_mp_exptmod.o \
+bn_mp_reduce_2k.o bn_mp_reduce_is_2k.o bn_mp_reduce_2k_setup.o \
+bn_mp_reduce_2k_l.o bn_mp_reduce_is_2k_l.o bn_mp_reduce_2k_setup_l.o \
+bn_mp_radix_smap.o bn_mp_read_radix.o bn_mp_toradix.o bn_mp_radix_size.o \
+bn_mp_fread.o bn_mp_fwrite.o bn_mp_cnt_lsb.o bn_error.o \
+bn_mp_init_multi.o bn_mp_clear_multi.o bn_mp_exteuclid.o bn_mp_toradix_n.o \
+bn_mp_prime_random_ex.o bn_mp_get_int.o bn_mp_sqrt.o bn_mp_is_square.o bn_mp_init_set.o \
+bn_mp_init_set_int.o bn_mp_invmod_slow.o bn_mp_prime_rabin_miller_trials.o \
+bn_mp_to_signed_bin_n.o bn_mp_to_unsigned_bin_n.o
+
+libtommath.a:  $(OBJECTS)
+	$(AR) $(ARFLAGS) libtommath.a $(OBJECTS)
+	$(RANLIB) libtommath.a
+
+#make a profiled library (takes a while!!!)
+#
+# This will build the library with profile generation
+# then run the test demo and rebuild the library.
+# 
+# So far I've seen improvements in the MP math
+profiled:
+	make CFLAGS="$(CFLAGS) -fprofile-arcs -DTESTING" timing
+	./ltmtest
+	rm -f *.a *.o ltmtest
+	make CFLAGS="$(CFLAGS) -fbranch-probabilities"
+
+#make a single object profiled library 
+profiled_single:
+	perl gen.pl
+	$(CC) $(CFLAGS) -fprofile-arcs -DTESTING -c mpi.c -o mpi.o
+	$(CC) $(CFLAGS) -DTESTING -DTIMER demo/timing.c mpi.o -o ltmtest
+	./ltmtest
+	rm -f *.o ltmtest
+	$(CC) $(CFLAGS) -fbranch-probabilities -DTESTING -c mpi.c -o mpi.o
+	$(AR) $(ARFLAGS) libtommath.a mpi.o
+	ranlib libtommath.a	
+
+install: libtommath.a
+	install -d -g $(GROUP) -o $(USER) $(DESTDIR)$(LIBPATH)
+	install -d -g $(GROUP) -o $(USER) $(DESTDIR)$(INCPATH)
+	install -g $(GROUP) -o $(USER) $(LIBNAME) $(DESTDIR)$(LIBPATH)
+	install -g $(GROUP) -o $(USER) $(HEADERS) $(DESTDIR)$(INCPATH)
+
+test: libtommath.a demo/demo.o
+	$(CC) $(CFLAGS) demo/demo.o libtommath.a -o test
+	
+mtest: test	
+	cd mtest ; $(CC) $(CFLAGS) mtest.c -o mtest
+        
+timing: libtommath.a
+	$(CC) $(CFLAGS) -DTIMER demo/timing.c libtommath.a -o ltmtest
+
+# makes the LTM book DVI file, requires tetex, perl and makeindex [part of tetex I think]
+docdvi: tommath.src
+	cd pics ; make 
+	echo "hello" > tommath.ind
+	perl booker.pl
+	latex tommath > /dev/null
+	latex tommath > /dev/null
+	makeindex tommath
+	latex tommath > /dev/null
+
+# poster, makes the single page PDF poster
+poster: poster.tex
+	pdflatex poster
+	rm -f poster.aux poster.log 
+
+# makes the LTM book PDF file, requires tetex, cleans up the LaTeX temp files
+docs:   docdvi
+	dvipdf tommath
+	rm -f tommath.log tommath.aux tommath.dvi tommath.idx tommath.toc tommath.lof tommath.ind tommath.ilg
+	cd pics ; make clean
+	
+#LTM user manual
+mandvi: bn.tex
+	echo "hello" > bn.ind
+	latex bn > /dev/null
+	latex bn > /dev/null
+	makeindex bn
+	latex bn > /dev/null
+
+#LTM user manual [pdf]
+manual:	mandvi
+	pdflatex bn >/dev/null
+	rm -f bn.aux bn.dvi bn.log bn.idx bn.lof bn.out bn.toc
+
+pretty: 
+	perl pretty.build
+
+clean:
+	rm -f *.bat *.pdf *.o *.a *.obj *.lib *.exe *.dll etclib/*.o demo/demo.o test ltmtest mpitest mtest/mtest mtest/mtest.exe \
+        *.idx *.toc *.log *.aux *.dvi *.lof *.ind *.ilg *.ps *.log *.s mpi.c *.da *.dyn *.dpi tommath.tex *.lo *.la
+	rm -rf .libs
+	cd etc && make clean
+	cd pics && make clean
+
+zipup: clean manual poster docs
+	perl gen.pl ; mv mpi.c pre_gen/ ; \
+	cd .. ; rm -rf ltm* libtommath-$(VERSION) ; mkdir libtommath-$(VERSION) ; \
+	cp -R ./libtommath/* ./libtommath-$(VERSION)/ ; \
+	tar -c libtommath-$(VERSION)/* | bzip2 -9vvc > ltm-$(VERSION).tar.bz2 ; \
+	zip -9 -r ltm-$(VERSION).zip libtommath-$(VERSION)/*
diff --git a/libtommath/TODO b/libtommath/TODO
new file mode 100644
index 0000000000000000000000000000000000000000..deffba1333190a7e0bac0cb4a3e7fbfe7a6fff01
--- /dev/null
+++ b/libtommath/TODO
@@ -0,0 +1,16 @@
+things for book in order of importance...
+
+- Fix up pseudo-code [only] for combas that are not consistent with source
+- Start in chapter 3 [basics] and work up...
+   - re-write to prose [less abrupt]
+   - clean up pseudo code [spacing]
+   - more examples where appropriate and figures
+
+Goal:
+   - Get sync done by mid January [roughly 8-12 hours work]
+   - Finish ch3-6 by end of January [roughly 12-16 hours of work]
+   - Finish ch7-end by mid Feb [roughly 20-24 hours of work].
+
+Goal isn't "first edition" but merely cleaner to read.
+
+
diff --git a/bn.tex b/libtommath/bn.tex
similarity index 100%
rename from bn.tex
rename to libtommath/bn.tex
diff --git a/bn_error.c b/libtommath/bn_error.c
similarity index 100%
rename from bn_error.c
rename to libtommath/bn_error.c
diff --git a/bn_fast_mp_invmod.c b/libtommath/bn_fast_mp_invmod.c
similarity index 100%
rename from bn_fast_mp_invmod.c
rename to libtommath/bn_fast_mp_invmod.c
diff --git a/bn_fast_mp_montgomery_reduce.c b/libtommath/bn_fast_mp_montgomery_reduce.c
similarity index 100%
rename from bn_fast_mp_montgomery_reduce.c
rename to libtommath/bn_fast_mp_montgomery_reduce.c
diff --git a/bn_fast_s_mp_mul_digs.c b/libtommath/bn_fast_s_mp_mul_digs.c
similarity index 100%
rename from bn_fast_s_mp_mul_digs.c
rename to libtommath/bn_fast_s_mp_mul_digs.c
diff --git a/bn_fast_s_mp_mul_high_digs.c b/libtommath/bn_fast_s_mp_mul_high_digs.c
similarity index 100%
rename from bn_fast_s_mp_mul_high_digs.c
rename to libtommath/bn_fast_s_mp_mul_high_digs.c
diff --git a/bn_fast_s_mp_sqr.c b/libtommath/bn_fast_s_mp_sqr.c
similarity index 100%
rename from bn_fast_s_mp_sqr.c
rename to libtommath/bn_fast_s_mp_sqr.c
diff --git a/bn_mp_2expt.c b/libtommath/bn_mp_2expt.c
similarity index 100%
rename from bn_mp_2expt.c
rename to libtommath/bn_mp_2expt.c
diff --git a/bn_mp_abs.c b/libtommath/bn_mp_abs.c
similarity index 100%
rename from bn_mp_abs.c
rename to libtommath/bn_mp_abs.c
diff --git a/bn_mp_add.c b/libtommath/bn_mp_add.c
similarity index 100%
rename from bn_mp_add.c
rename to libtommath/bn_mp_add.c
diff --git a/bn_mp_add_d.c b/libtommath/bn_mp_add_d.c
similarity index 100%
rename from bn_mp_add_d.c
rename to libtommath/bn_mp_add_d.c
diff --git a/bn_mp_addmod.c b/libtommath/bn_mp_addmod.c
similarity index 100%
rename from bn_mp_addmod.c
rename to libtommath/bn_mp_addmod.c
diff --git a/bn_mp_and.c b/libtommath/bn_mp_and.c
similarity index 100%
rename from bn_mp_and.c
rename to libtommath/bn_mp_and.c
diff --git a/bn_mp_clamp.c b/libtommath/bn_mp_clamp.c
similarity index 100%
rename from bn_mp_clamp.c
rename to libtommath/bn_mp_clamp.c
diff --git a/bn_mp_clear.c b/libtommath/bn_mp_clear.c
similarity index 100%
rename from bn_mp_clear.c
rename to libtommath/bn_mp_clear.c
diff --git a/bn_mp_clear_multi.c b/libtommath/bn_mp_clear_multi.c
similarity index 100%
rename from bn_mp_clear_multi.c
rename to libtommath/bn_mp_clear_multi.c
diff --git a/bn_mp_cmp.c b/libtommath/bn_mp_cmp.c
similarity index 100%
rename from bn_mp_cmp.c
rename to libtommath/bn_mp_cmp.c
diff --git a/bn_mp_cmp_d.c b/libtommath/bn_mp_cmp_d.c
similarity index 100%
rename from bn_mp_cmp_d.c
rename to libtommath/bn_mp_cmp_d.c
diff --git a/bn_mp_cmp_mag.c b/libtommath/bn_mp_cmp_mag.c
similarity index 100%
rename from bn_mp_cmp_mag.c
rename to libtommath/bn_mp_cmp_mag.c
diff --git a/bn_mp_cnt_lsb.c b/libtommath/bn_mp_cnt_lsb.c
similarity index 100%
rename from bn_mp_cnt_lsb.c
rename to libtommath/bn_mp_cnt_lsb.c
diff --git a/bn_mp_copy.c b/libtommath/bn_mp_copy.c
similarity index 100%
rename from bn_mp_copy.c
rename to libtommath/bn_mp_copy.c
diff --git a/bn_mp_count_bits.c b/libtommath/bn_mp_count_bits.c
similarity index 100%
rename from bn_mp_count_bits.c
rename to libtommath/bn_mp_count_bits.c
diff --git a/bn_mp_div.c b/libtommath/bn_mp_div.c
similarity index 100%
rename from bn_mp_div.c
rename to libtommath/bn_mp_div.c
diff --git a/bn_mp_div_2.c b/libtommath/bn_mp_div_2.c
similarity index 100%
rename from bn_mp_div_2.c
rename to libtommath/bn_mp_div_2.c
diff --git a/bn_mp_div_2d.c b/libtommath/bn_mp_div_2d.c
similarity index 100%
rename from bn_mp_div_2d.c
rename to libtommath/bn_mp_div_2d.c
diff --git a/bn_mp_div_3.c b/libtommath/bn_mp_div_3.c
similarity index 100%
rename from bn_mp_div_3.c
rename to libtommath/bn_mp_div_3.c
diff --git a/bn_mp_div_d.c b/libtommath/bn_mp_div_d.c
similarity index 100%
rename from bn_mp_div_d.c
rename to libtommath/bn_mp_div_d.c
diff --git a/bn_mp_dr_is_modulus.c b/libtommath/bn_mp_dr_is_modulus.c
similarity index 100%
rename from bn_mp_dr_is_modulus.c
rename to libtommath/bn_mp_dr_is_modulus.c
diff --git a/bn_mp_dr_reduce.c b/libtommath/bn_mp_dr_reduce.c
similarity index 100%
rename from bn_mp_dr_reduce.c
rename to libtommath/bn_mp_dr_reduce.c
diff --git a/bn_mp_dr_setup.c b/libtommath/bn_mp_dr_setup.c
similarity index 100%
rename from bn_mp_dr_setup.c
rename to libtommath/bn_mp_dr_setup.c
diff --git a/bn_mp_exch.c b/libtommath/bn_mp_exch.c
similarity index 100%
rename from bn_mp_exch.c
rename to libtommath/bn_mp_exch.c
diff --git a/bn_mp_expt_d.c b/libtommath/bn_mp_expt_d.c
similarity index 100%
rename from bn_mp_expt_d.c
rename to libtommath/bn_mp_expt_d.c
diff --git a/bn_mp_exptmod.c b/libtommath/bn_mp_exptmod.c
similarity index 100%
rename from bn_mp_exptmod.c
rename to libtommath/bn_mp_exptmod.c
diff --git a/bn_mp_exptmod_fast.c b/libtommath/bn_mp_exptmod_fast.c
similarity index 100%
rename from bn_mp_exptmod_fast.c
rename to libtommath/bn_mp_exptmod_fast.c
diff --git a/bn_mp_exteuclid.c b/libtommath/bn_mp_exteuclid.c
similarity index 100%
rename from bn_mp_exteuclid.c
rename to libtommath/bn_mp_exteuclid.c
diff --git a/bn_mp_fread.c b/libtommath/bn_mp_fread.c
similarity index 100%
rename from bn_mp_fread.c
rename to libtommath/bn_mp_fread.c
diff --git a/bn_mp_fwrite.c b/libtommath/bn_mp_fwrite.c
similarity index 100%
rename from bn_mp_fwrite.c
rename to libtommath/bn_mp_fwrite.c
diff --git a/bn_mp_gcd.c b/libtommath/bn_mp_gcd.c
similarity index 100%
rename from bn_mp_gcd.c
rename to libtommath/bn_mp_gcd.c
diff --git a/bn_mp_get_int.c b/libtommath/bn_mp_get_int.c
similarity index 100%
rename from bn_mp_get_int.c
rename to libtommath/bn_mp_get_int.c
diff --git a/bn_mp_grow.c b/libtommath/bn_mp_grow.c
similarity index 100%
rename from bn_mp_grow.c
rename to libtommath/bn_mp_grow.c
diff --git a/bn_mp_init.c b/libtommath/bn_mp_init.c
similarity index 100%
rename from bn_mp_init.c
rename to libtommath/bn_mp_init.c
diff --git a/bn_mp_init_copy.c b/libtommath/bn_mp_init_copy.c
similarity index 100%
rename from bn_mp_init_copy.c
rename to libtommath/bn_mp_init_copy.c
diff --git a/bn_mp_init_multi.c b/libtommath/bn_mp_init_multi.c
similarity index 100%
rename from bn_mp_init_multi.c
rename to libtommath/bn_mp_init_multi.c
diff --git a/bn_mp_init_set.c b/libtommath/bn_mp_init_set.c
similarity index 100%
rename from bn_mp_init_set.c
rename to libtommath/bn_mp_init_set.c
diff --git a/bn_mp_init_set_int.c b/libtommath/bn_mp_init_set_int.c
similarity index 100%
rename from bn_mp_init_set_int.c
rename to libtommath/bn_mp_init_set_int.c
diff --git a/bn_mp_init_size.c b/libtommath/bn_mp_init_size.c
similarity index 100%
rename from bn_mp_init_size.c
rename to libtommath/bn_mp_init_size.c
diff --git a/bn_mp_invmod.c b/libtommath/bn_mp_invmod.c
similarity index 100%
rename from bn_mp_invmod.c
rename to libtommath/bn_mp_invmod.c
diff --git a/bn_mp_invmod_slow.c b/libtommath/bn_mp_invmod_slow.c
similarity index 100%
rename from bn_mp_invmod_slow.c
rename to libtommath/bn_mp_invmod_slow.c
diff --git a/bn_mp_is_square.c b/libtommath/bn_mp_is_square.c
similarity index 100%
rename from bn_mp_is_square.c
rename to libtommath/bn_mp_is_square.c
diff --git a/bn_mp_jacobi.c b/libtommath/bn_mp_jacobi.c
similarity index 100%
rename from bn_mp_jacobi.c
rename to libtommath/bn_mp_jacobi.c
diff --git a/bn_mp_karatsuba_mul.c b/libtommath/bn_mp_karatsuba_mul.c
similarity index 100%
rename from bn_mp_karatsuba_mul.c
rename to libtommath/bn_mp_karatsuba_mul.c
diff --git a/bn_mp_karatsuba_sqr.c b/libtommath/bn_mp_karatsuba_sqr.c
similarity index 100%
rename from bn_mp_karatsuba_sqr.c
rename to libtommath/bn_mp_karatsuba_sqr.c
diff --git a/bn_mp_lcm.c b/libtommath/bn_mp_lcm.c
similarity index 100%
rename from bn_mp_lcm.c
rename to libtommath/bn_mp_lcm.c
diff --git a/bn_mp_lshd.c b/libtommath/bn_mp_lshd.c
similarity index 100%
rename from bn_mp_lshd.c
rename to libtommath/bn_mp_lshd.c
diff --git a/bn_mp_mod.c b/libtommath/bn_mp_mod.c
similarity index 100%
rename from bn_mp_mod.c
rename to libtommath/bn_mp_mod.c
diff --git a/bn_mp_mod_2d.c b/libtommath/bn_mp_mod_2d.c
similarity index 100%
rename from bn_mp_mod_2d.c
rename to libtommath/bn_mp_mod_2d.c
diff --git a/bn_mp_mod_d.c b/libtommath/bn_mp_mod_d.c
similarity index 100%
rename from bn_mp_mod_d.c
rename to libtommath/bn_mp_mod_d.c
diff --git a/bn_mp_montgomery_calc_normalization.c b/libtommath/bn_mp_montgomery_calc_normalization.c
similarity index 100%
rename from bn_mp_montgomery_calc_normalization.c
rename to libtommath/bn_mp_montgomery_calc_normalization.c
diff --git a/bn_mp_montgomery_reduce.c b/libtommath/bn_mp_montgomery_reduce.c
similarity index 100%
rename from bn_mp_montgomery_reduce.c
rename to libtommath/bn_mp_montgomery_reduce.c
diff --git a/bn_mp_montgomery_setup.c b/libtommath/bn_mp_montgomery_setup.c
similarity index 100%
rename from bn_mp_montgomery_setup.c
rename to libtommath/bn_mp_montgomery_setup.c
diff --git a/bn_mp_mul.c b/libtommath/bn_mp_mul.c
similarity index 100%
rename from bn_mp_mul.c
rename to libtommath/bn_mp_mul.c
diff --git a/bn_mp_mul_2.c b/libtommath/bn_mp_mul_2.c
similarity index 100%
rename from bn_mp_mul_2.c
rename to libtommath/bn_mp_mul_2.c
diff --git a/bn_mp_mul_2d.c b/libtommath/bn_mp_mul_2d.c
similarity index 100%
rename from bn_mp_mul_2d.c
rename to libtommath/bn_mp_mul_2d.c
diff --git a/bn_mp_mul_d.c b/libtommath/bn_mp_mul_d.c
similarity index 100%
rename from bn_mp_mul_d.c
rename to libtommath/bn_mp_mul_d.c
diff --git a/bn_mp_mulmod.c b/libtommath/bn_mp_mulmod.c
similarity index 100%
rename from bn_mp_mulmod.c
rename to libtommath/bn_mp_mulmod.c
diff --git a/bn_mp_n_root.c b/libtommath/bn_mp_n_root.c
similarity index 100%
rename from bn_mp_n_root.c
rename to libtommath/bn_mp_n_root.c
diff --git a/bn_mp_neg.c b/libtommath/bn_mp_neg.c
similarity index 100%
rename from bn_mp_neg.c
rename to libtommath/bn_mp_neg.c
diff --git a/bn_mp_or.c b/libtommath/bn_mp_or.c
similarity index 100%
rename from bn_mp_or.c
rename to libtommath/bn_mp_or.c
diff --git a/bn_mp_prime_fermat.c b/libtommath/bn_mp_prime_fermat.c
similarity index 100%
rename from bn_mp_prime_fermat.c
rename to libtommath/bn_mp_prime_fermat.c
diff --git a/bn_mp_prime_is_divisible.c b/libtommath/bn_mp_prime_is_divisible.c
similarity index 100%
rename from bn_mp_prime_is_divisible.c
rename to libtommath/bn_mp_prime_is_divisible.c
diff --git a/bn_mp_prime_is_prime.c b/libtommath/bn_mp_prime_is_prime.c
similarity index 100%
rename from bn_mp_prime_is_prime.c
rename to libtommath/bn_mp_prime_is_prime.c
diff --git a/bn_mp_prime_miller_rabin.c b/libtommath/bn_mp_prime_miller_rabin.c
similarity index 100%
rename from bn_mp_prime_miller_rabin.c
rename to libtommath/bn_mp_prime_miller_rabin.c
diff --git a/bn_mp_prime_next_prime.c b/libtommath/bn_mp_prime_next_prime.c
similarity index 100%
rename from bn_mp_prime_next_prime.c
rename to libtommath/bn_mp_prime_next_prime.c
diff --git a/bn_mp_prime_rabin_miller_trials.c b/libtommath/bn_mp_prime_rabin_miller_trials.c
similarity index 100%
rename from bn_mp_prime_rabin_miller_trials.c
rename to libtommath/bn_mp_prime_rabin_miller_trials.c
diff --git a/bn_mp_prime_random_ex.c b/libtommath/bn_mp_prime_random_ex.c
similarity index 100%
rename from bn_mp_prime_random_ex.c
rename to libtommath/bn_mp_prime_random_ex.c
diff --git a/bn_mp_radix_size.c b/libtommath/bn_mp_radix_size.c
similarity index 100%
rename from bn_mp_radix_size.c
rename to libtommath/bn_mp_radix_size.c
diff --git a/bn_mp_radix_smap.c b/libtommath/bn_mp_radix_smap.c
similarity index 100%
rename from bn_mp_radix_smap.c
rename to libtommath/bn_mp_radix_smap.c
diff --git a/bn_mp_rand.c b/libtommath/bn_mp_rand.c
similarity index 100%
rename from bn_mp_rand.c
rename to libtommath/bn_mp_rand.c
diff --git a/bn_mp_read_radix.c b/libtommath/bn_mp_read_radix.c
similarity index 100%
rename from bn_mp_read_radix.c
rename to libtommath/bn_mp_read_radix.c
diff --git a/bn_mp_read_signed_bin.c b/libtommath/bn_mp_read_signed_bin.c
similarity index 100%
rename from bn_mp_read_signed_bin.c
rename to libtommath/bn_mp_read_signed_bin.c
diff --git a/bn_mp_read_unsigned_bin.c b/libtommath/bn_mp_read_unsigned_bin.c
similarity index 100%
rename from bn_mp_read_unsigned_bin.c
rename to libtommath/bn_mp_read_unsigned_bin.c
diff --git a/bn_mp_reduce.c b/libtommath/bn_mp_reduce.c
similarity index 100%
rename from bn_mp_reduce.c
rename to libtommath/bn_mp_reduce.c
diff --git a/bn_mp_reduce_2k.c b/libtommath/bn_mp_reduce_2k.c
similarity index 100%
rename from bn_mp_reduce_2k.c
rename to libtommath/bn_mp_reduce_2k.c
diff --git a/bn_mp_reduce_2k_l.c b/libtommath/bn_mp_reduce_2k_l.c
similarity index 100%
rename from bn_mp_reduce_2k_l.c
rename to libtommath/bn_mp_reduce_2k_l.c
diff --git a/bn_mp_reduce_2k_setup.c b/libtommath/bn_mp_reduce_2k_setup.c
similarity index 100%
rename from bn_mp_reduce_2k_setup.c
rename to libtommath/bn_mp_reduce_2k_setup.c
diff --git a/bn_mp_reduce_2k_setup_l.c b/libtommath/bn_mp_reduce_2k_setup_l.c
similarity index 100%
rename from bn_mp_reduce_2k_setup_l.c
rename to libtommath/bn_mp_reduce_2k_setup_l.c
diff --git a/bn_mp_reduce_is_2k.c b/libtommath/bn_mp_reduce_is_2k.c
similarity index 100%
rename from bn_mp_reduce_is_2k.c
rename to libtommath/bn_mp_reduce_is_2k.c
diff --git a/bn_mp_reduce_is_2k_l.c b/libtommath/bn_mp_reduce_is_2k_l.c
similarity index 100%
rename from bn_mp_reduce_is_2k_l.c
rename to libtommath/bn_mp_reduce_is_2k_l.c
diff --git a/bn_mp_reduce_setup.c b/libtommath/bn_mp_reduce_setup.c
similarity index 100%
rename from bn_mp_reduce_setup.c
rename to libtommath/bn_mp_reduce_setup.c
diff --git a/bn_mp_rshd.c b/libtommath/bn_mp_rshd.c
similarity index 100%
rename from bn_mp_rshd.c
rename to libtommath/bn_mp_rshd.c
diff --git a/bn_mp_set.c b/libtommath/bn_mp_set.c
similarity index 100%
rename from bn_mp_set.c
rename to libtommath/bn_mp_set.c
diff --git a/bn_mp_set_int.c b/libtommath/bn_mp_set_int.c
similarity index 100%
rename from bn_mp_set_int.c
rename to libtommath/bn_mp_set_int.c
diff --git a/bn_mp_shrink.c b/libtommath/bn_mp_shrink.c
similarity index 100%
rename from bn_mp_shrink.c
rename to libtommath/bn_mp_shrink.c
diff --git a/bn_mp_signed_bin_size.c b/libtommath/bn_mp_signed_bin_size.c
similarity index 100%
rename from bn_mp_signed_bin_size.c
rename to libtommath/bn_mp_signed_bin_size.c
diff --git a/bn_mp_sqr.c b/libtommath/bn_mp_sqr.c
similarity index 100%
rename from bn_mp_sqr.c
rename to libtommath/bn_mp_sqr.c
diff --git a/bn_mp_sqrmod.c b/libtommath/bn_mp_sqrmod.c
similarity index 100%
rename from bn_mp_sqrmod.c
rename to libtommath/bn_mp_sqrmod.c
diff --git a/bn_mp_sqrt.c b/libtommath/bn_mp_sqrt.c
similarity index 100%
rename from bn_mp_sqrt.c
rename to libtommath/bn_mp_sqrt.c
diff --git a/bn_mp_sub.c b/libtommath/bn_mp_sub.c
similarity index 100%
rename from bn_mp_sub.c
rename to libtommath/bn_mp_sub.c
diff --git a/bn_mp_sub_d.c b/libtommath/bn_mp_sub_d.c
similarity index 100%
rename from bn_mp_sub_d.c
rename to libtommath/bn_mp_sub_d.c
diff --git a/bn_mp_submod.c b/libtommath/bn_mp_submod.c
similarity index 100%
rename from bn_mp_submod.c
rename to libtommath/bn_mp_submod.c
diff --git a/bn_mp_to_signed_bin.c b/libtommath/bn_mp_to_signed_bin.c
similarity index 100%
rename from bn_mp_to_signed_bin.c
rename to libtommath/bn_mp_to_signed_bin.c
diff --git a/bn_mp_to_signed_bin_n.c b/libtommath/bn_mp_to_signed_bin_n.c
similarity index 100%
rename from bn_mp_to_signed_bin_n.c
rename to libtommath/bn_mp_to_signed_bin_n.c
diff --git a/bn_mp_to_unsigned_bin.c b/libtommath/bn_mp_to_unsigned_bin.c
similarity index 100%
rename from bn_mp_to_unsigned_bin.c
rename to libtommath/bn_mp_to_unsigned_bin.c
diff --git a/bn_mp_to_unsigned_bin_n.c b/libtommath/bn_mp_to_unsigned_bin_n.c
similarity index 100%
rename from bn_mp_to_unsigned_bin_n.c
rename to libtommath/bn_mp_to_unsigned_bin_n.c
diff --git a/bn_mp_toom_mul.c b/libtommath/bn_mp_toom_mul.c
similarity index 100%
rename from bn_mp_toom_mul.c
rename to libtommath/bn_mp_toom_mul.c
diff --git a/bn_mp_toom_sqr.c b/libtommath/bn_mp_toom_sqr.c
similarity index 100%
rename from bn_mp_toom_sqr.c
rename to libtommath/bn_mp_toom_sqr.c
diff --git a/bn_mp_toradix.c b/libtommath/bn_mp_toradix.c
similarity index 100%
rename from bn_mp_toradix.c
rename to libtommath/bn_mp_toradix.c
diff --git a/bn_mp_toradix_n.c b/libtommath/bn_mp_toradix_n.c
similarity index 100%
rename from bn_mp_toradix_n.c
rename to libtommath/bn_mp_toradix_n.c
diff --git a/bn_mp_unsigned_bin_size.c b/libtommath/bn_mp_unsigned_bin_size.c
similarity index 100%
rename from bn_mp_unsigned_bin_size.c
rename to libtommath/bn_mp_unsigned_bin_size.c
diff --git a/bn_mp_xor.c b/libtommath/bn_mp_xor.c
similarity index 100%
rename from bn_mp_xor.c
rename to libtommath/bn_mp_xor.c
diff --git a/bn_mp_zero.c b/libtommath/bn_mp_zero.c
similarity index 100%
rename from bn_mp_zero.c
rename to libtommath/bn_mp_zero.c
diff --git a/bn_prime_tab.c b/libtommath/bn_prime_tab.c
similarity index 100%
rename from bn_prime_tab.c
rename to libtommath/bn_prime_tab.c
diff --git a/bn_reverse.c b/libtommath/bn_reverse.c
similarity index 100%
rename from bn_reverse.c
rename to libtommath/bn_reverse.c
diff --git a/bn_s_mp_add.c b/libtommath/bn_s_mp_add.c
similarity index 100%
rename from bn_s_mp_add.c
rename to libtommath/bn_s_mp_add.c
diff --git a/bn_s_mp_exptmod.c b/libtommath/bn_s_mp_exptmod.c
similarity index 100%
rename from bn_s_mp_exptmod.c
rename to libtommath/bn_s_mp_exptmod.c
diff --git a/bn_s_mp_mul_digs.c b/libtommath/bn_s_mp_mul_digs.c
similarity index 100%
rename from bn_s_mp_mul_digs.c
rename to libtommath/bn_s_mp_mul_digs.c
diff --git a/bn_s_mp_mul_high_digs.c b/libtommath/bn_s_mp_mul_high_digs.c
similarity index 100%
rename from bn_s_mp_mul_high_digs.c
rename to libtommath/bn_s_mp_mul_high_digs.c
diff --git a/bn_s_mp_sqr.c b/libtommath/bn_s_mp_sqr.c
similarity index 100%
rename from bn_s_mp_sqr.c
rename to libtommath/bn_s_mp_sqr.c
diff --git a/bn_s_mp_sub.c b/libtommath/bn_s_mp_sub.c
similarity index 100%
rename from bn_s_mp_sub.c
rename to libtommath/bn_s_mp_sub.c
diff --git a/bncore.c b/libtommath/bncore.c
similarity index 100%
rename from bncore.c
rename to libtommath/bncore.c
diff --git a/booker.pl b/libtommath/booker.pl
similarity index 100%
rename from booker.pl
rename to libtommath/booker.pl
diff --git a/callgraph.txt b/libtommath/callgraph.txt
similarity index 100%
rename from callgraph.txt
rename to libtommath/callgraph.txt
diff --git a/changes.txt b/libtommath/changes.txt
similarity index 100%
rename from changes.txt
rename to libtommath/changes.txt
diff --git a/demo/demo.c b/libtommath/demo/demo.c
similarity index 100%
rename from demo/demo.c
rename to libtommath/demo/demo.c
diff --git a/demo/timing.c b/libtommath/demo/timing.c
similarity index 100%
rename from demo/timing.c
rename to libtommath/demo/timing.c
diff --git a/dep.pl b/libtommath/dep.pl
similarity index 100%
rename from dep.pl
rename to libtommath/dep.pl
diff --git a/etc/2kprime.1 b/libtommath/etc/2kprime.1
similarity index 100%
rename from etc/2kprime.1
rename to libtommath/etc/2kprime.1
diff --git a/etc/2kprime.c b/libtommath/etc/2kprime.c
similarity index 100%
rename from etc/2kprime.c
rename to libtommath/etc/2kprime.c
diff --git a/etc/drprime.c b/libtommath/etc/drprime.c
similarity index 100%
rename from etc/drprime.c
rename to libtommath/etc/drprime.c
diff --git a/etc/drprimes.28 b/libtommath/etc/drprimes.28
similarity index 100%
rename from etc/drprimes.28
rename to libtommath/etc/drprimes.28
diff --git a/etc/drprimes.txt b/libtommath/etc/drprimes.txt
similarity index 100%
rename from etc/drprimes.txt
rename to libtommath/etc/drprimes.txt
diff --git a/etc/makefile b/libtommath/etc/makefile
similarity index 100%
rename from etc/makefile
rename to libtommath/etc/makefile
diff --git a/etc/makefile.icc b/libtommath/etc/makefile.icc
similarity index 100%
rename from etc/makefile.icc
rename to libtommath/etc/makefile.icc
diff --git a/etc/makefile.msvc b/libtommath/etc/makefile.msvc
similarity index 100%
rename from etc/makefile.msvc
rename to libtommath/etc/makefile.msvc
diff --git a/etc/mersenne.c b/libtommath/etc/mersenne.c
similarity index 100%
rename from etc/mersenne.c
rename to libtommath/etc/mersenne.c
diff --git a/etc/mont.c b/libtommath/etc/mont.c
similarity index 100%
rename from etc/mont.c
rename to libtommath/etc/mont.c
diff --git a/etc/pprime.c b/libtommath/etc/pprime.c
similarity index 100%
rename from etc/pprime.c
rename to libtommath/etc/pprime.c
diff --git a/etc/prime.1024 b/libtommath/etc/prime.1024
similarity index 100%
rename from etc/prime.1024
rename to libtommath/etc/prime.1024
diff --git a/etc/prime.512 b/libtommath/etc/prime.512
similarity index 100%
rename from etc/prime.512
rename to libtommath/etc/prime.512
diff --git a/etc/timer.asm b/libtommath/etc/timer.asm
similarity index 100%
rename from etc/timer.asm
rename to libtommath/etc/timer.asm
diff --git a/etc/tune.c b/libtommath/etc/tune.c
similarity index 100%
rename from etc/tune.c
rename to libtommath/etc/tune.c
diff --git a/gen.pl b/libtommath/gen.pl
similarity index 100%
rename from gen.pl
rename to libtommath/gen.pl
diff --git a/logs/README b/libtommath/logs/README
similarity index 100%
rename from logs/README
rename to libtommath/logs/README
diff --git a/logs/add.log b/libtommath/logs/add.log
similarity index 100%
rename from logs/add.log
rename to libtommath/logs/add.log
diff --git a/logs/addsub.png b/libtommath/logs/addsub.png
similarity index 100%
rename from logs/addsub.png
rename to libtommath/logs/addsub.png
diff --git a/logs/expt.log b/libtommath/logs/expt.log
similarity index 100%
rename from logs/expt.log
rename to libtommath/logs/expt.log
diff --git a/logs/expt.png b/libtommath/logs/expt.png
similarity index 100%
rename from logs/expt.png
rename to libtommath/logs/expt.png
diff --git a/logs/expt_2k.log b/libtommath/logs/expt_2k.log
similarity index 100%
rename from logs/expt_2k.log
rename to libtommath/logs/expt_2k.log
diff --git a/logs/expt_2kl.log b/libtommath/logs/expt_2kl.log
similarity index 100%
rename from logs/expt_2kl.log
rename to libtommath/logs/expt_2kl.log
diff --git a/logs/expt_dr.log b/libtommath/logs/expt_dr.log
similarity index 100%
rename from logs/expt_dr.log
rename to libtommath/logs/expt_dr.log
diff --git a/logs/graphs.dem b/libtommath/logs/graphs.dem
similarity index 100%
rename from logs/graphs.dem
rename to libtommath/logs/graphs.dem
diff --git a/logs/index.html b/libtommath/logs/index.html
similarity index 100%
rename from logs/index.html
rename to libtommath/logs/index.html
diff --git a/poster.out b/libtommath/logs/invmod.log
similarity index 100%
rename from poster.out
rename to libtommath/logs/invmod.log
diff --git a/logs/invmod.png b/libtommath/logs/invmod.png
similarity index 100%
rename from logs/invmod.png
rename to libtommath/logs/invmod.png
diff --git a/logs/mult.log b/libtommath/logs/mult.log
similarity index 100%
rename from logs/mult.log
rename to libtommath/logs/mult.log
diff --git a/logs/mult.png b/libtommath/logs/mult.png
similarity index 100%
rename from logs/mult.png
rename to libtommath/logs/mult.png
diff --git a/logs/mult_kara.log b/libtommath/logs/mult_kara.log
similarity index 100%
rename from logs/mult_kara.log
rename to libtommath/logs/mult_kara.log
diff --git a/logs/sqr.log b/libtommath/logs/sqr.log
similarity index 100%
rename from logs/sqr.log
rename to libtommath/logs/sqr.log
diff --git a/logs/sqr.old b/libtommath/logs/sqr.old
similarity index 100%
rename from logs/sqr.old
rename to libtommath/logs/sqr.old
diff --git a/logs/sqr_kara.log b/libtommath/logs/sqr_kara.log
similarity index 100%
rename from logs/sqr_kara.log
rename to libtommath/logs/sqr_kara.log
diff --git a/logs/sub.log b/libtommath/logs/sub.log
similarity index 100%
rename from logs/sub.log
rename to libtommath/logs/sub.log
diff --git a/makefile.bcc b/libtommath/makefile.bcc
similarity index 100%
rename from makefile.bcc
rename to libtommath/makefile.bcc
diff --git a/makefile.cygwin_dll b/libtommath/makefile.cygwin_dll
similarity index 100%
rename from makefile.cygwin_dll
rename to libtommath/makefile.cygwin_dll
diff --git a/makefile.icc b/libtommath/makefile.icc
similarity index 100%
rename from makefile.icc
rename to libtommath/makefile.icc
diff --git a/makefile.msvc b/libtommath/makefile.msvc
similarity index 100%
rename from makefile.msvc
rename to libtommath/makefile.msvc
diff --git a/makefile.shared b/libtommath/makefile.shared
similarity index 100%
rename from makefile.shared
rename to libtommath/makefile.shared
diff --git a/mtest/logtab.h b/libtommath/mtest/logtab.h
similarity index 100%
rename from mtest/logtab.h
rename to libtommath/mtest/logtab.h
diff --git a/mtest/mpi-config.h b/libtommath/mtest/mpi-config.h
similarity index 100%
rename from mtest/mpi-config.h
rename to libtommath/mtest/mpi-config.h
diff --git a/mtest/mpi-types.h b/libtommath/mtest/mpi-types.h
similarity index 100%
rename from mtest/mpi-types.h
rename to libtommath/mtest/mpi-types.h
diff --git a/mtest/mpi.c b/libtommath/mtest/mpi.c
similarity index 100%
rename from mtest/mpi.c
rename to libtommath/mtest/mpi.c
diff --git a/mtest/mpi.h b/libtommath/mtest/mpi.h
similarity index 100%
rename from mtest/mpi.h
rename to libtommath/mtest/mpi.h
diff --git a/mtest/mtest.c b/libtommath/mtest/mtest.c
similarity index 100%
rename from mtest/mtest.c
rename to libtommath/mtest/mtest.c
diff --git a/pics/design_process.sxd b/libtommath/pics/design_process.sxd
similarity index 100%
rename from pics/design_process.sxd
rename to libtommath/pics/design_process.sxd
diff --git a/pics/design_process.tif b/libtommath/pics/design_process.tif
similarity index 100%
rename from pics/design_process.tif
rename to libtommath/pics/design_process.tif
diff --git a/pics/expt_state.sxd b/libtommath/pics/expt_state.sxd
similarity index 100%
rename from pics/expt_state.sxd
rename to libtommath/pics/expt_state.sxd
diff --git a/pics/expt_state.tif b/libtommath/pics/expt_state.tif
similarity index 100%
rename from pics/expt_state.tif
rename to libtommath/pics/expt_state.tif
diff --git a/pics/makefile b/libtommath/pics/makefile
similarity index 100%
rename from pics/makefile
rename to libtommath/pics/makefile
diff --git a/pics/primality.tif b/libtommath/pics/primality.tif
similarity index 100%
rename from pics/primality.tif
rename to libtommath/pics/primality.tif
diff --git a/pics/radix.sxd b/libtommath/pics/radix.sxd
similarity index 100%
rename from pics/radix.sxd
rename to libtommath/pics/radix.sxd
diff --git a/pics/sliding_window.sxd b/libtommath/pics/sliding_window.sxd
similarity index 100%
rename from pics/sliding_window.sxd
rename to libtommath/pics/sliding_window.sxd
diff --git a/pics/sliding_window.tif b/libtommath/pics/sliding_window.tif
similarity index 100%
rename from pics/sliding_window.tif
rename to libtommath/pics/sliding_window.tif
diff --git a/libtommath/poster.out b/libtommath/poster.out
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/poster.tex b/libtommath/poster.tex
similarity index 100%
rename from poster.tex
rename to libtommath/poster.tex
diff --git a/pre_gen/mpi.c b/libtommath/pre_gen/mpi.c
similarity index 100%
rename from pre_gen/mpi.c
rename to libtommath/pre_gen/mpi.c
diff --git a/pretty.build b/libtommath/pretty.build
similarity index 100%
rename from pretty.build
rename to libtommath/pretty.build
diff --git a/tombc/grammar.txt b/libtommath/tombc/grammar.txt
similarity index 100%
rename from tombc/grammar.txt
rename to libtommath/tombc/grammar.txt
diff --git a/tommath.h b/libtommath/tommath.h
similarity index 100%
rename from tommath.h
rename to libtommath/tommath.h
diff --git a/tommath.out b/libtommath/tommath.out
similarity index 100%
rename from tommath.out
rename to libtommath/tommath.out
diff --git a/tommath.src b/libtommath/tommath.src
similarity index 100%
rename from tommath.src
rename to libtommath/tommath.src
diff --git a/tommath.tex b/libtommath/tommath.tex
similarity index 100%
rename from tommath.tex
rename to libtommath/tommath.tex
diff --git a/tommath_class.h b/libtommath/tommath_class.h
similarity index 100%
rename from tommath_class.h
rename to libtommath/tommath_class.h
diff --git a/tommath_superclass.h b/libtommath/tommath_superclass.h
similarity index 100%
rename from tommath_superclass.h
rename to libtommath/tommath_superclass.h
diff --git a/listener.c b/listener.c
new file mode 100644
index 0000000000000000000000000000000000000000..dd90c6bfe73aa6669f7c03b8afdad0ae9bfd81f8
--- /dev/null
+++ b/listener.c
@@ -0,0 +1,165 @@
+/*
+ * 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 "listener.h"
+#include "session.h"
+#include "dbutil.h"
+
+void listeners_initialise() {
+
+	/* just one slot to start with */
+	ses.listeners = (struct Listener**)m_malloc(sizeof(struct Listener*));
+	ses.listensize = 1;
+	ses.listeners[0] = NULL;
+
+}
+
+void set_listener_fds(fd_set * readfds) {
+
+	unsigned int i, j;
+	struct Listener *listener;
+
+	/* check each in turn */
+	for (i = 0; i < ses.listensize; i++) {
+		listener = ses.listeners[i];
+		if (listener != NULL) {
+			for (j = 0; j < listener->nsocks; j++) {
+				FD_SET(listener->socks[j], readfds);
+			}
+		}
+	}
+}
+
+
+void handle_listeners(fd_set * readfds) {
+
+	unsigned int i, j;
+	struct Listener *listener;
+	int sock;
+
+	/* check each in turn */
+	for (i = 0; i < ses.listensize; i++) {
+		listener = ses.listeners[i];
+		if (listener != NULL) {
+			for (j = 0; j < listener->nsocks; j++) {
+				sock = listener->socks[j];
+				if (FD_ISSET(sock, readfds)) {
+					listener->acceptor(listener, sock);
+				}
+			}
+		}
+	}
+} /* Woo brace matching */
+
+
+/* acceptor(int fd, void* typedata) is a function to accept connections, 
+ * cleanup(void* typedata) happens when cleaning up */
+struct Listener* new_listener(int socks[], unsigned int nsocks,
+		int type, void* typedata, 
+		void (*acceptor)(struct Listener* listener, int sock), 
+		void (*cleanup)(struct Listener*)) {
+
+	unsigned int i, j;
+	struct Listener *newlisten = NULL;
+	/* try get a new structure to hold it */
+	for (i = 0; i < ses.listensize; i++) {
+		if (ses.listeners[i] == NULL) {
+			break;
+		}
+	}
+
+	/* or create a new one */
+	if (i == ses.listensize) {
+		if (ses.listensize > MAX_LISTENERS) {
+			TRACE(("leave newlistener: too many already"))
+			for (j = 0; j < nsocks; j++) {
+				close(socks[i]);
+			}
+			return NULL;
+		}
+		
+		ses.listeners = (struct Listener**)m_realloc(ses.listeners,
+				(ses.listensize+LISTENER_EXTEND_SIZE)
+				*sizeof(struct Listener*));
+
+		ses.listensize += LISTENER_EXTEND_SIZE;
+
+		for (j = i; j < ses.listensize; j++) {
+			ses.listeners[j] = NULL;
+		}
+	}
+
+	for (j = 0; j < nsocks; j++) {
+		ses.maxfd = MAX(ses.maxfd, socks[j]);
+	}
+
+	TRACE(("new listener num %d ", i))
+
+	newlisten = (struct Listener*)m_malloc(sizeof(struct Listener));
+	newlisten->index = i;
+	newlisten->type = type;
+	newlisten->typedata = typedata;
+	newlisten->nsocks = nsocks;
+	memcpy(newlisten->socks, socks, nsocks * sizeof(int));
+	newlisten->acceptor = acceptor;
+	newlisten->cleanup = cleanup;
+
+	ses.listeners[i] = newlisten;
+	return newlisten;
+}
+
+/* Return the first listener which matches the type-specific comparison
+ * function. Particularly needed for global requests, like tcp */
+struct Listener * get_listener(int type, void* typedata,
+		int (*match)(void*, void*)) {
+
+	unsigned int i;
+	struct Listener* listener;
+
+	for (i = 0, listener = ses.listeners[i]; i < ses.listensize; i++) {
+		if (listener->type == type
+				&& match(typedata, listener->typedata)) {
+			return listener;
+		}
+	}
+
+	return NULL;
+}
+
+void remove_listener(struct Listener* listener) {
+
+	unsigned int j;
+
+	if (listener->cleanup) {
+		listener->cleanup(listener);
+	}
+
+	for (j = 0; j < listener->nsocks; j++) {
+		close(listener->socks[j]);
+	}
+	ses.listeners[listener->index] = NULL;
+	m_free(listener);
+
+}
diff --git a/listener.h b/listener.h
new file mode 100644
index 0000000000000000000000000000000000000000..5092efd9a24ff809d69f892c88a3cbdb3a650405
--- /dev/null
+++ b/listener.h
@@ -0,0 +1,63 @@
+/*
+ * 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. */
+
+#ifndef _LISTENER_H
+#define _LISTENER_H
+
+#define MAX_LISTENERS 20
+#define LISTENER_EXTEND_SIZE 1
+
+struct Listener {
+
+	int socks[DROPBEAR_MAX_SOCKS];
+	unsigned int nsocks;
+
+	int index; /* index in the array of listeners */
+
+	void (*acceptor)(struct Listener*, int sock);
+	void (*cleanup)(struct Listener*);
+
+	int type; /* CHANNEL_ID_X11, CHANNEL_ID_AGENT, 
+				 CHANNEL_ID_TCPDIRECT (for clients),
+				 CHANNEL_ID_TCPFORWARDED (for servers) */
+
+	void *typedata;
+
+};
+
+void listeners_initialise();
+void handle_listeners(fd_set * readfds);
+void set_listener_fds(fd_set * readfds);
+
+struct Listener* new_listener(int socks[], unsigned int nsocks, 
+		int type, void* typedata, 
+		void (*acceptor)(struct Listener* listener, int sock), 
+		void (*cleanup)(struct Listener*));
+
+struct Listener * get_listener(int type, void* typedata,
+		int (*match)(void*, void*));
+
+void remove_listener(struct Listener* listener);
+
+#endif /* _LISTENER_H */
diff --git a/loginrec.c b/loginrec.c
new file mode 100644
index 0000000000000000000000000000000000000000..f084566ee88a2e4cb31d5d4f8ac94d8c771a7a0e
--- /dev/null
+++ b/loginrec.c
@@ -0,0 +1,1394 @@
+/*
+ * Copyright (c) 2000 Andre Lucas.  All rights reserved.
+ * Portions copyright (c) 1998 Todd C. Miller
+ * Portions copyright (c) 1996 Jason Downs
+ * Portions copyright (c) 1996 Theo de Raadt
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+
+/**
+ ** loginrec.c:  platform-independent login recording and lastlog retrieval
+ **/
+
+/* For now lastlog code has been removed as it wasn't being used by Dropbear. */
+
+/*
+  The new login code explained
+  ============================
+
+  This code attempts to provide a common interface to login recording
+  (utmp and friends) and last login time retrieval.
+
+  Its primary means of achieving this is to use 'struct logininfo', a
+  union of all the useful fields in the various different types of
+  system login record structures one finds on UNIX variants.
+
+  We depend on autoconf to define which recording methods are to be
+  used, and which fields are contained in the relevant data structures
+  on the local system. Many C preprocessor symbols affect which code
+  gets compiled here.
+
+  The code is designed to make it easy to modify a particular
+  recording method, without affecting other methods nor requiring so
+  many nested conditional compilation blocks as were commonplace in
+  the old code.
+
+  For login recording, we try to use the local system's libraries as
+  these are clearly most likely to work correctly. For utmp systems
+  this usually means login() and logout() or setutent() etc., probably
+  in libutil, along with logwtmp() etc. On these systems, we fall back
+  to writing the files directly if we have to, though this method
+  requires very thorough testing so we do not corrupt local auditing
+  information. These files and their access methods are very system
+  specific indeed.
+
+  For utmpx systems, the corresponding library functions are
+  setutxent() etc. To the author's knowledge, all utmpx systems have
+  these library functions and so no direct write is attempted. If such
+  a system exists and needs support, direct analogues of the [uw]tmp
+  code should suffice.
+
+  Retrieving the time of last login ('lastlog') is in some ways even
+  more problemmatic than login recording. Some systems provide a
+  simple table of all users which we seek based on uid and retrieve a
+  relatively standard structure. Others record the same information in
+  a directory with a separate file, and others don't record the
+  information separately at all. For systems in the latter category,
+  we look backwards in the wtmp or wtmpx file for the last login entry
+  for our user. Naturally this is slower and on busy systems could
+  incur a significant performance penalty.
+
+  Calling the new code
+  --------------------
+
+  In OpenSSH all login recording and retrieval is performed in
+  login.c. Here you'll find working examples. Also, in the logintest.c
+  program there are more examples.
+
+  Internal handler calling method
+  -------------------------------
+
+  When a call is made to login_login() or login_logout(), both
+  routines set a struct logininfo flag defining which action (log in,
+  or log out) is to be taken. They both then call login_write(), which
+  calls whichever of the many structure-specific handlers autoconf
+  selects for the local system.
+
+  The handlers themselves handle system data structure specifics. Both
+  struct utmp and struct utmpx have utility functions (see
+  construct_utmp*()) to try to make it simpler to add extra systems
+  that introduce new features to either structure.
+
+  While it may seem terribly wasteful to replicate so much similar
+  code for each method, experience has shown that maintaining code to
+  write both struct utmp and utmpx in one function, whilst maintaining
+  support for all systems whether they have library support or not, is
+  a difficult and time-consuming task.
+
+  Lastlog support proceeds similarly. Functions login_get_lastlog()
+  (and its OpenSSH-tuned friend login_get_lastlog_time()) call
+  getlast_entry(), which tries one of three methods to find the last
+  login time. It uses local system lastlog support if it can,
+  otherwise it tries wtmp or wtmpx before giving up and returning 0,
+  meaning "tilt".
+
+  Maintenance
+  -----------
+
+  In many cases it's possible to tweak autoconf to select the correct
+  methods for a particular platform, either by improving the detection
+  code (best), or by presetting DISABLE_<method> or CONF_<method>_FILE
+  symbols for the platform.
+
+  Use logintest to check which symbols are defined before modifying
+  configure.ac and loginrec.c. (You have to build logintest yourself
+  with 'make logintest' as it's not built by default.)
+
+  Otherwise, patches to the specific method(s) are very helpful!
+
+*/
+
+/**
+ ** TODO:
+ **   homegrown ttyslot()
+ **   test, test, test
+ **
+ ** Platform status:
+ ** ----------------
+ **
+ ** Known good:
+ **   Linux (Redhat 6.2, Debian)
+ **   Solaris
+ **   HP-UX 10.20 (gcc only)
+ **   IRIX
+ **   NeXT - M68k/HPPA/Sparc (4.2/3.3)
+ **
+ ** Testing required: Please send reports!
+ **   NetBSD
+ **   HP-UX 11
+ **   AIX
+ **
+ ** Platforms with known problems:
+ **   Some variants of Slackware Linux
+ **
+ **/
+
+
+#include "includes.h"
+#include "loginrec.h"
+#include "dbutil.h"
+#include "atomicio.h"
+
+/**
+ ** prototypes for helper functions in this file
+ **/
+
+#if HAVE_UTMP_H
+void set_utmp_time(struct logininfo *li, struct utmp *ut);
+void construct_utmp(struct logininfo *li, struct utmp *ut);
+#endif
+
+#ifdef HAVE_UTMPX_H
+void set_utmpx_time(struct logininfo *li, struct utmpx *ut);
+void construct_utmpx(struct logininfo *li, struct utmpx *ut);
+#endif
+
+int utmp_write_entry(struct logininfo *li);
+int utmpx_write_entry(struct logininfo *li);
+int wtmp_write_entry(struct logininfo *li);
+int wtmpx_write_entry(struct logininfo *li);
+int lastlog_write_entry(struct logininfo *li);
+int syslogin_write_entry(struct logininfo *li);
+
+int wtmp_get_entry(struct logininfo *li);
+int wtmpx_get_entry(struct logininfo *li);
+
+/* pick the shortest string */
+#define MIN_SIZEOF(s1,s2) ( sizeof(s1) < sizeof(s2) ? sizeof(s1) : sizeof(s2) )
+
+/**
+ ** platform-independent login functions
+ **/
+
+/* login_login(struct logininfo *)     -Record a login
+ *
+ * Call with a pointer to a struct logininfo initialised with
+ * login_init_entry() or login_alloc_entry()
+ *
+ * Returns:
+ *  >0 if successful
+ *  0  on failure (will use OpenSSH's logging facilities for diagnostics)
+ */
+int
+login_login (struct logininfo *li)
+{
+	li->type = LTYPE_LOGIN;
+	return login_write(li);
+}
+
+
+/* login_logout(struct logininfo *)     - Record a logout
+ *
+ * Call as with login_login()
+ *
+ * Returns:
+ *  >0 if successful
+ *  0  on failure (will use OpenSSH's logging facilities for diagnostics)
+ */
+int
+login_logout(struct logininfo *li)
+{
+	li->type = LTYPE_LOGOUT;
+	return login_write(li);
+}
+
+
+/* login_alloc_entry(int, char*, char*, char*)    - Allocate and initialise
+ *                                                  a logininfo structure
+ *
+ * This function creates a new struct logininfo, a data structure
+ * meant to carry the information required to portably record login info.
+ *
+ * Returns a pointer to a newly created struct logininfo. If memory
+ * allocation fails, the program halts.
+ */
+struct
+logininfo *login_alloc_entry(int pid, const char *username,
+			     const char *hostname, const char *line)
+{
+	struct logininfo *newli;
+
+	newli = (struct logininfo *) m_malloc (sizeof(*newli));
+	(void)login_init_entry(newli, pid, username, hostname, line);
+	return newli;
+}
+
+
+/* login_free_entry(struct logininfo *)    - free struct memory */
+void
+login_free_entry(struct logininfo *li)
+{
+	m_free(li);
+}
+
+
+/* login_init_entry(struct logininfo *, int, char*, char*, char*)
+ *                                        - initialise a struct logininfo
+ *
+ * Populates a new struct logininfo, a data structure meant to carry
+ * the information required to portably record login info.
+ *
+ * Returns: 1
+ */
+int
+login_init_entry(struct logininfo *li, int pid, const char *username,
+		 const char *hostname, const char *line)
+{
+	struct passwd *pw;
+
+	memset(li, 0, sizeof(*li));
+
+	li->pid = pid;
+
+	/* set the line information */
+	if (line)
+		line_fullname(li->line, line, sizeof(li->line));
+
+	if (username) {
+		strlcpy(li->username, username, sizeof(li->username));
+		pw = getpwnam(li->username);
+		if (pw == NULL)
+			dropbear_exit("login_init_entry: Cannot find user \"%s\"",
+					li->username);
+		li->uid = pw->pw_uid;
+	}
+
+	if (hostname)
+		strlcpy(li->hostname, hostname, sizeof(li->hostname));
+
+	return 1;
+}
+
+/* login_set_current_time(struct logininfo *)    - set the current time
+ *
+ * Set the current time in a logininfo structure. This function is
+ * meant to eliminate the need to deal with system dependencies for
+ * time handling.
+ */
+void
+login_set_current_time(struct logininfo *li)
+{
+	struct timeval tv;
+
+	gettimeofday(&tv, NULL);
+
+	li->tv_sec = tv.tv_sec;
+	li->tv_usec = tv.tv_usec;
+}
+
+/* copy a sockaddr_* into our logininfo */
+void
+login_set_addr(struct logininfo *li, const struct sockaddr *sa,
+	       const unsigned int sa_size)
+{
+	unsigned int bufsize = sa_size;
+
+	/* make sure we don't overrun our union */
+	if (sizeof(li->hostaddr) < sa_size)
+		bufsize = sizeof(li->hostaddr);
+
+	memcpy((void *)&(li->hostaddr.sa), (const void *)sa, bufsize);
+}
+
+
+/**
+ ** login_write: Call low-level recording functions based on autoconf
+ ** results
+ **/
+int
+login_write (struct logininfo *li)
+{
+#ifndef HAVE_CYGWIN
+	if ((int)geteuid() != 0) {
+	  dropbear_log(LOG_WARNING,
+			  "Attempt to write login records by non-root user (aborting)");
+	  return 1;
+	}
+#endif
+
+	/* set the timestamp */
+	login_set_current_time(li);
+#ifdef USE_LOGIN
+	syslogin_write_entry(li);
+#endif
+#ifdef USE_LASTLOG
+	if (li->type == LTYPE_LOGIN) {
+		lastlog_write_entry(li);
+	}
+#endif
+#ifdef USE_UTMP
+	utmp_write_entry(li);
+#endif
+#ifdef USE_WTMP
+	wtmp_write_entry(li);
+#endif
+#ifdef USE_UTMPX
+	utmpx_write_entry(li);
+#endif
+#ifdef USE_WTMPX
+	wtmpx_write_entry(li);
+#endif
+	return 0;
+}
+
+#ifdef LOGIN_NEEDS_UTMPX
+int
+login_utmp_only(struct logininfo *li)
+{
+	li->type = LTYPE_LOGIN; 
+	login_set_current_time(li);
+# ifdef USE_UTMP
+	utmp_write_entry(li);
+# endif
+# ifdef USE_WTMP
+	wtmp_write_entry(li);
+# endif
+# ifdef USE_UTMPX
+	utmpx_write_entry(li);
+# endif
+# ifdef USE_WTMPX
+	wtmpx_write_entry(li);
+# endif
+	return 0;
+}
+#endif
+
+
+
+/*
+ * 'line' string utility functions
+ *
+ * These functions process the 'line' string into one of three forms:
+ *
+ * 1. The full filename (including '/dev')
+ * 2. The stripped name (excluding '/dev')
+ * 3. The abbreviated name (e.g. /dev/ttyp00 -> yp00
+ *                               /dev/pts/1  -> ts/1 )
+ *
+ * Form 3 is used on some systems to identify a .tmp.? entry when
+ * attempting to remove it. Typically both addition and removal is
+ * performed by one application - say, sshd - so as long as the choice
+ * uniquely identifies a terminal it's ok.
+ */
+
+
+/* line_fullname(): add the leading '/dev/' if it doesn't exist make
+ * sure dst has enough space, if not just copy src (ugh) */
+char *
+line_fullname(char *dst, const char *src, size_t dstsize)
+{
+	memset(dst, '\0', dstsize);
+	if ((strncmp(src, "/dev/", 5) == 0) || (dstsize < (strlen(src) + 5))) {
+		strlcpy(dst, src, dstsize);
+	} else {
+		strlcpy(dst, "/dev/", dstsize);
+		strlcat(dst, src, dstsize);
+	}
+	return dst;
+}
+
+/* line_stripname(): strip the leading '/dev' if it exists, return dst */
+char *
+line_stripname(char *dst, const char *src, size_t dstsize)
+{
+	memset(dst, '\0', dstsize);
+	if (strncmp(src, "/dev/", 5) == 0)
+		strlcpy(dst, src + 5, dstsize);
+	else
+		strlcpy(dst, src, dstsize);
+	return dst;
+}
+
+/* line_abbrevname(): Return the abbreviated (usually four-character)
+ * form of the line (Just use the last <dstsize> characters of the
+ * full name.)
+ *
+ * NOTE: use strncpy because we do NOT necessarily want zero
+ * termination */
+char *
+line_abbrevname(char *dst, const char *src, size_t dstsize)
+{
+	size_t len;
+
+	memset(dst, '\0', dstsize);
+
+	/* Always skip prefix if present */
+	if (strncmp(src, "/dev/", 5) == 0)
+		src += 5;
+
+#ifdef WITH_ABBREV_NO_TTY
+	if (strncmp(src, "tty", 3) == 0)
+		src += 3;
+#endif
+
+	len = strlen(src);
+
+	if (len > 0) {
+		if (((int)len - dstsize) > 0)
+			src +=  ((int)len - dstsize);
+
+		/* note: _don't_ change this to strlcpy */
+		strncpy(dst, src, (size_t)dstsize);
+	}
+
+	return dst;
+}
+
+/**
+ ** utmp utility functions
+ **
+ ** These functions manipulate struct utmp, taking system differences
+ ** into account.
+ **/
+
+#if defined(USE_UTMP) || defined (USE_WTMP) || defined (USE_LOGIN)
+
+/* build the utmp structure */
+void
+set_utmp_time(struct logininfo *li, struct utmp *ut)
+{
+# ifdef HAVE_STRUCT_UTMP_UT_TV
+	ut->ut_tv.tv_sec = li->tv_sec;
+	ut->ut_tv.tv_usec = li->tv_usec;
+# else
+#  ifdef HAVE_STRUCT_UTMP_UT_TIME
+	ut->ut_time = li->tv_sec;
+#  endif
+# endif
+}
+
+void
+construct_utmp(struct logininfo *li,
+		    struct utmp *ut)
+{
+# ifdef HAVE_ADDR_V6_IN_UTMP
+	struct sockaddr_in6 *sa6;
+#  endif
+	memset(ut, '\0', sizeof(*ut));
+
+	/* First fill out fields used for both logins and logouts */
+
+# ifdef HAVE_STRUCT_UTMP_UT_ID
+	line_abbrevname(ut->ut_id, li->line, sizeof(ut->ut_id));
+# endif
+
+# ifdef HAVE_STRUCT_UTMP_UT_TYPE
+	/* This is done here to keep utmp constants out of struct logininfo */
+	switch (li->type) {
+	case LTYPE_LOGIN:
+		ut->ut_type = USER_PROCESS;
+#ifdef _UNICOS
+		cray_set_tmpdir(ut);
+#endif
+		break;
+	case LTYPE_LOGOUT:
+		ut->ut_type = DEAD_PROCESS;
+#ifdef _UNICOS
+		cray_retain_utmp(ut, li->pid);
+#endif
+		break;
+	}
+# endif
+	set_utmp_time(li, ut);
+
+	line_stripname(ut->ut_line, li->line, sizeof(ut->ut_line));
+
+# ifdef HAVE_STRUCT_UTMP_UT_PID
+	ut->ut_pid = li->pid;
+# endif
+
+	/* If we're logging out, leave all other fields blank */
+	if (li->type == LTYPE_LOGOUT)
+	  return;
+
+	/*
+	 * These fields are only used when logging in, and are blank
+	 * for logouts.
+	 */
+
+	/* Use strncpy because we don't necessarily want null termination */
+	strncpy(ut->ut_name, li->username, MIN_SIZEOF(ut->ut_name, li->username));
+# ifdef HAVE_STRUCT_UTMP_UT_HOST
+	strncpy(ut->ut_host, li->hostname, MIN_SIZEOF(ut->ut_host, li->hostname));
+# endif
+# ifdef HAVE_STRUCT_UTMP_UT_ADDR
+	/* this is just a 32-bit IP address */
+	if (li->hostaddr.sa.sa_family == AF_INET)
+		ut->ut_addr = li->hostaddr.sa_in.sin_addr.s_addr;
+# endif
+# ifdef HAVE_ADDR_V6_IN_UTMP
+	/* this is just a 128-bit IPv6 address */
+	if (li->hostaddr.sa.sa_family == AF_INET6) {
+		sa6 = ((struct sockaddr_in6 *)&li->hostaddr.sa);
+		memcpy(ut->ut_addr_v6, sa6->sin6_addr.s6_addr, 16);
+		if (IN6_IS_ADDR_V4MAPPED(&sa6->sin6_addr)) {
+			ut->ut_addr_v6[0] = ut->ut_addr_v6[3];
+			ut->ut_addr_v6[1] = 0;
+			ut->ut_addr_v6[2] = 0;
+			ut->ut_addr_v6[3] = 0;
+		}
+	}
+# endif
+}
+#endif /* USE_UTMP || USE_WTMP || USE_LOGIN */
+
+/**
+ ** utmpx utility functions
+ **
+ ** These functions manipulate struct utmpx, accounting for system
+ ** variations.
+ **/
+
+#if defined(USE_UTMPX) || defined (USE_WTMPX)
+/* build the utmpx structure */
+void
+set_utmpx_time(struct logininfo *li, struct utmpx *utx)
+{
+# ifdef HAVE_STRUCT_UTMPX_UT_TV
+	utx->ut_tv.tv_sec = li->tv_sec;
+	utx->ut_tv.tv_usec = li->tv_usec;
+# else /* HAVE_STRUCT_UTMPX_UT_TV */
+#  ifdef HAVE_STRUCT_UTMPX_UT_TIME
+	utx->ut_time = li->tv_sec;
+#  endif /* HAVE_STRUCT_UTMPX_UT_TIME */
+# endif /* HAVE_STRUCT_UTMPX_UT_TV */
+}
+
+void
+construct_utmpx(struct logininfo *li, struct utmpx *utx)
+{
+# ifdef HAVE_ADDR_V6_IN_UTMP
+	struct sockaddr_in6 *sa6;
+#  endif
+	memset(utx, '\0', sizeof(*utx));
+# ifdef HAVE_STRUCT_UTMPX_UT_ID
+	line_abbrevname(utx->ut_id, li->line, sizeof(utx->ut_id));
+# endif
+
+	/* this is done here to keep utmp constants out of loginrec.h */
+	switch (li->type) {
+	case LTYPE_LOGIN:
+		utx->ut_type = USER_PROCESS;
+		break;
+	case LTYPE_LOGOUT:
+		utx->ut_type = DEAD_PROCESS;
+		break;
+	}
+	line_stripname(utx->ut_line, li->line, sizeof(utx->ut_line));
+	set_utmpx_time(li, utx);
+	utx->ut_pid = li->pid;
+	/* strncpy(): Don't necessarily want null termination */
+	strncpy(utx->ut_name, li->username, MIN_SIZEOF(utx->ut_name, li->username));
+
+	if (li->type == LTYPE_LOGOUT)
+		return;
+
+	/*
+	 * These fields are only used when logging in, and are blank
+	 * for logouts.
+	 */
+
+# ifdef HAVE_STRUCT_UTMPX_UT_HOST
+	strncpy(utx->ut_host, li->hostname, MIN_SIZEOF(utx->ut_host, li->hostname));
+# endif
+# ifdef HAVE_STRUCT_UTMPX_UT_ADDR
+	/* this is just a 32-bit IP address */
+	if (li->hostaddr.sa.sa_family == AF_INET)
+		utx->ut_addr = li->hostaddr.sa_in.sin_addr.s_addr;
+# endif
+# ifdef HAVE_ADDR_V6_IN_UTMP
+	/* this is just a 128-bit IPv6 address */
+	if (li->hostaddr.sa.sa_family == AF_INET6) {
+		sa6 = ((struct sockaddr_in6 *)&li->hostaddr.sa);
+		memcpy(ut->ut_addr_v6, sa6->sin6_addr.s6_addr, 16);
+		if (IN6_IS_ADDR_V4MAPPED(&sa6->sin6_addr)) {
+			ut->ut_addr_v6[0] = ut->ut_addr_v6[3];
+			ut->ut_addr_v6[1] = 0;
+			ut->ut_addr_v6[2] = 0;
+			ut->ut_addr_v6[3] = 0;
+		}
+	}
+# endif
+# ifdef HAVE_STRUCT_UTMPX_UT_SYSLEN
+	/* ut_syslen is the length of the utx_host string */
+	utx->ut_syslen = MIN(strlen(li->hostname), sizeof(utx->ut_host));
+# endif
+}
+#endif /* USE_UTMPX || USE_WTMPX */
+
+/**
+ ** Low-level utmp functions
+ **/
+
+/* FIXME: (ATL) utmp_write_direct needs testing */
+#ifdef USE_UTMP
+
+/* if we can, use pututline() etc. */
+# if !defined(DISABLE_PUTUTLINE) && defined(HAVE_SETUTENT) && \
+	defined(HAVE_PUTUTLINE)
+#  define UTMP_USE_LIBRARY
+# endif
+
+
+/* write a utmp entry with the system's help (pututline() and pals) */
+# ifdef UTMP_USE_LIBRARY
+static int
+utmp_write_library(struct logininfo *li, struct utmp *ut)
+{
+	setutent();
+	pututline(ut);
+
+#  ifdef HAVE_ENDUTENT
+	endutent();
+#  endif
+	return 1;
+}
+# else /* UTMP_USE_LIBRARY */
+
+/* write a utmp entry direct to the file */
+/* This is a slightly modification of code in OpenBSD's login.c */
+static int
+utmp_write_direct(struct logininfo *li, struct utmp *ut)
+{
+	struct utmp old_ut;
+	register int fd;
+	int tty;
+
+	/* FIXME: (ATL) ttyslot() needs local implementation */
+
+#if defined(HAVE_GETTTYENT)
+	register struct ttyent *ty;
+
+	tty=0;
+
+	setttyent();
+	while ((struct ttyent *)0 != (ty = getttyent())) {
+		tty++;
+		if (!strncmp(ty->ty_name, ut->ut_line, sizeof(ut->ut_line)))
+			break;
+	}
+	endttyent();
+
+	if((struct ttyent *)0 == ty) {
+		dropbear_log(LOG_WARNING, "utmp_write_entry: tty not found");
+		return(1);
+	}
+#else /* FIXME */
+
+	tty = ttyslot(); /* seems only to work for /dev/ttyp? style names */
+
+#endif /* HAVE_GETTTYENT */
+
+	if (tty > 0 && (fd = open(UTMP_FILE, O_RDWR|O_CREAT, 0644)) >= 0) {
+		(void)lseek(fd, (off_t)(tty * sizeof(struct utmp)), SEEK_SET);
+		/*
+		 * Prevent luser from zero'ing out ut_host.
+		 * If the new ut_line is empty but the old one is not
+		 * and ut_line and ut_name match, preserve the old ut_line.
+		 */
+		if (atomicio(read, fd, &old_ut, sizeof(old_ut)) == sizeof(old_ut) &&
+			(ut->ut_host[0] == '\0') && (old_ut.ut_host[0] != '\0') &&
+			(strncmp(old_ut.ut_line, ut->ut_line, sizeof(ut->ut_line)) == 0) &&
+			(strncmp(old_ut.ut_name, ut->ut_name, sizeof(ut->ut_name)) == 0)) {
+			(void)memcpy(ut->ut_host, old_ut.ut_host, sizeof(ut->ut_host));
+		}
+
+		(void)lseek(fd, (off_t)(tty * sizeof(struct utmp)), SEEK_SET);
+		if (atomicio(write, fd, ut, sizeof(*ut)) != sizeof(*ut))
+			dropbear_log(LOG_WARNING, "utmp_write_direct: error writing %s: %s",
+			    UTMP_FILE, strerror(errno));
+
+		(void)close(fd);
+		return 1;
+	} else {
+		return 0;
+	}
+}
+# endif /* UTMP_USE_LIBRARY */
+
+static int
+utmp_perform_login(struct logininfo *li)
+{
+	struct utmp ut;
+
+	construct_utmp(li, &ut);
+# ifdef UTMP_USE_LIBRARY
+	if (!utmp_write_library(li, &ut)) {
+		dropbear_log(LOG_WARNING, "utmp_perform_login: utmp_write_library() failed");
+		return 0;
+	}
+# else
+	if (!utmp_write_direct(li, &ut)) {
+		dropbear_log(LOG_WARNING, "utmp_perform_login: utmp_write_direct() failed");
+		return 0;
+	}
+# endif
+	return 1;
+}
+
+
+static int
+utmp_perform_logout(struct logininfo *li)
+{
+	struct utmp ut;
+
+	construct_utmp(li, &ut);
+# ifdef UTMP_USE_LIBRARY
+	if (!utmp_write_library(li, &ut)) {
+		dropbear_log(LOG_WARNING, "utmp_perform_logout: utmp_write_library() failed");
+		return 0;
+	}
+# else
+	if (!utmp_write_direct(li, &ut)) {
+		dropbear_log(LOG_WARNING, "utmp_perform_logout: utmp_write_direct() failed");
+		return 0;
+	}
+# endif
+	return 1;
+}
+
+
+int
+utmp_write_entry(struct logininfo *li)
+{
+	switch(li->type) {
+	case LTYPE_LOGIN:
+		return utmp_perform_login(li);
+
+	case LTYPE_LOGOUT:
+		return utmp_perform_logout(li);
+
+	default:
+		dropbear_log(LOG_WARNING, "utmp_write_entry: invalid type field");
+		return 0;
+	}
+}
+#endif /* USE_UTMP */
+
+
+/**
+ ** Low-level utmpx functions
+ **/
+
+/* not much point if we don't want utmpx entries */
+#ifdef USE_UTMPX
+
+/* if we have the wherewithall, use pututxline etc. */
+# if !defined(DISABLE_PUTUTXLINE) && defined(HAVE_SETUTXENT) && \
+	defined(HAVE_PUTUTXLINE)
+#  define UTMPX_USE_LIBRARY
+# endif
+
+
+/* write a utmpx entry with the system's help (pututxline() and pals) */
+# ifdef UTMPX_USE_LIBRARY
+static int
+utmpx_write_library(struct logininfo *li, struct utmpx *utx)
+{
+	setutxent();
+	pututxline(utx);
+
+#  ifdef HAVE_ENDUTXENT
+	endutxent();
+#  endif
+	return 1;
+}
+
+# else /* UTMPX_USE_LIBRARY */
+
+/* write a utmp entry direct to the file */
+static int
+utmpx_write_direct(struct logininfo *li, struct utmpx *utx)
+{
+	dropbear_log(LOG_WARNING, "utmpx_write_direct: not implemented!");
+	return 0;
+}
+# endif /* UTMPX_USE_LIBRARY */
+
+static int
+utmpx_perform_login(struct logininfo *li)
+{
+	struct utmpx utx;
+
+	construct_utmpx(li, &utx);
+# ifdef UTMPX_USE_LIBRARY
+	if (!utmpx_write_library(li, &utx)) {
+		dropbear_log(LOG_WARNING, "utmpx_perform_login: utmp_write_library() failed");
+		return 0;
+	}
+# else
+	if (!utmpx_write_direct(li, &ut)) {
+		dropbear_log(LOG_WARNING, "utmpx_perform_login: utmp_write_direct() failed");
+		return 0;
+	}
+# endif
+	return 1;
+}
+
+
+static int
+utmpx_perform_logout(struct logininfo *li)
+{
+	struct utmpx utx;
+
+	construct_utmpx(li, &utx);
+# ifdef HAVE_STRUCT_UTMPX_UT_ID
+	line_abbrevname(utx.ut_id, li->line, sizeof(utx.ut_id));
+# endif
+# ifdef HAVE_STRUCT_UTMPX_UT_TYPE
+	utx.ut_type = DEAD_PROCESS;
+# endif
+
+# ifdef UTMPX_USE_LIBRARY
+	utmpx_write_library(li, &utx);
+# else
+	utmpx_write_direct(li, &utx);
+# endif
+	return 1;
+}
+
+int
+utmpx_write_entry(struct logininfo *li)
+{
+	switch(li->type) {
+	case LTYPE_LOGIN:
+		return utmpx_perform_login(li);
+	case LTYPE_LOGOUT:
+		return utmpx_perform_logout(li);
+	default:
+		dropbear_log(LOG_WARNING, "utmpx_write_entry: invalid type field");
+		return 0;
+	}
+}
+#endif /* USE_UTMPX */
+
+
+/**
+ ** Low-level wtmp functions
+ **/
+
+#ifdef USE_WTMP
+
+/* write a wtmp entry direct to the end of the file */
+/* This is a slight modification of code in OpenBSD's logwtmp.c */
+static int
+wtmp_write(struct logininfo *li, struct utmp *ut)
+{
+	struct stat buf;
+	int fd, ret = 1;
+
+	if ((fd = open(WTMP_FILE, O_WRONLY|O_APPEND, 0)) < 0) {
+		dropbear_log(LOG_WARNING, "wtmp_write: problem writing %s: %s",
+		    WTMP_FILE, strerror(errno));
+		return 0;
+	}
+	if (fstat(fd, &buf) == 0)
+		if (atomicio(write, fd, ut, sizeof(*ut)) != sizeof(*ut)) {
+			ftruncate(fd, buf.st_size);
+			dropbear_log(LOG_WARNING, "wtmp_write: problem writing %s: %s",
+			    WTMP_FILE, strerror(errno));
+			ret = 0;
+		}
+	(void)close(fd);
+	return ret;
+}
+
+static int
+wtmp_perform_login(struct logininfo *li)
+{
+	struct utmp ut;
+
+	construct_utmp(li, &ut);
+	return wtmp_write(li, &ut);
+}
+
+
+static int
+wtmp_perform_logout(struct logininfo *li)
+{
+	struct utmp ut;
+
+	construct_utmp(li, &ut);
+	return wtmp_write(li, &ut);
+}
+
+
+int
+wtmp_write_entry(struct logininfo *li)
+{
+	switch(li->type) {
+	case LTYPE_LOGIN:
+		return wtmp_perform_login(li);
+	case LTYPE_LOGOUT:
+		return wtmp_perform_logout(li);
+	default:
+		dropbear_log(LOG_WARNING, "wtmp_write_entry: invalid type field");
+		return 0;
+	}
+}
+
+
+/* Notes on fetching login data from wtmp/wtmpx
+ *
+ * Logouts are usually recorded with (amongst other things) a blank
+ * username on a given tty line.  However, some systems (HP-UX is one)
+ * leave all fields set, but change the ut_type field to DEAD_PROCESS.
+ *
+ * Since we're only looking for logins here, we know that the username
+ * must be set correctly. On systems that leave it in, we check for
+ * ut_type==USER_PROCESS (indicating a login.)
+ *
+ * Portability: Some systems may set something other than USER_PROCESS
+ * to indicate a login process. I don't know of any as I write. Also,
+ * it's possible that some systems may both leave the username in
+ * place and not have ut_type.
+ */
+
+/* return true if this wtmp entry indicates a login */
+static int
+wtmp_islogin(struct logininfo *li, struct utmp *ut)
+{
+	if (strncmp(li->username, ut->ut_name,
+		MIN_SIZEOF(li->username, ut->ut_name)) == 0) {
+# ifdef HAVE_STRUCT_UTMP_UT_TYPE
+		if (ut->ut_type & USER_PROCESS)
+			return 1;
+# else
+		return 1;
+# endif
+	}
+	return 0;
+}
+
+int
+wtmp_get_entry(struct logininfo *li)
+{
+	struct stat st;
+	struct utmp ut;
+	int fd, found=0;
+
+	/* Clear the time entries in our logininfo */
+	li->tv_sec = li->tv_usec = 0;
+
+	if ((fd = open(WTMP_FILE, O_RDONLY)) < 0) {
+		dropbear_log(LOG_WARNING, "wtmp_get_entry: problem opening %s: %s",
+		    WTMP_FILE, strerror(errno));
+		return 0;
+	}
+	if (fstat(fd, &st) != 0) {
+		dropbear_log(LOG_WARNING, "wtmp_get_entry: couldn't stat %s: %s",
+		    WTMP_FILE, strerror(errno));
+		close(fd);
+		return 0;
+	}
+
+	/* Seek to the start of the last struct utmp */
+	if (lseek(fd, -(off_t)sizeof(struct utmp), SEEK_END) == -1) {
+		/* Looks like we've got a fresh wtmp file */
+		close(fd);
+		return 0;
+	}
+
+	while (!found) {
+		if (atomicio(read, fd, &ut, sizeof(ut)) != sizeof(ut)) {
+			dropbear_log(LOG_WARNING, "wtmp_get_entry: read of %s failed: %s",
+			    WTMP_FILE, strerror(errno));
+			close (fd);
+			return 0;
+		}
+		if ( wtmp_islogin(li, &ut) ) {
+			found = 1;
+			/* We've already checked for a time in struct
+			 * utmp, in login_getlast(). */
+# ifdef HAVE_STRUCT_UTMP_UT_TIME
+			li->tv_sec = ut.ut_time;
+# else
+#  if HAVE_STRUCT_UTMP_UT_TV
+			li->tv_sec = ut.ut_tv.tv_sec;
+#  endif
+# endif
+			line_fullname(li->line, ut.ut_line,
+				      MIN_SIZEOF(li->line, ut.ut_line));
+# ifdef HAVE_STRUCT_UTMP_UT_HOST
+			strlcpy(li->hostname, ut.ut_host,
+				MIN_SIZEOF(li->hostname, ut.ut_host));
+# endif
+			continue;
+		}
+		/* Seek back 2 x struct utmp */
+		if (lseek(fd, -(off_t)(2 * sizeof(struct utmp)), SEEK_CUR) == -1) {
+			/* We've found the start of the file, so quit */
+			close (fd);
+			return 0;
+		}
+	}
+
+	/* We found an entry. Tidy up and return */
+	close(fd);
+	return 1;
+}
+# endif /* USE_WTMP */
+
+
+/**
+ ** Low-level wtmpx functions
+ **/
+
+#ifdef USE_WTMPX
+/* write a wtmpx entry direct to the end of the file */
+/* This is a slight modification of code in OpenBSD's logwtmp.c */
+static int
+wtmpx_write(struct logininfo *li, struct utmpx *utx)
+{
+	struct stat buf;
+	int fd, ret = 1;
+
+	if ((fd = open(WTMPX_FILE, O_WRONLY|O_APPEND, 0)) < 0) {
+		dropbear_log(LOG_WARNING, "wtmpx_write: problem opening %s: %s",
+		    WTMPX_FILE, strerror(errno));
+		return 0;
+	}
+
+	if (fstat(fd, &buf) == 0)
+		if (atomicio(write, fd, utx, sizeof(*utx)) != sizeof(*utx)) {
+			ftruncate(fd, buf.st_size);
+			dropbear_log(LOG_WARNING, "wtmpx_write: problem writing %s: %s",
+			    WTMPX_FILE, strerror(errno));
+			ret = 0;
+		}
+	(void)close(fd);
+
+	return ret;
+}
+
+
+static int
+wtmpx_perform_login(struct logininfo *li)
+{
+	struct utmpx utx;
+
+	construct_utmpx(li, &utx);
+	return wtmpx_write(li, &utx);
+}
+
+
+static int
+wtmpx_perform_logout(struct logininfo *li)
+{
+	struct utmpx utx;
+
+	construct_utmpx(li, &utx);
+	return wtmpx_write(li, &utx);
+}
+
+
+int
+wtmpx_write_entry(struct logininfo *li)
+{
+	switch(li->type) {
+	case LTYPE_LOGIN:
+		return wtmpx_perform_login(li);
+	case LTYPE_LOGOUT:
+		return wtmpx_perform_logout(li);
+	default:
+		dropbear_log(LOG_WARNING, "wtmpx_write_entry: invalid type field");
+		return 0;
+	}
+}
+
+/* Please see the notes above wtmp_islogin() for information about the
+   next two functions */
+
+/* Return true if this wtmpx entry indicates a login */
+static int
+wtmpx_islogin(struct logininfo *li, struct utmpx *utx)
+{
+	if ( strncmp(li->username, utx->ut_name,
+		MIN_SIZEOF(li->username, utx->ut_name)) == 0 ) {
+# ifdef HAVE_STRUCT_UTMPX_UT_TYPE
+		if (utx->ut_type == USER_PROCESS)
+			return 1;
+# else
+		return 1;
+# endif
+	}
+	return 0;
+}
+
+
+int
+wtmpx_get_entry(struct logininfo *li)
+{
+	struct stat st;
+	struct utmpx utx;
+	int fd, found=0;
+
+	/* Clear the time entries */
+	li->tv_sec = li->tv_usec = 0;
+
+	if ((fd = open(WTMPX_FILE, O_RDONLY)) < 0) {
+		dropbear_log(LOG_WARNING, "wtmpx_get_entry: problem opening %s: %s",
+		    WTMPX_FILE, strerror(errno));
+		return 0;
+	}
+	if (fstat(fd, &st) != 0) {
+		dropbear_log(LOG_WARNING, "wtmpx_get_entry: couldn't stat %s: %s",
+		    WTMPX_FILE, strerror(errno));
+		close(fd);
+		return 0;
+	}
+
+	/* Seek to the start of the last struct utmpx */
+	if (lseek(fd, -(off_t)sizeof(struct utmpx), SEEK_END) == -1 ) {
+		/* probably a newly rotated wtmpx file */
+		close(fd);
+		return 0;
+	}
+
+	while (!found) {
+		if (atomicio(read, fd, &utx, sizeof(utx)) != sizeof(utx)) {
+			dropbear_log(LOG_WARNING, "wtmpx_get_entry: read of %s failed: %s",
+			    WTMPX_FILE, strerror(errno));
+			close (fd);
+			return 0;
+		}
+		/* Logouts are recorded as a blank username on a particular line.
+		 * So, we just need to find the username in struct utmpx */
+		if ( wtmpx_islogin(li, &utx) ) {
+			found = 1;
+# ifdef HAVE_STRUCT_UTMPX_UT_TV
+			li->tv_sec = utx.ut_tv.tv_sec;
+# else
+#  ifdef HAVE_STRUCT_UTMPX_UT_TIME
+			li->tv_sec = utx.ut_time;
+#  endif
+# endif
+			line_fullname(li->line, utx.ut_line, sizeof(li->line));
+# ifdef HAVE_STRUCT_UTMPX_UT_HOST
+			strlcpy(li->hostname, utx.ut_host,
+				MIN_SIZEOF(li->hostname, utx.ut_host));
+# endif
+			continue;
+		}
+		if (lseek(fd, -(off_t)(2 * sizeof(struct utmpx)), SEEK_CUR) == -1) {
+			close (fd);
+			return 0;
+		}
+	}
+
+	close(fd);
+	return 1;
+}
+#endif /* USE_WTMPX */
+
+/**
+ ** Low-level libutil login() functions
+ **/
+
+#ifdef USE_LOGIN
+static int
+syslogin_perform_login(struct logininfo *li)
+{
+	struct utmp *ut;
+
+	if (! (ut = (struct utmp *)malloc(sizeof(*ut)))) {
+		dropbear_log(LOG_WARNING, "syslogin_perform_login: couldn't malloc()");
+		return 0;
+	}
+	construct_utmp(li, ut);
+	login(ut);
+	free(ut);
+
+	return 1;
+}
+
+static int
+syslogin_perform_logout(struct logininfo *li)
+{
+# ifdef HAVE_LOGOUT
+	char line[8];
+
+	(void)line_stripname(line, li->line, sizeof(line));
+
+	if (!logout(line)) {
+		dropbear_log(LOG_WARNING, "syslogin_perform_logout: logout(%s) returned an error: %s", line, strerror(errno));
+#  ifdef HAVE_LOGWTMP
+	} else {
+		logwtmp(line, "", "");
+#  endif
+	}
+	/* FIXME: (ATL - if the need arises) What to do if we have
+	 * login, but no logout?  what if logout but no logwtmp? All
+	 * routines are in libutil so they should all be there,
+	 * but... */
+# endif
+	return 1;
+}
+
+int
+syslogin_write_entry(struct logininfo *li)
+{
+	switch (li->type) {
+	case LTYPE_LOGIN:
+		return syslogin_perform_login(li);
+	case LTYPE_LOGOUT:
+		return syslogin_perform_logout(li);
+	default:
+		dropbear_log(LOG_WARNING, "syslogin_write_entry: Invalid type field");
+		return 0;
+	}
+}
+#endif /* USE_LOGIN */
+
+/* end of file log-syslogin.c */
+
+/**
+ ** Low-level lastlog functions
+ **/
+
+#ifdef USE_LASTLOG
+#define LL_FILE 1
+#define LL_DIR 2
+#define LL_OTHER 3
+
+static void
+lastlog_construct(struct logininfo *li, struct lastlog *last)
+{
+	/* clear the structure */
+	memset(last, '\0', sizeof(*last));
+
+	(void)line_stripname(last->ll_line, li->line, sizeof(last->ll_line));
+	strlcpy(last->ll_host, li->hostname,
+		MIN_SIZEOF(last->ll_host, li->hostname));
+	last->ll_time = li->tv_sec;
+}
+
+static int
+lastlog_filetype(char *filename)
+{
+	struct stat st;
+
+	if (stat(filename, &st) != 0) {
+		dropbear_log(LOG_WARNING, "lastlog_perform_login: Couldn't stat %s: %s", filename,
+			strerror(errno));
+		return 0;
+	}
+	if (S_ISDIR(st.st_mode))
+		return LL_DIR;
+	else if (S_ISREG(st.st_mode))
+		return LL_FILE;
+	else
+		return LL_OTHER;
+}
+
+
+/* open the file (using filemode) and seek to the login entry */
+static int
+lastlog_openseek(struct logininfo *li, int *fd, int filemode)
+{
+	off_t offset;
+	int type;
+	char lastlog_file[1024];
+
+	type = lastlog_filetype(LASTLOG_FILE);
+	switch (type) {
+		case LL_FILE:
+			strlcpy(lastlog_file, LASTLOG_FILE, sizeof(lastlog_file));
+			break;
+		case LL_DIR:
+			snprintf(lastlog_file, sizeof(lastlog_file), "%s/%s",
+				 LASTLOG_FILE, li->username);
+			break;
+		default:
+			dropbear_log(LOG_WARNING, "lastlog_openseek: %.100s is not a file or directory!",
+			    LASTLOG_FILE);
+			return 0;
+	}
+
+	*fd = open(lastlog_file, filemode);
+	if ( *fd < 0) {
+		dropbear_log(LOG_INFO, "lastlog_openseek: Couldn't open %s: %s",
+		    lastlog_file, strerror(errno));
+		return 0;
+	}
+
+	if (type == LL_FILE) {
+		/* find this uid's offset in the lastlog file */
+		offset = (off_t) ((long)li->uid * sizeof(struct lastlog));
+
+		if ( lseek(*fd, offset, SEEK_SET) != offset ) {
+			dropbear_log(LOG_WARNING, "lastlog_openseek: %s->lseek(): %s",
+			 lastlog_file, strerror(errno));
+			return 0;
+		}
+	}
+
+	return 1;
+}
+
+static int
+lastlog_perform_login(struct logininfo *li)
+{
+	struct lastlog last;
+	int fd;
+
+	/* create our struct lastlog */
+	lastlog_construct(li, &last);
+
+	if (!lastlog_openseek(li, &fd, O_RDWR|O_CREAT))
+		return(0);
+
+	/* write the entry */
+	if (atomicio(write, fd, &last, sizeof(last)) != sizeof(last)) {
+		close(fd);
+		dropbear_log(LOG_WARNING, "lastlog_write_filemode: Error writing to %s: %s",
+		    LASTLOG_FILE, strerror(errno));
+		return 0;
+	}
+
+	close(fd);
+	return 1;
+}
+
+int
+lastlog_write_entry(struct logininfo *li)
+{
+	switch(li->type) {
+	case LTYPE_LOGIN:
+		return lastlog_perform_login(li);
+	default:
+		dropbear_log(LOG_WARNING, "lastlog_write_entry: Invalid type field");
+		return 0;
+	}
+}
+
+#endif /* USE_LASTLOG */
diff --git a/loginrec.h b/loginrec.h
new file mode 100644
index 0000000000000000000000000000000000000000..03d26b984f49da52df05053b0ae098441421c606
--- /dev/null
+++ b/loginrec.h
@@ -0,0 +1,185 @@
+#ifndef _HAVE_LOGINREC_H_
+#define _HAVE_LOGINREC_H_
+
+/*
+ * Copyright (c) 2000 Andre Lucas.  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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+
+/**
+ ** loginrec.h:  platform-independent login recording and lastlog retrieval
+ **/
+
+#include "includes.h"
+
+/* RCSID("Id: loginrec.h,v 1.2 2004/05/04 10:17:43 matt Exp "); */
+
+/* The following #defines are from OpenSSH's defines.h, required for loginrec */
+
+/* FIXME: put default paths back in */
+#ifndef UTMP_FILE
+#  ifdef _PATH_UTMP
+#    define UTMP_FILE _PATH_UTMP
+#  else
+#    ifdef CONF_UTMP_FILE
+#      define UTMP_FILE CONF_UTMP_FILE
+#    endif
+#  endif
+#endif
+#ifndef WTMP_FILE
+#  ifdef _PATH_WTMP
+#    define WTMP_FILE _PATH_WTMP
+#  else
+#    ifdef CONF_WTMP_FILE
+#      define WTMP_FILE CONF_WTMP_FILE
+#    endif
+#  endif
+#endif
+/* pick up the user's location for lastlog if given */
+#ifndef LASTLOG_FILE
+#  ifdef _PATH_LASTLOG
+#    define LASTLOG_FILE _PATH_LASTLOG
+#  else
+#    ifdef CONF_LASTLOG_FILE
+#      define LASTLOG_FILE CONF_LASTLOG_FILE
+#    endif
+#  endif
+#endif
+
+
+/* The login() library function in libutil is first choice */
+#if defined(HAVE_LOGIN) && !defined(DISABLE_LOGIN)
+#  define USE_LOGIN
+
+#else
+/* Simply select your favourite login types. */
+/* Can't do if-else because some systems use several... <sigh> */
+#  if defined(UTMPX_FILE) && !defined(DISABLE_UTMPX)
+#    define USE_UTMPX
+#  endif
+#  if defined(UTMP_FILE) && !defined(DISABLE_UTMP)
+#    define USE_UTMP
+#  endif
+#  if defined(WTMPX_FILE) && !defined(DISABLE_WTMPX)
+#    define USE_WTMPX
+#  endif
+#  if defined(WTMP_FILE) && !defined(DISABLE_WTMP)
+#    define USE_WTMP
+#  endif
+
+#endif
+
+/* I hope that the presence of LASTLOG_FILE is enough to detect this */
+#if defined(LASTLOG_FILE) && !defined(DISABLE_LASTLOG)
+#  define USE_LASTLOG
+#endif
+
+
+/**
+ ** you should use the login_* calls to work around platform dependencies
+ **/
+
+/*
+ * login_netinfo structure
+ */
+
+union login_netinfo {
+	struct sockaddr sa;
+	struct sockaddr_in sa_in;
+#ifdef HAVE_STRUCT_SOCKADDR_STORAGE
+	struct sockaddr_storage sa_storage;
+#endif
+};
+
+/*
+ *   * logininfo structure  *
+ */
+/* types - different to utmp.h 'type' macros */
+/* (though set to the same value as linux, openbsd and others...) */
+#define LTYPE_LOGIN    7
+#define LTYPE_LOGOUT   8
+
+/* string lengths - set very long */
+#define LINFO_PROGSIZE 64
+#define LINFO_LINESIZE 64
+#define LINFO_NAMESIZE 64
+#define LINFO_HOSTSIZE 256
+
+struct logininfo {
+	char       progname[LINFO_PROGSIZE];     /* name of program (for PAM) */
+	int        progname_null;
+	short int  type;                         /* type of login (LTYPE_*) */
+	int        pid;                          /* PID of login process */
+	int        uid;                          /* UID of this user */
+	char       line[LINFO_LINESIZE];         /* tty/pty name */
+	char       username[LINFO_NAMESIZE];     /* login username */
+	char       hostname[LINFO_HOSTSIZE];     /* remote hostname */
+	/* 'exit_status' structure components */
+	int        exit;                        /* process exit status */
+	int        termination;                 /* process termination status */
+	/* struct timeval (sys/time.h) isn't always available, if it isn't we'll
+	 * use time_t's value as tv_sec and set tv_usec to 0
+	 */
+	unsigned int tv_sec;
+	unsigned int tv_usec;
+	union login_netinfo hostaddr;       /* caller's host address(es) */
+}; /* struct logininfo */
+
+/*
+ * login recording functions
+ */
+
+/** 'public' functions */
+
+struct logininfo *login_alloc_entry(int pid, const char *username,
+				    const char *hostname, const char *line);
+/* free a structure */
+void login_free_entry(struct logininfo *li);
+/* fill out a pre-allocated structure with useful information */
+int login_init_entry(struct logininfo *li, int pid, const char *username,
+		     const char *hostname, const char *line);
+/* place the current time in a logininfo struct */
+void login_set_current_time(struct logininfo *li);
+
+/* record the entry */
+int login_login (struct logininfo *li);
+int login_logout(struct logininfo *li);
+#ifdef LOGIN_NEEDS_UTMPX
+int login_utmp_only(struct logininfo *li);
+#endif
+
+/** End of public functions */
+
+/* record the entry */
+int login_write (struct logininfo *li);
+int login_log_entry(struct logininfo *li);
+
+/* set the network address based on network address type */
+void login_set_addr(struct logininfo *li, const struct sockaddr *sa,
+		    const unsigned int sa_size);
+
+/* produce various forms of the line filename */
+char *line_fullname(char *dst, const char *src, size_t dstsize);
+char *line_stripname(char *dst, const char *src, size_t dstsize);
+char *line_abbrevname(char *dst, const char *src, size_t dstsize);
+
+#endif /* _HAVE_LOGINREC_H_ */
diff --git a/options.h b/options.h
new file mode 100644
index 0000000000000000000000000000000000000000..1feae40dfe6278b2855febc33940e9af35713b97
--- /dev/null
+++ b/options.h
@@ -0,0 +1,401 @@
+/* Dropbear SSH
+ * Copyright (c) 2002,2003 Matt Johnston
+ * All rights reserved. See LICENSE for the license. */
+
+#ifndef _OPTIONS_H_
+#define _OPTIONS_H_
+
+/******************************************************************
+ * Define compile-time options below - the "#ifndef DROPBEAR_XXX .... #endif"
+ * parts are to allow for commandline -DDROPBEAR_XXX options etc.
+ ******************************************************************/
+
+#ifndef DROPBEAR_DEFPORT
+#define DROPBEAR_DEFPORT "22"
+#endif
+
+/* Default hostkey paths - these can be specified on the command line */
+#ifndef DSS_PRIV_FILENAME
+#define DSS_PRIV_FILENAME "/etc/dropbear/dropbear_dss_host_key"
+#endif
+#ifndef RSA_PRIV_FILENAME
+#define RSA_PRIV_FILENAME "/etc/dropbear/dropbear_rsa_host_key"
+#endif
+
+/* Set NON_INETD_MODE if you require daemon functionality (ie Dropbear listens
+ * on chosen ports and keeps accepting connections. This is the default.
+ *
+ * Set INETD_MODE if you want to be able to run Dropbear with inetd (or
+ * similar), where it will use stdin/stdout for connections, and each process
+ * lasts for a single connection. Dropbear should be invoked with the -i flag
+ * for inetd, and can only accept IPv4 connections.
+ *
+ * Both of these flags can be defined at once, don't compile without at least
+ * one of them. */
+#define NON_INETD_MODE
+#define INETD_MODE
+
+/* Setting this disables the fast exptmod bignum code. It saves ~5kB, but is
+ * perhaps 20% slower for pubkey operations (it is probably worth experimenting
+ * if you want to use this) */
+/*#define NO_FAST_EXPTMOD*/
+
+/* Set this if you want to use the DROPBEAR_SMALL_CODE option. This can save
+several kB in binary size, however will make the symmetrical ciphers (AES, DES
+etc) slower (perhaps by 50%). Recommended for most small systems. */
+#define DROPBEAR_SMALL_CODE
+
+/* Enable X11 Forwarding - server only */
+#define ENABLE_X11FWD
+
+/* Enable TCP Fowarding */
+/* 'Local' is "-L" style (client listening port forwarded via server)
+ * 'Remote' is "-R" style (server listening port forwarded via client) */
+
+#define ENABLE_CLI_LOCALTCPFWD
+#define ENABLE_CLI_REMOTETCPFWD
+
+#define ENABLE_SVR_LOCALTCPFWD
+#define ENABLE_SVR_REMOTETCPFWD
+
+/* Enable Authentication Agent Forwarding - server only for now */
+#define ENABLE_AGENTFWD
+
+/* Encryption - at least one required.
+ * RFC Draft requires 3DES and recommends AES128 for interoperability.
+ * Including multiple keysize variants the same cipher 
+ * (eg AES256 as well as AES128) will result in a minimal size increase.*/
+#define DROPBEAR_AES128_CBC
+#define DROPBEAR_3DES_CBC
+#define DROPBEAR_AES256_CBC
+#define DROPBEAR_BLOWFISH_CBC
+#define DROPBEAR_TWOFISH256_CBC
+#define DROPBEAR_TWOFISH128_CBC
+
+/* Message Integrity - at least one required.
+ * RFC Draft requires sha1 and recommends sha1-96.
+ * sha1-96 may be of use for slow links, as it has a smaller overhead.
+ *
+ * Note: there's no point disabling sha1 to save space, since it's used
+ * for the random number generator and public-key cryptography anyway.
+ * Disabling it here will just stop it from being used as the integrity portion
+ * of the ssh protocol.
+ *
+ * These hashes are also used for public key fingerprints in logs.
+ * If you disable MD5, Dropbear will fall back to SHA1 fingerprints,
+ * which are not the standard form. */
+#define DROPBEAR_SHA1_HMAC
+#define DROPBEAR_SHA1_96_HMAC
+#define DROPBEAR_MD5_HMAC
+
+/* Hostkey/public key algorithms - at least one required, these are used
+ * for hostkey as well as for verifying signatures with pubkey auth.
+ * Removing either of these won't save very much space.
+ * SSH2 RFC Draft requires dss, recommends rsa */
+#define DROPBEAR_RSA
+#define DROPBEAR_DSS
+
+/* RSA can be vulnerable to timing attacks which use the time required for
+ * signing to guess the private key. Blinding avoids this attack, though makes
+ * signing operations slightly slower. */
+#define RSA_BLINDING
+
+/* Define DSS_PROTOK to use PuTTY's method of generating the value k for dss,
+ * rather than just from the random byte source. Undefining this will save you
+ * ~4k in binary size with static uclibc, but your DSS hostkey could be exposed
+ * if the random number source isn't good. In general this isn't required */
+/* #define DSS_PROTOK */
+
+/* Whether to do reverse DNS lookups. */
+#define DO_HOST_LOOKUP
+
+/* Whether to print the message of the day (MOTD). This doesn't add much code
+ * size */
+#define DO_MOTD
+
+/* The MOTD file path */
+#ifndef MOTD_FILENAME
+#define MOTD_FILENAME "/etc/motd"
+#endif
+
+/* Authentication Types - at least one required.
+   RFC Draft requires pubkey auth, and recommends password */
+
+/* Note: PAM auth is quite simple, and only works for PAM modules which just do
+ * a simple "Login: " "Password: " (you can edit the strings in svr-authpam.c).
+ * It's useful for systems like OS X where standard password crypts don't work,
+ * but there's an interface via a PAM module - don't bother using it otherwise.
+ * You can't enable both PASSWORD and PAM. */
+
+#define ENABLE_SVR_PASSWORD_AUTH
+/*#define ENABLE_SVR_PAM_AUTH*/
+#define ENABLE_SVR_PUBKEY_AUTH
+
+#define ENABLE_CLI_PASSWORD_AUTH
+#define ENABLE_CLI_PUBKEY_AUTH
+#define ENABLE_CLI_INTERACT_AUTH
+
+/* Define this (as well as ENABLE_CLI_PASSWORD_AUTH) to allow the use of
+ * a helper program for the ssh client. The helper program should be
+ * specified in the SSH_ASKPASS environment variable, and dbclient
+ * should be run with DISPLAY set and no tty. The program should
+ * return the password on standard output */
+/*#define ENABLE_CLI_ASKPASS_HELPER*/
+
+/* Random device to use - define either DROPBEAR_RANDOM_DEV or
+ * DROPBEAR_PRNGD_SOCKET.
+ * DROPBEAR_RANDOM_DEV is recommended on hosts with a good /dev/(u)random,
+ * otherwise use run prngd (or egd if you want), specifying the socket. 
+ * The device will be queried for a few dozen bytes of seed a couple of times
+ * per session (or more for very long-lived sessions). */
+
+/* If you are lacking entropy on the system then using /dev/urandom
+ * will prevent Dropbear from blocking on the device. This could
+ * however significantly reduce the security of your ssh connections
+ * if the PRNG state becomes guessable - make sure you know what you are
+ * doing if you change this. */
+#define DROPBEAR_RANDOM_DEV "/dev/random"
+
+/* prngd must be manually set up to produce output */
+/*#define DROPBEAR_PRNGD_SOCKET "/var/run/dropbear-rng"*/
+
+/* Specify the number of clients we will allow to be connected but
+ * not yet authenticated. After this limit, connections are rejected */
+/* The first setting is per-IP, to avoid denial of service */
+#ifndef MAX_UNAUTH_PER_IP
+#define MAX_UNAUTH_PER_IP 5
+#endif
+
+/* And then a global limit to avoid chewing memory if connections 
+ * come from many IPs */
+#ifndef MAX_UNAUTH_CLIENTS
+#define MAX_UNAUTH_CLIENTS 30
+#endif
+
+/* Maximum number of failed authentication tries (server option) */
+#ifndef MAX_AUTH_TRIES
+#define MAX_AUTH_TRIES 10
+#endif
+
+/* The file to store the daemon's process ID, for shutdown scripts etc */
+#ifndef DROPBEAR_PIDFILE
+#define DROPBEAR_PIDFILE "/var/run/dropbear.pid"
+#endif
+
+/* The command to invoke for xauth when using X11 forwarding.
+ * "-q" for quiet */
+#ifndef XAUTH_COMMAND
+#define XAUTH_COMMAND "/usr/X11R6/bin/xauth -q"
+#endif
+
+/* if you want to enable running an sftp server (such as the one included with
+ * OpenSSH), set the path below. If the path isn't defined, sftp will not
+ * be enabled */
+#ifndef SFTPSERVER_PATH
+#define SFTPSERVER_PATH "/usr/libexec/sftp-server"
+#endif
+
+/* This is used by the scp binary when used as a client binary. If you're
+ * not using the Dropbear client, you'll need to change it */
+#define _PATH_SSH_PROGRAM "/usr/bin/dbclient"
+
+/* Multi-purpose binary configuration has now moved. Look at the top
+ * of the Makefile for instructions, or INSTALL */
+
+/*******************************************************************
+ * You shouldn't edit below here unless you know you need to.
+ *******************************************************************/
+
+#ifndef DROPBEAR_VERSION
+#define DROPBEAR_VERSION "0.47"
+#endif
+
+#define LOCAL_IDENT "SSH-2.0-dropbear_" DROPBEAR_VERSION
+#define PROGNAME "dropbear"
+
+/* Spec recommends after one hour or 1 gigabyte of data. One hour
+ * is a bit too verbose, so we try 8 hours */
+#ifndef KEX_REKEY_TIMEOUT
+#define KEX_REKEY_TIMEOUT (3600 * 8)
+#endif
+#ifndef KEX_REKEY_DATA
+#define KEX_REKEY_DATA (1<<30) /* 2^30 == 1GB, this value must be < INT_MAX */
+#endif
+/* Close connections to clients which haven't authorised after AUTH_TIMEOUT */
+#ifndef AUTH_TIMEOUT
+#define AUTH_TIMEOUT 300 /* we choose 5 minutes */
+#endif
+
+/* Minimum key sizes for DSS and RSA */
+#ifndef MIN_DSS_KEYLEN
+#define MIN_DSS_KEYLEN 512
+#endif
+#ifndef MIN_RSA_KEYLEN
+#define MIN_RSA_KEYLEN 512
+#endif
+
+#define MAX_BANNER_SIZE 2000 /* this is 25*80 chars, any more is foolish */
+#define MAX_BANNER_LINES 20 /* How many lines the client will display */
+
+/* the number of NAME=VALUE pairs to malloc for environ, if we don't have
+ * the clearenv() function */
+#define ENV_SIZE 100
+
+#define MAX_CMD_LEN 1024 /* max length of a command */
+#define MAX_TERM_LEN 200 /* max length of TERM name */
+
+#define MAX_HOST_LEN 254 /* max hostname len for tcp fwding */
+#define MAX_IP_LEN 15 /* strlen("255.255.255.255") == 15 */
+
+#define DROPBEAR_MAX_PORTS 10 /* max number of ports which can be specified,
+								 ipv4 and ipv6 don't count twice */
+
+#define _PATH_TTY "/dev/tty"
+
+/* Timeouts in seconds */
+#define SELECT_TIMEOUT 20
+
+/* success/failure defines */
+#define DROPBEAR_SUCCESS 0
+#define DROPBEAR_FAILURE -1
+
+/* various algorithm identifiers */
+#define DROPBEAR_KEX_DH_GROUP1 0
+
+#define DROPBEAR_SIGNKEY_ANY 0
+#define DROPBEAR_SIGNKEY_RSA 1
+#define DROPBEAR_SIGNKEY_DSS 2
+#define DROPBEAR_SIGNKEY_NONE 3
+
+#define DROPBEAR_COMP_NONE 0
+#define DROPBEAR_COMP_ZLIB 1
+
+/* Required for pubkey auth */
+#if defined(ENABLE_SVR_PUBKEY_AUTH) || defined(DROPBEAR_CLIENT)
+#define DROPBEAR_SIGNKEY_VERIFY
+#endif
+
+/* SHA1 is 20 bytes == 160 bits */
+#define SHA1_HASH_SIZE 20
+/* SHA512 is 64 bytes == 512 bits */
+#define SHA512_HASH_SIZE 64
+/* MD5 is 16 bytes = 128 bits */
+#define MD5_HASH_SIZE 16
+
+/* largest of MD5 and SHA1 */
+#define MAX_MAC_LEN SHA1_HASH_SIZE
+
+
+#define MAX_KEY_LEN 32 /* 256 bits for aes256 etc */
+#define MAX_IV_LEN 20 /* must be same as max blocksize, 
+						 and >= SHA1_HASH_SIZE */
+#define MAX_MAC_KEY 20
+
+#define MAX_NAME_LEN 64 /* maximum length of a protocol name, isn't
+						   explicitly specified for all protocols (just
+						   for algos) but seems valid */
+
+#define MAX_PROPOSED_ALGO 20
+
+/* size/count limits */
+#define MAX_LISTEN_ADDR 10
+
+#define MAX_PACKET_LEN 35000
+#define MIN_PACKET_LEN 16
+#define MAX_PAYLOAD_LEN 32768
+
+#define MAX_TRANS_PAYLOAD_LEN 32768
+#define MAX_TRANS_PACKET_LEN (MAX_TRANS_PAYLOAD_LEN+50)
+
+#define MAX_TRANS_WINDOW 500000000 /* 500MB is sufficient, stopping overflow */
+#define MAX_TRANS_WIN_INCR 500000000 /* overflow prevention */
+
+#define MAX_STRING_LEN 1400 /* ~= MAX_PROPOSED_ALGO * MAX_NAME_LEN, also
+							   is the max length for a password etc */
+
+/* For a 4096 bit DSS key, empirically determined */
+#define MAX_PUBKEY_SIZE 1700
+/* For a 4096 bit DSS key, empirically determined */
+#define MAX_PRIVKEY_SIZE 1700
+
+/* The maximum size of the bignum portion of the kexhash buffer */
+/* Sect. 8 of the transport draft, K_S + e + f + K */
+#define KEXHASHBUF_MAX_INTS (1700 + 130 + 130 + 130)
+
+#define DROPBEAR_MAX_SOCKS 2 /* IPv4, IPv6 are all we'll get for now. Revisit
+								in a few years time.... */
+
+#define DROPBEAR_MAX_CLI_PASS 1024
+
+#define DROPBEAR_MAX_CLI_INTERACT_PROMPTS 80 /* The number of prompts we'll 
+												accept for keyb-interactive
+												auth */
+
+#if defined(DROPBEAR_AES256_CBC) || defined(DROPBEAR_AES128_CBC)
+#define DROPBEAR_AES_CBC
+#endif
+
+#if defined(DROPBEAR_TWOFISH256_CBC) || defined(DROPBEAR_TWOFISH128_CBC)
+#define DROPBEAR_TWOFISH_CBC
+#endif
+
+#ifndef ENABLE_X11FWD
+#define DISABLE_X11FWD
+#endif
+
+#ifndef ENABLE_AGENTFWD
+#define DISABLE_AGENTFWD
+#endif
+
+#if defined(ENABLE_CLI_REMOTETCPFWD) || defined(ENABLE_CLI_LOCALTCPFWD)
+#define ENABLE_CLI_ANYTCPFWD 
+#endif
+
+#if defined(ENABLE_CLI_LOCALTCPFWD) || defined(ENABLE_SVR_REMOTETCPFWD)
+#define DROPBEAR_TCP_ACCEPT
+#endif
+
+#if defined(ENABLE_CLI_REMOTETCPFWD) || defined(ENABLE_CLI_LOCALTCPFWD) || \
+	defined(ENABLE_SVR_REMOTETCPFWD) || defined(ENABLE_SVR_LOCALTCPFWD) || \
+	defined(ENABLE_AGENTFWD) || defined(ENABLE_X11FWD)
+#define USING_LISTENERS
+#endif
+
+#if defined(DROPBEAR_CLIENT) || defined(ENABLE_SVR_PUBKEY_AUTH)
+#define DROPBEAR_KEY_LINES /* ie we're using authorized_keys or known_hosts */
+#endif
+
+#if defined(ENABLE_SVR_PASSWORD_AUTH) && defined(ENABLE_SVR_PAM_AUTH)
+#error "You can't turn on PASSWORD and PAM auth both at once. Fix it in options.h"
+#endif
+
+#if defined(DROPBEAR_RANDOM_DEV) && defined(DROPBEAR_PRNGD_SOCKET)
+#error "You can't turn on DROPBEAR_PRNGD_SOCKET and DROPBEAR_RANDOM_DEV at once"
+#endif
+
+#if !defined(DROPBEAR_RANDOM_DEV) && !defined(DROPBEAR_PRNGD_SOCKET)
+#error "You must choose one of DROPBEAR_PRNGD_SOCKET or DROPBEAR_RANDOM_DEV in options.h"
+#endif
+
+/* We use dropbear_client and dropbear_server as shortcuts to avoid redundant
+ * code, if we're just compiling as client or server */
+#if defined(DROPBEAR_SERVER) && defined(DROPBEAR_CLIENT)
+
+#define IS_DROPBEAR_SERVER (ses.isserver == 1)
+#define IS_DROPBEAR_CLIENT (ses.isserver == 0)
+
+#elif defined(DROPBEAR_SERVER)
+
+#define IS_DROPBEAR_SERVER 1
+#define IS_DROPBEAR_CLIENT 0
+
+#elif defined(DROPBEAR_CLIENT)
+
+#define IS_DROPBEAR_SERVER 0
+#define IS_DROPBEAR_CLIENT 1
+
+#else
+#error You must compiled with either DROPBEAR_CLIENT or DROPBEAR_SERVER selected
+#endif
+
+#endif /* _OPTIONS_H_ */
diff --git a/packet.c b/packet.c
new file mode 100644
index 0000000000000000000000000000000000000000..b2c61747dc7d248346f667876fa4f84df463da7a
--- /dev/null
+++ b/packet.c
@@ -0,0 +1,613 @@
+/*
+ * 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. */
+
+#include "includes.h"
+#include "packet.h"
+#include "session.h"
+#include "dbutil.h"
+#include "ssh.h"
+#include "algo.h"
+#include "buffer.h"
+#include "kex.h"
+#include "random.h"
+#include "service.h"
+#include "auth.h"
+#include "channel.h"
+
+static void read_packet_init();
+static void writemac(buffer * outputbuffer, buffer * clearwritebuf);
+static int checkmac(buffer* hashbuf, buffer* readbuf);
+
+#define ZLIB_COMPRESS_INCR 20 /* this is 12 bytes + 0.1% of 8000 bytes */
+#define ZLIB_DECOMPRESS_INCR 100
+#ifndef DISABLE_ZLIB
+static buffer* buf_decompress(buffer* buf, unsigned int len);
+static void buf_compress(buffer * dest, buffer * src, unsigned int len);
+#endif
+
+/* non-blocking function writing out a current encrypted packet */
+void write_packet() {
+
+	int len, written;
+	buffer * writebuf = NULL;
+	
+	TRACE(("enter write_packet"))
+	dropbear_assert(!isempty(&ses.writequeue));
+
+	/* Get the next buffer in the queue of encrypted packets to write*/
+	writebuf = (buffer*)examine(&ses.writequeue);
+
+	len = writebuf->len - writebuf->pos;
+	dropbear_assert(len > 0);
+	/* Try to write as much as possible */
+	written = write(ses.sock, buf_getptr(writebuf, len), len);
+
+	if (written < 0) {
+		if (errno == EINTR) {
+			TRACE(("leave writepacket: EINTR"))
+			return;
+		} else {
+			dropbear_exit("error writing");
+		}
+	} 
+
+	if (written == 0) {
+		ses.remoteclosed();
+	}
+
+	if (written == len) {
+		/* We've finished with the packet, free it */
+		dequeue(&ses.writequeue);
+		buf_free(writebuf);
+		writebuf = NULL;
+	} else {
+		/* More packet left to write, leave it in the queue for later */
+		buf_incrpos(writebuf, written);
+	}
+
+	TRACE(("leave write_packet"))
+}
+
+/* Non-blocking function reading available portion of a packet into the
+ * ses's buffer, decrypting the length if encrypted, decrypting the
+ * full portion if possible */
+void read_packet() {
+
+	int len;
+	unsigned int maxlen;
+	unsigned char blocksize;
+
+	TRACE(("enter read_packet"))
+	blocksize = ses.keys->recv_algo_crypt->blocksize;
+	
+	if (ses.readbuf == NULL || ses.readbuf->len < blocksize) {
+		/* In the first blocksize of a packet */
+
+		/* Read the first blocksize of the packet, so we can decrypt it and
+		 * find the length of the whole packet */
+		read_packet_init();
+
+		/* If we don't have the length of decryptreadbuf, we didn't read
+		 * a whole blocksize and should exit */
+		if (ses.decryptreadbuf->len == 0) {
+			TRACE(("leave read_packet: packetinit done"))
+			return;
+		}
+	}
+
+	/* Attempt to read the remainder of the packet, note that there
+	 * mightn't be any available (EAGAIN) */
+	dropbear_assert(ses.readbuf != NULL);
+	maxlen = ses.readbuf->len - ses.readbuf->pos;
+	len = read(ses.sock, buf_getptr(ses.readbuf, maxlen), maxlen);
+
+	if (len == 0) {
+		ses.remoteclosed();
+	}
+
+	if (len < 0) {
+		if (errno == EINTR || errno == EAGAIN) {
+			TRACE(("leave read_packet: EINTR or EAGAIN"))
+			return;
+		} else {
+			dropbear_exit("error reading: %s", strerror(errno));
+		}
+	}
+
+	buf_incrpos(ses.readbuf, len);
+
+	if ((unsigned int)len == maxlen) {
+		/* The whole packet has been read */
+		decrypt_packet();
+		/* The main select() loop process_packet() to
+		 * handle the packet contents... */
+	}
+	TRACE(("leave read_packet"))
+}
+
+/* Function used to read the initial portion of a packet, and determine the
+ * length. Only called during the first BLOCKSIZE of a packet. */
+static void read_packet_init() {
+
+	unsigned int maxlen;
+	int len;
+	unsigned char blocksize;
+	unsigned char macsize;
+
+
+	blocksize = ses.keys->recv_algo_crypt->blocksize;
+	macsize = ses.keys->recv_algo_mac->hashsize;
+
+	if (ses.readbuf == NULL) {
+		/* start of a new packet */
+		ses.readbuf = buf_new(INIT_READBUF);
+		dropbear_assert(ses.decryptreadbuf == NULL);
+		ses.decryptreadbuf = buf_new(blocksize);
+	}
+
+	maxlen = blocksize - ses.readbuf->pos;
+			
+	/* read the rest of the packet if possible */
+	len = read(ses.sock, buf_getwriteptr(ses.readbuf, maxlen),
+			maxlen);
+	if (len == 0) {
+		ses.remoteclosed();
+	}
+	if (len < 0) {
+		if (errno == EINTR) {
+			TRACE(("leave read_packet_init: EINTR"))
+			return;
+		}
+		dropbear_exit("error reading: %s", strerror(errno));
+	}
+
+	buf_incrwritepos(ses.readbuf, len);
+
+	if ((unsigned int)len != maxlen) {
+		/* don't have enough bytes to determine length, get next time */
+		return;
+	}
+
+	/* now we have the first block, need to get packet length, so we decrypt
+	 * the first block (only need first 4 bytes) */
+	buf_setpos(ses.readbuf, 0);
+	if (ses.keys->recv_algo_crypt->cipherdesc == NULL) {
+		/* copy it */
+		memcpy(buf_getwriteptr(ses.decryptreadbuf, blocksize),
+				buf_getptr(ses.readbuf, blocksize),
+				blocksize);
+	} else {
+		/* decrypt it */
+		if (cbc_decrypt(buf_getptr(ses.readbuf, blocksize), 
+					buf_getwriteptr(ses.decryptreadbuf,blocksize),
+					blocksize,
+					&ses.keys->recv_symmetric_struct) != CRYPT_OK) {
+			dropbear_exit("error decrypting");
+		}
+	}
+	buf_setlen(ses.decryptreadbuf, blocksize);
+	len = buf_getint(ses.decryptreadbuf) + 4 + macsize;
+
+	buf_setpos(ses.readbuf, blocksize);
+
+	/* check packet length */
+	if ((len > MAX_PACKET_LEN) ||
+		(len < MIN_PACKET_LEN + macsize) ||
+		((len - macsize) % blocksize != 0)) {
+		dropbear_exit("bad packet size %d", len);
+	}
+
+	buf_resize(ses.readbuf, len);
+	buf_setlen(ses.readbuf, len);
+
+}
+
+/* handle the received packet */
+void decrypt_packet() {
+
+	unsigned char blocksize;
+	unsigned char macsize;
+	unsigned int padlen;
+	unsigned int len;
+
+	TRACE(("enter decrypt_packet"))
+	blocksize = ses.keys->recv_algo_crypt->blocksize;
+	macsize = ses.keys->recv_algo_mac->hashsize;
+
+	ses.kexstate.datarecv += ses.readbuf->len;
+
+	/* we've already decrypted the first blocksize in read_packet_init */
+	buf_setpos(ses.readbuf, blocksize);
+
+	buf_resize(ses.decryptreadbuf, ses.readbuf->len - macsize);
+	buf_setlen(ses.decryptreadbuf, ses.decryptreadbuf->size);
+	buf_setpos(ses.decryptreadbuf, blocksize);
+
+	/* decrypt if encryption is set, memcpy otherwise */
+	if (ses.keys->recv_algo_crypt->cipherdesc == NULL) {
+		/* copy it */
+		len = ses.readbuf->len - macsize - blocksize;
+		memcpy(buf_getwriteptr(ses.decryptreadbuf, len),
+				buf_getptr(ses.readbuf, len), len);
+	} else {
+		/* decrypt */
+		while (ses.readbuf->pos < ses.readbuf->len - macsize) {
+			if (cbc_decrypt(buf_getptr(ses.readbuf, blocksize), 
+						buf_getwriteptr(ses.decryptreadbuf, blocksize),
+						blocksize,
+						&ses.keys->recv_symmetric_struct) != CRYPT_OK) {
+				dropbear_exit("error decrypting");
+			}
+			buf_incrpos(ses.readbuf, blocksize);
+			buf_incrwritepos(ses.decryptreadbuf, blocksize);
+		}
+	}
+
+	/* check the hmac */
+	buf_setpos(ses.readbuf, ses.readbuf->len - macsize);
+	if (checkmac(ses.readbuf, ses.decryptreadbuf) != DROPBEAR_SUCCESS) {
+		dropbear_exit("Integrity error");
+	}
+
+	/* readbuf no longer required */
+	buf_free(ses.readbuf);
+	ses.readbuf = NULL;
+
+	/* get padding length */
+	buf_setpos(ses.decryptreadbuf, PACKET_PADDING_OFF);
+	padlen = buf_getbyte(ses.decryptreadbuf);
+		
+	/* payload length */
+	/* - 4 - 1 is for LEN and PADLEN values */
+	len = ses.decryptreadbuf->len - padlen - 4 - 1;
+	if ((len > MAX_PAYLOAD_LEN) || (len < 1)) {
+		dropbear_exit("bad packet size");
+	}
+
+	buf_setpos(ses.decryptreadbuf, PACKET_PAYLOAD_OFF);
+
+#ifndef DISABLE_ZLIB
+	if (ses.keys->recv_algo_comp == DROPBEAR_COMP_ZLIB) {
+		/* decompress */
+		ses.payload = buf_decompress(ses.decryptreadbuf, len);
+
+	} else 
+#endif
+	{
+		/* copy payload */
+		ses.payload = buf_new(len);
+		memcpy(ses.payload->data, buf_getptr(ses.decryptreadbuf, len), len);
+		buf_incrlen(ses.payload, len);
+	}
+
+	buf_free(ses.decryptreadbuf);
+	ses.decryptreadbuf = NULL;
+	buf_setpos(ses.payload, 0);
+
+	ses.recvseq++;
+
+	TRACE(("leave decrypt_packet"))
+}
+
+/* Checks the mac in hashbuf, for the data in readbuf.
+ * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
+static int checkmac(buffer* macbuf, buffer* sourcebuf) {
+
+	unsigned int macsize;
+	hmac_state hmac;
+	unsigned char tempbuf[MAX_MAC_LEN];
+	unsigned long bufsize;
+	unsigned int len;
+
+	macsize = ses.keys->recv_algo_mac->hashsize;
+	if (macsize == 0) {
+		return DROPBEAR_SUCCESS;
+	}
+
+	/* calculate the mac */
+	if (hmac_init(&hmac, 
+				find_hash(ses.keys->recv_algo_mac->hashdesc->name), 
+				ses.keys->recvmackey, 
+				ses.keys->recv_algo_mac->keysize) 
+				!= CRYPT_OK) {
+		dropbear_exit("HMAC error");
+	}
+	
+	/* sequence number */
+	STORE32H(ses.recvseq, tempbuf);
+	if (hmac_process(&hmac, tempbuf, 4) != CRYPT_OK) {
+		dropbear_exit("HMAC error");
+	}
+
+	buf_setpos(sourcebuf, 0);
+	len = sourcebuf->len;
+	if (hmac_process(&hmac, buf_getptr(sourcebuf, len), len) != CRYPT_OK) {
+		dropbear_exit("HMAC error");
+	}
+
+	bufsize = sizeof(tempbuf);
+	if (hmac_done(&hmac, tempbuf, &bufsize) != CRYPT_OK) {
+		dropbear_exit("HMAC error");
+	}
+
+	/* compare the hash */
+	if (memcmp(tempbuf, buf_getptr(macbuf, macsize), macsize) != 0) {
+		return DROPBEAR_FAILURE;
+	} else {
+		return DROPBEAR_SUCCESS;
+	}
+}
+
+#ifndef DISABLE_ZLIB
+/* returns a pointer to a newly created buffer */
+static buffer* buf_decompress(buffer* buf, unsigned int len) {
+
+	int result;
+	buffer * ret;
+	z_streamp zstream;
+
+	zstream = ses.keys->recv_zstream;
+	ret = buf_new(len);
+
+	zstream->avail_in = len;
+	zstream->next_in = buf_getptr(buf, len);
+
+	/* decompress the payload, incrementally resizing the output buffer */
+	while (1) {
+
+		zstream->avail_out = ret->size - ret->pos;
+		zstream->next_out = buf_getwriteptr(ret, zstream->avail_out);
+
+		result = inflate(zstream, Z_SYNC_FLUSH);
+
+		buf_setlen(ret, ret->size - zstream->avail_out);
+		buf_setpos(ret, ret->len);
+
+		if (result != Z_BUF_ERROR && result != Z_OK) {
+			dropbear_exit("zlib error");
+		}
+
+		if (zstream->avail_in == 0 &&
+		   		(zstream->avail_out != 0 || result == Z_BUF_ERROR)) {
+			/* we can only exit if avail_out hasn't all been used,
+			 * and there's no remaining input */
+			return ret;
+		}
+
+		if (zstream->avail_out == 0) {
+			buf_resize(ret, ret->size + ZLIB_DECOMPRESS_INCR);
+		}
+	}
+}
+#endif
+
+
+
+	
+/* encrypt the writepayload, putting into writebuf, ready for write_packet()
+ * to put on the wire */
+void encrypt_packet() {
+
+	unsigned char padlen;
+	unsigned char blocksize, macsize;
+	buffer * writebuf; /* the packet which will go on the wire */
+	buffer * clearwritebuf; /* unencrypted, possibly compressed */
+	
+	TRACE(("enter encrypt_packet()"))
+	TRACE(("encrypt_packet type is %d", ses.writepayload->data[0]))
+	blocksize = ses.keys->trans_algo_crypt->blocksize;
+	macsize = ses.keys->trans_algo_mac->hashsize;
+
+	/* Encrypted packet len is payload+5, then worst case is if we are 3 away
+	 * from a blocksize multiple. In which case we need to pad to the
+	 * multiple, then add another blocksize (or MIN_PACKET_LEN) */
+	clearwritebuf = buf_new((ses.writepayload->len+4+1) + MIN_PACKET_LEN + 3
+#ifndef DISABLE_ZLIB
+			+ ZLIB_COMPRESS_INCR /* bit of a kludge, but we can't know len*/
+#endif
+			);
+	buf_setlen(clearwritebuf, PACKET_PAYLOAD_OFF);
+	buf_setpos(clearwritebuf, PACKET_PAYLOAD_OFF);
+
+	buf_setpos(ses.writepayload, 0);
+
+#ifndef DISABLE_ZLIB
+	/* compression */
+	if (ses.keys->trans_algo_comp == DROPBEAR_COMP_ZLIB) {
+		buf_compress(clearwritebuf, ses.writepayload, ses.writepayload->len);
+	} else
+#endif
+	{
+		memcpy(buf_getwriteptr(clearwritebuf, ses.writepayload->len),
+				buf_getptr(ses.writepayload, ses.writepayload->len),
+				ses.writepayload->len);
+		buf_incrwritepos(clearwritebuf, ses.writepayload->len);
+	}
+
+	/* finished with payload */
+	buf_burn(ses.writepayload); /* XXX This is probably a good idea, and isn't
+								   _that_ likely to hurt performance too badly.
+								   Buffers can have cleartext passwords etc, or
+								   other sensitive data */
+	buf_setpos(ses.writepayload, 0);
+	buf_setlen(ses.writepayload, 0);
+
+	/* length of padding - packet length must be a multiple of blocksize,
+	 * with a minimum of 4 bytes of padding */
+	padlen = blocksize - (clearwritebuf->len) % blocksize;
+	if (padlen < 4) {
+		padlen += blocksize;
+	}
+	/* check for min packet length */
+	if (clearwritebuf->len + padlen < MIN_PACKET_LEN) {
+		padlen += blocksize;
+	}
+
+	buf_setpos(clearwritebuf, 0);
+	/* packet length excluding the packetlength uint32 */
+	buf_putint(clearwritebuf, clearwritebuf->len + padlen - 4);
+
+	/* padding len */
+	buf_putbyte(clearwritebuf, padlen);
+	/* actual padding */
+	buf_setpos(clearwritebuf, clearwritebuf->len);
+	buf_incrlen(clearwritebuf, padlen);
+	genrandom(buf_getptr(clearwritebuf, padlen), padlen);
+
+	/* do the actual encryption */
+	buf_setpos(clearwritebuf, 0);
+	/* create a new writebuffer, this is freed when it has been put on the 
+	 * wire by writepacket() */
+	writebuf = buf_new(clearwritebuf->len + macsize);
+
+	if (ses.keys->trans_algo_crypt->cipherdesc == NULL) {
+		/* copy it */
+		memcpy(buf_getwriteptr(writebuf, clearwritebuf->len),
+				buf_getptr(clearwritebuf, clearwritebuf->len),
+				clearwritebuf->len);
+		buf_incrwritepos(writebuf, clearwritebuf->len);
+	} else {
+		/* encrypt it */
+		while (clearwritebuf->pos < clearwritebuf->len) {
+			if (cbc_encrypt(buf_getptr(clearwritebuf, blocksize),
+						buf_getwriteptr(writebuf, blocksize),
+						blocksize,
+						&ses.keys->trans_symmetric_struct) != CRYPT_OK) {
+				dropbear_exit("error encrypting");
+			}
+			buf_incrpos(clearwritebuf, blocksize);
+			buf_incrwritepos(writebuf, blocksize);
+		}
+	}
+
+	/* now add a hmac and we're done */
+	writemac(writebuf, clearwritebuf);
+
+	/* clearwritebuf is finished with */
+	buf_free(clearwritebuf);
+	clearwritebuf = NULL;
+
+	/* enqueue the packet for sending */
+	buf_setpos(writebuf, 0);
+	enqueue(&ses.writequeue, (void*)writebuf);
+
+	/* Update counts */
+	ses.kexstate.datatrans += writebuf->len;
+	ses.transseq++;
+
+	TRACE(("leave encrypt_packet()"))
+}
+
+
+/* Create the packet mac, and append H(seqno|clearbuf) to the output */
+static void writemac(buffer * outputbuffer, buffer * clearwritebuf) {
+
+	unsigned int macsize;
+	unsigned char seqbuf[4];
+	unsigned char tempbuf[MAX_MAC_LEN];
+	unsigned long bufsize;
+	hmac_state hmac;
+
+	TRACE(("enter writemac"))
+
+	macsize = ses.keys->trans_algo_mac->hashsize;
+	if (macsize > 0) {
+		/* calculate the mac */
+		if (hmac_init(&hmac, 
+					find_hash(ses.keys->trans_algo_mac->hashdesc->name), 
+					ses.keys->transmackey, 
+					ses.keys->trans_algo_mac->keysize) != CRYPT_OK) {
+			dropbear_exit("HMAC error");
+		}
+	
+		/* sequence number */
+		STORE32H(ses.transseq, seqbuf);
+		if (hmac_process(&hmac, seqbuf, 4) != CRYPT_OK) {
+			dropbear_exit("HMAC error");
+		}
+	
+		/* the actual contents */
+		buf_setpos(clearwritebuf, 0);
+		if (hmac_process(&hmac, 
+					buf_getptr(clearwritebuf, 
+						clearwritebuf->len),
+					clearwritebuf->len) != CRYPT_OK) {
+			dropbear_exit("HMAC error");
+		}
+	
+		bufsize = sizeof(tempbuf);
+		if (hmac_done(&hmac, tempbuf, &bufsize) 
+				!= CRYPT_OK) {
+			dropbear_exit("HMAC error");
+		}
+		buf_putbytes(outputbuffer, tempbuf, macsize);
+	}
+	TRACE(("leave writemac"))
+}
+
+#ifndef DISABLE_ZLIB
+/* compresses len bytes from src, outputting to dest (starting from the
+ * respective current positions. */
+static void buf_compress(buffer * dest, buffer * src, unsigned int len) {
+
+	unsigned int endpos = src->pos + len;
+	int result;
+
+	TRACE(("enter buf_compress"))
+
+	while (1) {
+
+		ses.keys->trans_zstream->avail_in = endpos - src->pos;
+		ses.keys->trans_zstream->next_in = 
+			buf_getptr(src, ses.keys->trans_zstream->avail_in);
+
+		ses.keys->trans_zstream->avail_out = dest->size - dest->pos;
+		ses.keys->trans_zstream->next_out =
+			buf_getwriteptr(dest, ses.keys->trans_zstream->avail_out);
+
+		result = deflate(ses.keys->trans_zstream, Z_SYNC_FLUSH);
+
+		buf_setpos(src, endpos - ses.keys->trans_zstream->avail_in);
+		buf_setlen(dest, dest->size - ses.keys->trans_zstream->avail_out);
+		buf_setpos(dest, dest->len);
+
+		if (result != Z_OK) {
+			dropbear_exit("zlib error");
+		}
+
+		if (ses.keys->trans_zstream->avail_in == 0) {
+			break;
+		}
+
+		dropbear_assert(ses.keys->trans_zstream->avail_out == 0);
+
+		/* the buffer has been filled, we must extend. This only happens in
+		 * unusual circumstances where the data grows in size after deflate(),
+		 * but it is possible */
+		buf_resize(dest, dest->size + ZLIB_COMPRESS_INCR);
+
+	}
+	TRACE(("leave buf_compress"))
+}
+#endif
diff --git a/packet.h b/packet.h
new file mode 100644
index 0000000000000000000000000000000000000000..e9768cd26354f06e71a91ec3ee361267fa2ee468
--- /dev/null
+++ b/packet.h
@@ -0,0 +1,48 @@
+/*
+ * 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 _PACKET_H_
+
+#define _PACKET_H_
+
+#include "includes.h"
+
+void write_packet();
+void read_packet();
+void decrypt_packet();
+void encrypt_packet();
+
+void process_packet();
+
+typedef struct PacketType {
+	unsigned char type; /* SSH_MSG_FOO */
+	void (*handler)();
+} packettype;
+
+#define PACKET_PADDING_OFF 4
+#define PACKET_PAYLOAD_OFF 5
+
+#define INIT_READBUF 200
+
+#endif /* _PACKET_H_ */
diff --git a/process-packet.c b/process-packet.c
new file mode 100644
index 0000000000000000000000000000000000000000..07fc13010c9464db48b332b3b4c259d0a98659fc
--- /dev/null
+++ b/process-packet.c
@@ -0,0 +1,145 @@
+/*
+ * Dropbear - a SSH2 server
+ * 
+ * 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 "packet.h"
+#include "session.h"
+#include "dbutil.h"
+#include "ssh.h"
+#include "algo.h"
+#include "buffer.h"
+#include "kex.h"
+#include "random.h"
+#include "service.h"
+#include "auth.h"
+#include "channel.h"
+
+#define MAX_UNAUTH_PACKET_TYPE SSH_MSG_USERAUTH_PK_OK
+
+static void recv_unimplemented();
+
+/* process a decrypted packet, call the appropriate handler */
+void process_packet() {
+
+	unsigned char type;
+	unsigned int i;
+
+	TRACE(("enter process_packet"))
+
+	type = buf_getbyte(ses.payload);
+	TRACE(("process_packet: packet type = %d", type))
+
+	ses.lastpacket = type;
+
+	/* These packets we can receive at any time */
+	switch(type) {
+
+		case SSH_MSG_IGNORE:
+		case SSH_MSG_DEBUG:
+			TRACE(("received SSH_MSG_IGNORE or SSH_MSG_DEBUG"))
+			goto out;
+
+		case SSH_MSG_UNIMPLEMENTED:
+			/* debugging XXX */
+			TRACE(("SSH_MSG_UNIMPLEMENTED"))
+			dropbear_exit("received SSH_MSG_UNIMPLEMENTED");
+			
+		case SSH_MSG_DISCONNECT:
+			/* TODO cleanup? */
+			dropbear_close("Disconnect received");
+	}
+
+
+	/* This applies for KEX, where the spec says the next packet MUST be
+	 * NEWKEYS */
+	if (ses.requirenext != 0) {
+		if (ses.requirenext != type) {
+			/* TODO send disconnect? */
+			dropbear_exit("unexpected packet type %d, expected %d", type,
+					ses.requirenext);
+		} else {
+			/* Got what we expected */
+			ses.requirenext = 0;
+		}
+	}
+
+	/* Check if we should ignore this packet. Used currently only for
+	 * KEX code, with first_kex_packet_follows */
+	if (ses.ignorenext) {
+		TRACE(("Ignoring packet, type = %d", type))
+		ses.ignorenext = 0;
+		goto out;
+	}
+
+
+	/* Kindly the protocol authors gave all the preauth packets type values
+	 * less-than-or-equal-to 60 ( == MAX_UNAUTH_PACKET_TYPE ).
+	 * NOTE: if the protocol changes and new types are added, revisit this 
+	 * assumption */
+	if ( !ses.authstate.authdone && type > MAX_UNAUTH_PACKET_TYPE ) {
+		dropbear_exit("received message %d before userauth", type);
+	}
+
+	for (i = 0; ; i++) {
+		if (ses.packettypes[i].type == 0) {
+			/* end of list */
+			break;
+		}
+
+		if (ses.packettypes[i].type == type) {
+			ses.packettypes[i].handler();
+			goto out;
+		}
+	}
+
+	
+	/* TODO do something more here? */
+	TRACE(("preauth unknown packet"))
+	recv_unimplemented();
+
+out:
+	buf_burn(ses.payload); /* Clear the memory to avoid swapping it out */
+	buf_free(ses.payload);
+	ses.payload = NULL;
+
+	TRACE(("leave process_packet"))
+}
+
+
+
+/* This must be called directly after receiving the unimplemented packet.
+ * Isn't the most clean implementation, it relies on packet processing
+ * occurring directly after decryption (direct use of ses.recvseq).
+ * This is reasonably valid, since there is only a single decryption buffer */
+static void recv_unimplemented() {
+
+	CHECKCLEARTOWRITE();
+
+	buf_putbyte(ses.writepayload, SSH_MSG_UNIMPLEMENTED);
+	/* the decryption routine increments the sequence number, we must
+	 * decrement */
+	buf_putint(ses.writepayload, ses.recvseq - 1);
+
+	encrypt_packet();
+}
diff --git a/progressmeter.c b/progressmeter.c
new file mode 100644
index 0000000000000000000000000000000000000000..0d856cbaf5ccba466a996cf8d723f4db97c5c78a
--- /dev/null
+++ b/progressmeter.c
@@ -0,0 +1,267 @@
+#ifdef PROGRESS_METER
+/*
+ * Copyright (c) 2003 Nils Nordman.  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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+
+#include "includes.h"
+/*RCSID("OpenBSD: progressmeter.c,v 1.15 2003/08/31 12:14:22 markus Exp ");*/
+
+#include "progressmeter.h"
+#include "atomicio.h"
+#include "scpmisc.h"
+
+#define DEFAULT_WINSIZE 80
+#define MAX_WINSIZE 512
+#define PADDING 1		/* padding between the progress indicators */
+#define UPDATE_INTERVAL 1	/* update the progress meter every second */
+#define STALL_TIME 5		/* we're stalled after this many seconds */
+
+/* determines whether we can output to the terminal */
+static int can_output(void);
+
+/* formats and inserts the specified size into the given buffer */
+static void format_size(char *, int, off_t);
+static void format_rate(char *, int, off_t);
+
+/* updates the progressmeter to reflect the current state of the transfer */
+void refresh_progress_meter(void);
+
+/* signal handler for updating the progress meter */
+static void update_progress_meter(int);
+
+static time_t start; 		/* start progress */
+static time_t last_update; 	/* last progress update */
+static char *file; 		/* name of the file being transferred */
+static off_t end_pos; 		/* ending position of transfer */
+static off_t cur_pos; 		/* transfer position as of last refresh */
+static volatile off_t *counter;	/* progress counter */
+static long stalled; 		/* how long we have been stalled */
+static int bytes_per_second; 	/* current speed in bytes per second */
+static int win_size; 		/* terminal window size */
+
+/* units for format_size */
+static const char unit[] = " KMGT";
+
+static int
+can_output(void)
+{
+	return (getpgrp() == tcgetpgrp(STDOUT_FILENO));
+}
+
+static void
+format_rate(char *buf, int size, off_t bytes)
+{
+	int i;
+
+	bytes *= 100;
+	for (i = 0; bytes >= 100*1000 && unit[i] != 'T'; i++)
+		bytes = (bytes + 512) / 1024;
+	if (i == 0) {
+		i++;
+		bytes = (bytes + 512) / 1024;
+	}
+	snprintf(buf, size, "%3lld.%1lld%c%s",
+	    (int64_t) bytes / 100,
+	    (int64_t) (bytes + 5) / 10 % 10,
+	    unit[i],
+	    i ? "B" : " ");
+}
+
+static void
+format_size(char *buf, int size, off_t bytes)
+{
+	int i;
+
+	for (i = 0; bytes >= 10000 && unit[i] != 'T'; i++)
+		bytes = (bytes + 512) / 1024;
+	snprintf(buf, size, "%4lld%c%s",
+	    (int64_t) bytes,
+	    unit[i],
+	    i ? "B" : " ");
+}
+
+void
+refresh_progress_meter(void)
+{
+	char buf[MAX_WINSIZE + 1];
+	time_t now;
+	off_t transferred;
+	double elapsed;
+	int percent;
+	int bytes_left;
+	int cur_speed;
+	int hours, minutes, seconds;
+	int i, len;
+	int file_len;
+
+	transferred = *counter - cur_pos;
+	cur_pos = *counter;
+	now = time(NULL);
+	bytes_left = end_pos - cur_pos;
+
+	if (bytes_left > 0)
+		elapsed = now - last_update;
+	else
+		elapsed = now - start;
+
+	/* calculate speed */
+	if (elapsed != 0)
+		cur_speed = (transferred / elapsed);
+	else
+		cur_speed = 0;
+
+#define AGE_FACTOR 0.9
+	if (bytes_per_second != 0) {
+		bytes_per_second = (bytes_per_second * AGE_FACTOR) +
+		    (cur_speed * (1.0 - AGE_FACTOR));
+	} else
+		bytes_per_second = cur_speed;
+
+	/* filename */
+	buf[0] = '\0';
+	file_len = win_size - 35;
+	if (file_len > 0) {
+		len = snprintf(buf, file_len + 1, "\r%s", file);
+		if (len < 0)
+			len = 0;
+		for (i = len;  i < file_len; i++ )
+			buf[i] = ' ';
+		buf[file_len] = '\0';
+	}
+
+	/* percent of transfer done */
+	if (end_pos != 0)
+		percent = ((float)cur_pos / end_pos) * 100;
+	else
+		percent = 100;
+	snprintf(buf + strlen(buf), win_size - strlen(buf),
+	    " %3d%% ", percent);
+
+	/* amount transferred */
+	format_size(buf + strlen(buf), win_size - strlen(buf),
+	    cur_pos);
+	strlcat(buf, " ", win_size);
+
+	/* bandwidth usage */
+	format_rate(buf + strlen(buf), win_size - strlen(buf),
+	    bytes_per_second);
+	strlcat(buf, "/s ", win_size);
+
+	/* ETA */
+	if (!transferred)
+		stalled += elapsed;
+	else
+		stalled = 0;
+
+	if (stalled >= STALL_TIME)
+		strlcat(buf, "- stalled -", win_size);
+	else if (bytes_per_second == 0 && bytes_left)
+		strlcat(buf, "  --:-- ETA", win_size);
+	else {
+		if (bytes_left > 0)
+			seconds = bytes_left / bytes_per_second;
+		else
+			seconds = elapsed;
+
+		hours = seconds / 3600;
+		seconds -= hours * 3600;
+		minutes = seconds / 60;
+		seconds -= minutes * 60;
+
+		if (hours != 0)
+			snprintf(buf + strlen(buf), win_size - strlen(buf),
+			    "%d:%02d:%02d", hours, minutes, seconds);
+		else
+			snprintf(buf + strlen(buf), win_size - strlen(buf),
+			    "  %02d:%02d", minutes, seconds);
+
+		if (bytes_left > 0)
+			strlcat(buf, " ETA", win_size);
+		else
+			strlcat(buf, "    ", win_size);
+	}
+
+	atomicio(vwrite, STDOUT_FILENO, buf, win_size);
+	last_update = now;
+}
+
+static void
+update_progress_meter(int ignore)
+{
+	int save_errno;
+
+	save_errno = errno;
+
+	if (can_output())
+		refresh_progress_meter();
+
+	signal(SIGALRM, update_progress_meter);
+	alarm(UPDATE_INTERVAL);
+	errno = save_errno;
+}
+
+void
+start_progress_meter(char *f, off_t filesize, off_t *stat)
+{
+	struct winsize winsize;
+
+	start = last_update = time(NULL);
+	file = f;
+	end_pos = filesize;
+	cur_pos = 0;
+	counter = stat;
+	stalled = 0;
+	bytes_per_second = 0;
+
+	if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &winsize) != -1 &&
+	    winsize.ws_col != 0) {
+		if (winsize.ws_col > MAX_WINSIZE)
+			win_size = MAX_WINSIZE;
+		else
+			win_size = winsize.ws_col;
+	} else
+		win_size = DEFAULT_WINSIZE;
+	win_size += 1;					/* trailing \0 */
+
+	if (can_output())
+		refresh_progress_meter();
+
+	signal(SIGALRM, update_progress_meter);
+	alarm(UPDATE_INTERVAL);
+}
+
+void
+stop_progress_meter(void)
+{
+	alarm(0);
+
+	if (!can_output())
+		return;
+
+	/* Ensure we complete the progress */
+	if (cur_pos != end_pos)
+		refresh_progress_meter();
+
+	atomicio(vwrite, STDOUT_FILENO, "\n", 1);
+}
+#endif /* PROGRESS_METER */
diff --git a/progressmeter.h b/progressmeter.h
new file mode 100644
index 0000000000000000000000000000000000000000..bfb9a0b770fd81a802fee0feac4fe27e4986770c
--- /dev/null
+++ b/progressmeter.h
@@ -0,0 +1,27 @@
+/*	$OpenBSD: progressmeter.h,v 1.1 2003/01/10 08:19:07 fgsch Exp $	*/
+/*
+ * Copyright (c) 2002 Nils Nordman.  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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+
+void	start_progress_meter(char *, off_t, off_t *);
+void	stop_progress_meter(void);
diff --git a/queue.c b/queue.c
new file mode 100644
index 0000000000000000000000000000000000000000..7a801240b733918175b4728282a895a3c8cd344d
--- /dev/null
+++ b/queue.c
@@ -0,0 +1,89 @@
+/*
+ * 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. */
+
+#include "includes.h"
+#include "dbutil.h"
+#include "queue.h"
+
+void initqueue(struct Queue* queue) {
+
+	queue->head = NULL;
+	queue->tail = NULL;
+	queue->count = 0;
+}
+
+int isempty(struct Queue* queue) {
+
+	return (queue->head == NULL);
+}
+	
+void* dequeue(struct Queue* queue) {
+
+	void* ret;
+	struct Link* oldhead;
+	dropbear_assert(!isempty(queue));
+	
+	ret = queue->head->item;
+	oldhead = queue->head;
+	
+	if (oldhead->link != NULL) {
+		queue->head = oldhead->link;
+	} else {
+		queue->head = NULL;
+		queue->tail = NULL;
+		TRACE(("empty queue dequeing"))
+	}
+
+	m_free(oldhead);
+	queue->count--;
+	return ret;
+}
+
+void *examine(struct Queue* queue) {
+
+	dropbear_assert(!isempty(queue));
+	return queue->head->item;
+}
+
+void enqueue(struct Queue* queue, void* item) {
+
+	struct Link* newlink;
+
+	TRACE(("enter enqueue"))
+	newlink = (struct Link*)m_malloc(sizeof(struct Link));
+
+	newlink->item = item;
+	newlink->link = NULL;
+
+	if (queue->tail != NULL) {
+		queue->tail->link = newlink;
+	}
+	queue->tail = newlink;
+
+	if (queue->head == NULL) {
+		queue->head = newlink;
+	}
+	queue->count++;
+	TRACE(("leave enqueue"))
+}
diff --git a/queue.h b/queue.h
new file mode 100644
index 0000000000000000000000000000000000000000..80fbb9d45af24e3fd8e8f39a163614eb5f4dba0d
--- /dev/null
+++ b/queue.h
@@ -0,0 +1,49 @@
+/*
+ * 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 _QUEUE_H_
+#define _QUEUE_H_
+
+struct Link {
+
+	void* item;
+	struct Link* link;
+
+};
+
+struct Queue {
+
+	struct Link* head;
+	struct Link* tail;
+	unsigned int count; /* safety value */
+
+};
+
+void initqueue(struct Queue* queue);
+int isempty(struct Queue* queue);
+void* dequeue(struct Queue* queue);
+void *examine(struct Queue* queue);
+void enqueue(struct Queue* queue, void* item);
+
+#endif
diff --git a/random.c b/random.c
new file mode 100644
index 0000000000000000000000000000000000000000..cbbe01623cd79c4efe70da29081efd8a8918f06d
--- /dev/null
+++ b/random.c
@@ -0,0 +1,241 @@
+/*
+ * 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. */
+
+#include "includes.h"
+#include "buffer.h"
+#include "dbutil.h"
+#include "bignum.h"
+
+static int donerandinit = 0;
+
+/* this is used to generate unique output from the same hashpool */
+static uint32_t counter = 0;
+#define MAX_COUNTER 1<<31 /* the max value for the counter, so it won't loop */
+
+static unsigned char hashpool[SHA1_HASH_SIZE];
+
+#define INIT_SEED_SIZE 32 /* 256 bits */
+
+static void readrand(unsigned char* buf, unsigned int buflen);
+
+/* The basic setup is we read some data from /dev/(u)random or prngd and hash it
+ * into hashpool. To read data, we hash together current hashpool contents,
+ * and a counter. We feed more data in by hashing the current pool and new
+ * data into the pool.
+ *
+ * It is important to ensure that counter doesn't wrap around before we
+ * feed in new entropy.
+ *
+ */
+
+static void readrand(unsigned char* buf, unsigned int buflen) {
+
+	static int already_blocked = 0;
+	int readfd;
+	unsigned int readpos;
+	int readlen;
+#ifdef DROPBEAR_PRNGD_SOCKET
+	struct sockaddr_un egdsock;
+	char egdcmd[2];
+#endif
+
+#ifdef DROPBEAR_RANDOM_DEV
+	readfd = open(DROPBEAR_RANDOM_DEV, O_RDONLY);
+	if (readfd < 0) {
+		dropbear_exit("couldn't open random device");
+	}
+#endif
+
+#ifdef DROPBEAR_PRNGD_SOCKET
+	memset((void*)&egdsock, 0x0, sizeof(egdsock));
+	egdsock.sun_family = AF_UNIX;
+	strlcpy(egdsock.sun_path, DROPBEAR_PRNGD_SOCKET,
+			sizeof(egdsock.sun_path));
+
+	readfd = socket(PF_UNIX, SOCK_STREAM, 0);
+	if (readfd < 0) {
+		dropbear_exit("couldn't open random device");
+	}
+	/* todo - try various common locations */
+	if (connect(readfd, (struct sockaddr*)&egdsock, 
+			sizeof(struct sockaddr_un)) < 0) {
+		dropbear_exit("couldn't open random device");
+	}
+
+	if (buflen > 255)
+		dropbear_exit("can't request more than 255 bytes from egd");
+	egdcmd[0] = 0x02;	/* blocking read */
+	egdcmd[1] = (unsigned char)buflen;
+	if (write(readfd, egdcmd, 2) < 0)
+		dropbear_exit("can't send command to egd");
+#endif
+
+	/* read the actual random data */
+	readpos = 0;
+	do {
+		if (!already_blocked)
+		{
+			int ret;
+			struct timeval timeout;
+			fd_set read_fds;
+
+			timeout.tv_sec = 2; /* two seconds should be enough */
+			timeout.tv_usec = 0;
+
+			FD_ZERO(&read_fds);
+			FD_SET(readfd, &read_fds);
+			ret = select(readfd + 1, &read_fds, NULL, NULL, &timeout);
+			if (ret == 0)
+			{
+				dropbear_log(LOG_INFO, "Warning: Reading the random source seems to have blocked.\nIf you experience problems, you probably need to find a better entropy source.");
+				already_blocked = 1;
+			}
+		}
+		readlen = read(readfd, &buf[readpos], buflen - readpos);
+		if (readlen <= 0) {
+			if (readlen < 0 && errno == EINTR) {
+				continue;
+			}
+			dropbear_exit("error reading random source");
+		}
+		readpos += readlen;
+	} while (readpos < buflen);
+
+	close (readfd);
+}
+
+/* initialise the prng from /dev/(u)random or prngd */
+void seedrandom() {
+		
+	unsigned char readbuf[INIT_SEED_SIZE];
+
+	hash_state hs;
+
+	/* initialise so that things won't warn about
+     * hashing an undefined buffer */
+	if (!donerandinit) {
+		m_burn(hashpool, sizeof(hashpool));
+	}
+
+	/* get the seed data */
+	readrand(readbuf, sizeof(readbuf));
+
+	/* hash in the new seed data */
+	sha1_init(&hs);
+	sha1_process(&hs, (void*)hashpool, sizeof(hashpool));
+	sha1_process(&hs, (void*)readbuf, sizeof(readbuf));
+	sha1_done(&hs, hashpool);
+
+	counter = 0;
+	donerandinit = 1;
+}
+
+/* hash the current random pool with some unique identifiers
+ * for this process and point-in-time. this is used to separate
+ * the random pools for fork()ed processes. */
+void reseedrandom() {
+
+    pid_t pid;
+    struct timeval tv;
+
+	if (!donerandinit) {
+		dropbear_exit("seedrandom not done");
+	}
+
+    pid = getpid();
+    gettimeofday(&tv, NULL);
+
+	hash_state hs;
+	unsigned char hash[SHA1_HASH_SIZE];
+	sha1_init(&hs);
+	sha1_process(&hs, (void*)hashpool, sizeof(hashpool));
+	sha1_process(&hs, (void*)&pid, sizeof(pid));
+	sha1_process(&hs, (void*)&tv, sizeof(tv));
+	sha1_done(&hs, hashpool);
+}
+
+/* return len bytes of pseudo-random data */
+void genrandom(unsigned char* buf, unsigned int len) {
+
+	hash_state hs;
+	unsigned char hash[SHA1_HASH_SIZE];
+	unsigned int copylen;
+
+	if (!donerandinit) {
+		dropbear_exit("seedrandom not done");
+	}
+
+	while (len > 0) {
+		sha1_init(&hs);
+		sha1_process(&hs, (void*)hashpool, sizeof(hashpool));
+		sha1_process(&hs, (void*)&counter, sizeof(counter));
+		sha1_done(&hs, hash);
+
+		counter++;
+		if (counter > MAX_COUNTER) {
+			seedrandom();
+		}
+
+		copylen = MIN(len, SHA1_HASH_SIZE);
+		memcpy(buf, hash, copylen);
+		len -= copylen;
+		buf += copylen;
+	}
+	m_burn(hash, sizeof(hash));
+}
+
+/* Generates a random mp_int. 
+ * max is a *mp_int specifying an upper bound.
+ * rand must be an initialised *mp_int for the result.
+ * the result rand satisfies:  0 < rand < max 
+ * */
+void gen_random_mpint(mp_int *max, mp_int *rand) {
+
+	unsigned char *randbuf = NULL;
+	unsigned int len = 0;
+	const char masks[] = {0xff, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f};
+
+	const int size_bits = mp_count_bits(max);
+
+	len = size_bits / 8;
+	if ((size_bits % 8) != 0) {
+		len += 1;
+	}
+
+	randbuf = (unsigned char*)m_malloc(len);
+	do {
+		genrandom(randbuf, len);
+		/* Mask out the unrequired bits - mp_read_unsigned_bin expects
+		 * MSB first.*/
+		randbuf[0] &= masks[size_bits % 8];
+
+		bytes_to_mp(rand, randbuf, len);
+
+		/* keep regenerating until we get one satisfying
+		 * 0 < rand < max    */
+	} while ( ( (max != NULL) && (mp_cmp(rand, max) != MP_LT) )
+			|| (mp_cmp_d(rand, 0) != MP_GT) );
+	m_burn(randbuf, len);
+	m_free(randbuf);
+}
diff --git a/random.h b/random.h
new file mode 100644
index 0000000000000000000000000000000000000000..84a0a39905afdce7bf36e610b22e8350c2117e6c
--- /dev/null
+++ b/random.h
@@ -0,0 +1,36 @@
+/*
+ * 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 _RANDOM_H_
+#define _RANDOM_H_
+
+struct mp_int;
+
+void seedrandom();
+void reseedrandom();
+void genrandom(unsigned char* buf, int len);
+void addrandom(unsigned char* buf, int len);
+void gen_random_mpint(mp_int *max, mp_int *rand);
+
+#endif /* _RANDOM_H_ */
diff --git a/rsa.c b/rsa.c
new file mode 100644
index 0000000000000000000000000000000000000000..005e4ca133cf144274b345545bca4b5945248657
--- /dev/null
+++ b/rsa.c
@@ -0,0 +1,398 @@
+/*
+ * 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. */
+
+/* Perform RSA operations on data, including reading keys, signing and
+ * verification.
+ *
+ * The format is specified in rfc2437, Applied Cryptography or The Handbook of
+ * Applied Cryptography detail the general algorithm. */
+
+#include "includes.h"
+#include "dbutil.h"
+#include "bignum.h"
+#include "rsa.h"
+#include "buffer.h"
+#include "ssh.h"
+#include "random.h"
+
+#ifdef DROPBEAR_RSA 
+
+static void rsa_pad_em(rsa_key * key,
+		const unsigned char * data, unsigned int len,
+		mp_int * rsa_em);
+
+/* Load a public rsa key from a buffer, initialising the values.
+ * The key will have the same format as buf_put_rsa_key.
+ * These should be freed with rsa_key_free.
+ * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
+int buf_get_rsa_pub_key(buffer* buf, rsa_key *key) {
+
+	TRACE(("enter buf_get_rsa_pub_key"))
+	dropbear_assert(key != NULL);
+	key->e = m_malloc(sizeof(mp_int));
+	key->n = m_malloc(sizeof(mp_int));
+	m_mp_init_multi(key->e, key->n, NULL);
+	key->d = NULL;
+	key->p = NULL;
+	key->q = NULL;
+
+	buf_incrpos(buf, 4+SSH_SIGNKEY_RSA_LEN); /* int + "ssh-rsa" */
+
+	if (buf_getmpint(buf, key->e) == DROPBEAR_FAILURE
+	 || buf_getmpint(buf, key->n) == DROPBEAR_FAILURE) {
+		TRACE(("leave buf_get_rsa_pub_key: failure"))
+		return DROPBEAR_FAILURE;
+	}
+
+	if (mp_count_bits(key->n) < MIN_RSA_KEYLEN) {
+		dropbear_log(LOG_WARNING, "rsa key too short");
+		return DROPBEAR_FAILURE;
+	}
+
+	TRACE(("leave buf_get_rsa_pub_key: success"))
+	return DROPBEAR_SUCCESS;
+
+}
+
+/* Same as buf_get_rsa_pub_key, but reads a private "x" key at the end.
+ * Loads a private rsa key from a buffer
+ * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
+int buf_get_rsa_priv_key(buffer* buf, rsa_key *key) {
+
+	dropbear_assert(key != NULL);
+
+	TRACE(("enter buf_get_rsa_priv_key"))
+
+	if (buf_get_rsa_pub_key(buf, key) == DROPBEAR_FAILURE) {
+		TRACE(("leave buf_get_rsa_priv_key: pub: ret == DROPBEAR_FAILURE"))
+		return DROPBEAR_FAILURE;
+	}
+
+	key->d = m_malloc(sizeof(mp_int));
+	m_mp_init(key->d);
+	if (buf_getmpint(buf, key->d) == DROPBEAR_FAILURE) {
+		TRACE(("leave buf_get_rsa_priv_key: d: ret == DROPBEAR_FAILURE"))
+		return DROPBEAR_FAILURE;
+	}
+
+	/* old Dropbear private keys didn't keep p and q, so we will ignore them*/
+	if (buf->pos == buf->len) {
+		key->p = NULL;
+		key->q = NULL;
+	} else {
+		key->p = m_malloc(sizeof(mp_int));
+		key->q = m_malloc(sizeof(mp_int));
+		m_mp_init_multi(key->p, key->q, NULL);
+
+		if (buf_getmpint(buf, key->p) == DROPBEAR_FAILURE) {
+			TRACE(("leave buf_get_rsa_priv_key: p: ret == DROPBEAR_FAILURE"))
+			return DROPBEAR_FAILURE;
+		}
+
+		if (buf_getmpint(buf, key->q) == DROPBEAR_FAILURE) {
+			TRACE(("leave buf_get_rsa_priv_key: q: ret == DROPBEAR_FAILURE"))
+			return DROPBEAR_FAILURE;
+		}
+	}
+
+	TRACE(("leave buf_get_rsa_priv_key"))
+	return DROPBEAR_SUCCESS;
+}
+	
+
+/* Clear and free the memory used by a public or private key */
+void rsa_key_free(rsa_key *key) {
+
+	TRACE(("enter rsa_key_free"))
+
+	if (key == NULL) {
+		TRACE(("leave rsa_key_free: key == NULL"))
+		return;
+	}
+	if (key->d) {
+		mp_clear(key->d);
+		m_free(key->d);
+	}
+	if (key->e) {
+		mp_clear(key->e);
+		m_free(key->e);
+	}
+	if (key->n) {
+		 mp_clear(key->n);
+		 m_free(key->n);
+	}
+	if (key->p) {
+		mp_clear(key->p);
+		m_free(key->p);
+	}
+	if (key->q) {
+		mp_clear(key->q);
+		m_free(key->q);
+	}
+	m_free(key);
+	TRACE(("leave rsa_key_free"))
+}
+
+/* Put the public rsa key into the buffer in the required format:
+ *
+ * string	"ssh-rsa"
+ * mp_int	e
+ * mp_int	n
+ */
+void buf_put_rsa_pub_key(buffer* buf, rsa_key *key) {
+
+	TRACE(("enter buf_put_rsa_pub_key"))
+	dropbear_assert(key != NULL);
+
+	buf_putstring(buf, SSH_SIGNKEY_RSA, SSH_SIGNKEY_RSA_LEN);
+	buf_putmpint(buf, key->e);
+	buf_putmpint(buf, key->n);
+
+	TRACE(("leave buf_put_rsa_pub_key"))
+
+}
+
+/* Same as buf_put_rsa_pub_key, but with the private "x" key appended */
+void buf_put_rsa_priv_key(buffer* buf, rsa_key *key) {
+
+	TRACE(("enter buf_put_rsa_priv_key"))
+
+	dropbear_assert(key != NULL);
+	buf_put_rsa_pub_key(buf, key);
+	buf_putmpint(buf, key->d);
+
+	/* new versions have p and q, old versions don't */
+	if (key->p) {
+		buf_putmpint(buf, key->p);
+	}
+	if (key->q) {
+		buf_putmpint(buf, key->q);
+	}
+
+
+	TRACE(("leave buf_put_rsa_priv_key"))
+
+}
+
+#ifdef DROPBEAR_SIGNKEY_VERIFY
+/* Verify a signature in buf, made on data by the key given.
+ * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
+int buf_rsa_verify(buffer * buf, rsa_key *key, const unsigned char* data,
+		unsigned int len) {
+
+	unsigned int slen;
+	DEF_MP_INT(rsa_s);
+	DEF_MP_INT(rsa_mdash);
+	DEF_MP_INT(rsa_em);
+	int ret = DROPBEAR_FAILURE;
+
+	TRACE(("enter buf_rsa_verify"))
+
+	dropbear_assert(key != NULL);
+
+	m_mp_init_multi(&rsa_mdash, &rsa_s, &rsa_em, NULL);
+
+	slen = buf_getint(buf);
+	if (slen != (unsigned int)mp_unsigned_bin_size(key->n)) {
+		TRACE(("bad size"))
+		goto out;
+	}
+
+	if (mp_read_unsigned_bin(&rsa_s, buf_getptr(buf, buf->len - buf->pos),
+				buf->len - buf->pos) != MP_OKAY) {
+		TRACE(("failed reading rsa_s"))
+		goto out;
+	}
+
+	/* check that s <= n-1 */
+	if (mp_cmp(&rsa_s, key->n) != MP_LT) {
+		TRACE(("s > n-1"))
+		goto out;
+	}
+
+	/* create the magic PKCS padded value */
+	rsa_pad_em(key, data, len, &rsa_em);
+
+	if (mp_exptmod(&rsa_s, key->e, key->n, &rsa_mdash) != MP_OKAY) {
+		TRACE(("failed exptmod rsa_s"))
+		goto out;
+	}
+
+	if (mp_cmp(&rsa_em, &rsa_mdash) == MP_EQ) {
+		/* signature is valid */
+		TRACE(("success!"))
+		ret = DROPBEAR_SUCCESS;
+	}
+
+out:
+	mp_clear_multi(&rsa_mdash, &rsa_s, &rsa_em, NULL);
+	TRACE(("leave buf_rsa_verify: ret %d", ret))
+	return ret;
+}
+
+#endif /* DROPBEAR_SIGNKEY_VERIFY */
+
+/* Sign the data presented with key, writing the signature contents
+ * to the buffer */
+void buf_put_rsa_sign(buffer* buf, rsa_key *key, const unsigned char* data,
+		unsigned int len) {
+
+	unsigned int nsize, ssize;
+	unsigned int i;
+	DEF_MP_INT(rsa_s);
+	DEF_MP_INT(rsa_tmp1);
+	DEF_MP_INT(rsa_tmp2);
+	DEF_MP_INT(rsa_tmp3);
+	
+	TRACE(("enter buf_put_rsa_sign"))
+	dropbear_assert(key != NULL);
+
+	m_mp_init_multi(&rsa_s, &rsa_tmp1, &rsa_tmp2, &rsa_tmp3, NULL);
+
+	rsa_pad_em(key, data, len, &rsa_tmp1);
+
+	/* the actual signing of the padded data */
+
+#ifdef RSA_BLINDING
+
+	/* With blinding, s = (r^(-1))((em)*r^e)^d mod n */
+
+	/* generate the r blinding value */
+	/* rsa_tmp2 is r */
+	gen_random_mpint(key->n, &rsa_tmp2);
+
+	/* rsa_tmp1 is em */
+	/* em' = em * r^e mod n */
+
+	mp_exptmod(&rsa_tmp2, key->e, key->n, &rsa_s); /* rsa_s used as a temp var*/
+	mp_invmod(&rsa_tmp2, key->n, &rsa_tmp3);
+	mp_mulmod(&rsa_tmp1, &rsa_s, key->n, &rsa_tmp2);
+
+	/* rsa_tmp2 is em' */
+	/* s' = (em')^d mod n */
+	mp_exptmod(&rsa_tmp2, key->d, key->n, &rsa_tmp1);
+
+	/* rsa_tmp1 is s' */
+	/* rsa_tmp3 is r^(-1) mod n */
+	/* s = (s')r^(-1) mod n */
+	mp_mulmod(&rsa_tmp1, &rsa_tmp3, key->n, &rsa_s);
+
+#else
+
+	/* s = em^d mod n */
+	/* rsa_tmp1 is em */
+	if (mp_exptmod(&rsa_tmp1, key->d, key->n, &rsa_s) != MP_OKAY) {
+		dropbear_exit("rsa error");
+	}
+
+#endif /* RSA_BLINDING */
+
+	mp_clear_multi(&rsa_tmp1, &rsa_tmp2, &rsa_tmp3, NULL);
+	
+	/* create the signature to return */
+	buf_putstring(buf, SSH_SIGNKEY_RSA, SSH_SIGNKEY_RSA_LEN);
+
+	nsize = mp_unsigned_bin_size(key->n);
+
+	/* string rsa_signature_blob length */
+	buf_putint(buf, nsize);
+	/* pad out s to same length as n */
+	ssize = mp_unsigned_bin_size(&rsa_s);
+	dropbear_assert(ssize <= nsize);
+	for (i = 0; i < nsize-ssize; i++) {
+		buf_putbyte(buf, 0x00);
+	}
+
+	if (mp_to_unsigned_bin(&rsa_s, buf_getwriteptr(buf, ssize)) != MP_OKAY) {
+		dropbear_exit("rsa error");
+	}
+	buf_incrwritepos(buf, ssize);
+	mp_clear(&rsa_s);
+
+#if defined(DEBUG_RSA) && defined(DEBUG_TRACE)
+	printhex("RSA sig", buf->data, buf->len);
+#endif
+	
+
+	TRACE(("leave buf_put_rsa_sign"))
+}
+
+/* Creates the message value as expected by PKCS, see rfc2437 etc */
+/* format to be padded to is:
+ * EM = 01 | FF* | 00 | prefix | hash
+ *
+ * where FF is repeated enough times to make EM one byte
+ * shorter than the size of key->n
+ *
+ * prefix is the ASN1 designator prefix,
+ * hex 30 21 30 09 06 05 2B 0E 03 02 1A 05 00 04 14
+ *
+ * rsa_em must be a pointer to an initialised mp_int.
+ */
+static void rsa_pad_em(rsa_key * key,
+		const unsigned char * data, unsigned int len, 
+		mp_int * rsa_em) {
+
+	/* ASN1 designator (including the 0x00 preceding) */
+	const unsigned char rsa_asn1_magic[] = 
+		{0x00, 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 
+		 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14};
+	const unsigned int RSA_ASN1_MAGIC_LEN = 16;
+
+	buffer * rsa_EM = NULL;
+	hash_state hs;
+	unsigned int nsize;
+	
+	dropbear_assert(key != NULL);
+	dropbear_assert(data != NULL);
+	nsize = mp_unsigned_bin_size(key->n);
+
+	rsa_EM = buf_new(nsize-1);
+	/* type byte */
+	buf_putbyte(rsa_EM, 0x01);
+	/* Padding with 0xFF bytes */
+	while(rsa_EM->pos != rsa_EM->size - RSA_ASN1_MAGIC_LEN - SHA1_HASH_SIZE) {
+		buf_putbyte(rsa_EM, 0xff);
+	}
+	/* Magic ASN1 stuff */
+	memcpy(buf_getwriteptr(rsa_EM, RSA_ASN1_MAGIC_LEN),
+			rsa_asn1_magic, RSA_ASN1_MAGIC_LEN);
+	buf_incrwritepos(rsa_EM, RSA_ASN1_MAGIC_LEN);
+
+	/* The hash of the data */
+	sha1_init(&hs);
+	sha1_process(&hs, data, len);
+	sha1_done(&hs, buf_getwriteptr(rsa_EM, SHA1_HASH_SIZE));
+	buf_incrwritepos(rsa_EM, SHA1_HASH_SIZE);
+
+	dropbear_assert(rsa_EM->pos == rsa_EM->size);
+
+	/* Create the mp_int from the encoded bytes */
+	buf_setpos(rsa_EM, 0);
+	bytes_to_mp(rsa_em, buf_getptr(rsa_EM, rsa_EM->size),
+			rsa_EM->size);
+	buf_free(rsa_EM);
+}
+
+#endif /* DROPBEAR_RSA */
diff --git a/rsa.h b/rsa.h
new file mode 100644
index 0000000000000000000000000000000000000000..545ba9bf99584e6433d5967e99598295c05d7a2d
--- /dev/null
+++ b/rsa.h
@@ -0,0 +1,61 @@
+/*
+ * 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 _RSA_H_
+#define _RSA_H_
+
+#include "includes.h"
+#include "buffer.h"
+
+#ifdef DROPBEAR_RSA 
+
+#define RSA_SIGNATURE_SIZE 4+7+4+40
+
+struct RSA_key {
+
+	mp_int* n;
+	mp_int* e;
+	mp_int* d;
+	mp_int* p;
+	mp_int* q;
+
+};
+
+typedef struct RSA_key rsa_key;
+
+void buf_put_rsa_sign(buffer* buf, rsa_key *key, const unsigned char* data,
+		unsigned int len);
+#ifdef DROPBEAR_SIGNKEY_VERIFY
+int buf_rsa_verify(buffer * buf, rsa_key *key, const unsigned char* data,
+		unsigned int len);
+#endif
+int buf_get_rsa_pub_key(buffer* buf, rsa_key *key);
+int buf_get_rsa_priv_key(buffer* buf, rsa_key *key);
+void buf_put_rsa_pub_key(buffer* buf, rsa_key *key);
+void buf_put_rsa_priv_key(buffer* buf, rsa_key *key);
+void rsa_key_free(rsa_key *key);
+
+#endif /* DROPBEAR_RSA */
+
+#endif /* _RSA_H_ */
diff --git a/runopts.h b/runopts.h
new file mode 100644
index 0000000000000000000000000000000000000000..5107a9dff732a05e5156ea7577924708ef441f55
--- /dev/null
+++ b/runopts.h
@@ -0,0 +1,119 @@
+/*
+ * 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 _RUNOPTS_H_
+#define _RUNOPTS_H_
+
+#include "includes.h"
+#include "signkey.h"
+#include "buffer.h"
+#include "auth.h"
+#include "tcpfwd.h"
+
+typedef struct runopts {
+
+#if defined(ENABLE_SVR_REMOTETCPFWD) || defined(ENABLE_CLI_LOCALTCPFWD)
+	int listen_fwd_all;
+#endif
+
+} runopts;
+
+extern runopts opts;
+
+int readhostkey(const char * filename, sign_key * hostkey, int *type);
+
+typedef struct svr_runopts {
+
+	char * rsakeyfile;
+	char * dsskeyfile;
+	char * bannerfile;
+
+	int forkbg;
+	int usingsyslog;
+
+	/* ports is an array of the portcount listening ports */
+	char *ports[DROPBEAR_MAX_PORTS];
+	unsigned int portcount;
+
+	int inetdmode;
+
+	/* Flags indicating whether to use ipv4 and ipv6 */
+	/* not used yet
+	int ipv4;
+	int ipv6;
+	*/
+
+#ifdef DO_MOTD
+	/* whether to print the MOTD */
+	int domotd;
+#endif
+
+	int norootlogin;
+
+	int noauthpass;
+	int norootpass;
+
+#ifdef ENABLE_SVR_REMOTETCPFWD
+	int noremotetcp;
+#endif
+#ifdef ENABLE_SVR_LOCALTCPFWD
+	int nolocaltcp;
+#endif
+
+	sign_key *hostkey;
+	buffer * banner;
+
+} svr_runopts;
+
+extern svr_runopts svr_opts;
+
+void svr_getopts(int argc, char ** argv);
+void loadhostkeys();
+
+typedef struct cli_runopts {
+
+	char *progname;
+	char *remotehost;
+	char *remoteport;
+
+	char *username;
+
+	char *cmd;
+	int wantpty;
+#ifdef ENABLE_CLI_PUBKEY_AUTH
+	struct SignKeyList *privkeys; /* Keys to use for public-key auth */
+#endif
+#ifdef ENABLE_CLI_REMOTETCPFWD
+	struct TCPFwdList * remotefwds;
+#endif
+#ifdef ENABLE_CLI_LOCALTCPFWD
+	struct TCPFwdList * localfwds;
+#endif
+
+} cli_runopts;
+
+extern cli_runopts cli_opts;
+void cli_getopts(int argc, char ** argv);
+
+#endif /* _RUNOPTS_H_ */
diff --git a/scp.c b/scp.c
new file mode 100644
index 0000000000000000000000000000000000000000..ffc4deb40ecabbd96f8d4b728467173d288ac721
--- /dev/null
+++ b/scp.c
@@ -0,0 +1,1199 @@
+/*
+ * scp - secure remote copy.  This is basically patched BSD rcp which
+ * uses ssh to do the data transfer (instead of using rcmd).
+ *
+ * NOTE: This version should NOT be suid root.  (This uses ssh to
+ * do the transfer and ssh has the necessary privileges.)
+ *
+ * 1995 Timo Rinne <tri@iki.fi>, Tatu Ylonen <ylo@cs.hut.fi>
+ *
+ * As far as I am concerned, the code I have written for this software
+ * can be used freely for any purpose.  Any derived versions of this
+ * software must be clearly marked as such, and if the derived work is
+ * incompatible with the protocol description in the RFC file, it must be
+ * called by a name other than "ssh" or "Secure Shell".
+ */
+/*
+ * Copyright (c) 1999 Theo de Raadt.  All rights reserved.
+ * Copyright (c) 1999 Aaron Campbell.  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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+
+/*
+ * Parts from:
+ *
+ * Copyright (c) 1983, 1990, 1992, 1993, 1995
+ *	The Regents of the University of California.  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 University 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 REGENTS 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 REGENTS 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.
+ *
+ */
+
+#include "includes.h"
+#include "atomicio.h"
+#include "compat.h"
+#include "scpmisc.h"
+#include "progressmeter.h"
+
+#define _PATH_CP "/bin/cp"
+
+#ifndef TIMEVAL_TO_TIMESPEC
+#define	TIMEVAL_TO_TIMESPEC(tv, ts) {					\
+	(ts)->tv_sec = (tv)->tv_sec;					\
+	(ts)->tv_nsec = (tv)->tv_usec * 1000;				\
+}
+#endif
+
+#ifndef timersub
+#define timersub(tvp, uvp, vvp)					 \
+	do {								\
+		(vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec;	  \
+		(vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec;   \
+		if ((vvp)->tv_usec < 0) {			   \
+			(vvp)->tv_sec--;				\
+			(vvp)->tv_usec += 1000000;		  \
+		}						   \
+	} while (/* CONSTCOND */ 0)
+#endif /* timersub */
+
+
+void bwlimit(int);
+
+/* Struct for addargs */
+arglist args;
+
+/* Bandwidth limit */
+off_t limitbw = 0;
+
+/* Name of current file being transferred. */
+char *curfile;
+
+/* This is set to non-zero to enable verbose mode. */
+int verbose_mode = 0;
+
+#ifdef PROGRESS_METER
+/* This is set to zero if the progressmeter is not desired. */
+int showprogress = 1;
+#endif
+
+/* This is the program to execute for the secured connection. ("ssh" or -S) */
+char *ssh_program = _PATH_SSH_PROGRAM;
+
+/* This is used to store the pid of ssh_program */
+pid_t do_cmd_pid = -1;
+
+static void
+killchild(int signo)
+{
+	if (do_cmd_pid > 1)
+		kill(do_cmd_pid, signo);
+
+	_exit(1);
+}
+
+/*
+ * This function executes the given command as the specified user on the
+ * given host.  This returns < 0 if execution fails, and >= 0 otherwise. This
+ * assigns the input and output file descriptors on success.
+ */
+
+int
+do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout, int argc)
+{
+	int pin[2], pout[2], reserved[2];
+
+	if (verbose_mode)
+		fprintf(stderr,
+		    "Executing: program %s host %s, user %s, command %s\n",
+		    ssh_program, host,
+		    remuser ? remuser : "(unspecified)", cmd);
+
+	/*
+	 * Reserve two descriptors so that the real pipes won't get
+	 * descriptors 0 and 1 because that will screw up dup2 below.
+	 */
+	pipe(reserved);
+
+	/* Create a socket pair for communicating with ssh. */
+	if (pipe(pin) < 0 || pipe(pout) < 0)
+	{
+		fprintf(stderr, "Fatal error: pipe: %s\n", strerror(errno));
+		exit(1);
+	}
+
+	/* Free the reserved descriptors. */
+	close(reserved[0]);
+	close(reserved[1]);
+
+    // uClinux needs to build the args here before vforking,
+    // otherwise we do it later on.
+#ifdef __uClinux__
+	args.list[0] = ssh_program;
+	if (remuser != NULL)
+		addargs(&args, "-l%s", remuser);
+	addargs(&args, "%s", host);
+	addargs(&args, "%s", cmd);
+#endif /* __uClinux__ */
+
+	/* Fork a child to execute the command on the remote host using ssh. */
+#ifdef __uClinux__
+	do_cmd_pid = vfork();
+#else
+	do_cmd_pid = fork();
+#endif /* __uClinux__ */
+	if (do_cmd_pid == 0) {
+		/* Child. */
+		close(pin[1]);
+		close(pout[0]);
+		dup2(pin[0], 0);
+		dup2(pout[1], 1);
+		close(pin[0]);
+		close(pout[1]);
+
+#ifndef __uClinux__
+		args.list[0] = ssh_program;
+		if (remuser != NULL) {
+			addargs(&args, "-l");
+			addargs(&args, "%s", remuser);
+		}
+		addargs(&args, "%s", host);
+		addargs(&args, "%s", cmd);
+#endif
+
+		execvp(ssh_program, args.list);
+		perror(ssh_program);
+		exit(1);
+	} else if (do_cmd_pid == -1) {
+		fprintf(stderr, "Fatal error: fork: %s\n", strerror(errno));
+		exit(1);
+	}
+
+#ifdef __uClinux__
+	/* clean up command */
+	/* pop cmd */
+	free(args->list[--args->num]);
+	args->list[args->num]=NULL;
+	/* pop host */
+	free(args->list[--args->num-1]);
+	args->list[args->num]=NULL;
+	/* pop user */
+	if (remuser != NULL) {
+		free(args->list[--args->num-1]);
+		args->list[args->num]=NULL;
+	}
+#endif /* __uClinux__
+	  
+	/* Parent.  Close the other side, and return the local side. */
+	close(pin[0]);
+	*fdout = pin[1];
+	close(pout[1]);
+	*fdin = pout[0];
+	signal(SIGTERM, killchild);
+	signal(SIGINT, killchild);
+	signal(SIGHUP, killchild);
+	return 0;
+}
+
+typedef struct {
+	int cnt;
+	char *buf;
+} BUF;
+
+BUF *allocbuf(BUF *, int, int);
+void lostconn(int);
+void nospace(void);
+int okname(char *);
+void run_err(const char *,...);
+void verifydir(char *);
+
+struct passwd *pwd;
+uid_t userid;
+int errs, remin, remout;
+int pflag, iamremote, iamrecursive, targetshouldbedirectory;
+
+#define	CMDNEEDS	64
+char cmd[CMDNEEDS];		/* must hold "rcp -r -p -d\0" */
+
+int response(void);
+void rsource(char *, struct stat *);
+void sink(int, char *[]);
+void source(int, char *[]);
+void tolocal(int, char *[]);
+void toremote(char *, int, char *[]);
+void usage(void);
+
+#if defined(DBMULTI_scp) || !defined(DROPBEAR_MULTI)
+#if defined(DBMULTI_scp) && defined(DROPBEAR_MULTI)
+int scp_main(int argc, char **argv)
+#else
+main(int argc, char **argv)
+#endif
+{
+	int ch, fflag, tflag, status;
+	double speed;
+	char *targ, *endp;
+	extern char *optarg;
+	extern int optind;
+
+	args.list = NULL;
+	addargs(&args, "ssh");		/* overwritten with ssh_program */
+	addargs(&args, "-x");
+	addargs(&args, "-oForwardAgent no");
+	addargs(&args, "-oClearAllForwardings yes");
+
+	fflag = tflag = 0;
+	while ((ch = getopt(argc, argv, "dfl:prtvBCc:i:P:q1246S:o:F:")) != -1)
+		switch (ch) {
+		/* User-visible flags. */
+		case '1':
+		case '2':
+		case '4':
+		case '6':
+		case 'C':
+			addargs(&args, "-%c", ch);
+			break;
+		case 'o':
+		case 'c':
+		case 'i':
+		case 'F':
+			addargs(&args, "-%c%s", ch, optarg);
+			break;
+		case 'P':
+			addargs(&args, "-p%s", optarg);
+			break;
+		case 'B':
+			addargs(&args, "-oBatchmode yes");
+			break;
+		case 'l':
+			speed = strtod(optarg, &endp);
+			if (speed <= 0 || *endp != '\0')
+				usage();
+			limitbw = speed * 1024;
+			break;
+		case 'p':
+			pflag = 1;
+			break;
+		case 'r':
+			iamrecursive = 1;
+			break;
+		case 'S':
+			ssh_program = xstrdup(optarg);
+			break;
+		case 'v':
+			addargs(&args, "-v");
+			verbose_mode = 1;
+			break;
+#ifdef PROGRESS_METER
+		case 'q':
+			showprogress = 0;
+			break;
+#endif
+
+		/* Server options. */
+		case 'd':
+			targetshouldbedirectory = 1;
+			break;
+		case 'f':	/* "from" */
+			iamremote = 1;
+			fflag = 1;
+			break;
+		case 't':	/* "to" */
+			iamremote = 1;
+			tflag = 1;
+#ifdef HAVE_CYGWIN
+			setmode(0, O_BINARY);
+#endif
+			break;
+		default:
+			usage();
+		}
+	argc -= optind;
+	argv += optind;
+
+	if ((pwd = getpwuid(userid = getuid())) == NULL) {
+		fprintf(stderr, "unknown user %u", (u_int) userid);
+	}
+
+#ifdef PROGRESS_METER
+	if (!isatty(STDERR_FILENO))
+		showprogress = 0;
+#endif
+
+	remin = STDIN_FILENO;
+	remout = STDOUT_FILENO;
+
+	if (fflag) {
+		/* Follow "protocol", send data. */
+		(void) response();
+		source(argc, argv);
+		exit(errs != 0);
+	}
+	if (tflag) {
+		/* Receive data. */
+		sink(argc, argv);
+		exit(errs != 0);
+	}
+	if (argc < 2)
+		usage();
+	if (argc > 2)
+		targetshouldbedirectory = 1;
+
+	remin = remout = -1;
+	do_cmd_pid = -1;
+	/* Command to be executed on remote system using "ssh". */
+	(void) snprintf(cmd, sizeof cmd, "scp%s%s%s%s",
+	    verbose_mode ? " -v" : "",
+	    iamrecursive ? " -r" : "", pflag ? " -p" : "",
+	    targetshouldbedirectory ? " -d" : "");
+
+	(void) signal(SIGPIPE, lostconn);
+
+	if ((targ = colon(argv[argc - 1])))	/* Dest is remote host. */
+		toremote(targ, argc, argv);
+	else {
+		tolocal(argc, argv);	/* Dest is local host. */
+		if (targetshouldbedirectory)
+			verifydir(argv[argc - 1]);
+	}
+	/*
+	 * Finally check the exit status of the ssh process, if one was forked
+	 * and no error has occured yet
+	 */
+	if (do_cmd_pid != -1 && errs == 0) {
+		if (remin != -1)
+		    (void) close(remin);
+		if (remout != -1)
+		    (void) close(remout);
+		if (waitpid(do_cmd_pid, &status, 0) == -1)
+			errs = 1;
+		else {
+			if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
+				errs = 1;
+		}
+	}
+	exit(errs != 0);
+}
+#endif /* DBMULTI stuff */
+
+void
+toremote(char *targ, int argc, char **argv)
+{
+	int i, len;
+	char *bp, *host, *src, *suser, *thost, *tuser;
+
+	*targ++ = 0;
+	if (*targ == 0)
+		targ = ".";
+
+	if ((thost = strrchr(argv[argc - 1], '@'))) {
+		/* user@host */
+		*thost++ = 0;
+		tuser = argv[argc - 1];
+		if (*tuser == '\0')
+			tuser = NULL;
+	} else {
+		thost = argv[argc - 1];
+		tuser = NULL;
+	}
+
+	for (i = 0; i < argc - 1; i++) {
+		src = colon(argv[i]);
+		if (src) {	/* remote to remote */
+			static char *ssh_options =
+			    "-x -o'ClearAllForwardings yes'";
+			*src++ = 0;
+			if (*src == 0)
+				src = ".";
+			host = strrchr(argv[i], '@');
+			len = strlen(ssh_program) + strlen(argv[i]) +
+			    strlen(src) + (tuser ? strlen(tuser) : 0) +
+			    strlen(thost) + strlen(targ) +
+			    strlen(ssh_options) + CMDNEEDS + 20;
+			bp = xmalloc(len);
+			if (host) {
+				*host++ = 0;
+				host = cleanhostname(host);
+				suser = argv[i];
+				if (*suser == '\0')
+					suser = pwd->pw_name;
+				else if (!okname(suser)) {
+					xfree(bp);
+					continue;
+				}
+				if (tuser && !okname(tuser)) {
+					xfree(bp);
+					continue;
+				}
+				snprintf(bp, len,
+				    "%s%s %s -n "
+				    "-l %s %s %s %s '%s%s%s:%s'",
+				    ssh_program, verbose_mode ? " -v" : "",
+				    ssh_options, suser, host, cmd, src,
+				    tuser ? tuser : "", tuser ? "@" : "",
+				    thost, targ);
+			} else {
+				host = cleanhostname(argv[i]);
+				snprintf(bp, len,
+				    "exec %s%s %s -n %s "
+				    "%s %s '%s%s%s:%s'",
+				    ssh_program, verbose_mode ? " -v" : "",
+				    ssh_options, host, cmd, src,
+				    tuser ? tuser : "", tuser ? "@" : "",
+				    thost, targ);
+			}
+			if (verbose_mode)
+				fprintf(stderr, "Executing: %s\n", bp);
+			(void) system(bp);
+			(void) xfree(bp);
+		} else {	/* local to remote */
+			if (remin == -1) {
+				len = strlen(targ) + CMDNEEDS + 20;
+				bp = xmalloc(len);
+				(void) snprintf(bp, len, "%s -t %s", cmd, targ);
+				host = cleanhostname(thost);
+				if (do_cmd(host, tuser, bp, &remin,
+				    &remout, argc) < 0)
+					exit(1);
+				if (response() < 0)
+					exit(1);
+				(void) xfree(bp);
+			}
+			source(1, argv + i);
+		}
+	}
+}
+
+void
+tolocal(int argc, char **argv)
+{
+	int i, len;
+	char *bp, *host, *src, *suser;
+
+	for (i = 0; i < argc - 1; i++) {
+		if (!(src = colon(argv[i]))) {	/* Local to local. */
+			len = strlen(_PATH_CP) + strlen(argv[i]) +
+			    strlen(argv[argc - 1]) + 20;
+			bp = xmalloc(len);
+			(void) snprintf(bp, len, "exec %s%s%s %s %s", _PATH_CP,
+			    iamrecursive ? " -r" : "", pflag ? " -p" : "",
+			    argv[i], argv[argc - 1]);
+			if (verbose_mode)
+				fprintf(stderr, "Executing: %s\n", bp);
+			if (system(bp))
+				++errs;
+			(void) xfree(bp);
+			continue;
+		}
+		*src++ = 0;
+		if (*src == 0)
+			src = ".";
+		if ((host = strrchr(argv[i], '@')) == NULL) {
+			host = argv[i];
+			suser = NULL;
+		} else {
+			*host++ = 0;
+			suser = argv[i];
+			if (*suser == '\0')
+				suser = pwd->pw_name;
+		}
+		host = cleanhostname(host);
+		len = strlen(src) + CMDNEEDS + 20;
+		bp = xmalloc(len);
+		(void) snprintf(bp, len, "%s -f %s", cmd, src);
+		if (do_cmd(host, suser, bp, &remin, &remout, argc) < 0) {
+			(void) xfree(bp);
+			++errs;
+			continue;
+		}
+		xfree(bp);
+		sink(1, argv + argc - 1);
+		(void) close(remin);
+		remin = remout = -1;
+	}
+}
+
+void
+source(int argc, char **argv)
+{
+	struct stat stb;
+	static BUF buffer;
+	BUF *bp;
+	off_t i, amt, result, statbytes;
+	int fd, haderr, indx;
+	char *last, *name, buf[2048];
+	int len;
+
+	for (indx = 0; indx < argc; ++indx) {
+		name = argv[indx];
+		statbytes = 0;
+		len = strlen(name);
+		while (len > 1 && name[len-1] == '/')
+			name[--len] = '\0';
+		if (strchr(name, '\n') != NULL) {
+			run_err("%s: skipping, filename contains a newline",
+			    name);
+			goto next;
+		}
+		if ((fd = open(name, O_RDONLY, 0)) < 0)
+			goto syserr;
+		if (fstat(fd, &stb) < 0) {
+syserr:			run_err("%s: %s", name, strerror(errno));
+			goto next;
+		}
+		switch (stb.st_mode & S_IFMT) {
+		case S_IFREG:
+			break;
+		case S_IFDIR:
+			if (iamrecursive) {
+				rsource(name, &stb);
+				goto next;
+			}
+			/* FALLTHROUGH */
+		default:
+			run_err("%s: not a regular file", name);
+			goto next;
+		}
+		if ((last = strrchr(name, '/')) == NULL)
+			last = name;
+		else
+			++last;
+		curfile = last;
+		if (pflag) {
+			/*
+			 * Make it compatible with possible future
+			 * versions expecting microseconds.
+			 */
+			(void) snprintf(buf, sizeof buf, "T%lu 0 %lu 0\n",
+			    (u_long) stb.st_mtime,
+			    (u_long) stb.st_atime);
+			(void) atomicio(vwrite, remout, buf, strlen(buf));
+			if (response() < 0)
+				goto next;
+		}
+#define	FILEMODEMASK	(S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO)
+		snprintf(buf, sizeof buf, "C%04o %lld %s\n",
+		    (u_int) (stb.st_mode & FILEMODEMASK),
+		    (int64_t)stb.st_size, last);
+		if (verbose_mode) {
+			fprintf(stderr, "Sending file modes: %s", buf);
+		}
+		(void) atomicio(vwrite, remout, buf, strlen(buf));
+		if (response() < 0)
+			goto next;
+		if ((bp = allocbuf(&buffer, fd, 2048)) == NULL) {
+next:			(void) close(fd);
+			continue;
+		}
+#ifdef PROGRESS_METER
+		if (showprogress)
+			start_progress_meter(curfile, stb.st_size, &statbytes);
+#endif
+		/* Keep writing after an error so that we stay sync'd up. */
+		for (haderr = i = 0; i < stb.st_size; i += bp->cnt) {
+			amt = bp->cnt;
+			if (i + amt > stb.st_size)
+				amt = stb.st_size - i;
+			if (!haderr) {
+				result = atomicio(read, fd, bp->buf, amt);
+				if (result != amt)
+					haderr = result >= 0 ? EIO : errno;
+			}
+			if (haderr)
+				(void) atomicio(vwrite, remout, bp->buf, amt);
+			else {
+				result = atomicio(vwrite, remout, bp->buf, amt);
+				if (result != amt)
+					haderr = result >= 0 ? EIO : errno;
+				statbytes += result;
+			}
+			if (limitbw)
+				bwlimit(amt);
+		}
+#ifdef PROGRESS_METER
+		if (showprogress)
+			stop_progress_meter();
+#endif
+
+		if (close(fd) < 0 && !haderr)
+			haderr = errno;
+		if (!haderr)
+			(void) atomicio(vwrite, remout, "", 1);
+		else
+			run_err("%s: %s", name, strerror(haderr));
+		(void) response();
+	}
+}
+
+void
+rsource(char *name, struct stat *statp)
+{
+	DIR *dirp;
+	struct dirent *dp;
+	char *last, *vect[1], path[1100];
+
+	if (!(dirp = opendir(name))) {
+		run_err("%s: %s", name, strerror(errno));
+		return;
+	}
+	last = strrchr(name, '/');
+	if (last == 0)
+		last = name;
+	else
+		last++;
+	if (pflag) {
+		(void) snprintf(path, sizeof(path), "T%lu 0 %lu 0\n",
+		    (u_long) statp->st_mtime,
+		    (u_long) statp->st_atime);
+		(void) atomicio(vwrite, remout, path, strlen(path));
+		if (response() < 0) {
+			closedir(dirp);
+			return;
+		}
+	}
+	(void) snprintf(path, sizeof path, "D%04o %d %.1024s\n",
+	    (u_int) (statp->st_mode & FILEMODEMASK), 0, last);
+	if (verbose_mode)
+		fprintf(stderr, "Entering directory: %s", path);
+	(void) atomicio(vwrite, remout, path, strlen(path));
+	if (response() < 0) {
+		closedir(dirp);
+		return;
+	}
+	while ((dp = readdir(dirp)) != NULL) {
+		if (dp->d_ino == 0)
+			continue;
+		if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
+			continue;
+		if (strlen(name) + 1 + strlen(dp->d_name) >= sizeof(path) - 1) {
+			run_err("%s/%s: name too long", name, dp->d_name);
+			continue;
+		}
+		(void) snprintf(path, sizeof path, "%s/%s", name, dp->d_name);
+		vect[0] = path;
+		source(1, vect);
+	}
+	(void) closedir(dirp);
+	(void) atomicio(vwrite, remout, "E\n", 2);
+	(void) response();
+}
+
+void
+bwlimit(int amount)
+{
+	static struct timeval bwstart, bwend;
+	static int lamt, thresh = 16384;
+	uint64_t wait;
+	struct timespec ts, rm;
+
+	if (!timerisset(&bwstart)) {
+		gettimeofday(&bwstart, NULL);
+		return;
+	}
+
+	lamt += amount;
+	if (lamt < thresh)
+		return;
+
+	gettimeofday(&bwend, NULL);
+	timersub(&bwend, &bwstart, &bwend);
+	if (!timerisset(&bwend))
+		return;
+
+	lamt *= 8;
+	wait = (double)1000000L * lamt / limitbw;
+
+	bwstart.tv_sec = wait / 1000000L;
+	bwstart.tv_usec = wait % 1000000L;
+
+	if (timercmp(&bwstart, &bwend, >)) {
+		timersub(&bwstart, &bwend, &bwend);
+
+		/* Adjust the wait time */
+		if (bwend.tv_sec) {
+			thresh /= 2;
+			if (thresh < 2048)
+				thresh = 2048;
+		} else if (bwend.tv_usec < 100) {
+			thresh *= 2;
+			if (thresh > 32768)
+				thresh = 32768;
+		}
+
+		TIMEVAL_TO_TIMESPEC(&bwend, &ts);
+		while (nanosleep(&ts, &rm) == -1) {
+			if (errno != EINTR)
+				break;
+			ts = rm;
+		}
+	}
+
+	lamt = 0;
+	gettimeofday(&bwstart, NULL);
+}
+
+void
+sink(int argc, char **argv)
+{
+	static BUF buffer;
+	struct stat stb;
+	enum {
+		YES, NO, DISPLAYED
+	} wrerr;
+	BUF *bp;
+	off_t i, j;
+	int amt, count, exists, first, mask, mode, ofd, omode;
+	off_t size, statbytes;
+	int setimes, targisdir, wrerrno = 0;
+	char ch, *cp, *np, *targ, *why, *vect[1], buf[2048];
+	struct timeval tv[2];
+
+#define	atime	tv[0]
+#define	mtime	tv[1]
+#define	SCREWUP(str)	do { why = str; goto screwup; } while (0)
+
+	setimes = targisdir = 0;
+	mask = umask(0);
+	if (!pflag)
+		(void) umask(mask);
+	if (argc != 1) {
+		run_err("ambiguous target");
+		exit(1);
+	}
+	targ = *argv;
+	if (targetshouldbedirectory)
+		verifydir(targ);
+
+	(void) atomicio(vwrite, remout, "", 1);
+	if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode))
+		targisdir = 1;
+	for (first = 1;; first = 0) {
+		cp = buf;
+		if (atomicio(read, remin, cp, 1) <= 0)
+			return;
+		if (*cp++ == '\n')
+			SCREWUP("unexpected <newline>");
+		do {
+			if (atomicio(read, remin, &ch, sizeof(ch)) != sizeof(ch))
+				SCREWUP("lost connection");
+			*cp++ = ch;
+		} while (cp < &buf[sizeof(buf) - 1] && ch != '\n');
+		*cp = 0;
+
+		if (buf[0] == '\01' || buf[0] == '\02') {
+			if (iamremote == 0)
+				(void) atomicio(vwrite, STDERR_FILENO,
+				    buf + 1, strlen(buf + 1));
+			if (buf[0] == '\02')
+				exit(1);
+			++errs;
+			continue;
+		}
+		if (buf[0] == 'E') {
+			(void) atomicio(vwrite, remout, "", 1);
+			return;
+		}
+		if (ch == '\n')
+			*--cp = 0;
+
+		cp = buf;
+		if (*cp == 'T') {
+			setimes++;
+			cp++;
+			mtime.tv_sec = strtol(cp, &cp, 10);
+			if (!cp || *cp++ != ' ')
+				SCREWUP("mtime.sec not delimited");
+			mtime.tv_usec = strtol(cp, &cp, 10);
+			if (!cp || *cp++ != ' ')
+				SCREWUP("mtime.usec not delimited");
+			atime.tv_sec = strtol(cp, &cp, 10);
+			if (!cp || *cp++ != ' ')
+				SCREWUP("atime.sec not delimited");
+			atime.tv_usec = strtol(cp, &cp, 10);
+			if (!cp || *cp++ != '\0')
+				SCREWUP("atime.usec not delimited");
+			(void) atomicio(vwrite, remout, "", 1);
+			continue;
+		}
+		if (*cp != 'C' && *cp != 'D') {
+			/*
+			 * Check for the case "rcp remote:foo\* local:bar".
+			 * In this case, the line "No match." can be returned
+			 * by the shell before the rcp command on the remote is
+			 * executed so the ^Aerror_message convention isn't
+			 * followed.
+			 */
+			if (first) {
+				run_err("%s", cp);
+				exit(1);
+			}
+			SCREWUP("expected control record");
+		}
+		mode = 0;
+		for (++cp; cp < buf + 5; cp++) {
+			if (*cp < '0' || *cp > '7')
+				SCREWUP("bad mode");
+			mode = (mode << 3) | (*cp - '0');
+		}
+		if (*cp++ != ' ')
+			SCREWUP("mode not delimited");
+
+		for (size = 0; isdigit(*cp);)
+			size = size * 10 + (*cp++ - '0');
+		if (*cp++ != ' ')
+			SCREWUP("size not delimited");
+		if (targisdir) {
+			static char *namebuf;
+			static int cursize;
+			size_t need;
+
+			need = strlen(targ) + strlen(cp) + 250;
+			if (need > cursize) {
+				if (namebuf)
+					xfree(namebuf);
+				namebuf = xmalloc(need);
+				cursize = need;
+			}
+			(void) snprintf(namebuf, need, "%s%s%s", targ,
+			    strcmp(targ, "/") ? "/" : "", cp);
+			np = namebuf;
+		} else
+			np = targ;
+		curfile = cp;
+		exists = stat(np, &stb) == 0;
+		if (buf[0] == 'D') {
+			int mod_flag = pflag;
+			if (exists) {
+				if (!S_ISDIR(stb.st_mode)) {
+					errno = ENOTDIR;
+					goto bad;
+				}
+				if (pflag)
+					(void) chmod(np, mode);
+			} else {
+				/* Handle copying from a read-only
+				   directory */
+				mod_flag = 1;
+				if (mkdir(np, mode | S_IRWXU) < 0)
+					goto bad;
+			}
+			vect[0] = xstrdup(np);
+			sink(1, vect);
+			if (setimes) {
+				setimes = 0;
+				if (utimes(vect[0], tv) < 0)
+					run_err("%s: set times: %s",
+					    vect[0], strerror(errno));
+			}
+			if (mod_flag)
+				(void) chmod(vect[0], mode);
+			if (vect[0])
+				xfree(vect[0]);
+			continue;
+		}
+		omode = mode;
+		mode |= S_IWRITE;
+		if ((ofd = open(np, O_WRONLY|O_CREAT, mode)) < 0) {
+bad:			run_err("%s: %s", np, strerror(errno));
+			continue;
+		}
+		(void) atomicio(vwrite, remout, "", 1);
+		if ((bp = allocbuf(&buffer, ofd, 4096)) == NULL) {
+			(void) close(ofd);
+			continue;
+		}
+		cp = bp->buf;
+		wrerr = NO;
+
+		statbytes = 0;
+#ifdef PROGRESS_METER
+		if (showprogress)
+			start_progress_meter(curfile, size, &statbytes);
+#endif
+		for (count = i = 0; i < size; i += 4096) {
+			amt = 4096;
+			if (i + amt > size)
+				amt = size - i;
+			count += amt;
+			do {
+				j = read(remin, cp, amt);
+				if (j == -1 && (errno == EINTR ||
+				    errno == EAGAIN)) {
+					continue;
+				} else if (j <= 0) {
+					run_err("%s", j ? strerror(errno) :
+					    "dropped connection");
+					exit(1);
+				}
+				amt -= j;
+				cp += j;
+				statbytes += j;
+			} while (amt > 0);
+		
+			if (limitbw)
+				bwlimit(4096);
+
+			if (count == bp->cnt) {
+				/* Keep reading so we stay sync'd up. */
+				if (wrerr == NO) {
+					j = atomicio(vwrite, ofd, bp->buf, count);
+					if (j != count) {
+						wrerr = YES;
+						wrerrno = j >= 0 ? EIO : errno;
+					}
+				}
+				count = 0;
+				cp = bp->buf;
+			}
+		}
+#ifdef PROGRESS_METER
+		if (showprogress)
+			stop_progress_meter();
+#endif
+		if (count != 0 && wrerr == NO &&
+		    (j = atomicio(vwrite, ofd, bp->buf, count)) != count) {
+			wrerr = YES;
+			wrerrno = j >= 0 ? EIO : errno;
+		}
+		if (wrerr == NO && ftruncate(ofd, size) != 0) {
+			run_err("%s: truncate: %s", np, strerror(errno));
+			wrerr = DISPLAYED;
+		}
+		if (pflag) {
+			if (exists || omode != mode)
+#ifdef HAVE_FCHMOD
+				if (fchmod(ofd, omode))
+#else /* HAVE_FCHMOD */
+				if (chmod(np, omode))
+#endif /* HAVE_FCHMOD */
+					run_err("%s: set mode: %s",
+					    np, strerror(errno));
+		} else {
+			if (!exists && omode != mode)
+#ifdef HAVE_FCHMOD
+				if (fchmod(ofd, omode & ~mask))
+#else /* HAVE_FCHMOD */
+				if (chmod(np, omode & ~mask))
+#endif /* HAVE_FCHMOD */
+					run_err("%s: set mode: %s",
+					    np, strerror(errno));
+		}
+		if (close(ofd) == -1) {
+			wrerr = YES;
+			wrerrno = errno;
+		}
+		(void) response();
+		if (setimes && wrerr == NO) {
+			setimes = 0;
+			if (utimes(np, tv) < 0) {
+				run_err("%s: set times: %s",
+				    np, strerror(errno));
+				wrerr = DISPLAYED;
+			}
+		}
+		switch (wrerr) {
+		case YES:
+			run_err("%s: %s", np, strerror(wrerrno));
+			break;
+		case NO:
+			(void) atomicio(vwrite, remout, "", 1);
+			break;
+		case DISPLAYED:
+			break;
+		}
+	}
+screwup:
+	run_err("protocol error: %s", why);
+	exit(1);
+}
+
+int
+response(void)
+{
+	char ch, *cp, resp, rbuf[2048];
+
+	if (atomicio(read, remin, &resp, sizeof(resp)) != sizeof(resp))
+		lostconn(0);
+
+	cp = rbuf;
+	switch (resp) {
+	case 0:		/* ok */
+		return (0);
+	default:
+		*cp++ = resp;
+		/* FALLTHROUGH */
+	case 1:		/* error, followed by error msg */
+	case 2:		/* fatal error, "" */
+		do {
+			if (atomicio(read, remin, &ch, sizeof(ch)) != sizeof(ch))
+				lostconn(0);
+			*cp++ = ch;
+		} while (cp < &rbuf[sizeof(rbuf) - 1] && ch != '\n');
+
+		if (!iamremote)
+			(void) atomicio(vwrite, STDERR_FILENO, rbuf, cp - rbuf);
+		++errs;
+		if (resp == 1)
+			return (-1);
+		exit(1);
+	}
+	/* NOTREACHED */
+}
+
+void
+usage(void)
+{
+	(void) fprintf(stderr,
+	    "usage: scp [-pqrvBC1246] [-F config] [-S program] [-P port]\n"
+	    "           [-c cipher] [-i identity] [-l limit] [-o option]\n"
+	    "           [[user@]host1:]file1 [...] [[user@]host2:]file2\n");
+	exit(1);
+}
+
+void
+run_err(const char *fmt,...)
+{
+	static FILE *fp;
+	va_list ap;
+
+	++errs;
+	if (fp == NULL && !(fp = fdopen(remout, "w")))
+		return;
+	(void) fprintf(fp, "%c", 0x01);
+	(void) fprintf(fp, "scp: ");
+	va_start(ap, fmt);
+	(void) vfprintf(fp, fmt, ap);
+	va_end(ap);
+	(void) fprintf(fp, "\n");
+	(void) fflush(fp);
+
+	if (!iamremote) {
+		va_start(ap, fmt);
+		vfprintf(stderr, fmt, ap);
+		va_end(ap);
+		fprintf(stderr, "\n");
+	}
+}
+
+void
+verifydir(char *cp)
+{
+	struct stat stb;
+
+	if (!stat(cp, &stb)) {
+		if (S_ISDIR(stb.st_mode))
+			return;
+		errno = ENOTDIR;
+	}
+	run_err("%s: %s", cp, strerror(errno));
+	exit(1);
+}
+
+int
+okname(char *cp0)
+{
+	int c;
+	char *cp;
+
+	cp = cp0;
+	do {
+		c = (int)*cp;
+		if (c & 0200)
+			goto bad;
+		if (!isalpha(c) && !isdigit(c)) {
+			switch (c) {
+			case '\'':
+			case '"':
+			case '`':
+			case ' ':
+			case '#':
+				goto bad;
+			default:
+				break;
+			}
+		}
+	} while (*++cp);
+	return (1);
+
+bad:	fprintf(stderr, "%s: invalid user name\n", cp0);
+	return (0);
+}
+
+BUF *
+allocbuf(BUF *bp, int fd, int blksize)
+{
+	size_t size;
+#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
+	struct stat stb;
+
+	if (fstat(fd, &stb) < 0) {
+		run_err("fstat: %s", strerror(errno));
+		return (0);
+	}
+	size = roundup(stb.st_blksize, blksize);
+	if (size == 0)
+		size = blksize;
+#else /* HAVE_STRUCT_STAT_ST_BLKSIZE */
+	size = blksize;
+#endif /* HAVE_STRUCT_STAT_ST_BLKSIZE */
+	if (bp->cnt >= size)
+		return (bp);
+	if (bp->buf == NULL)
+		bp->buf = xmalloc(size);
+	else
+		bp->buf = xrealloc(bp->buf, size);
+	memset(bp->buf, 0, size);
+	bp->cnt = size;
+	return (bp);
+}
+
+void
+lostconn(int signo)
+{
+	if (!iamremote)
+		write(STDERR_FILENO, "lost connection\n", 16);
+	if (signo)
+		_exit(1);
+	else
+		exit(1);
+}
diff --git a/scpmisc.c b/scpmisc.c
new file mode 100644
index 0000000000000000000000000000000000000000..e15bf4db7df58450cf1700696d63a56345ec2576
--- /dev/null
+++ b/scpmisc.c
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 2000 Markus Friedl.  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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+
+/*RCSID("OpenBSD: misc.c,v 1.22 2003/09/18 08:49:45 markus Exp ");*/
+
+/* For xmalloc, xfree etc:
+ * Author: Tatu Ylonen <ylo@cs.hut.fi>
+ * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+ *                    All rights reserved
+ * Versions of malloc and friends that check their results, and never return
+ * failure (they call fatal if they encounter an error).
+ *
+ * As far as I am concerned, the code I have written for this software
+ * can be used freely for any purpose.  Any derived versions of this
+ * software must be clearly marked as such, and if the derived work is
+ * incompatible with the protocol description in the RFC file, it must be
+ * called by a name other than "ssh" or "Secure Shell".
+ */
+
+/*RCSID("OpenBSD: xmalloc.c,v 1.16 2001/07/23 18:21:46 stevesk Exp ");*/
+
+#include "includes.h"
+#include "scpmisc.h"
+
+void *
+xmalloc(size_t size)
+{
+	void *ptr;
+
+	if (size == 0) {
+		fprintf(stderr, "xmalloc: zero size\n");
+		exit(EXIT_FAILURE);
+	}
+	ptr = malloc(size);
+	if (ptr == NULL) {
+		fprintf(stderr, "xmalloc: out of memory (allocating %lu bytes)\n", (u_long) size);
+		exit(EXIT_FAILURE);
+	}
+	return ptr;
+}
+
+void *
+xrealloc(void *ptr, size_t new_size)
+{
+	void *new_ptr;
+
+	if (new_size == 0) {
+		fprintf(stderr, "xrealloc: zero size\n");
+		exit(EXIT_FAILURE);
+	}
+	if (ptr == NULL)
+		new_ptr = malloc(new_size);
+	else
+		new_ptr = realloc(ptr, new_size);
+	if (new_ptr == NULL) {
+		fprintf(stderr, "xrealloc: out of memory (new_size %lu bytes)\n", (u_long) new_size);
+		exit(EXIT_FAILURE);
+	}
+	return new_ptr;
+}
+
+void
+xfree(void *ptr)
+{
+	if (ptr == NULL) {
+		fprintf(stderr, "xfree: NULL pointer given as argument\n");
+		exit(EXIT_FAILURE);
+	}
+	free(ptr);
+}
+
+char *
+xstrdup(const char *str)
+{
+	size_t len;
+	char *cp;
+
+	len = strlen(str) + 1;
+	cp = xmalloc(len);
+	strncpy(cp, str, len);
+	return cp;
+}
+
+char *
+cleanhostname(char *host)
+{
+	if (*host == '[' && host[strlen(host) - 1] == ']') {
+		host[strlen(host) - 1] = '\0';
+		return (host + 1);
+	} else
+		return host;
+}
+
+char *
+colon(char *cp)
+{
+	int flag = 0;
+
+	if (*cp == ':')		/* Leading colon is part of file name. */
+		return (0);
+	if (*cp == '[')
+		flag = 1;
+
+	for (; *cp; ++cp) {
+		if (*cp == '@' && *(cp+1) == '[')
+			flag = 1;
+		if (*cp == ']' && *(cp+1) == ':' && flag)
+			return (cp+1);
+		if (*cp == ':' && !flag)
+			return (cp);
+		if (*cp == '/')
+			return (0);
+	}
+	return (0);
+}
+
+/* function to assist building execv() arguments */
+void
+addargs(arglist *args, char *fmt, ...)
+{
+	va_list ap;
+	char buf[1024];
+	int nalloc;
+
+	va_start(ap, fmt);
+	vsnprintf(buf, sizeof(buf), fmt, ap);
+	va_end(ap);
+
+	nalloc = args->nalloc;
+	if (args->list == NULL) {
+		nalloc = 32;
+		args->num = 0;
+	} else if (args->num+2 >= nalloc)
+		nalloc *= 2;
+
+	args->list = xrealloc(args->list, nalloc * sizeof(char *));
+	args->nalloc = nalloc;
+	args->list[args->num++] = xstrdup(buf);
+	args->list[args->num] = NULL;
+}
diff --git a/scpmisc.h b/scpmisc.h
new file mode 100644
index 0000000000000000000000000000000000000000..b6059c6a2fe83902459882e759d34601bca3ccfd
--- /dev/null
+++ b/scpmisc.h
@@ -0,0 +1,44 @@
+/*	$OpenBSD: misc.h,v 1.12 2002/03/19 10:49:35 markus Exp $	*/
+
+/*
+ * Author: Tatu Ylonen <ylo@cs.hut.fi>
+ * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+ *                    All rights reserved
+ *
+ * As far as I am concerned, the code I have written for this software
+ * can be used freely for any purpose.  Any derived versions of this
+ * software must be clearly marked as such, and if the derived work is
+ * incompatible with the protocol description in the RFC file, it must be
+ * called by a name other than "ssh" or "Secure Shell".
+ */
+
+/* actually from atomicio, but is only used in scp code */
+#define vwrite (ssize_t (*)(int, void *, size_t))write
+
+char	*chop(char *);
+char	*strdelim(char **);
+void	 set_nonblock(int);
+void	 unset_nonblock(int);
+void	 set_nodelay(int);
+int	 a2port(const char *);
+char	*cleanhostname(char *);
+char	*colon(char *);
+long	 convtime(const char *);
+
+struct passwd *pwcopy(struct passwd *);
+
+typedef struct arglist arglist;
+struct arglist {
+	char    **list;
+	int     num;
+	int     nalloc;
+};
+void	 addargs(arglist *, char *, ...);
+
+/* from xmalloc.h */
+void	*xmalloc(size_t);
+void	*xrealloc(void *, size_t);
+void     xfree(void *);
+char	*xstrdup(const char *);
+
+
diff --git a/service.h b/service.h
new file mode 100644
index 0000000000000000000000000000000000000000..197d8d1d5cc48bbe25315701b1ff6e4f0b6aeb5c
--- /dev/null
+++ b/service.h
@@ -0,0 +1,32 @@
+/*
+ * 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 _SERVICE_H_
+#define _SERVICE_H_
+
+void recv_msg_service_request(); /* Server */
+void send_msg_service_request(); /* Client */
+void recv_msg_service_accept(); /* Client */
+
+#endif /* _SERVICE_H_ */
diff --git a/session.h b/session.h
new file mode 100644
index 0000000000000000000000000000000000000000..a4ad45f5a09349e274d72cd2e5aeb1de49b6b5fa
--- /dev/null
+++ b/session.h
@@ -0,0 +1,257 @@
+/*
+ * 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 _SESSION_H_
+#define _SESSION_H_
+
+#include "includes.h"
+#include "options.h"
+#include "buffer.h"
+#include "signkey.h"
+#include "kex.h"
+#include "auth.h"
+#include "channel.h"
+#include "queue.h"
+#include "listener.h"
+#include "packet.h"
+#include "tcpfwd.h"
+#include "chansession.h"
+
+extern int sessinitdone; /* Is set to 0 somewhere */
+extern int exitflag;
+
+void common_session_init(int sock, char* remotehost);
+void session_loop(void(*loophandler)());
+void common_session_cleanup();
+void session_identification();
+
+
+/* Server */
+void svr_session(int sock, int childpipe, char *remotehost, char *addrstring);
+void svr_dropbear_exit(int exitcode, const char* format, va_list param);
+void svr_dropbear_log(int priority, const char* format, va_list param);
+
+/* Client */
+void cli_session(int sock, char *remotehost);
+void cli_session_cleanup();
+void cleantext(unsigned char* dirtytext);
+
+struct key_context {
+
+	const struct dropbear_cipher *recv_algo_crypt; /* NULL for none */
+	const struct dropbear_cipher *trans_algo_crypt; /* NULL for none */
+	const struct dropbear_hash *recv_algo_mac; /* NULL for none */
+	const struct dropbear_hash *trans_algo_mac; /* NULL for none */
+	char algo_kex;
+	char algo_hostkey;
+
+	char recv_algo_comp; /* compression */
+	char trans_algo_comp;
+#ifndef DISABLE_ZLIB
+	z_streamp recv_zstream;
+	z_streamp trans_zstream;
+#endif
+
+	/* actual keys */
+	symmetric_CBC recv_symmetric_struct;
+	symmetric_CBC trans_symmetric_struct;
+	unsigned char recvmackey[MAX_MAC_KEY];
+	unsigned char transmackey[MAX_MAC_KEY];
+
+};
+
+struct sshsession {
+
+	/* Is it a client or server? */
+	unsigned char isserver;
+
+	long connecttimeout; /* time to disconnect if we have a timeout (for
+							userauth etc), or 0 for no timeout */
+
+	int sock;
+
+	unsigned char *remotehost; /* the peer hostname */
+
+	unsigned char *remoteident;
+
+	int maxfd; /* the maximum file descriptor to check with select() */
+
+
+	/* Packet buffers/values etc */
+	buffer *writepayload; /* Unencrypted payload to write - this is used
+							 throughout the code, as handlers fill out this
+							 buffer with the packet to send. */
+	struct Queue writequeue; /* A queue of encrypted packets to send */
+	buffer *readbuf; /* Encrypted */
+	buffer *decryptreadbuf; /* Post-decryption */
+	buffer *payload; /* Post-decompression, the actual SSH packet */
+	unsigned int transseq, recvseq; /* Sequence IDs */
+
+	/* Packet-handling flags */
+	const packettype * packettypes; /* Packet handler mappings for this
+										session, see process-packet.c */
+
+	unsigned dataallowed : 1; /* whether we can send data packets or we are in
+								 the middle of a KEX or something */
+
+	unsigned char requirenext; /* byte indicating what packet we require next, 
+								or 0x00 for any */
+
+	unsigned char ignorenext; /* whether to ignore the next packet,
+								 used for kex_follows stuff */
+
+	unsigned char lastpacket; /* What the last received packet type was */
+	
+
+
+	/* KEX/encryption related */
+	struct KEXState kexstate;
+	struct key_context *keys;
+	struct key_context *newkeys;
+	unsigned char *session_id; /* this is the hash from the first kex */
+	/* The below are used temorarily during kex, are freed after use */
+	mp_int * dh_K; /* SSH_MSG_KEXDH_REPLY and sending SSH_MSH_NEWKEYS */
+	unsigned char hash[SHA1_HASH_SIZE]; /* the hash*/
+	buffer* kexhashbuf; /* session hash buffer calculated from various packets*/
+	buffer* transkexinit; /* the kexinit packet we send should be kept so we
+							 can add it to the hash when generating keys */
+
+	algo_type*(*buf_match_algo)(buffer*buf, algo_type localalgos[],
+			int *goodguess); /* The function to use to choose which algorithm
+								to use from the ones presented by the remote
+								side. Is specific to the client/server mode,
+								hence the function-pointer callback.*/
+
+	void(*remoteclosed)(); /* A callback to handle closure of the
+									  remote connection */
+
+
+	struct AuthState authstate; /* Common amongst client and server, since most
+								   struct elements are common */
+
+	/* Channel related */
+	struct Channel ** channels; /* these pointers may be null */
+	unsigned int chansize; /* the number of Channel*s allocated for channels */
+	unsigned int chancount; /* the number of Channel*s in use */
+	const struct ChanType **chantypes; /* The valid channel types */
+
+	
+	/* TCP forwarding - where manage listeners */
+	struct Listener ** listeners;
+	unsigned int listensize;
+
+	/* Whether to allow binding to privileged ports (<1024). This doesn't
+	 * really belong here, but nowhere else fits nicely */
+	int allowprivport;
+
+};
+
+struct serversession {
+
+	/* Server specific options */
+	int childpipe; /* kept open until we successfully authenticate */
+	/* userauth */
+
+	struct ChildPid * childpids; /* array of mappings childpid<->channel */
+	unsigned int childpidsize;
+
+	/* Used to avoid a race in the exit returncode handling - see
+	 * svr-chansession.c for details */
+	struct exitinfo lastexit;
+
+	/* The numeric address they connected from, used for logging */
+	char * addrstring;
+
+};
+
+typedef enum {
+	KEX_NOTHING,
+	KEXINIT_RCVD,
+	KEXDH_INIT_SENT,
+	KEXDONE,
+
+} cli_kex_state;
+
+typedef enum {
+	STATE_NOTHING,
+	SERVICE_AUTH_REQ_SENT,
+	SERVICE_AUTH_ACCEPT_RCVD,
+	SERVICE_CONN_REQ_SENT,
+	SERVICE_CONN_ACCEPT_RCVD,
+	USERAUTH_REQ_SENT,
+	USERAUTH_FAIL_RCVD,
+	USERAUTH_SUCCESS_RCVD,
+	SESSION_RUNNING,
+
+} cli_state;
+
+struct clientsession {
+
+	mp_int *dh_e, *dh_x; /* Used during KEX */
+	cli_kex_state kex_state; /* Used for progressing KEX */
+	cli_state state; /* Used to progress auth/channelsession etc */
+	unsigned donefirstkex : 1; /* Set when we set sentnewkeys, never reset */
+
+	int tty_raw_mode; /* Whether we're in raw mode (and have to clean up) */
+	struct termios saved_tio;
+	int stdincopy;
+	int stdinflags;
+	int stdoutcopy;
+	int stdoutflags;
+	int stderrcopy;
+	int stderrflags;
+
+	int winchange; /* Set to 1 when a windowchange signal happens */
+
+	int lastauthtype; /* either AUTH_TYPE_PUBKEY or AUTH_TYPE_PASSWORD,
+						 for the last type of auth we tried */
+#ifdef ENABLE_CLI_INTERACT_AUTH
+	int auth_interact_failed; /* flag whether interactive auth can still
+								 be used */
+	int interact_request_received; /* flag whether we've received an 
+									  info request from the server for
+									  interactive auth.*/
+#endif
+	struct SignKeyList *lastprivkey;
+
+	int retval; /* What the command exit status was - we emulate it */
+#if 0
+	TODO
+	struct AgentkeyList *agentkeys; /* Keys to use for public-key auth */
+#endif
+
+};
+
+/* Global structs storing the state */
+extern struct sshsession ses;
+
+#ifdef DROPBEAR_SERVER
+extern struct serversession svr_ses;
+#endif /* DROPBEAR_SERVER */
+
+#ifdef DROPBEAR_CLIENT
+extern struct clientsession cli_ses;
+#endif /* DROPBEAR_CLIENT */
+
+#endif /* _SESSION_H_ */
diff --git a/signkey.c b/signkey.c
new file mode 100644
index 0000000000000000000000000000000000000000..b8afb58c1c8a6b05ba74500f5e960a7e03857909
--- /dev/null
+++ b/signkey.c
@@ -0,0 +1,484 @@
+/*
+ * 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. */
+
+#include "includes.h"
+#include "dbutil.h"
+#include "signkey.h"
+#include "buffer.h"
+#include "ssh.h"
+
+/* malloc a new sign_key and set the dss and rsa keys to NULL */
+sign_key * new_sign_key() {
+
+	sign_key * ret;
+
+	ret = (sign_key*)m_malloc(sizeof(sign_key));
+#ifdef DROPBEAR_DSS
+	ret->dsskey = NULL;
+#endif
+#ifdef DROPBEAR_RSA
+	ret->rsakey = NULL;
+#endif
+	return ret;
+
+}
+
+/* Returns "ssh-dss" or "ssh-rsa" corresponding to the type. Exits fatally
+ * if the type is invalid */
+const char* signkey_name_from_type(int type, int *namelen) {
+
+#ifdef DROPBEAR_RSA
+	if (type == DROPBEAR_SIGNKEY_RSA) {
+		*namelen = SSH_SIGNKEY_RSA_LEN;
+		return SSH_SIGNKEY_RSA;
+	}
+#endif
+#ifdef DROPBEAR_DSS
+	if (type == DROPBEAR_SIGNKEY_DSS) {
+		*namelen = SSH_SIGNKEY_DSS_LEN;
+		return SSH_SIGNKEY_DSS;
+	}
+#endif
+	dropbear_exit("bad key type %d", type);
+	return NULL; /* notreached */
+}
+
+/* Returns DROPBEAR_SIGNKEY_RSA, DROPBEAR_SIGNKEY_DSS, 
+ * or DROPBEAR_SIGNKEY_NONE */
+int signkey_type_from_name(const char* name, int namelen) {
+
+#ifdef DROPBEAR_RSA
+	if (namelen == SSH_SIGNKEY_RSA_LEN
+			&& memcmp(name, SSH_SIGNKEY_RSA, SSH_SIGNKEY_RSA_LEN) == 0) {
+		return DROPBEAR_SIGNKEY_RSA;
+	}
+#endif
+#ifdef DROPBEAR_DSS
+	if (namelen == SSH_SIGNKEY_DSS_LEN
+			&& memcmp(name, SSH_SIGNKEY_DSS, SSH_SIGNKEY_DSS_LEN) == 0) {
+		return DROPBEAR_SIGNKEY_DSS;
+	}
+#endif
+
+	return DROPBEAR_SIGNKEY_NONE;
+}
+
+/* returns DROPBEAR_SUCCESS on success, DROPBEAR_FAILURE on fail.
+ * type should be set by the caller to specify the type to read, and
+ * on return is set to the type read (useful when type = _ANY) */
+int buf_get_pub_key(buffer *buf, sign_key *key, int *type) {
+
+	unsigned char* ident;
+	unsigned int len;
+	int keytype;
+	int ret = DROPBEAR_FAILURE;
+
+	TRACE(("enter buf_get_pub_key"))
+
+	ident = buf_getstring(buf, &len);
+	keytype = signkey_type_from_name(ident, len);
+	m_free(ident);
+
+	if (*type != DROPBEAR_SIGNKEY_ANY && *type != keytype) {
+		return DROPBEAR_FAILURE;
+	}
+
+	*type = keytype;
+
+	/* Rewind the buffer back before "ssh-rsa" etc */
+	buf_incrpos(buf, -len - 4);
+
+#ifdef DROPBEAR_DSS
+	if (keytype == DROPBEAR_SIGNKEY_DSS) {
+		dss_key_free(key->dsskey);
+		key->dsskey = (dss_key*)m_malloc(sizeof(dss_key));
+		ret = buf_get_dss_pub_key(buf, key->dsskey);
+		if (ret == DROPBEAR_FAILURE) {
+			m_free(key->dsskey);
+		}
+	}
+#endif
+#ifdef DROPBEAR_RSA
+	if (keytype == DROPBEAR_SIGNKEY_RSA) {
+		rsa_key_free(key->rsakey);
+		key->rsakey = (rsa_key*)m_malloc(sizeof(rsa_key));
+		ret = buf_get_rsa_pub_key(buf, key->rsakey);
+		if (ret == DROPBEAR_FAILURE) {
+			m_free(key->rsakey);
+		}
+	}
+#endif
+
+	TRACE(("leave buf_get_pub_key"))
+
+	return ret;
+	
+}
+
+/* returns DROPBEAR_SUCCESS on success, DROPBEAR_FAILURE on fail.
+ * type should be set by the caller to specify the type to read, and
+ * on return is set to the type read (useful when type = _ANY) */
+int buf_get_priv_key(buffer *buf, sign_key *key, int *type) {
+
+	unsigned char* ident;
+	unsigned int len;
+	int keytype;
+	int ret = DROPBEAR_FAILURE;
+
+	TRACE(("enter buf_get_priv_key"))
+
+	ident = buf_getstring(buf, &len);
+	keytype = signkey_type_from_name(ident, len);
+	m_free(ident);
+
+	if (*type != DROPBEAR_SIGNKEY_ANY && *type != keytype) {
+		TRACE(("wrong key type: %d %d", *type, keytype))
+		return DROPBEAR_FAILURE;
+	}
+
+	*type = keytype;
+
+	/* Rewind the buffer back before "ssh-rsa" etc */
+	buf_incrpos(buf, -len - 4);
+
+#ifdef DROPBEAR_DSS
+	if (keytype == DROPBEAR_SIGNKEY_DSS) {
+		dss_key_free(key->dsskey);
+		key->dsskey = (dss_key*)m_malloc(sizeof(dss_key));
+		ret = buf_get_dss_priv_key(buf, key->dsskey);
+		if (ret == DROPBEAR_FAILURE) {
+			m_free(key->dsskey);
+		}
+	}
+#endif
+#ifdef DROPBEAR_RSA
+	if (keytype == DROPBEAR_SIGNKEY_RSA) {
+		rsa_key_free(key->rsakey);
+		key->rsakey = (rsa_key*)m_malloc(sizeof(rsa_key));
+		ret = buf_get_rsa_priv_key(buf, key->rsakey);
+		if (ret == DROPBEAR_FAILURE) {
+			m_free(key->rsakey);
+		}
+	}
+#endif
+
+	TRACE(("leave buf_get_priv_key"))
+
+	return ret;
+	
+}
+
+/* type is either DROPBEAR_SIGNKEY_DSS or DROPBEAR_SIGNKEY_RSA */
+void buf_put_pub_key(buffer* buf, sign_key *key, int type) {
+
+	buffer *pubkeys;
+
+	TRACE(("enter buf_put_pub_key"))
+	pubkeys = buf_new(MAX_PUBKEY_SIZE);
+	
+#ifdef DROPBEAR_DSS
+	if (type == DROPBEAR_SIGNKEY_DSS) {
+		buf_put_dss_pub_key(pubkeys, key->dsskey);
+	}
+#endif
+#ifdef DROPBEAR_RSA
+	if (type == DROPBEAR_SIGNKEY_RSA) {
+		buf_put_rsa_pub_key(pubkeys, key->rsakey);
+	}
+#endif
+	if (pubkeys->len == 0) {
+		dropbear_exit("bad key types in buf_put_pub_key");
+	}
+
+	buf_setpos(pubkeys, 0);
+	buf_putstring(buf, buf_getptr(pubkeys, pubkeys->len),
+			pubkeys->len);
+	
+	buf_free(pubkeys);
+	TRACE(("leave buf_put_pub_key"))
+}
+
+/* type is either DROPBEAR_SIGNKEY_DSS or DROPBEAR_SIGNKEY_RSA */
+void buf_put_priv_key(buffer* buf, sign_key *key, int type) {
+
+	TRACE(("enter buf_put_priv_key"))
+	TRACE(("type is %d", type))
+
+#ifdef DROPBEAR_DSS
+	if (type == DROPBEAR_SIGNKEY_DSS) {
+		buf_put_dss_priv_key(buf, key->dsskey);
+	TRACE(("leave buf_put_priv_key: dss done"))
+	return;
+	}
+#endif
+#ifdef DROPBEAR_RSA
+	if (type == DROPBEAR_SIGNKEY_RSA) {
+		buf_put_rsa_priv_key(buf, key->rsakey);
+	TRACE(("leave buf_put_priv_key: rsa done"))
+	return;
+	}
+#endif
+	dropbear_exit("bad key types in put pub key");
+}
+
+void sign_key_free(sign_key *key) {
+
+	TRACE(("enter sign_key_free"))
+
+#ifdef DROPBEAR_DSS
+	dss_key_free(key->dsskey);
+	key->dsskey = NULL;
+#endif
+#ifdef DROPBEAR_RSA
+	rsa_key_free(key->rsakey);
+	key->rsakey = NULL;
+#endif
+
+	m_free(key);
+	TRACE(("leave sign_key_free"))
+}
+
+static char hexdig(unsigned char x) {
+
+	if (x > 0xf)
+		return 'X';
+
+	if (x < 10)
+		return '0' + x;
+	else
+		return 'a' + x - 10;
+}
+
+/* Since we're not sure if we'll have md5 or sha1, we present both.
+ * MD5 is used in preference, but sha1 could still be useful */
+#ifdef DROPBEAR_MD5_HMAC
+static char * sign_key_md5_fingerprint(unsigned char* keyblob,
+		unsigned int keybloblen) {
+
+	char * ret;
+	hash_state hs;
+	unsigned char hash[MD5_HASH_SIZE];
+	unsigned int i;
+	unsigned int buflen;
+
+	md5_init(&hs);
+
+	/* skip the size int of the string - this is a bit messy */
+	md5_process(&hs, keyblob, keybloblen);
+
+	md5_done(&hs, hash);
+
+	/* "md5 hexfingerprinthere\0", each hex digit is "AB:" etc */
+	buflen = 4 + 3*MD5_HASH_SIZE;
+	ret = (char*)m_malloc(buflen);
+
+	memset(ret, 'Z', buflen);
+	strcpy(ret, "md5 ");
+
+	for (i = 0; i < MD5_HASH_SIZE; i++) {
+		unsigned int pos = 4 + i*3;
+		ret[pos] = hexdig(hash[i] >> 4);
+		ret[pos+1] = hexdig(hash[i] & 0x0f);
+		ret[pos+2] = ':';
+	}
+	ret[buflen-1] = 0x0;
+
+	return ret;
+}
+
+#else /* use SHA1 rather than MD5 for fingerprint */
+static char * sign_key_sha1_fingerprint(unsigned char* keyblob, 
+		unsigned int keybloblen) {
+
+	char * ret;
+	hash_state hs;
+	unsigned char hash[SHA1_HASH_SIZE];
+	unsigned int i;
+	unsigned int buflen;
+
+	sha1_init(&hs);
+
+	/* skip the size int of the string - this is a bit messy */
+	sha1_process(&hs, keyblob, keybloblen);
+
+	sha1_done(&hs, hash);
+
+	/* "sha1 hexfingerprinthere\0", each hex digit is "AB:" etc */
+	buflen = 5 + 3*SHA1_HASH_SIZE;
+	ret = (char*)m_malloc(buflen);
+
+	strcpy(ret, "sha1 ");
+
+	for (i = 0; i < SHA1_HASH_SIZE; i++) {
+		unsigned int pos = 5 + 3*i;
+		ret[pos] = hexdig(hash[i] >> 4);
+		ret[pos+1] = hexdig(hash[i] & 0x0f);
+		ret[pos+2] = ':';
+	}
+	ret[buflen-1] = 0x0;
+
+	return ret;
+}
+
+#endif /* MD5/SHA1 switch */
+
+/* This will return a freshly malloced string, containing a fingerprint
+ * in either sha1 or md5 */
+char * sign_key_fingerprint(unsigned char* keyblob, unsigned int keybloblen) {
+
+#ifdef DROPBEAR_MD5_HMAC
+	return sign_key_md5_fingerprint(keyblob, keybloblen);
+#else
+	return sign_key_sha1_fingerprint(keyblob, keybloblen);
+#endif
+}
+
+void buf_put_sign(buffer* buf, sign_key *key, int type, 
+		const unsigned char *data, unsigned int len) {
+
+	buffer *sigblob;
+
+	sigblob = buf_new(MAX_PUBKEY_SIZE);
+
+#ifdef DROPBEAR_DSS
+	if (type == DROPBEAR_SIGNKEY_DSS) {
+		buf_put_dss_sign(sigblob, key->dsskey, data, len);
+	}
+#endif
+#ifdef DROPBEAR_RSA
+	if (type == DROPBEAR_SIGNKEY_RSA) {
+		buf_put_rsa_sign(sigblob, key->rsakey, data, len);
+	}
+#endif
+	if (sigblob->len == 0) {
+		dropbear_exit("non-matching signing type");
+	}
+
+	buf_setpos(sigblob, 0);
+	buf_putstring(buf, buf_getptr(sigblob, sigblob->len),
+			sigblob->len);
+			
+	buf_free(sigblob);
+
+}
+
+#ifdef DROPBEAR_SIGNKEY_VERIFY
+/* Return DROPBEAR_SUCCESS or DROPBEAR_FAILURE.
+ * If FAILURE is returned, the position of
+ * buf is undefined. If SUCCESS is returned, buf will be positioned after the
+ * signature blob */
+int buf_verify(buffer * buf, sign_key *key, const unsigned char *data,
+		unsigned int len) {
+	
+	unsigned int bloblen;
+	unsigned char * ident = NULL;
+	unsigned int identlen = 0;
+
+	TRACE(("enter buf_verify"))
+
+	bloblen = buf_getint(buf);
+	ident = buf_getstring(buf, &identlen);
+
+#ifdef DROPBEAR_DSS
+	if (bloblen == DSS_SIGNATURE_SIZE &&
+			memcmp(ident, SSH_SIGNKEY_DSS, identlen) == 0) {
+		m_free(ident);
+		if (key->dsskey == NULL) {
+			dropbear_exit("no dss key to verify signature");
+		}
+		return buf_dss_verify(buf, key->dsskey, data, len);
+	}
+#endif
+
+#ifdef DROPBEAR_RSA
+	if (memcmp(ident, SSH_SIGNKEY_RSA, identlen) == 0) {
+		m_free(ident);
+		if (key->rsakey == NULL) {
+			dropbear_exit("no rsa key to verify signature");
+		}
+		return buf_rsa_verify(buf, key->rsakey, data, len);
+	}
+#endif
+
+	m_free(ident);
+	dropbear_exit("non-matching signing type");
+	return DROPBEAR_FAILURE;
+}
+#endif /* DROPBEAR_SIGNKEY_VERIFY */
+
+#ifdef DROPBEAR_KEY_LINES /* ie we're using authorized_keys or known_hosts */
+
+/* Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE when given a buffer containing
+ * a key, a key, and a type. The buffer is positioned at the start of the
+ * base64 data, and contains no trailing data */
+int cmp_base64_key(const unsigned char* keyblob, unsigned int keybloblen, 
+					const unsigned char* algoname, unsigned int algolen, 
+					buffer * line) {
+
+	buffer * decodekey = NULL;
+	int ret = DROPBEAR_FAILURE;
+	unsigned int len, filealgolen;
+	unsigned long decodekeylen;
+	unsigned char* filealgo = NULL;
+
+	/* now we have the actual data */
+	len = line->len - line->pos;
+	decodekeylen = len * 2; /* big to be safe */
+	decodekey = buf_new(decodekeylen);
+
+	if (base64_decode(buf_getptr(line, len), len,
+				buf_getwriteptr(decodekey, decodekey->size),
+				&decodekeylen) != CRYPT_OK) {
+		TRACE(("checkpubkey: base64 decode failed"))
+		goto out;
+	}
+	TRACE(("checkpubkey: base64_decode success"))
+	buf_incrlen(decodekey, decodekeylen);
+	
+	/* compare the keys */
+	if ( ( decodekeylen != keybloblen )
+			|| memcmp( buf_getptr(decodekey, decodekey->len),
+						keyblob, decodekey->len) != 0) {
+		TRACE(("checkpubkey: compare failed"))
+		goto out;
+	}
+
+	/* ... and also check that the algo specified and the algo in the key
+	 * itself match */
+	filealgolen = buf_getint(decodekey);
+	filealgo = buf_getptr(decodekey, filealgolen);
+	if (filealgolen != algolen || memcmp(filealgo, algoname, algolen) != 0) {
+		TRACE(("checkpubkey: algo match failed")) 
+		goto out;
+	}
+
+	/* All checks passed */
+	ret = DROPBEAR_SUCCESS;
+
+out:
+	buf_free(decodekey);
+	decodekey = NULL;
+	return ret;
+}
+#endif
diff --git a/signkey.h b/signkey.h
new file mode 100644
index 0000000000000000000000000000000000000000..8bc7e8f10fb7ab67d8cacc6891690c88f72627f6
--- /dev/null
+++ b/signkey.h
@@ -0,0 +1,63 @@
+/*
+ * 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 _SIGNKEY_H_
+#define _SIGNKEY_H_
+
+#include "buffer.h"
+#include "dss.h"
+#include "rsa.h"
+
+struct SIGN_key {
+
+#ifdef DROPBEAR_DSS
+	dss_key * dsskey;
+#endif
+#ifdef DROPBEAR_RSA
+	rsa_key * rsakey;
+#endif
+};
+
+typedef struct SIGN_key sign_key;
+
+sign_key * new_sign_key();
+const char* signkey_name_from_type(int type, int *namelen);
+int signkey_type_from_name(const char* name, int namelen);
+int buf_get_pub_key(buffer *buf, sign_key *key, int *type);
+int buf_get_priv_key(buffer* buf, sign_key *key, int *type);
+void buf_put_pub_key(buffer* buf, sign_key *key, int type);
+void buf_put_priv_key(buffer* buf, sign_key *key, int type);
+void sign_key_free(sign_key *key);
+void buf_put_sign(buffer* buf, sign_key *key, int type, 
+		const unsigned char *data, unsigned int len);
+#ifdef DROPBEAR_SIGNKEY_VERIFY
+int buf_verify(buffer * buf, sign_key *key, const unsigned char *data,
+		unsigned int len);
+char * sign_key_fingerprint(unsigned char* keyblob, unsigned int keybloblen);
+#endif
+int cmp_base64_key(const unsigned char* keyblob, unsigned int keybloblen, 
+					const unsigned char* algoname, unsigned int algolen, 
+					buffer * line);
+
+#endif /* _SIGNKEY_H_ */
diff --git a/ssh.h b/ssh.h
new file mode 100644
index 0000000000000000000000000000000000000000..26b51bf2787f9c539a3607d6f3cdf763ba72aa8b
--- /dev/null
+++ b/ssh.h
@@ -0,0 +1,107 @@
+/*
+ * 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. */
+
+/* This file contains the various numbers in the protocol */
+
+
+/* message numbers */
+#define SSH_MSG_DISCONNECT             1
+#define SSH_MSG_IGNORE                 2
+#define SSH_MSG_UNIMPLEMENTED          3
+#define SSH_MSG_DEBUG                  4
+#define SSH_MSG_SERVICE_REQUEST        5
+#define SSH_MSG_SERVICE_ACCEPT         6
+#define SSH_MSG_KEXINIT                20
+#define SSH_MSG_NEWKEYS                21
+#define SSH_MSG_KEXDH_INIT             30
+#define SSH_MSG_KEXDH_REPLY            31
+
+/* userauth message numbers */
+#define SSH_MSG_USERAUTH_REQUEST            50
+#define SSH_MSG_USERAUTH_FAILURE            51
+#define SSH_MSG_USERAUTH_SUCCESS            52
+#define SSH_MSG_USERAUTH_BANNER             53
+
+/* packets 60-79 are method-specific, aren't one-one mapping */
+#define SSH_MSG_USERAUTH_SPECIFIC_60   60
+
+#define SSH_MSG_USERAUTH_PASSWD_CHANGEREQ   60
+
+#define SSH_MSG_USERAUTH_PK_OK				60
+
+/* keyboard interactive auth */
+#define SSH_MSG_USERAUTH_INFO_REQUEST           60
+#define SSH_MSG_USERAUTH_INFO_RESPONSE          61
+
+
+/* If adding numbers here, check MAX_UNAUTH_PACKET_TYPE in process-packet.c
+ * is still valid */
+
+/* connect message numbers */
+#define SSH_MSG_GLOBAL_REQUEST                  80
+#define SSH_MSG_REQUEST_SUCCESS                 81
+#define SSH_MSG_REQUEST_FAILURE                 82
+#define SSH_MSG_CHANNEL_OPEN                    90
+#define SSH_MSG_CHANNEL_OPEN_CONFIRMATION       91 
+#define SSH_MSG_CHANNEL_OPEN_FAILURE            92
+#define SSH_MSG_CHANNEL_WINDOW_ADJUST           93
+#define SSH_MSG_CHANNEL_DATA                    94
+#define SSH_MSG_CHANNEL_EXTENDED_DATA           95
+#define SSH_MSG_CHANNEL_EOF                     96
+#define SSH_MSG_CHANNEL_CLOSE                   97
+#define SSH_MSG_CHANNEL_REQUEST                 98
+#define SSH_MSG_CHANNEL_SUCCESS                 99
+#define SSH_MSG_CHANNEL_FAILURE                 100
+
+/* extended data types */
+#define SSH_EXTENDED_DATA_STDERR	1
+
+/* disconnect codes */
+#define SSH_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT      1
+#define SSH_DISCONNECT_PROTOCOL_ERROR                   2
+#define SSH_DISCONNECT_KEY_EXCHANGE_FAILED              3
+#define SSH_DISCONNECT_RESERVED                         4
+#define SSH_DISCONNECT_MAC_ERROR                        5
+#define SSH_DISCONNECT_COMPRESSION_ERROR                6
+#define SSH_DISCONNECT_SERVICE_NOT_AVAILABLE            7
+#define SSH_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED   8
+#define SSH_DISCONNECT_HOST_KEY_NOT_VERIFIABLE          9
+#define SSH_DISCONNECT_CONNECTION_LOST                 10
+#define SSH_DISCONNECT_BY_APPLICATION                  11
+#define SSH_DISCONNECT_TOO_MANY_CONNECTIONS            12
+#define SSH_DISCONNECT_AUTH_CANCELLED_BY_USER          13
+#define SSH_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE  14
+#define SSH_DISCONNECT_ILLEGAL_USER_NAME               15
+
+/* service types */
+#define SSH_SERVICE_USERAUTH "ssh-userauth"
+#define SSH_SERVICE_USERAUTH_LEN 12
+#define SSH_SERVICE_CONNECTION "ssh-connection"
+#define SSH_SERVICE_CONNECTION_LEN 14
+
+/* public key types */
+#define SSH_SIGNKEY_DSS "ssh-dss"
+#define SSH_SIGNKEY_DSS_LEN 7
+#define SSH_SIGNKEY_RSA "ssh-rsa"
+#define SSH_SIGNKEY_RSA_LEN 7
diff --git a/sshpty.c b/sshpty.c
new file mode 100644
index 0000000000000000000000000000000000000000..3526ff08c8c6b3935c188921b8b355702c609a70
--- /dev/null
+++ b/sshpty.c
@@ -0,0 +1,412 @@
+/*
+ * Dropbear - a SSH2 server
+ *
+ * Copied from OpenSSH-3.5p1 source, modified by Matt Johnston 2003
+ * 
+ * Author: Tatu Ylonen <ylo@cs.hut.fi>
+ * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+ *                    All rights reserved
+ * Allocating a pseudo-terminal, and making it the controlling tty.
+ *
+ * As far as I am concerned, the code I have written for this software
+ * can be used freely for any purpose.  Any derived versions of this
+ * software must be clearly marked as such, and if the derived work is
+ * incompatible with the protocol description in the RFC file, it must be
+ * called by a name other than "ssh" or "Secure Shell".
+ */
+
+/*RCSID("OpenBSD: sshpty.c,v 1.7 2002/06/24 17:57:20 deraadt Exp ");*/
+
+#include "includes.h"
+#include "dbutil.h"
+#include "errno.h"
+#include "sshpty.h"
+
+/* Pty allocated with _getpty gets broken if we do I_PUSH:es to it. */
+#if defined(HAVE__GETPTY) || defined(HAVE_OPENPTY)
+#undef HAVE_DEV_PTMX
+#endif
+
+#ifdef HAVE_PTY_H
+# include <pty.h>
+#endif
+#if defined(USE_DEV_PTMX) && defined(HAVE_STROPTS_H)
+# include <stropts.h>
+#endif
+
+#ifndef O_NOCTTY
+#define O_NOCTTY 0
+#endif
+
+/*
+ * Allocates and opens a pty.  Returns 0 if no pty could be allocated, or
+ * nonzero if a pty was successfully allocated.  On success, open file
+ * descriptors for the pty and tty sides and the name of the tty side are
+ * returned (the buffer must be able to hold at least 64 characters).
+ */
+
+int
+pty_allocate(int *ptyfd, int *ttyfd, char *namebuf, int namebuflen)
+{
+#if defined(HAVE_OPENPTY)
+	/* exists in recent (4.4) BSDs and OSF/1 */
+	char *name;
+	int i;
+
+	i = openpty(ptyfd, ttyfd, NULL, NULL, NULL);
+	if (i < 0) {
+		dropbear_log(LOG_WARNING, 
+				"pty_allocate: openpty: %.100s", strerror(errno));
+		return 0;
+	}
+	name = ttyname(*ttyfd);
+	if (!name) {
+		dropbear_exit("ttyname fails for openpty device");
+	}
+
+	strlcpy(namebuf, name, namebuflen);	/* possible truncation */
+	return 1;
+#else /* HAVE_OPENPTY */
+#ifdef HAVE__GETPTY
+	/*
+	 * _getpty(3) exists in SGI Irix 4.x, 5.x & 6.x -- it generates more
+	 * pty's automagically when needed
+	 */
+	char *slave;
+
+	slave = _getpty(ptyfd, O_RDWR, 0622, 0);
+	if (slave == NULL) {
+		dropbear_log(LOG_WARNING,
+				"pty_allocate: _getpty: %.100s", strerror(errno));
+		return 0;
+	}
+	strlcpy(namebuf, slave, namebuflen);
+	/* Open the slave side. */
+	*ttyfd = open(namebuf, O_RDWR | O_NOCTTY);
+	if (*ttyfd < 0) {
+		dropbear_log(LOG_WARNING,
+				"pty_allocate error: ttyftd open error");
+		close(*ptyfd);
+		return 0;
+	}
+	return 1;
+#else /* HAVE__GETPTY */
+#if defined(USE_DEV_PTMX)
+	/*
+	 * This code is used e.g. on Solaris 2.x.  (Note that Solaris 2.3
+	 * also has bsd-style ptys, but they simply do not work.)
+	 *
+	 * Linux systems may have the /dev/ptmx device, but this code won't work.
+	 */
+	int ptm;
+	char *pts;
+
+	ptm = open("/dev/ptmx", O_RDWR | O_NOCTTY);
+	if (ptm < 0) {
+		dropbear_log(LOG_WARNING,
+				"pty_allocate: /dev/ptmx: %.100s", strerror(errno));
+		return 0;
+	}
+	if (grantpt(ptm) < 0) {
+		dropbear_log(LOG_WARNING,
+				"grantpt: %.100s", strerror(errno));
+		return 0;
+	}
+	if (unlockpt(ptm) < 0) {
+		dropbear_log(LOG_WARNING,
+				"unlockpt: %.100s", strerror(errno));
+		return 0;
+	}
+	pts = ptsname(ptm);
+	if (pts == NULL) {
+		dropbear_log(LOG_WARNING,
+				"Slave pty side name could not be obtained.");
+	}
+	strlcpy(namebuf, pts, namebuflen);
+	*ptyfd = ptm;
+
+	/* Open the slave side. */
+	*ttyfd = open(namebuf, O_RDWR | O_NOCTTY);
+	if (*ttyfd < 0) {
+		dropbear_log(LOG_ERR,
+			"error opening pts %.100s: %.100s", namebuf, strerror(errno));
+		close(*ptyfd);
+		return 0;
+	}
+#ifndef HAVE_CYGWIN
+	/*
+	 * Push the appropriate streams modules, as described in Solaris pts(7).
+	 * HP-UX pts(7) doesn't have ttcompat module.
+	 */
+	if (ioctl(*ttyfd, I_PUSH, "ptem") < 0) {
+		dropbear_log(LOG_WARNING,
+				"ioctl I_PUSH ptem: %.100s", strerror(errno));
+	}
+	if (ioctl(*ttyfd, I_PUSH, "ldterm") < 0) {
+		dropbear_log(LOG_WARNING,
+			"ioctl I_PUSH ldterm: %.100s", strerror(errno));
+	}
+#ifndef __hpux
+	if (ioctl(*ttyfd, I_PUSH, "ttcompat") < 0) {
+		dropbear_log(LOG_WARNING,
+			"ioctl I_PUSH ttcompat: %.100s", strerror(errno));
+	}
+#endif
+#endif
+	return 1;
+#else /* USE_DEV_PTMX */
+#ifdef HAVE_DEV_PTS_AND_PTC
+	/* AIX-style pty code. */
+	const char *name;
+
+	*ptyfd = open("/dev/ptc", O_RDWR | O_NOCTTY);
+	if (*ptyfd < 0) {
+		dropbear_log(LOG_ERR,
+			"Could not open /dev/ptc: %.100s", strerror(errno));
+		return 0;
+	}
+	name = ttyname(*ptyfd);
+	if (!name) {
+		dropbear_exit("ttyname fails for /dev/ptc device");
+	}
+	strlcpy(namebuf, name, namebuflen);
+	*ttyfd = open(name, O_RDWR | O_NOCTTY);
+	if (*ttyfd < 0) {
+		dropbear_log(LOG_ERR,
+			"Could not open pty slave side %.100s: %.100s",
+		    name, strerror(errno));
+		close(*ptyfd);
+		return 0;
+	}
+	return 1;
+#else /* HAVE_DEV_PTS_AND_PTC */
+
+	/* BSD-style pty code. */
+	char buf[64];
+	int i;
+	const char *ptymajors = "pqrstuvwxyzabcdefghijklmnoABCDEFGHIJKLMNOPQRSTUVWXYZ";
+	const char *ptyminors = "0123456789abcdef";
+	int num_minors = strlen(ptyminors);
+	int num_ptys = strlen(ptymajors) * num_minors;
+	struct termios tio;
+
+	for (i = 0; i < num_ptys; i++) {
+		snprintf(buf, sizeof buf, "/dev/pty%c%c", ptymajors[i / num_minors],
+			 ptyminors[i % num_minors]);
+		snprintf(namebuf, namebuflen, "/dev/tty%c%c",
+		    ptymajors[i / num_minors], ptyminors[i % num_minors]);
+
+		*ptyfd = open(buf, O_RDWR | O_NOCTTY);
+		if (*ptyfd < 0) {
+			/* Try SCO style naming */
+			snprintf(buf, sizeof buf, "/dev/ptyp%d", i);
+			snprintf(namebuf, namebuflen, "/dev/ttyp%d", i);
+			*ptyfd = open(buf, O_RDWR | O_NOCTTY);
+			if (*ptyfd < 0) {
+				continue;
+			}
+		}
+
+		/* Open the slave side. */
+		*ttyfd = open(namebuf, O_RDWR | O_NOCTTY);
+		if (*ttyfd < 0) {
+			dropbear_log(LOG_ERR,
+				"pty_allocate: %.100s: %.100s", namebuf, strerror(errno));
+			close(*ptyfd);
+			return 0;
+		}
+		/* set tty modes to a sane state for broken clients */
+		if (tcgetattr(*ptyfd, &tio) < 0) {
+			dropbear_log(LOG_WARNING,
+				"ptyallocate: tty modes failed: %.100s", strerror(errno));
+		} else {
+			tio.c_lflag |= (ECHO | ISIG | ICANON);
+			tio.c_oflag |= (OPOST | ONLCR);
+			tio.c_iflag |= ICRNL;
+
+			/* Set the new modes for the terminal. */
+			if (tcsetattr(*ptyfd, TCSANOW, &tio) < 0) {
+				dropbear_log(LOG_WARNING,
+					"Setting tty modes for pty failed: %.100s",
+					strerror(errno));
+			}
+		}
+
+		return 1;
+	}
+	dropbear_log(LOG_WARNING, "failed to open any /dev/pty?? devices");
+	return 0;
+#endif /* HAVE_DEV_PTS_AND_PTC */
+#endif /* USE_DEV_PTMX */
+#endif /* HAVE__GETPTY */
+#endif /* HAVE_OPENPTY */
+}
+
+/* Releases the tty.  Its ownership is returned to root, and permissions to 0666. */
+
+void
+pty_release(const char *tty_name)
+{
+	if (chown(tty_name, (uid_t) 0, (gid_t) 0) < 0
+			&& (errno != ENOENT)) {
+		dropbear_log(LOG_ERR,
+				"chown %.100s 0 0 failed: %.100s", tty_name, strerror(errno));
+	}
+	if (chmod(tty_name, (mode_t) 0666) < 0
+			&& (errno != ENOENT)) {
+		dropbear_log(LOG_ERR,
+			"chmod %.100s 0666 failed: %.100s", tty_name, strerror(errno));
+	}
+}
+
+/* Makes the tty the processes controlling tty and sets it to sane modes. */
+
+void
+pty_make_controlling_tty(int *ttyfd, const char *tty_name)
+{
+	int fd;
+#ifdef USE_VHANGUP
+	void *old;
+#endif /* USE_VHANGUP */
+
+	/* Solaris has a problem with TIOCNOTTY for a bg process, so
+	 * we disable the signal which would STOP the process - matt */
+	signal(SIGTTOU, SIG_IGN);
+
+	/* First disconnect from the old controlling tty. */
+#ifdef TIOCNOTTY
+	fd = open(_PATH_TTY, O_RDWR | O_NOCTTY);
+	if (fd >= 0) {
+		(void) ioctl(fd, TIOCNOTTY, NULL);
+		close(fd);
+	}
+#endif /* TIOCNOTTY */
+	if (setsid() < 0) {
+		dropbear_log(LOG_ERR,
+			"setsid: %.100s", strerror(errno));
+	}
+
+	/*
+	 * Verify that we are successfully disconnected from the controlling
+	 * tty.
+	 */
+	fd = open(_PATH_TTY, O_RDWR | O_NOCTTY);
+	if (fd >= 0) {
+		dropbear_log(LOG_ERR,
+				"Failed to disconnect from controlling tty.\n");
+		close(fd);
+	}
+	/* Make it our controlling tty. */
+#ifdef TIOCSCTTY
+	if (ioctl(*ttyfd, TIOCSCTTY, NULL) < 0) {
+		dropbear_log(LOG_ERR,
+			"ioctl(TIOCSCTTY): %.100s", strerror(errno));
+	}
+#endif /* TIOCSCTTY */
+#ifdef HAVE_NEWS4
+	if (setpgrp(0,0) < 0) {
+		dropbear_log(LOG_ERR,
+			error("SETPGRP %s",strerror(errno)));
+	}
+#endif /* HAVE_NEWS4 */
+#ifdef USE_VHANGUP
+	old = mysignal(SIGHUP, SIG_IGN);
+	vhangup();
+	mysignal(SIGHUP, old);
+#endif /* USE_VHANGUP */
+	fd = open(tty_name, O_RDWR);
+	if (fd < 0) {
+		dropbear_log(LOG_ERR,
+			"%.100s: %.100s", tty_name, strerror(errno));
+	} else {
+#ifdef USE_VHANGUP
+		close(*ttyfd);
+		*ttyfd = fd;
+#else /* USE_VHANGUP */
+		close(fd);
+#endif /* USE_VHANGUP */
+	}
+	/* Verify that we now have a controlling tty. */
+	fd = open(_PATH_TTY, O_WRONLY);
+	if (fd < 0) {
+		dropbear_log(LOG_ERR,
+			"open /dev/tty failed - could not set controlling tty: %.100s",
+		    strerror(errno));
+	} else {
+		close(fd);
+	}
+}
+
+/* Changes the window size associated with the pty. */
+
+void
+pty_change_window_size(int ptyfd, int row, int col,
+	int xpixel, int ypixel)
+{
+	struct winsize w;
+
+	w.ws_row = row;
+	w.ws_col = col;
+	w.ws_xpixel = xpixel;
+	w.ws_ypixel = ypixel;
+	(void) ioctl(ptyfd, TIOCSWINSZ, &w);
+}
+
+void
+pty_setowner(struct passwd *pw, const char *tty_name)
+{
+	struct group *grp;
+	gid_t gid;
+	mode_t mode;
+	struct stat st;
+
+	/* Determine the group to make the owner of the tty. */
+	grp = getgrnam("tty");
+	if (grp) {
+		gid = grp->gr_gid;
+		mode = S_IRUSR | S_IWUSR | S_IWGRP;
+	} else {
+		gid = pw->pw_gid;
+		mode = S_IRUSR | S_IWUSR | S_IWGRP | S_IWOTH;
+	}
+
+	/*
+	 * Change owner and mode of the tty as required.
+	 * Warn but continue if filesystem is read-only and the uids match/
+	 * tty is owned by root.
+	 */
+	if (stat(tty_name, &st)) {
+		dropbear_exit("pty_setowner: stat(%.101s) failed: %.100s",
+				tty_name, strerror(errno));
+	}
+
+	if (st.st_uid != pw->pw_uid || st.st_gid != gid) {
+		if (chown(tty_name, pw->pw_uid, gid) < 0) {
+			if (errno == EROFS &&
+			    (st.st_uid == pw->pw_uid || st.st_uid == 0)) {
+				dropbear_log(LOG_ERR,
+					"chown(%.100s, %u, %u) failed: %.100s",
+						tty_name, (unsigned int)pw->pw_uid, (unsigned int)gid,
+						strerror(errno));
+			} else {
+				dropbear_exit("chown(%.100s, %u, %u) failed: %.100s",
+				    tty_name, (unsigned int)pw->pw_uid, (unsigned int)gid,
+				    strerror(errno));
+			}
+		}
+	}
+
+	if ((st.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO)) != mode) {
+		if (chmod(tty_name, mode) < 0) {
+			if (errno == EROFS &&
+			    (st.st_mode & (S_IRGRP | S_IROTH)) == 0) {
+				dropbear_log(LOG_ERR,
+					"chmod(%.100s, 0%o) failed: %.100s",
+					tty_name, mode, strerror(errno));
+			} else {
+				dropbear_exit("chmod(%.100s, 0%o) failed: %.100s",
+				    tty_name, mode, strerror(errno));
+			}
+		}
+	}
+}
diff --git a/sshpty.h b/sshpty.h
new file mode 100644
index 0000000000000000000000000000000000000000..cf72072397c46492c860e99d7f5490991db4e72e
--- /dev/null
+++ b/sshpty.h
@@ -0,0 +1,28 @@
+/*	$OpenBSD: sshpty.h,v 1.4 2002/03/04 17:27:39 stevesk Exp $	*/
+
+/*
+ * Copied from openssh-3.5p1 source by Matt Johnston 2003
+ *
+ * Author: Tatu Ylonen <ylo@cs.hut.fi>
+ * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+ *                    All rights reserved
+ * Functions for allocating a pseudo-terminal and making it the controlling
+ * tty.
+ *
+ * As far as I am concerned, the code I have written for this software
+ * can be used freely for any purpose.  Any derived versions of this
+ * software must be clearly marked as such, and if the derived work is
+ * incompatible with the protocol description in the RFC file, it must be
+ * called by a name other than "ssh" or "Secure Shell".
+ */
+
+#ifndef SSHPTY_H
+#define SSHPTY_H
+
+int	 pty_allocate(int *, int *, char *, int);
+void	 pty_release(const char *);
+void	 pty_make_controlling_tty(int *, const char *);
+void	 pty_change_window_size(int, int, int, int, int);
+void	 pty_setowner(struct passwd *, const char *);
+
+#endif /* SSHPTY_H */
diff --git a/svr-agentfwd.c b/svr-agentfwd.c
new file mode 100644
index 0000000000000000000000000000000000000000..512715810d19e270fdda9d95c089a4cc4af272b8
--- /dev/null
+++ b/svr-agentfwd.c
@@ -0,0 +1,266 @@
+/*
+ * 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. */
+
+/* This file (agentfwd.c) handles authentication agent forwarding, for OpenSSH
+ * style agents. */
+
+#include "includes.h"
+
+#ifndef DISABLE_AGENTFWD
+
+#include "agentfwd.h"
+#include "session.h"
+#include "ssh.h"
+#include "dbutil.h"
+#include "chansession.h"
+#include "channel.h"
+#include "packet.h"
+#include "buffer.h"
+#include "random.h"
+#include "listener.h"
+
+#define AGENTDIRPREFIX "/tmp/dropbear-"
+
+static int send_msg_channel_open_agent(int fd);
+static int bindagent(int fd, struct ChanSess * chansess);
+static void agentaccept(struct Listener * listener, int sock);
+
+/* Handles client requests to start agent forwarding, sets up listening socket.
+ * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
+int agentreq(struct ChanSess * chansess) {
+
+	int fd;
+
+	if (chansess->agentlistener != NULL) {
+		return DROPBEAR_FAILURE;
+	}
+
+	/* create listening socket */
+	fd = socket(PF_UNIX, SOCK_STREAM, 0);
+	if (fd < 0) {
+		goto fail;
+	}
+
+	/* create the unix socket dir and file */
+	if (bindagent(fd, chansess) == DROPBEAR_FAILURE) {
+		goto fail;
+	}
+
+	/* listen */
+	if (listen(fd, 20) < 0) {
+		goto fail;
+	}
+
+	/* set non-blocking */
+	setnonblocking(fd);
+
+	/* pass if off to listener */
+	chansess->agentlistener = new_listener( &fd, 1, 0, chansess, 
+								agentaccept, NULL);
+
+	if (chansess->agentlistener == NULL) {
+		goto fail;
+	}
+
+	return DROPBEAR_SUCCESS;
+
+fail:
+	/* cleanup */
+	agentcleanup(chansess);
+
+	return DROPBEAR_FAILURE;
+}
+
+/* accepts a connection on the forwarded socket and opens a new channel for it
+ * back to the client */
+/* returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
+static void agentaccept(struct Listener *UNUSED(listener), int sock) {
+
+	int fd;
+
+	fd = accept(sock, NULL, NULL);
+	if (fd < 0) {
+		TRACE(("accept failed"))
+		return;
+	}
+
+	if (send_msg_channel_open_agent(fd) != DROPBEAR_SUCCESS) {
+		close(fd);
+	}
+
+}
+
+/* set up the environment variable pointing to the socket. This is called
+ * just before command/shell execution, after dropping priveleges */
+void agentset(struct ChanSess * chansess) {
+
+	char *path = NULL;
+	int len;
+
+	if (chansess->agentlistener == NULL) {
+		return;
+	}
+
+	/* 2 for "/" and "\0" */
+	len = strlen(chansess->agentdir) + strlen(chansess->agentfile) + 2;
+
+	path = m_malloc(len);
+	snprintf(path, len, "%s/%s", chansess->agentdir, chansess->agentfile);
+	addnewvar("SSH_AUTH_SOCK", path);
+	m_free(path);
+}
+
+/* close the socket, remove the socket-file */
+void agentcleanup(struct ChanSess * chansess) {
+
+	char *path = NULL;
+	uid_t uid;
+	gid_t gid;
+	int len;
+
+	if (chansess->agentlistener != NULL) {
+		remove_listener(chansess->agentlistener);
+		chansess->agentlistener = NULL;
+	}
+
+	if (chansess->agentfile != NULL && chansess->agentdir != NULL) {
+
+		/* Remove the dir as the user. That way they can't cause problems except
+		 * for themselves */
+		uid = getuid();
+		gid = getgid();
+		if ((setegid(ses.authstate.pw->pw_gid)) < 0 ||
+			(seteuid(ses.authstate.pw->pw_uid)) < 0) {
+			dropbear_exit("failed to set euid");
+		}
+
+		/* 2 for "/" and "\0" */
+		len = strlen(chansess->agentdir) + strlen(chansess->agentfile) + 2;
+
+		path = m_malloc(len);
+		snprintf(path, len, "%s/%s", chansess->agentdir, chansess->agentfile);
+		unlink(path);
+		m_free(path);
+
+		rmdir(chansess->agentdir);
+
+		if ((seteuid(uid)) < 0 ||
+			(setegid(gid)) < 0) {
+			dropbear_exit("failed to revert euid");
+		}
+
+		m_free(chansess->agentfile);
+		m_free(chansess->agentdir);
+	}
+
+}
+
+static const struct ChanType chan_agent = {
+	0, /* sepfds */
+	"auth-agent@openssh.com",
+	NULL,
+	NULL,
+	NULL,
+	NULL
+};
+
+
+/* helper for accepting an agent request */
+static int send_msg_channel_open_agent(int fd) {
+
+	if (send_msg_channel_open_init(fd, &chan_agent) == DROPBEAR_SUCCESS) {
+		encrypt_packet();
+		return DROPBEAR_SUCCESS;
+	} else {
+		return DROPBEAR_FAILURE;
+	}
+}
+
+/* helper for creating the agent socket-file
+   returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
+static int bindagent(int fd, struct ChanSess * chansess) {
+
+	struct sockaddr_un addr;
+	unsigned int prefix;
+	char path[sizeof(addr.sun_path)], sockfile[sizeof(addr.sun_path)];
+	mode_t mode;
+	int i;
+	uid_t uid;
+	gid_t gid;
+	int ret = DROPBEAR_FAILURE;
+
+	/* drop to user privs to make the dir/file */
+	uid = getuid();
+	gid = getgid();
+	if ((setegid(ses.authstate.pw->pw_gid)) < 0 ||
+		(seteuid(ses.authstate.pw->pw_uid)) < 0) {
+		dropbear_exit("failed to set euid");
+	}
+
+	memset((void*)&addr, 0x0, sizeof(addr));
+	addr.sun_family = AF_UNIX;
+
+	mode = S_IRWXU;
+
+	for (i = 0; i < 20; i++) {
+		genrandom((unsigned char*)&prefix, sizeof(prefix));
+		/* we want 32 bits (8 hex digits) - "/tmp/dropbear-f19c62c0" */
+		snprintf(path, sizeof(path), AGENTDIRPREFIX "%.8x", prefix);
+
+		if (mkdir(path, mode) == 0) {
+			goto bindsocket;
+		}
+		if (errno != EEXIST) {
+			break;
+		}
+	}
+	/* couldn't make a dir */
+	goto out;
+
+bindsocket:
+	/* Format is "/tmp/dropbear-0246dead/auth-d00f7654-23".
+	 * The "23" is the file desc, the random data is to avoid collisions
+	 * between subsequent user processes reusing socket fds (odds are now
+	 * 1/(2^64) */
+	genrandom((unsigned char*)&prefix, sizeof(prefix));
+	snprintf(sockfile, sizeof(sockfile), "auth-%.8x-%d", prefix, fd);
+			
+	snprintf(addr.sun_path, sizeof(addr.sun_path), "%s/%s", path, sockfile);
+
+	if (bind(fd, (struct sockaddr*)&addr, sizeof(addr)) == 0) {
+		chansess->agentdir = m_strdup(path);
+		chansess->agentfile = m_strdup(sockfile);
+		ret = DROPBEAR_SUCCESS;
+	}
+
+
+out:
+	if ((seteuid(uid)) < 0 ||
+		(setegid(gid)) < 0) {
+		dropbear_exit("failed to revert euid");
+	}
+	return ret;
+}
+
+#endif
diff --git a/svr-algo.c b/svr-algo.c
new file mode 100644
index 0000000000000000000000000000000000000000..c0b7823621d3b6f85737e781bf297e542aaf87d2
--- /dev/null
+++ b/svr-algo.c
@@ -0,0 +1,100 @@
+/*
+ * 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 "algo.h"
+#include "dbutil.h"
+
+/* match the first algorithm in the comma-separated list in buf which is
+ * also in localalgos[], or return NULL on failure.
+ * (*goodguess) is set to 1 if the preferred client/server algos match,
+ * 0 otherwise. This is used for checking if the kexalgo/hostkeyalgos are
+ * guessed correctly */
+algo_type * svr_buf_match_algo(buffer* buf, algo_type localalgos[],
+		int *goodguess)
+{
+
+	unsigned char * algolist = NULL;
+	unsigned char * remotealgos[MAX_PROPOSED_ALGO];
+	unsigned int len;
+	unsigned int count, i, j;
+	algo_type * ret = NULL;
+
+	*goodguess = 0;
+
+	/* get the comma-separated list from the buffer ie "algo1,algo2,algo3" */
+	algolist = buf_getstring(buf, &len);
+	/* Debug this */
+	TRACE(("buf_match_algo: %s", algolist))
+	if (len > MAX_PROPOSED_ALGO*(MAX_NAME_LEN+1)) {
+		goto out; /* just a sanity check, no other use */
+	}
+
+	/* remotealgos will contain a list of the strings parsed out */
+	/* We will have at least one string (even if it's just "") */
+	remotealgos[0] = algolist;
+	count = 1;
+	/* Iterate through, replacing ','s with NULs, to split it into
+	 * words. */
+	for (i = 0; i < len; i++) {
+		if (algolist[i] == '\0') {
+			/* someone is trying something strange */
+			goto out;
+		}
+		if (algolist[i] == ',') {
+			algolist[i] = '\0';
+			remotealgos[count] = &algolist[i+1];
+			count++;
+		}
+		if (count == MAX_PROPOSED_ALGO) {
+			break;
+		}
+	}
+
+	/* iterate and find the first match */
+	for (i = 0; i < count; i++) {
+
+		len = strlen(remotealgos[i]);
+
+		for (j = 0; localalgos[j].name != NULL; j++) {
+			if (localalgos[j].usable) {
+				if (len == strlen(localalgos[j].name) &&
+						strncmp(localalgos[j].name, remotealgos[i], len) == 0) {
+					/* set if it was a good guess */
+					if (i == 0 && j == 0) {
+						*goodguess = 1;
+					}
+					/* set the algo to return */
+					ret = &localalgos[j];
+					goto out;
+				}
+			}
+		}
+	}
+
+out:
+	m_free(algolist);
+	return ret;
+}
diff --git a/svr-auth.c b/svr-auth.c
new file mode 100644
index 0000000000000000000000000000000000000000..f0fca3881885b836b6e0d094190c934e0069f91e
--- /dev/null
+++ b/svr-auth.c
@@ -0,0 +1,373 @@
+/*
+ * 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. */
+
+/* This file (auth.c) handles authentication requests, passing it to the
+ * particular type (auth-passwd, auth-pubkey). */
+
+#include "includes.h"
+#include "dbutil.h"
+#include "session.h"
+#include "buffer.h"
+#include "ssh.h"
+#include "packet.h"
+#include "auth.h"
+#include "runopts.h"
+
+static void authclear();
+static int checkusername(unsigned char *username, unsigned int userlen);
+static void send_msg_userauth_banner();
+
+/* initialise the first time for a session, resetting all parameters */
+void svr_authinitialise() {
+
+	ses.authstate.failcount = 0;
+	authclear();
+	
+}
+
+/* Reset the auth state, but don't reset the failcount. This is for if the
+ * user decides to try with a different username etc, and is also invoked
+ * on initialisation */
+static void authclear() {
+	
+	memset(&ses.authstate, 0, sizeof(ses.authstate));
+#ifdef ENABLE_SVR_PUBKEY_AUTH
+	ses.authstate.authtypes |= AUTH_TYPE_PUBKEY;
+#endif
+#if defined(ENABLE_SVR_PASSWORD_AUTH) || defined(ENABLE_SVR_PAM_AUTH)
+	if (!svr_opts.noauthpass) {
+		ses.authstate.authtypes |= AUTH_TYPE_PASSWORD;
+	}
+#endif
+
+}
+
+/* Send a banner message if specified to the client. The client might
+ * ignore this, but possibly serves as a legal "no trespassing" sign */
+static void send_msg_userauth_banner() {
+
+	TRACE(("enter send_msg_userauth_banner"))
+	if (svr_opts.banner == NULL) {
+		TRACE(("leave send_msg_userauth_banner: banner is NULL"))
+		return;
+	}
+
+	CHECKCLEARTOWRITE();
+
+	buf_putbyte(ses.writepayload, SSH_MSG_USERAUTH_BANNER);
+	buf_putstring(ses.writepayload, buf_getptr(svr_opts.banner,
+				svr_opts.banner->len), svr_opts.banner->len);
+	buf_putstring(ses.writepayload, "en", 2);
+
+	encrypt_packet();
+	buf_free(svr_opts.banner);
+	svr_opts.banner = NULL;
+
+	TRACE(("leave send_msg_userauth_banner"))
+}
+
+/* handle a userauth request, check validity, pass to password or pubkey
+ * checking, and handle success or failure */
+void recv_msg_userauth_request() {
+
+	unsigned char *username = NULL, *servicename = NULL, *methodname = NULL;
+	unsigned int userlen, servicelen, methodlen;
+
+	TRACE(("enter recv_msg_userauth_request"))
+
+	/* ignore packets if auth is already done */
+	if (ses.authstate.authdone == 1) {
+		TRACE(("leave recv_msg_userauth_request: authdone already"))
+		return;
+	}
+
+	/* send the banner if it exists, it will only exist once */
+	if (svr_opts.banner) {
+		send_msg_userauth_banner();
+	}
+
+	
+	username = buf_getstring(ses.payload, &userlen);
+	servicename = buf_getstring(ses.payload, &servicelen);
+	methodname = buf_getstring(ses.payload, &methodlen);
+
+	/* only handle 'ssh-connection' currently */
+	if (servicelen != SSH_SERVICE_CONNECTION_LEN
+			&& (strncmp(servicename, SSH_SERVICE_CONNECTION,
+					SSH_SERVICE_CONNECTION_LEN) != 0)) {
+		
+		/* TODO - disconnect here */
+		m_free(username);
+		m_free(servicename);
+		m_free(methodname);
+		dropbear_exit("unknown service in auth");
+	}
+
+	/* user wants to know what methods are supported */
+	if (methodlen == AUTH_METHOD_NONE_LEN &&
+			strncmp(methodname, AUTH_METHOD_NONE,
+				AUTH_METHOD_NONE_LEN) == 0) {
+		TRACE(("recv_msg_userauth_request: 'none' request"))
+		send_msg_userauth_failure(0, 0);
+		goto out;
+	}
+	
+	/* check username is good before continuing */
+	if (checkusername(username, userlen) == DROPBEAR_FAILURE) {
+		/* username is invalid/no shell/etc - send failure */
+		TRACE(("sending checkusername failure"))
+		send_msg_userauth_failure(0, 1);
+		goto out;
+	}
+
+#ifdef ENABLE_SVR_PASSWORD_AUTH
+	if (!svr_opts.noauthpass &&
+			!(svr_opts.norootpass && ses.authstate.pw->pw_uid == 0) ) {
+		/* user wants to try password auth */
+		if (methodlen == AUTH_METHOD_PASSWORD_LEN &&
+				strncmp(methodname, AUTH_METHOD_PASSWORD,
+					AUTH_METHOD_PASSWORD_LEN) == 0) {
+			svr_auth_password();
+			goto out;
+		}
+	}
+#endif
+
+#ifdef ENABLE_SVR_PAM_AUTH
+	if (!svr_opts.noauthpass &&
+			!(svr_opts.norootpass && ses.authstate.pw->pw_uid == 0) ) {
+		/* user wants to try password auth */
+		if (methodlen == AUTH_METHOD_PASSWORD_LEN &&
+				strncmp(methodname, AUTH_METHOD_PASSWORD,
+					AUTH_METHOD_PASSWORD_LEN) == 0) {
+			svr_auth_pam();
+			goto out;
+		}
+	}
+#endif
+
+#ifdef ENABLE_SVR_PUBKEY_AUTH
+	/* user wants to try pubkey auth */
+	if (methodlen == AUTH_METHOD_PUBKEY_LEN &&
+			strncmp(methodname, AUTH_METHOD_PUBKEY,
+				AUTH_METHOD_PUBKEY_LEN) == 0) {
+		svr_auth_pubkey();
+		goto out;
+	}
+#endif
+
+	/* nothing matched, we just fail */
+	send_msg_userauth_failure(0, 1);
+
+out:
+
+	m_free(username);
+	m_free(servicename);
+	m_free(methodname);
+}
+
+/* Check that the username exists, has a non-empty password, and has a valid
+ * shell.
+ * returns DROPBEAR_SUCCESS on valid username, DROPBEAR_FAILURE on failure */
+static int checkusername(unsigned char *username, unsigned int userlen) {
+
+	char* listshell = NULL;
+	char* usershell = NULL;
+	
+	TRACE(("enter checkusername"))
+	if (userlen > MAX_USERNAME_LEN) {
+		return DROPBEAR_FAILURE;
+	}
+
+	/* new user or username has changed */
+	if (ses.authstate.username == NULL ||
+		strcmp(username, ses.authstate.username) != 0) {
+			/* the username needs resetting */
+			if (ses.authstate.username != NULL) {
+				dropbear_log(LOG_WARNING, "client trying multiple usernames from %s",
+							svr_ses.addrstring);
+				m_free(ses.authstate.username);
+			}
+			authclear();
+			ses.authstate.pw = getpwnam((char*)username);
+			ses.authstate.username = m_strdup(username);
+			m_free(ses.authstate.printableuser);
+	}
+
+	/* check that user exists */
+	if (ses.authstate.pw == NULL) {
+		TRACE(("leave checkusername: user '%s' doesn't exist", username))
+		dropbear_log(LOG_WARNING,
+				"login attempt for nonexistent user from %s",
+				svr_ses.addrstring);
+		send_msg_userauth_failure(0, 1);
+		return DROPBEAR_FAILURE;
+	}
+
+	/* We can set it once we know its a real user */
+	ses.authstate.printableuser = m_strdup(ses.authstate.pw->pw_name);
+
+	/* check for non-root if desired */
+	if (svr_opts.norootlogin && ses.authstate.pw->pw_uid == 0) {
+		TRACE(("leave checkusername: root login disabled"))
+		dropbear_log(LOG_WARNING, "root login rejected");
+		send_msg_userauth_failure(0, 1);
+		return DROPBEAR_FAILURE;
+	}
+
+	/* check for an empty password */
+	if (ses.authstate.pw->pw_passwd[0] == '\0') {
+		TRACE(("leave checkusername: empty pword"))
+		dropbear_log(LOG_WARNING, "user '%s' has blank password, rejected",
+				ses.authstate.printableuser);
+		send_msg_userauth_failure(0, 1);
+		return DROPBEAR_FAILURE;
+	}
+
+	TRACE(("shell is %s", ses.authstate.pw->pw_shell))
+
+	/* check that the shell is set */
+	usershell = ses.authstate.pw->pw_shell;
+	if (usershell[0] == '\0') {
+		/* empty shell in /etc/passwd means /bin/sh according to passwd(5) */
+		usershell = "/bin/sh";
+	}
+
+	/* check the shell is valid. If /etc/shells doesn't exist, getusershell()
+	 * should return some standard shells like "/bin/sh" and "/bin/csh" (this
+	 * is platform-specific) */
+	setusershell();
+	while ((listshell = getusershell()) != NULL) {
+		TRACE(("test shell is '%s'", listshell))
+		if (strcmp(listshell, usershell) == 0) {
+			/* have a match */
+			goto goodshell;
+		}
+	}
+	/* no matching shell */
+	endusershell();
+	TRACE(("no matching shell"))
+	dropbear_log(LOG_WARNING, "user '%s' has invalid shell, rejected",
+				ses.authstate.printableuser);
+	send_msg_userauth_failure(0, 1);
+	return DROPBEAR_FAILURE;
+	
+goodshell:
+	endusershell();
+	TRACE(("matching shell"))
+
+	TRACE(("uid = %d", ses.authstate.pw->pw_uid))
+	TRACE(("leave checkusername"))
+	return DROPBEAR_SUCCESS;
+
+}
+
+/* Send a failure message to the client, in responds to a userauth_request.
+ * Partial indicates whether to set the "partial success" flag,
+ * incrfail is whether to count this failure in the failure count (which
+ * is limited. This function also handles disconnection after too many
+ * failures */
+void send_msg_userauth_failure(int partial, int incrfail) {
+
+	buffer *typebuf = NULL;
+
+	TRACE(("enter send_msg_userauth_failure"))
+
+	CHECKCLEARTOWRITE();
+	
+	buf_putbyte(ses.writepayload, SSH_MSG_USERAUTH_FAILURE);
+
+	/* put a list of allowed types */
+	typebuf = buf_new(30); /* long enough for PUBKEY and PASSWORD */
+
+	if (ses.authstate.authtypes & AUTH_TYPE_PUBKEY) {
+		buf_putbytes(typebuf, AUTH_METHOD_PUBKEY, AUTH_METHOD_PUBKEY_LEN);
+		if (ses.authstate.authtypes & AUTH_TYPE_PASSWORD) {
+			buf_putbyte(typebuf, ',');
+		}
+	}
+	
+	if (ses.authstate.authtypes & AUTH_TYPE_PASSWORD) {
+		buf_putbytes(typebuf, AUTH_METHOD_PASSWORD, AUTH_METHOD_PASSWORD_LEN);
+	}
+
+	buf_setpos(typebuf, 0);
+	buf_putstring(ses.writepayload, buf_getptr(typebuf, typebuf->len),
+			typebuf->len);
+	buf_free(typebuf);
+
+	buf_putbyte(ses.writepayload, partial ? 1 : 0);
+	encrypt_packet();
+
+	TRACE(("auth fail: methods %d, '%s'", ses.authstate.authtypes,
+				buf_getptr(typebuf, typebuf->len)));
+
+	if (incrfail) {
+		usleep(300000); /* XXX improve this */
+		ses.authstate.failcount++;
+	}
+
+	if (ses.authstate.failcount >= MAX_AUTH_TRIES) {
+		char * userstr;
+		/* XXX - send disconnect ? */
+		TRACE(("Max auth tries reached, exiting"))
+
+		if (ses.authstate.printableuser == NULL) {
+			userstr = "is invalid";
+		} else {
+			userstr = ses.authstate.printableuser;
+		}
+		dropbear_exit("Max auth tries reached - user '%s' from %s",
+				userstr, svr_ses.addrstring);
+	}
+	
+	TRACE(("leave send_msg_userauth_failure"))
+}
+
+/* Send a success message to the user, and set the "authdone" flag */
+void send_msg_userauth_success() {
+
+	TRACE(("enter send_msg_userauth_success"))
+
+	CHECKCLEARTOWRITE();
+
+	buf_putbyte(ses.writepayload, SSH_MSG_USERAUTH_SUCCESS);
+	encrypt_packet();
+
+	ses.authstate.authdone = 1;
+	ses.connecttimeout = 0;
+
+
+	if (ses.authstate.pw->pw_uid == 0) {
+		ses.allowprivport = 1;
+	}
+
+	/* Remove from the list of pre-auth sockets. Should be m_close(), since if
+	 * we fail, we might end up leaking connection slots, and disallow new
+	 * logins - a nasty situation. */							
+	m_close(svr_ses.childpipe);
+
+	TRACE(("leave send_msg_userauth_success"))
+
+}
diff --git a/svr-authpam.c b/svr-authpam.c
new file mode 100644
index 0000000000000000000000000000000000000000..4e257ae5b7b44baf54ea1f658125c87ffebf6fdb
--- /dev/null
+++ b/svr-authpam.c
@@ -0,0 +1,258 @@
+/*
+ * Dropbear SSH
+ * 
+ * Copyright (c) 2004 Martin Carlsson
+ * Portions (c) 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. */
+
+/* Validates a user password using PAM */
+
+#include "includes.h"
+#include "session.h"
+#include "buffer.h"
+#include "dbutil.h"
+#include "auth.h"
+
+#if defined(HAVE_SECURITY_PAM_APPL_H)
+#include <security/pam_appl.h>
+#elif defined (HAVE_PAM_PAM_APPL_H)
+#include <pam/pam_appl.h>
+#endif
+
+#ifdef ENABLE_SVR_PAM_AUTH
+
+struct UserDataS {
+	char* user;
+	char* passwd;
+};
+
+/* PAM conversation function - for now we only handle one message */
+int 
+pamConvFunc(int num_msg, 
+		const struct pam_message **msg,
+		struct pam_response **respp, 
+		void *appdata_ptr) {
+
+	int rc = PAM_SUCCESS;
+	struct pam_response* resp = NULL;
+	struct UserDataS* userDatap = (struct UserDataS*) appdata_ptr;
+	unsigned int msg_len = 0;
+	unsigned int i = 0;
+
+	const char* message = (*msg)->msg;
+
+	/* make a copy we can strip */
+	char * compare_message = m_strdup(message);
+
+	TRACE(("enter pamConvFunc"))
+
+	if (num_msg != 1) {
+		/* If you're getting here - Dropbear probably can't support your pam
+		 * modules. This whole file is a bit of a hack around lack of
+		 * asynchronocity in PAM anyway. */
+		dropbear_log(LOG_INFO, "pamConvFunc() called with >1 messages: not supported.");
+		return PAM_CONV_ERR;
+	}
+	
+	TRACE(("msg_style is %d", (*msg)->msg_style))
+	if (compare_message) {
+		TRACE(("message is '%s'", compare_message))
+	} else {
+		TRACE(("null message"))
+	}
+
+
+	/* Make the string lowercase. */
+	msg_len = strlen(compare_message);
+	for (i = 0; i < msg_len; i++) {
+		compare_message[i] = tolower(compare_message[i]);
+	}
+
+	/* If the string ends with ": ", remove the space. 
+	   ie "login: " vs "login:" */
+	if (msg_len > 2 
+			&& compare_message[msg_len-2] == ':' 
+			&& compare_message[msg_len-1] == ' ') {
+		compare_message[msg_len-1] = '\0';
+	}
+
+	switch((*msg)->msg_style) {
+
+		case PAM_PROMPT_ECHO_OFF:
+
+			if (!(strcmp(compare_message, "password:") == 0)) {
+				/* We don't recognise the prompt as asking for a password,
+				   so can't handle it. Add more above as required for
+				   different pam modules/implementations */
+				dropbear_log(LOG_NOTICE, "PAM unknown prompt %s (no echo)",
+						compare_message);
+				rc = PAM_CONV_ERR;
+				break;
+			}
+
+			/* You have to read the PAM module-writers' docs (do we look like
+			 * module writers? no.) to find out that the module will
+			 * free the pam_response and its resp element - ie we _must_ malloc
+			 * it here */
+			resp = (struct pam_response*) m_malloc(sizeof(struct pam_response));
+			memset(resp, 0, sizeof(struct pam_response));
+
+			resp->resp = m_strdup(userDatap->passwd);
+			m_burn(userDatap->passwd, strlen(userDatap->passwd));
+			(*respp) = resp;
+			break;
+
+
+		case PAM_PROMPT_ECHO_ON:
+
+			if (!((strcmp(compare_message, "login:" ) == 0) 
+				|| (strcmp(compare_message, "please enter username:") == 0))) {
+				/* We don't recognise the prompt as asking for a username,
+				   so can't handle it. Add more above as required for
+				   different pam modules/implementations */
+				dropbear_log(LOG_NOTICE, "PAM unknown prompt %s (with echo)",
+						compare_message);
+				rc = PAM_CONV_ERR;
+				break;
+			}
+
+			/* You have to read the PAM module-writers' docs (do we look like
+			 * module writers? no.) to find out that the module will
+			 * free the pam_response and its resp element - ie we _must_ malloc
+			 * it here */
+			resp = (struct pam_response*) m_malloc(sizeof(struct pam_response));
+			memset(resp, 0, sizeof(struct pam_response));
+
+			resp->resp = m_strdup(userDatap->user);
+			TRACE(("userDatap->user='%s'", userDatap->user))
+			(*respp) = resp;
+			break;
+
+		default:
+			TRACE(("Unknown message type"))
+			rc = PAM_CONV_ERR;
+			break;      
+	}
+
+	m_free(compare_message);
+	TRACE(("leave pamConvFunc, rc %d", rc))
+
+	return rc;
+}
+
+/* Process a password auth request, sending success or failure messages as
+ * appropriate. To the client it looks like it's doing normal password auth (as
+ * opposed to keyboard-interactive or something), so the pam module has to be
+ * fairly standard (ie just "what's your username, what's your password, OK").
+ *
+ * Keyboard interactive would be a lot nicer, but since PAM is synchronous, it
+ * gets very messy trying to send the interactive challenges, and read the
+ * interactive responses, over the network. */
+void svr_auth_pam() {
+
+	struct UserDataS userData = {NULL, NULL};
+	struct pam_conv pamConv = {
+		pamConvFunc,
+		&userData /* submitted to pamvConvFunc as appdata_ptr */ 
+	};
+
+	pam_handle_t* pamHandlep = NULL;
+
+	unsigned char * password = NULL;
+	unsigned int passwordlen;
+
+	int rc = PAM_SUCCESS;
+	unsigned char changepw;
+
+	/* check if client wants to change password */
+	changepw = buf_getbool(ses.payload);
+	if (changepw) {
+		/* not implemented by this server */
+		send_msg_userauth_failure(0, 1);
+		goto cleanup;
+	}
+
+	password = buf_getstring(ses.payload, &passwordlen);
+
+	/* used to pass data to the PAM conversation function - don't bother with
+	 * strdup() etc since these are touched only by our own conversation
+	 * function (above) which takes care of it */
+	userData.user = ses.authstate.printableuser;
+	userData.passwd = password;
+
+	/* Init pam */
+	if ((rc = pam_start("sshd", NULL, &pamConv, &pamHandlep)) != PAM_SUCCESS) {
+		dropbear_log(LOG_WARNING, "pam_start() failed, rc=%d, %s\n", 
+				rc, pam_strerror(pamHandlep, rc));
+		goto cleanup;
+	}
+
+	/* just to set it to something */
+	if ((rc = pam_set_item(pamHandlep, PAM_TTY, "ssh") != PAM_SUCCESS)) {
+		dropbear_log(LOG_WARNING, "pam_set_item() failed, rc=%d, %s\n", 
+				rc, pam_strerror(pamHandlep, rc));
+		goto cleanup;
+	}
+
+	(void) pam_fail_delay(pamHandlep, 0 /* musec_delay */);
+
+	/* (void) pam_set_item(pamHandlep, PAM_FAIL_DELAY, (void*) pamDelayFunc); */
+
+	if ((rc = pam_authenticate(pamHandlep, 0)) != PAM_SUCCESS) {
+		dropbear_log(LOG_WARNING, "pam_authenticate() failed, rc=%d, %s\n", 
+				rc, pam_strerror(pamHandlep, rc));
+		dropbear_log(LOG_WARNING,
+				"bad PAM password attempt for '%s' from %s",
+				ses.authstate.printableuser,
+				svr_ses.addrstring);
+		send_msg_userauth_failure(0, 1);
+		goto cleanup;
+	}
+
+	if ((rc = pam_acct_mgmt(pamHandlep, 0)) != PAM_SUCCESS) {
+		dropbear_log(LOG_WARNING, "pam_acct_mgmt() failed, rc=%d, %s\n", 
+				rc, pam_strerror(pamHandlep, rc));
+		dropbear_log(LOG_WARNING,
+				"bad PAM password attempt for '%s' from %s",
+				ses.authstate.printableuser,
+				svr_ses.addrstring);
+		send_msg_userauth_failure(0, 1);
+		goto cleanup;
+	}
+
+	/* successful authentication */
+	dropbear_log(LOG_NOTICE, "PAM password auth succeeded for '%s' from %s",
+			ses.authstate.printableuser,
+			svr_ses.addrstring);
+	send_msg_userauth_success();
+
+cleanup:
+	if (password != NULL) {
+		m_burn(password, passwordlen);
+		m_free(password);
+	}
+	if (pamHandlep != NULL) {
+		TRACE(("pam_end"))
+		(void) pam_end(pamHandlep, 0 /* pam_status */);
+	}
+}
+
+#endif /* ENABLE_SVR_PAM_AUTH */
diff --git a/svr-authpasswd.c b/svr-authpasswd.c
new file mode 100644
index 0000000000000000000000000000000000000000..5be1e2ad584e975d61a84ea9a4095d0029a3e22d
--- /dev/null
+++ b/svr-authpasswd.c
@@ -0,0 +1,105 @@
+/*
+ * 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. */
+
+/* Validates a user password */
+
+#include "includes.h"
+#include "session.h"
+#include "buffer.h"
+#include "dbutil.h"
+#include "auth.h"
+
+#ifdef ENABLE_SVR_PASSWORD_AUTH
+
+/* Process a password auth request, sending success or failure messages as
+ * appropriate */
+void svr_auth_password() {
+	
+#ifdef HAVE_SHADOW_H
+	struct spwd *spasswd = NULL;
+#endif
+	char * passwdcrypt = NULL; /* the crypt from /etc/passwd or /etc/shadow */
+	char * testcrypt = NULL; /* crypt generated from the user's password sent */
+	unsigned char * password;
+	unsigned int passwordlen;
+
+	unsigned int changepw;
+
+	passwdcrypt = ses.authstate.pw->pw_passwd;
+#ifdef HAVE_SHADOW_H
+	/* get the shadow password if possible */
+	spasswd = getspnam(ses.authstate.printableuser);
+	if (spasswd != NULL && spasswd->sp_pwdp != NULL) {
+		passwdcrypt = spasswd->sp_pwdp;
+	}
+#endif
+
+#ifdef DEBUG_HACKCRYPT
+	/* debugging crypt for non-root testing with shadows */
+	passwdcrypt = DEBUG_HACKCRYPT;
+#endif
+
+	/* check for empty password - need to do this again here
+	 * since the shadow password may differ to that tested
+	 * in auth.c */
+	if (passwdcrypt[0] == '\0') {
+		dropbear_log(LOG_WARNING, "user '%s' has blank password, rejected",
+				ses.authstate.printableuser);
+		send_msg_userauth_failure(0, 1);
+		return;
+	}
+
+	/* check if client wants to change password */
+	changepw = buf_getbool(ses.payload);
+	if (changepw) {
+		/* not implemented by this server */
+		send_msg_userauth_failure(0, 1);
+		return;
+	}
+
+	password = buf_getstring(ses.payload, &passwordlen);
+
+	/* the first bytes of passwdcrypt are the salt */
+	testcrypt = crypt((char*)password, passwdcrypt);
+	m_burn(password, passwordlen);
+	m_free(password);
+
+	if (strcmp(testcrypt, passwdcrypt) == 0) {
+		/* successful authentication */
+		dropbear_log(LOG_NOTICE, 
+				"password auth succeeded for '%s' from %s",
+				ses.authstate.printableuser,
+				svr_ses.addrstring);
+		send_msg_userauth_success();
+	} else {
+		dropbear_log(LOG_WARNING,
+				"bad password attempt for '%s' from %s",
+				ses.authstate.printableuser,
+				svr_ses.addrstring);
+		send_msg_userauth_failure(0, 1);
+	}
+
+}
+
+#endif
diff --git a/svr-authpubkey.c b/svr-authpubkey.c
new file mode 100644
index 0000000000000000000000000000000000000000..3942bd55ff8c217291cc02c7df3cd1e7c779c607
--- /dev/null
+++ b/svr-authpubkey.c
@@ -0,0 +1,347 @@
+/*
+ * 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. */
+
+/* Process a pubkey auth request */
+
+#include "includes.h"
+#include "session.h"
+#include "dbutil.h"
+#include "buffer.h"
+#include "signkey.h"
+#include "auth.h"
+#include "ssh.h"
+#include "packet.h"
+#include "algo.h"
+
+#ifdef ENABLE_SVR_PUBKEY_AUTH
+
+#define MIN_AUTHKEYS_LINE 10 /* "ssh-rsa AB" - short but doesn't matter */
+#define MAX_AUTHKEYS_LINE 4200 /* max length of a line in authkeys */
+
+static int checkpubkey(unsigned char* algo, unsigned int algolen,
+		unsigned char* keyblob, unsigned int keybloblen);
+static int checkpubkeyperms();
+static void send_msg_userauth_pk_ok(unsigned char* algo, unsigned int algolen,
+		unsigned char* keyblob, unsigned int keybloblen);
+static int checkfileperm(char * filename);
+
+/* process a pubkey auth request, sending success or failure message as
+ * appropriate */
+void svr_auth_pubkey() {
+
+	unsigned char testkey; /* whether we're just checking if a key is usable */
+	unsigned char* algo = NULL; /* pubkey algo */
+	unsigned int algolen;
+	unsigned char* keyblob = NULL;
+	unsigned int keybloblen;
+	buffer * signbuf = NULL;
+	sign_key * key = NULL;
+	char* fp = NULL;
+	int type = -1;
+
+	TRACE(("enter pubkeyauth"))
+
+	/* 0 indicates user just wants to check if key can be used, 1 is an
+	 * actual attempt*/
+	testkey = (buf_getbool(ses.payload) == 0);
+
+	algo = buf_getstring(ses.payload, &algolen);
+	keybloblen = buf_getint(ses.payload);
+	keyblob = buf_getptr(ses.payload, keybloblen);
+
+	/* check if the key is valid */
+	if (checkpubkey(algo, algolen, keyblob, keybloblen) == DROPBEAR_FAILURE) {
+		send_msg_userauth_failure(0, 0);
+		goto out;
+	}
+
+	/* let them know that the key is ok to use */
+	if (testkey) {
+		send_msg_userauth_pk_ok(algo, algolen, keyblob, keybloblen);
+		goto out;
+	}
+
+	/* now we can actually verify the signature */
+	
+	/* get the key */
+	key = new_sign_key();
+	type = DROPBEAR_SIGNKEY_ANY;
+	if (buf_get_pub_key(ses.payload, key, &type) == DROPBEAR_FAILURE) {
+		send_msg_userauth_failure(0, 1);
+		goto out;
+	}
+
+	/* create the data which has been signed - this a string containing
+	 * session_id, concatenated with the payload packet up to the signature */
+	signbuf = buf_new(ses.payload->pos + 4 + SHA1_HASH_SIZE);
+	buf_putstring(signbuf, ses.session_id, SHA1_HASH_SIZE);
+	buf_putbytes(signbuf, ses.payload->data, ses.payload->pos);
+	buf_setpos(signbuf, 0);
+
+	/* ... and finally verify the signature */
+	fp = sign_key_fingerprint(keyblob, keybloblen);
+	if (buf_verify(ses.payload, key, buf_getptr(signbuf, signbuf->len),
+				signbuf->len) == DROPBEAR_SUCCESS) {
+		dropbear_log(LOG_NOTICE,
+				"pubkey auth succeeded for '%s' with key %s from %s",
+				ses.authstate.printableuser, fp, svr_ses.addrstring);
+		send_msg_userauth_success();
+	} else {
+		dropbear_log(LOG_WARNING,
+				"pubkey auth bad signature for '%s' with key %s from %s",
+				ses.authstate.printableuser, fp, svr_ses.addrstring);
+		send_msg_userauth_failure(0, 1);
+	}
+	m_free(fp);
+
+out:
+	/* cleanup stuff */
+	if (signbuf) {
+		buf_free(signbuf);
+	}
+	if (algo) {
+		m_free(algo);
+	}
+	if (key) {
+		sign_key_free(key);
+		key = NULL;
+	}
+	TRACE(("leave pubkeyauth"))
+}
+
+/* Reply that the key is valid for auth, this is sent when the user sends
+ * a straight copy of their pubkey to test, to avoid having to perform
+ * expensive signing operations with a worthless key */
+static void send_msg_userauth_pk_ok(unsigned char* algo, unsigned int algolen,
+		unsigned char* keyblob, unsigned int keybloblen) {
+
+	TRACE(("enter send_msg_userauth_pk_ok"))
+	CHECKCLEARTOWRITE();
+
+	buf_putbyte(ses.writepayload, SSH_MSG_USERAUTH_PK_OK);
+	buf_putstring(ses.writepayload, algo, algolen);
+	buf_putstring(ses.writepayload, keyblob, keybloblen);
+
+	encrypt_packet();
+	TRACE(("leave send_msg_userauth_pk_ok"))
+
+}
+
+/* Checks whether a specified publickey (and associated algorithm) is an
+ * acceptable key for authentication */
+/* Returns DROPBEAR_SUCCESS if key is ok for auth, DROPBEAR_FAILURE otherwise */
+static int checkpubkey(unsigned char* algo, unsigned int algolen,
+		unsigned char* keyblob, unsigned int keybloblen) {
+
+	FILE * authfile = NULL;
+	char * filename = NULL;
+	int ret = DROPBEAR_FAILURE;
+	buffer * line = NULL;
+	unsigned int len, pos;
+	
+	TRACE(("enter checkpubkey"))
+
+	/* check that we can use the algo */
+	if (have_algo(algo, algolen, sshhostkey) == DROPBEAR_FAILURE) {
+		dropbear_log(LOG_WARNING,
+				"pubkey auth attempt with unknown algo for '%s' from %s",
+				ses.authstate.printableuser, svr_ses.addrstring);
+		goto out;
+	}
+
+	/* check file permissions, also whether file exists */
+	if (checkpubkeyperms() == DROPBEAR_FAILURE) {
+		TRACE(("bad authorized_keys permissions, or file doesn't exist"))
+		goto out;
+	}
+
+	/* we don't need to check pw and pw_dir for validity, since
+	 * its been done in checkpubkeyperms. */
+	len = strlen(ses.authstate.pw->pw_dir);
+	/* allocate max required pathname storage,
+	 * = path + "/.ssh/authorized_keys" + '\0' = pathlen + 22 */
+	filename = m_malloc(len + 22);
+	snprintf(filename, len + 22, "%s/.ssh/authorized_keys", 
+				ses.authstate.pw->pw_dir);
+
+	/* open the file */
+	authfile = fopen(filename, "r");
+	if (authfile == NULL) {
+		goto out;
+	}
+	TRACE(("checkpubkey: opened authorized_keys OK"))
+
+	line = buf_new(MAX_AUTHKEYS_LINE);
+
+	/* iterate through the lines */
+	do {
+
+		if (buf_getline(line, authfile) == DROPBEAR_FAILURE) {
+			/* EOF reached */
+			TRACE(("checkpubkey: authorized_keys EOF reached"))
+			break;
+		}
+
+		if (line->len < MIN_AUTHKEYS_LINE) {
+			TRACE(("checkpubkey: line too short"))
+			continue; /* line is too short for it to be a valid key */
+		}
+
+		/* check the key type - this also stops us from using keys
+		 * which have options with them */
+		if (strncmp(buf_getptr(line, algolen), algo, algolen) != 0) {
+			continue;
+		}
+		buf_incrpos(line, algolen);
+		
+		/* check for space (' ') character */
+		if (buf_getbyte(line) != ' ') {
+			TRACE(("checkpubkey: space character expected, isn't there"))
+			continue;
+		}
+
+		/* truncate the line at the space after the base64 data */
+		pos = line->pos;
+		for (len = 0; line->pos < line->len; len++) {
+			if (buf_getbyte(line) == ' ') break;
+		}	
+		buf_setpos(line, pos);
+		buf_setlen(line, line->pos + len);
+
+		TRACE(("checkpubkey: line pos = %d len = %d", line->pos, line->len))
+
+		ret = cmp_base64_key(keyblob, keybloblen, algo, algolen, line);
+		if (ret == DROPBEAR_SUCCESS) {
+			break;
+		}
+
+		/* We continue to the next line otherwise */
+		
+	} while (1);
+
+out:
+	if (authfile) {
+		fclose(authfile);
+	}
+	if (line) {
+		buf_free(line);
+	}
+	m_free(filename);
+	TRACE(("leave checkpubkey: ret=%d", ret))
+	return ret;
+}
+
+
+/* Returns DROPBEAR_SUCCESS if file permissions for pubkeys are ok,
+ * DROPBEAR_FAILURE otherwise.
+ * Checks that the user's homedir, ~/.ssh, and
+ * ~/.ssh/authorized_keys are all owned by either root or the user, and are
+ * g-w, o-w */
+static int checkpubkeyperms() {
+
+	char* filename = NULL; 
+	int ret = DROPBEAR_FAILURE;
+	unsigned int len;
+
+	TRACE(("enter checkpubkeyperms"))
+
+	if (ses.authstate.pw->pw_dir == NULL) {
+		goto out;
+	}
+
+	if ((len = strlen(ses.authstate.pw->pw_dir)) == 0) {
+		goto out;
+	}
+
+	/* allocate max required pathname storage,
+	 * = path + "/.ssh/authorized_keys" + '\0' = pathlen + 22 */
+	filename = m_malloc(len + 22);
+	strncpy(filename, ses.authstate.pw->pw_dir, len+1);
+
+	/* check ~ */
+	if (checkfileperm(filename) != DROPBEAR_SUCCESS) {
+		goto out;
+	}
+
+	/* check ~/.ssh */
+	strncat(filename, "/.ssh", 5); /* strlen("/.ssh") == 5 */
+	if (checkfileperm(filename) != DROPBEAR_SUCCESS) {
+		goto out;
+	}
+
+	/* now check ~/.ssh/authorized_keys */
+	strncat(filename, "/authorized_keys", 16);
+	if (checkfileperm(filename) != DROPBEAR_SUCCESS) {
+		goto out;
+	}
+
+	/* file looks ok, return success */
+	ret = DROPBEAR_SUCCESS;
+	
+out:
+	m_free(filename);
+
+	TRACE(("leave checkpubkeyperms"))
+	return ret;
+}
+
+/* Checks that a file is owned by the user or root, and isn't writable by
+ * group or other */
+/* returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
+static int checkfileperm(char * filename) {
+	struct stat filestat;
+	int badperm = 0;
+
+	TRACE(("enter checkfileperm(%s)", filename))
+
+	if (stat(filename, &filestat) != 0) {
+		TRACE(("leave checkfileperm: stat() != 0"))
+		return DROPBEAR_FAILURE;
+	}
+	/* check ownership - user or root only*/
+	if (filestat.st_uid != ses.authstate.pw->pw_uid
+			&& filestat.st_uid != 0) {
+		badperm = 1;
+		TRACE(("wrong ownership"))
+	}
+	/* check permissions - don't want group or others +w */
+	if (filestat.st_mode & (S_IWGRP | S_IWOTH)) {
+		badperm = 1;
+		TRACE(("wrong perms"))
+	}
+	if (badperm) {
+		if (!ses.authstate.perm_warn) {
+			ses.authstate.perm_warn = 1;
+			dropbear_log(LOG_INFO, "%s must be owned by user or root, and not writable by others", filename);
+		}
+		TRACE(("leave checkfileperm: failure perms/owner"))
+		return DROPBEAR_FAILURE;
+	}
+
+	TRACE(("leave checkfileperm: success"))
+	return DROPBEAR_SUCCESS;
+}
+
+
+#endif 
diff --git a/svr-chansession.c b/svr-chansession.c
new file mode 100644
index 0000000000000000000000000000000000000000..0916e7e6ca008d7ae55798a57ddbc2952c61d258
--- /dev/null
+++ b/svr-chansession.c
@@ -0,0 +1,1003 @@
+/*
+ * 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. */
+
+#include "includes.h"
+#include "packet.h"
+#include "buffer.h"
+#include "session.h"
+#include "dbutil.h"
+#include "channel.h"
+#include "chansession.h"
+#include "sshpty.h"
+#include "termcodes.h"
+#include "ssh.h"
+#include "random.h"
+#include "utmp.h"
+#include "x11fwd.h"
+#include "agentfwd.h"
+#include "runopts.h"
+
+/* Handles sessions (either shells or programs) requested by the client */
+
+static int sessioncommand(struct Channel *channel, struct ChanSess *chansess,
+		int iscmd, int issubsys);
+static int sessionpty(struct ChanSess * chansess);
+static int sessionsignal(struct ChanSess *chansess);
+static int noptycommand(struct Channel *channel, struct ChanSess *chansess);
+static int ptycommand(struct Channel *channel, struct ChanSess *chansess);
+static int sessionwinchange(struct ChanSess *chansess);
+static void execchild(struct ChanSess *chansess);
+static void addchildpid(struct ChanSess *chansess, pid_t pid);
+static void sesssigchild_handler(int val);
+static void closechansess(struct Channel *channel);
+static int newchansess(struct Channel *channel);
+static void chansessionrequest(struct Channel *channel);
+
+static void send_exitsignalstatus(struct Channel *channel);
+static void send_msg_chansess_exitstatus(struct Channel * channel,
+		struct ChanSess * chansess);
+static void send_msg_chansess_exitsignal(struct Channel * channel,
+		struct ChanSess * chansess);
+static int sesscheckclose(struct Channel *channel);
+static void get_termmodes(struct ChanSess *chansess);
+
+
+/* required to clear environment */
+extern char** environ;
+
+static int sesscheckclose(struct Channel *channel) {
+	struct ChanSess *chansess = (struct ChanSess*)channel->typedata;
+	return chansess->exit.exitpid >= 0;
+}
+
+/* Handler for childs exiting, store the state for return to the client */
+
+/* There's a particular race we have to watch out for: if the forked child
+ * executes, exits, and this signal-handler is called, all before the parent
+ * gets to run, then the childpids[] array won't have the pid in it. Hence we
+ * use the svr_ses.lastexit struct to hold the exit, which is then compared by
+ * the parent when it runs. This work correctly at least in the case of a
+ * single shell spawned (ie the usual case) */
+static void sesssigchild_handler(int UNUSED(dummy)) {
+
+	int status;
+	pid_t pid;
+	unsigned int i;
+	struct sigaction sa_chld;
+	struct exitinfo *exit = NULL;
+
+	TRACE(("enter sigchld handler"))
+	while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
+		/* find the corresponding chansess */
+		for (i = 0; i < svr_ses.childpidsize; i++) {
+			if (svr_ses.childpids[i].pid == pid) {
+
+				exit = &svr_ses.childpids[i].chansess->exit;
+				break;
+			}
+		}
+
+		/* If the pid wasn't matched, then we might have hit the race mentioned
+		 * above. So we just store the info for the parent to deal with */
+		if (i == svr_ses.childpidsize) {
+			exit = &svr_ses.lastexit;
+		}
+
+		exit->exitpid = pid;
+		if (WIFEXITED(status)) {
+			exit->exitstatus = WEXITSTATUS(status);
+		}
+		if (WIFSIGNALED(status)) {
+			exit->exitsignal = WTERMSIG(status);
+#if !defined(AIX) && defined(WCOREDUMP)
+			exit->exitcore = WCOREDUMP(status);
+#else
+			exit->exitcore = 0;
+#endif
+		} else {
+			/* we use this to determine how pid exited */
+			exit->exitsignal = -1;
+		}
+		exit = NULL;
+	}
+
+	
+	sa_chld.sa_handler = sesssigchild_handler;
+	sa_chld.sa_flags = SA_NOCLDSTOP;
+	sigaction(SIGCHLD, &sa_chld, NULL);
+	TRACE(("leave sigchld handler"))
+}
+
+/* send the exit status or the signal causing termination for a session */
+/* XXX server */
+static void send_exitsignalstatus(struct Channel *channel) {
+
+	struct ChanSess *chansess = (struct ChanSess*)channel->typedata;
+
+	if (chansess->exit.exitpid >= 0) {
+		if (chansess->exit.exitsignal > 0) {
+			send_msg_chansess_exitsignal(channel, chansess);
+		} else {
+			send_msg_chansess_exitstatus(channel, chansess);
+		}
+	}
+}
+
+/* send the exitstatus to the client */
+static void send_msg_chansess_exitstatus(struct Channel * channel,
+		struct ChanSess * chansess) {
+
+	dropbear_assert(chansess->exit.exitpid != -1);
+	dropbear_assert(chansess->exit.exitsignal == -1);
+
+	CHECKCLEARTOWRITE();
+
+	buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_REQUEST);
+	buf_putint(ses.writepayload, channel->remotechan);
+	buf_putstring(ses.writepayload, "exit-status", 11);
+	buf_putbyte(ses.writepayload, 0); /* boolean FALSE */
+	buf_putint(ses.writepayload, chansess->exit.exitstatus);
+
+	encrypt_packet();
+
+}
+
+/* send the signal causing the exit to the client */
+static void send_msg_chansess_exitsignal(struct Channel * channel,
+		struct ChanSess * chansess) {
+
+	int i;
+	char* signame = NULL;
+
+	dropbear_assert(chansess->exit.exitpid != -1);
+	dropbear_assert(chansess->exit.exitsignal > 0);
+
+	CHECKCLEARTOWRITE();
+
+	/* we check that we can match a signal name, otherwise
+	 * don't send anything */
+	for (i = 0; signames[i].name != NULL; i++) {
+		if (signames[i].signal == chansess->exit.exitsignal) {
+			signame = signames[i].name;
+			break;
+		}
+	}
+
+	if (signame == NULL) {
+		return;
+	}
+
+	buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_REQUEST);
+	buf_putint(ses.writepayload, channel->remotechan);
+	buf_putstring(ses.writepayload, "exit-signal", 11);
+	buf_putbyte(ses.writepayload, 0); /* boolean FALSE */
+	buf_putstring(ses.writepayload, signame, strlen(signame));
+	buf_putbyte(ses.writepayload, chansess->exit.exitcore);
+	buf_putstring(ses.writepayload, "", 0); /* error msg */
+	buf_putstring(ses.writepayload, "", 0); /* lang */
+
+	encrypt_packet();
+}
+
+/* set up a session channel */
+static int newchansess(struct Channel *channel) {
+
+	struct ChanSess *chansess;
+
+	dropbear_assert(channel->typedata == NULL);
+
+	chansess = (struct ChanSess*)m_malloc(sizeof(struct ChanSess));
+	chansess->cmd = NULL;
+	chansess->pid = 0;
+
+	/* pty details */
+	chansess->master = -1;
+	chansess->slave = -1;
+	chansess->tty = NULL;
+	chansess->term = NULL;
+
+	chansess->exit.exitpid = -1;
+
+	channel->typedata = chansess;
+
+#ifndef DISABLE_X11FWD
+	chansess->x11listener = NULL;
+	chansess->x11authprot = NULL;
+	chansess->x11authcookie = NULL;
+#endif
+
+#ifndef DISABLE_AGENTFWD
+	chansess->agentlistener = NULL;
+	chansess->agentfile = NULL;
+	chansess->agentdir = NULL;
+#endif
+
+	return 0;
+
+}
+
+/* clean a session channel */
+static void closechansess(struct Channel *channel) {
+
+	struct ChanSess *chansess;
+	unsigned int i;
+	struct logininfo *li;
+
+	chansess = (struct ChanSess*)channel->typedata;
+
+	send_exitsignalstatus(channel);
+
+	TRACE(("enter closechansess"))
+	if (chansess == NULL) {
+		TRACE(("leave closechansess: chansess == NULL"))
+		return;
+	}
+
+	m_free(chansess->cmd);
+	m_free(chansess->term);
+
+	if (chansess->tty) {
+		/* write the utmp/wtmp login record */
+		li = login_alloc_entry(chansess->pid, ses.authstate.username,
+				ses.remotehost, chansess->tty);
+		login_logout(li);
+		login_free_entry(li);
+
+		pty_release(chansess->tty);
+		m_free(chansess->tty);
+	}
+
+#ifndef DISABLE_X11FWD
+	x11cleanup(chansess);
+#endif
+
+#ifndef DISABLE_AGENTFWD
+	agentcleanup(chansess);
+#endif
+
+	/* clear child pid entries */
+	for (i = 0; i < svr_ses.childpidsize; i++) {
+		if (svr_ses.childpids[i].chansess == chansess) {
+			dropbear_assert(svr_ses.childpids[i].pid > 0);
+			TRACE(("closing pid %d", svr_ses.childpids[i].pid))
+			TRACE(("exitpid = %d", chansess->exit.exitpid))
+			svr_ses.childpids[i].pid = -1;
+			svr_ses.childpids[i].chansess = NULL;
+		}
+	}
+				
+	m_free(chansess);
+
+	TRACE(("leave closechansess"))
+}
+
+/* Handle requests for a channel. These can be execution requests,
+ * or x11/authagent forwarding. These are passed to appropriate handlers */
+static void chansessionrequest(struct Channel *channel) {
+
+	unsigned char * type = NULL;
+	unsigned int typelen;
+	unsigned char wantreply;
+	int ret = 1;
+	struct ChanSess *chansess;
+
+	TRACE(("enter chansessionrequest"))
+
+	type = buf_getstring(ses.payload, &typelen);
+	wantreply = buf_getbool(ses.payload);
+
+	if (typelen > MAX_NAME_LEN) {
+		TRACE(("leave chansessionrequest: type too long")) /* XXX send error?*/
+		goto out;
+	}
+
+	chansess = (struct ChanSess*)channel->typedata;
+	dropbear_assert(chansess != NULL);
+	TRACE(("type is %s", type))
+
+	if (strcmp(type, "window-change") == 0) {
+		ret = sessionwinchange(chansess);
+	} else if (strcmp(type, "shell") == 0) {
+		ret = sessioncommand(channel, chansess, 0, 0);
+	} else if (strcmp(type, "pty-req") == 0) {
+		ret = sessionpty(chansess);
+	} else if (strcmp(type, "exec") == 0) {
+		ret = sessioncommand(channel, chansess, 1, 0);
+	} else if (strcmp(type, "subsystem") == 0) {
+		ret = sessioncommand(channel, chansess, 1, 1);
+#ifndef DISABLE_X11FWD
+	} else if (strcmp(type, "x11-req") == 0) {
+		ret = x11req(chansess);
+#endif
+#ifndef DISABLE_AGENTFWD
+	} else if (strcmp(type, "auth-agent-req@openssh.com") == 0) {
+		ret = agentreq(chansess);
+#endif
+	} else if (strcmp(type, "signal") == 0) {
+		ret = sessionsignal(chansess);
+	} else {
+		/* etc, todo "env", "subsystem" */
+	}
+
+out:
+
+	if (wantreply) {
+		if (ret == DROPBEAR_SUCCESS) {
+			send_msg_channel_success(channel);
+		} else {
+			send_msg_channel_failure(channel);
+		}
+	}
+
+	m_free(type);
+	TRACE(("leave chansessionrequest"))
+}
+
+
+/* Send a signal to a session's process as requested by the client*/
+static int sessionsignal(struct ChanSess *chansess) {
+
+	int sig = 0;
+	unsigned char* signame = NULL;
+	int i;
+
+	if (chansess->pid == 0) {
+		/* haven't got a process pid yet */
+		return DROPBEAR_FAILURE;
+	}
+
+	signame = buf_getstring(ses.payload, NULL);
+
+	i = 0;
+	while (signames[i].name != 0) {
+		if (strcmp(signames[i].name, signame) == 0) {
+			sig = signames[i].signal;
+			break;
+		}
+		i++;
+	}
+
+	m_free(signame);
+
+	if (sig == 0) {
+		/* failed */
+		return DROPBEAR_FAILURE;
+	}
+			
+	if (kill(chansess->pid, sig) < 0) {
+		return DROPBEAR_FAILURE;
+	} 
+
+	return DROPBEAR_SUCCESS;
+}
+
+/* Let the process know that the window size has changed, as notified from the
+ * client. Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
+static int sessionwinchange(struct ChanSess *chansess) {
+
+	int termc, termr, termw, termh;
+
+	if (chansess->master < 0) {
+		/* haven't got a pty yet */
+		return DROPBEAR_FAILURE;
+	}
+			
+	termc = buf_getint(ses.payload);
+	termr = buf_getint(ses.payload);
+	termw = buf_getint(ses.payload);
+	termh = buf_getint(ses.payload);
+	
+	pty_change_window_size(chansess->master, termr, termc, termw, termh);
+
+	return DROPBEAR_FAILURE;
+}
+
+static void get_termmodes(struct ChanSess *chansess) {
+
+	struct termios termio;
+	unsigned char opcode;
+	unsigned int value;
+	const struct TermCode * termcode;
+	unsigned int len;
+
+	TRACE(("enter get_termmodes"))
+
+	/* Term modes */
+	/* We'll ignore errors and continue if we can't set modes.
+	 * We're ignoring baud rates since they seem evil */
+	if (tcgetattr(chansess->master, &termio) == -1) {
+		return;
+	}
+
+	len = buf_getint(ses.payload);
+	TRACE(("term mode str %d p->l %d p->p %d", 
+				len, ses.payload->len , ses.payload->pos));
+	if (len != ses.payload->len - ses.payload->pos) {
+		dropbear_exit("bad term mode string");
+	}
+
+	if (len == 0) {
+		TRACE(("leave get_termmodes: empty terminal modes string"))
+		return;
+	}
+
+	while (((opcode = buf_getbyte(ses.payload)) != 0x00) && opcode <= 159) {
+
+		/* must be before checking type, so that value is consumed even if
+		 * we don't use it */
+		value = buf_getint(ses.payload);
+
+		/* handle types of code */
+		if (opcode > MAX_TERMCODE) {
+			continue;
+		}
+		termcode = &termcodes[(unsigned int)opcode];
+		
+
+		switch (termcode->type) {
+
+			case TERMCODE_NONE:
+				break;
+
+			case TERMCODE_CONTROLCHAR:
+				termio.c_cc[termcode->mapcode] = value;
+				break;
+
+			case TERMCODE_INPUT:
+				if (value) {
+					termio.c_iflag |= termcode->mapcode;
+				} else {
+					termio.c_iflag &= ~(termcode->mapcode);
+				}
+				break;
+
+			case TERMCODE_OUTPUT:
+				if (value) {
+					termio.c_oflag |= termcode->mapcode;
+				} else {
+					termio.c_oflag &= ~(termcode->mapcode);
+				}
+				break;
+
+			case TERMCODE_LOCAL:
+				if (value) {
+					termio.c_lflag |= termcode->mapcode;
+				} else {
+					termio.c_lflag &= ~(termcode->mapcode);
+				}
+				break;
+
+			case TERMCODE_CONTROL:
+				if (value) {
+					termio.c_cflag |= termcode->mapcode;
+				} else {
+					termio.c_cflag &= ~(termcode->mapcode);
+				}
+				break;
+				
+		}
+	}
+	if (tcsetattr(chansess->master, TCSANOW, &termio) < 0) {
+		dropbear_log(LOG_INFO, "error setting terminal attributes");
+	}
+	TRACE(("leave get_termmodes"))
+}
+
+/* Set up a session pty which will be used to execute the shell or program.
+ * The pty is allocated now, and kept for when the shell/program executes.
+ * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
+static int sessionpty(struct ChanSess * chansess) {
+
+	unsigned int termlen;
+	unsigned char namebuf[65];
+
+	TRACE(("enter sessionpty"))
+	chansess->term = buf_getstring(ses.payload, &termlen);
+	if (termlen > MAX_TERM_LEN) {
+		/* TODO send disconnect ? */
+		TRACE(("leave sessionpty: term len too long"))
+		return DROPBEAR_FAILURE;
+	}
+
+	/* allocate the pty */
+	if (chansess->master != -1) {
+		dropbear_exit("multiple pty requests");
+	}
+	if (pty_allocate(&chansess->master, &chansess->slave, namebuf, 64) == 0) {
+		TRACE(("leave sessionpty: failed to allocate pty"))
+		return DROPBEAR_FAILURE;
+	}
+	
+	chansess->tty = (char*)m_strdup(namebuf);
+	if (!chansess->tty) {
+		dropbear_exit("out of memory"); /* TODO disconnect */
+	}
+
+	pty_setowner(ses.authstate.pw, chansess->tty);
+
+	/* Set up the rows/col counts */
+	sessionwinchange(chansess);
+
+	/* Read the terminal modes */
+	get_termmodes(chansess);
+
+	TRACE(("leave sessionpty"))
+	return DROPBEAR_SUCCESS;
+}
+
+/* Handle a command request from the client. This is used for both shell
+ * and command-execution requests, and passes the command to
+ * noptycommand or ptycommand as appropriate.
+ * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
+static int sessioncommand(struct Channel *channel, struct ChanSess *chansess,
+		int iscmd, int issubsys) {
+
+	unsigned int cmdlen;
+	int ret;
+
+	TRACE(("enter sessioncommand"))
+
+	if (chansess->cmd != NULL) {
+		/* Note that only one command can _succeed_. The client might try
+		 * one command (which fails), then try another. Ie fallback
+		 * from sftp to scp */
+		return DROPBEAR_FAILURE;
+	}
+
+	if (iscmd) {
+		/* "exec" */
+		chansess->cmd = buf_getstring(ses.payload, &cmdlen);
+
+		if (cmdlen > MAX_CMD_LEN) {
+			m_free(chansess->cmd);
+			/* TODO - send error - too long ? */
+			return DROPBEAR_FAILURE;
+		}
+		if (issubsys) {
+#ifdef SFTPSERVER_PATH
+			if ((cmdlen == 4) && strncmp(chansess->cmd, "sftp", 4) == 0) {
+				m_free(chansess->cmd);
+				chansess->cmd = m_strdup(SFTPSERVER_PATH);
+			} else 
+#endif
+			{
+				m_free(chansess->cmd);
+				return DROPBEAR_FAILURE;
+			}
+		}
+	}
+
+	if (chansess->term == NULL) {
+		/* no pty */
+		ret = noptycommand(channel, chansess);
+	} else {
+		/* want pty */
+		ret = ptycommand(channel, chansess);
+	}
+
+	if (ret == DROPBEAR_FAILURE) {
+		m_free(chansess->cmd);
+	}
+	return ret;
+}
+
+/* Execute a command and set up redirection of stdin/stdout/stderr without a
+ * pty.
+ * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
+static int noptycommand(struct Channel *channel, struct ChanSess *chansess) {
+
+	int infds[2];
+	int outfds[2];
+	int errfds[2];
+	pid_t pid;
+	unsigned int i;
+
+	TRACE(("enter noptycommand"))
+
+	/* redirect stdin/stdout/stderr */
+	if (pipe(infds) != 0)
+		return DROPBEAR_FAILURE;
+	if (pipe(outfds) != 0)
+		return DROPBEAR_FAILURE;
+	if (pipe(errfds) != 0)
+		return DROPBEAR_FAILURE;
+
+#ifdef __uClinux__
+	pid = vfork();
+#else
+	pid = fork();
+#endif
+
+	if (pid < 0)
+		return DROPBEAR_FAILURE;
+
+	if (!pid) {
+		/* child */
+
+		/* redirect stdin/stdout */
+#define FDIN 0
+#define FDOUT 1
+		if ((dup2(infds[FDIN], STDIN_FILENO) < 0) ||
+			(dup2(outfds[FDOUT], STDOUT_FILENO) < 0) ||
+			(dup2(errfds[FDOUT], STDERR_FILENO) < 0)) {
+			TRACE(("leave noptycommand: error redirecting FDs"))
+			return DROPBEAR_FAILURE;
+		}
+
+		close(infds[FDOUT]);
+		close(infds[FDIN]);
+		close(outfds[FDIN]);
+		close(outfds[FDOUT]);
+		close(errfds[FDIN]);
+		close(errfds[FDOUT]);
+
+		execchild(chansess);
+		/* not reached */
+
+	} else {
+		/* parent */
+		TRACE(("continue noptycommand: parent"))
+		chansess->pid = pid;
+
+		addchildpid(chansess, pid);
+
+		if (svr_ses.lastexit.exitpid != -1) {
+			/* The child probably exited and the signal handler triggered
+			 * possibly before we got around to adding the childpid. So we fill
+			 * out it's data manually */
+			for (i = 0; i < svr_ses.childpidsize; i++) {
+				if (svr_ses.childpids[i].pid == pid) {
+					svr_ses.childpids[i].chansess->exit = svr_ses.lastexit;
+					svr_ses.lastexit.exitpid = -1;
+				}
+			}
+		}
+
+
+		close(infds[FDIN]);
+		close(outfds[FDOUT]);
+		close(errfds[FDOUT]);
+		channel->writefd = infds[FDOUT];
+		channel->readfd = outfds[FDIN];
+		channel->errfd = errfds[FDIN];
+		ses.maxfd = MAX(ses.maxfd, channel->writefd);
+		ses.maxfd = MAX(ses.maxfd, channel->readfd);
+		ses.maxfd = MAX(ses.maxfd, channel->errfd);
+
+		setnonblocking(channel->readfd);
+		setnonblocking(channel->writefd);
+		setnonblocking(channel->errfd);
+
+	}
+#undef FDIN
+#undef FDOUT
+
+	TRACE(("leave noptycommand"))
+	return DROPBEAR_SUCCESS;
+}
+
+/* Execute a command or shell within a pty environment, and set up
+ * redirection as appropriate.
+ * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
+static int ptycommand(struct Channel *channel, struct ChanSess *chansess) {
+
+	pid_t pid;
+	struct logininfo *li = NULL;
+#ifdef DO_MOTD
+	buffer * motdbuf = NULL;
+	int len;
+	struct stat sb;
+	char *hushpath = NULL;
+#endif
+
+	TRACE(("enter ptycommand"))
+
+	/* we need to have a pty allocated */
+	if (chansess->master == -1 || chansess->tty == NULL) {
+		dropbear_log(LOG_WARNING, "no pty was allocated, couldn't execute");
+		return DROPBEAR_FAILURE;
+	}
+	
+#ifdef __uClinux__
+	pid = vfork();
+#else
+	pid = fork();
+#endif
+	if (pid < 0)
+		return DROPBEAR_FAILURE;
+
+	if (pid == 0) {
+		/* child */
+		
+		/* redirect stdin/stdout/stderr */
+		close(chansess->master);
+
+		pty_make_controlling_tty(&chansess->slave, chansess->tty);
+		
+		if ((dup2(chansess->slave, STDIN_FILENO) < 0) ||
+			(dup2(chansess->slave, STDERR_FILENO) < 0) ||
+			(dup2(chansess->slave, STDOUT_FILENO) < 0)) {
+			TRACE(("leave ptycommand: error redirecting filedesc"))
+			return DROPBEAR_FAILURE;
+		}
+
+		close(chansess->slave);
+
+		/* write the utmp/wtmp login record - must be after changing the
+		 * terminal used for stdout with the dup2 above */
+		li= login_alloc_entry(getpid(), ses.authstate.username,
+				ses.remotehost, chansess->tty);
+		login_login(li);
+		login_free_entry(li);
+
+		m_free(chansess->tty);
+
+#ifdef DO_MOTD
+		if (svr_opts.domotd) {
+			/* don't show the motd if ~/.hushlogin exists */
+
+			/* 11 == strlen("/hushlogin\0") */
+			len = strlen(ses.authstate.pw->pw_dir) + 11; 
+
+			hushpath = m_malloc(len);
+			snprintf(hushpath, len, "%s/hushlogin", ses.authstate.pw->pw_dir);
+
+			if (stat(hushpath, &sb) < 0) {
+				/* more than a screenful is stupid IMHO */
+				motdbuf = buf_new(80 * 25);
+				if (buf_readfile(motdbuf, MOTD_FILENAME) == DROPBEAR_SUCCESS) {
+					buf_setpos(motdbuf, 0);
+					while (motdbuf->pos != motdbuf->len) {
+						len = motdbuf->len - motdbuf->pos;
+						len = write(STDOUT_FILENO, 
+								buf_getptr(motdbuf, len), len);
+						buf_incrpos(motdbuf, len);
+					}
+				}
+				buf_free(motdbuf);
+			}
+			m_free(hushpath);
+		}
+#endif /* DO_MOTD */
+
+		execchild(chansess);
+		/* not reached */
+
+	} else {
+		/* parent */
+		TRACE(("continue ptycommand: parent"))
+		chansess->pid = pid;
+
+		/* add a child pid */
+		addchildpid(chansess, pid);
+
+		close(chansess->slave);
+		channel->writefd = chansess->master;
+		channel->readfd = chansess->master;
+		/* don't need to set stderr here */
+		ses.maxfd = MAX(ses.maxfd, chansess->master);
+
+		setnonblocking(chansess->master);
+
+	}
+
+	TRACE(("leave ptycommand"))
+	return DROPBEAR_SUCCESS;
+}
+
+/* Add the pid of a child to the list for exit-handling */
+static void addchildpid(struct ChanSess *chansess, pid_t pid) {
+
+	unsigned int i;
+	for (i = 0; i < svr_ses.childpidsize; i++) {
+		if (svr_ses.childpids[i].pid == -1) {
+			break;
+		}
+	}
+
+	/* need to increase size */
+	if (i == svr_ses.childpidsize) {
+		svr_ses.childpids = (struct ChildPid*)m_realloc(svr_ses.childpids,
+				sizeof(struct ChildPid) * (svr_ses.childpidsize+1));
+		svr_ses.childpidsize++;
+	}
+	
+	svr_ses.childpids[i].pid = pid;
+	svr_ses.childpids[i].chansess = chansess;
+
+}
+
+/* Clean up, drop to user privileges, set up the environment and execute
+ * the command/shell. This function does not return. */
+static void execchild(struct ChanSess *chansess) {
+
+	char *argv[4];
+	char * usershell = NULL;
+	char * baseshell = NULL;
+	unsigned int i;
+
+    /* with uClinux we'll have vfork()ed, so don't want to overwrite the
+     * hostkey. can't think of a workaround to clear it */
+#ifndef __uClinux__
+	/* wipe the hostkey */
+	sign_key_free(svr_opts.hostkey);
+	svr_opts.hostkey = NULL;
+
+	/* overwrite the prng state */
+	reseedrandom();
+#endif
+
+	/* close file descriptors except stdin/stdout/stderr
+	 * Need to be sure FDs are closed here to avoid reading files as root */
+	for (i = 3; i <= (unsigned int)ses.maxfd; i++) {
+		m_close(i);
+	}
+
+	/* clear environment */
+	/* if we're debugging using valgrind etc, we need to keep the LD_PRELOAD
+	 * etc. This is hazardous, so should only be used for debugging. */
+#ifndef DEBUG_VALGRIND
+#ifdef HAVE_CLEARENV
+	clearenv();
+#else /* don't HAVE_CLEARENV */
+	/* Yay for posix. */
+	if (environ) {
+		environ[0] = NULL;
+	}
+#endif /* HAVE_CLEARENV */
+#endif /* DEBUG_VALGRIND */
+
+	/* We can only change uid/gid as root ... */
+	if (getuid() == 0) {
+
+		if ((setgid(ses.authstate.pw->pw_gid) < 0) ||
+			(initgroups(ses.authstate.pw->pw_name, 
+						ses.authstate.pw->pw_gid) < 0)) {
+			dropbear_exit("error changing user group");
+		}
+		if (setuid(ses.authstate.pw->pw_uid) < 0) {
+			dropbear_exit("error changing user");
+		}
+	} else {
+		/* ... but if the daemon is the same uid as the requested uid, we don't
+		 * need to */
+
+		/* XXX - there is a minor issue here, in that if there are multiple
+		 * usernames with the same uid, but differing groups, then the
+		 * differing groups won't be set (as with initgroups()). The solution
+		 * is for the sysadmin not to give out the UID twice */
+		if (getuid() != ses.authstate.pw->pw_uid) {
+			dropbear_exit("couldn't	change user as non-root");
+		}
+	}
+
+	/* an empty shell should be interpreted as "/bin/sh" */
+	if (ses.authstate.pw->pw_shell[0] == '\0') {
+		usershell = "/bin/sh";
+	} else {
+		usershell = ses.authstate.pw->pw_shell;
+	}
+
+	/* set env vars */
+	addnewvar("USER", ses.authstate.pw->pw_name);
+	addnewvar("LOGNAME", ses.authstate.pw->pw_name);
+	addnewvar("HOME", ses.authstate.pw->pw_dir);
+	addnewvar("SHELL", usershell);
+	if (chansess->term != NULL) {
+		addnewvar("TERM", chansess->term);
+	}
+
+	/* change directory */
+	if (chdir(ses.authstate.pw->pw_dir) < 0) {
+		dropbear_exit("error changing directory");
+	}
+
+#ifndef DISABLE_X11FWD
+	/* set up X11 forwarding if enabled */
+	x11setauth(chansess);
+#endif
+#ifndef DISABLE_AGENTFWD
+	/* set up agent env variable */
+	agentset(chansess);
+#endif
+
+	/* Re-enable SIGPIPE for the executed process */
+	if (signal(SIGPIPE, SIG_DFL) == SIG_ERR) {
+		dropbear_exit("signal() error");
+	}
+
+	baseshell = basename(usershell);
+
+	if (chansess->cmd != NULL) {
+		argv[0] = baseshell;
+	} else {
+		/* a login shell should be "-bash" for "/bin/bash" etc */
+		int len = strlen(baseshell) + 2; /* 2 for "-" */
+		argv[0] = (char*)m_malloc(len);
+		snprintf(argv[0], len, "-%s", baseshell);
+	}
+
+	if (chansess->cmd != NULL) {
+		argv[1] = "-c";
+		argv[2] = chansess->cmd;
+		argv[3] = NULL;
+	} else {
+		/* construct a shell of the form "-bash" etc */
+		argv[1] = NULL;
+	}
+
+	execv(usershell, argv);
+
+	/* only reached on error */
+	dropbear_exit("child failed");
+}
+
+const struct ChanType svrchansess = {
+	0, /* sepfds */
+	"session", /* name */
+	newchansess, /* inithandler */
+	sesscheckclose, /* checkclosehandler */
+	chansessionrequest, /* reqhandler */
+	closechansess, /* closehandler */
+};
+
+
+/* Set up the general chansession environment, in particular child-exit
+ * handling */
+void svr_chansessinitialise() {
+
+	struct sigaction sa_chld;
+
+	/* single child process intially */
+	svr_ses.childpids = (struct ChildPid*)m_malloc(sizeof(struct ChildPid));
+	svr_ses.childpids[0].pid = -1; /* unused */
+	svr_ses.childpids[0].chansess = NULL;
+	svr_ses.childpidsize = 1;
+	svr_ses.lastexit.exitpid = -1; /* Nothing has exited yet */
+	sa_chld.sa_handler = sesssigchild_handler;
+	sa_chld.sa_flags = SA_NOCLDSTOP;
+	if (sigaction(SIGCHLD, &sa_chld, NULL) < 0) {
+		dropbear_exit("signal() error");
+	}
+	
+}
+
+/* add a new environment variable, allocating space for the entry */
+void addnewvar(const char* param, const char* var) {
+
+	char* newvar = NULL;
+	int plen, vlen;
+
+	plen = strlen(param);
+	vlen = strlen(var);
+
+	newvar = m_malloc(plen + vlen + 2); /* 2 is for '=' and '\0' */
+	memcpy(newvar, param, plen);
+	newvar[plen] = '=';
+	memcpy(&newvar[plen+1], var, vlen);
+	newvar[plen+vlen+1] = '\0';
+	if (putenv(newvar) < 0) {
+		dropbear_exit("environ error");
+	}
+}
diff --git a/svr-kex.c b/svr-kex.c
new file mode 100644
index 0000000000000000000000000000000000000000..a9954bbc576ea070ab2db246f269d37575d6d87f
--- /dev/null
+++ b/svr-kex.c
@@ -0,0 +1,104 @@
+/*
+ * Dropbear - a SSH2 server
+ * 
+ * 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 "algo.h"
+#include "buffer.h"
+#include "session.h"
+#include "kex.h"
+#include "ssh.h"
+#include "packet.h"
+#include "bignum.h"
+#include "random.h"
+#include "runopts.h"
+
+
+static void send_msg_kexdh_reply(mp_int *dh_e);
+
+/* Handle a diffie-hellman key exchange initialisation. This involves
+ * calculating a session key reply value, and corresponding hash. These
+ * are carried out by send_msg_kexdh_reply(). recv_msg_kexdh_init() calls
+ * that function, then brings the new keys into use */
+void recv_msg_kexdh_init() {
+
+	DEF_MP_INT(dh_e);
+
+	TRACE(("enter recv_msg_kexdh_init"))
+	if (!ses.kexstate.recvkexinit) {
+		dropbear_exit("Premature kexdh_init message received");
+	}
+
+	m_mp_init(&dh_e);
+	buf_getmpint(ses.payload, &dh_e);
+
+	send_msg_kexdh_reply(&dh_e);
+
+	mp_clear(&dh_e);
+
+	send_msg_newkeys();
+	ses.requirenext = SSH_MSG_NEWKEYS;
+	TRACE(("leave recv_msg_kexdh_init"))
+}
+	
+/* Generate our side of the diffie-hellman key exchange value (dh_f), and
+ * calculate the session key using the diffie-hellman algorithm. Following
+ * that, the session hash is calculated, and signed with RSA or DSS. The
+ * result is sent to the client. 
+ *
+ * See the ietf-secsh-transport draft, section 6, for details */
+static void send_msg_kexdh_reply(mp_int *dh_e) {
+
+	DEF_MP_INT(dh_y);
+	DEF_MP_INT(dh_f);
+
+	TRACE(("enter send_msg_kexdh_reply"))
+	m_mp_init_multi(&dh_y, &dh_f, NULL);
+	
+	gen_kexdh_vals(&dh_f, &dh_y);
+
+	kexdh_comb_key(&dh_f, &dh_y, dh_e, svr_opts.hostkey);
+	mp_clear(&dh_y);
+
+	/* we can start creating the kexdh_reply packet */
+	CHECKCLEARTOWRITE();
+	buf_putbyte(ses.writepayload, SSH_MSG_KEXDH_REPLY);
+	buf_put_pub_key(ses.writepayload, svr_opts.hostkey,
+			ses.newkeys->algo_hostkey);
+
+	/* put f */
+	buf_putmpint(ses.writepayload, &dh_f);
+	mp_clear(&dh_f);
+
+	/* calc the signature */
+	buf_put_sign(ses.writepayload, svr_opts.hostkey, 
+			ses.newkeys->algo_hostkey, ses.hash, SHA1_HASH_SIZE);
+
+	/* the SSH_MSG_KEXDH_REPLY is done */
+	encrypt_packet();
+
+	TRACE(("leave send_msg_kexdh_reply"))
+}
+
diff --git a/svr-main.c b/svr-main.c
new file mode 100644
index 0000000000000000000000000000000000000000..e59d895802702ebb1c614cf3a7c3bc52454bf971
--- /dev/null
+++ b/svr-main.c
@@ -0,0 +1,421 @@
+/*
+ * 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. */
+
+#include "includes.h"
+#include "dbutil.h"
+#include "session.h"
+#include "buffer.h"
+#include "signkey.h"
+#include "runopts.h"
+
+static size_t listensockets(int *sock, size_t sockcount, int *maxfd);
+static void sigchld_handler(int dummy);
+static void sigsegv_handler(int);
+static void sigintterm_handler(int fish);
+#ifdef INETD_MODE
+static void main_inetd();
+#endif
+#ifdef NON_INETD_MODE
+static void main_noinetd();
+#endif
+static void commonsetup();
+
+#if defined(DBMULTI_dropbear) || !defined(DROPBEAR_MULTI)
+#if defined(DBMULTI_dropbear) && defined(DROPBEAR_MULTI)
+int dropbear_main(int argc, char ** argv)
+#else
+int main(int argc, char ** argv)
+#endif
+{
+	
+
+	_dropbear_exit = svr_dropbear_exit;
+	_dropbear_log = svr_dropbear_log;
+
+	/* get commandline options */
+	svr_getopts(argc, argv);
+
+#ifdef INETD_MODE
+	/* service program mode */
+	if (svr_opts.inetdmode) {
+		main_inetd();
+		/* notreached */
+	}
+#endif
+
+#ifdef NON_INETD_MODE
+	main_noinetd();
+	/* notreached */
+#endif
+
+	dropbear_exit("Compiled without normal mode, can't run without -i\n");
+	return -1;
+}
+#endif
+
+#ifdef INETD_MODE
+static void main_inetd() {
+
+	struct sockaddr_storage remoteaddr;
+	socklen_t remoteaddrlen;
+	char * addrstring = NULL;
+
+	/* Set up handlers, syslog, seed random */
+	commonsetup();
+
+	remoteaddrlen = sizeof(remoteaddr);
+	if (getpeername(0, (struct sockaddr*)&remoteaddr, &remoteaddrlen) < 0) {
+		dropbear_exit("Unable to getpeername: %s", strerror(errno));
+	}
+
+	/* In case our inetd was lax in logging source addresses */
+	addrstring = getaddrstring(&remoteaddr, 1);
+	dropbear_log(LOG_INFO, "Child connection from %s", addrstring);
+
+	/* Don't check the return value - it may just fail since inetd has
+	 * already done setsid() after forking (xinetd on Darwin appears to do
+	 * this */
+	setsid();
+
+	/* Start service program 
+	 * -1 is a dummy childpipe, just something we can close() without 
+	 * mattering. */
+	svr_session(0, -1, getaddrhostname(&remoteaddr), addrstring);
+
+	/* notreached */
+}
+#endif /* INETD_MODE */
+
+#ifdef NON_INETD_MODE
+void main_noinetd() {
+	fd_set fds;
+	struct timeval seltimeout;
+	unsigned int i, j;
+	int val;
+	int maxsock = -1;
+	int listensocks[MAX_LISTEN_ADDR];
+	size_t listensockcount = 0;
+	FILE *pidfile = NULL;
+
+	int childpipes[MAX_UNAUTH_CLIENTS];
+	char * preauth_addrs[MAX_UNAUTH_CLIENTS];
+
+	int childsock;
+	int childpipe[2];
+
+	/* fork */
+	if (svr_opts.forkbg) {
+		int closefds = 0;
+#ifndef DEBUG_TRACE
+		if (!svr_opts.usingsyslog) {
+			closefds = 1;
+		}
+#endif
+		if (daemon(0, closefds) < 0) {
+			dropbear_exit("Failed to daemonize: %s", strerror(errno));
+		}
+	}
+
+	commonsetup();
+
+
+	/* should be done after syslog is working */
+	if (svr_opts.forkbg) {
+		dropbear_log(LOG_INFO, "Running in background");
+	} else {
+		dropbear_log(LOG_INFO, "Not forking");
+	}
+
+	/* create a PID file so that we can be killed easily */
+	pidfile = fopen(DROPBEAR_PIDFILE, "w");
+	if (pidfile) {
+		fprintf(pidfile, "%d\n", getpid());
+		fclose(pidfile);
+	}
+
+	/* sockets to identify pre-authenticated clients */
+	for (i = 0; i < MAX_UNAUTH_CLIENTS; i++) {
+		childpipes[i] = -1;
+	}
+	bzero(preauth_addrs, sizeof(preauth_addrs));
+	
+	/* Set up the listening sockets */
+	/* XXX XXX ports */
+	listensockcount = listensockets(listensocks, MAX_LISTEN_ADDR, &maxsock);
+	if (listensockcount == 0)
+	{
+		dropbear_exit("No listening ports available.");
+	}
+
+	/* incoming connection select loop */
+	for(;;) {
+
+		FD_ZERO(&fds);
+		
+		seltimeout.tv_sec = 60;
+		seltimeout.tv_usec = 0;
+		
+		/* listening sockets */
+		for (i = 0; i < listensockcount; i++) {
+			FD_SET(listensocks[i], &fds);
+		}
+
+		/* pre-authentication clients */
+		for (i = 0; i < MAX_UNAUTH_CLIENTS; i++) {
+			if (childpipes[i] >= 0) {
+				FD_SET(childpipes[i], &fds);
+				maxsock = MAX(maxsock, childpipes[i]);
+			}
+		}
+
+		val = select(maxsock+1, &fds, NULL, NULL, &seltimeout);
+
+		if (exitflag) {
+			unlink(DROPBEAR_PIDFILE);
+			dropbear_exit("Terminated by signal");
+		}
+		
+		if (val == 0) {
+			/* timeout reached */
+			continue;
+		}
+
+		if (val < 0) {
+			if (errno == EINTR) {
+				continue;
+			}
+			dropbear_exit("Listening socket error");
+		}
+
+		/* close fds which have been authed or closed - svr-auth.c handles
+		 * closing the auth sockets on success */
+		for (i = 0; i < MAX_UNAUTH_CLIENTS; i++) {
+			if (childpipes[i] >= 0 && FD_ISSET(childpipes[i], &fds)) {
+				m_close(childpipes[i]);
+				childpipes[i] = -1;
+				m_free(preauth_addrs[i]);
+			}
+		}
+
+		/* handle each socket which has something to say */
+		for (i = 0; i < listensockcount; i++) {
+
+			struct sockaddr_storage remoteaddr;
+			socklen_t remoteaddrlen = 0;
+			size_t num_unauthed_for_addr = 0;
+			size_t num_unauthed_total = 0;
+			char * remote_addr_str = NULL;
+			pid_t fork_ret = 0;
+			size_t conn_idx = 0;
+
+			if (!FD_ISSET(listensocks[i], &fds)) 
+				continue;
+
+			remoteaddrlen = sizeof(remoteaddr);
+			childsock = accept(listensocks[i], 
+					(struct sockaddr*)&remoteaddr, &remoteaddrlen);
+
+			if (childsock < 0) {
+				/* accept failed */
+				continue;
+			}
+
+			/* Limit the number of unauthenticated connections per IP */
+			remote_addr_str = getaddrstring(&remoteaddr, 0);
+
+			num_unauthed_for_addr = 0;
+			num_unauthed_total = 0;
+			for (j = 0; j < MAX_UNAUTH_CLIENTS; j++) {
+				if (childpipes[j] >= 0) {
+					num_unauthed_total++;
+					if (strcmp(remote_addr_str, preauth_addrs[j]) == 0) {
+						num_unauthed_for_addr++;
+					}
+				} else {
+					/* a free slot */
+					conn_idx = j;
+				}
+			}
+
+			if (num_unauthed_total >= MAX_UNAUTH_CLIENTS
+					|| num_unauthed_for_addr >= MAX_UNAUTH_PER_IP) {
+				goto out;
+			}
+
+			if (pipe(childpipe) < 0) {
+				TRACE(("error creating child pipe"))
+				goto out;
+			}
+
+			fork_ret = fork();
+			if (fork_ret < 0) {
+				dropbear_log(LOG_WARNING, "error forking: %s", strerror(errno));
+				goto out;
+
+			} else if (fork_ret > 0) {
+
+				/* parent */
+				childpipes[conn_idx] = childpipe[0];
+				m_close(childpipe[1]);
+				preauth_addrs[conn_idx] = remote_addr_str;
+				remote_addr_str = NULL;
+
+			} else {
+
+				/* child */
+				char * addrstring = NULL;
+#ifdef DEBUG_FORKGPROF
+				extern void _start(void), etext(void);
+				monstartup((u_long)&_start, (u_long)&etext);
+#endif /* DEBUG_FORKGPROF */
+
+				m_free(remote_addr_str);
+				addrstring = getaddrstring(&remoteaddr, 1);
+				dropbear_log(LOG_INFO, "Child connection from %s", addrstring);
+
+				if (setsid() < 0) {
+					dropbear_exit("setsid: %s", strerror(errno));
+				}
+
+				/* make sure we close sockets */
+				for (i = 0; i < listensockcount; i++) {
+					m_close(listensocks[i]);
+				}
+
+				m_close(childpipe[0]);
+
+				/* start the session */
+				svr_session(childsock, childpipe[1], 
+								getaddrhostname(&remoteaddr),
+								addrstring);
+				/* don't return */
+				dropbear_assert(0);
+			}
+
+out:
+			/* This section is important for the parent too */
+			m_close(childsock);
+			if (remote_addr_str) {
+				m_free(remote_addr_str);
+			}
+		}
+	} /* for(;;) loop */
+
+	/* don't reach here */
+}
+#endif /* NON_INETD_MODE */
+
+
+/* catch + reap zombie children */
+static void sigchld_handler(int UNUSED(unused)) {
+	struct sigaction sa_chld;
+
+	while(waitpid(-1, NULL, WNOHANG) > 0); 
+
+	sa_chld.sa_handler = sigchld_handler;
+	sa_chld.sa_flags = SA_NOCLDSTOP;
+	if (sigaction(SIGCHLD, &sa_chld, NULL) < 0) {
+		dropbear_exit("signal() error");
+	}
+}
+
+/* catch any segvs */
+static void sigsegv_handler(int UNUSED(unused)) {
+	fprintf(stderr, "Aiee, segfault! You should probably report "
+			"this as a bug to the developer\n");
+	exit(EXIT_FAILURE);
+}
+
+/* catch ctrl-c or sigterm */
+static void sigintterm_handler(int UNUSED(unused)) {
+
+	exitflag = 1;
+}
+
+/* Things used by inetd and non-inetd modes */
+static void commonsetup() {
+
+	struct sigaction sa_chld;
+#ifndef DISABLE_SYSLOG
+	if (svr_opts.usingsyslog) {
+		startsyslog();
+	}
+#endif
+
+	/* set up cleanup handler */
+	if (signal(SIGINT, sigintterm_handler) == SIG_ERR || 
+#ifndef DEBUG_VALGRIND
+		signal(SIGTERM, sigintterm_handler) == SIG_ERR ||
+#endif
+		signal(SIGPIPE, SIG_IGN) == SIG_ERR) {
+		dropbear_exit("signal() error");
+	}
+
+	/* catch and reap zombie children */
+	sa_chld.sa_handler = sigchld_handler;
+	sa_chld.sa_flags = SA_NOCLDSTOP;
+	if (sigaction(SIGCHLD, &sa_chld, NULL) < 0) {
+		dropbear_exit("signal() error");
+	}
+	if (signal(SIGSEGV, sigsegv_handler) == SIG_ERR) {
+		dropbear_exit("signal() error");
+	}
+
+	/* Now we can setup the hostkeys - needs to be after logging is on,
+	 * otherwise we might end up blatting error messages to the socket */
+	loadhostkeys();
+
+    seedrandom();
+}
+
+/* Set up listening sockets for all the requested ports */
+static size_t listensockets(int *sock, size_t sockcount, int *maxfd) {
+	
+	unsigned int i;
+	char* errstring = NULL;
+	size_t sockpos = 0;
+	int nsock;
+
+	TRACE(("listensockets: %d to try\n", svr_opts.portcount))
+
+	for (i = 0; i < svr_opts.portcount; i++) {
+
+		TRACE(("listening on '%s'", svr_opts.ports[i]))
+
+		nsock = dropbear_listen("", svr_opts.ports[i], &sock[sockpos], 
+				sockcount - sockpos,
+				&errstring, maxfd);
+
+		if (nsock < 0) {
+			dropbear_log(LOG_WARNING, "Failed listening on '%s': %s", 
+							svr_opts.ports[i], errstring);
+			m_free(errstring);
+			continue;
+		}
+
+		sockpos += nsock;
+
+	}
+	return sockpos;
+}
diff --git a/svr-runopts.c b/svr-runopts.c
new file mode 100644
index 0000000000000000000000000000000000000000..8d8b8df210f2ede165c18f0e07d68d05cc2b55ea
--- /dev/null
+++ b/svr-runopts.c
@@ -0,0 +1,315 @@
+/*
+ * 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. */
+
+#include "includes.h"
+#include "runopts.h"
+#include "signkey.h"
+#include "buffer.h"
+#include "dbutil.h"
+#include "algo.h"
+
+svr_runopts svr_opts; /* GLOBAL */
+
+static void printhelp(const char * progname);
+
+static void printhelp(const char * progname) {
+
+	fprintf(stderr, "Dropbear sshd v%s\n"
+					"Usage: %s [options]\n"
+					"Options are:\n"
+					"-b bannerfile	Display the contents of bannerfile"
+					" before user login\n"
+					"		(default: none)\n"
+#ifdef DROPBEAR_DSS
+					"-d dsskeyfile	Use dsskeyfile for the dss host key\n"
+					"		(default: %s)\n"
+#endif
+#ifdef DROPBEAR_RSA
+					"-r rsakeyfile	Use rsakeyfile for the rsa host key\n"
+					"		(default: %s)\n"
+#endif
+					"-F		Don't fork into background\n"
+#ifdef DISABLE_SYSLOG
+					"(Syslog support not compiled in, using stderr)\n"
+#else
+					"-E		Log to stderr rather than syslog\n"
+#endif
+#ifdef DO_MOTD
+					"-m		Don't display the motd on login\n"
+#endif
+					"-w		Disallow root logins\n"
+#if defined(ENABLE_SVR_PASSWORD_AUTH) || defined(ENABLE_SVR_PAM_AUTH)
+					"-s		Disable password logins\n"
+					"-g		Disable password logins for root\n"
+#endif
+#ifdef ENABLE_SVR_LOCALTCPFWD
+					"-j		Disable local port forwarding\n"
+#endif
+#ifdef ENABLE_SVR_REMOTETCPFWD
+					"-k		Disable remote port forwarding\n"
+					"-a		Allow connections to forwarded ports from any host\n"
+#endif
+					"-p port		Listen on specified tcp port, up to %d can be specified\n"
+					"		(default %s if none specified)\n"
+#ifdef INETD_MODE
+					"-i		Start for inetd\n"
+#endif
+#ifdef DEBUG_TRACE
+					"-v		verbose\n"
+#endif
+					,DROPBEAR_VERSION, progname,
+#ifdef DROPBEAR_DSS
+					DSS_PRIV_FILENAME,
+#endif
+#ifdef DROPBEAR_RSA
+					RSA_PRIV_FILENAME,
+#endif
+					DROPBEAR_MAX_PORTS, DROPBEAR_DEFPORT);
+}
+
+void svr_getopts(int argc, char ** argv) {
+
+	unsigned int i;
+	char ** next = 0;
+
+	/* see printhelp() for options */
+	svr_opts.rsakeyfile = NULL;
+	svr_opts.dsskeyfile = NULL;
+	svr_opts.bannerfile = NULL;
+	svr_opts.banner = NULL;
+	svr_opts.forkbg = 1;
+	svr_opts.norootlogin = 0;
+	svr_opts.noauthpass = 0;
+	svr_opts.norootpass = 0;
+	svr_opts.inetdmode = 0;
+	svr_opts.portcount = 0;
+	svr_opts.hostkey = NULL;
+#ifdef ENABLE_SVR_LOCALTCPFWD
+	svr_opts.nolocaltcp = 0;
+#endif
+#ifdef ENABLE_SVR_REMOTETCPFWD
+	svr_opts.noremotetcp = 0;
+#endif
+	/* not yet
+	opts.ipv4 = 1;
+	opts.ipv6 = 1;
+	*/
+#ifdef DO_MOTD
+	svr_opts.domotd = 1;
+#endif
+#ifndef DISABLE_SYSLOG
+	svr_opts.usingsyslog = 1;
+#endif
+#ifdef ENABLE_SVR_REMOTETCPFWD
+	opts.listen_fwd_all = 0;
+#endif
+
+	for (i = 1; i < (unsigned int)argc; i++) {
+		if (next) {
+			*next = argv[i];
+			if (*next == NULL) {
+				dropbear_exit("Invalid null argument");
+			}
+			next = 0x00;
+			continue;
+		}
+
+		if (argv[i][0] == '-') {
+			switch (argv[i][1]) {
+				case 'b':
+					next = &svr_opts.bannerfile;
+					break;
+#ifdef DROPBEAR_DSS
+				case 'd':
+					next = &svr_opts.dsskeyfile;
+					break;
+#endif
+#ifdef DROPBEAR_RSA
+				case 'r':
+					next = &svr_opts.rsakeyfile;
+					break;
+#endif
+				case 'F':
+					svr_opts.forkbg = 0;
+					break;
+#ifndef DISABLE_SYSLOG
+				case 'E':
+					svr_opts.usingsyslog = 0;
+					break;
+#endif
+#ifdef ENABLE_SVR_LOCALTCPFWD
+				case 'j':
+					svr_opts.nolocaltcp = 1;
+					break;
+#endif
+#ifdef ENABLE_SVR_REMOTETCPFWD
+				case 'k':
+					svr_opts.noremotetcp = 1;
+					break;
+				case 'a':
+					opts.listen_fwd_all = 1;
+					break;
+#endif
+#ifdef INETD_MODE
+				case 'i':
+					svr_opts.inetdmode = 1;
+					break;
+#endif
+				case 'p':
+					if (svr_opts.portcount < DROPBEAR_MAX_PORTS) {
+						svr_opts.ports[svr_opts.portcount] = NULL;
+						next = &svr_opts.ports[svr_opts.portcount];
+						/* Note: if it doesn't actually get set, we'll
+						 * decrement it after the loop */
+						svr_opts.portcount++;
+					}
+					break;
+#ifdef DO_MOTD
+				/* motd is displayed by default, -m turns it off */
+				case 'm':
+					svr_opts.domotd = 0;
+					break;
+#endif
+				case 'w':
+					svr_opts.norootlogin = 1;
+					break;
+#if defined(ENABLE_SVR_PASSWORD_AUTH) || defined(ENABLE_SVR_PAM_AUTH)
+				case 's':
+					svr_opts.noauthpass = 1;
+					break;
+				case 'g':
+					svr_opts.norootpass = 1;
+					break;
+#endif
+				case 'h':
+					printhelp(argv[0]);
+					exit(EXIT_FAILURE);
+					break;
+#ifdef DEBUG_TRACE
+				case 'v':
+					debug_trace = 1;
+					break;
+#endif
+				default:
+					fprintf(stderr, "Unknown argument %s\n", argv[i]);
+					printhelp(argv[0]);
+					exit(EXIT_FAILURE);
+					break;
+			}
+		}
+	}
+
+	/* Set up listening ports */
+	if (svr_opts.portcount == 0) {
+		svr_opts.ports[0] = m_strdup(DROPBEAR_DEFPORT);
+		svr_opts.portcount = 1;
+	} else {
+		/* we may have been given a -p option but no argument to go with
+		 * it */
+		if (svr_opts.ports[svr_opts.portcount-1] == NULL) {
+			svr_opts.portcount--;
+		}
+	}
+
+	if (svr_opts.dsskeyfile == NULL) {
+		svr_opts.dsskeyfile = DSS_PRIV_FILENAME;
+	}
+	if (svr_opts.rsakeyfile == NULL) {
+		svr_opts.rsakeyfile = RSA_PRIV_FILENAME;
+	}
+
+	if (svr_opts.bannerfile) {
+		struct stat buf;
+		if (stat(svr_opts.bannerfile, &buf) != 0) {
+			dropbear_exit("Error opening banner file '%s'",
+					svr_opts.bannerfile);
+		}
+		
+		if (buf.st_size > MAX_BANNER_SIZE) {
+			dropbear_exit("Banner file too large, max is %d bytes",
+					MAX_BANNER_SIZE);
+		}
+
+		svr_opts.banner = buf_new(buf.st_size);
+		if (buf_readfile(svr_opts.banner, svr_opts.bannerfile)!=DROPBEAR_SUCCESS) {
+			dropbear_exit("Error reading banner file '%s'",
+					svr_opts.bannerfile);
+		}
+		buf_setpos(svr_opts.banner, 0);
+	}
+
+}
+
+static void disablekey(int type, const char* filename) {
+
+	int i;
+
+	for (i = 0; sshhostkey[i].name != NULL; i++) {
+		if (sshhostkey[i].val == type) {
+			sshhostkey[i].usable = 0;
+			break;
+		}
+	}
+	dropbear_log(LOG_WARNING, "Failed reading '%s', disabling %s", filename,
+			type == DROPBEAR_SIGNKEY_DSS ? "DSS" : "RSA");
+}
+
+/* Must be called after syslog/etc is working */
+void loadhostkeys() {
+
+	int ret;
+	int type;
+
+	TRACE(("enter loadhostkeys"))
+
+	svr_opts.hostkey = new_sign_key();
+
+#ifdef DROPBEAR_RSA
+	type = DROPBEAR_SIGNKEY_RSA;
+	ret = readhostkey(svr_opts.rsakeyfile, svr_opts.hostkey, &type);
+	if (ret == DROPBEAR_FAILURE) {
+		disablekey(DROPBEAR_SIGNKEY_RSA, svr_opts.rsakeyfile);
+	}
+#endif
+#ifdef DROPBEAR_DSS
+	type = DROPBEAR_SIGNKEY_DSS;
+	ret = readhostkey(svr_opts.dsskeyfile, svr_opts.hostkey, &type);
+	if (ret == DROPBEAR_FAILURE) {
+		disablekey(DROPBEAR_SIGNKEY_DSS, svr_opts.dsskeyfile);
+	}
+#endif
+
+	if ( 1
+#ifdef DROPBEAR_DSS
+		&& svr_opts.hostkey->dsskey == NULL
+#endif
+#ifdef DROPBEAR_RSA
+		&& svr_opts.hostkey->rsakey == NULL
+#endif
+		) {
+		dropbear_exit("No hostkeys available");
+	}
+
+	TRACE(("leave loadhostkeys"))
+}
diff --git a/svr-service.c b/svr-service.c
new file mode 100644
index 0000000000000000000000000000000000000000..2c78e7d8ec478fc474d9f2200c77e27cc53fff76
--- /dev/null
+++ b/svr-service.c
@@ -0,0 +1,87 @@
+/*
+ * 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. */
+
+#include "includes.h"
+#include "dbutil.h"
+#include "service.h"
+#include "session.h"
+#include "packet.h"
+#include "ssh.h"
+#include "auth.h"
+
+static void send_msg_service_accept(unsigned char *name, int len);
+
+/* processes a SSH_MSG_SERVICE_REQUEST, returning 0 if finished,
+ * 1 if not */
+void recv_msg_service_request() {
+
+	unsigned char * name;
+	unsigned int len;
+
+	TRACE(("enter recv_msg_service_request"))
+
+	name = buf_getstring(ses.payload, &len);
+
+	/* ssh-userauth */
+	if (len == SSH_SERVICE_USERAUTH_LEN && 
+			strncmp(SSH_SERVICE_USERAUTH, name, len) == 0) {
+
+		send_msg_service_accept(name, len);
+		m_free(name);
+		TRACE(("leave recv_msg_service_request: done ssh-userauth"))
+		return;
+	}
+
+	/* ssh-connection */
+	if (len == SSH_SERVICE_CONNECTION_LEN &&
+			(strncmp(SSH_SERVICE_CONNECTION, name, len) == 0)) {
+		if (ses.authstate.authdone != 1) {
+			dropbear_exit("request for connection before auth");
+		}
+
+		send_msg_service_accept(name, len);
+		m_free(name);
+		TRACE(("leave recv_msg_service_request: done ssh-connection"))
+		return;
+	}
+
+	m_free(name);
+	/* TODO this should be a MSG_DISCONNECT */
+	dropbear_exit("unrecognised SSH_MSG_SERVICE_REQUEST");
+
+
+}
+
+static void send_msg_service_accept(unsigned char *name, int len) {
+
+	TRACE(("accepting service %s", name))
+
+	CHECKCLEARTOWRITE();
+
+	buf_putbyte(ses.writepayload, SSH_MSG_SERVICE_ACCEPT);
+	buf_putstring(ses.writepayload, name, len);
+
+	encrypt_packet();
+
+}
diff --git a/svr-session.c b/svr-session.c
new file mode 100644
index 0000000000000000000000000000000000000000..70029f89357732977ba002077bf00e8440d1c179
--- /dev/null
+++ b/svr-session.c
@@ -0,0 +1,201 @@
+/*
+ * 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. */
+
+#include "includes.h"
+#include "session.h"
+#include "dbutil.h"
+#include "packet.h"
+#include "algo.h"
+#include "buffer.h"
+#include "dss.h"
+#include "ssh.h"
+#include "random.h"
+#include "kex.h"
+#include "channel.h"
+#include "chansession.h"
+#include "atomicio.h"
+#include "tcpfwd.h"
+#include "service.h"
+#include "auth.h"
+#include "runopts.h"
+
+static void svr_remoteclosed();
+
+struct serversession svr_ses; /* GLOBAL */
+
+static const packettype svr_packettypes[] = {
+	{SSH_MSG_CHANNEL_DATA, recv_msg_channel_data},
+	{SSH_MSG_CHANNEL_WINDOW_ADJUST, recv_msg_channel_window_adjust},
+	{SSH_MSG_USERAUTH_REQUEST, recv_msg_userauth_request}, /* server */
+	{SSH_MSG_SERVICE_REQUEST, recv_msg_service_request}, /* server */
+	{SSH_MSG_KEXINIT, recv_msg_kexinit},
+	{SSH_MSG_KEXDH_INIT, recv_msg_kexdh_init}, /* server */
+	{SSH_MSG_NEWKEYS, recv_msg_newkeys},
+#ifdef ENABLE_SVR_REMOTETCPFWD
+	{SSH_MSG_GLOBAL_REQUEST, recv_msg_global_request_remotetcp},
+#endif
+	{SSH_MSG_CHANNEL_REQUEST, recv_msg_channel_request},
+	{SSH_MSG_CHANNEL_OPEN, recv_msg_channel_open},
+	{SSH_MSG_CHANNEL_EOF, recv_msg_channel_eof},
+	{SSH_MSG_CHANNEL_CLOSE, recv_msg_channel_close},
+#ifdef USING_LISTENERS
+	{SSH_MSG_CHANNEL_OPEN_CONFIRMATION, recv_msg_channel_open_confirmation},
+	{SSH_MSG_CHANNEL_OPEN_FAILURE, recv_msg_channel_open_failure},
+#endif
+	{0, 0} /* End */
+};
+
+static const struct ChanType *svr_chantypes[] = {
+	&svrchansess,
+#ifdef ENABLE_SVR_LOCALTCPFWD
+	&svr_chan_tcpdirect,
+#endif
+	NULL /* Null termination is mandatory. */
+};
+
+void svr_session(int sock, int childpipe, 
+		char* remotehost, char *addrstring) {
+
+	struct timeval timeout;
+
+    reseedrandom();
+
+	crypto_init();
+	common_session_init(sock, remotehost);
+
+	/* Initialise server specific parts of the session */
+	svr_ses.childpipe = childpipe;
+	svr_ses.addrstring = addrstring;
+	svr_authinitialise();
+	chaninitialise(svr_chantypes);
+	svr_chansessinitialise();
+
+	if (gettimeofday(&timeout, 0) < 0) {
+		dropbear_exit("Error getting time");
+	}
+
+	ses.connecttimeout = timeout.tv_sec + AUTH_TIMEOUT;
+
+	/* set up messages etc */
+	ses.remoteclosed = svr_remoteclosed;
+
+	/* packet handlers */
+	ses.packettypes = svr_packettypes;
+	ses.buf_match_algo = svr_buf_match_algo;
+
+	ses.isserver = 1;
+
+	/* We're ready to go now */
+	sessinitdone = 1;
+
+	/* exchange identification, version etc */
+	session_identification();
+
+	/* start off with key exchange */
+	send_msg_kexinit();
+
+	/* Run the main for loop. NULL is for the dispatcher - only the client
+	 * code makes use of it */
+	session_loop(NULL);
+
+	/* Not reached */
+
+}
+
+/* failure exit - format must be <= 100 chars */
+void svr_dropbear_exit(int exitcode, const char* format, va_list param) {
+
+	char fmtbuf[300];
+
+	if (!sessinitdone) {
+		/* before session init */
+		snprintf(fmtbuf, sizeof(fmtbuf), 
+				"premature exit: %s", format);
+	} else if (ses.authstate.authdone) {
+		/* user has authenticated */
+		snprintf(fmtbuf, sizeof(fmtbuf),
+				"exit after auth (%s): %s", 
+				ses.authstate.printableuser, format);
+	} else if (ses.authstate.printableuser) {
+		/* we have a potential user */
+		snprintf(fmtbuf, sizeof(fmtbuf), 
+				"exit before auth (user '%s', %d fails): %s",
+				ses.authstate.printableuser, ses.authstate.failcount, format);
+	} else {
+		/* before userauth */
+		snprintf(fmtbuf, sizeof(fmtbuf), 
+				"exit before auth: %s", format);
+	}
+
+	_dropbear_log(LOG_INFO, fmtbuf, param);
+
+	/* must be after we've done with username etc */
+	common_session_cleanup();
+
+	exit(exitcode);
+
+}
+
+/* priority is priority as with syslog() */
+void svr_dropbear_log(int priority, const char* format, va_list param) {
+
+	char printbuf[1024];
+	char datestr[20];
+	time_t timesec;
+	int havetrace = 0;
+
+	vsnprintf(printbuf, sizeof(printbuf), format, param);
+
+#ifndef DISABLE_SYSLOG
+	if (svr_opts.usingsyslog) {
+		syslog(priority, "%s", printbuf);
+	}
+#endif
+
+	/* if we are using DEBUG_TRACE, we want to print to stderr even if
+	 * syslog is used, so it is included in error reports */
+#ifdef DEBUG_TRACE
+	havetrace = debug_trace;
+#endif
+
+	if (!svr_opts.usingsyslog || havetrace)
+	{
+		timesec = time(NULL);
+		if (strftime(datestr, sizeof(datestr), "%b %d %H:%M:%S", 
+					localtime(&timesec)) == 0) {
+			datestr[0] = '?'; datestr[1] = '\0';
+		}
+		fprintf(stderr, "[%d] %s %s\n", getpid(), datestr, printbuf);
+	}
+}
+
+/* called when the remote side closes the connection */
+static void svr_remoteclosed() {
+
+	close(ses.sock);
+	ses.sock = -1;
+	dropbear_close("Exited normally");
+
+}
+
diff --git a/svr-tcpfwd.c b/svr-tcpfwd.c
new file mode 100644
index 0000000000000000000000000000000000000000..6391c4cf3f26ecdd9574dc47be59d99392247117
--- /dev/null
+++ b/svr-tcpfwd.c
@@ -0,0 +1,290 @@
+/*
+ * 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 "ssh.h"
+#include "tcpfwd.h"
+#include "dbutil.h"
+#include "session.h"
+#include "buffer.h"
+#include "packet.h"
+#include "listener.h"
+#include "runopts.h"
+
+#ifdef ENABLE_SVR_REMOTETCPFWD
+
+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 = {
+	1, /* sepfds */
+	"direct-tcpip",
+	newtcpdirect, /* init */
+	NULL, /* checkclose */
+	NULL, /* reqhandler */
+	NULL /* closehandler */
+};
+
+static const struct ChanType svr_chan_tcpremote = {
+	1, /* sepfds */
+	"forwarded-tcpip",
+	NULL,
+	NULL,
+	NULL,
+	NULL
+};
+
+/* At the moment this is completely used for tcp code (with the name reflecting
+ * that). If new request types are added, this should be replaced with code
+ * similar to the request-switching in chansession.c */
+void recv_msg_global_request_remotetcp() {
+
+	unsigned char* reqname = NULL;
+	unsigned int namelen;
+	unsigned int wantreply = 0;
+	int ret = DROPBEAR_FAILURE;
+
+	TRACE(("enter recv_msg_global_request_remotetcp"))
+
+	if (svr_opts.noremotetcp) {
+		TRACE(("leave recv_msg_global_request_remotetcp: remote tcp forwarding disabled"))
+		goto out;
+	}
+
+	reqname = buf_getstring(ses.payload, &namelen);
+	wantreply = buf_getbool(ses.payload);
+
+	if (namelen > MAX_NAME_LEN) {
+		TRACE(("name len is wrong: %d", namelen))
+		goto out;
+	}
+
+	if (strcmp("tcpip-forward", reqname) == 0) {
+		ret = svr_remotetcpreq();
+	} else if (strcmp("cancel-tcpip-forward", reqname) == 0) {
+		ret = svr_cancelremotetcp();
+	} else {
+		TRACE(("reqname isn't tcpip-forward: '%s'", reqname))
+	}
+
+out:
+	if (wantreply) {
+		if (ret == DROPBEAR_SUCCESS) {
+			send_msg_request_success();
+		} else {
+			send_msg_request_failure();
+		}
+	}
+
+	m_free(reqname);
+
+	TRACE(("leave recv_msg_global_request"))
+}
+
+
+static void send_msg_request_success() {
+
+	CHECKCLEARTOWRITE();
+	buf_putbyte(ses.writepayload, SSH_MSG_REQUEST_SUCCESS);
+	encrypt_packet();
+
+}
+
+static void send_msg_request_failure() {
+
+	CHECKCLEARTOWRITE();
+	buf_putbyte(ses.writepayload, SSH_MSG_REQUEST_FAILURE);
+	encrypt_packet();
+
+}
+
+static int matchtcp(void* typedata1, void* typedata2) {
+
+	const struct TCPListener *info1 = (struct TCPListener*)typedata1;
+	const struct TCPListener *info2 = (struct TCPListener*)typedata2;
+
+	return (info1->listenport == info2->listenport)
+			&& (info1->chantype == info2->chantype)
+			&& (strcmp(info1->listenaddr, info2->listenaddr) == 0);
+}
+
+static int svr_cancelremotetcp() {
+
+	int ret = DROPBEAR_FAILURE;
+	unsigned char * bindaddr = NULL;
+	unsigned int addrlen;
+	unsigned int port;
+	struct Listener * listener = NULL;
+	struct TCPListener tcpinfo;
+
+	TRACE(("enter cancelremotetcp"))
+
+	bindaddr = buf_getstring(ses.payload, &addrlen);
+	if (addrlen > MAX_IP_LEN) {
+		TRACE(("addr len too long: %d", addrlen))
+		goto out;
+	}
+
+	port = buf_getint(ses.payload);
+
+	tcpinfo.sendaddr = NULL;
+	tcpinfo.sendport = 0;
+	tcpinfo.listenaddr = bindaddr;
+	tcpinfo.listenport = port;
+	listener = get_listener(CHANNEL_ID_TCPFORWARDED, &tcpinfo, matchtcp);
+	if (listener) {
+		remove_listener( listener );
+		ret = DROPBEAR_SUCCESS;
+	}
+
+out:
+	m_free(bindaddr);
+	TRACE(("leave cancelremotetcp"))
+	return ret;
+}
+
+static int svr_remotetcpreq() {
+
+	int ret = DROPBEAR_FAILURE;
+	unsigned char * bindaddr = NULL;
+	unsigned int addrlen;
+	struct TCPListener *tcpinfo = NULL;
+	unsigned int port;
+
+	TRACE(("enter remotetcpreq"))
+
+	bindaddr = buf_getstring(ses.payload, &addrlen);
+	if (addrlen > MAX_IP_LEN) {
+		TRACE(("addr len too long: %d", addrlen))
+		goto out;
+	}
+
+	port = buf_getint(ses.payload);
+
+	if (port == 0) {
+		dropbear_log(LOG_INFO, "Server chosen tcpfwd ports are unsupported");
+		goto out;
+	}
+
+	if (port < 1 || port > 65535) {
+		TRACE(("invalid port: %d", port))
+		goto out;
+	}
+
+	if (!ses.allowprivport && port < IPPORT_RESERVED) {
+		TRACE(("can't assign port < 1024 for non-root"))
+		goto out;
+	}
+
+	tcpinfo = (struct TCPListener*)m_malloc(sizeof(struct TCPListener));
+	tcpinfo->sendaddr = NULL;
+	tcpinfo->sendport = 0;
+	tcpinfo->listenaddr = bindaddr;
+	tcpinfo->listenport = port;
+	tcpinfo->chantype = &svr_chan_tcpremote;
+	tcpinfo->tcp_type = forwarded;
+
+	ret = listen_tcpfwd(tcpinfo);
+
+out:
+	if (ret == DROPBEAR_FAILURE) {
+		/* we only free it if a listener wasn't created, since the listener
+		 * has to remember it if it's to be cancelled */
+		m_free(tcpinfo->listenaddr);
+		m_free(tcpinfo);
+	}
+	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 err = SSH_OPEN_ADMINISTRATIVELY_PROHIBITED;
+
+	if (svr_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) {
+		err = SSH_OPEN_CONNECT_FAILED;
+		TRACE(("leave newtcpdirect: sock failed"))
+		goto out;
+	}
+
+	ses.maxfd = MAX(ses.maxfd, sock);
+
+	 /* We don't set readfd, that will get set after the connection's
+	 * progress succeeds */
+	channel->writefd = sock;
+	channel->initconn = 1;
+	
+	err = SSH_OPEN_IN_PROGRESS;
+
+out:
+	m_free(desthost);
+	m_free(orighost);
+	TRACE(("leave newtcpdirect: err %d", err))
+	return err;
+}
+
+#endif
diff --git a/svr-x11fwd.c b/svr-x11fwd.c
new file mode 100644
index 0000000000000000000000000000000000000000..cbc8a799be061b50b04ce471e3e8fea7f225160d
--- /dev/null
+++ b/svr-x11fwd.c
@@ -0,0 +1,236 @@
+/*
+ * 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. */
+
+#include "includes.h"
+
+#ifndef DISABLE_X11FWD
+#include "x11fwd.h"
+#include "session.h"
+#include "ssh.h"
+#include "dbutil.h"
+#include "chansession.h"
+#include "channel.h"
+#include "packet.h"
+#include "buffer.h"
+
+#define X11BASEPORT 6000
+#define X11BINDBASE 6010
+
+static void x11accept(struct Listener* listener, int sock);
+static int bindport(int fd);
+static int send_msg_channel_open_x11(int fd, struct sockaddr_in* addr);
+
+/* called as a request for a session channel, sets up listening X11 */
+/* returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
+int x11req(struct ChanSess * chansess) {
+
+	int fd;
+
+	/* we already have an x11 connection */
+	if (chansess->x11listener != NULL) {
+		return DROPBEAR_FAILURE;
+	}
+
+	chansess->x11singleconn = buf_getbool(ses.payload);
+	chansess->x11authprot = buf_getstring(ses.payload, NULL);
+	chansess->x11authcookie = buf_getstring(ses.payload, NULL);
+	chansess->x11screennum = buf_getint(ses.payload);
+
+	/* create listening socket */
+	fd = socket(PF_INET, SOCK_STREAM, 0);
+	if (fd < 0) {
+		goto fail;
+	}
+
+	/* allocate port and bind */
+	chansess->x11port = bindport(fd);
+	if (chansess->x11port < 0) {
+		goto fail;
+	}
+
+	/* listen */
+	if (listen(fd, 20) < 0) {
+		goto fail;
+	}
+
+	/* set non-blocking */
+	setnonblocking(fd);
+
+	/* listener code will handle the socket now.
+	 * No cleanup handler needed, since listener_remove only happens
+	 * from our cleanup anyway */
+	chansess->x11listener = new_listener( &fd, 1, 0, chansess, x11accept, NULL);
+	if (chansess->x11listener == NULL) {
+		goto fail;
+	}
+
+	return DROPBEAR_SUCCESS;
+
+fail:
+	/* cleanup */
+	m_free(chansess->x11authprot);
+	m_free(chansess->x11authcookie);
+	close(fd);
+
+	return DROPBEAR_FAILURE;
+}
+
+/* accepts a new X11 socket */
+/* returns DROPBEAR_FAILURE or DROPBEAR_SUCCESS */
+static void x11accept(struct Listener* listener, int sock) {
+
+	int fd;
+	struct sockaddr_in addr;
+	int len;
+	int ret;
+	struct ChanSess * chansess = (struct ChanSess *)(listener->typedata);
+
+	len = sizeof(addr);
+
+	fd = accept(sock, (struct sockaddr*)&addr, &len);
+	if (fd < 0) {
+		return;
+	}
+
+	/* if single-connection we close it up */
+	if (chansess->x11singleconn) {
+		x11cleanup(chansess);
+	}
+
+	ret = send_msg_channel_open_x11(fd, &addr);
+	if (ret == DROPBEAR_FAILURE) {
+		close(fd);
+	}
+}
+
+/* This is called after switching to the user, and sets up the xauth
+ * and environment variables.  */
+void x11setauth(struct ChanSess *chansess) {
+
+	char display[20]; /* space for "localhost:12345.123" */
+	FILE * authprog = NULL;
+	int val;
+
+	if (chansess->x11listener == NULL) {
+		return;
+	}
+
+	/* create the DISPLAY string */
+	val = snprintf(display, sizeof(display), "localhost:%d.%d",
+			chansess->x11port - X11BASEPORT, chansess->x11screennum);
+	if (val < 0 || val >= (int)sizeof(display)) {
+		/* string was truncated */
+		return;
+	}
+
+	addnewvar("DISPLAY", display);
+
+	/* create the xauth string */
+	val = snprintf(display, sizeof(display), "unix:%d.%d",
+			chansess->x11port - X11BASEPORT, chansess->x11screennum);
+	if (val < 0 || val >= (int)sizeof(display)) {
+		/* string was truncated */
+		return;
+	}
+
+	/* popen is a nice function - code is strongly based on OpenSSH's */
+	authprog = popen(XAUTH_COMMAND, "w");
+	if (authprog) {
+		fprintf(authprog, "add %s %s %s\n",
+				display, chansess->x11authprot, chansess->x11authcookie);
+		pclose(authprog);
+	} else {
+		fprintf(stderr, "Failed to run %s\n", XAUTH_COMMAND);
+	}
+}
+
+void x11cleanup(struct ChanSess *chansess) {
+
+	m_free(chansess->x11authprot);
+	m_free(chansess->x11authcookie);
+
+	TRACE(("chansess %s", chansess))
+	if (chansess->x11listener != NULL) {
+		remove_listener(chansess->x11listener);
+		chansess->x11listener = NULL;
+	}
+}
+
+static const struct ChanType chan_x11 = {
+	0, /* sepfds */
+	"x11",
+	NULL, /* inithandler */
+	NULL, /* checkclose */
+	NULL, /* reqhandler */
+	NULL /* closehandler */
+};
+
+
+static int send_msg_channel_open_x11(int fd, struct sockaddr_in* addr) {
+
+	char* ipstring = NULL;
+
+	if (send_msg_channel_open_init(fd, &chan_x11) == DROPBEAR_SUCCESS) {
+		ipstring = inet_ntoa(addr->sin_addr);
+		buf_putstring(ses.writepayload, ipstring, strlen(ipstring));
+		buf_putint(ses.writepayload, addr->sin_port);
+
+		encrypt_packet();
+		return DROPBEAR_SUCCESS;
+	} else {
+		return DROPBEAR_FAILURE;
+	}
+
+}
+
+/* returns the port bound to, or -1 on failure.
+ * Will attempt to bind to a port X11BINDBASE (6010 usually) or upwards */
+static int bindport(int fd) {
+
+	struct sockaddr_in addr;
+	uint16_t port;
+
+	memset((void*)&addr, 0x0, sizeof(addr));
+	addr.sin_family = AF_INET;
+	addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+	/* if we can't find one in 2000 ports free, something's wrong */
+	for (port = X11BINDBASE; port < X11BINDBASE + 2000; port++) {
+		addr.sin_port = htons(port);
+		if (bind(fd, (struct sockaddr*)&addr, 
+					sizeof(struct sockaddr_in)) == 0) {
+			/* success */
+			return port;
+		}
+		if (errno == EADDRINUSE) {
+			/* try the next port */
+			continue;
+		}
+		/* otherwise it was an error we don't know about */
+		dropbear_log(LOG_DEBUG, "failed to bind x11 socket");
+		break;
+	}
+	return -1;
+}
+#endif /* DROPBEAR_X11FWD */
diff --git a/tcp-accept.c b/tcp-accept.c
new file mode 100644
index 0000000000000000000000000000000000000000..ffb175e19c148bb6b469cc293fb6de49d32645b1
--- /dev/null
+++ b/tcp-accept.c
@@ -0,0 +1,143 @@
+/*
+ * 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 "ssh.h"
+#include "tcpfwd.h"
+#include "dbutil.h"
+#include "session.h"
+#include "buffer.h"
+#include "packet.h"
+#include "listener.h"
+#include "runopts.h"
+
+#ifdef DROPBEAR_TCP_ACCEPT
+
+static void cleanup_tcp(struct Listener *listener) {
+
+	struct TCPListener *tcpinfo = (struct TCPListener*)(listener->typedata);
+
+	m_free(tcpinfo->sendaddr);
+	m_free(tcpinfo->listenaddr);
+	m_free(tcpinfo);
+}
+
+static void tcp_acceptor(struct Listener *listener, int sock) {
+
+	int fd;
+	struct sockaddr_storage addr;
+	socklen_t len;
+	char ipstring[NI_MAXHOST], portstring[NI_MAXSERV];
+	struct TCPListener *tcpinfo = (struct TCPListener*)(listener->typedata);
+
+	len = sizeof(addr);
+
+	fd = accept(sock, (struct sockaddr*)&addr, &len);
+	if (fd < 0) {
+		return;
+	}
+
+	if (getnameinfo((struct sockaddr*)&addr, len, ipstring, sizeof(ipstring),
+				portstring, sizeof(portstring), 
+				NI_NUMERICHOST | NI_NUMERICSERV) != 0) {
+		return;
+	}
+
+	if (send_msg_channel_open_init(fd, tcpinfo->chantype) == DROPBEAR_SUCCESS) {
+		unsigned char* addr = NULL;
+		unsigned int port = 0;
+
+		if (tcpinfo->tcp_type == direct) {
+			/* "direct-tcpip" */
+			/* host to connect, port to connect */
+			addr = tcpinfo->sendaddr;
+			port = tcpinfo->sendport;
+		} else {
+			dropbear_assert(tcpinfo->tcp_type == forwarded);
+			/* "forwarded-tcpip" */
+			/* address that was connected, port that was connected */
+			addr = tcpinfo->listenaddr;
+			port = tcpinfo->listenport;
+		}
+
+		buf_putstring(ses.writepayload, addr, strlen(addr));
+		buf_putint(ses.writepayload, port);
+
+		/* originator ip */
+		buf_putstring(ses.writepayload, ipstring, strlen(ipstring));
+		/* originator port */
+		buf_putint(ses.writepayload, atol(portstring));
+
+		encrypt_packet();
+
+	} else {
+		/* XXX debug? */
+		close(fd);
+	}
+}
+
+int listen_tcpfwd(struct TCPListener* tcpinfo) {
+
+	char portstring[NI_MAXSERV];
+	int socks[DROPBEAR_MAX_SOCKS];
+	struct Listener *listener = NULL;
+	int nsocks;
+	char* errstring = NULL;
+	// listen_spec = NULL indicates localhost
+	const char* listen_spec = NULL;
+
+	TRACE(("enter listen_tcpfwd"))
+
+	/* first we try to bind, so don't need to do so much cleanup on failure */
+	snprintf(portstring, sizeof(portstring), "%d", tcpinfo->listenport);
+
+	/* a listenaddr of "" will indicate all interfaces */
+	if (opts.listen_fwd_all 
+			&& (strcmp(tcpinfo->listenaddr, "localhost") != 0) ) {
+		listen_spec = tcpinfo->listenaddr;
+	}
+
+	nsocks = dropbear_listen(listen_spec, portstring, socks, 
+			DROPBEAR_MAX_SOCKS, &errstring, &ses.maxfd);
+	if (nsocks < 0) {
+		dropbear_log(LOG_INFO, "TCP forward failed: %s", errstring);
+		m_free(errstring);
+		TRACE(("leave listen_tcpfwd: dropbear_listen failed"))
+		return DROPBEAR_FAILURE;
+	}
+
+	listener = new_listener(socks, nsocks, CHANNEL_ID_TCPFORWARDED, tcpinfo, 
+			tcp_acceptor, cleanup_tcp);
+
+	if (listener == NULL) {
+		m_free(tcpinfo);
+		TRACE(("leave listen_tcpfwd: listener failed"))
+		return DROPBEAR_FAILURE;
+	}
+
+	TRACE(("leave listen_tcpfwd: success"))
+	return DROPBEAR_SUCCESS;
+}
+
+#endif /* DROPBEAR_TCP_ACCEPT */
diff --git a/tcpfwd.h b/tcpfwd.h
new file mode 100644
index 0000000000000000000000000000000000000000..28af02987dbef16957ad18fe3008c676e6e8312a
--- /dev/null
+++ b/tcpfwd.h
@@ -0,0 +1,69 @@
+/*
+ * Dropbear - a SSH2 server
+ * 
+ * Copyright (c) 2002,2003 Matt Johnston
+ * All rights reserved.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE. */
+#ifndef _TCPFWD_H
+#define _TCPFWD_H
+
+#include "channel.h"
+
+struct TCPListener {
+
+	/* 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 the address/port that we listen on. The address has special
+	 * meanings as per the rfc, "" for all interfaces, "localhost" for 
+	 * localhost, or a normal interface name. */
+	unsigned char *listenaddr;
+	unsigned int listenport;
+
+	const struct ChanType *chantype;
+	enum {direct, forwarded} tcp_type;
+};
+
+/* A link in a list of forwards */
+struct TCPFwdList {
+
+	const unsigned 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;
+
+/* Client */
+void setup_localtcp();
+void setup_remotetcp();
+extern const struct ChanType cli_chan_tcpremote;
+
+/* Common */
+int listen_tcpfwd(struct TCPListener* tcpinfo);
+
+
+#endif
diff --git a/termcodes.c b/termcodes.c
new file mode 100644
index 0000000000000000000000000000000000000000..d59505cfbacbf5f361d3f7a3b7a120c673651b73
--- /dev/null
+++ b/termcodes.c
@@ -0,0 +1,187 @@
+/*
+ * 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. */
+
+#include "includes.h"
+#include "termcodes.h"
+
+const struct TermCode termcodes[MAX_TERMCODE+1] = {
+
+		{0, 0}, /* TTY_OP_END */
+		{VINTR, TERMCODE_CONTROLCHAR}, /* control character codes */
+		{VQUIT, TERMCODE_CONTROLCHAR},
+		{VERASE, TERMCODE_CONTROLCHAR},
+		{VKILL, TERMCODE_CONTROLCHAR},
+		{VEOF, TERMCODE_CONTROLCHAR},
+		{VEOL, TERMCODE_CONTROLCHAR},
+		{VEOL2, TERMCODE_CONTROLCHAR},
+		{VSTART, TERMCODE_CONTROLCHAR},
+		{VSTOP, TERMCODE_CONTROLCHAR},
+		{VSUSP, TERMCODE_CONTROLCHAR},
+#ifdef VDSUSP
+		{VDSUSP, TERMCODE_CONTROLCHAR},
+#else
+		{0, 0},
+#endif
+#ifdef VREPRINT
+		{VREPRINT, TERMCODE_CONTROLCHAR},
+#else
+		{0, 0},
+#endif
+#ifdef AIX
+		{CERASE, TERMCODE_CONTROLCHAR},
+#else
+		{VWERASE, TERMCODE_CONTROLCHAR},
+#endif
+		{VLNEXT, TERMCODE_CONTROLCHAR},
+#ifdef VFLUSH
+		{VFLUSH, TERMCODE_CONTROLCHAR},
+#else	
+		{0, 0},
+#endif
+#ifdef VSWTCH
+		{VSWTCH, TERMCODE_CONTROLCHAR},
+#else	
+		{0, 0},
+#endif
+#ifdef VSTATUS
+		{VSTATUS, TERMCODE_CONTROLCHAR},
+#else
+		{0, 0},
+#endif
+#ifdef AIX
+		{CKILL, TERMCODE_CONTROLCHAR},
+#elif defined(VDISCARD)
+		{VDISCARD, TERMCODE_CONTROLCHAR},
+#else
+		{0, 0},
+#endif
+		{0, 0}, /* 19 */
+		{0, 0},
+		{0, 0},
+		{0, 0},
+		{0, 0},
+		{0, 0},
+		{0, 0},
+		{0, 0},
+		{0, 0},
+		{0, 0},
+		{0, 0}, /* 29 */
+		{IGNPAR, TERMCODE_INPUT}, /* input flags */
+		{PARMRK, TERMCODE_INPUT},
+		{INPCK, TERMCODE_INPUT},
+		{ISTRIP, TERMCODE_INPUT},
+		{INLCR, TERMCODE_INPUT},
+		{IGNCR, TERMCODE_INPUT},
+		{ICRNL, TERMCODE_INPUT},
+#ifdef IUCLC
+		{IUCLC, TERMCODE_INPUT},
+#else
+		{0, 0},
+#endif
+		{IXON, TERMCODE_INPUT},
+		{IXANY, TERMCODE_INPUT},
+		{IXOFF, TERMCODE_INPUT},
+#ifdef IMAXBEL
+		{IMAXBEL, TERMCODE_INPUT},
+#else
+		{0, 0},
+#endif
+		{0, 0}, /* 42 */
+		{0, 0},
+		{0, 0},
+		{0, 0},
+		{0, 0},
+		{0, 0},
+		{0, 0},
+		{0, 0}, /* 49 */
+		{ISIG, TERMCODE_LOCAL}, /* local flags */
+		{ICANON, TERMCODE_LOCAL},
+#ifdef XCASE
+		{XCASE, TERMCODE_LOCAL},
+#else
+		{0, 0},
+#endif
+		{ECHO, TERMCODE_LOCAL},
+		{ECHOE, TERMCODE_LOCAL},
+		{ECHOK, TERMCODE_LOCAL},
+		{ECHONL, TERMCODE_LOCAL},
+		{NOFLSH, TERMCODE_LOCAL},
+		{TOSTOP, TERMCODE_LOCAL},
+		{IEXTEN, TERMCODE_LOCAL},
+		{ECHOCTL, TERMCODE_LOCAL},
+		{ECHOKE, TERMCODE_LOCAL},
+#ifdef PENDIN
+		{PENDIN, TERMCODE_LOCAL},
+#else
+		{0, 0},
+#endif
+		{0, 0}, /* 63 */
+		{0, 0},
+		{0, 0},
+		{0, 0},
+		{0, 0},
+		{0, 0},
+		{0, 0}, /* 69 */
+		{OPOST, TERMCODE_OUTPUT}, /* output flags */
+#ifdef OLCUC
+		{OLCUC, TERMCODE_OUTPUT},
+#else
+		{0, 0},
+#endif
+		{ONLCR, TERMCODE_OUTPUT},
+#ifdef OCRNL
+		{OCRNL, TERMCODE_OUTPUT},
+#else
+		{0, 0},
+#endif
+#ifdef ONOCR
+		{ONOCR, TERMCODE_OUTPUT},
+#else
+		{0, 0},
+#endif
+#ifdef ONLRET
+		{ONLRET, TERMCODE_OUTPUT},
+#else
+		{0, 0},
+#endif
+		{0, 0}, /* 76 */
+		{0, 0},
+		{0, 0},
+		{0, 0},
+		{0, 0},
+		{0, 0},
+		{0, 0},
+		{0, 0},
+		{0, 0},
+		{0, 0},
+		{0, 0},
+		{0, 0},
+		{0, 0},
+		{0, 0}, /* 89 */
+		{CS7, TERMCODE_CONTROL},
+		{CS8, TERMCODE_CONTROL},
+		{PARENB, TERMCODE_CONTROL},
+		{PARODD, TERMCODE_CONTROL}
+		/* 94 */
+};
diff --git a/termcodes.h b/termcodes.h
new file mode 100644
index 0000000000000000000000000000000000000000..00792ea0509fc2e4bf53abaadda45abe553e4212
--- /dev/null
+++ b/termcodes.h
@@ -0,0 +1,46 @@
+/*
+ * 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 _TERMCODES_H_
+#define _TERMCODES_H_
+
+#define TERMCODE_NONE 0
+#define TERMCODE_CONTROL 1
+#define TERMCODE_INPUT 2
+#define TERMCODE_OUTPUT 3
+#define TERMCODE_LOCAL 4
+#define TERMCODE_CONTROLCHAR 5
+
+#define MAX_TERMCODE 93
+
+struct TermCode {
+
+	unsigned int mapcode;
+	unsigned char type;
+
+};
+
+extern const struct TermCode termcodes[];
+
+#endif /* _TERMCODES_H_ */
diff --git a/x11fwd.h b/x11fwd.h
new file mode 100644
index 0000000000000000000000000000000000000000..5855a6862cd24d225e7f9ce9a92974b387929776
--- /dev/null
+++ b/x11fwd.h
@@ -0,0 +1,37 @@
+/*
+ * 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 _X11FWD_H_
+#define _X11FWD_H_
+#ifndef DISABLE_X11FWD
+
+#include "includes.h"
+#include "chansession.h"
+#include "channel.h"
+
+int x11req(struct ChanSess * chansess);
+void x11setauth(struct ChanSess *chansess);
+void x11cleanup(struct ChanSess *chansess);
+
+#endif /* DROPBEAR_X11FWD */
+#endif /* _X11FWD_H_ */