- (djm) Pick up LOGIN_PROGRAM from environment or PATH if not set by headers
 - (djm) OpenBSD CVS updates:
   - deraadt@cvs.openbsd.org 2000/08/18 20:07:23
     [ssh.c]
     accept remsh as a valid name as well; roman@buildpoint.com
   - deraadt@cvs.openbsd.org 2000/08/18 20:17:13
     [deattack.c crc32.c packet.c]
     rename crc32() to ssh_crc32() to avoid zlib name clash.  do not move to
     libz crc32 function yet, because it has ugly "long"'s in it;
     oneill@cs.sfu.ca
   - deraadt@cvs.openbsd.org 2000/08/18 20:26:08
     [scp.1 scp.c]
     -S prog support; tv@debian.org
   - deraadt@cvs.openbsd.org 2000/08/18 20:50:07
     [scp.c]
     knf
   - deraadt@cvs.openbsd.org 2000/08/18 20:57:33
     [log-client.c]
     shorten
   - markus@cvs.openbsd.org  2000/08/19 12:48:11
     [channels.c channels.h clientloop.c ssh.c ssh.h]
     support for ~. in ssh2
   - deraadt@cvs.openbsd.org 2000/08/19 15:29:40
     [crc32.h]
     proper prototype
   - markus@cvs.openbsd.org  2000/08/19 15:34:44
     [authfd.c authfd.h key.c key.h ssh-add.1 ssh-add.c ssh-agent.1]
     [ssh-agent.c ssh-keygen.c sshconnect1.c sshconnect2.c Makefile]
     [fingerprint.c fingerprint.h]
     add SSH2/DSA support to the agent and some other DSA related cleanups.
     (note that we cannot talk to ssh.com's ssh2 agents)
   - markus@cvs.openbsd.org  2000/08/19 15:55:52
     [channels.c channels.h clientloop.c]
     more ~ support for ssh2
   - markus@cvs.openbsd.org  2000/08/19 16:21:19
     [clientloop.c]
     oops
   - millert@cvs.openbsd.org 2000/08/20 12:25:53
     [session.c]
     We have to stash the result of get_remote_name_or_ip() before we
     close our socket or getpeername() will get EBADF and the process
     will exit.  Only a problem for "UseLogin yes".
   - millert@cvs.openbsd.org 2000/08/20 12:30:59
     [session.c]
     Only check /etc/nologin if "UseLogin no" since login(1) may have its
     own policy on determining who is allowed to login when /etc/nologin
     is present.  Also use the _PATH_NOLOGIN define.
   - millert@cvs.openbsd.org 2000/08/20 12:42:43
     [auth1.c auth2.c session.c ssh.c]
     Add calls to setusercontext() and login_get*().  We basically call
     setusercontext() in most places where previously we did a setlogin().
     Add default login.conf file and put root in the "daemon" login class.
   - millert@cvs.openbsd.org 2000/08/21 10:23:31
     [session.c]
     Fix incorrect PATH setting; noted by Markus.
diff --git a/clientloop.c b/clientloop.c
index 67fa36d..d339e12 100644
--- a/clientloop.c
+++ b/clientloop.c
@@ -16,7 +16,7 @@
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: clientloop.c,v 1.29 2000/07/16 08:27:21 markus Exp $");
+RCSID("$OpenBSD: clientloop.c,v 1.32 2000/08/19 22:21:19 markus Exp $");
 
 #include "xmalloc.h"
 #include "ssh.h"
@@ -29,6 +29,9 @@
 #include "channels.h"
 #include "dispatch.h"
 
+#include "buffer.h"
+#include "bufaux.h"
+
 /* Flag indicating that stdin should be redirected from /dev/null. */
 extern int stdin_null_flag;
 
@@ -60,6 +63,8 @@
 static int in_non_blocking_mode = 0;
 
 /* Common data for the client loop code. */
+static int quit_pending;	/* Set to non-zero to quit the client loop. */
+static int escape_char;		/* Escape character. */
 static int escape_pending;	/* Last character was the escape character */
 static int last_was_cr;		/* Last character was a newline. */
 static int exit_status;		/* Used to store the exit status of the command. */
@@ -67,13 +72,11 @@
 static Buffer stdin_buffer;	/* Buffer for stdin data. */
 static Buffer stdout_buffer;	/* Buffer for stdout data. */
 static Buffer stderr_buffer;	/* Buffer for stderr data. */
+static unsigned long stdin_bytes, stdout_bytes, stderr_bytes;
 static unsigned int buffer_high;/* Soft max buffer size. */
 static int max_fd;		/* Maximum file descriptor number in select(). */
 static int connection_in;	/* Connection to server (input). */
 static int connection_out;	/* Connection to server (output). */
-static unsigned long stdin_bytes, stdout_bytes, stderr_bytes;
-static int quit_pending;	/* Set to non-zero to quit the client loop. */
-static int escape_char;		/* Escape character. */
 
 
 void	client_init_dispatch(void);
@@ -379,17 +382,15 @@
 }
 
 void
