From 172b1e5cef4cba12de2b3010eb3b21be9c60f9f6 Mon Sep 17 00:00:00 2001
From: Alexander Chernyakhovsky <achernya@mit.edu>
Date: Wed, 27 Mar 2013 02:15:25 -0400
Subject: [PATCH 01/40] Cast time_elapsed to int before calling
 human_readable_duration

When attempting to build againt EPEL 5, it was noticed that
human_readable_duration expects an int, but time_elapsed is an
integer.  Explicitly static_cast<int>( time_elapsed ) to appease older
compilers.
---
 src/frontend/terminaloverlay.cc | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/src/frontend/terminaloverlay.cc b/src/frontend/terminaloverlay.cc
index cd28f50..9c68d7b 100644
--- a/src/frontend/terminaloverlay.cc
+++ b/src/frontend/terminaloverlay.cc
@@ -242,13 +242,16 @@ void NotificationEngine::apply( Framebuffer &fb ) const
     return;
   } else if ( message.empty() && time_expired ) {
     swprintf( tmp, 128, L"mosh: Last %s %s ago.%s", explanation,
-	      human_readable_duration( time_elapsed, "seconds" ).c_str(),
+	      human_readable_duration( static_cast<int>( time_elapsed ),
+				       "seconds" ).c_str(),
 	      keystroke_str );
   } else if ( (!message.empty()) && (!time_expired) ) {
     swprintf( tmp, 128, L"mosh: %ls%s", message.c_str(), keystroke_str );
   } else {
     swprintf( tmp, 128, L"mosh: %ls (%s without %s.)%s", message.c_str(),
-	      human_readable_duration( time_elapsed, "s" ).c_str(), explanation, keystroke_str );
+	      human_readable_duration( static_cast<int>( time_elapsed ),
+				       "s" ).c_str(),
+	      explanation, keystroke_str );
   }
 
   wstring string_to_draw( tmp );

From 4a37359704dacd2b86b332432ee54f3e1dfd833d Mon Sep 17 00:00:00 2001
From: Alexander Chernyakhovsky <achernya@mit.edu>
Date: Wed, 27 Mar 2013 14:49:58 -0400
Subject: [PATCH 02/40] Update mosh.spec for mosh 1.2.4

---
 fedora/mosh.spec | 11 ++++++++++-
 1 file changed, 10 insertions(+), 1 deletion(-)

diff --git a/fedora/mosh.spec b/fedora/mosh.spec
index b8fa1ef..68cbbff 100644
--- a/fedora/mosh.spec
+++ b/fedora/mosh.spec
@@ -1,5 +1,5 @@
 Name:		mosh
-Version:	1.2.3
+Version:	1.2.4
 Release:	1%{?dist}
 Summary:	Mobile shell that supports roaming and intelligent local echo
 
@@ -51,6 +51,15 @@ make install DESTDIR=$RPM_BUILD_ROOT
 
 
 %changelog
+* Wed Mar 27 2013 Alexander Chernyakhovsky <achernya@mit.edu> - 1.2.4-1
+- Update to mosh 1.2.4
+
+* Sun Mar 10 2013 Alexander Chernyakhovsky <achernya@mit.edu> - 1.2.3-3
+- Rebuilt for Protobuf API change from 2.4.1 to 2.5.0
+
+* Thu Feb 14 2013 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.2.3-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_19_Mass_Rebuild
+
 * Fri Oct 19 2012 Alexander Chernyakhovsky <achernya@mit.edu> - 1.2.3-1
 - Update to mosh 1.2.3
 

