NB: big update - may break stuff. Please test!

 - (djm) OpenBSD CVS sync:
   - markus@cvs.openbsd.org  2001/02/03 03:08:38
     [auth-options.c auth-rh-rsa.c auth-rhosts.c auth.c canohost.c]
     [canohost.h servconf.c servconf.h session.c sshconnect1.c sshd.8]
     [sshd_config]
     make ReverseMappingCheck optional in sshd_config; ok djm@,dugsong@
   - markus@cvs.openbsd.org  2001/02/03 03:19:51
     [ssh.1 sshd.8 sshd_config]
     Skey is now called ChallengeResponse
   - markus@cvs.openbsd.org  2001/02/03 03:43:09
     [sshd.8]
     use no-pty option in .ssh/authorized_keys* if you need a 8-bit clean
     channel. note from Erik.Anggard@cygate.se (pr/1659)
   - stevesk@cvs.openbsd.org 2001/02/03 10:03:06
     [ssh.1]
     typos; ok markus@
   - djm@cvs.openbsd.org     2001/02/04 04:11:56
     [scp.1 sftp-server.c ssh.1 sshd.8 sftp-client.c sftp-client.h]
     [sftp-common.c sftp-common.h sftp-int.c sftp-int.h sftp.1 sftp.c]
     Basic interactive sftp client; ok theo@
 - (djm) Update RPM specs for new sftp binary
 - (djm) Update several bits for new optional reverse lookup stuff. I
   think I got them all.
diff --git a/ChangeLog b/ChangeLog
index 766c880..5afaf69 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -2,9 +2,32 @@
  - (bal) I think this is the last of the bsd-*.h that don't belong.
  - (bal) Minor Makefile fix
  - (bal) openbsd-compat/Makefile minor fix.  Ensure dependancies are done
-   right. 
+   right.
  - (bal) Changed order of LIB="" in -with-skey due to library resolving.
  - (bal) next-posix.h changed to bsd-nextstep.h
+ - (djm) OpenBSD CVS sync:
+   - markus@cvs.openbsd.org  2001/02/03 03:08:38
+     [auth-options.c auth-rh-rsa.c auth-rhosts.c auth.c canohost.c]
+     [canohost.h servconf.c servconf.h session.c sshconnect1.c sshd.8]
+     [sshd_config]
+     make ReverseMappingCheck optional in sshd_config; ok djm@,dugsong@
+   - markus@cvs.openbsd.org  2001/02/03 03:19:51
+     [ssh.1 sshd.8 sshd_config]
+     Skey is now called ChallengeResponse
+   - markus@cvs.openbsd.org  2001/02/03 03:43:09
+     [sshd.8]
+     use no-pty option in .ssh/authorized_keys* if you need a 8-bit clean
+     channel. note from Erik.Anggard@cygate.se (pr/1659)
+   - stevesk@cvs.openbsd.org 2001/02/03 10:03:06
+     [ssh.1]
+     typos; ok markus@
+   - djm@cvs.openbsd.org     2001/02/04 04:11:56
+     [scp.1 sftp-server.c ssh.1 sshd.8 sftp-client.c sftp-client.h]
+     [sftp-common.c sftp-common.h sftp-int.c sftp-int.h sftp.1 sftp.c]
+     Basic interactive sftp client; ok theo@
+ - (djm) Update RPM specs for new sftp binary
+ - (djm) Update several bits for new optional reverse lookup stuff. I 
+   think I got them all.
 
 20010103
  - (bal) Cygwin clean up by Corinna Vinschen <vinschen@redhat.com>
@@ -14,7 +37,7 @@
    platforms so builds fail.  (NeXT being a well known one)
 
 20010102
- - (bal) Makefile fix where sourcedir != builddir by Corinna Vinschen 
+ - (bal) Makefile fix where sourcedir != builddir by Corinna Vinschen
    <vinschen@redhat.com>
  - (bal) Makefile fix to use $(MAKE) instead of 'make'  for platforms
    that use 'gmake'.   Patch by Tim Rice <tim@multitalents.net>
@@ -75,7 +98,7 @@
      ``StrictHostKeyChecking ask'' documentation and small cleanup.
      ok markus@
    - stevesk@cvs.openbsd.org 2001/01/28 20:43:25
-     [sshd.8] 
+     [sshd.8]
      spelling.  ok markus@
    - stevesk@cvs.openbsd.org 2001/01/28 20:53:21
      [xmalloc.c]
@@ -94,7 +117,7 @@
   - (bal) Minor auth2.c resync.  Whitespace and moving of an #include.
 
 20010126
- - (bal) SSH_PROGRAM vs _PATH_SSH_PROGRAM fix pointed out by Roumen 
+ - (bal) SSH_PROGRAM vs _PATH_SSH_PROGRAM fix pointed out by Roumen
    Petrov <roumen.petrov@skalasoft.com>
  - (bal) OpenBSD Sync
    - deraadt@cvs.openbsd.org 2001/01/25 8:06:33
@@ -105,12 +128,12 @@
  - (djm) Sync bsd-* support files:
    - deraadt@cvs.openbsd.org 2000/01/26 03:43:20
      [rresvport.c bindresvport.c]
-     new bindresvport() semantics that itojun, shin, jean-luc and i have 
+     new bindresvport() semantics that itojun, shin, jean-luc and i have
      agreed on, which will be happy for the future. bindresvport_sa() for
      sockaddr *, too.  docs later..
    - deraadt@cvs.openbsd.org 2000/01/24 02:24:21
      [bindresvport.c]
-     in bindresvport(), if sin is non-NULL, example sin->sin_family for 
+     in bindresvport(), if sin is non-NULL, example sin->sin_family for
      the actual family being processed
  - (djm) Mention PRNGd in documentation, it is nicer than EGD
  - (djm) Automatically search for "well-known" EGD/PRNGd sockets in autoconf
@@ -124,7 +147,7 @@
  - (bal) OpenBSD Resync
    - markus@cvs.openbsd.org 2001/01/23 10:45:10
      [ssh.h]
-     nuke comment            
+     nuke comment
  - (bal) no 64bit support patch from Tim Rice <tim@multitalents.net>
  - (bal) #ifdef around S_IFSOCK if platform does not support it.
    patch by Tim Rice <tim@multitalents.net>
@@ -134,7 +157,7 @@
 20010123
  - (bal) regexp.h typo in configure.in.  Should have been regex.h
  - (bal) SSH_USER_DIR to _PATH_SSH_USER_DIR patch by stevesk@
- - (bal) SSH_ASKPASS_DEFAULT to _PATH_SSH_ASKPASS_DEFAULT 
+ - (bal) SSH_ASKPASS_DEFAULT to _PATH_SSH_ASKPASS_DEFAULT
  - (bal) OpenBSD Resync
    - markus@cvs.openbsd.org 2001/01/22 8:15:00
      [auth-krb4.c sshconnect1.c]
@@ -172,12 +195,12 @@
      fix typo; from stevesk@
    - markus@cvs.openbsd.org 2001/01/19 16:50:58
      [ssh-dss.c]
-     clear and free digest, make consistent with other code (use dlen); from 
+     clear and free digest, make consistent with other code (use dlen); from
      stevesk@
    - markus@cvs.openbsd.org 2001/01/20 15:55:20 GMT 2001 by markus
      [auth-options.c auth-options.h auth-rsa.c auth2.c]
      pass the filename to auth_parse_options()
-   - markus@cvs.openbsd.org 2001/01/20 17:59:40 GMT 2001 
+   - markus@cvs.openbsd.org 2001/01/20 17:59:40 GMT 2001
      [readconf.c]
      fix SIGSEGV from -o ""; problem noted by jehsom@togetherweb.com
    - stevesk@cvs.openbsd.org 2001/01/20 18:20:29
@@ -185,7 +208,7 @@
      dh_new_group() does not return NULL.  ok markus@
    - markus@cvs.openbsd.org 2001/01/20 21:33:42
      [ssh-add.c]
-     do not loop forever if askpass does not exist; from 
+     do not loop forever if askpass does not exist; from
      andrew@pimlott.ne.mediaone.net
    - djm@cvs.openbsd.org 2001/01/20 23:00:56
      [servconf.c]
@@ -207,13 +230,13 @@
       match.c misc.c misc.h nchan.c packet.c pty.c radix.h readconf.c
       readpass.c readpass.h rsa.c scp.c servconf.c serverloop.c serverloop.h
       session.c sftp-server.c ssh-add.c ssh-agent.c ssh-dss.c ssh-keygen.c
-      ssh-keyscan.c ssh-rsa.c ssh.c ssh.h sshconnect.c sshconnect.h 
+      ssh-keyscan.c ssh-rsa.c ssh.c ssh.h sshconnect.c sshconnect.h
       sshconnect1.c sshconnect2.c sshd.c tildexpand.c tildexpand.h
       ttysmodes.c uidswap.c xmalloc.c]
-     split ssh.h and try to cleanup the #include mess. remove unnecessary 
+     split ssh.h and try to cleanup the #include mess. remove unnecessary
      #includes.  rename util.[ch] -> misc.[ch]
  - (bal) renamed 'PIDDIR' to '_PATH_SSH_PIDDIR' to match OpenBSD tree
- - (bal) Moved #ifdef KRB4 in auth-krb4.c above the #include to resolve 
+ - (bal) Moved #ifdef KRB4 in auth-krb4.c above the #include to resolve
    conflict when compiling for non-kerb install
  - (bal) removed the #ifdef SKEY in auth1.c to match Markus' changes
    on 1/19.
@@ -233,7 +256,7 @@
    - markus@cvs.openbsd.org 2001/01/18 16:20:21
      [log-client.c log-server.c log.c readconf.c servconf.c ssh.1 ssh.h
       sshd.8 sshd.c]
-     log() is at pri=LOG_INFO, since LOG_NOTICE goes to /dev/console on many 
+     log() is at pri=LOG_INFO, since LOG_NOTICE goes to /dev/console on many
      systems
    - markus@cvs.openbsd.org 2001/01/18 16:59:59
      [auth-passwd.c auth.c auth.h auth1.c auth2.c serverloop.c session.c
@@ -250,7 +273,7 @@
    to fix NULL pointer deref and fake authloop breakage in PAM code.
  - (bal) Updated contrib/cygwin/ by Corinna Vinschen <vinschen@redhat.com>
  - (bal) Minor cygwin patch to auth1.c.  Suggested by djm.
- 
+
 20010118
  - (bal) Super Sized OpenBSD Resync
    - markus@cvs.openbsd.org 2001/01/11 22:14:20 GMT 2001 by markus
@@ -272,7 +295,7 @@
      [ssh-add.c]
      typo, from stevesk@sweden.hp.com
    - markus@cvs.openbsd.org 2001/01/13 18:32:50
-     [packet.c session.c ssh.c sshconnect.c sshd.c] 
+     [packet.c session.c ssh.c sshconnect.c sshd.c]
      split out keepalive from packet_interactive (from dale@accentre.com)
      set IPTOS_LOWDELAY TCP_NODELAY IPTOS_THROUGHPUT for ssh2, too.
    - markus@cvs.openbsd.org 2001/01/13 18:36:45
@@ -284,7 +307,7 @@
    - markus@cvs.openbsd.org 2001/01/13 18:43:31
      [session.c]
      Wall
-   - markus@cvs.openbsd.org 2001/01/13 19:14:08 
+   - markus@cvs.openbsd.org 2001/01/13 19:14:08
      [clientloop.h clientloop.c ssh.c]
      move callback to headerfile
    - markus@cvs.openbsd.org 2001/01/15 21:40:10
@@ -301,12 +324,12 @@
      readable long listing for sftp-server, ok deraadt@
    - markus@cvs.openbsd.org 2001/01/16 19:20:06
      [key.c ssh-rsa.c]
-     make "ssh-rsa" key format for ssh2 confirm to the ietf-drafts; from 
-     galb@vandyke.com.  note that you have to delete older ssh2-rsa keys, 
-     since they are in the wrong format, too. they must be removed from 
+     make "ssh-rsa" key format for ssh2 confirm to the ietf-drafts; from
+     galb@vandyke.com.  note that you have to delete older ssh2-rsa keys,
+     since they are in the wrong format, too. they must be removed from
      .ssh/authorized_keys2 and .ssh/known_hosts2, etc.
-     (cd; grep -v ssh-rsa .ssh/authorized_keys2 > TMP && mv TMP 
-     .ssh/authorized_keys2) additionally, we now check that 
+     (cd; grep -v ssh-rsa .ssh/authorized_keys2 > TMP && mv TMP
+     .ssh/authorized_keys2) additionally, we now check that
      BN_num_bits(rsa->n) >= 768.
    - markus@cvs.openbsd.org 2001/01/16 20:54:27
      [sftp-server.c]
@@ -317,15 +340,15 @@
  - (bal) Added bsd-strmode.[ch] since some non-OpenBSD platforms may
    be missing such feature.
 
- 
+
 20010117
  - (djm) Only write random seed file at exit
  - (djm) Make PAM support optional, enable with --with-pam
- - (djm) Try to use libcrypt on Linux, but link it after OpenSSL (which 
+ - (djm) Try to use libcrypt on Linux, but link it after OpenSSL (which
    provides a crypt() of its own)
  - (djm) Avoid a warning in bsd-bindresvport.c
  - (djm) Try to avoid adding -I/usr/include to CPPFLAGS during SSL tests. This
-   can cause weird segfaults errors on Solaris 
+   can cause weird segfaults errors on Solaris
  - (djm) Avoid warning in PAM code by making read_passphrase arguments const
  - (djm) Add --with-pam to RPM spec files
 
@@ -345,7 +368,7 @@
      [auth.c sshd.8]
      support supplementary group in {Allow,Deny}Groups
      from stevesk@pobox.com
-	
+
 20010112
  - (bal) OpenBSD Sync
    - markus@cvs.openbsd.org 2001/01/10 22:56:22
@@ -358,11 +381,11 @@
 	     use #defines from the draft
 	     move #definations to sftp.h
      more info:
-     http://www.ietf.org/internet-drafts/draft-ietf-secsh-filexfer-00.txt 
+     http://www.ietf.org/internet-drafts/draft-ietf-secsh-filexfer-00.txt
    - markus@cvs.openbsd.org 2001/01/10 19:43:20
      [sshd.c]
      XXX - generate_empheral_server_key() is not safe against races,
-     because it calls log()     
+     because it calls log()
    - markus@cvs.openbsd.org 2001/01/09 21:19:50
      [packet.c]
      allow TCP_NDELAY for ipv6; from netbsd via itojun@
@@ -446,7 +469,7 @@
      [sshconnect2.c]
      handle SSH2_MSG_USERAUTH_BANNER; fixes bug when connecting to a server
      that prints a banner (e.g. /etc/issue.net)
- 
+
 20010105
  - (bal) contrib/caldera/ provided by Tim Rice <tim@multitalents.net>
  - (bal) bsd-getcwd.c and bsd-setenv.c changed from bcopy() to memmove()
@@ -464,9 +487,9 @@
      log remote ip on disconnect; PR 1600 from jcs@rt.fm
    - markus@cvs.openbsd.org 2001/01/02 20:50:56
      [sshconnect.c]
-     strict_host_key_checking for host_status != HOST_CHANGED && 
+     strict_host_key_checking for host_status != HOST_CHANGED &&
      ip_status == HOST_CHANGED
- - (bal) authfile.c: Synced CVS ID tag 
+ - (bal) authfile.c: Synced CVS ID tag
  - (bal) UnixWare 2.0 fixes by Tim Rice <tim@multitalents.net>
  - (bal) Disable sftp-server if no 64bit int support exists.  Based on
    patch by Tim Rice <tim@multitalents.net>
@@ -496,11 +519,11 @@
  - (bal) if no MAXHOSTNAMELEN is defined.  Default to 64 character defination.
    Suggested by Christian Kurz <shorty@debian.org>
  - (bal) Add in '.c.o' section to Makefile.in to address make programs that
-    don't honor CPPFLAGS by default.  Suggested by Lutz Jaenicke 
+    don't honor CPPFLAGS by default.  Suggested by Lutz Jaenicke
     <Lutz.Jaenicke@aet.TU-Cottbus.DE>
 
 20001229
- - (bal) Fixed spelling of 'authorized_keys' in ssh-copy-id.1 by Christian 
+ - (bal) Fixed spelling of 'authorized_keys' in ssh-copy-id.1 by Christian
    Kurz <shorty@debian.org>
  - (bal) OpenBSD CVS Update
    - markus@cvs.openbsd.org 2000/12/28 14:25:51
@@ -544,21 +567,21 @@
    bad reference to 'NeXT including it else were' on the #ifdef version.
 
 20001227
- - (bal) Typo in configure.in: entut?ent should be endut?ent.  Suggested by 
+ - (bal) Typo in configure.in: entut?ent should be endut?ent.  Suggested by
    Takumi Yamane <yamtak@b-session.com>
  - (bal) Checks for getrlimit(), sysconf(), and setdtablesize().  Patch
    by Corinna Vinschen <vinschen@redhat.com>
  - (djm) Fix catman-do target for non-bash
- - (bal) Typo in configure.in: entut?ent should be endut?ent.  Suggested by 
+ - (bal) Typo in configure.in: entut?ent should be endut?ent.  Suggested by
    Takumi Yamane <yamtak@b-session.com>
  - (bal) Checks for getrlimit(), sysconf(), and setdtablesize().  Patch
    by Corinna Vinschen <vinschen@redhat.com>
  - (djm) Fix catman-do target for non-bash
- - (bal) Fixed NeXT's lack of CPPFLAGS honoring.  
- - (bal) ssh-keyscan.c: NeXT (and older BSDs) don't support getrlimit() w/ 
+ - (bal) Fixed NeXT's lack of CPPFLAGS honoring.
+ - (bal) ssh-keyscan.c: NeXT (and older BSDs) don't support getrlimit() w/
    'RLIMIT_NOFILE'
- - (djm) Remove *.Ylonen files. They are no longer in the OpenBSD tree, 
-   the info in COPYING.Ylonen has been moved to the start of each 
+ - (djm) Remove *.Ylonen files. They are no longer in the OpenBSD tree,
+   the info in COPYING.Ylonen has been moved to the start of each
    SSH1-derived file and README.Ylonen is well out of date.
 
 20001223
@@ -609,9 +632,9 @@
    - markus@cvs.openbsd.org 2000/12/17 02:33:40
      [uidswap.c]
      typo; from wsanchez@apple.com
-	
+
 20001220
- - (djm) Workaround PAM inconsistencies between Solaris derived PAM code 
+ - (djm) Workaround PAM inconsistencies between Solaris derived PAM code
    and Linux-PAM. Based on report and fix from Andrew Morgan
    <morgan@transmeta.com>
 
@@ -672,7 +695,7 @@
  - (stevesk) OpenBSD CVS update:
    - markus@cvs.openbsd.org 2000/12/12 15:30:02
      [ssh-keyscan.c ssh.c sshd.c]
-     consistently use __progname; from stevesk@pobox.com	
+     consistently use __progname; from stevesk@pobox.com
 
 20001211
  - (bal) Applied patch to include ssh-keyscan into Redhat's package, and
@@ -686,16 +709,16 @@
 
 20001210
  - (bal) OpenBSD CVS updates
-   - markus@cvs.openbsd.org 2000/12/09 13:41:51 
+   - markus@cvs.openbsd.org 2000/12/09 13:41:51
      [cipher.c cipher.h rijndael.c rijndael.h rijndael_boxes.h]
      undo rijndael changes
-   - markus@cvs.openbsd.org 2000/12/09 13:48:31 
+   - markus@cvs.openbsd.org 2000/12/09 13:48:31
      [rijndael.c]
      fix byte order bug w/o introducing new implementation
-   - markus@cvs.openbsd.org 2000/12/09 14:08:27 
+   - markus@cvs.openbsd.org 2000/12/09 14:08:27
      [sftp-server.c]
      "" -> "." for realpath; from vinschen@redhat.com
-   - markus@cvs.openbsd.org 2000/12/09 14:06:54 
+   - markus@cvs.openbsd.org 2000/12/09 14:06:54
      [ssh-agent.c]
      extern int optind; from stevesk@sweden.hp.com
    - provos@cvs.openbsd.org 2000/12/09 23:51:11
@@ -704,19 +727,19 @@
 
 20001209
  - (bal) OpenBSD CVS updates:
-   - djm@cvs.openbsd.org 2000/12/07 4:24:59 
+   - djm@cvs.openbsd.org 2000/12/07 4:24:59
      [ssh.1]
      Typo fix from Wilfredo Sanchez <wsanchez@apple.com>; ok theo
 
 20001207
  - (bal) OpenBSD CVS updates:
-   - markus@cvs.openbsd.org 2000/12/06 22:58:14 
+   - markus@cvs.openbsd.org 2000/12/06 22:58:14
      [compat.c compat.h packet.c]
      disable debug messages for ssh.com/f-secure 2.0.1x, 2.1.0
    - markus@cvs.openbsd.org 2000/12/06 23:10:39
      [rijndael.c]
      unexpand(1)
-   - markus@cvs.openbsd.org 2000/12/06 23:05:43 
+   - markus@cvs.openbsd.org 2000/12/06 23:05:43
      [cipher.c cipher.h rijndael.c rijndael.h rijndael_boxes.h]
      new rijndael implementation. fixes endian bugs
 
@@ -746,14 +769,14 @@
 
 20001204
  - (bal) More C functions defined in NeXT that are unaccessable without
-   defining -POSIX. 
- - (bal) OpenBSD CVS updates: 
-   - markus@cvs.openbsd.org 2000/12/03 11:29:04 
+   defining -POSIX.
+ - (bal) OpenBSD CVS updates:
+   - markus@cvs.openbsd.org 2000/12/03 11:29:04
      [compat.c]
      remove fallback to SSH_BUG_HMAC now that the drafts are updated
    - markus@cvs.openbsd.org 2000/12/03 11:27:55
      [compat.c]
-     correctly match "2.1.0.pl2 SSH" etc; from 
+     correctly match "2.1.0.pl2 SSH" etc; from
      pekkas@netcore.fi/bugzilla.redhat
    - markus@cvs.openbsd.org 2000/12/03 11:15:03
      [auth2.c compat.c compat.h sshconnect2.c]
@@ -763,7 +786,7 @@
  - (bal) OpenBSD CVS updates:
   - markus@cvs.openbsd.org 2000/11/30 22:54:31
     [channels.c]
-    debug->warn if tried to do -R style fwd w/o client requesting this; 
+    debug->warn if tried to do -R style fwd w/o client requesting this;
     ok neils@
   - markus@cvs.openbsd.org 2000/11/29 20:39:17
     [cipher.c]
@@ -771,7 +794,7 @@
   - markus@cvs.openbsd.org 2000/11/30 18:33:05
     [ssh-agent.c]
     agents must not dump core, ok niels@
-  - markus@cvs.openbsd.org 2000/11/30 07:04:02 
+  - markus@cvs.openbsd.org 2000/11/30 07:04:02
     [ssh.1]
     T is for both protocols
   - markus@cvs.openbsd.org 2000/12/01 00:00:51
@@ -782,7 +805,7 @@
     check -T before isatty()
   - provos@cvs.openbsd.org 2000/11/29 13:51:27
     [sshconnect.c]
-    show IP address and hostname when new key is encountered. okay markus@ 
+    show IP address and hostname when new key is encountered. okay markus@
   - markus@cvs.openbsd.org 2000/11/30 22:53:35
     [sshconnect.c]
     disable agent/x11/port fwding if hostkey has changed; ok niels@
@@ -796,14 +819,14 @@
 
 20001202
  - (bal) Backed out of part of Alain St-Denis' loginrec.c patch.
- - (bal) Irix need some sort of mansubdir, patch by Michael Stone 
+ - (bal) Irix need some sort of mansubdir, patch by Michael Stone
    <mstone@cs.loyola.edu>
 
 20001129
  - (djm) Back out all the serverloop.c hacks. sshd will now hang again
    if there are background children with open fds.
  - (djm) bsd-rresvport.c bzero -> memset
- - (djm) Don't fail in defines.h on absence of 64 bit types (we will 
+ - (djm) Don't fail in defines.h on absence of 64 bit types (we will
    still fail during compilation of sftp-server).
  - (djm) Fail if ar is not found during configure
  - (djm) OpenBSD CVS updates:
@@ -833,7 +856,7 @@
  - (bal) Merge OpenBSD changes:
    - markus@cvs.openbsd.org  2000/11/15 22:31:36
      [auth-options.c]
-     case insensitive key options; from stevesk@sweeden.hp.com    
+     case insensitive key options; from stevesk@sweeden.hp.com
    - markus@cvs.openbsd.org  2000/11/16 17:55:43
      [dh.c]
      do not use perror() in sshd, after child is forked()
@@ -851,7 +874,7 @@
      do not reorder keys if a key is removed
    - markus@cvs.openbsd.org  2000/11/15 19:58:08
      [ssh.c]
-     just ignore non existing user keys   
+     just ignore non existing user keys
    - millert@cvs.openbsd.org  200/11/15 20:24:43
      [ssh-keygen.c]
      Add missing \n at end of error message.
@@ -864,7 +887,7 @@
 20001117
  - (bal) Changed from 'primes' to 'primes.out' for consistancy sake.  It
    has no affect the output.  Patch by Corinna Vinschen <vinschen@redhat.com>
- - (stevesk) Reworked progname support. 
+ - (stevesk) Reworked progname support.
  - (bal) Misplaced #include "includes.h" in bsd-setproctitle.c.  Patch by
    Shinichi Maruyama <marya@st.jip.co.jp>
 
@@ -875,7 +898,7 @@
    <roth@feep.net>
 
 20001113
- - (djm) Add pointer to http://www.imasy.or.jp/~gotoh/connect.c to 
+ - (djm) Add pointer to http://www.imasy.or.jp/~gotoh/connect.c to
    contrib/README
  - (djm) Merge OpenBSD changes:
    - markus@cvs.openbsd.org  2000/11/06 16:04:56
@@ -902,7 +925,7 @@
      [readconf.c readconf.h rsa.c rsa.h servconf.c servconf.h ssh-add.c]
      [ssh-agent.c ssh-keygen.1 ssh-keygen.c ssh.1 ssh.c ssh_config]
      [sshconnect1.c sshconnect2.c sshd.8 sshd.c sshd_config ssh-dss.c]
-     [ssh-dss.h ssh-rsa.c ssh-rsa.h dsa.c dsa.h]                   
+     [ssh-dss.h ssh-rsa.c ssh-rsa.h dsa.c dsa.h]
      add support for RSA to SSH2.  please test.
      there are now 3 types of keys: RSA1 is used by ssh-1 only,
      RSA and DSA are used by SSH2.
@@ -926,10 +949,10 @@
  - (djm) Added /etc/primes for kex DH group neg, fixup Makefile.in and
    packaging files
  - (djm) Fix new Makefile.in warnings
- - (djm) Fix vsprintf("%h") in bsd-snprintf.c, short int va_args are 
-   promoted to type int. Report and fix from Dan Astoorian 
+ - (djm) Fix vsprintf("%h") in bsd-snprintf.c, short int va_args are
+   promoted to type int. Report and fix from Dan Astoorian
    <djast@cs.toronto.edu>
- - (djm) Hardwire sysconfdir in RPM spec files as some RPM versions get 
+ - (djm) Hardwire sysconfdir in RPM spec files as some RPM versions get
    it wrong. Report from Bennett Todd <bet@rahul.net>
 
 20001110
@@ -937,10 +960,10 @@
  - (bal) Changed from --with-skey to --with-skey=PATH in configure.in
  - (bal) Added in check to verify S/Key library is being detected in
    configure.in
- - (bal) next-posix.h - added another prototype wrapped in POSIX ifdef/endif. 
+ - (bal) next-posix.h - added another prototype wrapped in POSIX ifdef/endif.
    Patch by Mark Miller <markm@swoon.net>
  - (bal) Added 'util.h' header to loginrec.c only if HAVE_UTIL_H is defined
-   to remove warnings under MacOS X.  Patch by Mark Miller <markm@swoon.net> 
+   to remove warnings under MacOS X.  Patch by Mark Miller <markm@swoon.net>
  - (bal) Fixed LDFLAG mispelling in configure.in for --with-afs
 
 20001107
@@ -954,7 +977,7 @@
 20001106
  - (djm) Use Jim's new 1.0.3 askpass in Redhat RPMs
  - (djm) Manually fix up missed diff hunks (mainly RCS idents)
- - (djm) Remove UPGRADING document in favour of a link to the better 
+ - (djm) Remove UPGRADING document in favour of a link to the better
    maintained FAQ on www.openssh.com
  - (djm) Fix multiple dependancy on gnome-libs from Pekka Savola
    <pekkas@netcore.fi>
@@ -989,10 +1012,10 @@
  - (bal) next-posix.h - spelling and forgot a prototype
 
 20001028
- - (djm) fix select hack in serverloop.c from Philippe WILLEM 
+ - (djm) fix select hack in serverloop.c from Philippe WILLEM
    <Philippe.WILLEM@urssaf.fr>
  - (djm) Fix mangled AIXAUTHENTICATE code
- - (djm) authctxt->pw may be NULL. Fix from Markus Friedl 
+ - (djm) authctxt->pw may be NULL. Fix from Markus Friedl
    <markus.friedl@informatik.uni-erlangen.de>
  - (djm) Sync with OpenBSD:
    - markus@cvs.openbsd.org  2000/10/16 15:46:32
@@ -1029,7 +1052,7 @@
    - markus@cvs.openbsd.org  2000/10/27 01:32:19
      [channels.c channels.h clientloop.c serverloop.c session.c]
      [ssh.c util.c]
-     enable non-blocking IO on channels, and tty's (except for the 
+     enable non-blocking IO on channels, and tty's (except for the
      client ttys).
 
 20001027