-client_suspend_self()
+client_suspend_self(Buffer *bin, Buffer *bout, Buffer *berr)
 {
 	struct winsize oldws, newws;
 
 	/* Flush stdout and stderr buffers. */
-	if (buffer_len(&stdout_buffer) > 0)
-		atomicio(write, fileno(stdout), buffer_ptr(&stdout_buffer),
-		    buffer_len(&stdout_buffer));
-	if (buffer_len(&stderr_buffer) > 0)
-		atomicio(write, fileno(stderr), buffer_ptr(&stderr_buffer),
-		    buffer_len(&stderr_buffer));
+	if (buffer_len(bout) > 0)
+		atomicio(write, fileno(stdout), buffer_ptr(bout), buffer_len(bout));
+	if (buffer_len(berr) > 0)
+		atomicio(write, fileno(stderr), buffer_ptr(berr), buffer_len(berr));
 
 	leave_raw_mode();
 
@@ -397,9 +398,9 @@
 	 * Free (and clear) the buffer to reduce the amount of data that gets
 	 * written to swap.
 	 */
-	buffer_free(&stdin_buffer);
-	buffer_free(&stdout_buffer);
-	buffer_free(&stderr_buffer);
+	buffer_free(bin);
+	buffer_free(bout);
+	buffer_free(berr);
 
 	/* Save old window size. */
 	ioctl(fileno(stdin), TIOCGWINSZ, &oldws);
@@ -416,9 +417,9 @@
 		received_window_change_signal = 1;
 
 	/* OK, we have been continued by the user. Reinitialize buffers. */
-	buffer_init(&stdin_buffer);
-	buffer_init(&stdout_buffer);
-	buffer_init(&stderr_buffer);
+	buffer_init(bin);
+	buffer_init(bout);
+	buffer_init(berr);
 
 	enter_raw_mode();
 }
@@ -466,12 +467,155 @@
 	}
 }
 
