diff -N -U 5 -u openssh-4.2p1/Makefile.in sshop-4.2p1/Makefile.in
--- openssh-4.2p1/Makefile.in	Sun May 29 03:22:29 2005
+++ sshop-4.2p1/Makefile.in	Fri Oct 21 16:02:25 2005
@@ -58,11 +58,11 @@
 EXEEXT=@EXEEXT@
 
 INSTALL_SSH_PRNG_CMDS=@INSTALL_SSH_PRNG_CMDS@
 INSTALL_SSH_RAND_HELPER=@INSTALL_SSH_RAND_HELPER@
 
-TARGETS=ssh$(EXEEXT) sshd$(EXEEXT) ssh-add$(EXEEXT) ssh-keygen$(EXEEXT) ssh-keyscan${EXEEXT} ssh-keysign${EXEEXT} ssh-agent$(EXEEXT) scp$(EXEEXT) ssh-rand-helper${EXEEXT} sftp-server$(EXEEXT) sftp$(EXEEXT)
+TARGETS=ssh$(EXEEXT) sshd$(EXEEXT) ssh-add$(EXEEXT) ssh-keygen$(EXEEXT) ssh-keyscan${EXEEXT} ssh-keysign${EXEEXT} ssh-agent$(EXEEXT) scp$(EXEEXT) ssh-rand-helper${EXEEXT} sftp-server$(EXEEXT) sftp$(EXEEXT) sshhop_test$(EXEEXT)
 
 LIBSSH_OBJS=acss.o authfd.o authfile.o bufaux.o buffer.o \
 	canohost.o channels.o cipher.o cipher-acss.o cipher-aes.o \
 	cipher-bf1.o cipher-ctr.o cipher-3des1.o cleanup.o \
 	compat.o compress.o crc32.o deattack.o fatal.o hostfile.o \
@@ -72,11 +72,11 @@
 	monitor_fdpass.o rijndael.o ssh-dss.o ssh-rsa.o dh.o kexdh.o \
 	kexgex.o kexdhc.o kexgexc.o scard.o msg.o progressmeter.o dns.o \
 	entropy.o scard-opensc.o gss-genr.o
 
 SSHOBJS= ssh.o readconf.o clientloop.o sshtty.o \
-	sshconnect.o sshconnect1.o sshconnect2.o
+	sshconnect.o sshconnect1.o sshconnect2.o sshhop.o
 
 SSHDOBJS=sshd.o auth-rhosts.o auth-passwd.o auth-rsa.o auth-rh-rsa.o \
 	sshpty.o sshlogin.o servconf.o serverloop.o \
 	auth.o auth1.o auth2.o auth-options.o session.o \
 	auth-chall.o auth2-chall.o groupaccess.o \
@@ -162,10 +162,13 @@
 sftp$(EXEEXT): $(LIBCOMPAT) libssh.a sftp.o sftp-client.o sftp-common.o sftp-glob.o progressmeter.o
 	$(LD) -o $@ progressmeter.o sftp.o sftp-client.o sftp-common.o sftp-glob.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) $(LIBEDIT)
 
 ssh-rand-helper${EXEEXT}: $(LIBCOMPAT) libssh.a ssh-rand-helper.o
 	$(LD) -o $@ ssh-rand-helper.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS)
+
+sshhop_test$(EXEEXT): $(LIBCOMPAT) libssh.a sshhop.o sshhop_test.o
+	$(LD) -o $@ sshhop.o sshhop_test.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) $(LIBEDIT)
 
 # test driver for the loginrec code - not built by default
 logintest: logintest.o $(LIBCOMPAT) libssh.a loginrec.o
 	$(LD) -o $@ logintest.o $(LDFLAGS) loginrec.o -lopenbsd-compat -lssh $(LIBS)
 
diff -N -U 5 -u openssh-4.2p1/README.sshhop sshop-4.2p1/README.sshhop
--- openssh-4.2p1/README.sshhop	Wed Dec 31 19:00:00 1969
+++ sshop-4.2p1/README.sshhop	Thu Dec 22 09:26:12 2005
@@ -0,0 +1,398 @@
+SSH is a protocol used to open a command shell on a remote host via an
+encrypted communications channel.  When a firewall or other network
+roadblock prevents a user on host A from directly opening an SSH connection
+to a host C it is often necessary to connect through an intermediary.
+Users will often enter a command on host A to establish an SSH connection
+to the intermediate host B, and then enter a command to host B via
+that connection to establish an SSH connection from host B to host C.
+If an attacker controls host B, the attacker can either capture the
+credentials (password) used to establish the connection between the user
+and host C or may simply capture the connection after it is established.
+
+The SSH hop modifications, discussed in this README, introduce a
+mechanism to facilitate a more secure use of SSH for establishing remote
+command shells via connections through intermediate hosts. As before,
+an encrypted channel is established from host A and host B.  As before,
+a connection is then opened from host B to host C. However, this
+connection is used to establish an encrypted channel between host A and
+host C, preventing the owner of host B from eavesdropping on or modifying
+its contents.
+
+The OpenBSD project's version of SSH, OpenSSH, supports those secure
+connections via intermediary hosts by configuring a sequence of port
+forwarding SSH connections. In the general case, SSH port forwarding
+allows a user process to make a TCP/IP connection to a specially
+configured SSH client which forwards communications from the user process
+to a server on a remote host via the SSH tunnel between the SSH client
+and SSH server. These four processes may be running on up to four
+separate hosts. Barring any application-dependent encryption between
+the user process and remote server, only the communications between
+the SSH client and SSH server are encrypted. When port forwarding is
+used for SSH hopping as described above, the user process is another
+SSH client process and the server on the remote host is an SSH server.
+In that case, the four processes are running on at least three hosts
+and the communications between the user process and its remote server
+are encrypted as they transit the intermediate host.
+
+Setting up such a port forwarding connection for SSH hopping involves
+spawning multiple SSH client processes at the command line. Although
+not difficult to set up, it does require some thought and a deeper
+knowledge of the SSH options than would be possessed by a casual user.
+In addition, the TCP/IP port provided by the specially configured SSH
+client process to be used for port forwarding is potentially accessible
+to other users on that host, which would give them access to the remote
+host.
+
+The SSH hop modifications described below seek to improve the usability
+of the SSH connection chaining to encourage use of a more secure
+connection strategy through intermediaries than might otherwise be used.
+
+Each host in the chain of encrypted connections from the source host
+to the ultimate destination host represents a single "hop". The
+authentication credentials are revealed only at the source host and at
+the host for which those credentials are intended. The following SSH
+hop command line syntax is provided to access the destination host
+(hostn) via a chain of hosts (...) with host1 as the first intermediate
+host in the chain:
+
+	ssh [ssh_global_options]
+	    -H [ssh_local_options] [-p port1] [user1@]host1
+	    ...
+	    -H [ssh_local_options] [-p portn] [usern@]hostn
+	    [command]
+
+The "ssh_global_options" and "ssh_local_options" represent any SSH
+options, other than -H, that are relevant in, respectively, the global
+and local contexts. The global options appear before the first -H in
+the command and are applied, as appropriate, to every hop in the chain.
+The effect is as if those options preceded every set of hop specific
+options for every hop in the chain. The local options follow a -H and
+are specific only to that one hop. For more information on which
+SSH options can be used as global options and which can be used
+as local options, please see the next section ("SSH option processing
+in conjunction with the -H option").
+
+A host name or IP address must be provided following each -H. The user
+name of the account at each hop is determined by matching the first of
+the following rules to apply:
+	1) user name attached to the host name (e.g., -H user@host)
+	2) user name specified in the local options (using -l option)
+	   (e.g., -H -l user host)
+	3) user name specified in the global options (using -l option)
+	   (e.g., ssh -l user -H ...)
+	4) the name of the user account issuing the SSH command
+
+Example:
+	+-------+        +------------+        +--------------------+
+	|student|        |   hobbes   |        |      calvin        |
+	|@school| -----> |@gateway.com| -----> |@spaceship.world.net|
+	+-------+        +------------+        +--------------------+
+	Suppose "student@school" wants to log into "spaceship.world.net"
+	as "calvin" via his "hobbes" account at "gateway.com". The
+	following command could be used:
+
+	%school> ssh -l calvin -H hobbes@gateway.com -H spaceship.world.net
+
+	That command will create a chain of subprocesses on the host
+	"school" to construct two encrypted tunnels. The first encrypted
+	tunnel will connect "student@school" to "hobbes@gateway.com". The
+	second encrypted tunnel will connect "student@school" to
+	"calvin@spaceship.world.net" via the first encrypted tunnel.
+
+To make SSH hopping more secure on the source host, support was also
+added for using Unix Domain Sockets, rather than TCP/IP sockets, for
+the ports on that host used for port forwarding (port forwarded ports).
+The disadvantage to using TCP/IP for the port forwarded port is that
+other users on the source host might be able to connect to the TCP/IP
+port and gain access to a remote host to which they should not have
+access. Unlike TCP/IP sockets, Unix Domain Sockets can be protected
+from access by users other than the one who created the port forwarding
+tunnel. By extension, the connection to the remote host will also be
+protected from unauthorized access. To support use of Unix Domain Sockets
+for port forwarding, two options were added to SSH.
+
+The -U and -u options were added to support use of Unix Domain Sockets
+for port forwarding. The syntax is as follows:
+-U <uds_path>,<remote_host>,<remote_ssh_port>
+	Local port forwarding information used for creating this
+	connection. The parameters are: the path for the Unix Domain
+	Socket (UDS) to be created, the remote host to which to
+	authenticate, and the SSH port on the remote host.
+-u <uds_path>
+	Path to the Unix Domain Socket (UDS) to which SSH should connect.
+	The UDS will have been created from the -U option in a previous
+	SSH command.
+
+
+Example:
+	In the previous example, two encryption tunnels were created to
+	connect "student@school" to "calvin@spaceship.world.net". The
+	tunnels are created by spawning two subprocesses Those two
+	subprocesses will be described next.
+
+	The first subprocess creates an SSH tunnel from the "student"
+	account on "school" to the "hobbes" account on "gateway.com".
+	It runs in the background and prohibits use of stdin, remote
+	commands, and X11 forwarding (-n -N -x). It also creates a Unix
+	Domain Socket on the local host to be used for port forwarding
+	(-U). As noted above, the -U option specifies the three parameters
+	needed for port forwarding in a comma separated list. The Unix
+	Domain Socket is created in the user's .ssh directory and its
+	name contains information about the remote endpoint of the tunnel.
+	Note that the remote account is calvin@spaceship.world.net and
+	that the remote port for contacting the SSH daemon is the standard
+	SSH port, 22.
+
+	ssh -n -N -x \
+	    -U ~/.ssh/.ssh_184_513_calvin@spaceship.world.net_22,calvin@spaceship.world.net,22 \
+	    hobbes@gateway.world.com
+
+	The second process creates a tunnel from the "student" account
+	on "school" to the "calvin" account on "spaceship.world.net"
+	through the tunnel from "student@school" to "hobbes@gateway.com".
+	It forces allocation of a pseudo-tty (-t -t) for command line
+	input and sets the name of the remote host (-o). If the name
+	of the remote host is not set using the -o option, it will be
+	shown as localhost since that is where the entry point for the
+	forwarding port resides. The Unix Domain Socket created by the
+	first process is used for accessing the remote machine (-u). If
+	the Unix Domain Socket specified in the -u option does not exist,
+	the second process will report an error and exit.
+
+	ssh -t -t \
+	    -u ~/.ssh/.ssh_1034_513_calvin@spaceship.world.net_22 \
+	    -o HostKeyAlias=spaceship.world.net \
+	    calvin@localhost
+
+
+Please see the ssh.1 man page for more information.
+
+=============================================================================
+SSH option processing in conjunction with -H option.
+
+The following table contains a list of SSH options and how those
+options are handled in the global and local contexts. In general,
+the options are handled the same in both contexts. Keys to the
+symbols and terminology precede the table.
+
+---------------------------------------------------------------------
+KEY:
+Column labels:
+Option = SSH command line option
+Global = options specified before the first -H option
+Local  = options specified after a single -H option
+
+Symbols in Global and Local columns:
+f = only on first hop 
+l = only on last hop
+y = all applicable hops
+
+Key to numbers in comment field:
+1 - will be inserted by the top level (hopper) process to all but
+    the last child
+2 - will be inserted by the top level (hopper) process to all but
+    the first child
+---------------------------------------------------------------------
+
+Option	Global	Local	Comments
+------	------	-----	---------------------------------------------
+a	y	y	
+A	l	y	
+b	f	y	
+c	y	y	
+e	l	y	
+f	l	l	1
+g	l	l	
+i	y	y	
+I	y	y	
+k	y	y	
+l	y	y	
+m	y	y	
+n	l	l	1
+N	l	l	1
+o	y	y	2 for -o HostKeyAlias=<host>
+p	y*	y*	Apply when port not previously specified.
+			Order of precedence for port assignment:
+			1) first local p
+			2) first global p
+			3) default SSH port
+P	y	y	Deprecated but still supported (use privileged port)
+q	y	y	
+s	l	l	
+t	l	l	
+T	l	l	
+v	y	y	
+x	l	l	1
+X	l	l	
+Y	l	l	
+C	y	y	
+F	y	y	
+L	l	l	
+R	l	l	
+D	l	l	
+U	l	l	1
+u	f	f	2
+O	l	l	
+M	l	l	
+S	l	l	
+1	y	y	
+2	y	y	
+4	y	y	
+6	y	y	
+=============================================================================
+
+TODO:
+1) SCP
+2) SFTP
+
+=============================================================================
+
+Overview of changes to the source files:
+
+Makefile.in
+===========
+Added target for sshhop_test.
+Added sshhop.o to dependency list and object list for sshhop_test and ssh.
+
+README.sshhop
+=============
+New. This file.
+
+channels.c
+==========
+Added function port_open_helper_base().
+
+Additional cleanup added for unix domain sockets.
+
+Function port_open_helper() functions as previously but the bulk of the
+code now resides in port_open_helper_base() which is invoked from
+port_open_helper() or directly. Port_open_helper_base() is called directly
+to omit looking up the ip address and port when Unix Domain Sockets are
+in use.
+
+The original function channel_setup_fwd_listener() is renamed 
+channel_setup_fwd_port_listener().
+
+A new function channel_setup_fwd_uds_listener() is added to handle
+processing for Unix Domain Sockets.
+
+The new channel_setup_fwd_listener() function calls one of two functions:
+channel_setup_fwd_port_listener() and  channel_setup_fwd_uds_listener()
+depending on whether ports or unix domain sockets are being used.
+
+channels.h
+==========
+Added fwd_path to Channel structure to support use of Unix Domain Sockets
+for port forwarding.
+
+clientloop.c
+============
+Added remote prompt id flag to read_passphrase() call.
+
+misc.c
+======
+Added #include "pathnames.h". Needed for new construct_uds_path() function.
+
+Added comma (,) as separator for forwarding information. Colon (:) and
+slash (/) could both be used in a unix domain socket designator and thus
+cannot be used as separators.
+
+Added function construct_uds_path() used to ensure a consistent naming
+convention for Unix Domain Sockets.
+
+misc.h
+======
+Added function declaration for construct_uds_path().
+
+Added bit code for remote prompt id flag.
+
+readconf.c
+==========
+Added configuration options:
+	"PrivateLocalForward" (takes a UDS path as the forwarding port)
+	"UnixDomainSocket" (UnixDomainSocket to which to connect)
+Added handling of listen_port value when Unix Domain Socket specified
+(i.e., listen_port < 0).
+
+readconf.h
+==========
+Revised information on listen_port (to reflect use with Unix Domain Sockets).
+Added uds_path configuration parameter.
+
+readpass.c
+==========
+Added function add_id_to_prompt() to add remote process id to prompt.
+Added conditional to call add_id_to_prompt() if RP_ADD_ID flag is set.
+
+scard.c
+=======
+Added remote prompt id flag to read_passphrase() call.
+
+ssh.1
+=====
+Added descriptions of new options (-u -U -H) and new config
+parameters (PrivateLocalForward and UnixDomainSocket)
+
+ssh.c
+=====
+Declared variables static that are used only in scope of ssh.c
+Updated usage information to reflect new usage (-H option).
+Moved option parsing from main() into new function parse_options()
+(the diff output is somewhat confusing since lines from main() show up
+as being deleted from parse_options() and then added to the "new" main()).
+Use SSH_CMD_LINE_OPTIONS #define for option list so it can be shared with
+code in sshhop.c
+Added -u and -U options.
+Added check for config option for uds_path variable.
+Added call to sshop_parse_options() and additional processing if multiple
+hops were specified. Functions to support processing of multiple hop
+information are all located in sshhop.c (new). If no -H options (no hop
+processing) is indicated then options are parsed and the program continues
+as before.
+Added tilde expansion check for uds path.
+Modified connection logic to handle UDS connection.
+
+ssh.h
+=====
+Added SSH_CMDLINE_OPTIONS
+
+sshconnect.c
+============
+Added function ssh_connect_uds() to support using a Unix Domain Socket as
+a connection endpoint instead of a regular socket. The Unix Domain Socket
+is a protected resource of an individual rather than a resource that may
+be accessible to any user on the machine.
+Added remote prompt id flag to read_passphrase() call.
+Added AF_UNIX processing to check_host_key() to support use of Unix
+Domain Sockets for port forwarding: a) new case (AF_UNIX) added to switch
+and b) conditional based on the address family added when no proxy command.
+
+sshconnect.h
+============
+Added function declaration for ssh_connect_uds().
+
+sshconnect1.c
+============
+Added remote prompt id flag to read_passphrase() calls.
+
+sshconnect2.c
+============
+Includes patch supplied by openSSH folks to provide hostname in prompt
+(patch is not included in openSSH distribution).
+Added remote prompt id flag to read_passphrase() calls.
+
+sshhop.c
+========
+New. Contains command line processing for new -H option.
+Contains functions for properly configuring argument lists for multiple
+SSH subprocesses, functions for starting the SSH subprocesses, and
+functions for terminating the multiple SSH subprocesses. The top-level
+control for the multiple hop processing is in ssh.c.
+
+sshhop.h
+========
+New. Function declarations for functions used in sshhop.c
+
+sshhop_test.c
+=============
+New. Program to test sshhop.c functionality outside of ssh.
diff -N -U 5 -u openssh-4.2p1/channels.c sshop-4.2p1/channels.c
--- openssh-4.2p1/channels.c	Sun Jul 17 03:22:45 2005
+++ sshop-4.2p1/channels.c	Fri Oct 21 16:02:27 2005
@@ -136,10 +136,11 @@
 /* AF_UNSPEC or AF_INET or AF_INET6 */
 static int IPv4or6 = AF_UNSPEC;
 
 /* helper */
 static void port_open_helper(Channel *c, char *rtype);