@@ -1060,7 +1083,7 @@
    supplied passphrase. Problem report from Lutz Jaenicke
    <Lutz.Jaenicke@aet.TU-Cottbus.DE>
  - (bal) Changed from GNU rx to PCRE on suggestion from djm.
- - (bal) Integrated Sony NEWS-OS patches from NAKAJI Hirouyuki 
+ - (bal) Integrated Sony NEWS-OS patches from NAKAJI Hirouyuki
    <nakaji@tutrp.tut.ac.jp>
 
 20001016
@@ -1079,7 +1102,7 @@
      AllowTcpForwarding; from naddy@
    - markus@cvs.openbsd.org  2000/10/14 06:16:56
      [auth2.c compat.c compat.h sshconnect2.c version.h]
-     OpenSSH_2.3; note that is is not complete, but the version number 
+     OpenSSH_2.3; note that is is not complete, but the version number
      needs to be changed for interoperability reasons
    - markus@cvs.openbsd.org  2000/10/14 06:19:45
      [auth-rsa.c]
@@ -1091,12 +1114,12 @@
    - markus@cvs.openbsd.org  2000/10/15 08:18:31
      [rijndael.c]
      typo
- - (djm) Copy manpages back over from OpenBSD - too tedious to wade 
+ - (djm) Copy manpages back over from OpenBSD - too tedious to wade
    through diffs
- - (djm) Added condrestart to Redhat init script. Patch from Pekka Savola 
+ - (djm) Added condrestart to Redhat init script. Patch from Pekka Savola
    <pekkas@netcore.fi>
  - (djm) Update version in Redhat spec file
- - (djm) Merge some of Nalin Dahyabhai <nalin@redhat.com> changes from the 
+ - (djm) Merge some of Nalin Dahyabhai <nalin@redhat.com> changes from the
    Redhat 7.0 spec file
  - (djm) Make inability to read/write PRNG seedfile non-fatal
 
@@ -1108,7 +1131,7 @@
  - (bal) Add support for realpath and getcwd for platforms with broken
    or missing realpath implementations for sftp-server.
  - (bal) Corrected mistake in INSTALL in regards to GNU rx library
- - (bal) Add support for GNU rx library for those lacking regexp support 
+ - (bal) Add support for GNU rx library for those lacking regexp support
  - (djm) Don't accept PAM_PROMPT_ECHO_ON messages during initial auth
  - (djm) Revert SSH2 serverloop hack, will find a better way.
  - (djm) Add workaround for Linux 2.4's gratuitious errno change. Patch
@@ -1214,11 +1237,11 @@
 
 20000930
  - (djm) Irix ssh_prng_cmds path fix from Pekka Savola <pekkas@netcore.fi>
- - (djm) Support in bsd-snprintf.c for long long conversions from 
+ - (djm) Support in bsd-snprintf.c for long long conversions from
    Ben Lindstrom <mouring@pconline.com>
  - (djm) Cleanup NeXT support from Ben Lindstrom <mouring@pconline.com>
  - (djm) Ignore SIGPIPEs from serverloop to child. Fixes crashes with
-   very short lived X connections. Bug report from Tobias Oetiker 
+   very short lived X connections. Bug report from Tobias Oetiker
    <oetiker@ee.ethz.ch>. Fix from Markus Friedl <markus@cvs.openbsd.org>
  - (djm) Add recent InitScripts as a RPM dependancy for openssh-server
    patch from Pekka Savola <pekkas@netcore.fi>
@@ -1234,27 +1257,27 @@
    - markus@cvs.openbsd.org  2000/09/28 12:03:18
      [channels.c]
      debug -> debug2 cleanup
- - (djm) Irix strips "/dev/tty" from [uw]tmp entries (other systems only 
+ - (djm) Irix strips "/dev/tty" from [uw]tmp entries (other systems only
    strip "/dev/"). Fix loginrec.c based on patch from Alain St-Denis
    <Alain.St-Denis@ec.gc.ca>
- - (djm) Fix 9 character passphrase failure with gnome-ssh-askpass. 
-   Problem was caused by interrupted read in ssh-add. Report from Donald 
+ - (djm) Fix 9 character passphrase failure with gnome-ssh-askpass.
+   Problem was caused by interrupted read in ssh-add. Report from Donald
    J. Barry <don@astro.cornell.edu>
 
 20000929
  - (djm) Fix SSH2 not terminating until all background tasks done problem.
- - (djm) Another off-by-one fix from Pavel Kankovsky 
-   <peak@argo.troja.mff.cuni.cz> 
+ - (djm) Another off-by-one fix from Pavel Kankovsky
+   <peak@argo.troja.mff.cuni.cz>
  - (djm) Clean up. Strip some unnecessary differences with OpenBSD's code,
    tidy necessary differences. Use Markus' new debugN() in entropy.c
- - (djm) Merged big SCO portability patch from Tim Rice 
+ - (djm) Merged big SCO portability patch from Tim Rice
    <tim@multitalents.net>
 
 20000926
  - (djm) Update X11-askpass to 1.0.2 in RPM spec file
  - (djm) Define _REENTRANT to pickup strtok_r() on HP/UX
- - (djm) Security: fix off-by-one buffer overrun in fake-getnameinfo.c. 
-   Report and fix from Pavel Kankovsky <peak@argo.troja.mff.cuni.cz> 
+ - (djm) Security: fix off-by-one buffer overrun in fake-getnameinfo.c.
+   Report and fix from Pavel Kankovsky <peak@argo.troja.mff.cuni.cz>
 
 20000924
  - (djm) Merged cleanup patch from Mark Miller <markm@swoon.net>
@@ -1263,14 +1286,14 @@
    <markm@swoon.net>
 
 20000923
- - (djm) Fix address logging in utmp from Kevin Steves 
+ - (djm) Fix address logging in utmp from Kevin Steves
    <stevesk@sweden.hp.com>
  - (djm) Redhat spec and manpage fixes from Pekka Savola <pekkas@netcore.fi>
  - (djm) Seperate tests for int64_t and u_int64_t types
- - (djm) Tweak password expiry checking at suggestion of Kevin Steves 
+ - (djm) Tweak password expiry checking at suggestion of Kevin Steves
    <stevesk@sweden.hp.com>
  - (djm) NeXT patch from Ben Lindstrom <mouring@pconline.com>
- - (djm) Use printf %lld instead of %qd in sftp-server.c. Fix from 
+ - (djm) Use printf %lld instead of %qd in sftp-server.c. Fix from
    Michael Stone <mstone@cs.loyola.edu>
  - (djm) OpenBSD CVS sync:
    - markus@cvs.openbsd.org  2000/09/17 09:38:59
@@ -1304,13 +1327,13 @@
    <asminer@cs.iastate.edu>
 
 20000916
- - (djm) Fix SSL search order from Lutz Jaenicke 
+ - (djm) Fix SSL search order from Lutz Jaenicke
    <Lutz.Jaenicke@aet.TU-Cottbus.DE>
  - (djm) New SuSE spec from Corinna Vinschen <corinna@vinschen.de>
  - (djm) Update CygWin support from Corinna Vinschen <vinschen@cygnus.com>
  - (djm) Use a real struct sockaddr inside the fake struct sockaddr_storage.
    Patch from Larry Jones <larry.jones@sdrc.com>
- - (djm) Add Steve VanDevender's <stevev@darkwing.uoregon.edu> PAM 
+ - (djm) Add Steve VanDevender's <stevev@darkwing.uoregon.edu> PAM
    password change patch.
  - (djm) Bring licenses on my stuff in line with OpenBSD's
  - (djm) Cleanup auth-passwd.c and unify HP/UX authentication. Patch from
@@ -1321,9 +1344,9 @@
  - (djm) Update Redhat SPEC file accordingly
  - (djm) Add Kevin Steves <stevesk@sweden.hp.com> HP/UX contrib files
  - (djm) Add Charles Levert <charles@comm.polymtl.ca> getpgrp patch
- - (djm) Fix password auth on HP/UX 10.20. Patch from Dirk De Wachter 
+ - (djm) Fix password auth on HP/UX 10.20. Patch from Dirk De Wachter
    <Dirk.DeWachter@rug.ac.be>
- - (djm) Fixprogs and entropy list fixes from Larry Jones 
+ - (djm) Fixprogs and entropy list fixes from Larry Jones
    <larry.jones@sdrc.com>
  - (djm) Fix for SuSE spec file from Takashi YOSHIDA
    <tyoshida@gemini.rc.kyushu-u.ac.jp>
@@ -1342,10 +1365,10 @@
      prototype
    - deraadt@cvs.openbsd.org 2000/09/07 14:27:56
      [ALL]
-     cleanup copyright notices on all files.  I have attempted to be 
-     accurate with the details.  everything is now under Tatu's licence 
-     (which I copied from his readme), and/or the core-sdi bsd-ish thing 
-     for deattack, or various openbsd developers under a 2-term bsd 
+     cleanup copyright notices on all files.  I have attempted to be
+     accurate with the details.  everything is now under Tatu's licence
+     (which I copied from his readme), and/or the core-sdi bsd-ish thing
+     for deattack, or various openbsd developers under a 2-term bsd
      licence.  We're not changing any rules, just being accurate.
    - markus@cvs.openbsd.org  2000/09/07 14:40:30
      [channels.c channels.h clientloop.c serverloop.c ssh.c]
@@ -1799,7 +1822,7 @@
  - (djm) Added 'distprep' make target to simplify packaging
  - (djm) Added patch from Chris Adams <cmadams@hiwaay.net> to add OSF SIA
    support. Enable using "USE_SIA=1 ./configure [options]"
-  
+
 20000627
  - (djm) Fixes to login code - not setting li->uid, cleanups
  - (djm) Formatting
@@ -1921,7 +1944,7 @@
   - Don't try to retrieve lastlog from wtmp/wtmpx if DISABLE_LASTLOG is
      def'd
   - Set AIX to use preformatted manpages
-  
+
 20000610
  - (djm) Minor doc tweaks
  - (djm) Fix for configure on bash2 from Jim Knoble <jmknoble@jmknoble.cx>
@@ -1947,7 +1970,7 @@
     teach protocol v2 to count login failures properly and also enable an
     explanation of why the password prompt comes up again like v1; this is NOT
     crypto
-  - markus@cvs.openbsd.org 
+  - markus@cvs.openbsd.org
     [readconf.c readconf.h servconf.c servconf.h session.c ssh.1 ssh.c sshd.8]
     xauth_location support; pr 1234
     [readconf.c sshconnect2.c]
@@ -1978,7 +2001,7 @@
  - (andre) New login code
     - Remove bsd-login.[ch] and all the OpenBSD-derived code in login.c
     - Add loginrec.[ch], logintest.c and autoconf code
-  
+
 20000531
  - Cleanup of auth.c, login.c and fake-*
  - Cleanup of auth-pam.c, save and print "account expired" error messages
@@ -2383,7 +2406,7 @@
      no adjust after close
    - [sshd.c compat.c ]
      interop w/ latest ssh.com windows client.
- 
+
 20000406
  - OpenBSD CVS update:
    - [channels.c]
@@ -2704,7 +2727,7 @@
    - [readpass.c]
      instead of blocking SIGINT, catch it ourselves, so that we can clean
      the tty modes up and kill ourselves -- instead of our process group
-     leader (scp, cvs, ...) going away and leaving us in noecho mode. 
+     leader (scp, cvs, ...) going away and leaving us in noecho mode.
      people with cbreak shells never even noticed..
    - [ssh-add.1 ssh-agent.1 ssh-keygen.1 ssh.1 sshd.8]
      ie. -> i.e.,
@@ -2741,7 +2764,7 @@
 20000118
  - Fixed --with-pid-dir option
  - Makefile fix from Gary E. Miller <gem@rellim.com>
- - Compile fix for HPUX and Solaris from Andre Lucas   
+ - Compile fix for HPUX and Solaris from Andre Lucas
    <andre.lucas@dial.pipex.com>
 
 20000117
@@ -2844,7 +2867,7 @@
 
 20000103
  - Add explicit make rules for files proccessed by fixpaths.
- - Fix "make install" in RPM spec files. Report from Tenkou N. Hattori 
+ - Fix "make install" in RPM spec files. Report from Tenkou N. Hattori
    <tnh@kondara.org>
  - Removed "nullok" directive from default PAM configuration files.
    Added information on enabling EmptyPasswords on openssh+PAM in
@@ -3019,7 +3042,7 @@
    - Use LDFLAGS correctly
    - Fix SIGIO error in scp
    - Simplify status line printing in scp
- - Added better test for inline functions compiler support from 
+ - Added better test for inline functions compiler support from
    Darren_Hall@progressive.com
 
 19991214
@@ -3247,7 +3270,7 @@
      print usage() everytime we get bad options
    - [ssh-keygen.c] overflow, djm@mindrot.org
    - [sshd.c] fix sigchld race; cjc5@po.cwru.edu
-    
+
 19991120
  - Merged more Solaris support from Marc G. Fournier
    <marc.fournier@acadiau.ca>
diff --git a/Makefile.in b/Makefile.in
index c3cd580..8ea7191 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -33,9 +33,9 @@
 
 INSTALL_SSH_PRNG_CMDS=@INSTALL_SSH_PRNG_CMDS@
 
-@NO_SFTP@SFTP-SERVER=sftp-server$(EXEEXT)
+@NO_SFTP@SFTP_PROGS=sftp-server$(EXEEXT) sftp$(EXEEXT)
 
-TARGETS=ssh$(EXEEXT) sshd$(EXEEXT) ssh-add$(EXEEXT) ssh-keygen$(EXEEXT) ssh-keyscan${EXEEXT} ssh-agent$(EXEEXT) scp$(EXEEXT) $(SFTP-SERVER)
+TARGETS=ssh$(EXEEXT) sshd$(EXEEXT) ssh-add$(EXEEXT) ssh-keygen$(EXEEXT) ssh-keyscan${EXEEXT} ssh-agent$(EXEEXT) scp$(EXEEXT) $(SFTP_PROGS)
 
 LIBSSH_OBJS=atomicio.o authfd.o authfile.o bufaux.o buffer.o canohost.o channels.o cipher.o cli.o compat.o compress.o crc32.o deattack.o dispatch.o hmac.o hostfile.o key.o kex.o log.o match.o misc.o mpaux.o nchan.o packet.o radix.o rijndael.o entropy.o readpass.o rsa.o ssh-dss.o ssh-rsa.o tildexpand.o ttymodes.o uidswap.o uuencode.o xmalloc.o 
 
@@ -43,8 +43,8 @@
 
 SSHDOBJS= sshd.o auth.o auth1.o auth2.o auth-chall.o auth2-chall.o auth-rhosts.o auth-options.o auth-krb4.o auth-pam.o auth2-pam.o auth-passwd.o auth-rsa.o auth-rh-rsa.o dh.o pty.o log-server.o login.o loginrec.o servconf.o serverloop.o md5crypt.o session.o groupaccess.o
 
-TROFFMAN	= scp.1 ssh-add.1 ssh-agent.1 ssh-keygen.1 ssh-keyscan.1 ssh.1 sshd.8 sftp-server.8
-CATMAN		= scp.0 ssh-add.0 ssh-agent.0 ssh-keygen.0 ssh-keyscan.0 ssh.0 sshd.0 sftp-server.0
+TROFFMAN	= scp.1 ssh-add.1 ssh-agent.1 ssh-keygen.1 ssh-keyscan.1 ssh.1 sshd.8 sftp-server.8 sftp.1
+CATMAN		= scp.0 ssh-add.0 ssh-agent.0 ssh-keygen.0 ssh-keyscan.0 ssh.0 sshd.0 sftp-server.0 sftp.1
 MANPAGES	= @MANTYPE@
 
 CONFIGFILES=sshd_config ssh_config primes
@@ -105,8 +105,12 @@
 ssh-keyscan$(EXEEXT): $(LIBCOMPAT) libssh.a log-client.o ssh-keyscan.o
 	$(LD) -o $@ ssh-keyscan.o log-client.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) 
 