+/* process the characters one by one */
+int
+process_escapes(Buffer *bin, Buffer *bout, Buffer *berr, char *buf, int len)
+{
+	char string[1024];
+	pid_t pid;
+	int bytes = 0;
+	unsigned int i;
+	unsigned char ch;
+	char *s;
+
+	for (i = 0; i < len; i++) {
+		/* Get one character at a time. */
+		ch = buf[i];
+
+		if (escape_pending) {
+			/* We have previously seen an escape character. */
+			/* Clear the flag now. */
+			escape_pending = 0;
+
+			/* Process the escaped character. */
+			switch (ch) {
+			case '.':
+				/* Terminate the connection. */
+				snprintf(string, sizeof string, "%c.\r\n", escape_char);
+				buffer_append(berr, string, strlen(string));
+				/*stderr_bytes += strlen(string); XXX*/
+
+				quit_pending = 1;
+				return -1;
+
+			case 'Z' - 64:
+				/* Suspend the program. */
+				/* Print a message to that effect to the user. */
+				snprintf(string, sizeof string, "%c^Z [suspend ssh]\r\n", escape_char);
+				buffer_append(berr, string, strlen(string));
+				/*stderr_bytes += strlen(string); XXX*/
+
+				/* Restore terminal modes and suspend. */
+				client_suspend_self(bin, bout, berr);
+
+				/* We have been continued. */
+				continue;
+
+			case '&':
+				/* XXX does not work yet with proto 2 */
+				if (compat20)
+					continue;
+				/*
+				 * Detach the program (continue to serve connections,
+				 * but put in background and no more new connections).
+				 */
+				if (!stdin_eof) {
+					/*
+					 * Sending SSH_CMSG_EOF alone does not always appear
+					 * to be enough.  So we try to send an EOF character
+					 * first.
+					 */
+					packet_start(SSH_CMSG_STDIN_DATA);
+					packet_put_string("\004", 1);
+					packet_send();
+					/* Close stdin. */
+					stdin_eof = 1;
+					if (buffer_len(bin) == 0) {
+						packet_start(SSH_CMSG_EOF);
+						packet_send();
+					}
+				}
+				/* Restore tty modes. */
+				leave_raw_mode();
+
+				/* Stop listening for new connections. */
+				channel_stop_listening();
+
+				printf("%c& [backgrounded]\n", escape_char);
+
+				/* Fork into background. */
+				pid = fork();
+				if (pid < 0) {
+					error("fork: %.100s", strerror(errno));
+					continue;
+				}
+				if (pid != 0) {	/* This is the parent. */
+					/* The parent just exits. */
+					exit(0);
+				}
+				/* The child continues serving connections. */
+				continue; /*XXX ? */
+
+			case '?':
+				snprintf(string, sizeof string,
+"%c?\r\n\
+Supported escape sequences:\r\n\
+~.  - terminate connection\r\n\
+~^Z - suspend ssh\r\n\
+~#  - list forwarded connections\r\n\
+~&  - background ssh (when waiting for connections to terminate)\r\n\
+~?  - this message\r\n\
+~~  - send the escape character by typing it twice\r\n\
+(Note that escapes are only recognized immediately after newline.)\r\n",
+					 escape_char);
+				buffer_append(berr, string, strlen(string));
+				continue;
+
+			case '#':
+				snprintf(string, sizeof string, "%c#\r\n", escape_char);
+				buffer_append(berr, string, strlen(string));
+				s = channel_open_message();
+				buffer_append(berr, s, strlen(s));
+				xfree(s);
+				continue;
+
+			default:
+				if (ch != escape_char) {
+					buffer_put_char(bin, escape_char);
+					bytes++;
+				}
+				/* Escaped characters fall through here */
+				break;
+			}
+		} else {
+			/*
+			 * The previous character was not an escape char. Check if this
+			 * is an escape.
+			 */
+			if (last_was_cr && ch == escape_char) {
+				/* It is. Set the flag and continue to next character. */
+				escape_pending = 1;
+				continue;
+			}
+		}
+
+		/*
+		 * Normal character.  Record whether it was a newline,
+		 * and append it to the buffer.
+		 */
+		last_was_cr = (ch == '\r' || ch == '\n');
+		buffer_put_char(bin, ch);
+		bytes++;
+	}
+	return bytes;
+}
+
 void
 client_process_input(fd_set * readset)
 {
+	int ret;
 	int len;
-	pid_t pid;
-	char buf[8192], *s;
+	char buf[8192];
 
 	/* Read input from stdin. */
 	if (FD_ISSET(fileno(stdin), readset)) {
@@ -513,145 +657,10 @@
 			 * Normal, successful read.  But we have an escape character
 			 * and have to process the characters one by one.
 			 */
-			unsigned int i;
-			for (i = 0; i < len; i++) {
-				unsigned char ch;
-				/* Get one character at a time. */
-				ch = buf[i];
-
-				if (escape_pending) {
-					/* We have previously seen an escape character. */
-					/* Clear the flag now. */
-					escape_pending = 0;
-					/* Process the escaped character. */
-					switch (ch) {
-					case '.':
-						/* Terminate the connection. */
-						snprintf(buf, sizeof buf, "%c.\r\n", escape_char);
-						buffer_append(&stderr_buffer, buf, strlen(buf));
-						stderr_bytes += strlen(buf);
-						quit_pending = 1;
-						return;
-
-					case 'Z' - 64:
-						/* Suspend the program. */
-						/* Print a message to that effect to the user. */
-						snprintf(buf, sizeof buf, "%c^Z\r\n", escape_char);
-						buffer_append(&stderr_buffer, buf, strlen(buf));
-						stderr_bytes += strlen(buf);
-
-						/* Restore terminal modes and suspend. */
-						client_suspend_self();
-
-						/* We have been continued. */
-						continue;
-
-					case '&':
-						/*
-						 * Detach the program (continue to serve connections,
-						 * but put in background and no more new connections).
-						 */
-						if (!stdin_eof) {
-							/*
-							 * Sending SSH_CMSG_EOF alone does not always appear
-							 * to be enough.  So we try to send an EOF character
-							 * first.
-							 */
-							packet_start(SSH_CMSG_STDIN_DATA);
-							packet_put_string("\004", 1);
-							packet_send();
-							/* Close stdin. */
-							stdin_eof = 1;
-							if (buffer_len(&stdin_buffer) == 0) {
-								packet_start(SSH_CMSG_EOF);
-								packet_send();
-							}
-						}
-						/* Restore tty modes. */
-						leave_raw_mode();
-
-						/* Stop listening for new connections. */
-						channel_stop_listening();
-
-						printf("%c& [backgrounded]\n", escape_char);
-
-						/* Fork into background. */
-						pid = fork();
-						if (pid < 0) {
-							error("fork: %.100s", strerror(errno));
-							continue;
-						}
-						if (pid != 0) {	/* This is the parent. */
-							/* The parent just exits. */
-							exit(0);
-						}
-						/* The child continues serving connections. */
-						continue;
-
-					case '?':
-						snprintf(buf, sizeof buf,
-"%c?\r\n\
-Supported escape sequences:\r\n\
-~.  - terminate connection\r\n\
-~^Z - suspend ssh\r\n\
-~#  - list forwarded connections\r\n\
-~&  - background ssh (when waiting for connections to terminate)\r\n\
-~?  - this message\r\n\
-~~  - send the escape character by typing it twice\r\n\
-(Note that escapes are only recognized immediately after newline.)\r\n",
-							 escape_char);
-						buffer_append(&stderr_buffer, buf, strlen(buf));
-						continue;
-
-					case '#':
-						snprintf(buf, sizeof buf, "%c#\r\n", escape_char);
-						buffer_append(&stderr_buffer, buf, strlen(buf));
-						s = channel_open_message();
-						buffer_append(&stderr_buffer, s, strlen(s));
-						xfree(s);
-						continue;
-
-					default:
-						if (ch != escape_char) {
-							/*
-							 * Escape character followed by non-special character.
-							 * Append both to the input buffer.
-							 */
-							buf[0] = escape_char;
-							buf[1] = ch;
-							buffer_append(&stdin_buffer, buf, 2);
-							stdin_bytes += 2;
-							continue;
-						}
-						/*
-						 * Note that escape character typed twice
-						 * falls through here; the latter gets processed
-						 * as a normal character below.
-						 */
-						break;
-					}
-				} else {
-					/*
-					 * The previous character was not an escape char. Check if this
-					 * is an escape.
-					 */
-					if (last_was_cr && ch == escape_char) {
-						/* It is. Set the flag and continue to next character. */
-						escape_pending = 1;
-						continue;
-					}
-				}
-
-				/*
-				 * Normal character.  Record whether it was a newline,
-				 * and append it to the buffer.
-				 */
-				last_was_cr = (ch == '\r' || ch == '\n');
-				buf[0] = ch;
-				buffer_append(&stdin_buffer, buf, 1);
-				stdin_bytes += 1;
-				continue;
-			}
+			ret = process_escapes(&stdin_buffer, &stdout_buffer, &stderr_buffer, buf, len);
+			if (ret == -1)
+				return;
+			stdout_bytes += ret;
 		}
 	}
 }