From 9669178f07e3fe0b24f8982164ff4ee218db2515 Mon Sep 17 00:00:00 2001
From: Keith Winstein <keithw@mit.edu>
Date: Wed, 27 Mar 2013 15:21:20 -0400
Subject: [PATCH 03/40] Eliminate ocb.cc test program (closes #408)

This test doesn't return an error on failure and also was triggering a
stack protector warning on some platforms. We have an end-to-end test of
OCB in src/tests/ocb-aes.cc that seems to work well.
---
 src/crypto/Makefile.am | 7 -------
 1 file changed, 7 deletions(-)

diff --git a/src/crypto/Makefile.am b/src/crypto/Makefile.am
index 995c388..3f3b138 100644
--- a/src/crypto/Makefile.am
+++ b/src/crypto/Makefile.am
@@ -15,10 +15,3 @@ libmoshcrypto_a_SOURCES = \
 	crypto.cc \
 	crypto.h \
 	prng.h
-
-check_PROGRAMS = ocb-test
-TESTS = ocb-test
-
-ocb_test_SOURCES = $(OCB_SRCS)
-ocb_test_CPPFLAGS = -DOCB_TEST_PROGRAM
-ocb_test_LDADD = $(OPENSSL_LIBS)

From 1b9915fcaf6205f718433b904618e1e5a4602c66 Mon Sep 17 00:00:00 2001
From: Keith Winstein <keithw@mit.edu>
Date: Wed, 27 Mar 2013 18:34:21 -0400
Subject: [PATCH 04/40] Bump version to 1.2.4a

---
 configure.ac     | 2 +-
 debian/changelog | 7 +++++++
 scripts/mosh     | 2 +-
 3 files changed, 9 insertions(+), 2 deletions(-)

diff --git a/configure.ac b/configure.ac
index 11d3ecc..282f4ed 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2,7 +2,7 @@
 # Process this file with autoconf to produce a configure script.
 
 AC_PREREQ([2.61])
-AC_INIT([mosh], [1.2.4], [mosh-devel@mit.edu])
+AC_INIT([mosh], [1.2.4a], [mosh-devel@mit.edu])
 AM_INIT_AUTOMAKE([-Wall -Werror foreign])
 m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
 AC_CONFIG_SRCDIR([src/frontend/mosh-client.cc])
diff --git a/debian/changelog b/debian/changelog
index d5ba2ba..47f588f 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,10 @@
+mosh (1.2.4a-1) unstable; urgency=low
+
+  * Eliminate redundant ocb.cc test (fixes build warning on ARM/MIPS/s390)
+  * Add explicit cast to int (fixes build warning on older gcc)
+
+ -- Keith Winstein <keithw@mit.edu>  Wed, 27 Mar 2013 18:32:33 -0400
+
 mosh (1.2.4-1) unstable; urgency=low
 
   * Version 1.2.4 released.
diff --git a/scripts/mosh b/scripts/mosh
index 30da42e..c9e6c29 100755
--- a/scripts/mosh
+++ b/scripts/mosh
@@ -30,7 +30,7 @@
 #   this exception statement from all source files in the program, then
 #   also delete it here.
 
-my $MOSH_VERSION = '1.2.4';
+my $MOSH_VERSION = '1.2.4a';
 
 use warnings;
 use strict;

From fb39fe2c7ed9fd09f3a6b5eaa7484aa743b82f16 Mon Sep 17 00:00:00 2001
From: Anton Lundin <glance@acc.umu.se>
Date: Tue, 22 Jan 2013 20:29:01 +0100
Subject: [PATCH 05/40] Simplify pipe management

We don't need to write perl like C.
---
 scripts/mosh | 14 ++++----------
 1 file changed, 4 insertions(+), 10 deletions(-)

diff --git a/scripts/mosh b/scripts/mosh
index c9e6c29..421dbb1 100755
--- a/scripts/mosh
+++ b/scripts/mosh
@@ -217,15 +217,10 @@ if ( (not defined $colors)
   $colors = 0;
 }
 
-my ($p_read, $p_write);
-pipe($p_read, $p_write);
-my $pid = fork;
+my $pid = open(my $pipe, "-|");
 die "$0: fork: $!\n" unless ( defined $pid );
 if ( $pid == 0 ) { # child
-  open STDOUT, ">&", $p_write or die;
-  open STDERR, ">&", $p_write or die;
-  close $p_write;
-  close $p_read;
+  open(STDERR, ">&STDOUT") or die;
 
   my @server = ( 'new', '-s' );
 
@@ -249,8 +244,7 @@ if ( $pid == 0 ) { # child
 } else { # parent
   my ( $ip, $port, $key );
   my $bad_udp_port_warning = 0;
-  close $p_write;
-  LINE: while ( <$p_read> ) {
+  LINE: while ( <$pipe> ) {
     chomp;
     if ( m{^MOSH IP } ) {
       if ( defined $ip ) {
@@ -271,7 +265,7 @@ if ( $pid == 0 ) { # child
     }
   }
   waitpid $pid, 0;
-  close $p_read;
+  close $pipe;
 
   if ( not defined $ip ) {
       die "$0: Did not find remote IP address (is SSH ProxyCommand disabled?).\n";

From 8ff636d54e7fbb73a334f0b268239a593b0d7b42 Mon Sep 17 00:00:00 2001
From: Anders Kaseorg <andersk@mit.edu>
Date: Tue, 9 Apr 2013 18:04:43 -0400
Subject: [PATCH 06/40] Work around automake bug that dropped portability
 warnings
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

There’s a bug in automake ≥ 1.10, < 1.12 where ‘-Wall foreign’
incorrectly turns off warnings about portability issues.  To get
consistent results across automake versions, use ‘foreign -Wall’
instead.

http://debbugs.gnu.org/cgi-bin/bugreport.cgi?bug=7669

Signed-off-by: Anders Kaseorg <andersk@mit.edu>

Closes #413
---
 configure.ac | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/configure.ac b/configure.ac
index 282f4ed..5be21c8 100644
--- a/configure.ac
+++ b/configure.ac
@@ -3,7 +3,7 @@
 
 AC_PREREQ([2.61])
 AC_INIT([mosh], [1.2.4a], [mosh-devel@mit.edu])
-AM_INIT_AUTOMAKE([-Wall -Werror foreign])
+AM_INIT_AUTOMAKE([foreign -Wall -Werror])
 m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
 AC_CONFIG_SRCDIR([src/frontend/mosh-client.cc])
 AC_CONFIG_MACRO_DIR([m4])

From fbd2d184fb37a10ec7199ba72e83d4274a605387 Mon Sep 17 00:00:00 2001
From: Andrew Chin <achin@eminence32.net>
Date: Sat, 30 Mar 2013 23:26:52 -0400
Subject: [PATCH 07/40] In the OSX build script, link with system libs.

Previously, mosh was linking with macports-provided libs in
/opt/local/lib, which was not portable to users without macports

Closes #411
---
 macosx/build.sh | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/macosx/build.sh b/macosx/build.sh
index 2d08801..982a6c8 100755
--- a/macosx/build.sh
+++ b/macosx/build.sh
@@ -27,14 +27,16 @@ fi
 echo "Building for x86_64..."
 ./configure --prefix="$PREFIX_x86_64" ac_cv_poll_pty=no \
     CC="clang -arch x86_64" CPP="clang -arch x86_64 -E" CXX="clang++ -arch x86_64" \
-    TINFO_LIBS=-lncurses protobuf_LIBS=/opt/local/lib/libprotobuf.a
+    TINFO_LIBS=-lncurses protobuf_LIBS=/opt/local/lib/libprotobuf.a \
+    OPENSSL_CFLAGS="  " OPENSSL_LIBS="-lssl -lcrypto -lz"
 make clean
 make install -j8
 
 echo "Building for i386..."
 ./configure --prefix="$PREFIX_i386" ac_cv_poll_pty=no \
     CC="clang -arch i386" CPP="clang -arch i386 -E" CXX="clang++ -arch i386" \
-    TINFO_LIBS=-lncurses protobuf_LIBS=/opt/local/lib/libprotobuf.a
+    TINFO_LIBS=-lncurses protobuf_LIBS=/opt/local/lib/libprotobuf.a \
+    OPENSSL_CFLAGS="  " OPENSSL_LIBS="-lssl -lcrypto -lz"
 make clean
 make install -j8
 

From 24b078a5a126fc7c7c207747d5f2e20022264e9f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20Courr=C3=A8ges-Anglas?= <jca@wxcvbn.org>
Date: Tue, 16 Apr 2013 11:09:42 +0200
Subject: [PATCH 08/40] workaround for systems not providing pselect
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

using a mix of good old select and sigprocmask

Signed-off-by: Jérémie Courrèges-Anglas <jca@wxcvbn.org>
---
 configure.ac      |  2 +-
 src/util/select.h | 21 +++++++++++++++++++--
 2 files changed, 20 insertions(+), 3 deletions(-)

diff --git a/configure.ac b/configure.ac
index 5be21c8..11153ea 100644
--- a/configure.ac
+++ b/configure.ac
@@ -195,7 +195,7 @@ AC_TYPE_UINTPTR_T
 # Checks for library functions.
 AC_FUNC_FORK
 AC_FUNC_MBRTOWC
-AC_CHECK_FUNCS([gettimeofday setrlimit inet_ntoa iswprint memchr memset nl_langinfo posix_memalign setenv setlocale sigaction socket strchr strdup strncasecmp strtok strerror strtol wcwidth cfmakeraw])
+AC_CHECK_FUNCS([gettimeofday setrlimit inet_ntoa iswprint memchr memset nl_langinfo posix_memalign setenv setlocale sigaction socket strchr strdup strncasecmp strtok strerror strtol wcwidth cfmakeraw pselect])
 
 AC_SEARCH_LIBS([clock_gettime], [rt], [AC_DEFINE([HAVE_CLOCK_GETTIME], [1], [Define if clock_gettime is available.])])
 
diff --git a/src/util/select.h b/src/util/select.h
index dc06716..6d9922f 100644
--- a/src/util/select.h
+++ b/src/util/select.h
@@ -118,6 +118,7 @@ class Select {
     fatal_assert( 0 == sigaction( signum, &sa, NULL ) );
   }
 
+  /* timeout unit: milliseconds; negative timeout means wait forever */
   int select( int timeout )
   {
     memcpy( &read_fds,  &all_fds, sizeof( read_fds  ) );
@@ -125,18 +126,34 @@ class Select {
     clear_got_signal();
     got_any_signal = 0;
 
+#ifdef HAVE_PSELECT
     struct timespec ts;
     struct timespec *tsp = NULL;
 
     if ( timeout >= 0 ) {
-      // timeout in milliseconds
       ts.tv_sec  = timeout / 1000;
       ts.tv_nsec = 1000000 * (long( timeout ) % 1000);
       tsp = &ts;
     }
-    // negative timeout means wait forever
 
     int ret = ::pselect( max_fd + 1, &read_fds, NULL, &error_fds, tsp, &empty_sigset );
+#else
+    struct timeval tv;
+    struct timeval *tvp = NULL;
+    sigset_t old_sigset;
+
+    if ( timeout >= 0 ) {
+      tv.tv_sec  = timeout / 1000;
+      tv.tv_usec = 1000 * (long( timeout ) % 1000);
+      tvp = &tv;
+    }
+
+    int ret = sigprocmask( SIG_SETMASK, &empty_sigset, &old_sigset );
+    if ( ret != -1 ) {
+      ret = ::select( max_fd + 1, &read_fds, NULL, &error_fds, tvp );
+      sigprocmask( SIG_SETMASK, &old_sigset, NULL );
+    }
+#endif
 
     if ( ( ret == -1 ) && ( errno == EINTR ) ) {
       /* The user should process events as usual. */

From 9314ea18fab323f3b981a412d7a765f66abfaab3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20Courr=C3=A8ges-Anglas?= <jca@wxcvbn.org>
Date: Tue, 16 Apr 2013 11:11:45 +0200
Subject: [PATCH 09/40] use betoh64 if be64toh not found
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Instead of looking for htobe64 which is be available both when
be64toh or betoh64 are, check for the latter functions.  If we
find betoh64 but not be64toh, use compat #defines.  If both
can't be found, search for OSX' OSSwapHostToBigInt64.
Also include sys/types.h in byteorder.h (which is necessary for
byteorder functions on OpenBSD), and incidentally fixes build
of networkfragment.cc.

Fixes build on OpenBSD

Signed-off-by: Jérémie Courrèges-Anglas <jca@wxcvbn.org>
---
 configure.ac           | 18 ++++++++++--------
 src/crypto/byteorder.h |  9 ++++++++-
 2 files changed, 18 insertions(+), 9 deletions(-)

diff --git a/configure.ac b/configure.ac
index 11153ea..b07291a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -285,20 +285,22 @@ AC_CHECK_DECL([mach_absolute_time],
      [Define if mach_absolute_time is available.])],
   , [[#include <mach/mach_time.h>]])
 
-AC_CHECK_DECL([htobe64],
-  [AC_DEFINE([HAVE_HTOBE64], [1],
-  [Define if htobe64 and friends exist.])],
-  [AC_CHECK_DECL([OSSwapHostToBigInt64],
-     [AC_DEFINE([HAVE_OSX_SWAP], [1],
-        [Define if OSSwapHostToBigInt64 and friends exist.])],
-     [AC_MSG_WARN([Unable to find byte swapping functions; using built-in routines.])],
-     [[#include <libkern/OSByteOrder.h>]])],
+AC_CHECK_DECLS([be64toh, betoh64], [], [],
   [[#if defined(HAVE_ENDIAN_H)
 #include <endian.h>
 #elif defined(HAVE_SYS_ENDIAN_H)
+#include <sys/types.h>
 #include <sys/endian.h>
 #endif]])
 
+AS_IF([test x"$ac_cv_have_decl_be64toh" != xyes &&
+       test x"$ac_cv_have_decl_betoh64" != xyes],
+  [AC_CHECK_DECL([OSSwapHostToBigInt64],
+     [AC_DEFINE([HAVE_OSX_SWAP], [1],
+        [Define if OSSwapHostToBigInt64 and friends exist.])],
+     [AC_MSG_WARN([Unable to find byte swapping functions; using built-in routines.])],
+     [[#include <libkern/OSByteOrder.h>]])])
+
 AC_CHECK_DECL([IP_MTU_DISCOVER],
   [AC_DEFINE([HAVE_IP_MTU_DISCOVER], [1],
      [Define if IP_MTU_DISCOVER is a valid sockopt.])],
diff --git a/src/crypto/byteorder.h b/src/crypto/byteorder.h
index a5f0a47..e5f9baa 100644
--- a/src/crypto/byteorder.h
+++ b/src/crypto/byteorder.h
@@ -35,13 +35,20 @@
 
 #include "config.h"
 
-#ifdef HAVE_HTOBE64
+#if HAVE_DECL_BE64TOH || HAVE_DECL_BETOH64
+
 # if defined(HAVE_ENDIAN_H)
 #  include <endian.h>
 # elif defined(HAVE_SYS_ENDIAN_H)
+#  include <sys/types.h>
 #  include <sys/endian.h>
 # endif
 
+#if !HAVE_DECL_BE64TOH && HAVE_DECL_BETOH64
+#define be64toh betoh64
+#define be16toh betoh16
+#endif
+
 #elif HAVE_OSX_SWAP
 # include <libkern/OSByteOrder.h>
 # define htobe64 OSSwapHostToBigInt64

From fc70612de6d1390c943b7a470699e73dced5f110 Mon Sep 17 00:00:00 2001
From: Philipp Haselwarter <philipp@haselwarter.org>
Date: Tue, 16 Apr 2013 18:37:40 +0200
Subject: [PATCH 10/40] mosh perl wrapper: New option --bind-ip={ssh|IP}

Allow overriding the switch passed to mosh-server from -s to -i IP.

Signed-off-by: Philipp Haselwarter <philipp@haselwarter.org>
---
 man/mosh.1   | 10 +++++++++-
 scripts/mosh | 16 ++++++++++++++--
 2 files changed, 23 insertions(+), 3 deletions(-)

diff --git a/man/mosh.1 b/man/mosh.1
index 9463740..f7f3efe 100644
--- a/man/mosh.1
+++ b/man/mosh.1
@@ -2,7 +2,7 @@
 .\" First parameter, NAME, should be all caps
 .\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
 .\" other parameters are allowed: see man(7), man(1)
-.TH MOSH 1 "October 2012"
+.TH MOSH 1 "April 2013"
 .\" Please adjust this date whenever revising the manpage.
 .\"
 .\" Some roff macros, for reference:
@@ -129,6 +129,14 @@ server. Otherwise, \fBmosh\fP will choose a port between 60000 and
 61000.
 
 .TP
+.B \-\-bind\-ip={ssh|\fIIP\fP}
+Bind the server to the ip of the ssh client or to IP by passing either the -s
+switch (default) or -i IP to \fBmosh-server\fP. This is useful when the login
+happens from an address different from the one used by the \fBmosh-client\fP.
+For example, if ssh is served through \fBsslh\fP, \fBSSH_CONNECTION\fP might be
+set to 127.0.0.1, but \fBmosh-server\fP can still be bound to 0.0.0.0.
+
+.TP
 .B \-\-no\-init
 Do not send the \fBsmcup\fP initialization string and \fBrmcup\fP
 deinitialization string to the client's terminal. On many terminals
diff --git a/scripts/mosh b/scripts/mosh
index 421dbb1..36b5c9c 100755
--- a/scripts/mosh
+++ b/scripts/mosh
@@ -44,6 +44,8 @@ my $server = 'mosh-server';
 
 my $predict = undef;
 
+my $bind_ip = undef;
+
 my $port_request = undef;
 
 my $ssh = 'ssh';
@@ -69,6 +71,9 @@ qq{Usage: $0 [options] [--] [user@]host [command...]
 
 -p PORT[:PORT2]
         --port=PORT[:PORT2]  server-side UDP port or range
+        --bind-ip={ssh|IP}   bind the server to the ssh client's ip or to IP
+                                (example: "0.0.0.0")
+                                (default: "ssh")
 
         --ssh=COMMAND        ssh command to run when setting up session
                                 (example: "ssh -p 2222")
@@ -111,7 +116,8 @@ GetOptions( 'client=s' => \$client,
 	    'init!' => \$term_init,
 	    'help' => \$help,
 	    'version' => \$version,
-	    'fake-proxy!' => \my $fake_proxy ) or die $usage;
+	    'fake-proxy!' => \my $fake_proxy,
+	    'bind-ip=s' => \$bind_ip) or die $usage;
 
 die $usage if ( defined $help );
 die $version_message if ( defined $version );
@@ -222,10 +228,16 @@ die "$0: fork: $!\n" unless ( defined $pid );
 if ( $pid == 0 ) { # child
   open(STDERR, ">&STDOUT") or die;
 
-  my @server = ( 'new', '-s' );
+  my @server = ( 'new' );
 
   push @server, ( '-c', $colors );
 
+  if ( not defined $bind_ip or $bind_ip =~ 'ssh' ) {
+      push @server , '-s';
+  } else {
+      push @server, ('-i', "$bind_ip");
+  }
+
   if ( defined $port_request ) {
     push @server, ( '-p', $port_request );
   }

From 4792992afa78d606fff35346bcce5a819fc72b69 Mon Sep 17 00:00:00 2001
From: Keith Winstein <keithw@mit.edu>
Date: Sun, 28 Apr 2013 15:33:36 -0400
Subject: [PATCH 11/40] Rename --bind-ip to --bind-server, add =ANY option, add
 error checking.

Closes #415.
---
 man/mosh.1   | 21 +++++++++++++++------
 scripts/mosh | 26 +++++++++++++++++---------
 2 files changed, 32 insertions(+), 15 deletions(-)

diff --git a/man/mosh.1 b/man/mosh.1
index f7f3efe..14405bf 100644
--- a/man/mosh.1
+++ b/man/mosh.1
@@ -129,12 +129,21 @@ server. Otherwise, \fBmosh\fP will choose a port between 60000 and
 61000.
 
 .TP
-.B \-\-bind\-ip={ssh|\fIIP\fP}
-Bind the server to the ip of the ssh client or to IP by passing either the -s
-switch (default) or -i IP to \fBmosh-server\fP. This is useful when the login
-happens from an address different from the one used by the \fBmosh-client\fP.
-For example, if ssh is served through \fBsslh\fP, \fBSSH_CONNECTION\fP might be
-set to 127.0.0.1, but \fBmosh-server\fP can still be bound to 0.0.0.0.
+.B \-\-bind\-server={ssh|any|\fIIP\fP}
+Control the IP address that the \fBmosh-server\fP binds to.
+
+The default is `ssh', in which case the server will reply from the IP
+address that the SSH connection came from (as found in the
+\fBSSH_CONNECTION\fP environment variable). This is useful for
+multihomed servers.
+
+With \-\-bind\-server=any, the server will reply on the default interface
+and will not bind to a particular IP address. This can be useful if
+the connection is made through \fBsslh\fP or another tool that makes
+the SSH connection appear to come from localhost.
+
+With \-\-bind\-server=\fIIP\fP, the server will attempt to bind to the
+specified IP address.
 
 .TP
 .B \-\-no\-init
diff --git a/scripts/mosh b/scripts/mosh
index 36b5c9c..4e8b796 100755
--- a/scripts/mosh
+++ b/scripts/mosh
@@ -71,9 +71,8 @@ qq{Usage: $0 [options] [--] [user@]host [command...]
 
 -p PORT[:PORT2]
         --port=PORT[:PORT2]  server-side UDP port or range
-        --bind-ip={ssh|IP}   bind the server to the ssh client's ip or to IP
-                                (example: "0.0.0.0")
-                                (default: "ssh")
+        --bind-server={ssh|any|IP}  ask the server to reply from an IP address
+                                       (default: "ssh")
 
         --ssh=COMMAND        ssh command to run when setting up session
                                 (example: "ssh -p 2222")
@@ -117,7 +116,7 @@ GetOptions( 'client=s' => \$client,
 	    'help' => \$help,
 	    'version' => \$version,
 	    'fake-proxy!' => \my $fake_proxy,
-	    'bind-ip=s' => \$bind_ip) or die $usage;
+	    'bind-server=s' => \$bind_ip) or die $usage;
 
 die $usage if ( defined $help );
 die $version_message if ( defined $version );
@@ -154,6 +153,19 @@ if ( defined $port_request ) {
 
 delete $ENV{ 'MOSH_PREDICTION_DISPLAY' };
 
+my @bind_arguments;
+if ( not defined $bind_ip or $bind_ip =~ m{^ssh$}i ) {
+  push @bind_arguments, '-s';
+} elsif ( $bind_ip =~ m{^any$}i ) {
+  # do nothing
+} elsif ( $bind_ip =~ m{^[0-9\.]+$} ) {
+  push @bind_arguments, ('-i', "$bind_ip");
+} else {
+  print STDERR qq{$0: Unknown server binding option: $bind_ip\n};
+
+  die $usage;
+}
+
 if ( defined $fake_proxy ) {
   use Errno qw(EINTR);
   use IO::Socket::INET;
@@ -232,11 +244,7 @@ if ( $pid == 0 ) { # child
 
   push @server, ( '-c', $colors );
 
-  if ( not defined $bind_ip or $bind_ip =~ 'ssh' ) {
-      push @server , '-s';
-  } else {
-      push @server, ('-i', "$bind_ip");
-  }
+  push @server, @bind_arguments;
 
   if ( defined $port_request ) {
     push @server, ( '-p', $port_request );

From f960a8bcf56294ca7aafe3c14fc8c8c4c3941535 Mon Sep 17 00:00:00 2001
From: "Timo J. Rinne" <tri@iki.fi>
Date: Thu, 16 May 2013 18:09:15 +0000
Subject: [PATCH 12/40] mosh-client: Make terminal escape character
 configurable

Uses environment variable MOSH_ESCAPE_KEY. Defaults to current Ctrl-^
which is somewhat problematic for many non-US keyboards.

Signed-off-by: Timo J. Rinne <tri@iki.fi>

Closes #425. Closes #215.
---
 src/frontend/stmclient.cc | 90 +++++++++++++++++++++++++++++++++++++++++------
 src/frontend/stmclient.h  | 11 +++++-
 2 files changed, 89 insertions(+), 12 deletions(-)

diff --git a/src/frontend/stmclient.cc b/src/frontend/stmclient.cc
index 7d68ab1..c2ff2a5 100644
--- a/src/frontend/stmclient.cc
+++ b/src/frontend/stmclient.cc
@@ -121,6 +121,72 @@ void STMClient::init( void )
     overlays.set_title_prefix( wstring( L"[mosh] " ) );
   }
 
+  /* Set terminal escape key. */
+  const char *escape_key_env;
+  if ( (escape_key_env = getenv( "MOSH_ESCAPE_KEY" )) != NULL ) {
+    if ( strlen( escape_key_env ) == 1 ) {
+      escape_key = (int)escape_key_env[0];
+      if ( (escape_key > 0) || (escape_key < 128) ) {
+	if ( escape_key < 32 ) {
+	  /* If escape is ctrl-something, pass it with repeating the key without ctrl. */
+	  escape_pass_key = escape_key + (int)'@';
+	} else {
+	  /* If escape is something else, pass it with repeating the key itself. */
+	  escape_pass_key = escape_key;
+	}
+	if ( escape_pass_key >= 'A' && escape_pass_key <= 'Z' ) {
+	  /* If escape pass is an upper case character, define optional version
+	     as lower case of the same. */
+	  escape_pass_key2 = escape_pass_key + (int)'a' - (int)'A';
+	} else {
+	  escape_pass_key2 = escape_pass_key;
+	}
+      } else {
+	escape_key = 0x1E;
+	escape_pass_key = '^';
+	escape_pass_key2 = '^';
+      }
+    } else if ( strlen( escape_key_env ) == 0 ) {
+      escape_key = -1;
+    } else {
+      escape_key = 0x1E;
+      escape_pass_key = '^';
+      escape_pass_key2 = '^';
+    }
+  } else {
+    escape_key = 0x1E;
+    escape_pass_key = '^';
+    escape_pass_key2 = '^';
+  }
+
+  /* There are so many better ways to shoot oneself into leg than
+     setting escape key to Ctrl-C, Ctrl-D, NewLine, Ctrl-L or CarriageReturn
+     that we just won't allow that. */
+  if ( escape_key == 0x03 || escape_key == 0x04 || escape_key == 0x0A || escape_key == 0x0C || escape_key == 0x0D ) {
+    escape_key = 0x1E;
+    escape_pass_key = '^';
+    escape_pass_key2 = '^';
+  }
+
+  /* Adjust escape help differently if escape is a control character. */
+  if ( escape_key > 0 ) {
+    char escape_pass_name_buf[16];
+    char escape_key_name_buf[16];
+    sprintf(escape_pass_name_buf, "\"%c\"", escape_pass_key);
+    if (escape_key < 32) {
+      sprintf(escape_key_name_buf, "Ctrl-%c", escape_pass_key);
+      escape_requires_lf = false;
+    } else {
+      sprintf(escape_key_name_buf, "\"%c\"", escape_key);
+      escape_requires_lf = true;
+    }
+    string tmp;
+    tmp = string( escape_pass_name_buf );
+    wstring escape_pass_name = std::wstring(tmp.begin(), tmp.end());
+    tmp = string( escape_key_name_buf );
+    wstring escape_key_name = std::wstring(tmp.begin(), tmp.end());
+    escape_key_help = L"Commands: Ctrl-Z suspends, \".\" quits, " + escape_pass_name + L" gives literal " + escape_key_name;
+  }
   wchar_t tmp[ 128 ];
   swprintf( tmp, 128, L"Nothing received from server on UDP port %d.", port );
   connecting_notification = wstring( tmp );
@@ -255,8 +321,6 @@ bool STMClient::process_user_input( int fd )
 
       overlays.get_prediction_engine().new_user_byte( the_byte, *local_framebuffer );
 
-      const static wstring help_message( L"Commands: Ctrl-Z suspends, \".\" quits, \"^\" gives literal Ctrl-^" );
-
       if ( quit_sequence_started ) {
 	if ( the_byte == '.' ) { /* Quit sequence is Ctrl-^ . */
 	  if ( network->has_remote_addr() && (!network->shutdown_in_progress()) ) {
@@ -266,7 +330,7 @@ bool STMClient::process_user_input( int fd )
 	  } else {
 	    return false;
 	  }
-	} else if ( the_byte == 0x1a ) { /* Suspend sequence is Ctrl-^ Ctrl-Z */
+	} else if ( the_byte == 0x1a ) { /* Suspend sequence is escape_key Ctrl-Z */
 	  /* Restore terminal and terminal-driver state */
 	  swrite( STDOUT_FILENO, display.close().c_str() );
 
@@ -283,30 +347,34 @@ bool STMClient::process_user_input( int fd )
 	  kill( 0, SIGSTOP );
 
 	  resume();
-	} else if ( the_byte == '^' ) {
-	  /* Emulation sequence to type Ctrl-^ is Ctrl-^ ^ */
-	  network->get_current_state().push_back( Parser::UserByte( 0x1E ) );
+	} else if ( (the_byte == escape_pass_key) || (the_byte == escape_pass_key2) ) {
+	  /* Emulation sequence to type escape_key is escape_key +
+	     escape_pass_key (that is escape key without Ctrl) */
+	  network->get_current_state().push_back( Parser::UserByte( escape_key ) );
 	} else {
-	  /* Ctrl-^ followed by anything other than . and ^ gets sent literally */
-	  network->get_current_state().push_back( Parser::UserByte( 0x1E ) );
+	  /* Escape key followed by anything other than . and ^ gets sent literally */
+	  network->get_current_state().push_back( Parser::UserByte( escape_key ) );
 	  network->get_current_state().push_back( Parser::UserByte( the_byte ) );	  
 	}
 
 	quit_sequence_started = false;
 
-	if ( overlays.get_notification_engine().get_notification_string() == help_message ) {
+	if ( overlays.get_notification_engine().get_notification_string() == escape_key_help ) {
 	  overlays.get_notification_engine().set_notification_string( L"" );
 	}
 
 	continue;
       }
 
-      quit_sequence_started = (the_byte == 0x1E);
+      quit_sequence_started = (escape_key > 0) && (the_byte == escape_key) && (lf_entered || (! escape_requires_lf));
       if ( quit_sequence_started ) {
-	overlays.get_notification_engine().set_notification_string( help_message, true, false );
+	lf_entered = false;
+	overlays.get_notification_engine().set_notification_string( escape_key_help, true, false );
 	continue;
       }
 
+      lf_entered = ( (the_byte == 0x0A) || (the_byte == 0x0D) ); /* LineFeed, Ctrl-J, '\n' or CarriageReturn, Ctrl-M, '\r' */
+
       if ( the_byte == 0x0C ) { /* Ctrl-L */
 	repaint_requested = true;
       }
diff --git a/src/frontend/stmclient.h b/src/frontend/stmclient.h
index 51150c6..e4bab9b 100644
--- a/src/frontend/stmclient.h
+++ b/src/frontend/stmclient.h
@@ -48,6 +48,12 @@ class STMClient {
   int port;
   std::string key;
 
+  int escape_key;
+  int escape_pass_key;
+  int escape_pass_key2;
+  bool escape_requires_lf;
+  std::wstring escape_key_help;
+
   struct termios saved_termios, raw_termios;
 
   struct winsize window_size;
@@ -58,7 +64,7 @@ class STMClient {
   Terminal::Display display;
 
   std::wstring connecting_notification;
-  bool repaint_requested, quit_sequence_started;
+  bool repaint_requested, lf_entered, quit_sequence_started;
   bool clean_shutdown;
 
   void main_init( void );
@@ -79,6 +85,8 @@ class STMClient {
 public:
   STMClient( const char *s_ip, int s_port, const char *s_key, const char *predict_mode )
     : ip( s_ip ), port( s_port ), key( s_key ),
+    escape_key( 0x1E ), escape_pass_key( '^' ), escape_pass_key2( '^' ),
+    escape_requires_lf( false ), escape_key_help( L"?" ),
       saved_termios(), raw_termios(),
       window_size(),
       local_framebuffer( NULL ),
@@ -88,6 +96,7 @@ class STMClient {
       display( true ), /* use TERM environment var to initialize display */
       connecting_notification(),
       repaint_requested( false ),
+      lf_entered( false ),
       quit_sequence_started( false ),
       clean_shutdown( false )
   {

From c3e31f1d710dd4380c3eb6dd79024263ae567bd8 Mon Sep 17 00:00:00 2001
From: Keith Winstein <keithw@mit.edu>
Date: Wed, 5 Jun 2013 19:19:05 +0200
Subject: [PATCH 13/40] Get rid of obsolete DM-Upload-Allowed field

---
 debian/control | 1 -
 1 file changed, 1 deletion(-)

diff --git a/debian/control b/debian/control
index 5a36b9b..d4b9983 100644
--- a/debian/control
+++ b/debian/control
@@ -2,7 +2,6 @@ Source: mosh
 Section: net
 Priority: optional
 Maintainer: Keith Winstein <keithw@mit.edu>
-DM-Upload-Allowed: yes
 Build-Depends: debhelper (>= 7.0.50), autotools-dev, protobuf-compiler, libprotobuf-dev, dh-autoreconf, pkg-config, libutempter-dev, zlib1g-dev, libncurses5-dev, libssl-dev
 Standards-Version: 3.9.4.0
 Homepage: http://mosh.mit.edu

From d8711618c90e591449aeda8c27fb02b3ff96b39c Mon Sep 17 00:00:00 2001
From: Andrew Chin <achin@eminence32.net>
Date: Tue, 14 May 2013 19:58:58 -0400
Subject: [PATCH 14/40] Set MACOSX_DEPLOYMENT_TARGET so that older macs can run
 the mosh binaries

Closes #424
---
 macosx/build.sh | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/macosx/build.sh b/macosx/build.sh
index 982a6c8..19af048 100755
--- a/macosx/build.sh
+++ b/macosx/build.sh
@@ -10,6 +10,8 @@ PREFIX_x86_64=`pwd`/prefix_x86_64
 #PREFIX_ppc=`pwd`/prefix_ppc
 #PREFIX_ppc64=`pwd`/prefix_ppc64
 
+export MACOSX_DEPLOYMENT_TARGET=10.6
+
 mkdir -p "$PREFIX"
 mkdir -p "$PREFIX_i386"
 mkdir -p "$PREFIX_x86_64"

From 06561d350036d972507dc155ee10dfa65f9d697e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20=22Tracerneo=22=20Zi=C3=B3=C5=82kowski?=
 <ziolkoneo@gmail.com>
Date: Fri, 28 Jun 2013 04:28:32 +0200
Subject: [PATCH 15/40] Add support for ECMA-48 escape sequence for italic

Closes #443
---
 src/terminal/terminalframebuffer.cc | 6 ++++--
 src/terminal/terminalframebuffer.h  | 4 ++--
 2 files changed, 6 insertions(+), 4 deletions(-)

diff --git a/src/terminal/terminalframebuffer.cc b/src/terminal/terminalframebuffer.cc
index 7eacb9c..dfd2d34 100644
--- a/src/terminal/terminalframebuffer.cc
+++ b/src/terminal/terminalframebuffer.cc
@@ -388,7 +388,7 @@ void DrawState::resize( int s_width, int s_height )
 }
 
 Renditions::Renditions( int s_background )
-  : bold( false ), underlined( false ), blink( false ),
+  : bold( false ), italic( false ), underlined( false ), blink( false ),
     inverse( false ), invisible( false ), foreground_color( 0 ),
     background_color( s_background )
 {}
@@ -397,7 +397,7 @@ Renditions::Renditions( int s_background )
 void Renditions::set_rendition( int num )
 {
   if ( num == 0 ) {
-    bold = underlined = blink = inverse = invisible = false;
+    bold = italic = underlined = blink = inverse = invisible = false;
     foreground_color = background_color = 0;
     return;
   }
@@ -426,6 +426,7 @@ void Renditions::set_rendition( int num )
 
   switch ( num ) {
   case 1: case 22: bold = (num == 1); break;
+  case 3: case 23: italic = (num == 3); break;
   case 4: case 24: underlined = (num == 4); break;
   case 5: case 25: blink = (num == 5); break;
   case 7: case 27: inverse = (num == 7); break;
@@ -453,6 +454,7 @@ std::string Renditions::sgr( void ) const
 
   ret.append( "\033[0" );
   if ( bold ) ret.append( ";1" );
+  if ( italic ) ret.append( ";3" );
   if ( underlined ) ret.append( ";4" );
   if ( blink ) ret.append( ";5" );
   if ( inverse ) ret.append( ";7" );
diff --git a/src/terminal/terminalframebuffer.h b/src/terminal/terminalframebuffer.h
index f0a395c..1d5d88a 100644
--- a/src/terminal/terminalframebuffer.h
+++ b/src/terminal/terminalframebuffer.h
@@ -44,7 +44,7 @@
 namespace Terminal {
   class Renditions {
   public:
-    bool bold, underlined, blink, inverse, invisible;
+    bool bold, italic, underlined, blink, inverse, invisible;
     int foreground_color;
     int background_color;
 
@@ -58,7 +58,7 @@ namespace Terminal {
 
     bool operator==( const Renditions &x ) const
     {
-      return (bold == x.bold) && (underlined == x.underlined)
+      return (bold == x.bold) && (italic == x.italic) && (underlined == x.underlined)
 	&& (blink == x.blink) && (inverse == x.inverse)
 	&& (invisible == x.invisible) && (foreground_color == x.foreground_color)
 	&& (background_color == x.background_color);

From c6bf3a2025e86b34512a995ea8b1e45d7586860f Mon Sep 17 00:00:00 2001
From: Barosl LEE <vcs@barosl.com>
Date: Wed, 29 May 2013 12:54:29 +0900
Subject: [PATCH 16/40] Implement bracketed paste mode

Allow bracketed paste mode-setting control sequences to be passed to the
outer terminal.

Signed-off-by: Barosl LEE <vcs@barosl.com>

Closes #430
---
 src/terminal/terminaldisplay.cc     | 6 ++++++
 src/terminal/terminalframebuffer.cc | 2 +-
 src/terminal/terminalframebuffer.h  | 4 +++-
 src/terminal/terminalfunctions.cc   | 2 ++
 4 files changed, 12 insertions(+), 2 deletions(-)

diff --git a/src/terminal/terminaldisplay.cc b/src/terminal/terminaldisplay.cc
index 3df7491..a8dcf2e 100644
--- a/src/terminal/terminaldisplay.cc
+++ b/src/terminal/terminaldisplay.cc
@@ -286,6 +286,12 @@ std::string Display::new_frame( bool initialized, const Framebuffer &last, const
     frame.current_rendition = f.ds.get_renditions();
   }
 
+  /* has bracketed paste mode changed? */
+  if ( (!initialized)
+       || (f.ds.bracketed_paste != frame.last_frame.ds.bracketed_paste) ) {
+    frame.append( f.ds.bracketed_paste ? "\033[?2004h" : "\033[?2004l" );
+  }
+
   return frame.str;
 }
 
diff --git a/src/terminal/terminalframebuffer.cc b/src/terminal/terminalframebuffer.cc
index dfd2d34..66ca876 100644
--- a/src/terminal/terminalframebuffer.cc
+++ b/src/terminal/terminalframebuffer.cc
@@ -62,7 +62,7 @@ DrawState::DrawState( int s_width, int s_height )
     renditions( 0 ), save(),
     next_print_will_wrap( false ), origin_mode( false ), auto_wrap_mode( true ),
     insert_mode( false ), cursor_visible( true ), reverse_video( false ),
-    application_mode_cursor_keys( false )
+    bracketed_paste( false ), application_mode_cursor_keys( false )
 {
   reinitialize_tabs( 0 );
 }
diff --git a/src/terminal/terminalframebuffer.h b/src/terminal/terminalframebuffer.h
index 1d5d88a..7ecb2f3 100644
--- a/src/terminal/terminalframebuffer.h
+++ b/src/terminal/terminalframebuffer.h
@@ -189,6 +189,7 @@ namespace Terminal {
     bool insert_mode;
     bool cursor_visible;
     bool reverse_video;
+    bool bracketed_paste;
 
     bool application_mode_cursor_keys;
 
@@ -237,7 +238,8 @@ namespace Terminal {
       /* only compare fields that affect display */
       return ( width == x.width ) && ( height == x.height ) && ( cursor_col == x.cursor_col )
 	&& ( cursor_row == x.cursor_row ) && ( cursor_visible == x.cursor_visible ) &&
-	( reverse_video == x.reverse_video ) && ( renditions == x.renditions );
+	( reverse_video == x.reverse_video ) && ( renditions == x.renditions ) &&
+  ( bracketed_paste == x.bracketed_paste );
     }
   };
 
diff --git a/src/terminal/terminalfunctions.cc b/src/terminal/terminalfunctions.cc
index b509cbe..ec2e27b 100644
--- a/src/terminal/terminalfunctions.cc
+++ b/src/terminal/terminalfunctions.cc
@@ -268,6 +268,8 @@ static bool *get_DEC_mode( int param, Framebuffer *fb ) {
     return &(fb->ds.auto_wrap_mode);
   case 25:
     return &(fb->ds.cursor_visible);
+  case 2004: /* bracketed paste */
+    return &(fb->ds.bracketed_paste);
   }
   return NULL;
 }

From 45bba44c83d0b5f6da32327bbcbd2a42d22adbb2 Mon Sep 17 00:00:00 2001
From: Keith Winstein <keithw@mit.edu>
Date: Mon, 15 Jul 2013 11:20:07 -0400
Subject: [PATCH 17/40] mosh-server: Allow startup without PTY

---
 src/frontend/mosh-server.cc | 43 +++++++++++++++++++++++++------------------
 1 file changed, 25 insertions(+), 18 deletions(-)

diff --git a/src/frontend/mosh-server.cc b/src/frontend/mosh-server.cc
index ae2505f..f686e60 100644
--- a/src/frontend/mosh-server.cc
+++ b/src/frontend/mosh-server.cc
@@ -334,9 +334,13 @@ int run_server( const char *desired_ip, const char *desired_port,
   /* get initial window size */
   struct winsize window_size;
   if ( ioctl( STDIN_FILENO, TIOCGWINSZ, &window_size ) < 0 ) {
-    perror( "ioctl TIOCGWINSZ" );
-    fprintf( stderr, "If running with ssh, please use ssh -t to provide a PTY.\n" );
-    exit( 1 );
+    fprintf( stderr, "Server started without pseudo-terminal. Opening 80x24 terminal.\n" );
+
+    /* Fill in sensible defaults. */
+    /* They will be overwritten by client on first connection. */
+    memset( &window_size, 0, sizeof( window_size ) );
+    window_size.ws_col = 80;
+    window_size.ws_row = 24;
   }
 
   /* open parser and terminal */
@@ -361,13 +365,6 @@ int run_server( const char *desired_ip, const char *desired_port,
   fatal_assert( 0 == sigaction( SIGHUP, &sa, NULL ) );
   fatal_assert( 0 == sigaction( SIGPIPE, &sa, NULL ) );
 
-  struct termios child_termios;
-
-  /* Get terminal configuration */
-  if ( tcgetattr( STDIN_FILENO, &child_termios ) < 0 ) {
-    perror( "tcgetattr" );
-    exit( 1 );
-  }
 
   /* detach from terminal */
   pid_t the_pid = fork();
@@ -385,13 +382,7 @@ int run_server( const char *desired_ip, const char *desired_port,
 
   int master;
 
-#ifdef HAVE_IUTF8
-  if ( !(child_termios.c_iflag & IUTF8) ) {
-    /* SSH should also convey IUTF8 across connection. */
-    //    fprintf( stderr, "Warning: Locale is UTF-8 but termios IUTF8 flag not set. Setting IUTF8 flag.\n" );
-    child_termios.c_iflag |= IUTF8;
-  }
-#else
+#ifndef HAVE_IUTF8
   fprintf( stderr, "\nWarning: termios IUTF8 flag not defined.\nCharacter-erase of multibyte character sequence\nprobably does not work properly on this platform.\n" );
 #endif /* HAVE_IUTF8 */
 
@@ -423,7 +414,7 @@ int run_server( const char *desired_ip, const char *desired_port,
   snprintf( utmp_entry, 64, "mosh [%d]", getpid() );
 
   /* Fork child process */
-  pid_t child = forkpty( &master, NULL, &child_termios, &window_size );
+  pid_t child = forkpty( &master, NULL, NULL, &window_size );
 
   if ( child == -1 ) {
     perror( "forkpty" );
@@ -444,6 +435,22 @@ int run_server( const char *desired_ip, const char *desired_port,
     /* close server-related file descriptors */
     delete network;
 
+    /* set IUTF8 if available */
+#ifdef HAVE_IUTF8
+    struct termios child_termios;
+    if ( tcgetattr( STDIN_FILENO, &child_termios ) < 0 ) {
+      perror( "tcgetattr" );
+      exit( 1 );
+    }
+
+    child_termios.c_iflag |= IUTF8;
+
+    if ( tcsetattr( STDIN_FILENO, TCSANOW, &child_termios ) < 0 ) {
+      perror( "tcsetattr" );
+      exit( 1 );
+    }
+#endif /* HAVE_IUTF8 */
+
     /* set TERM */
     const char default_term[] = "xterm";
     const char color_term[] = "xterm-256color";

From 9187e307c60fe0ef58d3a1cf68228675a13349bf Mon Sep 17 00:00:00 2001
From: Anders Kaseorg <andersk@mit.edu>
Date: Sat, 17 Aug 2013 08:23:21 -0400
Subject: [PATCH 18/40] mosh-client: Print exceptions after the client is shut
 down

Otherwise the printed exception may be immediately hidden when the
terminal exits alternate screen mode.

Signed-off-by: Anders Kaseorg <andersk@mit.edu>
---
 src/frontend/mosh-client.cc | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/frontend/mosh-client.cc b/src/frontend/mosh-client.cc
index e338682..58b94e4 100644
--- a/src/frontend/mosh-client.cc
+++ b/src/frontend/mosh-client.cc
@@ -176,11 +176,11 @@ int main( int argc, char *argv[] )
     try {
       client.main();
     } catch ( const Network::NetworkException &e ) {
-      fprintf( stderr, "Network exception: %s: %s\r\n",
-	       e.function.c_str(), strerror( e.the_errno ) );
+      client.shutdown();
+      throw e;
     } catch ( const Crypto::CryptoException &e ) {
-      fprintf( stderr, "Crypto exception: %s\r\n",
-	       e.text.c_str() );
+      client.shutdown();
+      throw e;
     }
 
     client.shutdown();

From c1f96b37a1936cc4a5d1582e9752e155ac69b3d2 Mon Sep 17 00:00:00 2001
From: Anders Kaseorg <andersk@mit.edu>
Date: Sat, 17 Aug 2013 05:31:06 -0400
Subject: [PATCH 19/40] Connection: Store the port number as a string

Signed-off-by: Anders Kaseorg <andersk@mit.edu>
---
 src/examples/ntester.cc         |  6 +++---
 src/frontend/mosh-client.cc     |  5 +----
 src/frontend/mosh-server.cc     |  2 +-
 src/frontend/stmclient.cc       |  8 ++++----
 src/frontend/stmclient.h        |  4 ++--
 src/network/network.cc          | 10 ++++++----
 src/network/network.h           |  4 ++--
 src/network/networktransport.cc |  2 +-
 src/network/networktransport.h  |  4 ++--
 9 files changed, 22 insertions(+), 23 deletions(-)

diff --git a/src/examples/ntester.cc b/src/examples/ntester.cc
index 49ee097..1ca23d3 100644
--- a/src/examples/ntester.cc
+++ b/src/examples/ntester.cc
@@ -46,7 +46,7 @@ int main( int argc, char *argv[] )
   bool server = true;
   char *key;
   char *ip;
-  int port;
+  char *port;
 
   UserStream me, remote;
 
@@ -59,7 +59,7 @@ int main( int argc, char *argv[] )
       
       key = argv[ 1 ];
       ip = argv[ 2 ];
-      port = atoi( argv[ 3 ] );
+      port = argv[ 3 ];
       
       n = new Transport<UserStream, UserStream>( me, remote, key, ip, port );
     } else {
@@ -70,7 +70,7 @@ int main( int argc, char *argv[] )
     exit( 1 );
   }
 
-  fprintf( stderr, "Port bound is %d, key is %s\n", n->port(), n->get_key().c_str() );
+  fprintf( stderr, "Port bound is %s, key is %s\n", n->port().c_str(), n->get_key().c_str() );
 
   if ( server ) {
     Select &sel = Select::get_instance();
diff --git a/src/frontend/mosh-client.cc b/src/frontend/mosh-client.cc
index 58b94e4..e4f4b48 100644
--- a/src/frontend/mosh-client.cc
+++ b/src/frontend/mosh-client.cc
@@ -117,7 +117,6 @@ int main( int argc, char *argv[] )
   }
 
   char *ip, *desired_port;
-  int port;
 
   if ( argc - optind != 2 ) {
     usage( argv[ 0 ] );
@@ -142,8 +141,6 @@ int main( int argc, char *argv[] )
     exit( 1 );
   }
 
-  port = myatoi( desired_port );
-
   /* Read key from environment */
   char *env_key = getenv( "MOSH_KEY" );
   if ( env_key == NULL ) {
@@ -170,7 +167,7 @@ int main( int argc, char *argv[] )
   set_native_locale();
 
   try {
-    STMClient client( ip, port, key, predict_mode );
+    STMClient client( ip, desired_port, key, predict_mode );
     client.init();
 
     try {
diff --git a/src/frontend/mosh-server.cc b/src/frontend/mosh-server.cc
index f686e60..ee27fea 100644
--- a/src/frontend/mosh-server.cc
+++ b/src/frontend/mosh-server.cc
@@ -354,7 +354,7 @@ int run_server( const char *desired_ip, const char *desired_port,
     network->set_verbose();
   }
 
-  printf( "\nMOSH CONNECT %d %s\n", network->port(), network->get_key().c_str() );
+  printf( "\nMOSH CONNECT %s %s\n", network->port().c_str(), network->get_key().c_str() );
   fflush( stdout );
 
   /* don't let signals kill us */
diff --git a/src/frontend/stmclient.cc b/src/frontend/stmclient.cc
index c2ff2a5..1c12df4 100644
--- a/src/frontend/stmclient.cc
+++ b/src/frontend/stmclient.cc
@@ -188,7 +188,7 @@ void STMClient::init( void )
     escape_key_help = L"Commands: Ctrl-Z suspends, \".\" quits, " + escape_pass_name + L" gives literal " + escape_key_name;
   }
   wchar_t tmp[ 128 ];
-  swprintf( tmp, 128, L"Nothing received from server on UDP port %d.", port );
+  swprintf( tmp, 128, L"Nothing received from server on UDP port %s.", port.c_str() );
   connecting_notification = wstring( tmp );
 }
 
@@ -209,8 +209,8 @@ void STMClient::shutdown( void )
   }
 
   if ( still_connecting() ) {
-    fprintf( stderr, "\nmosh did not make a successful connection to %s:%d.\n", ip.c_str(), port );
-    fprintf( stderr, "Please verify that UDP port %d is not firewalled and can reach the server.\n\n", port );
+    fprintf( stderr, "\nmosh did not make a successful connection to %s:%s.\n", ip.c_str(), port.c_str() );
+    fprintf( stderr, "Please verify that UDP port %s is not firewalled and can reach the server.\n\n", port.c_str() );
     fprintf( stderr, "(By default, mosh uses a UDP port between 60000 and 61000. The -p option\nselects a specific UDP port number.)\n" );
   } else if ( network ) {
     if ( !clean_shutdown ) {
@@ -247,7 +247,7 @@ void STMClient::main_init( void )
   Network::UserStream blank;
   Terminal::Complete local_terminal( window_size.ws_col, window_size.ws_row );
   network = new Network::Transport< Network::UserStream, Terminal::Complete >( blank, local_terminal,
-									       key.c_str(), ip.c_str(), port );
+									       key.c_str(), ip.c_str(), port.c_str() );
 
   network->set_send_delay( 1 ); /* minimal delay on outgoing keystrokes */
 
diff --git a/src/frontend/stmclient.h b/src/frontend/stmclient.h
index e4bab9b..187b4f5 100644
--- a/src/frontend/stmclient.h
+++ b/src/frontend/stmclient.h
@@ -45,7 +45,7 @@
 class STMClient {
 private:
   std::string ip;
-  int port;
+  std::string port;
   std::string key;
 
   int escape_key;
@@ -83,7 +83,7 @@ class STMClient {
   void resume( void ); /* restore state after SIGCONT */
 
 public:
-  STMClient( const char *s_ip, int s_port, const char *s_key, const char *predict_mode )
+  STMClient( const char *s_ip, const char *s_port, const char *s_key, const char *predict_mode )
     : ip( s_ip ), port( s_port ), key( s_key ),
     escape_key( 0x1E ), escape_pass_key( '^' ), escape_pass_key2( '^' ),
     escape_requires_lf( false ), escape_key_help( L"?" ),
diff --git a/src/network/network.cc b/src/network/network.cc
index 2f4e0bf..7c96d91 100644
--- a/src/network/network.cc
+++ b/src/network/network.cc
@@ -306,7 +306,7 @@ bool Connection::try_bind( int socket, uint32_t addr, int port_low, int port_hig
   return false;
 }
 
-Connection::Connection( const char *key_str, const char *ip, int port ) /* client */
+Connection::Connection( const char *key_str, const char *ip, const char *port ) /* client */
   : socks(),
     has_remote_addr( false ),
     remote_addr(),
@@ -332,7 +332,7 @@ Connection::Connection( const char *key_str, const char *ip, int port ) /* clien
 
   /* associate socket with remote host and port */
   remote_addr.sin_family = AF_INET;
-  remote_addr.sin_port = htons( port );
+  remote_addr.sin_port = htons( myatoi( port ) );
   if ( !inet_aton( ip, &remote_addr.sin_addr ) ) {
     int saved_errno = errno;
     char buffer[ 2048 ];
@@ -524,7 +524,7 @@ string Connection::recv_one( int sock_to_recv, bool nonblocking )
   return p.payload; /* we do return out-of-order or duplicated packets to caller */
 }
 
-int Connection::port( void ) const
+std::string Connection::port( void ) const
 {
   struct sockaddr_in local_addr;
   socklen_t addrlen = sizeof( local_addr );
@@ -533,7 +533,9 @@ int Connection::port( void ) const
     throw NetworkException( "getsockname", errno );
   }
 
-  return ntohs( local_addr.sin_port );
+  char buf[ 32 ];
+  snprintf( buf, sizeof( buf ), "%d", ntohs( local_addr.sin_port ) );
+  return std::string( buf );
 }
 
 uint64_t Network::timestamp( void )
diff --git a/src/network/network.h b/src/network/network.h
index 61efe50..37b1225 100644
--- a/src/network/network.h
+++ b/src/network/network.h
@@ -161,14 +161,14 @@ namespace Network {
 
   public:
     Connection( const char *desired_ip, const char *desired_port ); /* server */
-    Connection( const char *key_str, const char *ip, int port ); /* client */
+    Connection( const char *key_str, const char *ip, const char *port ); /* client */
 
     void send( string s );
     string recv( void );
     const std::vector< int > fds( void ) const;
     int get_MTU( void ) const { return MTU; }
 
-    int port( void ) const;
+    std::string port( void ) const;
     string get_key( void ) const { return key.printable_key(); }
     bool get_has_remote_addr( void ) const { return has_remote_addr; }
 
diff --git a/src/network/networktransport.cc b/src/network/networktransport.cc
index 127e80c..0e0bcf2 100644
--- a/src/network/networktransport.cc
+++ b/src/network/networktransport.cc
@@ -55,7 +55,7 @@ Transport<MyState, RemoteState>::Transport( MyState &initial_state, RemoteState
 
 template <class MyState, class RemoteState>
 Transport<MyState, RemoteState>::Transport( MyState &initial_state, RemoteState &initial_remote,
-					    const char *key_str, const char *ip, int port )
+					    const char *key_str, const char *ip, const char *port )
   : connection( key_str, ip, port ),
     sender( &connection, initial_state ),
     received_states( 1, TimestampedState<RemoteState>( timestamp(), 0, initial_remote ) ),
diff --git a/src/network/networktransport.h b/src/network/networktransport.h
index c23d0bd..5cea434 100644
--- a/src/network/networktransport.h
+++ b/src/network/networktransport.h
@@ -69,7 +69,7 @@ namespace Network {
     Transport( MyState &initial_state, RemoteState &initial_remote,
 	       const char *desired_ip, const char *desired_port );
     Transport( MyState &initial_state, RemoteState &initial_remote,
-	       const char *key_str, const char *ip, int port );
+	       const char *key_str, const char *ip, const char *port );
 
     /* Send data or an ack if necessary. */
     void tick( void ) { sender.tick(); }
@@ -94,7 +94,7 @@ namespace Network {
     /* Other side has requested shutdown and we have sent one ACK */
     bool counterparty_shutdown_ack_sent( void ) const { return sender.get_counterparty_shutdown_acknowledged(); }
 
-    int port( void ) const { return connection.port(); }
+    std::string port( void ) const { return connection.port(); }
     string get_key( void ) const { return connection.get_key(); }
 
     MyState &get_current_state( void ) { return sender.get_current_state(); }

From 110cce37736a3d5b938d594778d2a4cbb5084944 Mon Sep 17 00:00:00 2001
From: Anders Kaseorg <andersk@mit.edu>
Date: Sat, 17 Aug 2013 08:45:39 -0400
Subject: [PATCH 20/40] Connection: Move socket creation out of setup
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Once we support IPv6, we won’t be able to create a socket until we
know which address family to use.

Signed-off-by: Anders Kaseorg <andersk@mit.edu>
---
 src/network/network.cc | 16 +++++++++-------
 src/network/network.h  |  2 +-
 2 files changed, 10 insertions(+), 8 deletions(-)

diff --git a/src/network/network.cc b/src/network/network.cc
index 7c96d91..a214b1a 100644
--- a/src/network/network.cc
+++ b/src/network/network.cc
@@ -119,6 +119,7 @@ void Connection::hop_port( void )
   assert( !server );
 
   setup();
+  socks.push_back( Socket() );
 
   prune_sockets();
 }
@@ -180,9 +181,6 @@ Connection::Socket::Socket()
 
 void Connection::setup( void )
 {
-  /* create socket */
-  socks.push_back( Socket() );
-
   last_port_choice = timestamp();
 }
 
@@ -251,7 +249,7 @@ Connection::Connection( const char *desired_ip, const char *desired_port ) /* se
   /* try to bind to desired IP first */
   if ( desired_ip_addr != INADDR_ANY ) {
     try {
-      if ( try_bind( sock(), desired_ip_addr, desired_port_low, desired_port_high ) ) { return; }
+      if ( try_bind( desired_ip_addr, desired_port_low, desired_port_high ) ) { return; }
     } catch ( const NetworkException& e ) {
       struct in_addr sin_addr;
       sin_addr.s_addr = desired_ip_addr;
@@ -263,7 +261,7 @@ Connection::Connection( const char *desired_ip, const char *desired_port ) /* se
 
   /* now try any local interface */
   try {
-    if ( try_bind( sock(), INADDR_ANY, desired_port_low, desired_port_high ) ) { return; }
+    if ( try_bind( INADDR_ANY, desired_port_low, desired_port_high ) ) { return; }
   } catch ( const NetworkException& e ) {
     fprintf( stderr, "Error binding to any interface: %s: %s\n",
 	     e.function.c_str(), strerror( e.the_errno ) );
@@ -274,7 +272,7 @@ Connection::Connection( const char *desired_ip, const char *desired_port ) /* se
   throw NetworkException( "Could not bind", errno );
 }
 
-bool Connection::try_bind( int socket, uint32_t addr, int port_low, int port_high )
+bool Connection::try_bind( uint32_t addr, int port_low, int port_high )
 {
   struct sockaddr_in local_addr;
   local_addr.sin_family = AF_INET;
@@ -289,15 +287,17 @@ bool Connection::try_bind( int socket, uint32_t addr, int port_low, int port_hig
     search_high = port_high;
   }
 
+  socks.push_back( Socket() );
   for ( int i = search_low; i <= search_high; i++ ) {
     local_addr.sin_port = htons( i );
 
-    if ( bind( socket, (sockaddr *)&local_addr, sizeof( local_addr ) ) == 0 ) {
+    if ( bind( sock(), (sockaddr *)&local_addr, sizeof( local_addr ) ) == 0 ) {
       return true;
     } else if ( i == search_high ) { /* last port to search */
       fprintf( stderr, "Failed binding to %s:%d\n",
 	       inet_ntoa( local_addr.sin_addr ),
 	       ntohs( local_addr.sin_port ) );
+      socks.pop_back();
       throw NetworkException( "bind", errno );
     }
   }
@@ -341,6 +341,8 @@ Connection::Connection( const char *key_str, const char *ip, const char *port )
   }
 
   has_remote_addr = true;
+
+  socks.push_back( Socket() );
 }
 
 void Connection::send( string s )
diff --git a/src/network/network.h b/src/network/network.h
index 37b1225..90749d5 100644
--- a/src/network/network.h
+++ b/src/network/network.h
@@ -101,7 +101,7 @@ namespace Network {
 
     static const int CONGESTION_TIMESTAMP_PENALTY = 500; /* ms */
 
-    static bool try_bind( int socket, uint32_t addr, int port_low, int port_high );
+    bool try_bind( uint32_t addr, int port_low, int port_high );
 
     class Socket
     {

From 553918d3f2f9e944d7ea475756309f3ac3bc122b Mon Sep 17 00:00:00 2001
From: Anders Kaseorg <andersk@mit.edu>
Date: Sat, 17 Aug 2013 05:13:27 -0400
Subject: [PATCH 21/40] Connection: Support IPv6 addresses

Signed-off-by: Anders Kaseorg <andersk@mit.edu>
---
 configure.ac                   |  48 ++++++++++++-
 src/frontend/mosh-client.cc    |   7 --
 src/frontend/mosh-server.cc    |  31 +++++----
 src/network/network.cc         | 153 ++++++++++++++++++++++++++---------------
 src/network/network.h          |  17 +++--
 src/network/networktransport.h |   3 +-
 6 files changed, 177 insertions(+), 82 deletions(-)

diff --git a/configure.ac b/configure.ac
index b07291a..83d6ceb 100644
--- a/configure.ac
+++ b/configure.ac
@@ -170,7 +170,28 @@ AC_SEARCH_LIBS([socket], [socket])
 AC_SEARCH_LIBS([inet_addr], [nsl])
 
 # Checks for header files.
-AC_CHECK_HEADERS([arpa/inet.h fcntl.h langinfo.h limits.h locale.h netinet/in.h stddef.h stdint.h inttypes.h stdlib.h string.h sys/ioctl.h sys/resource.h sys/socket.h sys/stat.h sys/time.h termios.h unistd.h wchar.h wctype.h], [], [AC_MSG_ERROR([Missing required header file.])])
+AC_CHECK_HEADERS([m4_normalize([
+  fcntl.h
+  langinfo.h
+  limits.h
+  locale.h
+  netdb.h
+  netinet/in.h
+  stddef.h
+  stdint.h
+  inttypes.h
+  stdlib.h
+  string.h
+  sys/ioctl.h
+  sys/resource.h
+  sys/socket.h
+  sys/stat.h
+  sys/time.h
+  termios.h
+  unistd.h
+  wchar.h
+  wctype.h
+  ])], [], [AC_MSG_ERROR([Missing required header file.])])
 
 AC_CHECK_HEADERS([pty.h util.h libutil.h paths.h])
 AC_CHECK_HEADERS([endian.h sys/endian.h])
@@ -195,7 +216,30 @@ AC_TYPE_UINTPTR_T
 # Checks for library functions.
 AC_FUNC_FORK
 AC_FUNC_MBRTOWC
-AC_CHECK_FUNCS([gettimeofday setrlimit inet_ntoa iswprint memchr memset nl_langinfo posix_memalign setenv setlocale sigaction socket strchr strdup strncasecmp strtok strerror strtol wcwidth cfmakeraw pselect])
+AC_CHECK_FUNCS([m4_normalize([
+  gettimeofday
+  setrlimit
+  iswprint
+  memchr
+  memset
+  nl_langinfo
+  posix_memalign
+  setenv
+  setlocale
+  sigaction
+  socket
+  strchr
+  strdup
+  strncasecmp
+  strtok
+  strerror
+  strtol
+  wcwidth
+  cfmakeraw
+  pselect
+  getaddrinfo
+  getnameinfo
+  ])])
 
 AC_SEARCH_LIBS([clock_gettime], [rt], [AC_DEFINE([HAVE_CLOCK_GETTIME], [1], [Define if clock_gettime is available.])])
 
diff --git a/src/frontend/mosh-client.cc b/src/frontend/mosh-client.cc
index e4f4b48..d3f9744 100644
--- a/src/frontend/mosh-client.cc
+++ b/src/frontend/mosh-client.cc
@@ -127,13 +127,6 @@ int main( int argc, char *argv[] )
   desired_port = argv[ optind + 1 ];
 
   /* Sanity-check arguments */
-  if ( ip
-       && ( strspn( ip, "0123456789." ) != strlen( ip ) ) ) {
-    fprintf( stderr, "%s: Bad IP address (%s)\n\n", argv[ 0 ], ip );
-    usage( argv[ 0 ] );
-    exit( 1 );
-  }
-
   if ( desired_port
        && ( strspn( desired_port, "0123456789" ) != strlen( desired_port ) ) ) {
     fprintf( stderr, "%s: Bad UDP port (%s)\n\n", argv[ 0 ], desired_port );
diff --git a/src/frontend/mosh-server.cc b/src/frontend/mosh-server.cc
index ee27fea..d91cbc7 100644
--- a/src/frontend/mosh-server.cc
+++ b/src/frontend/mosh-server.cc
@@ -50,8 +50,7 @@
 #include <utempter.h>
 #endif
 #include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
+#include <netdb.h>
 #include <time.h>
 #include <sys/stat.h>
 
@@ -227,13 +226,6 @@ int main( int argc, char *argv[] )
   }
 
   /* Sanity-check arguments */
-  if ( desired_ip
-       && ( strspn( desired_ip, "0123456789." ) != strlen( desired_ip ) ) ) {
-    fprintf( stderr, "%s: Bad IP address (%s)\n", argv[ 0 ], desired_ip );
-    print_usage( argv[ 0 ] );
-    exit( 1 );
-  }
-
   int dpl, dph;
   if ( desired_port && ! Connection::parse_portrange( desired_port, dpl, dph ) ) {
     fprintf( stderr, "%s: Bad UDP port range (%s)\n", argv[ 0 ], desired_port );
@@ -532,8 +524,8 @@ void serve( int host_fd, Terminal::Complete &terminal, ServerConnection &network
   #ifdef HAVE_UTEMPTER
   bool connected_utmp = false;
 
-  struct in_addr saved_addr;
-  saved_addr.s_addr = 0;
+  Addr saved_addr;
+  socklen_t saved_addr_len = 0;
   #endif
 
   while ( 1 ) {
@@ -616,13 +608,24 @@ void serve( int host_fd, Terminal::Complete &terminal, ServerConnection &network
 	  #ifdef HAVE_UTEMPTER
 	  /* update utmp entry if we have become "connected" */
 	  if ( (!connected_utmp)
-	       || ( saved_addr.s_addr != network.get_remote_ip().s_addr ) ) {
+	       || saved_addr_len != network.get_remote_addr_len()
+	       || memcmp( &saved_addr, &network.get_remote_addr(),
+			  saved_addr_len ) != 0 ) {
 	    utempter_remove_record( host_fd );
 
-	    saved_addr = network.get_remote_ip();
+	    saved_addr = network.get_remote_addr();
+	    saved_addr_len = network.get_remote_addr_len();
+
+	    char host[ NI_MAXHOST ];
+	    int errcode = getnameinfo( &saved_addr.sa, saved_addr_len,
+				       host, sizeof( host ), NULL, 0,
+				       NI_NUMERICHOST );
+	    if ( errcode != 0 ) {
+	      throw NetworkException( std::string( "serve: getnameinfo: " ) + gai_strerror( errcode ), 0 );
+	    }
 
 	    char tmp[ 64 ];
-	    snprintf( tmp, 64, "%s via mosh [%d]", inet_ntoa( saved_addr ), getpid() );
+	    snprintf( tmp, 64, "%s via mosh [%d]", host, getpid() );
 	    utempter_add_record( host_fd, tmp );
 
 	    connected_utmp = true;
diff --git a/src/network/network.cc b/src/network/network.cc
index a214b1a..08b263a 100644
--- a/src/network/network.cc
+++ b/src/network/network.cc
@@ -37,13 +37,14 @@
 #ifdef HAVE_SYS_UIO_H
 #include <sys/uio.h>
 #endif
+#include <netdb.h>
 #include <netinet/in.h>
-#include <arpa/inet.h>
 #include <assert.h>
 #include <errno.h>
 #include <unistd.h>
 
 #include "dos_assert.h"
+#include "fatal_assert.h"
 #include "byteorder.h"
 #include "network.h"
 #include "crypto.h"
@@ -119,7 +120,8 @@ void Connection::hop_port( void )
   assert( !server );
 
   setup();
-  socks.push_back( Socket() );
+  assert( remote_addr_len != 0 );
+  socks.push_back( Socket( remote_addr.sa.sa_family ) );
 
   prune_sockets();
 }
@@ -147,8 +149,8 @@ void Connection::prune_sockets( void )
   }
 }
 
-Connection::Socket::Socket()
-  : _fd( socket( AF_INET, SOCK_DGRAM, 0 ) )
+Connection::Socket::Socket( int family )
+  : _fd( socket( family, SOCK_DGRAM, 0 ) )
 {
   if ( _fd < 0 ) {
     throw NetworkException( "socket", errno );
@@ -197,10 +199,28 @@ const std::vector< int > Connection::fds( void ) const
   return ret;
 }
 
+class AddrInfo {
+public:
+  struct addrinfo *res;
+  AddrInfo( const char *node, const char *service,
+	    const struct addrinfo *hints ) :
+    res( NULL ) {
+    int errcode = getaddrinfo( node, service, hints, &res );
+    if ( errcode != 0 ) {
+      throw NetworkException( std::string( "Bad IP address (" ) + node + "): " + gai_strerror( errcode ), 0 );
+    }
+  }
+  ~AddrInfo() { freeaddrinfo(res); }
+private:
+  AddrInfo(const AddrInfo &);
+  AddrInfo &operator=(const AddrInfo &);
+};
+
 Connection::Connection( const char *desired_ip, const char *desired_port ) /* server */
   : socks(),
     has_remote_addr( false ),
     remote_addr(),
+    remote_addr_len( 0 ),
     server( true ),
     MTU( DEFAULT_SEND_MTU ),
     key(),
@@ -235,33 +255,20 @@ Connection::Connection( const char *desired_ip, const char *desired_port ) /* se
     throw NetworkException("Invalid port range", 0);
   }
 
-  /* convert desired IP */
-  uint32_t desired_ip_addr = INADDR_ANY;
-
-  if ( desired_ip ) {
-    struct in_addr sin_addr;
-    if ( inet_aton( desired_ip, &sin_addr ) == 0 ) {
-      throw NetworkException( "Invalid IP address", errno );
-    }
-    desired_ip_addr = sin_addr.s_addr;
-  }
-
   /* try to bind to desired IP first */
-  if ( desired_ip_addr != INADDR_ANY ) {
+  if ( desired_ip ) {
     try {
-      if ( try_bind( desired_ip_addr, desired_port_low, desired_port_high ) ) { return; }
+      if ( try_bind( desired_ip, desired_port_low, desired_port_high ) ) { return; }
     } catch ( const NetworkException& e ) {
-      struct in_addr sin_addr;
-      sin_addr.s_addr = desired_ip_addr;
       fprintf( stderr, "Error binding to IP %s: %s: %s\n",
-	       inet_ntoa( sin_addr ),
+	       desired_ip,
 	       e.function.c_str(), strerror( e.the_errno ) );
     }
   }
 
   /* now try any local interface */
   try {
-    if ( try_bind( INADDR_ANY, desired_port_low, desired_port_high ) ) { return; }
+    if ( try_bind( NULL, desired_port_low, desired_port_high ) ) { return; }
   } catch ( const NetworkException& e ) {
     fprintf( stderr, "Error binding to any interface: %s: %s\n",
 	     e.function.c_str(), strerror( e.the_errno ) );
@@ -272,11 +279,18 @@ Connection::Connection( const char *desired_ip, const char *desired_port ) /* se
   throw NetworkException( "Could not bind", errno );
 }
 
-bool Connection::try_bind( uint32_t addr, int port_low, int port_high )
+bool Connection::try_bind( const char *addr, int port_low, int port_high )
 {
-  struct sockaddr_in local_addr;
-  local_addr.sin_family = AF_INET;
-  local_addr.sin_addr.s_addr = addr;
+  struct addrinfo hints;
+  memset( &hints, 0, sizeof( hints ) );
+  hints.ai_family = AF_UNSPEC;
+  hints.ai_socktype = SOCK_DGRAM;
+  hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST | AI_NUMERICSERV;
+  AddrInfo ai( addr, 0, &hints );
+
+  Addr local_addr;
+  socklen_t local_addr_len = ai.res->ai_addrlen;
+  memcpy( &local_addr.sa, ai.res->ai_addr, local_addr_len );
 
   int search_low = PORT_RANGE_LOW, search_high = PORT_RANGE_HIGH;
 
@@ -287,18 +301,34 @@ bool Connection::try_bind( uint32_t addr, int port_low, int port_high )
     search_high = port_high;
   }
 
-  socks.push_back( Socket() );
+  socks.push_back( Socket( local_addr.sa.sa_family ) );
   for ( int i = search_low; i <= search_high; i++ ) {
-    local_addr.sin_port = htons( i );
+    switch (local_addr.sa.sa_family) {
+    case AF_INET:
+      local_addr.sin.sin_port = htons( i );
+      break;
+    case AF_INET6:
+      local_addr.sin6.sin6_port = htons( i );
+      break;
+    default:
+      throw NetworkException( "Unknown address family", 0 );
+    }
 
-    if ( bind( sock(), (sockaddr *)&local_addr, sizeof( local_addr ) ) == 0 ) {
+    if ( bind( sock(), &local_addr.sa, local_addr_len ) == 0 ) {
       return true;
     } else if ( i == search_high ) { /* last port to search */
-      fprintf( stderr, "Failed binding to %s:%d\n",
-	       inet_ntoa( local_addr.sin_addr ),
-	       ntohs( local_addr.sin_port ) );
+      int saved_errno = errno;
       socks.pop_back();
-      throw NetworkException( "bind", errno );
+      char host[ NI_MAXHOST ], serv[ NI_MAXSERV ];
+      int errcode = getnameinfo( &local_addr.sa, local_addr_len,
+				 host, sizeof( host ), serv, sizeof( serv ),
+				 NI_DGRAM | NI_NUMERICHOST | NI_NUMERICSERV );
+      if ( errcode != 0 ) {
+	throw NetworkException( std::string( "bind: getnameinfo: " ) + gai_strerror( errcode ), 0 );
+      }
+      fprintf( stderr, "Failed binding to %s:%s\n",
+	       host, serv );
+      throw NetworkException( "bind", saved_errno );
     }
   }
 
@@ -310,6 +340,7 @@ Connection::Connection( const char *key_str, const char *ip, const char *port )
   : socks(),
     has_remote_addr( false ),
     remote_addr(),
+    remote_addr_len( 0 ),
     server( false ),
     MTU( DEFAULT_SEND_MTU ),
     key( key_str ),
@@ -331,18 +362,19 @@ Connection::Connection( const char *key_str, const char *ip, const char *port )
   setup();
 
   /* associate socket with remote host and port */
-  remote_addr.sin_family = AF_INET;
-  remote_addr.sin_port = htons( myatoi( port ) );
-  if ( !inet_aton( ip, &remote_addr.sin_addr ) ) {
-    int saved_errno = errno;
-    char buffer[ 2048 ];
-    snprintf( buffer, 2048, "Bad IP address (%s)", ip );
-    throw NetworkException( buffer, saved_errno );
-  }
+  struct addrinfo hints;
+  memset( &hints, 0, sizeof( hints ) );
+  hints.ai_family = AF_UNSPEC;
+  hints.ai_socktype = SOCK_DGRAM;
+  hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV;
+  AddrInfo ai( ip, port, &hints );
+  fatal_assert( ai.res->ai_addrlen <= sizeof( remote_addr ) );
+  remote_addr_len = ai.res->ai_addrlen;
+  memcpy( &remote_addr.sa, ai.res->ai_addr, remote_addr_len );
 
   has_remote_addr = true;
 
-  socks.push_back( Socket() );
+  socks.push_back( Socket( remote_addr.sa.sa_family ) );
 }
 
 void Connection::send( string s )
@@ -356,7 +388,7 @@ void Connection::send( string s )
   string p = px.tostring( &session );
 
   ssize_t bytes_sent = sendto( sock(), p.data(), p.size(), MSG_DONTWAIT,
-			       (sockaddr *)&remote_addr, sizeof( remote_addr ) );
+			       &remote_addr.sa, remote_addr_len );
 
   if ( bytes_sent == static_cast<ssize_t>( p.size() ) ) {
     have_send_exception = false;
@@ -417,7 +449,7 @@ string Connection::recv( void )
 string Connection::recv_one( int sock_to_recv, bool nonblocking )
 {
   /* receive source address, ECN, and payload in msghdr structure */
-  struct sockaddr_in packet_remote_addr;
+  Addr packet_remote_addr;
   struct msghdr header;
   struct iovec msg_iovec;
 
@@ -425,7 +457,7 @@ string Connection::recv_one( int sock_to_recv, bool nonblocking )
   char msg_control[ Session::RECEIVE_MTU ];
 
   /* receive source address */
-  header.msg_name = &packet_remote_addr;
+  header.msg_name = &packet_remote_addr.sa;
   header.msg_namelen = sizeof( packet_remote_addr );
 
   /* receive payload */
@@ -513,12 +545,19 @@ string Connection::recv_one( int sock_to_recv, bool nonblocking )
     last_heard = timestamp();
 
     if ( server ) { /* only client can roam */
-      if ( (remote_addr.sin_addr.s_addr != packet_remote_addr.sin_addr.s_addr)
-	   || (remote_addr.sin_port != packet_remote_addr.sin_port) ) {
+      if ( remote_addr_len != header.msg_namelen ||
+	   memcmp( &remote_addr, &packet_remote_addr, remote_addr_len ) != 0 ) {
 	remote_addr = packet_remote_addr;
-	fprintf( stderr, "Server now attached to client at %s:%d\n",
-		 inet_ntoa( remote_addr.sin_addr ),
-		 ntohs( remote_addr.sin_port ) );
+	remote_addr_len = header.msg_namelen;
+	char host[ NI_MAXHOST ], serv[ NI_MAXSERV ];
+	int errcode = getnameinfo( &remote_addr.sa, remote_addr_len,
+				   host, sizeof( host ), serv, sizeof( serv ),
+				   NI_DGRAM | NI_NUMERICHOST | NI_NUMERICSERV );
+	if ( errcode != 0 ) {
+	  throw NetworkException( std::string( "recv_one: getnameinfo: " ) + gai_strerror( errcode ), 0 );
+	}
+	fprintf( stderr, "Server now attached to client at %s:%s\n",
+		 host, serv );
       }
     }
   }
@@ -528,16 +567,22 @@ string Connection::recv_one( int sock_to_recv, bool nonblocking )
 
 std::string Connection::port( void ) const
 {
-  struct sockaddr_in local_addr;
+  Addr local_addr;
   socklen_t addrlen = sizeof( local_addr );
 
-  if ( getsockname( sock(), (sockaddr *)&local_addr, &addrlen ) < 0 ) {
+  if ( getsockname( sock(), &local_addr.sa, &addrlen ) < 0 ) {
     throw NetworkException( "getsockname", errno );
   }
 
-  char buf[ 32 ];
-  snprintf( buf, sizeof( buf ), "%d", ntohs( local_addr.sin_port ) );
-  return std::string( buf );
+  char serv[ NI_MAXSERV ];
+  int errcode = getnameinfo( &local_addr.sa, addrlen,
+			     NULL, 0, serv, sizeof( serv ),
+			     NI_DGRAM | NI_NUMERICSERV );
+  if ( errcode != 0 ) {
+    throw NetworkException( std::string( "port: getnameinfo: " ) + gai_strerror( errcode ), 0 );
+  }
+
+  return std::string( serv );
 }
 
 uint64_t Network::timestamp( void )
diff --git a/src/network/network.h b/src/network/network.h
index 90749d5..c248ff7 100644
--- a/src/network/network.h
+++ b/src/network/network.h
@@ -84,6 +84,13 @@ namespace Network {
     string tostring( Session *session );
   };
 
+  union Addr {
+    struct sockaddr sa;
+    struct sockaddr_in sin;
+    struct sockaddr_in6 sin6;
+    struct sockaddr_storage ss;
+  };
+
   class Connection {
   private:
     static const int DEFAULT_SEND_MTU = 1300;
@@ -101,7 +108,7 @@ namespace Network {
 
     static const int CONGESTION_TIMESTAMP_PENALTY = 500; /* ms */
 
-    bool try_bind( uint32_t addr, int port_low, int port_high );
+    bool try_bind( const char *addr, int port_low, int port_high );
 
     class Socket
     {
@@ -110,7 +117,7 @@ namespace Network {
 
     public:
       int fd( void ) const { return _fd; }
-      Socket();
+      Socket( int family );
       ~Socket();
 
       Socket( const Socket & other );
@@ -119,7 +126,8 @@ namespace Network {
 
     std::deque< Socket > socks;
     bool has_remote_addr;
-    struct sockaddr_in remote_addr;
+    Addr remote_addr;
+    socklen_t remote_addr_len;
 
     bool server;
 
@@ -175,7 +183,8 @@ namespace Network {
     uint64_t timeout( void ) const;
     double get_SRTT( void ) const { return SRTT; }
 
-    const struct in_addr & get_remote_ip( void ) const { return remote_addr.sin_addr; }
+    const Addr &get_remote_addr( void ) const { return remote_addr; }
+    socklen_t get_remote_addr_len( void ) const { return remote_addr_len; }
 
     const NetworkException *get_send_exception( void ) const
     {
diff --git a/src/network/networktransport.h b/src/network/networktransport.h
index 5cea434..6f3efd5 100644
--- a/src/network/networktransport.h
+++ b/src/network/networktransport.h
@@ -116,7 +116,8 @@ namespace Network {
 
     unsigned int send_interval( void ) const { return sender.send_interval(); }
 
-    const struct in_addr & get_remote_ip( void ) const { return connection.get_remote_ip(); }
+    const Addr &get_remote_addr( void ) const { return connection.get_remote_addr(); }
+    socklen_t get_remote_addr_len( void ) const { return connection.get_remote_addr_len(); }
 
     const NetworkException *get_send_exception( void ) const { return connection.get_send_exception(); }
   };

From aeee659c2841a19de7c0bd94a0f42bbfd3f7c79b Mon Sep 17 00:00:00 2001
From: Anders Kaseorg <andersk@mit.edu>
Date: Thu, 15 Aug 2013 08:33:49 -0400
Subject: [PATCH 22/40] scripts/mosh: Let IO::Socket resolve the hostname

Signed-off-by: Anders Kaseorg <andersk@mit.edu>
---
 scripts/mosh | 22 +++++++++-------------
 1 file changed, 9 insertions(+), 13 deletions(-)

diff --git a/scripts/mosh b/scripts/mosh
index 4e8b796..3e6359e 100755
--- a/scripts/mosh
+++ b/scripts/mosh
@@ -34,8 +34,8 @@ my $MOSH_VERSION = '1.2.4a';
 
 use warnings;
 use strict;
-use Socket;
 use Getopt::Long;
+use IO::Socket;
 
 $|=1;
 
@@ -168,25 +168,21 @@ if ( not defined $bind_ip or $bind_ip =~ m{^ssh$}i ) {
 
 if ( defined $fake_proxy ) {
   use Errno qw(EINTR);
-  use IO::Socket::INET;
   use POSIX qw(_exit);
 
   my ( $host, $port ) = @ARGV;
 
-  # Resolve hostname
-  my $packed_ip = gethostbyname $host;
-  if ( not defined $packed_ip ) {
-    die "$0: Could not resolve hostname $host\n";
-  }
-  my $ip = inet_ntoa $packed_ip;
+  # Resolve hostname and connect
+  my $sock = IO::Socket->new( Domain => AF_INET,
+			      Family => AF_INET,
+			      PeerHost => $host,
+			      PeerPort => $port,
+			      Proto => "tcp" )
+    or die "$0: Could not connect to $host: $@\n";
 
-  print STDERR "MOSH IP $ip\n";
+  print STDERR "MOSH IP ", $sock->peerhost, "\n";
 
   # Act like netcat
-  my $sock = IO::Socket::INET->new( PeerAddr => $ip,
-				    PeerPort => $port,
-				    Proto => "tcp" )
-    or die "$0: connect to host $ip port $port: $!\n";
   binmode($sock);
   binmode(STDIN);
   binmode(STDOUT);

From a4c58298121fee987c46e91d165130bc5cdd6ce3 Mon Sep 17 00:00:00 2001
From: Anders Kaseorg <andersk@mit.edu>
Date: Sat, 17 Aug 2013 02:29:40 -0400
Subject: [PATCH 23/40] scripts/mosh: Add -6 option to connect with IPv6

Signed-off-by: Anders Kaseorg <andersk@mit.edu>
---
 man/mosh.1   | 13 +++++++++++++
 scripts/mosh | 15 ++++++++++++---
 2 files changed, 25 insertions(+), 3 deletions(-)

diff --git a/man/mosh.1 b/man/mosh.1
index 14405bf..3a4fdec 100644
--- a/man/mosh.1
+++ b/man/mosh.1
@@ -121,6 +121,19 @@ Synonym for \-\-predict=always
 Synonym for \-\-predict=never
 
 .TP
+.B --family=\fIFAMILY\fP
+Force the use of a particular address family, which defaults to `inet'
+(IPv4), and can also be `inet6' (IPv6; requires IO::Socket::IP).
+
+.TP
+.B -4
+Synonym for \-\-family=inet
+
+.TP
+.B -6
+Synonym for \-\-family=inet6
+
+.TP
 .B \-p \fIPORT\fP[:\fIPORT2\fP], \-\-port=\fIPORT\fP[:\fIPORT2\fP]
 Use a particular server-side UDP port or port range,
 for example, if this is the
diff --git a/scripts/mosh b/scripts/mosh
index 3e6359e..f13de81 100755
--- a/scripts/mosh
+++ b/scripts/mosh
@@ -46,6 +46,7 @@ my $predict = undef;
 
 my $bind_ip = undef;
 
+my $family = 'inet';
 my $port_request = undef;
 
 my $ssh = 'ssh';
@@ -69,6 +70,8 @@ qq{Usage: $0 [options] [--] [user@]host [command...]
 -n      --predict=never         never use local echo
         --predict=experimental  aggressively echo even when incorrect
 
+-4      --family=inet        use IPv4 only [default]
+-6      --family=inet6       use IPv6 only
 -p PORT[:PORT2]
         --port=PORT[:PORT2]  server-side UDP port or range
         --bind-server={ssh|any|IP}  ask the server to reply from an IP address
@@ -110,6 +113,9 @@ GetOptions( 'client=s' => \$client,
 	    'port=s' => \$port_request,
 	    'a' => sub { $predict = 'always' },
 	    'n' => sub { $predict = 'never' },
+	    'family=s' => \$family,
+	    '4' => sub { $family = 'inet' },
+	    '6' => sub { $family = 'inet6' },
 	    'p=s' => \$port_request,
 	    'ssh=s' => \$ssh,
 	    'init!' => \$term_init,
@@ -168,13 +174,16 @@ if ( not defined $bind_ip or $bind_ip =~ m{^ssh$}i ) {
 
 if ( defined $fake_proxy ) {
   use Errno qw(EINTR);
+  BEGIN { eval { require IO::Socket::IP; IO::Socket::IP->import('-register'); }; }
   use POSIX qw(_exit);
 
   my ( $host, $port ) = @ARGV;
 
   # Resolve hostname and connect
-  my $sock = IO::Socket->new( Domain => AF_INET,
-			      Family => AF_INET,
+  my $afstr = 'AF_' . uc( $family );
+  my $af = eval { IO::Socket->$afstr } or die "$0: Invalid family $family\n";
+  my $sock = IO::Socket->new( Domain => $af,
+			      Family => $af,
 			      PeerHost => $host,
 			      PeerPort => $port,
 			      Proto => "tcp" )
@@ -254,7 +263,7 @@ if ( $pid == 0 ) { # child
     push @server, '--', @command;
   }
 
-  my $quoted_self = shell_quote( $0 );
+  my $quoted_self = shell_quote( $0, "--family=$family" );
   exec "$ssh " . shell_quote( '-S', 'none', '-o', "ProxyCommand=$quoted_self --fake-proxy -- %h %p", '-n', '-tt', $userhost, '--', "$server " . shell_quote( @server ) );
   die "Cannot exec ssh: $!\n";
 } else { # parent

From 6f0669b909b774ed69ada01c0d2a7e19a3bd4b07 Mon Sep 17 00:00:00 2001
From: Anders Kaseorg <andersk@mit.edu>
Date: Sat, 17 Aug 2013 09:19:19 -0400
Subject: [PATCH 24/40] debian/control: Recommend IO::Socket::IP

This is optional, but needed for IPv6 support.

Signed-off-by: Anders Kaseorg <andersk@mit.edu>
---
 debian/control | 1 +
 1 file changed, 1 insertion(+)

diff --git a/debian/control b/debian/control
index d4b9983..e4103ef 100644
--- a/debian/control
+++ b/debian/control
@@ -11,6 +11,7 @@ Vcs-Browser: https://github.com/keithw/mosh
 Package: mosh
 Architecture: any
 Depends: ${shlibs:Depends}, ${misc:Depends}, openssh-client
+Recommends: libio-socket-ip-perl
 Description: Mobile shell that supports roaming and intelligent local echo
  Mosh is a remote terminal application that supports:
    - intermittent network connectivity,

From 5ef7f43479327b2ad3e96439c4785af2c658dfab Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bazyli=20Brz=C3=B3ska?= <bazyli.brzoska@gmail.com>
Date: Mon, 9 Sep 2013 01:45:03 +0200
Subject: [PATCH 25/40] Implement xterm mouse mode

---
 src/terminal/terminaldisplay.cc     | 6 ++++++
 src/terminal/terminalframebuffer.cc | 3 ++-
 src/terminal/terminalframebuffer.h  | 3 ++-
 src/terminal/terminalfunctions.cc   | 3 +++
 4 files changed, 13 insertions(+), 2 deletions(-)

diff --git a/src/terminal/terminaldisplay.cc b/src/terminal/terminaldisplay.cc
index a8dcf2e..ba7f751 100644
--- a/src/terminal/terminaldisplay.cc
+++ b/src/terminal/terminaldisplay.cc
@@ -292,6 +292,12 @@ std::string Display::new_frame( bool initialized, const Framebuffer &last, const
     frame.append( f.ds.bracketed_paste ? "\033[?2004h" : "\033[?2004l" );
   }
 
+  /* has xterm mouse mode changed (SET_ANY_EVENT_MOUSE)? */
+  if ( (!initialized)
+       || (f.ds.xterm_mouse != frame.last_frame.ds.xterm_mouse) ) {
+    frame.append( f.ds.xterm_mouse ? "\033[?1002h" : "\033[?1002l" );
+  }
+
   return frame.str;
 }
 
diff --git a/src/terminal/terminalframebuffer.cc b/src/terminal/terminalframebuffer.cc
index 66ca876..c8a13ec 100644
--- a/src/terminal/terminalframebuffer.cc
+++ b/src/terminal/terminalframebuffer.cc
@@ -62,7 +62,8 @@ DrawState::DrawState( int s_width, int s_height )
     renditions( 0 ), save(),
     next_print_will_wrap( false ), origin_mode( false ), auto_wrap_mode( true ),
     insert_mode( false ), cursor_visible( true ), reverse_video( false ),
-    bracketed_paste( false ), application_mode_cursor_keys( false )
+    bracketed_paste( false ), xterm_mouse( false ), 
+    application_mode_cursor_keys( false )
 {
   reinitialize_tabs( 0 );
 }
diff --git a/src/terminal/terminalframebuffer.h b/src/terminal/terminalframebuffer.h
index 7ecb2f3..16edc83 100644
--- a/src/terminal/terminalframebuffer.h
+++ b/src/terminal/terminalframebuffer.h
@@ -190,6 +190,7 @@ namespace Terminal {
     bool cursor_visible;
     bool reverse_video;
     bool bracketed_paste;
+    bool xterm_mouse;
 
     bool application_mode_cursor_keys;
 
@@ -239,7 +240,7 @@ namespace Terminal {
       return ( width == x.width ) && ( height == x.height ) && ( cursor_col == x.cursor_col )
 	&& ( cursor_row == x.cursor_row ) && ( cursor_visible == x.cursor_visible ) &&
 	( reverse_video == x.reverse_video ) && ( renditions == x.renditions ) &&
-  ( bracketed_paste == x.bracketed_paste );
+  ( bracketed_paste == x.bracketed_paste ) && ( xterm_mouse == x.xterm_mouse );
     }
   };
 
diff --git a/src/terminal/terminalfunctions.cc b/src/terminal/terminalfunctions.cc
index ec2e27b..563b253 100644
--- a/src/terminal/terminalfunctions.cc
+++ b/src/terminal/terminalfunctions.cc
@@ -268,6 +268,9 @@ static bool *get_DEC_mode( int param, Framebuffer *fb ) {
     return &(fb->ds.auto_wrap_mode);
   case 25:
     return &(fb->ds.cursor_visible);
+  case 1000:           /* xterm mouse 1 (normal) */
+  case 1002:           /* xterm mouse 2 (inc. button drags) */
+    return &(fb->ds.xterm_mouse);
   case 2004: /* bracketed paste */
     return &(fb->ds.bracketed_paste);
   }

From 926bbcc746e9da8193dbdf781a26fb1151239f39 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bazyli=20Brz=C3=B3ska?= <bazyli.brzoska@gmail.com>
Date: Mon, 9 Sep 2013 13:32:41 +0200
Subject: [PATCH 26/40] Implement xterm extended mouse mode

---
 src/terminal/terminaldisplay.cc     | 6 ++++++
 src/terminal/terminalframebuffer.cc | 2 +-
 src/terminal/terminalframebuffer.h  | 4 +++-
 src/terminal/terminalfunctions.cc   | 2 ++
 4 files changed, 12 insertions(+), 2 deletions(-)

diff --git a/src/terminal/terminaldisplay.cc b/src/terminal/terminaldisplay.cc
index ba7f751..5b0b580 100644
--- a/src/terminal/terminaldisplay.cc
+++ b/src/terminal/terminaldisplay.cc
@@ -298,6 +298,12 @@ std::string Display::new_frame( bool initialized, const Framebuffer &last, const
     frame.append( f.ds.xterm_mouse ? "\033[?1002h" : "\033[?1002l" );
   }
 
+  /* has xterm mouse mode changed (SET_ANY_EVENT_MOUSE)? */
+  if ( (!initialized)
+       || (f.ds.xterm_extended_mouse != frame.last_frame.ds.xterm_extended_mouse) ) {
+    frame.append( f.ds.xterm_extended_mouse ? "\033[?1006h" : "\033[?1006l" );
+  }
+
   return frame.str;
 }
 
diff --git a/src/terminal/terminalframebuffer.cc b/src/terminal/terminalframebuffer.cc
index c8a13ec..0659c31 100644
--- a/src/terminal/terminalframebuffer.cc
+++ b/src/terminal/terminalframebuffer.cc
@@ -62,7 +62,7 @@ DrawState::DrawState( int s_width, int s_height )
     renditions( 0 ), save(),
     next_print_will_wrap( false ), origin_mode( false ), auto_wrap_mode( true ),
     insert_mode( false ), cursor_visible( true ), reverse_video( false ),
-    bracketed_paste( false ), xterm_mouse( false ), 
+    bracketed_paste( false ), xterm_mouse( false ), xterm_extended_mouse( false ), 
     application_mode_cursor_keys( false )
 {
   reinitialize_tabs( 0 );
diff --git a/src/terminal/terminalframebuffer.h b/src/terminal/terminalframebuffer.h
index 16edc83..01576b7 100644
--- a/src/terminal/terminalframebuffer.h
+++ b/src/terminal/terminalframebuffer.h
@@ -191,6 +191,7 @@ namespace Terminal {
     bool reverse_video;
     bool bracketed_paste;
     bool xterm_mouse;
+    bool xterm_extended_mouse;
 
     bool application_mode_cursor_keys;
 
@@ -240,7 +241,8 @@ namespace Terminal {
       return ( width == x.width ) && ( height == x.height ) && ( cursor_col == x.cursor_col )
 	&& ( cursor_row == x.cursor_row ) && ( cursor_visible == x.cursor_visible ) &&
 	( reverse_video == x.reverse_video ) && ( renditions == x.renditions ) &&
-  ( bracketed_paste == x.bracketed_paste ) && ( xterm_mouse == x.xterm_mouse );
+  ( bracketed_paste == x.bracketed_paste ) && ( xterm_mouse == x.xterm_mouse ) &&
+  ( xterm_extended_mouse == x.xterm_extended_mouse );
     }
   };
 
diff --git a/src/terminal/terminalfunctions.cc b/src/terminal/terminalfunctions.cc
index 563b253..992c63f 100644
--- a/src/terminal/terminalfunctions.cc
+++ b/src/terminal/terminalfunctions.cc
@@ -271,6 +271,8 @@ static bool *get_DEC_mode( int param, Framebuffer *fb ) {
   case 1000:           /* xterm mouse 1 (normal) */
   case 1002:           /* xterm mouse 2 (inc. button drags) */
     return &(fb->ds.xterm_mouse);
+  case 1006:           /* xterm extended mouse */
+    return &(fb->ds.xterm_extended_mouse);
   case 2004: /* bracketed paste */
     return &(fb->ds.bracketed_paste);
   }

From bb369334722b23120bf0b0e91e6a6c69e0884474 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bazyli=20Brz=C3=B3ska?= <bazyli.brzoska@gmail.com>
Date: Mon, 9 Sep 2013 21:06:48 +0200
Subject: [PATCH 27/40] xterm extended fix

---
 src/terminal/terminaldisplay.cc | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/terminal/terminaldisplay.cc b/src/terminal/terminaldisplay.cc
index 5b0b580..b76d84e 100644
--- a/src/terminal/terminaldisplay.cc
+++ b/src/terminal/terminaldisplay.cc
@@ -298,10 +298,10 @@ std::string Display::new_frame( bool initialized, const Framebuffer &last, const
     frame.append( f.ds.xterm_mouse ? "\033[?1002h" : "\033[?1002l" );
   }
 
-  /* has xterm mouse mode changed (SET_ANY_EVENT_MOUSE)? */
+  /* has xterm mouse mode changed? */
   if ( (!initialized)
        || (f.ds.xterm_extended_mouse != frame.last_frame.ds.xterm_extended_mouse) ) {
-    frame.append( f.ds.xterm_extended_mouse ? "\033[?1006h" : "\033[?1006l" );
+    frame.append( f.ds.xterm_extended_mouse ? "\033[?1006h\033[?1002h" : "\033[?1006l\033[?1002l" );
   }
 
   return frame.str;

From 492cfa3449d8a7b29d08d71a58bd00aedb17d711 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bazyli=20Brz=C3=B3ska?= <bazyli.brzoska@gmail.com>
Date: Tue, 10 Sep 2013 01:48:50 +0200
Subject: [PATCH 28/40] Implement vt100 mouse mode (1000)

(closes #461)
---
 src/terminal/terminaldisplay.cc     | 8 +++++++-
 src/terminal/terminalframebuffer.cc | 4 ++--
 src/terminal/terminalframebuffer.h  | 5 +++--
 src/terminal/terminalfunctions.cc   | 1 +
 4 files changed, 13 insertions(+), 5 deletions(-)

diff --git a/src/terminal/terminaldisplay.cc b/src/terminal/terminaldisplay.cc
index b76d84e..a6a06bc 100644
--- a/src/terminal/terminaldisplay.cc
+++ b/src/terminal/terminaldisplay.cc
@@ -292,7 +292,13 @@ std::string Display::new_frame( bool initialized, const Framebuffer &last, const
     frame.append( f.ds.bracketed_paste ? "\033[?2004h" : "\033[?2004l" );
   }
 
-  /* has xterm mouse mode changed (SET_ANY_EVENT_MOUSE)? */
+  /* has xterm VT100 mouse mode changed? */
+  if ( (!initialized)
+       || (f.ds.vt100_mouse != frame.last_frame.ds.vt100_mouse) ) {
+    frame.append( f.ds.vt100_mouse ? "\033[?1000h" : "\033[?1000l" );
+  }
+
+  /* has xterm mouse mode changed? */
   if ( (!initialized)
        || (f.ds.xterm_mouse != frame.last_frame.ds.xterm_mouse) ) {
     frame.append( f.ds.xterm_mouse ? "\033[?1002h" : "\033[?1002l" );
diff --git a/src/terminal/terminalframebuffer.cc b/src/terminal/terminalframebuffer.cc
index 0659c31..c1ba094 100644
--- a/src/terminal/terminalframebuffer.cc
+++ b/src/terminal/terminalframebuffer.cc
@@ -62,8 +62,8 @@ DrawState::DrawState( int s_width, int s_height )
     renditions( 0 ), save(),
     next_print_will_wrap( false ), origin_mode( false ), auto_wrap_mode( true ),
     insert_mode( false ), cursor_visible( true ), reverse_video( false ),
-    bracketed_paste( false ), xterm_mouse( false ), xterm_extended_mouse( false ), 
-    application_mode_cursor_keys( false )
+    bracketed_paste( false ), vt100_mouse( false ), xterm_mouse( false ), 
+    xterm_extended_mouse( false ), application_mode_cursor_keys( false )
 {
   reinitialize_tabs( 0 );
 }
diff --git a/src/terminal/terminalframebuffer.h b/src/terminal/terminalframebuffer.h
index 01576b7..68c288f 100644
--- a/src/terminal/terminalframebuffer.h
+++ b/src/terminal/terminalframebuffer.h
@@ -190,6 +190,7 @@ namespace Terminal {
     bool cursor_visible;
     bool reverse_video;
     bool bracketed_paste;
+    bool vt100_mouse;
     bool xterm_mouse;
     bool xterm_extended_mouse;
 
@@ -241,8 +242,8 @@ namespace Terminal {
       return ( width == x.width ) && ( height == x.height ) && ( cursor_col == x.cursor_col )
 	&& ( cursor_row == x.cursor_row ) && ( cursor_visible == x.cursor_visible ) &&
 	( reverse_video == x.reverse_video ) && ( renditions == x.renditions ) &&
-  ( bracketed_paste == x.bracketed_paste ) && ( xterm_mouse == x.xterm_mouse ) &&
-  ( xterm_extended_mouse == x.xterm_extended_mouse );
+  ( bracketed_paste == x.bracketed_paste ) && ( vt100_mouse == x.vt100_mouse ) &&
+  ( xterm_mouse == x.xterm_mouse ) && ( xterm_extended_mouse == x.xterm_extended_mouse );
     }
   };
 
diff --git a/src/terminal/terminalfunctions.cc b/src/terminal/terminalfunctions.cc
index 992c63f..8598ad9 100644
--- a/src/terminal/terminalfunctions.cc
+++ b/src/terminal/terminalfunctions.cc
@@ -269,6 +269,7 @@ static bool *get_DEC_mode( int param, Framebuffer *fb ) {
   case 25:
     return &(fb->ds.cursor_visible);
   case 1000:           /* xterm mouse 1 (normal) */
+    return &(fb->ds.vt100_mouse);
   case 1002:           /* xterm mouse 2 (inc. button drags) */
     return &(fb->ds.xterm_mouse);
   case 1006:           /* xterm extended mouse */

From 27678941d46cf78ec2be74c7d1f9632e8efa2128 Mon Sep 17 00:00:00 2001
From: Keith Winstein <keithw@mit.edu>
Date: Sat, 4 Jan 2014 02:50:13 -0500
Subject: [PATCH 29/40] network.cc: Properly close old fd on Socket assignment
 operator

Fix thanks to Igor Bukanov. Closes #470.
---
 src/network/network.cc | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/src/network/network.cc b/src/network/network.cc
index 08b263a..c269866 100644
--- a/src/network/network.cc
+++ b/src/network/network.cc
@@ -640,9 +640,7 @@ Connection::Socket::Socket( const Socket & other )
 
 Connection::Socket & Connection::Socket::operator=( const Socket & other )
 {
-  _fd = dup( other._fd );
-  
-  if ( _fd < 0 ) {
+  if ( dup2( other._fd, _fd ) < 0 ) {
     throw NetworkException( "socket", errno );
   }
 

From 96bf5af1b4e8a8164cc004b942553a8ca88f55df Mon Sep 17 00:00:00 2001
From: Igor Bukanov <igor@mir2.org>
Date: Tue, 29 Oct 2013 14:41:34 +0100
Subject: [PATCH 30/40] mosh-server: allow startup with PTY with zero width or
 height like Emacs shell.

Closes #472
Closes #473
---
 src/frontend/mosh-server.cc | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/src/frontend/mosh-server.cc b/src/frontend/mosh-server.cc
index d91cbc7..84cf7b6 100644
--- a/src/frontend/mosh-server.cc
+++ b/src/frontend/mosh-server.cc
@@ -325,7 +325,9 @@ int run_server( const char *desired_ip, const char *desired_port,
 		const int colors, bool verbose, bool with_motd ) {
   /* get initial window size */
   struct winsize window_size;
-  if ( ioctl( STDIN_FILENO, TIOCGWINSZ, &window_size ) < 0 ) {
+  if ( ioctl( STDIN_FILENO, TIOCGWINSZ, &window_size ) < 0 ||
+       window_size.ws_col == 0 ||
+       window_size.ws_row == 0 ) {
     fprintf( stderr, "Server started without pseudo-terminal. Opening 80x24 terminal.\n" );
 
     /* Fill in sensible defaults. */

From 578db45fbf65d4e90bba371fe8d55f0455193a27 Mon Sep 17 00:00:00 2001
From: Keith Winstein <keithw@mit.edu>
Date: Sat, 4 Jan 2014 03:08:57 -0500
Subject: [PATCH 31/40] Fix mosh-server segfault on startup when run without
 arguments

---
 src/network/network.cc | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/network/network.cc b/src/network/network.cc
index c269866..d9d4678 100644
--- a/src/network/network.cc
+++ b/src/network/network.cc
@@ -205,7 +205,8 @@ class AddrInfo {
   AddrInfo( const char *node, const char *service,
 	    const struct addrinfo *hints ) :
     res( NULL ) {
-    int errcode = getaddrinfo( node, service, hints, &res );
+    int errcode = getaddrinfo( node ? node : "0",
+			       service ? service : "0", hints, &res );
     if ( errcode != 0 ) {
       throw NetworkException( std::string( "Bad IP address (" ) + node + "): " + gai_strerror( errcode ), 0 );
     }

From 5532117984e22d5a841efc667d27294dbd4d51d4 Mon Sep 17 00:00:00 2001
From: Anders Kaseorg <andersk@mit.edu>
Date: Sat, 4 Jan 2014 04:14:55 -0500
Subject: [PATCH 32/40] Revert "Fix mosh-server segfault on startup when run
 without arguments"

This reverts commit 578db45fbf65d4e90bba371fe8d55f0455193a27.

The NULL inputs to getaddrinfo are valid in certain cases, and at
least in the case of node, important.  The segfault happens not with
getaddrinfo, but with the error message generation below it.

Signed-off-by: Anders Kaseorg <andersk@mit.edu>
---
 src/network/network.cc | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/src/network/network.cc b/src/network/network.cc
index d9d4678..c269866 100644
--- a/src/network/network.cc
+++ b/src/network/network.cc
@@ -205,8 +205,7 @@ class AddrInfo {
   AddrInfo( const char *node, const char *service,
 	    const struct addrinfo *hints ) :
     res( NULL ) {
-    int errcode = getaddrinfo( node ? node : "0",
-			       service ? service : "0", hints, &res );
+    int errcode = getaddrinfo( node, service, hints, &res );
     if ( errcode != 0 ) {
       throw NetworkException( std::string( "Bad IP address (" ) + node + "): " + gai_strerror( errcode ), 0 );
     }

From b39da20edc6025a9120e5959ea2e4dc8f0bd7604 Mon Sep 17 00:00:00 2001
From: Anders Kaseorg <andersk@mit.edu>
Date: Sat, 4 Jan 2014 04:18:26 -0500
Subject: [PATCH 33/40] AddrInfo: Fix error message generation when node ==
 NULL

Signed-off-by: Anders Kaseorg <andersk@mit.edu>
---
 src/network/network.cc | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/network/network.cc b/src/network/network.cc
index c269866..714005a 100644
--- a/src/network/network.cc
+++ b/src/network/network.cc
@@ -207,7 +207,7 @@ class AddrInfo {
     res( NULL ) {
     int errcode = getaddrinfo( node, service, hints, &res );
     if ( errcode != 0 ) {
-      throw NetworkException( std::string( "Bad IP address (" ) + node + "): " + gai_strerror( errcode ), 0 );
+      throw NetworkException( std::string( "Bad IP address (" ) + (node != NULL ? node : "(null)") + "): " + gai_strerror( errcode ), 0 );
     }
   }
   ~AddrInfo() { freeaddrinfo(res); }

From 7c942e68363126647fe55682209533868b0918f1 Mon Sep 17 00:00:00 2001
From: Anders Kaseorg <andersk@mit.edu>
Date: Sat, 4 Jan 2014 04:30:44 -0500
Subject: [PATCH 34/40] Connection::try_bind: Pass port argument 0 as a string

Signed-off-by: Anders Kaseorg <andersk@mit.edu>
---
 src/network/network.cc | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/network/network.cc b/src/network/network.cc
index 714005a..e55259c 100644
--- a/src/network/network.cc
+++ b/src/network/network.cc
@@ -286,7 +286,7 @@ bool Connection::try_bind( const char *addr, int port_low, int port_high )
   hints.ai_family = AF_UNSPEC;
   hints.ai_socktype = SOCK_DGRAM;
   hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST | AI_NUMERICSERV;
-  AddrInfo ai( addr, 0, &hints );
+  AddrInfo ai( addr, "0", &hints );
 
   Addr local_addr;
   socklen_t local_addr_len = ai.res->ai_addrlen;

From 56837881fc24be25955a9ca04ec1b5e87598a609 Mon Sep 17 00:00:00 2001
From: Anders Kaseorg <andersk@mit.edu>
Date: Wed, 8 Jan 2014 18:52:08 -0500
Subject: [PATCH 35/40] Fix MOSH_ESCAPE_KEY validation

Signed-off-by: Anders Kaseorg <andersk@mit.edu>
---
 src/frontend/stmclient.cc | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/frontend/stmclient.cc b/src/frontend/stmclient.cc
index 1c12df4..cfb473a 100644
--- a/src/frontend/stmclient.cc
+++ b/src/frontend/stmclient.cc
@@ -126,7 +126,7 @@ void STMClient::init( void )
   if ( (escape_key_env = getenv( "MOSH_ESCAPE_KEY" )) != NULL ) {
     if ( strlen( escape_key_env ) == 1 ) {
       escape_key = (int)escape_key_env[0];
-      if ( (escape_key > 0) || (escape_key < 128) ) {
+      if ( escape_key > 0 && escape_key < 128 ) {
 	if ( escape_key < 32 ) {
 	  /* If escape is ctrl-something, pass it with repeating the key without ctrl. */
 	  escape_pass_key = escape_key + (int)'@';

From 50152bfcba872dd2ef5318877823005ad0c4593f Mon Sep 17 00:00:00 2001
From: Anders Kaseorg <andersk@mit.edu>
Date: Wed, 8 Jan 2014 18:54:30 -0500
Subject: [PATCH 36/40] mosh-client: Catch all exceptions when ensuring client
 is shut down

Signed-off-by: Anders Kaseorg <andersk@mit.edu>
---
 src/frontend/mosh-client.cc | 7 ++-----
 1 file changed, 2 insertions(+), 5 deletions(-)

diff --git a/src/frontend/mosh-client.cc b/src/frontend/mosh-client.cc
index d3f9744..83230a1 100644
--- a/src/frontend/mosh-client.cc
+++ b/src/frontend/mosh-client.cc
@@ -165,12 +165,9 @@ int main( int argc, char *argv[] )
 
     try {
       client.main();
-    } catch ( const Network::NetworkException &e ) {
+    } catch ( ... ) {
       client.shutdown();
-      throw e;
-    } catch ( const Crypto::CryptoException &e ) {
-      client.shutdown();
-      throw e;
+      throw;
     }
 
     client.shutdown();

From 94a35958c254daea8f0851c957925c0e9c805b47 Mon Sep 17 00:00:00 2001
From: Andrew Chin <achin@eminence32.net>
Date: Tue, 9 Sep 2014 20:40:56 -0400
Subject: [PATCH 37/40] Revert bb369334722b23120bf0b0e91e6a6c69e0884474

We aren't sure why this change was made, and reverting it appears to help
fix mouse issues in emacs related to text hilighting.

Signed-off-by: Andrew Chin <achin@eminence32.net>
---
 src/terminal/terminaldisplay.cc | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/terminal/terminaldisplay.cc b/src/terminal/terminaldisplay.cc
index a6a06bc..c80b215 100644
--- a/src/terminal/terminaldisplay.cc
+++ b/src/terminal/terminaldisplay.cc
@@ -307,7 +307,7 @@ std::string Display::new_frame( bool initialized, const Framebuffer &last, const
   /* has xterm mouse mode changed? */
   if ( (!initialized)
        || (f.ds.xterm_extended_mouse != frame.last_frame.ds.xterm_extended_mouse) ) {
-    frame.append( f.ds.xterm_extended_mouse ? "\033[?1006h\033[?1002h" : "\033[?1006l\033[?1002l" );
+    frame.append( f.ds.xterm_extended_mouse ? "\033[?1006h" : "\033[?1006l" );
   }
 
   return frame.str;

From 0be97a80bdce88a6a17e8e99387552000c1f8a6b Mon Sep 17 00:00:00 2001
From: Andrew Chin <achin@eminence32.net>
Date: Tue, 9 Sep 2014 20:48:44 -0400
Subject: [PATCH 38/40] Implement UTF8 mouse mode (SET_EXT_MODE_MOUSE DECSET
 1005)

Signed-off-by: Andrew Chin <achin@eminence32.net>
---
 src/terminal/terminaldisplay.cc     | 5 +++++
 src/terminal/terminalframebuffer.cc | 2 +-
 src/terminal/terminalframebuffer.h  | 6 ++++--
 src/terminal/terminalfunctions.cc   | 2 ++
 4 files changed, 12 insertions(+), 3 deletions(-)

diff --git a/src/terminal/terminaldisplay.cc b/src/terminal/terminaldisplay.cc
index c80b215..70d698c 100644
--- a/src/terminal/terminaldisplay.cc
+++ b/src/terminal/terminaldisplay.cc
@@ -309,6 +309,11 @@ std::string Display::new_frame( bool initialized, const Framebuffer &last, const
        || (f.ds.xterm_extended_mouse != frame.last_frame.ds.xterm_extended_mouse) ) {
     frame.append( f.ds.xterm_extended_mouse ? "\033[?1006h" : "\033[?1006l" );
   }
+  
+  if ( (!initialized)
+       || (f.ds.xterm_utf8_mouse != frame.last_frame.ds.xterm_utf8_mouse) ) {
+    frame.append( f.ds.xterm_utf8_mouse ? "\033[?1005h" : "\033[?1005l" );
+  }
 
   return frame.str;
 }
diff --git a/src/terminal/terminalframebuffer.cc b/src/terminal/terminalframebuffer.cc
index c1ba094..43bd6b4 100644
--- a/src/terminal/terminalframebuffer.cc
+++ b/src/terminal/terminalframebuffer.cc
@@ -63,7 +63,7 @@ DrawState::DrawState( int s_width, int s_height )
     next_print_will_wrap( false ), origin_mode( false ), auto_wrap_mode( true ),
     insert_mode( false ), cursor_visible( true ), reverse_video( false ),
     bracketed_paste( false ), vt100_mouse( false ), xterm_mouse( false ), 
-    xterm_extended_mouse( false ), application_mode_cursor_keys( false )
+    xterm_extended_mouse( false ), xterm_utf8_mouse( false ), application_mode_cursor_keys( false )
 {
   reinitialize_tabs( 0 );
 }
diff --git a/src/terminal/terminalframebuffer.h b/src/terminal/terminalframebuffer.h
index 68c288f..97fd355 100644
--- a/src/terminal/terminalframebuffer.h
+++ b/src/terminal/terminalframebuffer.h
@@ -192,7 +192,8 @@ namespace Terminal {
     bool bracketed_paste;
     bool vt100_mouse;
     bool xterm_mouse;
-    bool xterm_extended_mouse;
+    bool xterm_extended_mouse; // aka SGR
+    bool xterm_utf8_mouse;
 
     bool application_mode_cursor_keys;
 
@@ -243,7 +244,8 @@ namespace Terminal {
 	&& ( cursor_row == x.cursor_row ) && ( cursor_visible == x.cursor_visible ) &&
 	( reverse_video == x.reverse_video ) && ( renditions == x.renditions ) &&
   ( bracketed_paste == x.bracketed_paste ) && ( vt100_mouse == x.vt100_mouse ) &&
-  ( xterm_mouse == x.xterm_mouse ) && ( xterm_extended_mouse == x.xterm_extended_mouse );
+  ( xterm_mouse == x.xterm_mouse ) && ( xterm_extended_mouse == x.xterm_extended_mouse ) && 
+  ( xterm_utf8_mouse == x.xterm_utf8_mouse );
     }
   };
 
diff --git a/src/terminal/terminalfunctions.cc b/src/terminal/terminalfunctions.cc
index 8598ad9..63558f0 100644
--- a/src/terminal/terminalfunctions.cc
+++ b/src/terminal/terminalfunctions.cc
@@ -272,6 +272,8 @@ static bool *get_DEC_mode( int param, Framebuffer *fb ) {
     return &(fb->ds.vt100_mouse);
   case 1002:           /* xterm mouse 2 (inc. button drags) */
     return &(fb->ds.xterm_mouse);
+  case 1005:           /* xterm UTF8 mouse */
+    return &(fb->ds.xterm_utf8_mouse);
   case 1006:           /* xterm extended mouse */
     return &(fb->ds.xterm_extended_mouse);
   case 2004: /* bracketed paste */

From e115f05f0de566bf3dbd5b847e10467e0e9f8402 Mon Sep 17 00:00:00 2001
From: Andrew Chin <achin@eminence32.net>
Date: Tue, 9 Sep 2014 20:51:07 -0400
Subject: [PATCH 39/40] Clear other mouse modes if 1000 or 1002 are set

I'm not sure if this is correct, but tmux does this!

Signed-off-by: Andrew Chin <achin@eminence32.net>
---
 src/terminal/terminalfunctions.cc | 16 +++++++++++++++-
 1 file changed, 15 insertions(+), 1 deletion(-)

diff --git a/src/terminal/terminalfunctions.cc b/src/terminal/terminalfunctions.cc
index 63558f0..c167c63 100644
--- a/src/terminal/terminalfunctions.cc
+++ b/src/terminal/terminalfunctions.cc
@@ -286,7 +286,21 @@ static bool *get_DEC_mode( int param, Framebuffer *fb ) {
 void CSI_DECSM( Framebuffer *fb, Dispatcher *dispatch )
 {
   for ( int i = 0; i < dispatch->param_count(); i++ ) {
-    bool *mode = get_DEC_mode( dispatch->getparam( i, 0 ), fb );
+    int param = dispatch->getparam( i, 0 );
+
+    if ( (param == 1000) || (param == 1002) ) {
+      // clear the other mouse modes before setting this one
+      bool *c_mode = get_DEC_mode( 1000, fb );
+      if ( c_mode ) { *c_mode = false; }
+      *c_mode = get_DEC_mode( 1002, fb );
+      if ( c_mode ) { *c_mode = false; }
+      *c_mode = get_DEC_mode( 1005, fb );
+      if ( c_mode ) { *c_mode = false; }
+      *c_mode = get_DEC_mode( 1006, fb );
+      if ( c_mode ) { *c_mode = false; }
+    }
+
+    bool *mode = get_DEC_mode( param, fb );
     if ( mode ) {
       *mode = true;
     }

From c6cd99ba971123aa4072d3ab8466c3d2467db9bd Mon Sep 17 00:00:00 2001
From: Keith Winstein <keithw@mit.edu>
Date: Mon, 15 Dec 2014 21:04:08 -0800
Subject: [PATCH 40/40] terminalfunctions.cc: Refactor out set_if_available()

---
 src/terminal/terminalfunctions.cc | 32 +++++++++++++++-----------------
 1 file changed, 15 insertions(+), 17 deletions(-)

diff --git a/src/terminal/terminalfunctions.cc b/src/terminal/terminalfunctions.cc
index c167c63..a1902a8 100644
--- a/src/terminal/terminalfunctions.cc
+++ b/src/terminal/terminalfunctions.cc
@@ -282,6 +282,12 @@ static bool *get_DEC_mode( int param, Framebuffer *fb ) {
   return NULL;
 }
 
+/* helper for CSI_DECSM and CSI_DECRM */
+void set_if_available( bool *mode, bool value )
+{
+  if ( mode ) { *mode = value; }
+}
+
 /* set private mode */
 void CSI_DECSM( Framebuffer *fb, Dispatcher *dispatch )
 {
@@ -289,21 +295,16 @@ void CSI_DECSM( Framebuffer *fb, Dispatcher *dispatch )
     int param = dispatch->getparam( i, 0 );
 
     if ( (param == 1000) || (param == 1002) ) {
-      // clear the other mouse modes before setting this one
-      bool *c_mode = get_DEC_mode( 1000, fb );
-      if ( c_mode ) { *c_mode = false; }
-      *c_mode = get_DEC_mode( 1002, fb );
-      if ( c_mode ) { *c_mode = false; }
-      *c_mode = get_DEC_mode( 1005, fb );
-      if ( c_mode ) { *c_mode = false; }
-      *c_mode = get_DEC_mode( 1006, fb );
-      if ( c_mode ) { *c_mode = false; }
+      /* we believe all mouse modes should be set to false
+	 when either of these two modes are enabled */
+      /* XXX can we cite something for this? -KJW 15Dec2014 */
+      set_if_available( get_DEC_mode( 1000, fb ), false );
+      set_if_available( get_DEC_mode( 1002, fb ), false );
+      set_if_available( get_DEC_mode( 1005, fb ), false );
+      set_if_available( get_DEC_mode( 1006, fb ), false );
     }
 
-    bool *mode = get_DEC_mode( param, fb );
-    if ( mode ) {
-      *mode = true;
-    }
+    set_if_available( get_DEC_mode( param, fb ), true );
   }
 }
 
@@ -311,10 +312,7 @@ void CSI_DECSM( Framebuffer *fb, Dispatcher *dispatch )
 void CSI_DECRM( Framebuffer *fb, Dispatcher *dispatch )
 {
   for ( int i = 0; i < dispatch->param_count(); i++ ) {
-    bool *mode = get_DEC_mode( dispatch->getparam( i, 0 ), fb );
-    if ( mode ) {
-      *mode = false;
-    }
+    set_if_available( get_DEC_mode( dispatch->getparam( i, 0 ), fb ), false );
   }
 }
 