-sftp-server$(EXEEXT): $(LIBCOMPAT) libssh.a sftp-server.o log-server.o
-	$(LD) -o $@ sftp-server.o log-server.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) 
+sftp-server$(EXEEXT): $(LIBCOMPAT) libssh.a sftp-server.o sftp-common.o log-server.o
+	$(LD) -o $@ sftp-server.o sftp-common.o log-server.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) 
+
+# XXX: need to -lssh twice here!
+sftp$(EXEEXT): $(LIBCOMPAT) libssh.a sftp.o sftp-client.o sftp-int.o sftp-common.o log-client.o
+	$(LD) -o $@ sftp.o sftp-client.o sftp-common.o sftp-int.o log-client.o $(LDFLAGS) -lssh -lopenbsd-compat -lssh $(LIBS)
 
 # test driver for the loginrec code - not built by default
 logintest: logintest.o $(LIBCOMPAT) libssh.a log-client.o loginrec.o
@@ -156,6 +160,7 @@
 	$(INSTALL) -m 0755 -s ssh-keygen $(DESTDIR)$(bindir)/ssh-keygen
 	$(INSTALL) -m 0775 -s ssh-keyscan $(DESTDIR)$(bindir)/ssh-keyscan
 	$(INSTALL) -m 0755 -s sshd $(DESTDIR)$(sbindir)/sshd
+	@NO_SFTP@$$(INSTALL) -m 0755 -s sftp $(DESTDIR)$(bindir)/sftp
 	@NO_SFTP@$(INSTALL) -m 0755 -s sftp-server $(DESTDIR)$(libexecdir)/sftp-server
 	$(INSTALL) -m 644 ssh.[01].out $(DESTDIR)$(mandir)/$(mansubdir)1/ssh.1
 	$(INSTALL) -m 644 scp.[01].out $(DESTDIR)$(mandir)/$(mansubdir)1/scp.1
@@ -164,6 +169,7 @@
 	$(INSTALL) -m 644 ssh-keygen.[01].out $(DESTDIR)$(mandir)/$(mansubdir)1/ssh-keygen.1
 	$(INSTALL) -m 644 ssh-keyscan.[01].out $(DESTDIR)$(mandir)/$(mansubdir)1/ssh-keyscan.1
 	$(INSTALL) -m 644 sshd.[08].out $(DESTDIR)$(mandir)/$(mansubdir)8/sshd.8
+	@NO_SFTP@$$(INSTALL) -m 644 sftp.[01].out $(DESTDIR)$(mandir)/$(mansubdir)1/sftp.1
 	@NO_SFTP@$(INSTALL) -m 644 sftp-server.[08].out $(DESTDIR)$(mandir)/$(mansubdir)8/sftp-server.8
 	-rm -f $(DESTDIR)$(bindir)/slogin
 	ln -s ssh$(EXEEXT) $(DESTDIR)$(bindir)/slogin
@@ -241,6 +247,7 @@
 	-rm -f $(DESTDIR)$(bindir)/ssh-agent$(EXEEXT)
 	-rm -f $(DESTDIR)$(bindir)/ssh-keygen$(EXEEXT)
 	-rm -f $(DESTDIR)$(bindir)/ssh-keyscan$(EXEEXT)
+	-rm -f $(DESTDIR)$(bindir)/sftp$(EXEEXT)
 	-rm -f $(DESTDIR)$(sbindir)/sshd$(EXEEXT)
 	-rm -r $(DESTDIR)$(libexecdir)/sftp-server$(EXEEXT)
 	-rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/ssh.1
@@ -248,6 +255,7 @@
 	-rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/ssh-add.1
 	-rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/ssh-agent.1
 	-rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/ssh-keygen.1
+	-rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/sftp.1
 	-rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/ssh-keyscan.1
 	-rm -f $(DESTDIR)$(mandir)/$(mansubdir)8/sshd.8
 	-rm -f $(DESTDIR)$(mandir)/$(mansubdir)8/sftp-server.8
diff --git a/TODO b/TODO
index 62c51e1..1165a0d 100644
--- a/TODO
+++ b/TODO
@@ -1,4 +1,6 @@
 Programming:
+- Grep for 'XXX' comments and fix
+
 - Replacement for setproctitle() - HP/UX support only currently
 
 - Improve PAM support (a pam_lastlog module will cause sshd to exit)
diff --git a/auth-options.c b/auth-options.c
index 5457d9b..04d2f08 100644
--- a/auth-options.c
+++ b/auth-options.c
@@ -10,7 +10,7 @@
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: auth-options.c,v 1.11 2001/01/21 19:05:41 markus Exp $");
+RCSID("$OpenBSD: auth-options.c,v 1.12 2001/02/03 10:08:36 markus Exp $");
 
 #include "packet.h"
 #include "xmalloc.h"
@@ -18,6 +18,7 @@
 #include "log.h"
 #include "canohost.h"
 #include "auth-options.h"
+#include "servconf.h"
 
 /* Flags set authorized_keys flags */
 int no_port_forwarding_flag = 0;
@@ -31,6 +32,8 @@
 /* "environment=" options. */
 struct envstring *custom_environment = NULL;
 