+static void port_open_helper_base(Channel *c, char *rtype, char *remote_ipaddr, u_short remote_port);
 
 /* -- channel core */
 
 Channel *
 channel_lookup(int id)
@@ -351,10 +352,16 @@
 	buffer_free(&c->extended);
 	if (c->remote_name) {
 		xfree(c->remote_name);
 		c->remote_name = NULL;
 	}
+	if ((c->type == SSH_CHANNEL_PORT_LISTENER) &&
+	    (c->fwd_path != NULL)) {
+		unlink(c->fwd_path);
+		xfree(c->fwd_path);
+		c->fwd_path = NULL;
+	}
 	channels[c->self] = NULL;
 	xfree(c);
 }
 
 void
@@ -1174,27 +1181,28 @@
 		}
 		xfree(remote_ipaddr);
 	}
 }
 
+/* Send port information to the other end of the ssh connection. */
 static void
-port_open_helper(Channel *c, char *rtype)
+port_open_helper_base(Channel *c, char *rtype, char *remote_ipaddr,
+    u_short remote_port)
 {
 	int direct;
 	char buf[1024];
-	char *remote_ipaddr = get_peer_ipaddr(c->sock);
-	int remote_port = get_peer_port(c->sock);
 
 	direct = (strcmp(rtype, "direct-tcpip") == 0);
 
 	snprintf(buf, sizeof buf,
 	    "%s: listening port %d for %.100s port %d, "
 	    "connect from %.200s port %d",
 	    rtype, c->listening_port, c->path, c->host_port,
 	    remote_ipaddr, remote_port);
 
-	xfree(c->remote_name);
+	if (c->remote_name)
+		xfree(c->remote_name);
 	c->remote_name = xstrdup(buf);
 
 	if (compat20) {
 		packet_start(SSH2_MSG_CHANNEL_OPEN);
 		packet_put_cstring(rtype);
@@ -1222,10 +1230,25 @@
 		if (packet_get_protocol_flags() &
 		    SSH_PROTOFLAG_HOST_IN_FWD_OPEN)
 			packet_put_cstring(c->remote_name);
 		packet_send();
 	}
+}
+
+/*
+ * Send port information to the other side of the connection when
+ * socket used for communication is a regular IP socket (i.e., not
+ * a Unix Domain Socket).
+ */
+static void
+port_open_helper(Channel *c, char *rtype)
+{
+	char *remote_ipaddr = get_peer_ipaddr(c->sock);
+	u_short remote_port = get_peer_port(c->sock);
+
+	port_open_helper_base(c, rtype, remote_ipaddr, remote_port);
+
 	xfree(remote_ipaddr);
 }
 
 /*
  * This socket is listening for connections to a forwarded TCP/IP port.
@@ -1236,10 +1259,12 @@
 	Channel *nc;
 	struct sockaddr addr;
 	int newsock, nextstate;
 	socklen_t addrlen;
 	char *rtype;
+	char hostname[MAXHOSTNAMELEN];
+	struct hostent *he;
 
 	if (FD_ISSET(c->sock, readset)) {
 		debug("Connection to port %d forwarding "
 		    "to %.100s port %d requested.",
 		    c->listening_port, c->path, c->host_port);
@@ -1276,11 +1301,35 @@
 			 * this flag has been reset by a pre-handler.
 			 * otherwise the FD_ISSET calls might overflow
 			 */
 			nc->delayed = 1;
 		} else {
-			port_open_helper(nc, rtype);
+			/* If there is no unix domain socket, use the regular
+			 * function to set up the channel. (Gets the IP
+			 * address from the socket connection).
+			 */
+			if (c->fwd_path == NULL) {
+				port_open_helper(nc, rtype);
+			/* Otherwise, we are using a unix domain socket, so
+			 * use the IP address for the local host.
+			 */
+			} else {
+				nc->fwd_path = xstrdup(c->fwd_path);
+				memset(hostname, '\0', sizeof(hostname));
+				gethostname(hostname, sizeof(hostname));
+				/* Get the host IP address */
+				if ((he = gethostbyname(hostname)) != 0) {
+					port_open_helper_base(nc, rtype,
+					    he->h_addr_list[0], 0);
+				/* Couldn't get the host IP address, use the
+				 * address for localhost.
+				 */
+				} else {
+					port_open_helper_base(nc, rtype,
+					    "127.0.0.1", 0);
+				}
+			}
 		}
 	}
 }
 
 /*
@@ -2182,11 +2231,11 @@
 {
 	IPv4or6 = af;
 }
 
 static int
-channel_setup_fwd_listener(int type, const char *listen_addr, u_short listen_port,
+channel_setup_fwd_port_listener(int type, const char *listen_addr, u_short listen_port,
     const char *host_to_connect, u_short port_to_connect, int gateway_ports)
 {
 	Channel *c;
 	int sock, r, success = 0, on = 1, wildcard = 0, is_client;
 	struct addrinfo hints, *ai, *aitop;
@@ -2313,10 +2362,119 @@
 	}
 	if (success == 0)
 		error("channel_setup_fwd_listener: cannot listen to port: %d",
 		    listen_port);
 	freeaddrinfo(aitop);
+	return success;
+}
+
+/*
+ * Create and activate the Unix Domain Socket (UDS) to be used for
+ * forwarding.  The name of the UDS is in listen_addr.
+ */
+static int
+channel_setup_fwd_uds_listener(int type, const char *listen_addr,
+    u_short listen_port, const char *host, u_short port_to_connect)
+{
+	Channel *c;
+        struct sockaddr_un addr;
+        mode_t old_umask;
+	int   sock;
+        int   addr_len;
+	int   success = 0;
+	const char *uds_path = listen_addr;
+	struct stat stat_info;
+
+	/* File doesn't exist, create Unix Domain Socket */
+	if (uds_path != NULL && stat(uds_path, &stat_info) == -1) {
+		/* Format the address info */
+		memset(&addr, '\0', sizeof(addr));
+		addr.sun_family = AF_UNIX;
+		addr_len = offsetof(struct sockaddr_un, sun_path) +
+				strlen(uds_path) + 1;
+		if (strlcpy(addr.sun_path, uds_path, sizeof(addr.sun_path))
+			>= sizeof(addr.sun_path))
+			fatal("Forwarding Socket path too long");
+
+		/* Create the socket */
+		if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
+			fatal("%s socket(): %s\n", __func__, strerror(errno));
+
+		/* Bind the socket to the address. Note that the
+		 * permissions are set to be fairly restrictive.
+		 */
+		old_umask = umask(0177);
+		if (bind(sock, (struct sockaddr*)&addr, addr_len) == -1) {
+			sock = -1;
+			if (errno == EINVAL)
+				fatal("Forwarding Socket %s already exists",
+					uds_path);
+			else
+				fatal("%s bind(%s): %s\n", __func__,
+					addr.sun_path, strerror(errno));
+		}
+		umask(old_umask);
+
+		/* Start listening on the socket */
+		if (listen(sock, SSH_LISTEN_BACKLOG) == -1)
+			fatal("%s listen(): %s\n", __func__, strerror(errno));
+
+		set_nonblock(sock);
+
+		/* Allocate a channel number for the socket. */
+		c = channel_new("port listener", type, sock, sock, -1,
+		    CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT,
+		    0, "port listener", 1);
+		strlcpy(c->path, host, sizeof(c->path));
+		c->host_port = port_to_connect;
+		c->fwd_path = xstrdup(uds_path);
+		c->listening_port = listen_port;
+		success = 1;
+	}
+	else {
+		fatal("Forwarding Socket %s already exists", uds_path);
+	}
+
+	return(success);
+}
+
+/*
+ * Initiate the proper initialization procedure for setting up port
+ * forwarding based on the inputs.
+ */
+static int
+channel_setup_fwd_listener(int type, const char *listen_addr, u_short listen_port,
+    const char *host_to_connect, u_short port_to_connect, int gateway_ports)
+{
+	int        success = 0;
+	const char *host;
+
+	host = (type == SSH_CHANNEL_RPORT_LISTENER) ?
+	    listen_addr : host_to_connect;
+
+	if (host == NULL) {
+		error("No forward host name.");
+		return success;
+	}
+	if (strlen(host) > SSH_CHANNEL_PATH_LEN - 1) {
+		error("Forward host name too long.");
+		return success;
+	}
+	/* If this is a local listener and it is not a regular socket,
+	 * since the port=0, set up a Unix Domain Socket.
+	 */
+	if ((type != SSH_CHANNEL_RPORT_LISTENER) && !gateway_ports &&
+	    (listen_addr != NULL) && (listen_port == 0)) {
+		success = channel_setup_fwd_uds_listener(type, listen_addr,
+				listen_port, host, port_to_connect);
+	/* Otherwise, set up a regular socket. */
+	} else {
+		success = channel_setup_fwd_port_listener(type, listen_addr,
+				listen_port, host, port_to_connect,
+				gateway_ports);
+	}
+
 	return success;
 }
 
 int
 channel_cancel_rport_listener(const char *host, u_short port)
diff -N -U 5 -u openssh-4.2p1/channels.h sshop-4.2p1/channels.h
--- openssh-4.2p1/channels.h	Sun Jul 17 03:19:25 2005
+++ sshop-4.2p1/channels.h	Fri Oct 21 16:02:27 2005
@@ -90,10 +90,12 @@
 	char    path[SSH_CHANNEL_PATH_LEN];
 		/* path for unix domain sockets, or host name for forwards */
 	int     listening_port;	/* port being listened for forwards */
 	int     host_port;	/* remote port to connect for forwards */
 	char   *remote_name;	/* remote hostname */
+	char   *fwd_path;
+		/* path for unix domain sockets used for forwards */
 
 	u_int	remote_window;
 	u_int	remote_maxpacket;
 	u_int	local_window;
 	u_int	local_window_max;
diff -N -U 5 -u openssh-4.2p1/clientloop.c sshop-4.2p1/clientloop.c
--- openssh-4.2p1/clientloop.c	Sun Jul 17 03:02:10 2005
+++ sshop-4.2p1/clientloop.c	Fri Oct 21 16:02:27 2005
@@ -897,11 +897,11 @@
 	u_short cancel_port;
 	Forward fwd;
 
 	leave_raw_mode();
 	handler = signal(SIGINT, SIG_IGN);
-	cmd = s = read_passphrase("\r\nssh> ", RP_ECHO);
+	cmd = s = read_passphrase("\r\nssh> ", RP_ECHO | RP_ADD_ID);
 	if (s == NULL)
 		goto out;
 	while (*s && isspace(*s))
 		s++;
 	if (*s == '-')
Common subdirectories: openssh-4.2p1/contrib and sshop-4.2p1/contrib
diff -N -U 5 -u openssh-4.2p1/misc.c sshop-4.2p1/misc.c
--- openssh-4.2p1/misc.c	Thu Jul 14 03:05:02 2005
+++ sshop-4.2p1/misc.c	Fri Oct 21 16:02:28 2005
@@ -27,10 +27,11 @@
 RCSID("$OpenBSD: misc.c,v 1.34 2005/07/08 09:26:18 dtucker Exp $");
 
 #include "misc.h"
 #include "log.h"
 #include "xmalloc.h"