@@ -722,6 +731,15 @@
 	dispatch_run(DISPATCH_NONBLOCK, &quit_pending);
 }
 
+/* scan buf[] for '~' before sending data to the peer */
+
+int
+simple_escape_filter(Channel *c, char *buf, int len)
+{
+	/* XXX we assume c->extended is writeable */
+	return process_escapes(&c->input, &c->output, &c->extended, buf, len);
+}
+
 /*
  * Implements the interactive session with the server.  This is called after
  * the user has been authenticated, and a command has been started on the
@@ -730,7 +748,7 @@
  */
 
 int
-client_loop(int have_pty, int escape_char_arg)
+client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id)
 {
 	extern Options options;
 	double start_time, total_time;
@@ -780,6 +798,9 @@
 	if (!compat20)
 		client_check_initial_eof_on_stdin();
 
+	if (compat20 && escape_char != -1)
+		channel_register_filter(ssh2_chan_id, simple_escape_filter);
+
 	/* Main loop of the client for the interactive session mode. */
 	while (!quit_pending) {
 		fd_set readset, writeset;
@@ -989,6 +1010,7 @@
 		/* XXX move to channels.c */
 		sock = x11_connect_display();
 		if (sock >= 0) {
+/*XXX MAXPACK */
 			id = channel_new("x11", SSH_CHANNEL_X11_OPEN,
 			    sock, sock, -1, 4*1024, 32*1024, 0,
 			    xstrdup("x11"));