+extern ServerOptions options;
+
 void
 auth_clear_options(void)
 {
@@ -55,61 +58,61 @@
  * side effect: sets key option flags
  */
 int
-auth_parse_options(struct passwd *pw, char *options, char *file, u_long linenum)
+auth_parse_options(struct passwd *pw, char *opts, char *file, u_long linenum)
 {
 	const char *cp;
-	if (!options)
+	if (!opts)
 		return 1;
 
 	/* reset options */
 	auth_clear_options();
 
-	while (*options && *options != ' ' && *options != '\t') {
+	while (*opts && *opts != ' ' && *opts != '\t') {
 		cp = "no-port-forwarding";
-		if (strncasecmp(options, cp, strlen(cp)) == 0) {
+		if (strncasecmp(opts, cp, strlen(cp)) == 0) {
 			packet_send_debug("Port forwarding disabled.");
 			no_port_forwarding_flag = 1;
-			options += strlen(cp);
+			opts += strlen(cp);
 			goto next_option;
 		}
 		cp = "no-agent-forwarding";
-		if (strncasecmp(options, cp, strlen(cp)) == 0) {
+		if (strncasecmp(opts, cp, strlen(cp)) == 0) {
 			packet_send_debug("Agent forwarding disabled.");
 			no_agent_forwarding_flag = 1;
-			options += strlen(cp);
+			opts += strlen(cp);
 			goto next_option;
 		}
 		cp = "no-X11-forwarding";
-		if (strncasecmp(options, cp, strlen(cp)) == 0) {
+		if (strncasecmp(opts, cp, strlen(cp)) == 0) {
 			packet_send_debug("X11 forwarding disabled.");
 			no_x11_forwarding_flag = 1;
-			options += strlen(cp);
+			opts += strlen(cp);
 			goto next_option;
 		}
 		cp = "no-pty";
-		if (strncasecmp(options, cp, strlen(cp)) == 0) {
+		if (strncasecmp(opts, cp, strlen(cp)) == 0) {
 			packet_send_debug("Pty allocation disabled.");
 			no_pty_flag = 1;
-			options += strlen(cp);
+			opts += strlen(cp);
 			goto next_option;
 		}
 		cp = "command=\"";
-		if (strncasecmp(options, cp, strlen(cp)) == 0) {
+		if (strncasecmp(opts, cp, strlen(cp)) == 0) {
 			int i;
-			options += strlen(cp);
-			forced_command = xmalloc(strlen(options) + 1);
+			opts += strlen(cp);
+			forced_command = xmalloc(strlen(opts) + 1);
 			i = 0;
-			while (*options) {
-				if (*options == '"')
+			while (*opts) {
+				if (*opts == '"')
 					break;
-				if (*options == '\\' && options[1] == '"') {
-					options += 2;
+				if (*opts == '\\' && opts[1] == '"') {
+					opts += 2;
 					forced_command[i++] = '"';
 					continue;
 				}
-				forced_command[i++] = *options++;
+				forced_command[i++] = *opts++;
 			}
-			if (!*options) {
+			if (!*opts) {
 				debug("%.100s, line %lu: missing end quote",
 				    file, linenum);
 				packet_send_debug("%.100s, line %lu: missing end quote",
@@ -118,28 +121,28 @@
 			}
 			forced_command[i] = 0;
 			packet_send_debug("Forced command: %.900s", forced_command);
-			options++;
+			opts++;
 			goto next_option;
 		}
 		cp = "environment=\"";
-		if (strncasecmp(options, cp, strlen(cp)) == 0) {
+		if (strncasecmp(opts, cp, strlen(cp)) == 0) {
 			int i;
 			char *s;
 			struct envstring *new_envstring;
-			options += strlen(cp);
-			s = xmalloc(strlen(options) + 1);
+			opts += strlen(cp);
+			s = xmalloc(strlen(opts) + 1);
 			i = 0;
-			while (*options) {
-				if (*options == '"')
+			while (*opts) {
+				if (*opts == '"')
 					break;
-				if (*options == '\\' && options[1] == '"') {
-					options += 2;
+				if (*opts == '\\' && opts[1] == '"') {
+					opts += 2;
 					s[i++] = '"';
 					continue;
 				}
-				s[i++] = *options++;
+				s[i++] = *opts++;
 			}
-			if (!*options) {
+			if (!*opts) {
 				debug("%.100s, line %lu: missing end quote",
 				    file, linenum);
 				packet_send_debug("%.100s, line %lu: missing end quote",
@@ -149,7 +152,7 @@
 			s[i] = 0;
 			packet_send_debug("Adding to environment: %.900s", s);
 			debug("Adding to environment: %.900s", s);
-			options++;
+			opts++;
 			new_envstring = xmalloc(sizeof(struct envstring));
 			new_envstring->s = s;
 			new_envstring->next = custom_environment;
@@ -157,23 +160,26 @@
 			goto next_option;
 		}
 		cp = "from=\"";
-		if (strncasecmp(options, cp, strlen(cp)) == 0) {
+		if (strncasecmp(opts, cp, strlen(cp)) == 0) {
 			int mname, mip;
-			char *patterns = xmalloc(strlen(options) + 1);
+			const char *remote_ip = get_remote_ipaddr();
+			const char *remote_host = get_canonical_hostname(
+			    options.reverse_mapping_check);
+			char *patterns = xmalloc(strlen(opts) + 1);
 			int i;
-			options += strlen(cp);
+			opts += strlen(cp);
 			i = 0;
-			while (*options) {
-				if (*options == '"')
+			while (*opts) {
+				if (*opts == '"')
 					break;
-				if (*options == '\\' && options[1] == '"') {
-					options += 2;
+				if (*opts == '\\' && opts[1] == '"') {
+					opts += 2;
 					patterns[i++] = '"';
 					continue;
 				}
-				patterns[i++] = *options++;
+				patterns[i++] = *opts++;
 			}
-			if (!*options) {
+			if (!*opts) {
 				debug("%.100s, line %lu: missing end quote",
 				    file, linenum);
 				packet_send_debug("%.100s, line %lu: missing end quote",
@@ -181,24 +187,26 @@
 				continue;
 			}
 			patterns[i] = 0;
-			options++;
+			opts++;
 			/*
 			 * Deny access if we get a negative
 			 * match for the hostname or the ip
 			 * or if we get not match at all
 			 */
-			mname = match_hostname(get_canonical_hostname(),
-			    patterns, strlen(patterns));
-			mip = match_hostname(get_remote_ipaddr(),
-			    patterns, strlen(patterns));
+			mname = match_hostname(remote_host, patterns,
+			    strlen(patterns));
+			mip = match_hostname(remote_ip, patterns,
+			    strlen(patterns));
 			xfree(patterns);
 			if (mname == -1 || mip == -1 ||
 			    (mname != 1 && mip != 1)) {
-				log("Authentication tried for %.100s with correct key but not from a permitted host (host=%.200s, ip=%.200s).",
-				    pw->pw_name, get_canonical_hostname(),
-				    get_remote_ipaddr());
-				packet_send_debug("Your host '%.200s' is not permitted to use this key for login.",
-				get_canonical_hostname());
+				log("Authentication tried for %.100s with "
+				    "correct key but not from a permitted "
+				    "host (host=%.200s, ip=%.200s).",
+				    pw->pw_name, remote_host, remote_ip);
+				packet_send_debug("Your host '%.200s' is not "
+				    "permitted to use this key for login.",
+				    remote_host);
 				/* deny access */
 				return 0;
 			}
@@ -210,13 +218,13 @@
 		 * Skip the comma, and move to the next option
 		 * (or break out if there are no more).
 		 */
-		if (!*options)
+		if (!*opts)
 			fatal("Bugs in auth-options.c option processing.");
-		if (*options == ' ' || *options == '\t')
+		if (*opts == ' ' || *opts == '\t')
 			break;		/* End of options. */
-		if (*options != ',')
+		if (*opts != ',')
 			goto bad_option;
-		options++;
+		opts++;
 		/* Process the next option. */
 	}
 	/* grant access */
@@ -224,9 +232,9 @@
 
 bad_option:
 	log("Bad options in %.100s file, line %lu: %.50s",
-	    file, linenum, options);
+	    file, linenum, opts);
 	packet_send_debug("Bad options in %.100s file, line %lu: %.50s",
-	    file, linenum, options);
+	    file, linenum, opts);
 	/* deny access */
 	return 0;
 }
diff --git a/auth-pam.c b/auth-pam.c
index 3d550b4..122896c 100644
--- a/auth-pam.c
+++ b/auth-pam.c
@@ -32,7 +32,7 @@
 #include "canohost.h"
 #include "readpass.h"
 
-RCSID("$Id: auth-pam.c,v 1.22 2001/01/22 05:34:40 mouring Exp $");
+RCSID("$Id: auth-pam.c,v 1.23 2001/02/04 12:20:19 djm Exp $");
 
 #define NEW_AUTHTOK_MSG \
 	"Warning: Your password has expired, please change it now"
@@ -211,10 +211,12 @@
 int do_pam_account(char *username, char *remote_user)
 {
 	int pam_retval;
+	extern ServerOptions options;
 	
-	debug("PAM setting rhost to \"%.200s\"", get_canonical_hostname());
+	debug("PAM setting rhost to \"%.200s\"", 
+	    get_canonical_hostname(options.reverse_mapping_check));
 	pam_retval = pam_set_item(pamh, PAM_RHOST, 
-		get_canonical_hostname());
+		get_canonical_hostname(options.reverse_mapping_check));
 	if (pam_retval != PAM_SUCCESS) {
 		fatal("PAM set rhost failed[%d]: %.200s", 
 			pam_retval, PAM_STRERROR(pamh, pam_retval));
diff --git a/auth-rh-rsa.c b/auth-rh-rsa.c
index 87d5154..0edbdb5 100644
--- a/auth-rh-rsa.c
+++ b/auth-rh-rsa.c
@@ -13,7 +13,7 @@
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: auth-rh-rsa.c,v 1.21 2001/01/21 19:05:42 markus Exp $");
+RCSID("$OpenBSD: auth-rh-rsa.c,v 1.22 2001/02/03 10:08:36 markus Exp $");
 
 #include "packet.h"
 #include "xmalloc.h"
@@ -49,7 +49,8 @@
 	if (!auth_rhosts(pw, client_user))
 		return 0;
 
-	canonical_hostname = get_canonical_hostname();
+	canonical_hostname = get_canonical_hostname(
+	    options.reverse_mapping_check);
 
 	debug("Rhosts RSA authentication: canonical host %.900s", canonical_hostname);
 
diff --git a/auth-rhosts.c b/auth-rhosts.c
index 4f9ea88..d8d10ff 100644
--- a/auth-rhosts.c
+++ b/auth-rhosts.c
@@ -14,7 +14,7 @@
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: auth-rhosts.c,v 1.19 2001/01/21 19:05:42 markus Exp $");
+RCSID("$OpenBSD: auth-rhosts.c,v 1.20 2001/02/03 10:08:36 markus Exp $");
 
 #include "packet.h"
 #include "xmalloc.h"
@@ -183,7 +183,7 @@
 	    stat(_PATH_SSH_HOSTS_EQUIV, &st) < 0)
 		return 0;
 
-	hostname = get_canonical_hostname();
+	hostname = get_canonical_hostname(options.reverse_mapping_check);
 	ipaddr = get_remote_ipaddr();
 
 	/* If not logging in as superuser, try /etc/hosts.equiv and shosts.equiv. */
diff --git a/auth.c b/auth.c
index 187216d..4e3cf67 100644
--- a/auth.c
+++ b/auth.c
@@ -23,7 +23,7 @@
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: auth.c,v 1.14 2001/01/21 19:05:43 markus Exp $");
+RCSID("$OpenBSD: auth.c,v 1.15 2001/02/03 10:08:37 markus Exp $");
 
 #ifdef HAVE_LOGIN_H
 #include <login.h>
@@ -228,7 +228,7 @@
 		log("Root login accepted for forced command.");
 		return 1;
 	} else {
-		log("ROOT LOGIN REFUSED FROM %.200s", get_canonical_hostname());
+		log("ROOT LOGIN REFUSED FROM %.200s", get_remote_ipaddr());
 		return 0;
 	}
 }
diff --git a/auth1.c b/auth1.c
index 6e9808e..1986b2d 100644
--- a/auth1.c
+++ b/auth1.c
@@ -266,8 +266,8 @@
 #elif defined(HAVE_OSF_SIA)
 			/* Do SIA auth with password */
 			if (sia_validate_user(NULL, saved_argc, saved_argv, 
-				get_canonical_hostname(), pw->pw_name, NULL, 0, 
-				NULL, password) == SIASUCCESS) {
+			    get_canonical_hostname(options.reverse_mapping_check), 
+			    pw->pw_name, NULL, 0, NULL, password) == SIASUCCESS) {
 				authenticated = 1;
 			}
 #else /* !USE_PAM && !HAVE_OSF_SIA */
@@ -347,7 +347,9 @@
 
 		if (authctxt->failures++ > AUTH_FAIL_MAX) {
 #ifdef WITH_AIXAUTHENTICATE 
-			loginfailed(user,get_canonical_hostname(),"ssh");
+			loginfailed(user, 
+			    get_canonical_hostname(options.reverse_mapping_check), 
+			    "ssh");
 #endif /* WITH_AIXAUTHENTICATE */
 			packet_disconnect(AUTH_FAIL_MSG, authctxt->user);
 		}
@@ -433,7 +435,9 @@
 
 #ifdef WITH_AIXAUTHENTICATE
 	/* We don't have a pty yet, so just label the line as "ssh" */
-	if (loginsuccess(authctxt->user,get_canonical_hostname(),"ssh",&aixloginmsg) < 0)
+	if (loginsuccess(authctxt->user, 
+	    get_canonical_hostname(options.reverse_mapping_check),
+	    "ssh", &aixloginmsg) < 0)
 		aixloginmsg = NULL;
 #endif /* WITH_AIXAUTHENTICATE */
 
diff --git a/auth2.c b/auth2.c
index cff34c6..5f8b423 100644
--- a/auth2.c
+++ b/auth2.c
@@ -310,7 +310,8 @@
 #ifdef WITH_AIXAUTHENTICATE
 		/* We don't have a pty yet, so just label the line as "ssh" */
 		if (loginsuccess(authctxt->user?authctxt->user:"NOUSER", 
-			get_canonical_hostname(), "ssh", &aixloginmsg) < 0)
+		    get_canonical_hostname(options.reverse_mapping_check), 
+		    "ssh", &aixloginmsg) < 0)
 			aixloginmsg = NULL;
 #endif /* WITH_AIXAUTHENTICATE */
 		/* turn off userauth */
@@ -354,8 +355,9 @@
 	return auth_pam_password(authctxt->pw, "");
 #elif defined(HAVE_OSF_SIA)
 	return (sia_validate_user(NULL, saved_argc, saved_argv, 
-		get_canonical_hostname(), authctxt->user?authctxt->user:"NOUSER", 
-			NULL, 0, NULL, "") == SIASUCCESS);
+	    get_canonical_hostname(options.reverse_mapping_check), 
+	    authctxt->user?authctxt->user:"NOUSER", NULL, 0, 
+	    NULL, "") == SIASUCCESS);
 #else /* !HAVE_OSF_SIA && !USE_PAM */
 	return auth_password(authctxt->pw, "");
 #endif /* USE_PAM */
@@ -381,8 +383,9 @@
 	    auth_pam_password(authctxt->pw, password) == 1)
 #elif defined(HAVE_OSF_SIA)
 	    sia_validate_user(NULL, saved_argc, saved_argv, 
-		 	get_canonical_hostname(), authctxt->user?authctxt->user:"NOUSER", 
-			NULL, 0, NULL, password) == SIASUCCESS)
+	    get_canonical_hostname(options.reverse_mapping_check), 
+	    authctxt->user?authctxt->user:"NOUSER", NULL, 0, NULL, 
+	    password) == SIASUCCESS)
 #else /* !USE_PAM && !HAVE_OSF_SIA */
 	    auth_password(authctxt->pw, password) == 1)
 #endif /* USE_PAM */
diff --git a/canohost.c b/canohost.c
index f3a6593..8253e9b 100644
--- a/canohost.c
+++ b/canohost.c
@@ -12,35 +12,35 @@
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: canohost.c,v 1.19 2001/01/29 19:42:33 markus Exp $");
+RCSID("$OpenBSD: canohost.c,v 1.20 2001/02/03 10:08:37 markus Exp $");
 
 #include "packet.h"
 #include "xmalloc.h"
 #include "log.h"
 
+void	check_ip_options(int socket, char *ipaddr);
+
 /*
  * Return the canonical name of the host at the other end of the socket. The
  * caller should free the returned string with xfree.
  */
 
 char *
-get_remote_hostname(int socket)
+get_remote_hostname(int socket, int reverse_mapping_check)
 {
 	struct sockaddr_storage from;
 	int i;
 	socklen_t fromlen;
 	struct addrinfo hints, *ai, *aitop;
-	char name[MAXHOSTNAMELEN];
-	char ntop[NI_MAXHOST], ntop2[NI_MAXHOST];
+	char name[NI_MAXHOST], ntop[NI_MAXHOST], ntop2[NI_MAXHOST];
 
 	/* Get IP address of client. */
 	fromlen = sizeof(from);
 	memset(&from, 0, sizeof(from));
-	if (getpeername(socket, (struct sockaddr *) & from, &fromlen) < 0) {
+	if (getpeername(socket, (struct sockaddr *) &from, &fromlen) < 0) {
 		debug("getpeername failed: %.100s", strerror(errno));
 		fatal_cleanup();
 	}
-
 #ifdef IPV4_IN_IPV6
 	if (from.ss_family == AF_INET6) {
 		struct sockaddr_in6 *from6 = (struct sockaddr_in6 *)&from;
@@ -63,6 +63,8 @@
 		}
 	}
 #endif
+	if (from.ss_family == AF_INET)
+		check_ip_options(socket, ntop);
 
 	if (getnameinfo((struct sockaddr *)&from, fromlen, ntop, sizeof(ntop),
 	     NULL, 0, NI_NUMERICHOST) != 0)
@@ -70,120 +72,127 @@
 
 	/* Map the IP address to a host name. */
 	if (getnameinfo((struct sockaddr *)&from, fromlen, name, sizeof(name),
-	     NULL, 0, NI_NAMEREQD) == 0) {
-		/* Got host name. */
-		name[sizeof(name) - 1] = '\0';
-		/*
-		 * Convert it to all lowercase (which is expected by the rest
-		 * of this software).
-		 */
-		for (i = 0; name[i]; i++)
-			if (isupper(name[i]))
-				name[i] = tolower(name[i]);
-
-		/*
-		 * Map it back to an IP address and check that the given
-		 * address actually is an address of this host.  This is
-		 * necessary because anyone with access to a name server can
-		 * define arbitrary names for an IP address. Mapping from
-		 * name to IP address can be trusted better (but can still be
-		 * fooled if the intruder has access to the name server of
-		 * the domain).
-		 */
-		memset(&hints, 0, sizeof(hints));
-		hints.ai_family = from.ss_family;
-		hints.ai_socktype = SOCK_STREAM;
-		if (getaddrinfo(name, NULL, &hints, &aitop) != 0) {
-			log("reverse mapping checking getaddrinfo for %.700s failed - POSSIBLE BREAKIN ATTEMPT!", name);
-			strlcpy(name, ntop, sizeof name);
-			goto check_ip_options;
-		}
-		/* Look for the address from the list of addresses. */
-		for (ai = aitop; ai; ai = ai->ai_next) {
-			if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop2,
-			    sizeof(ntop2), NULL, 0, NI_NUMERICHOST) == 0 &&
-			    (strcmp(ntop, ntop2) == 0))
-					break;
-		}
-		freeaddrinfo(aitop);
-		/* If we reached the end of the list, the address was not there. */
-		if (!ai) {
-			/* Address not found for the host name. */
-			log("Address %.100s maps to %.600s, but this does not map back to the address - POSSIBLE BREAKIN ATTEMPT!",
-			    ntop, name);
-			strlcpy(name, ntop, sizeof name);
-			goto check_ip_options;
-		}
-		/* Address was found for the host name.  We accept the host name. */
-	} else {
-		/* Host name not found.  Use ascii representation of the address. */
-		strlcpy(name, ntop, sizeof name);
-		log("Could not reverse map address %.100s.", name);
+	     NULL, 0, NI_NAMEREQD) != 0) {
+		/* Host name not found.  Use ip address. */
+		log("Could not reverse map address %.100s.", ntop);
+		return xstrdup(ntop);
 	}
 
-check_ip_options:
-
+	/* Got host name. */
+	name[sizeof(name) - 1] = '\0';
 	/*
-	 * If IP options are supported, make sure there are none (log and
-	 * disconnect them if any are found).  Basically we are worried about
-	 * source routing; it can be used to pretend you are somebody
-	 * (ip-address) you are not. That itself may be "almost acceptable"
-	 * under certain circumstances, but rhosts autentication is useless
-	 * if source routing is accepted. Notice also that if we just dropped
-	 * source routing here, the other side could use IP spoofing to do
-	 * rest of the interaction and could still bypass security.  So we
-	 * exit here if we detect any IP options.
+	 * Convert it to all lowercase (which is expected by the rest
+	 * of this software).
 	 */
-	/* IP options -- IPv4 only */
-	if (from.ss_family == AF_INET) {
-		u_char options[200], *ucp;
-		char text[1024], *cp;
-		socklen_t option_size;
-		int ipproto;
-		struct protoent *ip;
+	for (i = 0; name[i]; i++)
+		if (isupper(name[i]))
+			name[i] = tolower(name[i]);
 
-		if ((ip = getprotobyname("ip")) != NULL)
-			ipproto = ip->p_proto;
-		else
-			ipproto = IPPROTO_IP;
-		option_size = sizeof(options);
-		if (getsockopt(socket, ipproto, IP_OPTIONS, (char *) options,
-		    &option_size) >= 0 && option_size != 0) {
-			cp = text;
-			/* Note: "text" buffer must be at least 3x as big as options. */
-			for (ucp = options; option_size > 0; ucp++, option_size--, cp += 3)
-				sprintf(cp, " %2.2x", *ucp);
-			log("Connection from %.100s with IP options:%.800s",
-			    ntop, text);
-			packet_disconnect("Connection from %.100s with IP options:%.800s",
-					  ntop, text);
-		}
+	if (!reverse_mapping_check)
+		return xstrdup(name);
+	/*
+	 * Map it back to an IP address and check that the given
+	 * address actually is an address of this host.  This is
+	 * necessary because anyone with access to a name server can
+	 * define arbitrary names for an IP address. Mapping from
+	 * name to IP address can be trusted better (but can still be
+	 * fooled if the intruder has access to the name server of
+	 * the domain).
+	 */
+	memset(&hints, 0, sizeof(hints));
+	hints.ai_family = from.ss_family;
+	hints.ai_socktype = SOCK_STREAM;
+	if (getaddrinfo(name, NULL, &hints, &aitop) != 0) {
+		log("reverse mapping checking getaddrinfo for %.700s "
+		    "failed - POSSIBLE BREAKIN ATTEMPT!", name);
+		return xstrdup(ntop);
 	}
-
+	/* Look for the address from the list of addresses. */
+	for (ai = aitop; ai; ai = ai->ai_next) {
+		if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop2,
+		    sizeof(ntop2), NULL, 0, NI_NUMERICHOST) == 0 &&
+		    (strcmp(ntop, ntop2) == 0))
+				break;
+	}
+	freeaddrinfo(aitop);
+	/* If we reached the end of the list, the address was not there. */
+	if (!ai) {
+		/* Address not found for the host name. */
+		log("Address %.100s maps to %.600s, but this does not "
+		    "map back to the address - POSSIBLE BREAKIN ATTEMPT!",
+		    ntop, name);
+		return xstrdup(ntop);
+	}
 	return xstrdup(name);
 }
 
 /*
+ * If IP options are supported, make sure there are none (log and
+ * disconnect them if any are found).  Basically we are worried about
+ * source routing; it can be used to pretend you are somebody
+ * (ip-address) you are not. That itself may be "almost acceptable"
+ * under certain circumstances, but rhosts autentication is useless
+ * if source routing is accepted. Notice also that if we just dropped
+ * source routing here, the other side could use IP spoofing to do
+ * rest of the interaction and could still bypass security.  So we
+ * exit here if we detect any IP options.
+ */
+/* IPv4 only */
+void
+check_ip_options(int socket, char *ipaddr)
+{
+	u_char options[200], *ucp;
+	char text[1024], *cp;
+	socklen_t option_size;
+	int ipproto;
+	struct protoent *ip;
+
+	if ((ip = getprotobyname("ip")) != NULL)
+		ipproto = ip->p_proto;
+	else
+		ipproto = IPPROTO_IP;
+	option_size = sizeof(options);
+	if (getsockopt(socket, ipproto, IP_OPTIONS, (void *)options,
+	    &option_size) >= 0 && option_size != 0) {
+		cp = text;
+		/* Note: "text" buffer must be at least 3x as big as options. */
+		for (ucp = options; option_size > 0; ucp++, option_size--, cp += 3)
+			sprintf(cp, " %2.2x", *ucp);
+		log("Connection from %.100s with IP options:%.800s",
+		    ipaddr, text);
+		packet_disconnect("Connection from %.100s with IP options:%.800s",
+		    ipaddr, text);
+	}
+}
+
+/*
  * Return the canonical name of the host in the other side of the current
  * connection.  The host name is cached, so it is efficient to call this
  * several times.
  */
 
 const char *
-get_canonical_hostname()
+get_canonical_hostname(int reverse_mapping_check)
 {
 	static char *canonical_host_name = NULL;
+	static int reverse_mapping_checked = 0;
 
-	/* Check if we have previously retrieved this same name. */
-	if (canonical_host_name != NULL)
-		return canonical_host_name;
+	/* Check if we have previously retrieved name with same option. */
+	if (canonical_host_name != NULL) {
+		if (reverse_mapping_checked != reverse_mapping_check)
+			xfree(canonical_host_name);
+		else
+			return canonical_host_name;
+	}
 
 	/* Get the real hostname if socket; otherwise return UNKNOWN. */
 	if (packet_connection_is_on_socket())
-		canonical_host_name = get_remote_hostname(packet_get_connection_in());
+		canonical_host_name = get_remote_hostname(
+		    packet_get_connection_in(), reverse_mapping_check);
 	else
 		canonical_host_name = xstrdup("UNKNOWN");
 
+	reverse_mapping_checked = reverse_mapping_check;
 	return canonical_host_name;
 }
 
diff --git a/canohost.h b/canohost.h
index 982ec59..da60b3a 100644
--- a/canohost.h
+++ b/canohost.h
@@ -1,4 +1,4 @@
-/*	$OpenBSD: canohost.h,v 1.3 2001/01/29 19:42:35 markus Exp $	*/
+/*	$OpenBSD: canohost.h,v 1.4 2001/02/03 10:08:37 markus Exp $	*/
 
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -11,22 +11,17 @@
  * incompatible with the protocol description in the RFC file, it must be
  * called by a name other than "ssh" or "Secure Shell".
  */
-/*
- * Returns the name of the machine at the other end of the socket.  The
- * returned string should be freed by the caller.
- */
-char   *get_remote_hostname(int socket);
 
 /*
  * Return the canonical name of the host in the other side of the current
  * connection (as returned by packet_get_connection).  The host name is
  * cached, so it is efficient to call this several times.
  */
-const char *get_canonical_hostname(void);
+const char *get_canonical_hostname(int reverse_mapping_check);
 
 /*
  * Returns the IP-address of the remote host as a string.  The returned
- * string must not be freed.
+ * string is cached and must not be freed.
  */
 const char *get_remote_ipaddr(void);
 
diff --git a/channels.c b/channels.c
index 82a2db0..d343ac8 100644
--- a/channels.c
+++ b/channels.c
@@ -40,7 +40,7 @@
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: channels.c,v 1.85 2001/01/29 19:42:35 markus Exp $");
+RCSID("$OpenBSD: channels.c,v 1.88 2001/02/01 21:58:08 markus Exp $");
 
 #include <openssl/rsa.h>
 #include <openssl/dsa.h>
@@ -600,7 +600,7 @@
 	struct sockaddr addr;
 	int newsock, newch;
 	socklen_t addrlen;
-	char buf[1024], *remote_hostname, *rtype;
+	char buf[1024], *remote_ipaddr, *rtype;
 	int remote_port;
 
 	rtype = (c->type == SSH_CHANNEL_RPORT_LISTENER) ?
@@ -616,13 +616,13 @@
 			error("accept: %.100s", strerror(errno));
 			return;
 		}
-		remote_hostname = get_remote_hostname(newsock);
+		remote_ipaddr = get_peer_ipaddr(newsock);
 		remote_port = get_peer_port(newsock);
 		snprintf(buf, sizeof buf,
 		    "listen port %d for %.100s port %d, "
 		    "connect from %.200s port %d",
 		    c->listening_port, c->path, c->host_port,
-		    remote_hostname, remote_port);
+		    remote_ipaddr, remote_port);
 
 		newch = channel_new(rtype,
 		    SSH_CHANNEL_OPENING, newsock, newsock, -1,
@@ -644,7 +644,7 @@
 				packet_put_int(c->host_port);
 			}
 			/* originator host and port */
-			packet_put_cstring(remote_hostname);
+			packet_put_cstring(remote_ipaddr);
 			packet_put_int(remote_port);
 			packet_send();
 		} else {
@@ -657,7 +657,7 @@
 			}
 			packet_send();
 		}
-		xfree(remote_hostname);
+		xfree(remote_ipaddr);
 	}
 }
 