+#include "pathnames.h"
 
 /* remove newline at end of string */
 char *
 chop(char *s)
 {
@@ -305,10 +306,11 @@
 		*cp = NULL;	/* no more fields*/
 		break;
 
 	case ':':
 	case '/':
+	case ',':
 		*s = '\0';	/* terminate */
 		*cp = s + 1;
 		break;
 
 	default:
@@ -519,7 +521,86 @@
 	for (i = 0; i < l; i++) {
 		snprintf(b, sizeof(b), "%02x", d[i]);
 		strlcat(r, b, hl);
 	}
 	return (r);
+}
+
+/*
+ * Given information about the local side and remote side of the connection,
+ * construct a path for a UNIX Domain Socket to be used for the communication.
+ * If no directory is specified, the socket path will start in the user's
+ * .ssh directory.  The path has the structure:
+ *	<dir>/.ssh_<uniqueid>_<uid>_<host_user>@<host>_<host_port>
+ *	<dir>/.ssh_<uniqueid>_<uid>_<host>_<host_port>
+ * The second variant is only used if the user can't be established.
+ * Note that for Unix Domain Sockets, buffer should be set to
+ * &sockadder_un->sun_path to prevent problems later.
+ *
+ * The result is placed into buffer. Upon success the length of the path
+ * name is returned. Otherwise, -1 is returned.
+ */
+int
+construct_uds_path(const char *dirpath, const char *host, u_short uniqueid,
+    char **host_user, u_short host_port, char *buffer, int buffer_size)
+{
+	uid_t  uid;
+	struct passwd *pw = NULL;
+	char  *local_user = NULL,
+	      *remote_user = NULL;
+	char  *buf = buffer;
+	int   len = 0;
+
+	/* Problem with the inputs */
+	if ((buffer == NULL) || (buffer_size < 32) || (host == NULL))
+		return -1;
+
+	/* Get the identity of the local user */
+	uid = getuid();
+
+	/* Get the password entry for this user */
+	pw = getpwuid(uid);
+
+	/* Identify the remote user */
+	if ((host_user != NULL) && (*host_user != NULL)) {
+		remote_user = *host_user;
+	/* No remote user specified, assume same as local user */
+	} else if (pw) {
+		*host_user = xstrdup(pw->pw_name);
+		remote_user = *host_user;
+	}
+
+	/* Construct a path name */
+	/* First determine the directory it should be in */
+	memset(buffer, '\0', buffer_size);
+	if (dirpath != NULL) {
+		snprintf(buffer, buffer_size, "%.100s", dirpath);
+	} else if (pw) {
+		snprintf(buffer, buffer_size, "%.100s%s%.100s",
+		    pw->pw_dir,
+		    strcmp(pw->pw_dir, "/") ? "/" : "", _PATH_SSH_USER_DIR);
+	} else {
+		fatal("construct_uds_path: no uds directory specified and can't get local user name");
+		/*
+		 * Another option would be to use /tmp, but could have
+		 * problems with protecting access to the socket file
+		 * so we won't do that.
+		 */
+	}
+	/* Then construct the name */
+	len = strlen(buffer);
+	buf = buffer + len;
+	buffer_size -= len;
+	if (buffer_size < 12) {
+		fatal("construct_uds_path: buffer too small for pathname");
+	} else if (remote_user != NULL) {
+		snprintf(buf, buffer_size, "/.ssh_%d_%d_%s@%s_%d",
+		    uniqueid, uid, remote_user, host, host_port);
+	} else {
+		snprintf(buf, buffer_size, "/.ssh_%d_%d_%s_%d",
+		    uniqueid, uid, host, host_port);
+	}
+
+	endpwent();
+	return strlen(buffer);
 }
 
diff -N -U 5 -u openssh-4.2p1/misc.h sshop-4.2p1/misc.h
--- openssh-4.2p1/misc.h	Thu Jul 14 03:07:21 2005
+++ sshop-4.2p1/misc.h	Fri Oct 21 16:02:28 2005
@@ -25,10 +25,12 @@
 char	*colon(char *);
 long	 convtime(const char *);
 char	*tilde_expand_filename(const char *, uid_t);
 char	*percent_expand(const char *, ...) __attribute__((__sentinel__));
 char	*tohex(const u_char *, u_int);
+int	 construct_uds_path(const char *, const char *, u_short,
+		char **, u_short, char *, int);
 
 struct passwd *pwcopy(struct passwd *);
 
 typedef struct arglist arglist;
 struct arglist {
@@ -42,9 +44,10 @@
 
 #define RP_ECHO			0x0001
 #define RP_ALLOW_STDIN		0x0002
 #define RP_ALLOW_EOF		0x0004
 #define RP_USE_ASKPASS		0x0008
+#define RP_ADD_ID		0x0010
 
 char	*read_passphrase(const char *, int);
 int	 ask_permission(const char *, ...) __attribute__((format(printf, 1, 2)));
 int	 read_keyfile_line(FILE *, const char *, char *, size_t, u_long *);
Common subdirectories: openssh-4.2p1/openbsd-compat and sshop-4.2p1/openbsd-compat
diff -N -U 5 -u openssh-4.2p1/readconf.c sshop-4.2p1/readconf.c
--- openssh-4.2p1/readconf.c	Fri Aug 12 08:11:18 2005
+++ sshop-4.2p1/readconf.c	Fri Oct 21 16:02:29 2005
@@ -105,10 +105,11 @@
 	oClearAllForwardings, oNoHostAuthenticationForLocalhost,
 	oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout,
 	oAddressFamily, oGssAuthentication, oGssDelegateCreds,
 	oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly,
 	oSendEnv, oControlPath, oControlMaster, oHashKnownHosts,
+	oPrivateLocalForward, oUnixDomainSocket,
 	oDeprecated, oUnsupported
 } OpCodes;
 
 /* Textual representations of the tokens. */
 
@@ -196,10 +197,12 @@
 	{ "serveralivecountmax", oServerAliveCountMax },
 	{ "sendenv", oSendEnv },
 	{ "controlpath", oControlPath },
 	{ "controlmaster", oControlMaster },
 	{ "hashknownhosts", oHashKnownHosts },
+	{ "privatelocalforward", oPrivateLocalForward },
+	{ "unixdomainsocket", oUnixDomainSocket },
 	{ NULL, oBadOption }
 };
 
 /*
  * Adds a local TCP/IP port forward to options.  Never returns if there is an
@@ -210,11 +213,12 @@
 add_local_forward(Options *options, const Forward *newfwd)
 {
 	Forward *fwd;
 #ifndef NO_IPPORT_RESERVED_CONCEPT
 	extern uid_t original_real_uid;
-	if (newfwd->listen_port < IPPORT_RESERVED && original_real_uid != 0)
+	if ((newfwd->listen_port > 0) &&
+	    (newfwd->listen_port < IPPORT_RESERVED) && original_real_uid != 0)
 		fatal("Privileged ports can only be forwarded by root.");
 #endif
 	if (options->num_local_forwards >= SSH_MAX_FORWARDS_PER_DIRECTION)
 		fatal("Too many local forwards (max %d).", SSH_MAX_FORWARDS_PER_DIRECTION);
 	fwd = &options->local_forwards[options->num_local_forwards++];
@@ -294,10 +298,13 @@
 process_config_line(Options *options, const char *host,
 		    char *line, const char *filename, int linenum,
 		    int *activep)
 {
 	char *s, **charptr, *endofnumber, *keyword, *arg, *arg2, fwdarg[256];
+	u_short fwd_host_port;
+	char sfwd_host_port[6];
+	char sfwd_uds_path[1024];
 	int opcode, *intptr, value;
 	size_t len;
 	Forward fwd;
 
 	/* Strip trailing whitespace */
@@ -702,10 +709,34 @@
 			    filename, linenum);
 		if (*activep)
 			add_local_forward(options, &fwd);
 		break;
 
+	case oPrivateLocalForward:
+		arg = strdelim(&s);
+		memset(&fwd, '\0', sizeof(fwd));
+		/* parse the input */
+		if (sscanf(arg, "%1023[^,],%255[^,],%5[0123456789]",
+		    sfwd_uds_path, fwdarg, sfwd_host_port) < 3) {
+			fatal("%.200s line %d: Bad domain socket specification.",
+			    filename, linenum);
+		}
+
+		/* check the port info */
+		if ((fwd_host_port = a2port(sfwd_host_port)) == 0) {
+			fatal("%.200s line %d: Remote port not defined.",
+			    filename, linenum);
+		}
+		fwd.listen_host  = xstrdup(sfwd_uds_path);
+		fwd.listen_port  = 0;
+		fwd.connect_host = xstrdup(fwdarg);
+		fwd.connect_host = cleanhostname(fwd.connect_host);
+		fwd.connect_port = fwd_host_port;
+		if (*activep)
+			add_local_forward(options, &fwd);
+		break;
+
 	case oClearAllForwardings:
 		intptr = &options->clear_forwardings;
 		goto parse_flag;
 
 	case oHost:
@@ -820,10 +851,14 @@
 
 	case oHashKnownHosts:
 		intptr = &options->hash_known_hosts;
 		goto parse_flag;
 
+	case oUnixDomainSocket:
+		charptr = &options->uds_path;
+		goto parse_string;
+
 	case oDeprecated:
 		debug("%s line %d: Deprecated option \"%s\"",
 		    filename, linenum, keyword);
 		return 0;
 
@@ -964,10 +999,11 @@
 	options->server_alive_count_max = -1;
 	options->num_send_env = 0;
 	options->control_path = NULL;
 	options->control_master = -1;
 	options->hash_known_hosts = -1;
+	options->uds_path = NULL;
 }
 
 /*
  * Called after processing other sources of option data, this fills those
  * options for which no value has been specified with their default values.
@@ -1093,10 +1129,11 @@
 	/* options->proxy_command should not be set by default */
 	/* options->user will be set in the main program if appropriate */
 	/* options->hostname will be set in the main program if appropriate */
 	/* options->host_key_alias should not be set by default */
 	/* options->preferred_authentications will be set in ssh */
+	/* options->uds_path should not be set by default */
 }
 
 /*
  * parse_forward
  * parses a string containing a port forwarding specification of the form:
diff -N -U 5 -u openssh-4.2p1/readconf.h sshop-4.2p1/readconf.h
--- openssh-4.2p1/readconf.h	Wed Jun 15 23:19:42 2005
+++ sshop-4.2p1/readconf.h	Fri Oct 21 16:02:29 2005
@@ -20,11 +20,12 @@
 
 /* Data structure for representing a forwarding request. */
 
 typedef struct {
 	char	 *listen_host;		/* Host (address) to listen on. */
-	u_short	  listen_port;		/* Port to forward. */
+	u_short	  listen_port;		/* Port to forward.  if 0, listen_host
+					 * is a unix domain socket. */
 	char	 *connect_host;		/* Host to connect. */
 	u_short	  connect_port;		/* Port to connect on connect_host. */
 }       Forward;
 /* Data structure for representing option data. */
 
@@ -112,10 +113,13 @@
 
 	char	*control_path;
 	int	control_master;
 
 	int	hash_known_hosts;
+
+	char	*uds_path;	/* Path for unix domain socket to connect to
+				 * (used in place of port). */
 }       Options;
 
 #define SSHCTL_MASTER_NO	0
 #define SSHCTL_MASTER_YES	1
 #define SSHCTL_MASTER_AUTO	2
diff -N -U 5 -u openssh-4.2p1/readpass.c sshop-4.2p1/readpass.c
--- openssh-4.2p1/readpass.c	Wed May 25 22:07:48 2005
+++ sshop-4.2p1/readpass.c	Fri Oct 21 16:02:29 2005
@@ -89,20 +89,76 @@
 	memset(buf, 0, sizeof(buf));
 	return pass;
 }
 
 /*
+ * Function to prepend the process id to every line in a prompt.
+ */
+void
+add_id_to_prompt(const char *prompt, char *newprompt, int newprompt_size)
+{
+	char  *np;
+	const char *p = prompt;
+	int  len, cnt;
+	int  pid = getpid();
+
+	if (newprompt == NULL)
+		return;
+
+	np = newprompt;
+	len = newprompt_size;
+
+	while (p != NULL) {
+		cnt = snprintf(np, len, "[%d] ", pid);
+
+		/* Able to write the entire label */
+		if (cnt < len) {
+			len -= cnt;
+			np  += cnt;
+		/* Out of space */
+		}  else {
+			np += len;
+			len = 0;
+		}
+		/* Copy characters until out of space, or an
+		* EOL or newline is found.
+		*/
+		for (; *p != '\0' && *p != '\n' && len > 0;
+		   p++, np++, len--) {
+			*np = *p;
+		}
+		/* Out of space */
+		if (len == 0) {
+			*(np-1) = '\0';
+			p = NULL;
+		/* Copy the EOL or newline. */
+		} else {
+			*np = *p;
+			/* EOL, terminate the loop */
+			if (*p == '\0') {
+				p = NULL;
+			/* Otherwise, move to next character */
+			} else {
+				p++;
+				np++;
+			}
+		}
+	}
+}
+
+/*
  * Reads a passphrase from /dev/tty with echo turned off/on.  Returns the
  * passphrase (allocated with xmalloc).  Exits if EOF is encountered. If
  * RP_ALLOW_STDIN is set, the passphrase will be read from stdin if no
  * tty is available
  */
 char *
 read_passphrase(const char *prompt, int flags)
 {
 	char *askpass = NULL, *ret, buf[1024];
-	int rppflags, use_askpass = 0, ttyfd;
+	int rppflags, use_askpass = 0, ttyfd, len;
+	char newprompt[1024];
 
 	rppflags = (flags & RP_ECHO) ? RPP_ECHO_ON : RPP_ECHO_OFF;
 	if (flags & RP_USE_ASKPASS)
 		use_askpass = 1;
 	else if (flags & RP_ALLOW_STDIN) {
@@ -119,26 +175,35 @@
 			debug("read_passphrase: can't open %s: %s", _PATH_TTY,
 			    strerror(errno));
 			use_askpass = 1;
 		}
 	}
+	/* Prepend the process id to each line in the prompt */
+	if ((flags & RP_ADD_ID)) {
+		add_id_to_prompt(prompt, newprompt, sizeof(newprompt));
+	/* Nothing to be added to the prompt, use the original */
+	} else {
+		len = sizeof(newprompt);
+		strncpy(newprompt, prompt, len);
+		newprompt[len-1] = '\0';
+	}
 
 	if ((flags & RP_USE_ASKPASS) && getenv("DISPLAY") == NULL)
 		return (flags & RP_ALLOW_EOF) ? NULL : xstrdup("");
 
 	if (use_askpass && getenv("DISPLAY")) {
 		if (getenv(SSH_ASKPASS_ENV))
 			askpass = getenv(SSH_ASKPASS_ENV);
 		else
 			askpass = _PATH_SSH_ASKPASS_DEFAULT;
-		if ((ret = ssh_askpass(askpass, prompt)) == NULL)
+		if ((ret = ssh_askpass(askpass, newprompt)) == NULL)
 			if (!(flags & RP_ALLOW_EOF))
 				return xstrdup("");
 		return ret;
 	}
 