diff --git a/contrib/caldera/openssh.spec b/contrib/caldera/openssh.spec
index 557770d..6bba4d3 100644
--- a/contrib/caldera/openssh.spec
+++ b/contrib/caldera/openssh.spec
@@ -253,9 +253,13 @@
 %attr(4755,root,root) %{_bindir}/ssh
 %attr(0755,root,root) %{_bindir}/ssh-agent
 %attr(0755,root,root) %{_bindir}/ssh-add
+%attr(0755,root,root) %{_bindir}/ssh-keyscan
+%attr(0755,root,root) %{_bindir}/sftp
 %attr(0644,root,root) %{_mandir}/man1/ssh.1*
 %attr(0644,root,root) %{_mandir}/man1/ssh-agent.1*
 %attr(0644,root,root) %{_mandir}/man1/ssh-add.1*
+%attr(0644,root,root) %{_mandir}/man1/ssh-keyscan.1*
+%attr(0644,root,root) %{_mandir}/man1/sftp.1*
 %attr(0644,root,root) %config(noreplace) %{_sysconfdir}/ssh_config
 %attr(-,root,root) %{_bindir}/slogin
 %attr(-,root,root) %{_mandir}/man1/slogin.1*
diff --git a/contrib/redhat/openssh.spec b/contrib/redhat/openssh.spec
index 482306f..c6574a2 100644
--- a/contrib/redhat/openssh.spec
+++ b/contrib/redhat/openssh.spec
@@ -223,9 +223,13 @@
 %attr(4755,root,root) %{_bindir}/ssh
 %attr(0755,root,root) %{_bindir}/ssh-agent
 %attr(0755,root,root) %{_bindir}/ssh-add
+%attr(0755,root,root) %{_bindir}/ssh-keyscan
+%attr(0755,root,root) %{_bindir}/sftp
 %attr(0644,root,root) %{_mandir}/man1/ssh.1*
 %attr(0644,root,root) %{_mandir}/man1/ssh-agent.1*
 %attr(0644,root,root) %{_mandir}/man1/ssh-add.1*
+%attr(0644,root,root) %{_mandir}/man1/ssh-keyscan.1*
+%attr(0644,root,root) %{_mandir}/man1/sftp.1*
 %attr(0644,root,root) %config(noreplace) %{_sysconfdir}/ssh_config
 %attr(-,root,root) %{_bindir}/slogin
 %attr(-,root,root) %{_mandir}/man1/slogin.1*
diff --git a/contrib/suse/openssh.spec b/contrib/suse/openssh.spec
index b4cc2d9..01ff204 100644
--- a/contrib/suse/openssh.spec
+++ b/contrib/suse/openssh.spec
@@ -180,6 +180,8 @@
 %attr(-,root,root) /usr/bin/slogin
 %attr(0755,root,root) /usr/bin/ssh-agent
 %attr(0755,root,root) /usr/bin/ssh-add
+%attr(0755,root,root) /usr/bin/ssh-keyscan
+%attr(0755,root,root) /usr/bin/sftp
 %attr(0755,root,root) /usr/sbin/sshd
 %attr(-,root,root) /usr/sbin/rcsshd
 %attr(0755,root,root) %dir /usr/lib/ssh
diff --git a/scp.1 b/scp.1
index 0a2ca1a..10e67aa 100644
--- a/scp.1
+++ b/scp.1
@@ -9,7 +9,7 @@
 .\"
 .\" Created: Sun May  7 00:14:37 1995 ylo
 .\"
-.\" $OpenBSD: scp.1,v 1.13 2000/10/16 09:38:44 djm Exp $
+.\" $OpenBSD: scp.1,v 1.14 2001/02/04 11:11:53 djm Exp $
 .\"
 .Dd September 25, 1999
 .Dt SCP 1
@@ -129,6 +129,7 @@
 California.
 .Sh SEE ALSO
 .Xr rcp 1 ,
+.Xr sftp 1 ,
 .Xr ssh 1 ,
 .Xr ssh-add 1 ,
 .Xr ssh-agent 1 ,
diff --git a/servconf.c b/servconf.c
index 9f292b6..5fa41e0 100644
--- a/servconf.c
+++ b/servconf.c
@@ -10,7 +10,7 @@
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: servconf.c,v 1.63 2001/01/22 23:06:39 markus Exp $");
+RCSID("$OpenBSD: servconf.c,v 1.64 2001/02/03 10:08:37 markus Exp $");
 
 #ifdef KRB4
 #include <krb.h>
@@ -92,6 +92,7 @@
 	options->max_startups_rate = -1;
 	options->max_startups = -1;
 	options->banner = NULL;
+	options->reverse_mapping_check = -1;
 }
 
 void
@@ -186,6 +187,8 @@
 		options->max_startups_rate = 100;		/* 100% */
 	if (options->max_startups_begin == -1)
 		options->max_startups_begin = options->max_startups;
+	if (options->reverse_mapping_check == -1)
+		options->reverse_mapping_check = 0;
 }
 
 /* Keyword tokens. */
@@ -208,7 +211,7 @@
 	sAllowUsers, sDenyUsers, sAllowGroups, sDenyGroups,
 	sIgnoreUserKnownHosts, sCiphers, sProtocol, sPidFile,
 	sGatewayPorts, sPubkeyAuthentication, sXAuthLocation, sSubsystem, sMaxStartups,
-	sBanner
+	sBanner, sReverseMappingCheck
 } ServerOpCodes;
 
 /* Textual representation of the tokens. */
@@ -268,6 +271,7 @@
 	{ "subsystem", sSubsystem },
 	{ "maxstartups", sMaxStartups },
 	{ "banner", sBanner },
+	{ "reversemappingcheck", sReverseMappingCheck },
 	{ NULL, 0 }
 };
 
@@ -577,6 +581,10 @@
 			intptr = &options->gateway_ports;
 			goto parse_flag;
 
+		case sReverseMappingCheck:
+			intptr = &options->reverse_mapping_check;
+			goto parse_flag;
+
 		case sLogFacility:
 			intptr = (int *) &options->log_facility;
 			arg = strdelim(&cp);
diff --git a/servconf.h b/servconf.h
index e316367..e7abb94 100644
--- a/servconf.h
+++ b/servconf.h
@@ -11,7 +11,7 @@
  * called by a name other than "ssh" or "Secure Shell".
  */
 
-/* RCSID("$OpenBSD: servconf.h,v 1.35 2001/01/22 23:06:40 markus Exp $"); */
+/* RCSID("$OpenBSD: servconf.h,v 1.36 2001/02/03 10:08:37 markus Exp $"); */
 
 #ifndef SERVCONF_H
 #define SERVCONF_H
@@ -102,6 +102,7 @@
 	int	max_startups_rate;
 	int	max_startups;
 	char   *banner;			/* SSH-2 banner message */
+	int	reverse_mapping_check;	/* cross-check ip and dns */
 
 }       ServerOptions;
 /*
diff --git a/session.c b/session.c
index b6ab887..51b661a 100644
--- a/session.c
+++ b/session.c
@@ -33,7 +33,7 @@
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: session.c,v 1.51 2001/01/21 19:05:56 markus Exp $");
+RCSID("$OpenBSD: session.c,v 1.52 2001/02/03 10:08:37 markus Exp $");
 
 #include "ssh.h"
 #include "ssh1.h"
@@ -683,7 +683,7 @@
 {
 	static const char *remote = "";
 	if (utmp_len > 0)
-		remote = get_canonical_hostname();
+		remote = get_canonical_hostname(options.reverse_mapping_check);
 	if (utmp_len == 0 || strlen(remote) > utmp_len)
 		remote = get_remote_ipaddr();
 	return remote;
@@ -1061,7 +1061,7 @@
 #ifdef HAVE_OSF_SIA
 		extern char **saved_argv;
 		extern int saved_argc;
-		char *host = get_canonical_hostname ();
+		char *host = get_canonical_hostname(options.reverse_mapping_check);
 
 		if (sia_become_user(NULL, saved_argc, saved_argv, host,
 		    pw->pw_name, ttyname, 0, NULL, NULL, SIA_BEU_SETLUID) !=
diff --git a/sftp-client.c b/sftp-client.c
new file mode 100644
index 0000000..458d736
--- /dev/null
+++ b/sftp-client.c
@@ -0,0 +1,792 @@
+/*
+ * Copyright (c) 2001 Damien Miller.  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.
+ */
+
+/* XXX: memleaks */
+/* XXX: signed vs unsigned */
+/* XXX: redesign to allow concurrent overlapped operations */
+/* XXX: we use fatal too much, error may be more appropriate in places */
+/* XXX: copy between two remote sites */
+
+#include "includes.h"
+RCSID("$OpenBSD: sftp-client.c,v 1.1 2001/02/04 11:11:54 djm Exp $");
+
+#include "ssh.h"
+#include "buffer.h"
+#include "bufaux.h"
+#include "getput.h"
+#include "xmalloc.h"
+#include "log.h"
+#include "atomicio.h"
+#include "pathnames.h"
+
+#include "sftp.h"
+#include "sftp-common.h"
+#include "sftp-client.h"
+
+/* How much data to read/write at at time during copies */
+/* XXX: what should this be? */
+#define COPY_SIZE	8192
+
+void
+send_msg(int fd, Buffer *m)
+{
+	int mlen = buffer_len(m);
+	int len;
+	Buffer oqueue;
+
+	buffer_init(&oqueue);
+	buffer_put_int(&oqueue, mlen);
+	buffer_append(&oqueue, buffer_ptr(m), mlen);
+	buffer_consume(m, mlen);
+
+	len = atomicio(write, fd, buffer_ptr(&oqueue), buffer_len(&oqueue));
+	if (len <= 0)
+		fatal("Couldn't send packet: %s", strerror(errno));
+
+	buffer_free(&oqueue);
+}
+
+void
+get_msg(int fd, Buffer *m)
+{
+	u_int len, msg_len;
+	unsigned char buf[4096];
+
+	len = atomicio(read, fd, buf, 4);
+	if (len != 4)
+		fatal("Couldn't read packet: %s", strerror(errno));
+
+	msg_len = GET_32BIT(buf);
+	if (msg_len > 256 * 1024)
+		fatal("Received message too long %d", msg_len);
+
+	while (msg_len) {
+		len = atomicio(read, fd, buf, MIN(msg_len, sizeof(buf)));
+		if (len <= 0)
+			fatal("Couldn't read packet: %s", strerror(errno));
+
+		msg_len -= len;
+		buffer_append(m, buf, len);
+	}
+}
+
+void
+send_string_request(int fd, u_int id, u_int code, char *s,
+    u_int len)
+{
+	Buffer msg;
+
+	buffer_init(&msg);
+	buffer_put_char(&msg, code);
+	buffer_put_int(&msg, id);
+	buffer_put_string(&msg, s, len);
+	send_msg(fd, &msg);
+	debug3("Sent message fd %d T:%d I:%d", fd, code, id);
+	buffer_free(&msg);
+}
+
+void
+send_string_attrs_request(int fd, u_int id, u_int code, char *s,
+    u_int len, Attrib *a)
+{
+	Buffer msg;
+
+	buffer_init(&msg);
+	buffer_put_char(&msg, code);
+	buffer_put_int(&msg, id);
+	buffer_put_string(&msg, s, len);
+	encode_attrib(&msg, a);
+	send_msg(fd, &msg);
+	debug3("Sent message fd %d T:%d I:%d", fd, code, id);
+	buffer_free(&msg);
+}
+
+u_int
+get_status(int fd, int expected_id)
+{
+	Buffer msg;
+	u_int type, id, status;
+
+	buffer_init(&msg);
+	get_msg(fd, &msg);
+	type = buffer_get_char(&msg);
+	id = buffer_get_int(&msg);
+
+	if (id != expected_id)
+		fatal("ID mismatch (%d != %d)", id, expected_id);
+	if (type != SSH2_FXP_STATUS)
+		fatal("Expected SSH2_FXP_STATUS(%d) packet, got %d",
+		    SSH2_FXP_STATUS, type);
+
+	status = buffer_get_int(&msg);
+	buffer_free(&msg);
+
+	debug3("SSH2_FXP_STATUS %d", status);
+
+	return(status);
+}
+
+char *
+get_handle(int fd, u_int expected_id, u_int *len)
+{
+	Buffer msg;
+	u_int type, id;
+	char *handle;
+
+	buffer_init(&msg);
+	get_msg(fd, &msg);
+	type = buffer_get_char(&msg);
+	id = buffer_get_int(&msg);
+
+	if (id != expected_id)
+		fatal("ID mismatch (%d != %d)", id, expected_id);
+	if (type == SSH2_FXP_STATUS) {
+		int status = buffer_get_int(&msg);
+
+		error("Couldn't get handle: %s", fx2txt(status));
+		return(NULL);
+	} else if (type != SSH2_FXP_HANDLE)
+		fatal("Expected SSH2_FXP_HANDLE(%d) packet, got %d",
+		    SSH2_FXP_HANDLE, type);
+
+	handle = buffer_get_string(&msg, len);
+	buffer_free(&msg);
+
+	return(handle);
+}
+
+Attrib *
+get_decode_stat(int fd, u_int expected_id)
+{
+	Buffer msg;
+	u_int type, id;
+	Attrib *a;
+
+	buffer_init(&msg);
+	get_msg(fd, &msg);
+
+	type = buffer_get_char(&msg);
+	id = buffer_get_int(&msg);
+
+	debug3("Received stat reply T:%d I:%d", type, id);
+	if (id != expected_id)
+		fatal("ID mismatch (%d != %d)", id, expected_id);
+	if (type == SSH2_FXP_STATUS) {
+		int status = buffer_get_int(&msg);
+
+		error("Couldn't stat remote file: %s", fx2txt(status));
+		return(NULL);
+	} else if (type != SSH2_FXP_ATTRS) {
+		fatal("Expected SSH2_FXP_ATTRS(%d) packet, got %d",
+		    SSH2_FXP_ATTRS, type);
+	}
+	a = decode_attrib(&msg);
+	buffer_free(&msg);
+
+	return(a);
+}
+
+int
+do_init(int fd_in, int fd_out)
+{
+	int type, version;
+	Buffer msg;
+
+	buffer_init(&msg);
+	buffer_put_char(&msg, SSH2_FXP_INIT);
+	buffer_put_int(&msg, SSH2_FILEXFER_VERSION);
+	send_msg(fd_out, &msg);
+
+	buffer_clear(&msg);
+
+	get_msg(fd_in, &msg);
+
+ 	/* Expecting a VERSION reply */
+	if ((type = buffer_get_char(&msg)) != SSH2_FXP_VERSION) {
+		error("Invalid packet back from SSH2_FXP_INIT (type %d)",
+		    type);
+		buffer_free(&msg);
+		return(-1);
+	}
+	version = buffer_get_int(&msg);
+
+	debug2("Remote version: %d", version);
+
+	/* Check for extensions */
+	while (buffer_len(&msg) > 0) {
+		char *name = buffer_get_string(&msg, NULL);
+		char *value = buffer_get_string(&msg, NULL);
+
+		debug2("Init extension: \"%s\"", name);
+		xfree(name);
+		xfree(value);
+	}
+
+	buffer_free(&msg);
+	return(0);
+}
+
+int
+do_close(int fd_in, int fd_out, char *handle, u_int handle_len)
+{
+	u_int id, status;
+	Buffer msg;
+
+	buffer_init(&msg);
+
+	id = arc4random();
+	buffer_put_char(&msg, SSH2_FXP_CLOSE);
+	buffer_put_int(&msg, id);
+	buffer_put_string(&msg, handle, handle_len);
+	send_msg(fd_out, &msg);
+	debug3("Sent message SSH2_FXP_CLOSE I:%d", id);
+
+	status = get_status(fd_in, id);
+	if (status != SSH2_FX_OK)
+		error("Couldn't close file: %s", fx2txt(status));
+
+	buffer_free(&msg);
+
+	return(status);
+}
+
+int
+do_ls(int fd_in, int fd_out, char *path)
+{
+	Buffer msg;
+	u_int type, id, handle_len, i, expected_id;
+	char *handle;
+
+	id = arc4random();
+
+	buffer_init(&msg);
+	buffer_put_char(&msg, SSH2_FXP_OPENDIR);
+	buffer_put_int(&msg, id);
+	buffer_put_cstring(&msg, path);
+	send_msg(fd_out, &msg);
+
+	buffer_clear(&msg);
+
+	handle = get_handle(fd_in, id, &handle_len);
+	if (handle == NULL)
+		return(-1);
+
+	for(;;) {
+		int count;
+
+		expected_id = ++id;
+
+		debug3("Sending SSH2_FXP_READDIR I:%d", id);
+
+		buffer_clear(&msg);
+		buffer_put_char(&msg, SSH2_FXP_READDIR);
+		buffer_put_int(&msg, id);
+		buffer_put_string(&msg, handle, handle_len);
+		send_msg(fd_out, &msg);
+
+		buffer_clear(&msg);
+
+		get_msg(fd_in, &msg);
+
+		type = buffer_get_char(&msg);
+		id = buffer_get_int(&msg);
+
+		debug3("Received reply T:%d I:%d", type, id);
+
+		if (id != expected_id)
+			fatal("ID mismatch (%d != %d)", id, expected_id);
+
+		if (type == SSH2_FXP_STATUS) {
+			int status = buffer_get_int(&msg);
+
+			debug3("Received SSH2_FXP_STATUS %d", status);
+
+			if (status == SSH2_FX_EOF) {
+				break;
+			} else {
+				error("Couldn't read directory: %s",
+				    fx2txt(status));
+				do_close(fd_in, fd_out, handle, handle_len);
+				return(NULL);
+			}
+		} else if (type != SSH2_FXP_NAME)
+			fatal("Expected SSH2_FXP_NAME(%d) packet, got %d",
+			    SSH2_FXP_NAME, type);
+
+		count = buffer_get_int(&msg);
+		debug3("Received %i SSH2_FXP_NAME responses", count);
+		for(i = 0; i < count; i++) {
+			char *filename, *longname;
+			Attrib *a;
+
+			filename = buffer_get_string(&msg, NULL);
+			longname = buffer_get_string(&msg, NULL);
+			a = decode_attrib(&msg);
+
+			printf("%s\n", longname);
+
+			xfree(filename);
+			xfree(longname);
+		}
+	}
+
+	buffer_free(&msg);
+	do_close(fd_in, fd_out, handle, handle_len);
+	xfree(handle);
+
+	return(0);
+}
+
+int
+do_rm(int fd_in, int fd_out, char *path)
+{
+	u_int status, id;
+
+	debug2("Sending SSH2_FXP_REMOVE \"%s\"", path);
+
+	id = arc4random();
+	send_string_request(fd_out, id, SSH2_FXP_REMOVE, path, strlen(path));
+	status = get_status(fd_in, id);
+	if (status != SSH2_FX_OK)
+		error("Couldn't delete file: %s", fx2txt(status));
+	return(status);
+}
+
+int
+do_mkdir(int fd_in, int fd_out, char *path, Attrib *a)
+{
+	u_int status, id;
+
+	id = arc4random();
+	send_string_attrs_request(fd_out, id, SSH2_FXP_MKDIR, path,
+	    strlen(path), a);
+
+	status = get_status(fd_in, id);
+	if (status != SSH2_FX_OK)
+		error("Couldn't create directory: %s", fx2txt(status));
+
+	return(status);
+}
+
+int
+do_rmdir(int fd_in, int fd_out, char *path)
+{
+	u_int status, id;
+
+	id = arc4random();
+	send_string_request(fd_out, id, SSH2_FXP_RMDIR, path, strlen(path));
+
+	status = get_status(fd_in, id);
+	if (status != SSH2_FX_OK)
+		error("Couldn't remove directory: %s", fx2txt(status));
+
+	return(status);
+}
+
+Attrib *
+do_stat(int fd_in, int fd_out, char *path)
+{
+	u_int id;
+
+	id = arc4random();
+	send_string_request(fd_out, id, SSH2_FXP_STAT, path, strlen(path));
+	return(get_decode_stat(fd_in, id));
+}
+
+Attrib *
+do_lstat(int fd_in, int fd_out, char *path)
+{
+	u_int id;
+
+	id = arc4random();
+	send_string_request(fd_out, id, SSH2_FXP_LSTAT, path, strlen(path));
+	return(get_decode_stat(fd_in, id));
+}
+
+Attrib *
+do_fstat(int fd_in, int fd_out, char *handle,
+    u_int handle_len)
+{
+	u_int id;
+
+	id = arc4random();
+	send_string_request(fd_out, id, SSH2_FXP_FSTAT, handle, handle_len);
+	return(get_decode_stat(fd_in, id));
+}
+
+int
+do_setstat(int fd_in, int fd_out, char *path, Attrib *a)
+{
+	u_int status, id;
+
+	id = arc4random();
+	send_string_attrs_request(fd_out, id, SSH2_FXP_SETSTAT, path,
+	    strlen(path), a);
+
+	status = get_status(fd_in, id);
+	if (status != SSH2_FX_OK)
+		error("Couldn't setstat on \"%s\": %s", path,
+		    fx2txt(status));
+
+	return(status);
+}
+
+int
+do_fsetstat(int fd_in, int fd_out, char *handle, u_int handle_len,
+    Attrib *a)
+{
+	u_int status, id;
+
+	id = arc4random();
+	send_string_attrs_request(fd_out, id, SSH2_FXP_FSETSTAT, handle,
+	    handle_len, a);
+
+	status = get_status(fd_in, id);
+	if (status != SSH2_FX_OK)
+		error("Couldn't fsetstat: %s", fx2txt(status));
+
+	return(status);
+}
+
+char *
+do_realpath(int fd_in, int fd_out, char *path)
+{
+	Buffer msg;
+	u_int type, expected_id, count, id;
+	char *filename, *longname;
+	Attrib *a;
+
+	expected_id = id = arc4random();
+	send_string_request(fd_out, id, SSH2_FXP_REALPATH, path,
+	    strlen(path));
+
+	buffer_init(&msg);
+
+	get_msg(fd_in, &msg);
+	type = buffer_get_char(&msg);
+	id = buffer_get_int(&msg);
+
+	if (id != expected_id)
+		fatal("ID mismatch (%d != %d)", id, expected_id);
+
+	if (type == SSH2_FXP_STATUS) {
+		u_int status = buffer_get_int(&msg);
+
+		error("Couldn't canonicalise: %s", fx2txt(status));
+		return(NULL);
+	} else if (type != SSH2_FXP_NAME)
+		fatal("Expected SSH2_FXP_NAME(%d) packet, got %d",
+		    SSH2_FXP_NAME, type);
+
+	count = buffer_get_int(&msg);
+	if (count != 1)
+		fatal("Got multiple names (%d) from SSH_FXP_REALPATH", count);
+
+	filename = buffer_get_string(&msg, NULL);
+	longname = buffer_get_string(&msg, NULL);
+	a = decode_attrib(&msg);
+
+	debug3("SSH_FXP_REALPATH %s -> %s", path, filename);
+
+	xfree(longname);
+
+	buffer_free(&msg);
+
+	return(filename);
+}
+
+int
+do_rename(int fd_in, int fd_out, char *oldpath, char *newpath)
+{
+	Buffer msg;
+	u_int status, id;
+
+	buffer_init(&msg);
+
+	/* Send rename request */
+	id = arc4random();
+	buffer_put_char(&msg, SSH2_FXP_RENAME);
+	buffer_put_int(&msg, id);
+	buffer_put_cstring(&msg, oldpath);
+	buffer_put_cstring(&msg, newpath);
+	send_msg(fd_out, &msg);
+	debug3("Sent message SSH2_FXP_RENAME \"%s\" -> \"%s\"", oldpath,
+	    newpath);
+	buffer_free(&msg);
+
+	status = get_status(fd_in, id);
+	if (status != SSH2_FX_OK)
+		error("Couldn't rename file \"%s\" to \"%s\": %s", oldpath, newpath,
+		    fx2txt(status));
+
+	return(status);
+}
+
+int
+do_download(int fd_in, int fd_out, char *remote_path, char *local_path,
+    int pflag)
+{
+	int local_fd;
+	u_int expected_id, handle_len, mode, type, id;
+	u_int64_t offset;
+	char *handle;
+	Buffer msg;
+	Attrib junk, *a;
+
+	a = do_stat(fd_in, fd_out, remote_path);
+	if (a == NULL)
+		return(-1);
+
+	/* XXX: should we preserve set[ug]id? */
+	if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)
+		mode = S_IWRITE | (a->perm & 0777);
+	else
+		mode = 0666;
+
+	local_fd = open(local_path, O_WRONLY | O_CREAT | O_TRUNC, mode);
+	if (local_fd == -1) {
+		error("Couldn't open local file \"%s\" for writing: %s",
+		    local_path, strerror(errno));
+		return(errno);
+	}
+
+	/* Override umask and utimes if asked */
+	if (pflag && fchmod(local_fd, mode) == -1)
+		error("Couldn't set mode on \"%s\": %s", local_path,
+		    strerror(errno));
+	if (pflag && (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME)) {
+		struct timeval tv;
+
+		tv.tv_sec = a->atime;
+		tv.tv_usec = a->mtime;
+		if (utimes(local_path, &tv) == -1)
+			error("Can't set times on \"%s\": %s", local_path,
+			    strerror(errno));
+	}
+
+	buffer_init(&msg);
+
+	/* Send open request */
+	id = arc4random();
+	buffer_put_char(&msg, SSH2_FXP_OPEN);
+	buffer_put_int(&msg, id);
+	buffer_put_cstring(&msg, remote_path);
+	buffer_put_int(&msg, SSH2_FXF_READ);
+	attrib_clear(&junk); /* Send empty attributes */
+	encode_attrib(&msg, &junk);
+	send_msg(fd_out, &msg);
+	debug3("Sent message SSH2_FXP_OPEN I:%d P:%s", id, remote_path);
+
+	handle = get_handle(fd_in, id, &handle_len);
+	if (handle == NULL) {
+		buffer_free(&msg);
+		close(local_fd);
+		return(-1);
+	}
+
+	/* Read from remote and write to local */
+	offset = 0;
+	for(;;) {
+		u_int len;
+		char *data;
+
+		expected_id = ++id;
+
+		buffer_clear(&msg);
+		buffer_put_char(&msg, SSH2_FXP_READ);
+		buffer_put_int(&msg, id);
+		buffer_put_string(&msg, handle, handle_len);
+		buffer_put_int64(&msg, offset);
+		buffer_put_int(&msg, COPY_SIZE);
+		send_msg(fd_out, &msg);
+		debug3("Sent message SSH2_FXP_READ I:%d O:%llu S:%u",
+		    id, offset, COPY_SIZE);
+
+		buffer_clear(&msg);
+
+		get_msg(fd_in, &msg);
+		type = buffer_get_char(&msg);
+		id = buffer_get_int(&msg);
+		debug3("Received reply T:%d I:%d", type, id);
+		if (id != expected_id)
+			fatal("ID mismatch (%d != %d)", id, expected_id);
+		if (type == SSH2_FXP_STATUS) {
+			int status = buffer_get_int(&msg);
+
+			if (status == SSH2_FX_EOF)
+				break;
+			else {
+				error("Couldn't read from remote "
+				    "file \"%s\" : %s", remote_path,
+				     fx2txt(status));
+				do_close(fd_in, fd_out, handle, handle_len);
+				xfree(handle);
+				close(local_fd);
+				buffer_free(&msg);
+				return(status);
+			}
+		} else if (type != SSH2_FXP_DATA) {
+			fatal("Expected SSH2_FXP_DATA(%d) packet, got %d",
+			    SSH2_FXP_DATA, type);
+		}
+
+		data = buffer_get_string(&msg, &len);
+		if (len > COPY_SIZE)
+			fatal("Received more data than asked for %d > %d",
+			    len, COPY_SIZE);
+
+		debug3("In read loop, got %d offset %lld", len, offset);
+		if (atomicio(write, local_fd, data, len) != len) {
+			error("Couldn't write to \"%s\": %s", local_path,
+			    strerror(errno));
+			do_close(fd_in, fd_out, handle, handle_len);
+			xfree(handle);
+			close(local_fd);
+			xfree(data);
+			buffer_free(&msg);
+			return(-1);
+		}
+
+		offset += len;
+		xfree(data);
+	}
+	xfree(handle);
+	buffer_free(&msg);
+	close(local_fd);
+
+	return(do_close(fd_in, fd_out, handle, handle_len));
+}
+
+int
+do_upload(int fd_in, int fd_out, char *local_path, char *remote_path,
+    int pflag)
+{
+	int local_fd;
+	u_int handle_len, id;
+	u_int64_t offset;
+	char *handle;
+	Buffer msg;
+	struct stat sb;
+	Attrib a;
+
+	if ((local_fd = open(local_path, O_RDONLY, 0)) == -1) {
+		error("Couldn't open local file \"%s\" for reading: %s",
+		    local_path, strerror(errno));
+		return(-1);
+	}
+	if (fstat(local_fd, &sb) == -1) {
+		error("Couldn't fstat local file \"%s\": %s",
+		    local_path, strerror(errno));
+		close(local_fd);
+		return(-1);
+	}
+	stat_to_attrib(&sb, &a);
+
+	a.flags &= ~SSH2_FILEXFER_ATTR_SIZE;
+	a.flags &= ~SSH2_FILEXFER_ATTR_UIDGID;
+	a.perm &= 0777;
+	if (!pflag)
+		a.flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME;
+
+	buffer_init(&msg);
+
+	/* Send open request */
+	id = arc4random();
+	buffer_put_char(&msg, SSH2_FXP_OPEN);
+	buffer_put_int(&msg, id);
+	buffer_put_cstring(&msg, remote_path);
+	buffer_put_int(&msg, SSH2_FXF_WRITE|SSH2_FXF_CREAT|SSH2_FXF_TRUNC);
+	encode_attrib(&msg, &a);
+	send_msg(fd_out, &msg);
+	debug3("Sent message SSH2_FXP_OPEN I:%d P:%s", id, remote_path);
+
+	buffer_clear(&msg);
+
+	handle = get_handle(fd_in, id, &handle_len);
+	if (handle == NULL) {
+		close(local_fd);
+		buffer_free(&msg);
+		return(-1);
+	}
+
+	/* Override umask and utimes if asked */
+	if (pflag)
+		do_fsetstat(fd_in, fd_out, handle, handle_len, &a);
+
+	/* Read from local and write to remote */
+	offset = 0;
+	for(;;) {
+		int len;
+		char data[COPY_SIZE];
+		u_int status;
+
+		/*
+		 * Can't use atomicio here because it returns 0 on EOF, thus losing
+		 * the last block of the file
+		 */
+		do
+			len = read(local_fd, data, COPY_SIZE);
+		while ((len == -1) && (errno == EINTR || errno == EAGAIN));
+
+		if (len == -1)
+			fatal("Couldn't read from \"%s\": %s", local_path,
+			    strerror(errno));
+		if (len == 0)
+			break;
+
+		buffer_clear(&msg);
+		buffer_put_char(&msg, SSH2_FXP_WRITE);
+		buffer_put_int(&msg, ++id);
+		buffer_put_string(&msg, handle, handle_len);
+		buffer_put_int64(&msg, offset);
+		buffer_put_string(&msg, data, len);
+		send_msg(fd_out, &msg);
+		debug3("Sent message SSH2_FXP_WRITE I:%d O:%llu S:%u",
+		    id, offset, len);
+
+		status = get_status(fd_in, id);
+		if (status != SSH2_FX_OK) {
+			error("Couldn't write to remote file \"%s\": %s",
+			    remote_path, fx2txt(status));
+			do_close(fd_in, fd_out, handle, handle_len);
+			xfree(handle);
+			close(local_fd);
+			return(-1);
+		}
+		debug3("In write loop, got %d offset %lld", len, offset);
+
+		offset += len;
+	}
+	xfree(handle);
+	buffer_free(&msg);
+
+	if (close(local_fd) == -1) {
+		error("Couldn't close local file \"%s\": %s", local_path,
+		    strerror(errno));
+		do_close(fd_in, fd_out, handle, handle_len);
+		return(-1);
+	}
+
+	return(do_close(fd_in, fd_out, handle, handle_len));
+}
diff --git a/sftp-client.h b/sftp-client.h
new file mode 100644
index 0000000..838b46b
--- /dev/null
+++ b/sftp-client.h
@@ -0,0 +1,84 @@
+/* $OpenBSD: sftp-client.h,v 1.1 2001/02/04 11:11:54 djm Exp $ */
+
+/*
+ * Copyright (c) 2001 Damien Miller.  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.
+ */
+
+/* Client side of SSH2 filexfer protocol */
+
+/* Initialiase a SSH filexfer connection */
+int do_init(int fd_in, int fd_out);
+
+/* Close file referred to by 'handle' */
+int do_close(int fd_in, int fd_out, char *handle, u_int handle_len);
+
+/* List contents of directory 'path' to stdout */
+int do_ls(int fd_in, int fd_out, char *path);
+
+/* Delete file 'path' */
+int do_rm(int fd_in, int fd_out, char *path);
+
+/* Create directory 'path' */
+int do_mkdir(int fd_in, int fd_out, char *path, Attrib *a);
+
+/* Remove directory 'path' */
+int do_rmdir(int fd_in, int fd_out, char *path);
+
+/* Get file attributes of 'path' (follows symlinks) */
+Attrib *do_stat(int fd_in, int fd_out, char *path);
+
+/* Get file attributes of 'path' (does not follow symlinks) */
+Attrib *do_lstat(int fd_in, int fd_out, char *path);
+
+/* Get file attributes of open file 'handle' */
+Attrib *do_fstat(int fd_in, int fd_out, char *handle,
+    u_int handle_len);
+
+/* Set file attributes of 'path' */
+int do_setstat(int fd_in, int fd_out, char *path, Attrib *a);
+
+/* Set file attributes of open file 'handle' */
+int do_fsetstat(int fd_in, int fd_out, char *handle,
+    u_int handle_len, Attrib *a);
+
+/* Canonicalise 'path' - caller must free result */
+char *do_realpath(int fd_in, int fd_out, char *path);
+
+/* Rename 'oldpath' to 'newpath' */
+int do_rename(int fd_in, int fd_out, char *oldpath, char *newpath);
+
+/* XXX: add callbacks to do_download/do_upload so we can do progress meter */
+
+/*
+ * Download 'remote_path' to 'local_path'. Preserve permissions and times
+ * if 'pflag' is set
+ */
+int do_download(int fd_in, int fd_out, char *remote_path, char *local_path,
+    int pflag);
+
+/*
+ * Upload 'local_path' to 'remote_path'. Preserve permissions and times
+ * if 'pflag' is set
+ */
+int do_upload(int fd_in, int fd_out, char *local_path, char *remote_path,
+    int pflag);
diff --git a/sftp-common.c b/sftp-common.c
new file mode 100644
index 0000000..aed9b33
--- /dev/null
+++ b/sftp-common.c
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2001 Markus Friedl.  All rights reserved.
+ * Copyright (c) 2001 Damien Miller.  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: sftp-common.c,v 1.1 2001/02/04 11:11:54 djm Exp $");
+
+#include "buffer.h"
+#include "bufaux.h"
+#include "getput.h"
+#include "log.h"
+#include "xmalloc.h"
+
+#include "sftp.h"
+#include "sftp-common.h"
+
+void
+attrib_clear(Attrib *a)
+{
+	a->flags = 0;
+	a->size = 0;
+	a->uid = 0;
+	a->gid = 0;
+	a->perm = 0;
+	a->atime = 0;
+	a->mtime = 0;
+}
+
+void
+stat_to_attrib(struct stat *st, Attrib *a)
+{
+	attrib_clear(a);
+	a->flags = 0;
+	a->flags |= SSH2_FILEXFER_ATTR_SIZE;
+	a->size = st->st_size;
+	a->flags |= SSH2_FILEXFER_ATTR_UIDGID;
+	a->uid = st->st_uid;
+	a->gid = st->st_gid;
+	a->flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
+	a->perm = st->st_mode;
+	a->flags |= SSH2_FILEXFER_ATTR_ACMODTIME;
+	a->atime = st->st_atime;
+	a->mtime = st->st_mtime;
+}
+
+Attrib *
+decode_attrib(Buffer *b)
+{
+	static Attrib a;
+	attrib_clear(&a);
+	a.flags = buffer_get_int(b);
+	if (a.flags & SSH2_FILEXFER_ATTR_SIZE)
+		a.size = buffer_get_int64(b);
+	if (a.flags & SSH2_FILEXFER_ATTR_UIDGID) {
+		a.uid = buffer_get_int(b);
+		a.gid = buffer_get_int(b);
+	}
+	if (a.flags & SSH2_FILEXFER_ATTR_PERMISSIONS)
+		a.perm = buffer_get_int(b);
+	if (a.flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
+		a.atime = buffer_get_int(b);
+		a.mtime = buffer_get_int(b);
+	}
+	/* vendor-specific extensions */
+	if (a.flags & SSH2_FILEXFER_ATTR_EXTENDED) {
+		char *type, *data;
+		int i, count;
+		count = buffer_get_int(b);
+		for (i = 0; i < count; i++) {
+			type = buffer_get_string(b, NULL);
+			data = buffer_get_string(b, NULL);
+			debug3("Got file attribute \"%s\"", type);
+			xfree(type);
+			xfree(data);
+		}
+	}
+	return &a;
+}
+
+void
+encode_attrib(Buffer *b, Attrib *a)
+{
+	buffer_put_int(b, a->flags);
+	if (a->flags & SSH2_FILEXFER_ATTR_SIZE)
+		buffer_put_int64(b, a->size);
+	if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) {
+		buffer_put_int(b, a->uid);
+		buffer_put_int(b, a->gid);
+	}
+	if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)
+		buffer_put_int(b, a->perm);
+	if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
+		buffer_put_int(b, a->atime);
+		buffer_put_int(b, a->mtime);
+	}
+}
+
+const char *
+fx2txt(int status)
+{
+	switch (status) {
+	case SSH2_FX_OK:
+		return("No Error");
+	case SSH2_FX_EOF:
+		return("End of File");
+	case SSH2_FX_NO_SUCH_FILE:
+		return("No Such File");
+	case SSH2_FX_PERMISSION_DENIED:
+		return("Permission Denied");
+	case SSH2_FX_FAILURE:
+		return("Failure");
+	case SSH2_FX_BAD_MESSAGE:
+		return("Bad message");
+	case SSH2_FX_NO_CONNECTION:
+		return("No connection");
+	case SSH2_FX_CONNECTION_LOST:
+		return("Connection lost");
+	case SSH2_FX_OP_UNSUPPORTED:
+		return("Operation unsupported");
+	default:
+		return("Unknown status");
+	};
+	/* NOTREACHED */
+}
+
diff --git a/sftp-common.h b/sftp-common.h
new file mode 100644
index 0000000..6dc1a32
--- /dev/null
+++ b/sftp-common.h
@@ -0,0 +1,55 @@
+/*	$OpenBSD: sftp-common.h,v 1.1 2001/02/04 11:11:54 djm Exp $	*/
+
+/*
+ * Copyright (c) 2001 Markus Friedl.  All rights reserved.
+ * Copyright (c) 2001 Damien Miller.  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.
+ */
+
+typedef struct Attrib Attrib;
+
+/* File attributes */
+struct Attrib {
+	u_int32_t	flags;
+	u_int64_t	size;
+	u_int32_t	uid;
+	u_int32_t	gid;
+	u_int32_t	perm;
+	u_int32_t	atime;
+	u_int32_t	mtime;
+};
+
+/* Clear contents of attributes structure */
+void attrib_clear(Attrib *a);
+
+/* Convert from struct stat to filexfer attribs */
+void stat_to_attrib(struct stat *st, Attrib *a);
+
+/* Decode attributes in buffer */
+Attrib *decode_attrib(Buffer *b);
+
+/* Encode attributes to buffer */
+void encode_attrib(Buffer *b, Attrib *a);
+
+/* Convert from SSH2_FX_ status to text error message */
+const char *fx2txt(int status);
+
diff --git a/sftp-int.c b/sftp-int.c
new file mode 100644
index 0000000..f050c09
--- /dev/null
+++ b/sftp-int.c
@@ -0,0 +1,583 @@
+/*
+ * Copyright (c) 2001 Damien Miller.  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.
+ */
+
+/* XXX: finish implementation of all commands */
+/* XXX: do fnmatch() instead of using raw pathname */
+/* XXX: recursive operations */
+
+#include "includes.h"
+RCSID("$OpenBSD: sftp-int.c,v 1.1 2001/02/04 11:11:54 djm Exp $");
+
+#include "buffer.h"
+#include "xmalloc.h"
+#include "log.h"
+#include "pathnames.h"
+
+#include "sftp.h"
+#include "sftp-common.h"
+#include "sftp-client.h"
+#include "sftp-int.h"
+
+/* Seperators for interactive commands */
+#define WHITESPACE " \t\r\n"
+
+/* Commands for interactive mode */
+#define I_CHDIR		1
+#define I_CHGRP		2
+#define I_CHMOD		3
+#define I_CHOWN		4
+#define I_GET		5
+#define I_HELP		6
+#define I_LCHDIR	7
+#define I_LLS		8
+#define I_LMKDIR	9
+#define I_LPWD		10
+#define I_LS		11
+#define I_LUMASK	12
+#define I_MKDIR		13
+#define I_PUT		14
+#define I_PWD		15
+#define I_QUIT		16
+#define I_RENAME	17
+#define I_RM		18
+#define I_RMDIR		19
+#define I_SHELL		20
+
+struct CMD {
+	const int n;
+	const char *c;
+};
+
+const struct CMD cmds[] = {
+	{ I_CHDIR,	"CD" },
+	{ I_CHDIR,	"CHDIR" },
+	{ I_CHDIR,	"LCD" },
+	{ I_CHGRP,	"CHGRP" },
+	{ I_CHMOD,	"CHMOD" },
+	{ I_CHOWN,	"CHOWN" },
+	{ I_HELP,	"HELP" },
+	{ I_GET,	"GET" },
+	{ I_LCHDIR,	"LCHDIR" },
+	{ I_LLS,	"LLS" },
+	{ I_LMKDIR,	"LMKDIR" },
+	{ I_LPWD,	"LPWD" },
+	{ I_LS,		"LS" },
+	{ I_LUMASK,	"LUMASK" },
+	{ I_MKDIR,	"MKDIR" },
+	{ I_PUT,	"PUT" },
+	{ I_PWD,	"PWD" },
+	{ I_QUIT,	"EXIT" },
+	{ I_QUIT,	"QUIT" },
+	{ I_RENAME,	"RENAME" },
+	{ I_RMDIR,	"RMDIR" },
+	{ I_RM,		"RM" },
+	{ I_SHELL,	"!" },
+	{ -1,		NULL}
+};
+
+void
+help(void)
+{
+	printf("Available commands:\n");
+	printf("CD path                       Change remote directory to 'path'\n");
+	printf("LCD path                      Change local directory to 'path'\n");
+	printf("CHGRP grp path                Change group of file 'path' to 'grp'\n");
+	printf("CHMOD mode path               Change permissions of file 'path' to 'mode'\n");
+	printf("CHOWN own path                Change owner of file 'path' to 'own'\n");
+	printf("HELP                          Display this help text\n");
+	printf("GET remote-path [local-path]  Download file\n");
+	printf("LLS [ls options] [path]       Display local directory listing\n");
+	printf("LMKDIR path                   Create local directory\n");
+	printf("LPWD                          Print local working directory\n");
+	printf("LS [path]                     Display remote directory listing\n");
+	printf("LUMASK umask                  Set local umask to 'umask'\n");
+	printf("MKDIR path                    Create remote directory\n");
+	printf("PUT local-path [remote-path]  Upload file\n");
+	printf("PWD                           Display remote working directory\n");
+	printf("EXIT                          Quit sftp\n");
+	printf("QUIT                          Quit sftp\n");
+	printf("RENAME oldpath newpath        Rename remote file\n");
+	printf("RMDIR path                    Remove remote directory\n");
+	printf("RM path                       Delete remote file\n");
+	printf("!command                      Execute 'command' in local shell\n");
+	printf("!                             Escape to local shell\n");
+}
+
+void
+local_do_shell(const char *args)
+{
+	int ret, status;
+	char *shell;
+	pid_t pid;
+	
+	if (!*args)
+		args = NULL;
+	
+	if ((shell = getenv("SHELL")) == NULL)
+		shell = _PATH_BSHELL;
+
+	if ((pid = fork()) == -1)
+		fatal("Couldn't fork: %s", strerror(errno));
+
+	if (pid == 0) {
+		/* XXX: child has pipe fds to ssh subproc open - issue? */
+		if (args) {
+			debug3("Executing %s -c \"%s\"", shell, args);
+			ret = execl(shell, shell, "-c", args, NULL);
+		} else {
+			debug3("Executing %s", shell);
+			ret = execl(shell, shell, NULL);
+		}
+		fprintf(stderr, "Couldn't execute \"%s\": %s\n", shell, 
+		    strerror(errno));
+		_exit(1);
+	}
+	if (waitpid(pid, &status, 0) == -1)
+		fatal("Couldn't wait for child: %s", strerror(errno));
+	if (!WIFEXITED(status))
+		error("Shell exited abormally");
+	else if (WEXITSTATUS(status))
+		error("Shell exited with status %d", WEXITSTATUS(status));
+}
+
+void 
+local_do_ls(const char *args)
+{
+	if (!args || !*args)
+		local_do_shell("ls");
+	else {
+		char *buf = xmalloc(8 + strlen(args) + 1);
+
+		/* XXX: quoting - rip quoting code from ftp? */
+		sprintf(buf, "/bin/ls %s", args);
+		local_do_shell(buf);
+	}
+}
+
+char *
+make_absolute(char *p, char *pwd)
+{
+	char buf[2048];
+
+	/* Derelativise */
+	if (p && p[0] != '/') {
+		snprintf(buf, sizeof(buf), "%s/%s", pwd, p);
+		xfree(p);
+		p = xstrdup(buf);
+	}
+
+	return(p);
+}
+
+int
+parse_getput_flags(const char **cpp, int *pflag)
+{
+	const char *cp = *cpp;
+
+	/* Check for flags */
+	if (cp[0] == '-' && cp[1] && strchr(WHITESPACE, cp[2])) {
+		switch (*cp) {
+		case 'P':
+			*pflag = 1;
+			break;
+		default:
+			error("Invalid flag -%c", *cp);
+			return(-1);
+		}
+		cp += 2;
+		*cpp = cp + strspn(cp, WHITESPACE);
+	}
+
+	return(0);
+}
+
+int
+get_pathname(const char **cpp, char **path)
+{
+	const char *quot, *cp = *cpp;
+	int i;
+
+	cp += strspn(cp, WHITESPACE);
+	if (!*cp) {
+		*cpp = cp;
+		*path = NULL;
+		return(0);
+	}
+
+	/* Check for quoted filenames */
+	if (*cp == '\"' || *cp == '\'') {
+		quot = cp++;
+		for(i = 0; cp[i] && cp[i] != *quot; i++)
+			;
+		if (!cp[i]) {
+			error("Unterminated quote");
+			*path = NULL;
+			return(-1);
+		}
+		if (i == 0) {
+			error("Empty quotes");
+			*path = NULL;
+			return(-1);
+		}
+		*path = xmalloc(i + 1);
+		memcpy(*path, cp, i);
+		(*path)[i] = '\0';
+		cp += i + 1;
+		*cpp = cp + strspn(cp, WHITESPACE);
+		return(0);
+	}
+
+	/* Read to end of filename */
+	for(i = 0; cp[i] && cp[i] != ' '; i++)
+		;
+
+	*path = xmalloc(i + 1);
+	memcpy(*path, cp, i);
+	(*path)[i] = '\0';
+	cp += i;
+	*cpp = cp + strspn(cp, WHITESPACE);
+
+	return(0);
+}
+
+int
+infer_path(const char *p, char **ifp)
+{
+	char *cp;
+
+	debug("XXX: P = \"%s\"", p);
+
+	cp = strrchr(p, '/');
+
+	if (cp == NULL) {
+		*ifp = xstrdup(p);
+		return(0);
+	}
+
+	if (!cp[1]) {
+		error("Invalid path");
+		return(-1);
+	}
+
+	*ifp = xstrdup(cp + 1);
+	return(0);
+}
+
+int
+parse_args(const char **cpp, int *pflag, unsigned long *n_arg,
+    char **path1, char **path2)
+{
+	const char *cmd, *cp = *cpp;
+	int i, cmdnum;
+
+	/* Skip leading whitespace */
+	cp = cp + strspn(cp, WHITESPACE);
+
+	/* Ignore blank lines */
+	if (!*cp)
+		return(-1);
+
+	/* Figure out which command we have */
+	for(i = 0; cmds[i].c; i++) {
+		int cmdlen = strlen(cmds[i].c);
+
+		/* Check for command followed by whitespace */
+		if (!strncasecmp(cp, cmds[i].c, cmdlen) &&
+		    strchr(WHITESPACE, cp[cmdlen])) {
+			cp += cmdlen;
+			cp = cp + strspn(cp, WHITESPACE);
+			break;
+		}
+	}
+	cmdnum = cmds[i].n;
+	cmd = cmds[i].c;
+
+	/* Special case */
+	if (*cp == '!') {
+		cp++;
+		cmdnum = I_SHELL;
+	} else if (cmdnum == -1) {
+		error("Invalid command.");
+		return(-1);
+	}
+
+	/* Get arguments and parse flags */
+	*pflag = *n_arg = 0;
+	*path1 = *path2 = NULL;
+	switch (cmdnum) {
+	case I_GET:
+	case I_PUT:
+		if (parse_getput_flags(&cp, pflag))
+			return(-1);
+		/* Get first pathname (mandatory) */
+		if (get_pathname(&cp, path1))
+			return(-1);
+		if (*path1 == NULL) {
+			error("You must specify at least one path after a "
+			    "%s command.", cmd);
+			return(-1);
+		}
+		/* Try to get second pathname (optional) */
+		if (get_pathname(&cp, path2))
+			return(-1);
+		/* Otherwise try to guess it from first path */
+		if (*path2 == NULL && infer_path(*path1, path2))
+			return(-1);
+		break;
+	case I_RENAME:
+		/* Get first pathname (mandatory) */
+		if (get_pathname(&cp, path1))
+			return(-1);
+		if (get_pathname(&cp, path2))
+			return(-1);
+		if (!*path1 || !*path2) {
+			error("You must specify two paths after a %s "
+			    "command.", cmd);
+			return(-1);
+		}
+		break;
+	case I_RM:
+	case I_MKDIR:
+	case I_RMDIR:
+	case I_CHDIR:
+	case I_LCHDIR:
+	case I_LMKDIR:
+		/* Get pathname (mandatory) */
+		if (get_pathname(&cp, path1))
+			return(-1);
+		if (*path1 == NULL) {
+			error("You must specify a path after a %s command.", 
+			    cmd);
+			return(-1);
+		}
+		break;
+	case I_LS:
+		/* Path is optional */
+		if (get_pathname(&cp, path1))
+			return(-1);
+		break;
+	case I_LLS:
+	case I_SHELL:
+		/* Uses the rest of the line */
+		break;
+	case I_LUMASK:
+	case I_CHMOD:
+	case I_CHOWN:
+	case I_CHGRP:
+		/* Get numeric arg (mandatory) */
+		if (*cp < '0' && *cp > '9') {
+			error("You must supply a numeric argument "
+			    "to the %s command.", cmd);
+			return(-1);
+		}
+		*n_arg = strtoul(cp, (char**)&cp, 0);
+		if (!*cp || !strchr(WHITESPACE, *cp)) {
+			error("You must supply a numeric argument "
+			    "to the %s command.", cmd);
+			return(-1);
+		}
+		cp += strspn(cp, WHITESPACE);
+
+		/* Get pathname (mandatory) */
+		if (get_pathname(&cp, path1))
+			return(-1);
+		if (*path1 == NULL) {
+			error("You must specify a path after a %s command.", 
+			    cmd);
+			return(-1);
+		}
+		break;
+	case I_QUIT:
+	case I_PWD:
+	case I_LPWD:
+	case I_HELP:
+		break;
+	default:
+		fatal("Command not implemented");
+	}
+
+	*cpp = cp;
+
+	return(cmdnum);
+}
+
+int
+parse_dispatch_command(int in, int out, const char *cmd, char **pwd)
+{
+	char *path1, *path2;
+	int pflag, cmdnum;
+	unsigned long n_arg;
+	Attrib a, *aa;
+	char path_buf[PATH_MAX];
+
+	path1 = path2 = NULL;
+	cmdnum = parse_args(&cmd, &pflag, &n_arg, &path1, &path2);
+
+	/* Perform command */
+	switch (cmdnum) {
+	case -1:
+		break;
+	case I_GET:
+		path1 = make_absolute(path1, *pwd);
+		do_download(in, out, path1, path2, pflag);
+		break;
+	case I_PUT:
+		path2 = make_absolute(path2, *pwd);
+		do_upload(in, out, path1, path2, pflag);
+		break;
+	case I_RENAME:
+		path1 = make_absolute(path1, *pwd);
+		path2 = make_absolute(path2, *pwd);
+		do_rename(in, out, path1, path2);
+		break;
+	case I_RM:
+		path1 = make_absolute(path1, *pwd);
+		do_rm(in, out, path1);
+		break;
+	case I_MKDIR:
+		path1 = make_absolute(path1, *pwd);
+		attrib_clear(&a);
+		a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
+		a.perm = 0777;
+		do_mkdir(in, out, path1, &a);
+		break;
+	case I_RMDIR:
+		path1 = make_absolute(path1, *pwd);
+		do_rmdir(in, out, path1);
+		break;
+	case I_CHDIR:
+		path1 = make_absolute(path1, *pwd);
+		xfree(*pwd);
+		*pwd = do_realpath(in, out, path1);
+		break;
+	case I_LS:
+		path1 = make_absolute(path1, *pwd);
+		do_ls(in, out, path1?path1:*pwd);
+		break;
+	case I_LCHDIR:
+		if (chdir(path1) == -1)
+			error("Couldn't change local directory to "
+			    "\"%s\": %s", path1, strerror(errno));
+		break;
+	case I_LMKDIR:
+		if (mkdir(path1, 0777) == -1)
+			error("Couldn't create local directory to "
+			    "\"%s\": %s", path1, strerror(errno));
+		break;
+	case I_LLS:
+		local_do_ls(cmd);
+		break;
+	case I_SHELL:
+		local_do_shell(cmd);
+		break;
+	case I_LUMASK:
+		umask(n_arg);
+		break;
+	case I_CHMOD:
+		path1 = make_absolute(path1, *pwd);
+		attrib_clear(&a);
+		a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
+		a.perm = n_arg;
+		do_setstat(in, out, path1, &a);
+	case I_CHOWN:
+		path1 = make_absolute(path1, *pwd);
+		aa = do_stat(in, out, path1);
+		if (!aa->flags & SSH2_FILEXFER_ATTR_UIDGID) {
+			error("Can't get current ownership of "
+			    "remote file \"%s\"", path1);
+			break;
+		}
+		aa->uid = n_arg;
+		do_setstat(in, out, path1, aa);
+		break;
+	case I_CHGRP:
+		path1 = make_absolute(path1, *pwd);
+		aa = do_stat(in, out, path1);
+		if (!aa->flags & SSH2_FILEXFER_ATTR_UIDGID) {
+			error("Can't get current ownership of "
+			    "remote file \"%s\"", path1);
+			break;
+		}
+		aa->gid = n_arg;
+		do_setstat(in, out, path1, aa);
+		break;
+	case I_PWD:
+		printf("Remote working directory: %s\n", *pwd);
+		break;
+	case I_LPWD:
+		if (!getcwd(path_buf, sizeof(path_buf)))
+			error("Couldn't get local cwd: %s\n",
+			    strerror(errno));
+		else
+			printf("Local working directory: %s\n",
+			    path_buf);
+		break;
+	case I_QUIT:
+		return(-1);
+	case I_HELP:
+		help();
+		break;
+	default:
+		fatal("%d is not implemented", cmdnum);
+	}
+
+	if (path1)
+		xfree(path1);
+	if (path2)
+		xfree(path2);
+
+	return(0);
+}
+
+void
+interactive_loop(int fd_in, int fd_out)
+{
+	char *pwd;
+	char cmd[2048];
+
+	pwd = do_realpath(fd_in, fd_out, ".");
+	if (pwd == NULL)
+		fatal("Need cwd");
+
+	setlinebuf(stdout);
+	setlinebuf(stdin);
+
+	for(;;) {
+		char *cp;
+
+		printf("sftp> ");
+
+		/* XXX: use libedit */
+		if (fgets(cmd, sizeof(cmd), stdin) == NULL) {
+			printf("\n");
+			break;
+		}
+		cp = strrchr(cmd, '\n');
+		if (cp)
+			*cp = '\0';
+		if (parse_dispatch_command(fd_in, fd_out, cmd, &pwd))
+			break;
+	}
+	xfree(pwd);
+}
diff --git a/sftp-int.h b/sftp-int.h
new file mode 100644
index 0000000..234d800
--- /dev/null
+++ b/sftp-int.h
@@ -0,0 +1,27 @@
+/* $OpenBSD: sftp-int.h,v 1.1 2001/02/04 11:11:54 djm Exp $ */
+
+/*
+ * Copyright (c) 2001 Damien Miller.  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 interactive_loop(int fd_in, int fd_out);
diff --git a/sftp-server.c b/sftp-server.c
index a3e11ce..0e00400 100644
--- a/sftp-server.c
+++ b/sftp-server.c
@@ -22,7 +22,7 @@
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 #include "includes.h"
-RCSID("$OpenBSD: sftp-server.c,v 1.14 2001/01/21 19:05:56 markus Exp $");
+RCSID("$OpenBSD: sftp-server.c,v 1.15 2001/02/04 11:11:54 djm Exp $");
 
 #include "buffer.h"
 #include "bufaux.h"
@@ -31,6 +31,7 @@
 #include "xmalloc.h"
 
 #include "sftp.h"
+#include "sftp-common.h"
 
 /* helper */
 #define get_int64()			buffer_get_int64(&iqueue);