-	if (readpassphrase(prompt, buf, sizeof buf, rppflags) == NULL) {
+	if (readpassphrase(newprompt, buf, sizeof buf, rppflags) == NULL) {
 		if (flags & RP_ALLOW_EOF)
 			return NULL;
 		return xstrdup("");
 	}
 
Common subdirectories: openssh-4.2p1/regress and sshop-4.2p1/regress
Common subdirectories: openssh-4.2p1/scard and sshop-4.2p1/scard
diff -N -U 5 -u openssh-4.2p1/scard.c sshop-4.2p1/scard.c
--- openssh-4.2p1/scard.c	Thu May 13 02:15:48 2004
+++ sshop-4.2p1/scard.c	Wed May 11 17:54:01 2005
@@ -429,11 +429,11 @@
 static int
 get_AUT0(u_char *aut0)
 {
 	char *pass;
 
-	pass = read_passphrase("Enter passphrase for smartcard: ", RP_ALLOW_STDIN);
+	pass = read_passphrase("Enter passphrase for smartcard: ", RP_ALLOW_STDIN|RP_ADD_ID);
 	if (pass == NULL)
 		return -1;
 	if (!strcmp(pass, "-")) {
 		memcpy(aut0, DEFAUT0, sizeof DEFAUT0);
 		return 0;
diff -N -U 5 -u openssh-4.2p1/ssh.1 sshop-4.2p1/ssh.1
--- openssh-4.2p1/ssh.1	Thu Jul 14 03:04:18 2005
+++ sshop-4.2p1/ssh.1	Fri Oct 21 16:02:31 2005
@@ -66,13 +66,25 @@
 .Sm off
 .Oo Ar bind_address : Oc
 .Ar port : host : hostport
 .Sm on
 .Oc
+.Op Fl u Ar socket_path
+.Oo Fl U\ \&
+.Sm off
+.Ar socket_path : host : hostport
+.Sm on
+.Oc
 .Op Fl S Ar ctl_path
 .Oo Ar user Ns @ Oc Ns Ar hostname
 .Op Ar command
+.Nm ssh
+.Op Ar ssh_options
+.Op Fl H Oo Ar ssh_options Oc Oo Ar user Ns @ Oc Ns Ar remote_host
+.Op Ar ...
+.Fl H Oo Ar ssh_options Oc Oo Ar user Ns @ Oc Ns Ar remote_host
+.Op Ar command
 .Ek
 .Sh DESCRIPTION
 .Nm
 (SSH client) is a program for logging into a remote machine and for
 executing commands on a remote machine.
@@ -542,10 +554,20 @@
 The recommended way to start X11 programs at a remote site is with
 something like
 .Ic ssh -f host xterm .
 .It Fl g
 Allows remote hosts to connect to local forwarded ports.
+.It Fl H Xo
+.Oo Ar ssh_options Oc
+.Oo Ar user Ns @ Oc Ns Ar remote_host
+.Xc
+Specifies a remote host in a SSH hop chain.
+SSH options specific to that host can be provided following the H.
+For example,
+.Fl p
+can be used to specify a non-standard SSH port number
+on the remote host.
 .It Fl I Ar smartcard_device
 Specifies which smartcard device to use.
 The argument is the device
 .Nm
 should use to communicate with a smartcard used for storing the user's
@@ -716,10 +738,11 @@
 .It NumberOfPasswordPrompts
 .It PasswordAuthentication
 .It Port
 .It PreferredAuthentications
 .It Protocol
+.It PrivateLocalForward
 .It ProxyCommand
 .It PubkeyAuthentication
 .It RemoteForward
 .It RhostsRSAAuthentication
 .It RSAAuthentication
@@ -727,10 +750,11 @@
 .It ServerAliveInterval
 .It ServerAliveCountMax
 .It SmartcardDevice
 .It StrictHostKeyChecking
 .It TCPKeepAlive
+.It UnixDomainSocket
 .It UsePrivilegedPort
 .It User
 .It UserKnownHostsFile
 .It VerifyHostKeyDNS
 .It XAuthLocation
@@ -813,10 +837,30 @@
 Multiple
 .Fl t
 options force tty allocation, even if
 .Nm
 has no local tty.
+.It Fl U Xo
+.Sm off
+.Ar socket_path , host , hostport
+.Sm on
+.Xc
+Specifies that the given Unix Domain Socket on the local (client) host is to be
+forwarded to the given host and port on the remote side.
+This works by allocating a socket to listen to
+.Ar socket_path
+on the local side
+Whenever a connection is made to this socket, the
+connection is forwarded over the secure channel, and a connection is
+made to
+.Ar host
+port
+.Ar hostport
+from the remote machine.
+These forwardings can also be specified in the configuration file.
+.It Fl u Ar socket_path
+Path to a Unix Domain Socket being used for local forwarding.
 .It Fl V
 Display the version number and exit.
 .It Fl v
 Verbose mode.
 Causes
diff -N -U 5 -u openssh-4.2p1/ssh.c sshop-4.2p1/ssh.c
--- openssh-4.2p1/ssh.c	Fri Aug 12 08:10:56 2005
+++ sshop-4.2p1/ssh.c	Fri Oct 21 16:02:31 2005
@@ -70,180 +70,147 @@
 #include "sshpty.h"
 #include "match.h"
 #include "msg.h"
 #include "monitor_fdpass.h"
 #include "uidswap.h"
+#include "sshhop.h"
 
 #ifdef SMARTCARD
 #include "scard.h"
 #endif
 
 extern char *__progname;
 
-/* Flag indicating whether debug mode is on.  This can be set on the command line. */
+/* Flag indicating whether debug mode is on.  This can be set on the
+ * command line. This variable is exported (global).
+ */
 int debug_flag = 0;
 
 /* Flag indicating whether a tty should be allocated */
-int tty_flag = 0;
-int no_tty_flag = 0;
-int force_tty_flag = 0;
+static int tty_flag = 0;
+static int no_tty_flag = 0;
+static int force_tty_flag = 0;
 
-/* don't exec a shell */
+/* don't exec a shell. This is an exported variable (global). */
 int no_shell_flag = 0;
 
 /*
  * Flag indicating that nothing should be read from stdin.  This can be set
- * on the command line.
+ * on the command line. This variable is exported (global).
  */
 int stdin_null_flag = 0;
 
 /*
  * Flag indicating that ssh should fork after authentication.  This is useful
  * so that the passphrase can be entered manually, and then ssh goes to the
  * background.
  */
-int fork_after_authentication_flag = 0;
+static int fork_after_authentication_flag = 0;
 
 /*
  * General data structure for command line options and options configurable
- * in configuration files.  See readconf.h.
+ * in configuration files.  See readconf.h. This variable is exported
+ * (global).
  */
 Options options;
 
 /* optional user configfile */
-char *config = NULL;
+static char *config = NULL;
 
 /*
  * Name of the host we are connecting to.  This is the name given on the
  * command line, or the HostName specified for the user-supplied name in a
- * configuration file.
+ * configuration file. This variable is exported (global).
  */
 char *host;
 
 /* socket address the host resolves to */
-struct sockaddr_storage hostaddr;
+static struct sockaddr_storage hostaddr;
 
 /* Private host keys. */
-Sensitive sensitive_data;
+static Sensitive sensitive_data;
 
-/* Original real UID. */
+/* Original real UID. These variables are exported (global) */
 uid_t original_real_uid;
 uid_t original_effective_uid;
 
 /* command to be executed */
-Buffer command;
+static Buffer command;
 
 /* Should we execute a command or invoke a subsystem? */
-int subsystem_flag = 0;
+static int subsystem_flag = 0;
 
 /* # of replies received for global requests */
 static int client_global_request_id = 0;
 
-/* pid of proxycommand child process */
+/* pid of proxycommand child process. This variable is exported (global) */
 pid_t proxy_command_pid = 0;
 
-/* fd to control socket */
+/* fd to control socket. This variable is exported (global). */
 int control_fd = -1;
 
 /* Multiplexing control command */
 static u_int mux_command = 0;
 
 /* Only used in control client mode */
 volatile sig_atomic_t control_client_terminate = 0;
-u_int control_server_pid = 0;
+static u_int control_server_pid = 0;
 
 /* Prints a help message to the user.  This function never returns. */
-
 static void
 usage(void)
 {
 	fprintf(stderr,
+	/* Standard SSH command line syntax */
 "usage: ssh [-1246AaCfgkMNnqsTtVvXxY] [-b bind_address] [-c cipher_spec]\n"
 "           [-D port] [-e escape_char] [-F configfile]\n"
 "           [-i identity_file] [-L [bind_address:]port:host:hostport]\n"
 "           [-l login_name] [-m mac_spec] [-O ctl_cmd] [-o option] [-p port]\n"
 "           [-R [bind_address:]port:host:hostport] [-S ctl_path]\n"
 "           [user@]hostname [command]\n"
+"    or\n"
+	/* SSH hop command line syntax.
+	 * Command line syntax for automatically creating a chain of SSH
+	 * processes to securely SSH from this host to the destination host
+	 * through intermediary hosts. Each host in this chain represents a
+	 * single "hop". The authentication credentials are only revealed
+	 * at this host and at the host for which those credentials are
+	 * intended.
+	 */
+"       ssh [ssh_options] -H [ssh_options] [-p port1] [user1@]host1\n"
+"                     ... -H [ssh_options] [-p portn] [usern@]hostn\n"
+"           [command]\n"
 	);
 	exit(1);
 }
 
 static int ssh_session(void);
 static int ssh_session2(void);
 static void load_public_identity_files(void);
 static void control_client(const char *path);
 
-/*
- * Main program for the ssh client.
- */
 int
-main(int ac, char **av)
+parse_options(int ac, char **av)
 {
-	int i, opt, exit_status;
+	int opt;
+	u_short fwd_host_port;
+	char sfwd_host_port[6];
+	char sfwd_uds_path[1024];
 	char *p, *cp, *line, buf[256];
 	struct stat st;
-	struct passwd *pw;
 	int dummy;
 	extern int optind, optreset;
 	extern char *optarg;
-	struct servent *sp;
 	Forward fwd;
-
-	__progname = ssh_get_progname(av[0]);
-	init_rng();
-
-	/*
-	 * Save the original real uid.  It will be needed later (uid-swapping
-	 * may clobber the real uid).
-	 */
-	original_real_uid = getuid();
-	original_effective_uid = geteuid();
-
-	/*
-	 * Use uid-swapping to give up root privileges for the duration of
-	 * option processing.  We will re-instantiate the rights when we are
-	 * ready to create the privileged port, and will permanently drop
-	 * them when the port has been created (actually, when the connection
-	 * has been made, as we may need to create the port several times).
-	 */
-	PRIV_END;
-
-#ifdef HAVE_SETRLIMIT
-	/* If we are installed setuid root be careful to not drop core. */
-	if (original_real_uid != original_effective_uid) {
-		struct rlimit rlim;
-		rlim.rlim_cur = rlim.rlim_max = 0;
-		if (setrlimit(RLIMIT_CORE, &rlim) < 0)
-			fatal("setrlimit failed: %.100s", strerror(errno));
-	}
-#endif
-	/* Get user data. */
-	pw = getpwuid(original_real_uid);
-	if (!pw) {
-		logit("You don't exist, go away!");
-		exit(1);
-	}
-	/* Take a copy of the returned structure. */
-	pw = pwcopy(pw);
-
-	/*
-	 * Set our umask to something reasonable, as some files are created
-	 * with the default umask.  This will make them world-readable but
-	 * writable only by the owner, which is ok for all files for which we
-	 * don't set the modes explicitly.
-	 */
-	umask(022);
-
-	/* Initialize option structure to indicate that no values have been set. */
-	initialize_options(&options);
+	optind = 1;
+	optreset = 1;
 
 	/* Parse command-line arguments. */
 	host = NULL;
 
 again:
-	while ((opt = getopt(ac, av,
-	    "1246ab:c:e:fgi:kl:m:no:p:qstvxACD:F:I:L:MNO:PR:S:TVXY")) != -1) {
+	while ((opt = getopt(ac, av, SSH_CMDLINE_OPTIONS)) != -1) {
 		switch (opt) {
 		case '1':
 			options.protocol = SSH_PROTO_1;
 			break;
 		case '2':
@@ -481,10 +448,41 @@
 			options.bind_address = optarg;
 			break;
 		case 'F':
 			config = optarg;
 			break;
+		case 'u':
+			if (options.uds_path != NULL) {
+				free(options.uds_path);
+			}
+			options.uds_path = xstrdup(optarg);
+			break;
+		case 'U':
+			memset(&fwd, '\0', sizeof(fwd));
+			/* parse the input */
+			if (sscanf(optarg, "%1023[^,],%255[^,],%5[0123456789]",
+			    sfwd_uds_path, buf, sfwd_host_port) < 3) {
+				fprintf(stderr,
+				    "Bad domain socket specification '%s'\n",
+				    optarg);
+				usage();
+				/* NOTREACHED */
+			}
+
+			/* check the port info */
+			if ((fwd_host_port = a2port(sfwd_host_port)) == 0) {
+				fprintf(stderr,
+				    "Remote port not defined.\n");
+				usage();
+			}
+			fwd.listen_host  = xstrdup(sfwd_uds_path);
+			fwd.listen_port  = 0;
+			fwd.connect_host = xstrdup(buf);
+			fwd.connect_host = cleanhostname(fwd.connect_host);
+			fwd.connect_port = fwd_host_port;
+			add_local_forward(&options, &fwd);
+			break;
 		default:
 			usage();
 		}
 	}
 
@@ -511,10 +509,146 @@
 
 	/* Check that we got a host name. */
 	if (!host)
 		usage();
 
+	/* Check for consistency between uds_path and host name */
+	if (options.uds_path != NULL && strcmp(host, "localhost") != 0) {
+		char hostname[MAXHOSTNAMELEN];
+		memset(hostname, '\0', sizeof(hostname));
+		if (gethostname(hostname, sizeof(hostname)) != 0 ||
+		    strcmp(host, hostname) != 0) {
+			fprintf(stderr,
+			"Can't use Unix Domain Sockets on a remote host\n");
+			exit(1);
+		}
+	}
+	return(ac);
+}
+
+/*
+ * Main program for the ssh client.
+ */
+int
+main(int ac, char **av)
+{
+	int i, exit_status, hops;
+	char *p, *cp, buf[256];
+	struct stat st;
+	struct passwd *pw;
+	struct servent *sp;
+
+	__progname = ssh_get_progname(av[0]);
+	init_rng();
+
+	/*
+	 * Save the original real uid.  It will be needed later (uid-swapping
+	 * may clobber the real uid).
+	 */
+	original_real_uid = getuid();
+	original_effective_uid = geteuid();
+
+	/*
+	 * Use uid-swapping to give up root privileges for the duration of
+	 * option processing.  We will re-instantiate the rights when we are
+	 * ready to create the privileged port, and will permanently drop
+	 * them when the port has been created (actually, when the connection
+	 * has been made, as we may need to create the port several times).
+	 */
+	PRIV_END;
+
+#ifdef HAVE_SETRLIMIT
+	/* If we are installed setuid root be careful to not drop core. */
+	if (original_real_uid != original_effective_uid) {
+		struct rlimit rlim;
+		rlim.rlim_cur = rlim.rlim_max = 0;
+		if (setrlimit(RLIMIT_CORE, &rlim) < 0)
+			fatal("setrlimit failed: %.100s", strerror(errno));
+	}
+#endif
+	/* Get user data. */
+	pw = getpwuid(original_real_uid);
+	if (!pw) {
+		logit("You don't exist, go away!");
+		exit(1);
+	}
+	/* Take a copy of the returned structure. */
+	pw = pwcopy(pw);
+
+	/*
+	 * Set our umask to something reasonable, as some files are created
+	 * with the default umask.  This will make them world-readable but
+	 * writable only by the owner, which is ok for all files for which we
+	 * don't set the modes explicitly.
+	 */
+	umask(022);
+
+	/* Initialize option structure to indicate that no values have been set. */
+	initialize_options(&options);
+
+	/* Check for hops indicated with the -H option on the command line.
+	 * Each hop is a connection from this host to the next host in
+	 * the chain of hosts. The chain extends from this host to the
+	 * final destination through a string of intermediary hosts.
+	 */
+	hops = sshop_parse_options(ac, av, usage);
+
+	/* If either no -H options or only one -H option then no hopping
+	 * is needed. Free any memory allocated during hop processing and
+	 * then continue with the standard processing.
+	 */
+	if (hops <= 1) {
+		sshop_cleanup();
+		/* Continue below */
+	} else {
+	/*
+	 * Otherwise, multiple -H options (hops) indicated; process the hops.
+	 * Since a separate ssh process is invoked for each hop, we will
+	 * exit when hop processing is completed and not continue with the
+	 * standard ssh session processing.
+	 */
+		int error;
+		/*
+		 * Initialize "log" output.  Since we are the client, all output
+		 * actually goes to stderr.
+		 */
+		log_init(av[0],
+		    options.log_level == -1 ? SYSLOG_LEVEL_INFO : options.log_level,
+		    SYSLOG_FACILITY_USER, 1);
+
+		/* Make sure the user name is set for all hops */
+		sshop_set_default_user(pw->pw_name);
+
+		/* Build the chain of SSH subprocesses to implement the
+		 * secured connection from one host to another through
+		 * intermediate hosts. This involves processing command
+		 * line options and invoking SSH for each hop.
+		 * None of the processes in this chain are shared with other
+		 * hop chains. Thus, the processes will all need to be 
+		 * terminated when the session ends. */
+		error = sshop_build_chain(av[0]);
+
+		/* Hop chain established successfully, wait to clean up
+		 * intermediate processes when session is completed.
+		 */
+		if (error == 0) {
+			sshop_wait_for_exit();
+		} else {
+		/* The SSH subprocesses were not started properly, shut
+		 * everything down.
+		 */
+			sshop_kill_subprocesses();
+			sshop_wait_for_exit();
+		}
+		/* Make sure we don't fall into regular SSH processing */
+		exit(0);
+		/* Exit here */
+	}
+
+	/* Parse command-line arguments. */
+	ac = parse_options(ac, av);
+
 	SSLeay_add_all_algorithms();
 	ERR_load_crypto_strings();
 
 	/* Initialize the command to execute on remote host. */
 	buffer_init(&command);
@@ -614,10 +748,16 @@
 	}
 
 	if (options.proxy_command != NULL &&
 	    strcmp(options.proxy_command, "none") == 0)
 		options.proxy_command = NULL;
+
+	if (options.uds_path != NULL) {
+		options.uds_path = tilde_expand_filename(
+		   options.uds_path, original_real_uid);
+	}
+
 	if (options.control_path != NULL &&
 	    strcmp(options.control_path, "none") == 0)
 		options.control_path = NULL;
 
 	if (options.control_path != NULL) {
@@ -631,20 +771,28 @@
 	if (mux_command != 0 && options.control_path == NULL)
 		fatal("No ControlPath specified for \"-O\" command");
 	if (options.control_path != NULL)
 		control_client(options.control_path);
 
-	/* Open a connection to the remote host. */
-	if (ssh_connect(host, &hostaddr, options.port,
-	    options.address_family, options.connection_attempts,
+	/* Open a connection to the remote host. We need to use different
+	 * connection algorithms, depending on whether we are using regular
+	 * sockets or Unix Domain Sockets (UDS).
+	 */
+	if (options.uds_path == NULL) {
+	    if (ssh_connect(host, &hostaddr, options.port,
+	        options.address_family, options.connection_attempts,
 #ifdef HAVE_CYGWIN
-	    options.use_privileged_port,
+	        options.use_privileged_port,
 #else
-	    original_effective_uid == 0 && options.use_privileged_port,
+	        original_effective_uid == 0 && options.use_privileged_port,
 #endif
-	    options.proxy_command) != 0)
-		exit(1);
+	        options.proxy_command) != 0)
+		    exit(1);
+	} else {
+		if(ssh_connect_uds(options.uds_path, &hostaddr) != 0)
+			exit(1);
+	}
 
 	/*
 	 * If we successfully made the connection, load the host private key
 	 * in case we will need it later for combined rsa-rhosts
 	 * authentication. This must be done before releasing extra
diff -N -U 5 -u openssh-4.2p1/ssh.h sshop-4.2p1/ssh.h
--- openssh-4.2p1/ssh.h	Mon Dec  6 06:47:42 2004
+++ sshop-4.2p1/ssh.h	Tue May 10 14:36:17 2005
@@ -111,6 +111,7 @@
 #define SSH_RSA_MINIMUM_MODULUS_SIZE	768
 
 /* Listen backlog for sshd, ssh-agent and forwarding sockets */
 #define SSH_LISTEN_BACKLOG		128
 
+#define SSH_CMDLINE_OPTIONS	"1246ab:c:e:fgi:kl:m:no:p:qstu:vxACD:F:HI:L:MNO:PR:S:TU:VXY"
 #endif				/* SSH_H */
diff -N -U 5 -u openssh-4.2p1/sshconnect.c sshop-4.2p1/sshconnect.c
--- openssh-4.2p1/sshconnect.c	Sun Jul 17 03:22:46 2005
+++ sshop-4.2p1/sshconnect.c	Fri Oct 21 16:02:31 2005
@@ -383,10 +383,47 @@
 	packet_set_connection(sock, sock);
 
 	return 0;
 }
 
+int
+ssh_connect_uds(const char *uds_path, struct sockaddr_storage * hostaddr)
+{
+	struct sockaddr_un addr;
+	int sock, addr_len;
+
+	if (uds_path == NULL)
+		return -1;
+	else if (strlen(uds_path) == 0)
+		return -1;
+
+	memset(&addr, '\0', sizeof(addr));
+	addr.sun_family = AF_UNIX;
+	addr_len = offsetof(struct sockaddr_un, sun_path) +
+	    strlen(uds_path) + 1;
+
+	if (strlcpy(addr.sun_path, uds_path,
+	    sizeof(addr.sun_path)) >= sizeof(addr.sun_path))
+		fatal("Path for socket too long");
+
+	if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
+		fatal("%s socket(): %s", __func__, strerror(errno));
+
+	/* Return failure if we didn't get a successful connection. */
+	if (connect(sock, (struct sockaddr*)&addr, addr_len) == -1) {
+		logit("ssh: Couldn't connect to %s: %s", uds_path,					strerror(errno));
+		return errno;
+	}
+
+	memcpy(hostaddr, (void *)&addr, addr_len);
+
+	/* Set the connection. */
+	packet_set_connection(sock, sock);
+
+	return 0;
+}
+
 /*
  * Waits for the server identification string, and sends our own
  * identification string.
  */
 static void
@@ -498,11 +535,11 @@
 	int ret = -1;
 
 	if (options.batch_mode)
 		return 0;
 	for (msg = prompt;;msg = again) {
-		p = read_passphrase(msg, RP_ECHO);
+		p = read_passphrase(msg, RP_ECHO|RP_ADD_ID);
 		if (p == NULL ||
 		    (p[0] == '\0') || (p[0] == '\n') ||
 		    strncasecmp(p, "no", 2) == 0)
 			ret = 0;
 		if (p && strncasecmp(p, "yes", 3) == 0)
@@ -543,10 +580,13 @@
 	 * essentially disables host authentication for localhost; however,
 	 * this is probably not a real problem.
 	 */
 	/**  hostaddr == 0! */
 	switch (hostaddr->sa_family) {
+	case AF_UNIX:
+		local = 1;
+		break;
 	case AF_INET:
 		local = (ntohl(((struct sockaddr_in *)hostaddr)->
 		    sin_addr.s_addr) >> 24) == IN_LOOPBACKNET;
 		salen = sizeof(struct sockaddr_in);
 		break;
@@ -570,14 +610,24 @@
 	/*
 	 * We don't have the remote ip-address for connections
 	 * using a proxy command
 	 */
 	if (options.proxy_command == NULL) {
-		if (getnameinfo(hostaddr, salen, ntop, sizeof(ntop),
-		    NULL, 0, NI_NUMERICHOST) != 0)
-			fatal("check_host_key: getnameinfo failed");
-		ip = xstrdup(ntop);
+		/* Will work for the IP address families only */
+		if ((hostaddr->sa_family == AF_INET) ||
+		    (hostaddr->sa_family == AF_INET6)) {
+			if (getnameinfo(hostaddr, salen, ntop, sizeof(ntop),
+		    	    NULL, 0, NI_NUMERICHOST) != 0)
+				fatal("check_host_key: getnameinfo failed");
+			ip = xstrdup(ntop);
+		/* Unix Domain Socket is local so we won't check IP */
+		} else if (hostaddr->sa_family == AF_UNIX) {
+			ip = xstrdup("<no hostip for unix domain socket>");
+		/* Unsupported address family */
+		} else {
+			fatal("check_host_key: can't get name info");
+		}
 	} else {
 		ip = xstrdup("<no hostip for proxy command>");
 	}
 	/*
 	 * Turn off check_host_ip if the connection is to localhost, via proxy
diff -N -U 5 -u openssh-4.2p1/sshconnect.h sshop-4.2p1/sshconnect.h
--- openssh-4.2p1/sshconnect.h	Thu Jun 20 20:41:53 2002
+++ sshop-4.2p1/sshconnect.h	Tue May  3 17:45:23 2005
@@ -34,10 +34,12 @@
 };
 
 int
 ssh_connect(const char *, struct sockaddr_storage *, u_short, int, int,
     int, const char *);
+int
+ssh_connect_uds(const char *, struct sockaddr_storage *);
 
 void
 ssh_login(Sensitive *, const char *, struct sockaddr *, struct passwd *);
 
 int	 verify_host_key(char *, struct sockaddr *, Key *);
diff -N -U 5 -u openssh-4.2p1/sshconnect1.c sshop-4.2p1/sshconnect1.c
--- openssh-4.2p1/sshconnect1.c	Thu Jun 16 22:59:35 2005
+++ sshop-4.2p1/sshconnect1.c	Fri Oct 21 16:02:31 2005
@@ -246,11 +246,11 @@
 		private = key_load_private_type(KEY_RSA1, authfile, "", NULL);
 	if (private == NULL && !options.batch_mode) {
 		snprintf(buf, sizeof(buf),
 		    "Enter passphrase for RSA key '%.100s': ", comment);
 		for (i = 0; i < options.number_of_password_prompts; i++) {
-			passphrase = read_passphrase(buf, 0);
+			passphrase = read_passphrase(buf, RP_ADD_ID);
 			if (strcmp(passphrase, "") != 0) {
 				private = key_load_private_type(KEY_RSA1,
 				    authfile, passphrase, NULL);
 				quit = 0;
 			} else {
@@ -404,11 +404,11 @@
 		if (i != 0)
 			error("Permission denied, please try again.");
 		if (options.cipher == SSH_CIPHER_NONE)
 			logit("WARNING: Encryption is disabled! "
 			    "Response will be transmitted in clear text.");
-		response = read_passphrase(prompt, 0);
+		response = read_passphrase(prompt, RP_ADD_ID);
 		if (strcmp(response, "") == 0) {
 			xfree(response);
 			break;
 		}
 		packet_start(SSH_CMSG_AUTH_TIS_RESPONSE);
@@ -441,11 +441,11 @@
 	if (options.cipher == SSH_CIPHER_NONE)
 		logit("WARNING: Encryption is disabled! Password will be transmitted in clear text.");
 	for (i = 0; i < options.number_of_password_prompts; i++) {
 		if (i != 0)
 			error("Permission denied, please try again.");
-		password = read_passphrase(prompt, 0);
+		password = read_passphrase(prompt, RP_ADD_ID);
 		packet_start(SSH_CMSG_AUTH_PASSWORD);
 		ssh_put_password(password);
 		memset(password, 0, strlen(password));
 		xfree(password);
 		packet_send();
diff -N -U 5 -u openssh-4.2p1/sshconnect2.c sshop-4.2p1/sshconnect2.c
--- openssh-4.2p1/sshconnect2.c	Wed Aug 31 05:46:27 2005
+++ sshop-4.2p1/sshconnect2.c	Fri Oct 21 16:02:31 2005
@@ -724,20 +724,22 @@
 userauth_passwd(Authctxt *authctxt)
 {
 	static int attempt = 0;
 	char prompt[150];
 	char *password;
+	const char *host = options.host_key_alias ?  options.host_key_alias :
+	    authctxt->host;
 
 	if (attempt++ >= options.number_of_password_prompts)
 		return 0;
 
 	if (attempt != 1)
 		error("Permission denied, please try again.");
 
 	snprintf(prompt, sizeof(prompt), "%.30s@%.128s's password: ",
-	    authctxt->server_user, authctxt->host);
-	password = read_passphrase(prompt, 0);
+	    authctxt->server_user, host);
+	password = read_passphrase(prompt, RP_ADD_ID);
 	packet_start(SSH2_MSG_USERAUTH_REQUEST);
 	packet_put_cstring(authctxt->server_user);
 	packet_put_cstring(authctxt->service);
 	packet_put_cstring(authctxt->method->name);
 	packet_put_char(0);
@@ -759,10 +761,12 @@
 input_userauth_passwd_changereq(int type, u_int32_t seqnr, void *ctxt)
 {
 	Authctxt *authctxt = ctxt;
 	char *info, *lang, *password = NULL, *retype = NULL;
 	char prompt[150];
+	const char *host = options.host_key_alias ? options.host_key_alias :
+	    authctxt->host;
 
 	debug2("input_userauth_passwd_changereq");
 
 	if (authctxt == NULL)
 		fatal("input_userauth_passwd_changereq: "
@@ -779,29 +783,29 @@
 	packet_put_cstring(authctxt->service);
 	packet_put_cstring(authctxt->method->name);
 	packet_put_char(1);			/* additional info */
 	snprintf(prompt, sizeof(prompt),
 	    "Enter %.30s@%.128s's old password: ",
-	    authctxt->server_user, authctxt->host);
-	password = read_passphrase(prompt, 0);
+	    authctxt->server_user, host);
+	password = read_passphrase(prompt, RP_ADD_ID);
 	packet_put_cstring(password);
 	memset(password, 0, strlen(password));
 	xfree(password);
 	password = NULL;
 	while (password == NULL) {
 		snprintf(prompt, sizeof(prompt),
 		    "Enter %.30s@%.128s's new password: ",
-		    authctxt->server_user, authctxt->host);
-		password = read_passphrase(prompt, RP_ALLOW_EOF);
+		    authctxt->server_user, host);
+		password = read_passphrase(prompt, RP_ALLOW_EOF|RP_ADD_ID);
 		if (password == NULL) {
 			/* bail out */
 			return;
 		}
 		snprintf(prompt, sizeof(prompt),
 		    "Retype %.30s@%.128s's new password: ",
-		    authctxt->server_user, authctxt->host);
-		retype = read_passphrase(prompt, 0);
+		    authctxt->server_user, host);
+		retype = read_passphrase(prompt, RP_ADD_ID);
 		if (strcmp(password, retype) != 0) {
 			memset(password, 0, strlen(password));
 			xfree(password);
 			logit("Mismatch; try again, EOF to quit.");
 			password = NULL;
@@ -975,11 +979,11 @@
 		if (options.batch_mode)
 			return NULL;
 		snprintf(prompt, sizeof prompt,
 		    "Enter passphrase for key '%.100s': ", filename);
 		for (i = 0; i < options.number_of_password_prompts; i++) {
-			passphrase = read_passphrase(prompt, 0);
+			passphrase = read_passphrase(prompt, RP_ADD_ID);
 			if (strcmp(passphrase, "") != 0) {
 				private = key_load_private_type(KEY_UNSPEC, filename,
 				    passphrase, NULL);
 				quit = 0;
 			} else {
@@ -1200,11 +1204,12 @@
 	debug2("input_userauth_info_req: num_prompts %d", num_prompts);
 	for (i = 0; i < num_prompts; i++) {
 		prompt = packet_get_string(NULL);
 		echo = packet_get_char();
 
-		response = read_passphrase(prompt, echo ? RP_ECHO : 0);
+		response = read_passphrase(prompt,
+		    echo ? RP_ECHO|RP_ADD_ID : RP_ADD_ID);
 
 		packet_put_cstring(response);
 		memset(response, 0, strlen(response));
 		xfree(response);
 		xfree(prompt);
diff -N -U 5 -u openssh-4.2p1/sshhop.c sshop-4.2p1/sshhop.c
--- openssh-4.2p1/sshhop.c	Wed Dec 31 19:00:00 1969
+++ sshop-4.2p1/sshhop.c	Fri Oct 21 17:54:48 2005
@@ -0,0 +1,1256 @@
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *                    All rights reserved
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+/*
+ * Functions for implementing SSH hopping as a single command line input.
+ */
+
+#include "includes.h"
+RCSID("$OpenBSD$");
+
+#include <ctype.h>
+#include <errno.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdio.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <openssl/evp.h>
+#include <openssl/err.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include "log.h"
+#include "misc.h"
+#include "pathnames.h"
+#include "ssh.h"
+#include "readconf.h"
+#include "xmalloc.h"
+#include "sshhop.h"
+
+/* Structure to encapsulate the information needed to represent each hop
+ * in the chain connecting this host to the final destination. A hop is
+ * an invocation of SSH that connects this host to an intermediary host or
+ * a destination host, where there is at least one intermediary host between
+ * this host (source) and the destination host.
+ * command line syntax: ssh [ssh_options] -H [ssh_options] [user1@]host1 ...
+ */
+struct Hop {
+	char	*user;		/* User name at remote host for this hop */
+	char	*host;		/* Remote host to hop to (name or IP address) */
+	int	address_type;	/* AF_INET | AF_INET6 (for "host" value) */
+	pid_t	 pid;		/* Process id for spawned SSH subprocess */
+	u_short  port;		/* SSH port number on this hop's remote host */
+	int	 argc;		/* Number of command line options for this hop */
+	char	**argv;		/* List of command line options for this hop */
+	int	 cmdc;		/* Number of words in command to be invoked
+				 * on this hop's remote host by ssh
+				 */
+	char	**cmdv;		/* Command to be invoked by SSH on this hop's
+				 * remote host
+				 */
+};
+typedef struct Hop Hop;
+#define MAX_HOPS	10	/* Allow no more hops than this in the chain */
+
+/* "Class" variables. We avoid passing these variables around as arguments
+ * since 1) it simplifies the API in ssh.c and 2) it protects the integrity
+ * of the information by limiting access to functions in this module.
+ */
+static Hop	s_hoplist[MAX_HOPS];	/* Information for each hop */
+static int	s_numhops = 0;		/* Number of hops */
+static char   **s_global_argv = NULL;	/* List of options that apply to all hops */
+static int      s_global_argc = 0;	/* Number of options that apply to all hops */
+
+/* External variables used for command line option parsing in getopt. */
+extern int optind;
+extern int optreset;
+
+/* Local functions */
+static int  sshop_get_option_list(int argc, char **argv, int starting_index,
+    int *subset_argc, char ***subset_argv, int *hostloc, usage_fn *usage);
+static int  sshop_parse_hop_info(char *input_str);
+static void sshop_copy_global_args(Hop *hl, int hl_index, int g_index, int num);
+static void sshop_process_global_options(usage_fn *usage);
+static void sshop_process_local_options(Hop *hl, usage_fn *usage);
+static int  sshop_set_hop_args(int i, char *host, struct sockaddr_un *tmp_uds,
+    int max_argc, char **new_argv);
+static int  sshop_start_ssh_client(char *argv[], Hop *hopinfo, char *uds_path);
+
+
+/*========================= Functions used in ssh.c =========================*/
+
+/* sshop_parse_options
+ * Parse the command line options to process -H options
+ * 
+ * Inputs:
+ *		argc	command line argument count (argc from input to main())
+ *		argv	command line argument vector (argv from input to main())
+ *		usage	function to print command usage information
+ * Outputs:
+ *		s_global_argv
+ *		s_global_argc
+ *		s_hoplist
+ *		s_numhops
+ * Returns:	number of hops
+ *		-1 if error
+ */
+int
+sshop_parse_options(int argc, char **argv, usage_fn *usage)
+{
+	int   new_argc, local_argc = 0;		/* argument counts */
+	char **local_argv = NULL;		/* temp storage for arg list */
+	int   hops = 0;				/* return variable */
+	int   hop;				/* loop index */
+	int   hoploc;				/* position in arg list of -H */
+
+	/* Get the global options (options before the first -H option or EOL) */
+	new_argc = sshop_get_option_list(argc, argv, 1, &s_global_argc,
+				&s_global_argv, &hoploc, usage);
+
+	/* While options remain, get information for next hop */
+	while (new_argc > 0) {
+		/* Make sure there is room for more hops */
+		if (s_numhops == MAX_HOPS) {
+			hops = -1;
+			break;
+		}
+		/* Get option list for the next hop (to the next -H or EOL) */
+		argv += (argc - new_argc);
+		argc = new_argc;
+		local_argv = NULL;
+		local_argc = 0;
+		new_argc = sshop_get_option_list(argc, argv, 0, &local_argc,
+				&local_argv, &hoploc, usage);
+
+		/* Process and store information about the next hop */
+		if ((local_argc > 0) && (-1 < hoploc)) {
+			/* Parse the user/hostname string */
+			hop = sshop_parse_hop_info(argv[hoploc]);
+			/* If user and host was successfully loaded (no parse
+			 * errors), store the option information.
+			 */
+			if (hop > 0) {
+				Hop *hl = &s_hoplist[s_numhops];
+				hl->argc = hoploc;
+				/* Commands follow the options for this hop */
+				if (hl->argc > 0) {
+					hl->argv = local_argv;
+					hl->cmdc = local_argc - hoploc - 1;
+				/* No commands follow options for this hop */
+				} else if (local_argc > 0) {
+					hl->argv = local_argv;
+					hl->cmdc = 0;
+				/* No options for this hop */
+				} else if (local_argv) {
+					xfree(local_argv);
+					hl->argv = NULL;
+					hl->cmdc = 0;
+				}
+				/* Number of command words > 0, store command */
+				if (hl->cmdc > 0) {
+					hl->cmdv = local_argv + hoploc + 1;
+				/* Otherwise, no command */
+				} else {
+					hl->cmdv = NULL;
+				}
+				local_argv = NULL;
+				s_numhops++;
+			/* Otherwise, user/host parse error. End processing */
+			} else {
+				hops = -1;
+				break;
+			}
+		/* The beginning of the option list for this host must be -H.
+		 * If it isn't, move on to the next host but indicate an error.
+		 */
+		} else if (strcmp(argv[0], "-H") != 0) {
+			hops = -1;
+		}
+	}
+
+	/* Too many hops -- error */
+	if (s_numhops == MAX_HOPS) {
+		fprintf(stderr, "Limit of %d hops\n", MAX_HOPS-1);
+		hops = -1;
+	/* No errors and multiple hops. Prepare the hop option lists. */
+	} else if (hops == 0 && 1 < s_numhops) {
+		for (hop = 0; hop < s_numhops; hop++) {
+			sshop_process_local_options(&s_hoplist[hop], usage);
+		}
+		sshop_process_global_options(usage);
+		hops = s_numhops;
+	/* No multiple hops, thus no need for any additional preparation of the
+	 * option lists. Free function memory and return. */
+	} else if (local_argv != NULL) {
+		xfree(local_argv);
+		local_argv = NULL;
+	}
+
+	return(hops);
+}
+
+/* sshop_set_default_user
+ * Set the value of the user field for each hop where it is not already set.
+ *
+ * Inputs:
+ *		user		default user name
+ *		s_hoplist
+ *		s_numhops
+ * Outputs:
+ *		s_hoplist (changes user value)
+ * Returns:	none
+ */
+void
+sshop_set_default_user(char *user)
+{
+	int i;
+	Hop *hl;
+
+	if (user == NULL)
+		return;
+
+	for (i = 0; i < s_numhops; i++) {
+		hl = &s_hoplist[i];
+		if (hl->user == NULL) {
+			hl->user = xstrdup(user);
+		}
+	}
+}
+
+/* sshop_build_chain
+ * Build the hop chain by starting each SSH subprocess in turn
+ *
+ * Inputs:
+ *		ssh_path	path to SSH executable
+ *		s_global_argc
+ *		s_hoplist
+ *		s_numhops
+ * Outputs:	none
+ * Returns:	 0 if all hops established successfully
+ *		-1 otherwise
+ */
+int
+sshop_build_chain(char *ssh_path)
+{
+	char  *localhost = "localhost";
+	char  *host = s_hoplist[0].host;/* Source end of all connections */
+	char  **new_argv;		/* Argument list used for ssh call */
+	int   new_argc = 0;		/* Number of args for single ssh call */
+	int   max_argc = 0;		/* Length of new_argv */
+	int   i;			/* Loop counter */
+	int   error = 0;		/* Return variable */
+	struct sockaddr_un tmp_uds;	/* Unix domain socket */
+
+	/* (Over-)Allocate space for the new, larger, argument list
+	 * Number of elements is the sum of:
+	 *	 #command line args specific to this hop
+	 *	 #words in command
+	 *	 max # options added by rote (hard-coded value)
+	 *	 #command line args applied globally
+	 */
+	for (i = 0; i < s_numhops; i++) {
+		new_argc = s_hoplist[i].argc + s_hoplist[i].cmdc;
+		if (max_argc < new_argc) {
+			max_argc = new_argc;
+		}
+	}
+	max_argc += (15 + s_global_argc);
+
+	/* Expand the size of the arg list and copy the arguments */
+	new_argv = (char **)xmalloc(max_argc*sizeof(char *)); 
+	/* Set the command arg: <ssh_path> */
+	if (new_argv) {
+		new_argv[0] = ssh_path;
+	} else {
+		exit(1);
+	}
+
+	memset(&tmp_uds, '\0', sizeof(tmp_uds));
+
+	/* Create a connection for each hop in the chain */
+	for (i = 0; (i < s_numhops) && (error == 0); i++) {
+		/* Fill in the argument list for this hop */
+		new_argc = sshop_set_hop_args(i, host, &tmp_uds,
+					max_argc, new_argv);
+
+		/* Start a new SSH session */
+		if (new_argc > 5) {
+			error = sshop_start_ssh_client(new_argv,
+					&s_hoplist[i], tmp_uds.sun_path);
+		} else {
+			error = -1;
+		}
+
+		/* Reset for the next pass */
+		host = localhost;
+	}
+
+	return(error);
+}
+
+/* sshop_kill_subprocesses
+ * Kill the subprocesses. Doesn't guarantee that all processes have been
+ * killed. (See sshop_wait_for_exit())
+ *
+ * Inputs:
+ *		s_hoplist
+ *		s_numhops
+ * Outputs:
+ *		s_hoplist (changes pid value)
+ * Returns:	0  if all processes killed successfully
+ *		1  if some processes may still be running
+ */
+int
+sshop_kill_subprocesses()
+{
+	int   i;		/* Loop counter */
+	pid_t pid;		/* Process id of killed process */
+	int   looping = 0;	/* Loop status */
+	int   status = 0;	/* Wait status */
+	Hop   *hl;		/* Hop pointer */
+
+	/* Debugging printout */
+	debug("%d hop processes to kill", s_numhops);
+	for (i = 0; i < s_numhops; i++) {
+		hl = &s_hoplist[i];
+		debug("hop %d: host: %s  pid: %d  port: %d",
+		    i, hl->host, hl->pid, hl->port);
+	}
+
+	for (i = 0; i < s_numhops; i++) {
+		hl = &s_hoplist[i];
+		/* Send a TERM signal to running process */
+		if (hl->pid > 0) {
+			kill(hl->pid, SIGTERM);
+			sleep(1);
+			pid = waitpid(hl->pid, &status, WNOHANG);
+			if (pid == hl->pid) {
+				printf("process %d exited (host %s)\n",
+				    pid, hl->host);
+				hl->pid = 0;
+			} else {
+				looping = 1;
+			}
+		}
+	}
+	return(looping);
+}
+
+/* sshop_wait_for_exit
+ * Wait for any one of the subprocesses to exit and then clean up the rest.
+ * Doesn't return until all processes are killed and reaped.
+ *
+ * Inputs:
+ *		s_hoplist
+ *		s_numhops
+ * Outputs:
+ *		s_hoplist (changes pid value)
+ * Returns:	none
+ */
+void
+sshop_wait_for_exit()
+{
+	int   i;		/* Loop counter */
+	pid_t pid;		/* Process id for process to kill */
+	int   looping = 0;	/* Whether to keep looping or not */
+	int   status = 0;	/* Wait status */
+	Hop   *hl;		/* Pointer to hop info */
+
+	/* Debugging printout */
+	debug("%d hop process settings", s_numhops);
+	for (i = 0; i < s_numhops; i++) {
+		hl = &s_hoplist[i];
+		debug("hop %d: host: %s  pid: %d  port: %d",
+		    i, hl->host, hl->pid, hl->port);
+	}
+
+	/* Make sure there are processes to wait for */
+	for (i = 0; (i < s_numhops) && !looping; i++) {
+		looping = s_hoplist[i].pid != 0;
+	}
+
+	/* Wait for all subprocesses to exit */
+	while (looping) {
+		pid = wait(&status);
+		for (i = 0; (i < s_numhops) && (pid != s_hoplist[i].pid); i++);
+
+		/* If any process in the hop chain has exited, kill the rest */
+		if (i < s_numhops) {
+			printf("process %d exited (host %s)\n", pid,
+			    s_hoplist[i].host);
+			s_hoplist[i].pid = 0;
+			looping = sshop_kill_subprocesses();
+		}
+	}
+}
+
+/* sshop_cleanup
+ * Free memory associated with hop processing.
+ *
+ * Inputs:
+ *		s_global_argv
+ *		s_global_argc
+ *		s_hoplist
+ *		s_numhops
+ * Outputs:
+ *		s_global_argv
+ *		s_global_argc
+ *		s_hoplist
+ *		s_numhops
+ * Returns:	None
+ */
+void
+sshop_cleanup()
+{
+	int i;
+	Hop *hl;
+
+	/* Free the list of arguments applied to all hops */
+	if (s_global_argv) {
+		xfree(s_global_argv);
+		s_global_argv = NULL;
+	}
+	s_global_argc = 0;
+
+	/* Free the information associated with each hop */
+	for (i = 0; i < s_numhops; i++) {
+		hl = &s_hoplist[i];
+		if (hl->user) {
+			xfree(hl->user);
+			hl->user = NULL;
+		}
+		if (hl->host) {
+			xfree(hl->host);
+			hl->host = NULL;
+		}
+		if (hl->argv) {
+			xfree(hl->argv);
+			hl->argv = NULL;
+		}
+		hl->argc = 0;
+		hl->cmdc = 0;
+	}
+	s_numhops = 0;
+}
+
+/*====================== Internal functions =============================*/
+
+/*
+ * Store the command line options for a single subsection. Each
+ * subsection ends with either -H or '\0' (for the last subsection).
+ *
+ * Inputs:
+ *		argc		command line argument count
+ *		argv		command line argument vector
+ *		starting_index	which argv to start with (usually 0 or 1)
+ *		usage		function to print command usage information
+ *		s_global_argc
+ * Outputs:
+ *		subset_argc	number of args in subsection
+ *		subset_argv	list of args in subsection
+ *		hostloc		location of host name in arg list
+ * Returns:	number of arguments remaining to be processed
+ */
+static int
+sshop_get_option_list(int argc, char **argv, int starting_index,
+    int *subset_argc, char ***subset_argv, int *hostloc, usage_fn *usage)
+{
+	int  size;		/* Memory allocation size */
+	int  hop_not_found = 1;	/* Loop terminator = -H or \0 not yet found */
+	char ch;		/* Option value */
+	int  new_argc = 0;	/* Return value */
+	int  next_arg = 1;	/* Argument list index */
+	int  i;			/* Loop counter */
+
+	/* Initialize */
+	*subset_argv = NULL;
+	*subset_argc = 0;
+	*hostloc = -1;
+	optind   = starting_index;
+	optreset = 1;
+
+	/* Since the hop info includes hostname and possibly command
+	 * information, 
+	 */
+	while (hop_not_found &&
+		(ch = getopt(argc, argv, SSH_CMDLINE_OPTIONS)) != -1) {
+		switch (ch) {
+		/* Hop info, -H signals beginning of next hop information
+		 * We use it here to signal the end of the previous set of
+		 * ssh options. If there is a hostname, we will not find
+		 * the -H before exiting the option list. Thus, this case
+		 * will be used only for finding the end of the global options.
+		 */
+		case 'H':
+			new_argc = argc - optind;
+			*subset_argc = optind - 1;
+			*hostloc = optind - 2;
+			hop_not_found = 0;
+			break;
+		/* We'll copy all the other options later, keep track of
+		 * where we are.
+		 */
+		default:
+			if (ch != '?') {
+				next_arg = optind;
+			} else {
+				usage();
+			}
+			break;
+		}
+	}
+	/* -H not found yet (probably hit a hostname first), continue looking */
+	if (hop_not_found && (optind <= argc)) {
+		*hostloc = optind;
+		for (i = next_arg; (i < argc) && (strcmp(argv[i], "-H") != 0);
+		     i++);
+		if (i <= argc) {
+			new_argc = argc - i;
+			*subset_argc  = i;
+			hop_not_found = 0;
+		}
+	}
+	/* Copy the arguments up to the next hop info or the end of the
+	 * args, whichever came first. We will eventually use the allocated
+	 * arg list for storing global arguments as well as hop specific
+	 * arguments, so size it accordingly. Note that the first time
+	 * this function is called, it will read in the global arguments.
+	 * So the global arg list will not have extra space allocated.
+	 */
+	if ((s_global_argc + *subset_argc) > 0) {
+		size = (s_global_argc + *subset_argc) * sizeof(*argv);
+		*subset_argv = (char **)xmalloc(size);
+		memset((void *)*subset_argv, '\0', size);
+		if (*subset_argc > 0) {
+			memcpy((void *)*subset_argv, (void *)argv,
+				*subset_argc * sizeof(*argv));
+		}
+	}
+
+	return(new_argc);
+}
+
+/* sshop_parse_hop_info
+ * Parse the hop argument and store the username and hostname in
+ * the hop structure. Uses s_numhops but does not change its value.
+ * Format of input_str: [<user>@]<host>
+ *
+ * Inputs:
+ *		input_str	string of the form [<user>@]<host>
+ *		s_hoplist
+ *		s_numhops
+ * Outputs:
+ *		s_hoplist
+ * Returns:	number of hops, including this one, on success
+ *		-1 on failure
+ */
+static int
+sshop_parse_hop_info(char *input_str)
+{
+	char *p, buf[512];	/* if buf size changes, change the sscanf
+				 * format statement
+				 */
+	Hop  *hl = NULL;	/* Ptr to hop information */
+	char *hopstr;		/* Temporary string with user/host */
+	int  hops = 0;		/* return variable */
+
+	hopstr = xstrdup(input_str);
+	/* Proceed only if the string is valid */
+	if (hopstr && (*hopstr != '\0') && (s_numhops < MAX_HOPS)) {
+		/* Skip over whitespace at beginning of string */
+		while (isspace(*hopstr))
+			hopstr++;
+		/* If there are characters left, parse the string
+		 * The string should have the format: [user@]host
+		 */
+		if (*hopstr != '\0') {
+			memset(buf, '\0', sizeof(buf));
+			/* Nothing scanned: Bad format */
+			if (sscanf(hopstr, "%511c", buf) < 1) {
+				fprintf(stderr,
+				    "Bad SSH hop specification '%s'\n",
+				    hopstr);
+				hops = -1;
+			/* Otherwise, input formatted ok, store the information.
+			 * Don't increment hop count here. Just store the info.
+			 */
+			} else {
+				hl = &s_hoplist[s_numhops];
+				hl->pid = 0;
+				/* User name specified */
+				if ((p = strrchr(buf, '@'))) {
+					*p = '\0';
+					hl->user = xstrdup(buf);
+					hl->host = xstrdup(++p);
+				/* No user name specified */
+				} else {
+					hl->user = NULL;
+					hl->host = xstrdup(buf);
+				}
+				hl->port = 0;
+				/* Host string looks like IPv6 address */
+				if (strchr(hl->host, ':')) {
+					hl->address_type = AF_INET6;
+				/* Otherwise, assume IPv4 address */
+				} else {
+					hl->address_type = AF_INET;
+				}
+				/* Set return value */
+				hops = s_numhops + 1;
+			}
+		}
+	}
+
+	xfree(hopstr);
+
+	return(hops);
+}
+
+/* sshop_copy_global_args
+ * Copy consecutive arguments from the global list to the beginning of
+ * the local list. Assumes the array was sized to allow addition of
+ * the max number of global arguments.
+ *
+ * Inputs:
+ *		hl		pointer to hop list item
+ *		hl_index	location in arg list to copy to
+ *		ga_index	index of the last global arg to be copied
+ *		num		num of global arguments to copy
+ *		s_global_argv
+ *		s_global_argc
+ * Outputs:
+ *		hl (changes argv and argc values)
+ * Returns:	none
+ */
+static void
+sshop_copy_global_args(Hop *hl, int hl_index, int ga_index, int num)
+{
+	int i, j;
+
+	if (hl->argv && (0 < num) && (num < s_global_argc)) {
+		/* Make space for the global arguments at the indicated
+		 * position in the hop argument list.
+		 */
+		bcopy(hl->argv+hl_index, hl->argv+num+hl_index,
+		    (hl->argc-hl_index)*sizeof(*hl->argv));
+		/* Copy in the global args. Note that the address is
+		 * being copied, not the contents.
+		 */
+		for (i=hl_index, j=ga_index-num; i < hl_index+num; i++, j++) {
+			hl->argv[i] = s_global_argv[j];
+			hl->argc++;
+		}
+	} else {
+		fatal("sshop_copy_global_args: no argument list to copy");
+	}
+}
+
+/* sshop_process_global_options
+ * Examine each command line option to be associated with all hops and
+ * copy the options into the individual hop option lists, as appropriate.
+ * Assumes that the individual option lists were sized large enough
+ * to hold all of the global options, in addition to the local options.
+ *
+ * Inputs:
+ *		usage		function to print command usage information
+ *		s_global_argv
+ *		s_global_argc
+ *		s_hoplist
+ *		s_numhops
+ * Outputs:
+ *		s_global_argv
+ *		s_global_argc
+ *		s_hoplist	(possibly update arguments)
+ * Returns:	none
+ */
+static void
+sshop_process_global_options(usage_fn *usage)
+{
+	extern Options options;		/* from ssh.c */
+	extern int     debug_flag;	/* from ssh.c */
+
+	int     i, j;		/* loop counters */
+	u_short port;		/* port number specified as global option */
+	char    ch;		/* option variable */
+	Hop     *hl;		/* pointer to information for a single hop */
+	int     hl_index0 = 0, hl_indexn = 0;
+	optind   = 1;		/* option index for getopt */
+	optreset = 1;		/* getopt to treat this as a new option list */
+
+	/* Loop to process the global option list. The options will be
+	 * added to the argument lists of the appropriate hops.
+	 */
+	while ((ch = getopt(s_global_argc, s_global_argv, SSH_CMDLINE_OPTIONS)) != -1) {
+		switch (ch) {
+		/* All hops, no arguments */
+		case '1':
+		case '2':
+		case '4':
+		case '6':
+		case 'a':
+		case 'C':
+		case 'k':
+		case 'P':
+		case 'q':
+			/* Retain these options */
+			break;
+
+		/* Verbosity, retain this option for subprocesses and apply
+		 * it to the main process as well.
+		 */
+		case 'v':
+			if (debug_flag == 0) {
+				debug_flag = 1;
+				options.log_level = SYSLOG_LEVEL_DEBUG1;
+			} else {
+				if (options.log_level < SYSLOG_LEVEL_DEBUG3)
+					options.log_level++;
+				break;
+			}
+			/* FALLTHROUGH */
+
+		/* Version, retain this option for subprocesses and apply
+		 * it to the main process as well.
+		 */
+		case 'V':
+			fprintf(stderr, "%s, %s\n",
+			    SSH_RELEASE, SSLeay_version(SSLEAY_VERSION));
+			break;
+
+		/* All hops, with arguments */
+		case 'c':
+		case 'F':
+		case 'i':
+		case 'I':
+		case 'm':
+		case 'o':
+			/* Retain these options */
+			break;
+
+		/* Applies to all hops, copy the name */
+		case 'l':
+			for (i = 0; i < s_numhops; i++) {
+				if (s_hoplist[i].user == 0) {
+					s_hoplist[i].user = xstrdup(optarg);
+				}
+			}
+			s_global_argv[optind-2] = NULL;
+			s_global_argv[optind-1] = NULL;
+			break;
+
+		/* Use as default when port not specified */
+		case 'p':
+			port = a2port(optarg);
+			if (0 < port) {
+				for (i = 0; i < s_numhops; i++) {
+					if (s_hoplist[i].port == 0) {
+						s_hoplist[i].port = port;
+					}
+				}
+			}
+			s_global_argv[optind-2] = NULL;
+			s_global_argv[optind-1] = NULL;
+			break;
+
+		/* First hop only, no arguments
+		 * Prepend this option to the list of options */
+		case 'b':
+			hl = &s_hoplist[0];
+			sshop_copy_global_args(hl, hl_index0, optind, 1);
+			hl_index0++;
+			s_global_argv[optind-1] = NULL;
+			break;
+
+		/* First hop only, with arguments 
+		 * Prepend this option to the list of options */
+		case 'u':
+			hl = &s_hoplist[0];
+			sshop_copy_global_args(hl, hl_index0, optind, 2);
+			hl_index0 += 2;
+			s_global_argv[optind-2] = NULL;
+			s_global_argv[optind-1] = NULL;
+			break;
+
+		/* Last hop only, no arguments 
+		 * Prepend this option to the list of options */
+		case 'A':
+		case 'f':
+		case 'g':
+		case 'n':
+		case 'M':
+		case 'N':
+		case 's':
+		case 't':
+		case 'T':
+		case 'x':
+		case 'X':
+		case 'Y':
+			hl = &s_hoplist[s_numhops-1];
+			sshop_copy_global_args(hl, hl_indexn, optind, 1);
+			hl_indexn++;
+			s_global_argv[optind-1] = NULL;
+			break;
+
+		/* Last hop only, with arguments 
+		 * Prepend this option to the list of options */
+		case 'D':
+		case 'e':
+		case 'L':
+		case 'O':
+		case 'R':
+		case 'S':
+		case 'U':
+			hl = &s_hoplist[s_numhops-1];
+			sshop_copy_global_args(hl, hl_indexn, optind, 2);
+			hl_indexn += 2;
+			s_global_argv[optind-2] = NULL;
+			s_global_argv[optind-1] = NULL;
+			break;
+
+		default:
+			usage();
+			break;
+		}
+	}
+
+	/* Remove all the nulls (from deleted args) from the global argv list
+	 * and update the global arg count
+	 */
+	i = 0;
+	while (i < s_global_argc) {
+		for (; i < s_global_argc && s_global_argv[i] != NULL; i++);
+		for (j = i; j < s_global_argc && s_global_argv[j] == NULL; j++);
+		if (i < s_global_argc && j < s_global_argc) {
+			bcopy(s_global_argv+j, s_global_argv+i,
+				(s_global_argc - j) * sizeof(*s_global_argv));
+			s_global_argc -= (j - i);
+		} else if (i < s_global_argc && j >= s_global_argc) {
+			s_global_argc = i;
+		}
+	}
+}
+
+/* sshop_process_local_options
+ * Examine each command line option associated with a single hop and
+ * determine whether the option should be retained or not
+ *
+ * Inputs:
+ *		hl	hop for which to process options
+ *		usage	function to print command usage information
+ *		s_hoplist
+ *		s_numhops
+ * Outputs:
+ *		hl	(updates argv and argc)
+ * Returns:	none
+ */
+static void
+sshop_process_local_options(Hop *hl, usage_fn *usage)
+{
+	int  i, j;	/* Loop counters */
+	char ch;	/* option value */
+	optind   = 0;	/* option index to start with */
+	optreset = 1;	/* getopt to treat this as a new option list */
+
+	/* Check for valid input */
+	if (hl == NULL) {
+		return;
+	}
+	if (hl->argc == 0) {
+		return;
+	}
+
+	/* Loop to process the local option list for a single hop. Delete
+	 * any options that are disallowed for this hop (based on whether
+	 * it is the first, last, or an intermediate hop).
+	 */
+	while ((ch = getopt(hl->argc, hl->argv, SSH_CMDLINE_OPTIONS)) != -1) {
+	switch (ch) {
+		/* All hops, no arguments */
+		case '1':
+		case '2':
+		case '4':
+		case '6':
+		case 'a':
+		case 'A':
+		case 'b':
+		case 'C':
+		case 'k':
+		case 'P':
+		case 'q':
+		case 'v':
+		case 'V':
+			/* Retain these options */
+			break;
+
+		/* All hops, with arguments */
+		case 'c':
+		case 'e':
+		case 'F':
+		case 'i':
+		case 'I':
+		case 'm':
+		case 'o':
+			/* Retain these options */
+			break;
+
+		/* Copy the name into an empty user field. Whether copied
+		 * or not, delete the option since it is extraneous.
+		 */
+		case 'l':
+			if (hl->user == NULL) {
+				hl->user = xstrdup(optarg);
+			}
+			hl->argv[optind-1] = NULL;
+			hl->argv[optind-2] = NULL;
+			break;
+
+		/* First hop only, with arguments */
+		case 'u':
+			/* If not the first hop, delete this option and its
+			 * argument. */
+			if (hl != &s_hoplist[0]) {
+				hl->argv[optind-1] = NULL;
+				hl->argv[optind-2] = NULL;
+			}
+			break;
+
+		/* Last hop only, no arguments */
+		case 'f':
+		case 'g':
+		case 'n':
+		case 'M':
+		case 'N':
+		case 'p':
+		case 's':
+		case 't':
+		case 'T':
+		case 'x':
+		case 'X':
+		case 'Y':
+			/* If not the last hop, delete this option */
+			if (hl != &s_hoplist[s_numhops-1]) {
+				hl->argv[optind-1] = NULL;
+			}
+			break;
+
+		/* Last hop only, with arguments */
+		case 'D':
+		case 'L':
+		case 'O':
+		case 'R':
+		case 'S':
+		case 'U':
+			/* If not the last hop, delete this option and its
+			 * argument. */
+			if (hl != &s_hoplist[s_numhops-1]) {
+				hl->argv[optind-1] = NULL;
+				hl->argv[optind-2] = NULL;
+			}
+			break;
+
+		default:
+			usage();
+			break;
+	}
+	}
+
+	/* Remove all the nulls (from deleted options) from the local
+	 * argv list and update the arg count
+	 */
+	i = 0;
+	while (i < hl->argc) {
+		for (; i < hl->argc && hl->argv[i] != NULL; i++);
+		for (j = i; j < hl->argc && hl->argv[j] == NULL; j++);
+		if (i < hl->argc && j < hl->argc) {
+			bcopy(hl->argv+j, hl->argv+i,
+				(hl->argc - j) * sizeof(*hl->argv));
+			hl->argc -= (j - i);
+		} else if (i < hl->argc && j >= hl->argc) {
+			hl->argc = i - 1;
+		}
+	}
+
+}
+
+/* sshop_set_hop_args
+ * Non-re-entrant function
+ * Fill in the argument list for the next ssh command
+ * Returns the number of arguments in the argument list -- should be > 5
+ * for a valid option list.
+ *
+ * Inputs:
+ *		i 		hop index for this hop
+ *		host
+ *		tmp_uds 	Unix domain socket used for the last hop
+ *		max_argc	maximum length of the argument list
+ *		new_argv	argument list that will be used to start ssh
+ *		s_global_argv
+ *		s_global_argc
+ *		s_hoplist	(contains ssh path as first arg in arg list)
+ *		s_numhops
+ * Outputs:
+ *		tmp_uds 	Unix domain socket used for this hop
+ *		s_hoplist	(argv and argc updated)
+ * Returns:	Number of arguments in the argument list
+ */
+static int
+sshop_set_hop_args(int i, char *host, struct sockaddr_un *tmp_uds,
+    int max_argc, char **new_argv)
+{
+	static char  *c_bckgnd_flag    = "-n";
+		/* Don't use -f since we don't want to fork a new process */
+	static char  *c_no_cmd_flag    = "-N";
+	static char  *c_force_tty_flag = "-t";
+	static char  *c_no_forward_x11 = "-x";
+	static char  tmp_port_arg[8];
+	static char  tmp_fwd_arg[372];
+	static char  tmp_host_alias[272];
+	static struct sockaddr_un tmp_uds_last;
+
+	char  tmp_dst_host_arg[1024];
+	int   new_argc;			/* return variable */
+	pid_t pid = getpid();
+	char  *user = NULL;
+	char  delimiter = ',';
+	struct servent *sp;		/* Used to get the SSH port # */
+	int   j = i + 1;
+
+	/*----- Set up -----*/
+	/* Save the last value to be used later */
+	memcpy(tmp_uds_last.sun_path, tmp_uds->sun_path,
+	    sizeof(tmp_uds_last.sun_path));
+
+	/* Generate the argument to the -U option for forwarding.
+	 * Since -U is not used for the last ssh invocation (no
+	 * more forwarding), we only  generate the argument for
+	 * s_numhops - 1 hops.
+	 */
+	if (j < s_numhops) {
+		/* If needed, set the remote ssh port number */
+		if (s_hoplist[j].port == 0) {
+			sp = getservbyname(SSH_SERVICE_NAME, "tcp");
+			if (sp)
+				s_hoplist[j].port = ntohs(sp->s_port);
+			else
+				s_hoplist[j].port = SSH_DEFAULT_PORT;
+		}
+		/* Get the user name on the remote host */
+		if (s_hoplist[j].user) {
+			user = xstrdup(s_hoplist[j].user);
+		} else {
+			user = NULL;
+		}
+		/* Create a name for the unix domain socket that
+		 * will be used for forwarding.
+		 */
+		construct_uds_path(NULL, s_hoplist[j].host, pid,
+			&user, s_hoplist[j].port,
+			tmp_uds->sun_path, sizeof(tmp_uds->sun_path));
+		/* Create the argument in the form:
+		 * local_path,remote_host,remote_port
+		 */
+		snprintf(tmp_fwd_arg, sizeof(tmp_fwd_arg), "%s%c%s%c%d",
+			tmp_uds->sun_path, delimiter, s_hoplist[j].host,
+			delimiter, s_hoplist[j].port);
+		/* Clean up */
+		if (user)
+			xfree(user);
+
+	/* Last hop in the chain, zero out values to signal that
+	 * they shouldn't be used. */
+	} else {
+		memset(tmp_fwd_arg, '\0', sizeof(tmp_fwd_arg));
+		memset(tmp_uds->sun_path, '\0',
+			sizeof(tmp_uds->sun_path));
+	}
+
+	/* Construct the host argument (will follow all other options
+	 * and precede command)
+	 */
+	if (s_hoplist[i].user) {
+		snprintf(tmp_dst_host_arg, sizeof(tmp_dst_host_arg),
+			"%s@%s", s_hoplist[i].user, host);
+	} else {
+		strncpy(tmp_dst_host_arg, host,
+			sizeof(tmp_dst_host_arg));
+	}
+	/* -o HostKeyAlias= argument */
+	if (strcmp(host, "localhost") == 0) {
+		snprintf(tmp_host_alias, sizeof(tmp_host_alias),
+			"HostKeyAlias=%s", s_hoplist[i].host);
+	} else {
+		memset(tmp_host_alias, '\0', sizeof(tmp_host_alias));
+	}
+
+	/*--- Start building the argument list ----*/
+
+	/* Copy in the new arguments. Do something different for the last
+	 * hop in the chain since it will be an interactive session. */
+	new_argc = 1;
+	if (j < s_numhops) {
+		/* ssh -n -N -X */
+		new_argv[new_argc++] = c_bckgnd_flag;
+		new_argv[new_argc++] = c_no_cmd_flag;
+		new_argv[new_argc++] = c_no_forward_x11;
+	} else {
+		/* ssh -t -t */
+		new_argv[new_argc++] = c_force_tty_flag;
+		new_argv[new_argc++] = c_force_tty_flag;
+	}
+	/* Tell ssh to use the key for the real hostname
+	 * -o HostKeyAlias=<hostname> */
+	if (strlen(tmp_host_alias) > 0) {
+		new_argv[new_argc++] = "-o";
+		new_argv[new_argc++] = tmp_host_alias;
+	}
+	/* Port info for the first hop only
+	 * [-p <SSH_port_number>] */
+	if (i == 0 && 0 < s_hoplist[i].port) {
+		snprintf(tmp_port_arg, sizeof(tmp_port_arg), "%d",
+			s_hoplist[i].port);
+		new_argv[new_argc++] = "-p";
+		new_argv[new_argc++] = tmp_port_arg;
+	}
+	/* SSH forwarding information
+	 * -U <uds_path>,<remote_host>,<remote_SSH_port> */
+	if (strlen(tmp_fwd_arg) > 0) {
+		new_argv[new_argc++] = "-U";
+		new_argv[new_argc++] = tmp_fwd_arg;
+	}
+	/* Unix domain socket to connect to for SSH connection
+	 * -u <last_hop_uds_path> */
+	if (strlen(tmp_uds_last.sun_path) > 0) {
+		new_argv[new_argc++] = "-u";
+		new_argv[new_argc++] = tmp_uds_last.sun_path;
+	}
+	/* Copy in the global options, if any. Skip the first
+	 * since that is the program path
+	 * ssh <fixed_options> [<global_ssh_options>]
+	 */
+	if (s_global_argc > 1) {
+		memcpy(new_argv+new_argc, s_global_argv+1,
+			(s_global_argc-1) * sizeof(char *));
+		new_argc += (s_global_argc - 1);
+	}
+	/* Copy in the options specific to this host, if any
+	 * ssh <fixed_options> [<global_ssh_options>]
+	 *     [<hop_specific_ssh_options>]
+	 */
+	if (s_hoplist[i].argc > 0) {
+		memcpy(new_argv+new_argc, s_hoplist[i].argv,
+	    		s_hoplist[i].argc * sizeof(char *));
+		new_argc += s_hoplist[i].argc;
+	}
+	/* Copy in the destination host
+	 * ssh <fixed_options> [<global_ssh_options>]
+	 *     [<hop_specific_ssh_options>] [<user>@]<host>
+	 */
+	new_argv[new_argc++] = tmp_dst_host_arg;
+	/* Copy in any commands
+	 * ssh <fixed_options> [<global_ssh_options>]
+	 *     [<hop_specific_ssh_options>] [<user>@]<host> [<commmands>]
+	 */
+	if (s_hoplist[i].cmdc > 0) {
+		memcpy(new_argv+new_argc, s_hoplist[i].cmdv,
+			s_hoplist[i].cmdc * sizeof(char *));
+		new_argc += s_hoplist[i].cmdc;
+	}
+	/* Terminate argument list */
+	new_argv[new_argc++] = NULL;
+
+	return(new_argc);
+}
+
+/* sshop_start_ssh_client
+ * Spawn a subprocess
+ * Inputs:
+ *		argv - null terminated argument vector for invoking ssh
+ * 		hopinfo - information about this hop
+ *		uds_path - path to the unix domain socket. The existence
+ *			   of this socket is monitored to determine when
+ *			   ssh process has made the remote connection.
+ * Outputs:
+ * 		hopinfo (pid updated)
+ * Returns:	 0 if the remote connection appears to have been established
+ *		-1 otherwise
+ */
+static int
+sshop_start_ssh_client(char *argv[], Hop *hopinfo, char *uds_path)
+{
+	pid_t pid;		/* return value from fork() */
+	int   error = 0;	/* return variable */
+	int   status;		/* wait status */
+
+	pid = fork();
+	/* Child process; exec the command */
+	if (pid == 0) {
+		/* Should never return from this call */
+		execv(argv[0], argv);
+		/* If we do return, kill off the process immediately. */
+		fatal("sshop_start_ssh_client: exec(%s): %s", argv[0],
+		    strerror(errno));
+	/* Parent; store the pid for the child */
+	} else if (pid > 0) {
+		/* Store the process id for the child so we can kill it later */
+		hopinfo->pid = pid;
+
+		/* Make sure the process didn't have issues at startup */
+		if (waitpid(pid, &status, WNOHANG) == pid) {
+			hopinfo->pid = 0;
+			return -1;
+		}
+
+		/* Put up notification that SSH subprocess is starting */
+		if (hopinfo->user != NULL) {
+			fprintf(stdout, "[%d]\n[%d] login for %s@%s\n",
+			    hopinfo->pid, hopinfo->pid, hopinfo->user,
+			    hopinfo->host);
+			fflush(stdout);
+		} else {
+			fprintf(stdout, "[%d]\n[%d] login to %s\n",
+			    hopinfo->pid, hopinfo->pid, hopinfo->host);
+			fflush(stdout);
+		}
+		/* Wait for the process to establish the connection
+		 * If there is a problem, we will sit here indefinitely.
+		 * The user will need to kill the process (Ctl-C)
+		 * Since the last process in the chain does not create
+		 * a unix domain socket, it will return immediately.
+		 */
+		if (uds_path && (strlen(uds_path) > 0)) {
+			while (access(uds_path, F_OK) == -1 && error == 0) {
+				/* Make sure the process is still running */
+				pid = hopinfo->pid;
+				if (waitpid(pid, &status, WNOHANG) == pid) {
+					hopinfo->pid = 0;
+					error = -1;
+				} else {
+					sleep(1);
+				}
+			}
+			sleep(1);
+		}
+	/* Error */
+	} else {
+		fprintf(stderr,
+		    "sshop_start_ssh_client: Unable to fork child process\n");
+		error = -1;
+	}
+
+	return(error);
+}
diff -N -U 5 -u openssh-4.2p1/sshhop.h sshop-4.2p1/sshhop.h
--- openssh-4.2p1/sshhop.h	Wed Dec 31 19:00:00 1969
+++ sshop-4.2p1/sshhop.h	Fri Oct 21 15:48:24 2005
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *                    All rights reserved
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#ifndef SSHHOP_H
+#define SSHHOP_H
+
+typedef void usage_fn(void);
+
+int  sshop_build_chain(char *ssh_path);
+void sshop_cleanup();
+int  sshop_kill_subprocesses();
+int  sshop_parse_options(int argc, char **argv, usage_fn *);
+void sshop_set_default_user(char *user);
+void sshop_wait_for_exit();
+
+#endif				/* SSHHOP_H */
diff -N -U 5 -u openssh-4.2p1/sshhop_test.c sshop-4.2p1/sshhop_test.c
--- openssh-4.2p1/sshhop_test.c	Wed Dec 31 19:00:00 1969
+++ sshop-4.2p1/sshhop_test.c	Mon Nov 28 13:18:07 2005
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2005 Massachusetts Institute of Technology
+ *                    All rights reserved
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "includes.h"
+RCSID("$OpenBSD: readconf.c,v 1.137 2005/03/04 08:48:06 djm Exp $");
+
+#include <pwd.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include "log.h"
+#include "misc.h"
+#include "sshhop.h"
+#include "xmalloc.h"
+#include "ssh.h"
+#include "readconf.h"
+
+#undef  _PATH_SSH_PROGRAM
+#define _PATH_SSH_PROGRAM	"./ssh"
+
+Options options;
+int     debug_flag;
+
+/* !!!NOTE: Toy program -- no signal handlers !!!*/
+
+/* Prints a help message to the user.  This function never returns. */
+static void
+usage(void)
+{
+	fprintf(stderr,
+"usage: sshhop_test [ssh_options] -H [ssh_options] [-p port1] [user1@]host1\n"
+"                             ... -H [ssh_options] [-p portn] [usern@]hostn\n"
+"                   [command]\n"
+	);
+	exit(1);
+}
+
+/*
+ * Main for sshhop test program.
+ */
+int
+main(int argc, char *argv[])
+{
+	int hops;
+	int error;
+	int original_real_uid;
+	char *ssh_path = xstrdup(_PATH_SSH_PROGRAM);
+	struct passwd *pw;
+
+	/* May be enough arguments */
+	if (argc > 4) {
+		original_real_uid = getuid();
+		/* Get user data. */
+		pw = getpwuid(original_real_uid);
+		if (!pw) {
+			logit("You don't exist, go away!");
+			exit(1);
+		}
+		/* Take a copy of the returned structure. */
+		pw = pwcopy(pw);
+
+		hops = sshop_parse_options(argc, argv, usage);
+		/* No hosts given, exit after printing usage */
+		if (hops <= 0) {
+			usage();
+		}
+		/* Only one host, run the standard SSH.
+		 * Overwrite this process */
+		else if (hops == 1) {
+			argv[0] = ssh_path;
+			execv(argv[0], argv);
+		}
+		/* More than one hop, set up the hop chain */
+		else {
+			/*
+		 	* Initialize "log" output.  Since we are the client all output
+		 	* actually goes to stderr.
+		 	*/
+			log_init(argv[0],
+			    options.log_level == -1 ? SYSLOG_LEVEL_INFO : options.log_level,
+			    SYSLOG_FACILITY_USER, 1);
+
+			/* Make sure the user is set */
+			sshop_set_default_user(pw->pw_name);
+
+			/* Build the hop chain */
+			error = sshop_build_chain(ssh_path);
+
+			/* Hop chain established successfully, wait to clean
+			 * processes up.
+			 */
+			if (error == 0) {
+				sshop_wait_for_exit();
+			/* Things were not started properly, shutdown */
+			} else {
+				sshop_kill_subprocesses();
+				sshop_wait_for_exit();
+			}
+		}
+	/* Not enough arguments */
+	} else {
+		usage();
+	}
+	return(0);
+}
+