@@ -50,22 +51,9 @@
 
 /* portable attibutes, etc. */
 
-typedef struct Attrib Attrib;
 typedef struct Stat Stat;
 
-struct Attrib
-{
-	u_int32_t	flags;
-	u_int64_t	size;
-	u_int32_t	uid;
-	u_int32_t	gid;
-	u_int32_t	perm;
-	u_int32_t	atime;
-	u_int32_t	mtime;
-};
-
-struct Stat
-{
+struct Stat {
 	char *name;
 	char *long_name;
 	Attrib attrib;
@@ -122,90 +110,6 @@
 	return flags;
 }
 
-void
-attrib_clear(Attrib *a)
-{
-	a->flags = 0;
-	a->size = 0;
-	a->uid = 0;
-	a->gid = 0;
-	a->perm = 0;
-	a->atime = 0;
-	a->mtime = 0;
-}
-
-Attrib *
-decode_attrib(Buffer *b)
-{
-	static Attrib a;
-	attrib_clear(&a);
-	a.flags = buffer_get_int(b);
-	if (a.flags & SSH2_FILEXFER_ATTR_SIZE) {
-		a.size = buffer_get_int64(b);
-	}
-	if (a.flags & SSH2_FILEXFER_ATTR_UIDGID) {
-		a.uid = buffer_get_int(b);
-		a.gid = buffer_get_int(b);
-	}
-	if (a.flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
-		a.perm = buffer_get_int(b);
-	}
-	if (a.flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
-		a.atime = buffer_get_int(b);
-		a.mtime = buffer_get_int(b);
-	}
-	/* vendor-specific extensions */
-	if (a.flags & SSH2_FILEXFER_ATTR_EXTENDED) {
-		char *type, *data;
-		int i, count;
-		count = buffer_get_int(b);
-		for (i = 0; i < count; i++) {
-			type = buffer_get_string(b, NULL);
-			data = buffer_get_string(b, NULL);
-			xfree(type);
-			xfree(data);
-		}
-	}
-	return &a;
-}
-
-void
-encode_attrib(Buffer *b, Attrib *a)
-{
-	buffer_put_int(b, a->flags);
-	if (a->flags & SSH2_FILEXFER_ATTR_SIZE) {
-		buffer_put_int64(b, a->size);
-	}
-	if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) {
-		buffer_put_int(b, a->uid);
-		buffer_put_int(b, a->gid);
-	}
-	if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
-		buffer_put_int(b, a->perm);
-	}
-	if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
-		buffer_put_int(b, a->atime);
-		buffer_put_int(b, a->mtime);
-	}
-}
-
-void
-stat_to_attrib(struct stat *st, Attrib *a)
-{
-	attrib_clear(a);
-	a->flags = 0;
-	a->flags |= SSH2_FILEXFER_ATTR_SIZE;
-	a->size = st->st_size;
-	a->flags |= SSH2_FILEXFER_ATTR_UIDGID;
-	a->uid = st->st_uid;
-	a->gid = st->st_gid;
-	a->flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
-	a->perm = st->st_mode;
-	a->flags |= SSH2_FILEXFER_ATTR_ACMODTIME;
-	a->atime = st->st_atime;
-	a->mtime = st->st_mtime;
-}
-
 Attrib *
 get_attrib(void)
 {
diff --git a/sftp.1 b/sftp.1
new file mode 100644
index 0000000..59206b6
--- /dev/null
+++ b/sftp.1
@@ -0,0 +1,156 @@
+.\" $OpenBSD: sftp.1,v 1.1 2001/02/04 11:11:54 djm Exp $
+.\"
+.\" Copyright (c) 2001 Damien Miller. 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.
+.\"
+.Dd Febuary 4, 2001
+.Dt SFTP 1
+.Os
+.Sh NAME
+.Nm sftp
+.Nd Secure file tranfer program
+.Sh SYNOPSIS
+.Nm sftp
+.Op Fl v Li | Fl C
+.Op Fl o Ar ssh_option
+.Op Ar hostname | user@hostname
+.Sh DESCRIPTION
+.Nm
+is an interactive file transfer program, similar to
+.Xr ftp 1 ,
+which performs all operations over an encrypted
+.Xr ssh 1
+transport.
+It may also use many features of ssh, such as public key authentication and
+compression.
+.Nm
+connects and logs into the specified
+.Ar hostname
+then enters an interactive command mode.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl C
+Enables compression (via ssh's 
+.Fl C
+flag)
+.It Fl v
+Raise logging level. This option is also passed to ssh.
+.It Fl o Ar ssh_option
+Specify an option to be directly passed to
+.Xr ssh 1 .
+.El
+.Sh INTERACTIVE COMMANDS
+Once in interactive mode
+.Nm ,
+understands a set of commands similar to those of 
+.Xr ftp 1 .
+Commands are case insensitive.
+.Bl -tag -width Ds
+.It Ic CD Ar path
+Change remote directory to 
+.Ar path
+.It Ic LCD Ar path
+Change local directory to 
+.Ar path
+.It Ic CHGRP Ar grp Ar path
+Change group of file 
+.Ar path to 
+.Ar grp .
+.Ar grp
+must be numeric.
+.It Ic CHMOD Ar mode Ar path
+Change permissions of file 
+.Ar path to 
+.Ar mode
+.It Ic CHOWN Ar own Ar path
+Change owner of file 
+.Ar path to 
+.Ar own .
+.Ar own
+must be a numeric UID.
+.It Ic HELP
+Display help text
+.It Ic GET Ar remote-file Op Ar local-file
+Retrieve the
+.Ar remote-file
+and store it on the local machine.
+If the local
+file name is not specified, it is given the same name it has on the 
+remote machine.
+.It Ic LLS Op Ar ls-options Op Ar path
+Display local directory listing of either 
+.Ar path
+or current directory if
+.Ar path
+was not specified.
+.It Ic LMKDIR Ar path
+Create local directory specified by
+.Ar path
+.It Ic LPWD
+Print local working directory
+.It Ic LS Op Ar path
+Display remote directory listing of either
+.Ar path
+or current directory, is
+.Ar path not specified.
+.It Ic LUMASK Ar umask
+Set local umask to 
+.Ar umask
+.It Ic MKDIR Ar path
+Create remote directory specified by
+.Ar path
+.It Ic PUT local-file Op Ar remote-file
+Upload
+.Ar local-file
+and store it on the remote machine. If the local file name is not specified, 
+it is given the same name it has on the local machine.
+.It Ic PWD
+Display remote working directory
+.It Ic EXIT
+Quit sftp
+.It Ic QUIT
+Quit sftp
+.It Ic RENAME Ar oldpath Ar newpath
+Rename remote file from
+.Ar oldpath
+to
+.Ar newpath
+.It Ic RMDIR Ar path
+Remove remote directory specified by
+.Ar path
+.It Ic RM Ar path
+Delete remote file specified by
+.Ar path
+.It Ic ! Ar command
+Execute 
+.Ar command
+in local shell
+.It Ic !
+Escape to local shell
+.Sh AUTHORS
+Damien Miller <djm@mindrot.org>
+.Sh SEE ALSO
+.Xr ssh 1 ,
+.Xr ssh-add 1 ,
+.Xr ssh-keygen 1 ,
+.Xr sshd 8
diff --git a/sftp.c b/sftp.c
new file mode 100644
index 0000000..0dca12d
--- /dev/null
+++ b/sftp.c
@@ -0,0 +1,222 @@
+/*
+ * Copyright (c) 2001 Damien Miller.  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: sftp.c,v 1.1 2001/02/04 11:11:54 djm Exp $");
+
+/* XXX: commandline mode */
+/* XXX: copy between two remote hosts (commandline) */
+/* XXX: short-form remote directory listings (like 'ls -C') */
+
+#include "buffer.h"
+#include "xmalloc.h"
+#include "log.h"
+#include "pathnames.h"
+
+#include "sftp.h"
+#include "sftp-common.h"
+#include "sftp-client.h"
+#include "sftp-int.h"
+
+void
+connect_to_server(char **args, int *in, int *out, pid_t *sshpid)
+{
+	int c_in, c_out;
+#ifdef USE_PIPES
+	int pin[2], pout[2];
+	if ((pipe(pin) == -1) || (pipe(pout) == -1))
+		fatal("pipe: %s", strerror(errno));
+	*in = pin[0];
+	*out = pout[1];
+	c_in = pout[0];
+	c_out = pin[1];
+#else /* USE_PIPES */
+	int inout[2];
+	if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1)
+		fatal("socketpair: %s", strerror(errno));
+	*in = *out = inout[0];
+	c_in = c_out = inout[1];
+#endif /* USE_PIPES */
+
+	if ((*sshpid = fork()) == -1)
+		fatal("fork: %s", strerror(errno));
+	else if (*sshpid == 0) {
+		if ((dup2(c_in, STDIN_FILENO) == -1) ||
+		    (dup2(c_out, STDOUT_FILENO) == -1)) {
+			fprintf(stderr, "dup2: %s\n", strerror(errno));
+			exit(1);
+		}
+		close(*in);
+		close(*out);
+		close(c_in);
+		close(c_out);
+		execv(_PATH_SSH_PROGRAM, args);
+		fprintf(stderr, "exec: %s", strerror(errno));
+		exit(1);
+	}
+
+	close(c_in);
+	close(c_out);
+}
+
+char **
+make_ssh_args(char *add_arg)
+{
+	static char **args = NULL;
+	static int nargs = 0;
+	char debug_buf[4096];
+	int i;
+
+	/* Init args array */
+	if (args == NULL) {
+		nargs = 4;
+		i = 0;
+		args = xmalloc(sizeof(*args) * nargs);
+		args[i++] = "ssh";
+		args[i++] = "-oProtocol=2";
+		args[i++] = "-s";
+		args[i++] = NULL;
+	}
+
+	/* If asked to add args, then do so and return */
+	if (add_arg) {
+		i = nargs++ - 1;
+		args = xrealloc(args, sizeof(*args) * nargs);
+		args[i++] = add_arg;
+		args[i++] = NULL;
+		return(NULL);
+	}
+
+	/* Otherwise finish up and return the arg array */
+	make_ssh_args("sftp");
+
+	/* XXX: overflow - doesn't grow debug_buf */
+	debug_buf[0] = '\0';
+	for(i = 0; args[i]; i++) {
+		if (i)
+			strlcat(debug_buf, " ", sizeof(debug_buf));
+
+		strlcat(debug_buf, args[i], sizeof(debug_buf));
+	}
+	debug("SSH args \"%s\"", debug_buf);
+
+	return(args);
+}
+
+void 
+usage(void)
+{
+	fprintf(stderr, "usage: sftp [-vC] [-osshopt=value] [user@]host\n");
+	exit(1);
+}
+
+int 
+main(int argc, char **argv)
+{
+	int in, out, i, debug_level, compress_flag;
+	pid_t sshpid;
+	char *cp;
+	LogLevel ll;
+
+	debug_level = compress_flag = 0;
+	for(i = 1; i < argc && argv[i][0] == '-'; i++) {
+		if (!strcmp(argv[i], "-v"))
+			debug_level = MIN(3, debug_level + 1);
+		else if (!strcmp(argv[i], "-C"))
+			compress_flag = 1;
+		else if (!strncmp(argv[i], "-o", 2)) {
+			make_ssh_args(argv[i]);
+		} else {
+			fprintf(stderr, "Unknown option \"%s\"\n", argv[i]);
+			usage();
+		}
+	}
+
+	if (i == argc || argc > (i + 1))
+		usage();
+
+	if ((cp = strchr(argv[i], '@')) == NULL)
+		cp = argv[i];
+	else {
+		*cp = '\0';
+		if (!argv[i][0]) {
+			fprintf(stderr, "Missing username\n");
+			usage();
+		}
+		make_ssh_args("-l");
+		make_ssh_args(argv[i]);
+		cp++;
+	}
+
+	if (!*cp) {
+		fprintf(stderr, "Missing hostname\n");
+		usage();
+	}
+
+	/* Set up logging and debug '-d' arguments to ssh */
+	ll = SYSLOG_LEVEL_INFO;
+	switch (debug_level) {
+	case 1:
+		ll = SYSLOG_LEVEL_DEBUG1;
+		make_ssh_args("-v");
+		break;
+	case 2:
+		ll = SYSLOG_LEVEL_DEBUG2;
+		make_ssh_args("-v");
+		make_ssh_args("-v");
+		break;
+	case 3:
+		ll = SYSLOG_LEVEL_DEBUG3;
+		make_ssh_args("-v");
+		make_ssh_args("-v");
+		make_ssh_args("-v");
+		break;
+	}
+
+	if (compress_flag)
+		make_ssh_args("-C");
+
+	log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1);
+
+	make_ssh_args(cp);
+
+	fprintf(stderr, "Connecting to %s...\n", cp);
+
+	connect_to_server(make_ssh_args(NULL), &in, &out, &sshpid);
+
+	do_init(in, out);
+
+	interactive_loop(in, out);
+
+	close(in);
+	close(out);
+
+	if (kill(sshpid, SIGHUP) == -1)
+		fatal("Couldn't terminate ssh process: %s", strerror(errno));
+
+	/* XXX: wait? */
+
+	exit(0);
+}
diff --git a/ssh.1 b/ssh.1
index 6f10436..99fb8c7 100644
--- a/ssh.1
+++ b/ssh.1
@@ -34,7 +34,7 @@
 .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 .\"
-.\" $OpenBSD: ssh.1,v 1.80 2001/01/29 12:36:10 djm Exp $
+.\" $OpenBSD: ssh.1,v 1.83 2001/02/04 11:11:55 djm Exp $
 .Dd September 25, 1999
 .Dt SSH 1
 .Os
@@ -753,8 +753,8 @@
 .It Cm HostKeyAlias
 Specifies an alias that should be used instead of the
 real host name when looking up or saving the host key
-the kown_hosts files.
-This option is useful for tunneling ssh connection
+in the known_hosts files.
+This option is useful for tunneling ssh connections
 or if you have multiple servers running on a single host.
 .It Cm HostName
 Specifies the real host name to log into.
@@ -914,8 +914,9 @@
 attempted if the identity file exists, or an authentication agent is
 running.
 Note that this option applies to protocol version 1 only.
-.It Cm SkeyAuthentication
-Specifies whether to use
+.It Cm ChallengeResponseAuthentication
+Specifies whether to use challenge response authentication.
+Currently there is only support for
 .Xr skey 1
 authentication.
 The argument to this keyword must be
@@ -1270,6 +1271,7 @@
 .Xr rlogin 1 ,
 .Xr rsh 1 ,
 .Xr scp 1 ,
+.Xr sftp 1 ,
 .Xr ssh-add 1 ,
 .Xr ssh-agent 1 ,
 .Xr ssh-keygen 1 ,
diff --git a/ssh_config b/ssh_config
index e7dabbf..cfaf231 100644
--- a/ssh_config
+++ b/ssh_config
@@ -1,8 +1,8 @@
-#	$OpenBSD: ssh_config,v 1.7 2001/01/29 01:58:18 niklas Exp $
+#	$OpenBSD: ssh_config,v 1.8 2001/02/02 12:57:51 deraadt Exp $
 
-# This is ssh client systemwide configuration file.  This file provides 
-# defaults for users, and the values can be changed in per-user configuration
-# files or on the command line.
+# This is ssh client systemwide configuration file.  See ssh(1) for more
+# information.  This file provides defaults for users, and the values can
+# be changed in per-user configuration files or on the command line.
 
 # Configuration data is parsed as follows:
 #  1. command line options
diff --git a/sshconnect1.c b/sshconnect1.c
index e732806..80b769b 100644
--- a/sshconnect1.c
+++ b/sshconnect1.c
@@ -13,7 +13,7 @@
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: sshconnect1.c,v 1.21 2001/01/29 19:47:31 markus Exp $");
+RCSID("$OpenBSD: sshconnect1.c,v 1.22 2001/02/03 10:08:37 markus Exp $");
 
 #include <openssl/bn.h>
 #include <openssl/evp.h>
@@ -399,11 +399,11 @@
 	if (stat(tkt_string(), &st) < 0)
 		return 0;
 
-	strncpy(inst, (char *) krb_get_phost(get_canonical_hostname()), INST_SZ);
+	strncpy(inst, (char *) krb_get_phost(get_canonical_hostname(1)), INST_SZ);
 
-	realm = (char *) krb_realmofhost(get_canonical_hostname());
+	realm = (char *) krb_realmofhost(get_canonical_hostname(1));
 	if (!realm) {
-		debug("Kerberos V4: no realm for %s", get_canonical_hostname());
+		debug("Kerberos V4: no realm for %s", get_canonical_hostname(1));
 		return 0;
 	}
 	/* This can really be anything. */
diff --git a/sshd.8 b/sshd.8
index 1aad0fc..c71ecb2 100644
--- a/sshd.8
+++ b/sshd.8
@@ -34,7 +34,7 @@
 .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 .\"
-.\" $OpenBSD: sshd.8,v 1.86 2001/01/28 20:43:25 stevesk Exp $
+.\" $OpenBSD: sshd.8,v 1.90 2001/02/04 11:11:55 djm Exp $
 .Dd September 25, 1999
 .Dt SSHD 8
 .Os
@@ -593,6 +593,14 @@
 .It Cm RandomSeed
 Obsolete.
 Random number generation uses other techniques.
+.It Cm ReverseMappingCheck
+Specifies whether
+.Nm
+should try to verify the remote host name and check that
+the resolved host name for the remote IP address maps back to the
+very same IP address.
+The default is
+.Dq no .
 .It Cm RhostsAuthentication
 Specifies whether authentication using rhosts or /etc/hosts.equiv
 files is sufficient.
@@ -616,15 +624,15 @@
 .It Cm ServerKeyBits
 Defines the number of bits in the server key.
 The minimum value is 512, and the default is 768.
-.It Cm SkeyAuthentication
+.It Cm ChallengeResponseAuthentication
 Specifies whether
-.Xr skey 1
+challenge reponse
 authentication is allowed.
+Currently there is only support for
+.Xr skey 1
+authentication.
 The default is
 .Dq yes .
-Note that s/key authentication is enabled only if
-.Cm PasswordAuthentication
-is allowed, too.
 .It Cm StrictModes
 Specifies whether
 .Nm
@@ -797,6 +805,9 @@
 The command supplied by the user (if any) is ignored.
 The command is run on a pty if the connection requests a pty;
 otherwise it is run without a tty.
+Note that if you want a 8-bit clean channel,
+you must not request a pty or should specify
+.Cm no-pty .
 A quote may be included in the command by quoting it with a backslash.
 This option might be useful
 to restrict certain RSA keys to perform just a specific operation.
@@ -1076,6 +1087,7 @@
 protocol versions 1.5 and 2.0.
 .Sh SEE ALSO
 .Xr scp 1 ,
+.Xr sftp 1 ,
 .Xr sftp-server 8 ,
 .Xr ssh 1 ,
 .Xr ssh-add 1 ,
diff --git a/sshd_config b/sshd_config
index 25c0ec3..9ba1789 100644
--- a/sshd_config
+++ b/sshd_config
@@ -1,6 +1,7 @@
-#	$OpenBSD: sshd_config,v 1.27 2001/01/29 01:58:19 niklas Exp $
+#	$OpenBSD: sshd_config,v 1.30 2001/02/03 10:19:51 markus Exp $
 
-# This is ssh server systemwide configuration file.
+# This is the sshd server system-wide configuration file.  See sshd(8)
+# for more information.
 
 Port 22
 #Protocol 2,1
@@ -39,9 +40,9 @@
 # To disable tunneled clear text passwords, change to no here!
 PasswordAuthentication yes
 PermitEmptyPasswords no
+
 # Uncomment to disable s/key passwords 
-#SkeyAuthentication no
-#KbdInteractiveAuthentication yes
+#ChallengeResposeAuthentication no
 
 # To change Kerberos options
 #KerberosAuthentication no
@@ -59,3 +60,4 @@
 #Subsystem	sftp	/usr/libexec/sftp-server
 #MaxStartups 10:30:60
 #Banner /etc/issue.net
+#ReverseMappingCheck yes