diff options
author | Francesco Colista <fcolista@alpinelinux.org> | 2015-07-29 14:57:48 +0000 |
---|---|---|
committer | Francesco Colista <fcolista@alpinelinux.org> | 2015-07-29 14:57:53 +0000 |
commit | e5fcf52dcf5a3e88603bcafe12a845a1535290d3 (patch) | |
tree | c6d59d56c211ff0a0c1867d95fec1130158fd1d1 /main/mosh | |
parent | adf8d91711f3f62202459232e64325d9610d5846 (diff) | |
download | aports-e5fcf52dcf5a3e88603bcafe12a845a1535290d3.tar.bz2 aports-e5fcf52dcf5a3e88603bcafe12a845a1535290d3.tar.xz |
main/mosh: upgrade to 1.2.5
Diffstat (limited to 'main/mosh')
-rw-r--r-- | main/mosh/APKBUILD | 28 | ||||
-rw-r--r-- | main/mosh/out-of-band-data-and-ssh-agent-forwarding.patch | 2301 |
2 files changed, 12 insertions, 2317 deletions
diff --git a/main/mosh/APKBUILD b/main/mosh/APKBUILD index 233533317d..df0b4b578f 100644 --- a/main/mosh/APKBUILD +++ b/main/mosh/APKBUILD @@ -1,19 +1,18 @@ -# Contributor: Francesco Colista <francesco.colista@gmail.com> -# Maintainer: Francesco Colista <francesco.colista@gmail.com> +# Contributor: Francesco Colista <fcolista@alpinelinux.org> +# Maintainer: Francesco Colista <fcolista@alpinelinux.org> pkgname=mosh -pkgver=1.2.4 -pkgrel=8 +pkgver=1.2.5 +pkgrel=0 pkgdesc="Mobile shell (mosh) surviving disconnects with local echo and line editing" url="http://mosh.mit.edu" -arch="all" +arch="noarch" license="GPL3+" depends="$pkgname-client $pkgname-server" makedepends="ncurses-dev zlib-dev openssl-dev perl-dev perl-io-tty protobuf-dev automake autoconf libtool" subpackages="$pkgname-doc $pkgname-client $pkgname-server" source="http://$pkgname.mit.edu/$pkgname-$pkgver.tar.gz - disable-utf8-check.patch - out-of-band-data-and-ssh-agent-forwarding.patch" + disable-utf8-check.patch" _builddir="$srcdir"/$pkgname-$pkgver @@ -61,12 +60,9 @@ client() { "$subpkgdir"/usr/bin/ || return 1 } -md5sums="c2d918f4d91fdc32546e2e089f9281b2 mosh-1.2.4.tar.gz -f9e6a14dc7a300d95625265ab5e847d7 disable-utf8-check.patch -8f05f2418ca7311ceb1bc6732db17ca3 out-of-band-data-and-ssh-agent-forwarding.patch" -sha256sums="e74d0d323226046e402dd469a176075fc2013b69b0e67cea49762c957175df46 mosh-1.2.4.tar.gz -60416de55be97a3c80d3b89e44b8602a8b4dcca6de8e70cb15d2c96e30a7de42 disable-utf8-check.patch -5f35f7e84c08e38f112d8b8f06df09063f54f35feccaf62e972b4b52302aa2d6 out-of-band-data-and-ssh-agent-forwarding.patch" -sha512sums="f7505faffdc8da734179b37339b554f83cbf5450b251cd2aa50d63cd6e4cbefa0da17a1c1b2a61858735ac9e5cee5841ed20e81e244380f5f9a02af1b87199cc mosh-1.2.4.tar.gz -3c3b60b9aa837d76e53855907c59c3b1648e3a2e166b3ec902aec117e4e56d850553a089401a3bb9901412c125d30d4dac76d204721a17286a0ddc922508f6fc disable-utf8-check.patch -54d8ce032a3d1cb5adaf7272b685a263e9aebe4daefae7dfb6d7f52be275d7a05c22e06b9d7424b3aa05642e60e1edc88d093a4feeca5b97a313ae8cd883a28f out-of-band-data-and-ssh-agent-forwarding.patch" +md5sums="56d7147cf7031583ba7d8db09033e0c5 mosh-1.2.5.tar.gz +f9e6a14dc7a300d95625265ab5e847d7 disable-utf8-check.patch" +sha256sums="1af809e5d747c333a852fbf7acdbf4d354dc4bbc2839e3afe5cf798190074be3 mosh-1.2.5.tar.gz +60416de55be97a3c80d3b89e44b8602a8b4dcca6de8e70cb15d2c96e30a7de42 disable-utf8-check.patch" +sha512sums="6a5a42e5ed3f265bc8bee045340a59f604ab8f08b041573264f5679c29678e84d101537aa2d856923eee8d0a0f9c052dc81cfbfa50ce12bd0eeddc5c8f1fc3ae mosh-1.2.5.tar.gz +3c3b60b9aa837d76e53855907c59c3b1648e3a2e166b3ec902aec117e4e56d850553a089401a3bb9901412c125d30d4dac76d204721a17286a0ddc922508f6fc disable-utf8-check.patch" diff --git a/main/mosh/out-of-band-data-and-ssh-agent-forwarding.patch b/main/mosh/out-of-band-data-and-ssh-agent-forwarding.patch deleted file mode 100644 index 7132f0b6ae..0000000000 --- a/main/mosh/out-of-band-data-and-ssh-agent-forwarding.patch +++ /dev/null @@ -1,2301 +0,0 @@ -From 0c1b247b616bd4810041f48f4d903828af0ba837 Mon Sep 17 00:00:00 2001 -From: "Timo J. Rinne" <tri@iki.fi> -Date: Sat, 11 May 2013 21:52:40 +0000 -Subject: [PATCH] Pluggable out of band communication mechanism over Mosh - transport layer and agent forwarding support in top of out of band mechanism. - -I contrubute this code to Mosh project under the same license that -Mosh itself is distributed. All licensing options and clauses -included. - -Signed-off-by: Timo J. Rinne <tri@iki.fi> ---- - configure.ac | 17 +- - man/mosh.1 | 9 + - scripts/mosh | 20 +- - src/Makefile.am | 2 +- - src/agent/Makefile.am | 7 + - src/agent/agent.cc | 486 +++++++++++++++++++++++++++++++ - src/agent/agent.h | 110 +++++++ - src/frontend/Makefile.am | 4 +- - src/frontend/mosh-client.cc | 9 +- - src/frontend/mosh-server.cc | 62 +++- - src/frontend/stmclient.cc | 31 +- - src/frontend/stmclient.h | 5 +- - src/network/Makefile.am | 2 +- - src/network/networktransport.cc | 5 + - src/network/networktransport.h | 3 + - src/network/outofband.cc | 265 +++++++++++++++++ - src/network/outofband.h | 107 +++++++ - src/network/transportsender.cc | 21 +- - src/network/transportsender.h | 8 +- - src/protobufs/Makefile.am | 2 +- - src/protobufs/agent.proto | 8 + - src/protobufs/oob.proto | 11 + - src/protobufs/transportinstruction.proto | 2 + - src/util/Makefile.am | 2 +- - src/util/swrite.cc | 45 +++ - src/util/swrite.h | 3 + - 26 files changed, 1217 insertions(+), 29 deletions(-) - create mode 100644 src/agent/Makefile.am - create mode 100644 src/agent/agent.cc - create mode 100644 src/agent/agent.h - create mode 100644 src/network/outofband.cc - create mode 100644 src/network/outofband.h - create mode 100644 src/protobufs/agent.proto - create mode 100644 src/protobufs/oob.proto - -diff --git a/configure.ac b/configure.ac -index b07291a..bf1a841 100644 ---- a/configure.ac -+++ b/configure.ac -@@ -164,19 +164,28 @@ AS_IF([test x"$with_utempter" != xno], - [AC_MSG_WARN([Unable to find libutempter; utmp entries will not be made.])], - [AC_MSG_ERROR([--with-utempter was given but libutempter was not found.])])])]) - -+# Handle --disable-agent-forwarding -+AC_ARG_ENABLE(agent-forwarding, -+ AS_HELP_STRING([--disable-agent-forwarding], -+ [disable ssh agent forwarding in compile time]), -+ , enable_agent_forwarding=yes) -+ -+ - AC_SEARCH_LIBS([compress], [z], , [AC_MSG_ERROR([Unable to find zlib.])]) - - 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([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 errno.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]) - AC_CHECK_HEADERS([utmpx.h]) - AC_CHECK_HEADERS([termio.h]) - AC_CHECK_HEADERS([sys/uio.h]) -+AC_CHECK_HEADERS([sys/un.h]) -+AC_CHECK_HEADERS([sys/types.h]) - - # Checks for typedefs, structures, and compiler characteristics. - AC_HEADER_STDBOOL -@@ -322,6 +331,11 @@ AC_CHECK_DECL([IUTF8], - [AC_MSG_WARN([No IUTF8 termios mode; character-erase of multibyte character sequence probably does not work properly in canonical mode on this platform.])], - [[#include <termios.h>]]) - -+if test "$enable_agent_forwarding" = "yes"; then -+ AC_DEFINE([SUPPORT_AGENT_FORWARDING], [], [ -+ Define to enable support for SSH agent forwarding]) -+fi -+ - # Checks for protobuf - PKG_CHECK_MODULES([protobuf], [protobuf]) - -@@ -334,6 +348,7 @@ AC_CONFIG_FILES([ - src/protobufs/Makefile - src/statesync/Makefile - src/terminal/Makefile -+ src/agent/Makefile - src/util/Makefile - scripts/Makefile - src/examples/Makefile -diff --git a/man/mosh.1 b/man/mosh.1 -index 14405bf..5d98053 100644 ---- a/man/mosh.1 -+++ b/man/mosh.1 -@@ -99,6 +99,11 @@ OpenSSH command to remotely execute mosh-server on remote machine (default: "ssh - An alternate ssh port can be specified with, \fIe.g.\fP, \-\-ssh="ssh \-p 2222". - - .TP -+.B \-\-forward-agent -+Enable ssh authentication agent forwarding. If you use this, please be -+aware of the security implications. -+ -+.TP - .B \-\-predict=\fIWHEN\fP - Controls use of speculative local echo. WHEN defaults to `adaptive' - (show predictions on slower links and to smooth out network glitches) -@@ -113,6 +118,10 @@ of the terminal has been confirmed by the server, without any - intervening control character keystrokes. - - .TP -+.B \-A -+Synonym for \-\-forward-agent -+ -+.TP - .B \-a - Synonym for \-\-predict=always - -diff --git a/scripts/mosh b/scripts/mosh -index 4e8b796..3c7f197 100755 ---- a/scripts/mosh -+++ b/scripts/mosh -@@ -52,6 +52,8 @@ my $ssh = 'ssh'; - - my $term_init = 1; - -+my $forward_agent = 0; -+ - my $help = undef; - my $version = undef; - -@@ -78,6 +80,8 @@ qq{Usage: $0 [options] [--] [user@]host [command...] - (example: "ssh -p 2222") - (default: "ssh") - -+-A --forward-agent enable ssh agent forwarding -+ - --no-init do not send terminal initialization string - - --help this message -@@ -112,6 +116,8 @@ GetOptions( 'client=s' => \$client, - 'n' => sub { $predict = 'never' }, - 'p=s' => \$port_request, - 'ssh=s' => \$ssh, -+ 'A' => \$forward_agent, -+ 'forward-agent!' => \$forward_agent, - 'init!' => \$term_init, - 'help' => \$help, - 'version' => \$version, -@@ -242,6 +248,10 @@ if ( $pid == 0 ) { # child - - my @server = ( 'new' ); - -+ if ( $forward_agent ) { -+ push @server, ( '-A' ); -+ } -+ - push @server, ( '-c', $colors ); - - push @server, @bind_arguments; -@@ -259,6 +269,7 @@ if ( $pid == 0 ) { # child - } - - my $quoted_self = shell_quote( $0 ); -+ - 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 -@@ -302,7 +313,14 @@ if ( $pid == 0 ) { # child - $ENV{ 'MOSH_KEY' } = $key; - $ENV{ 'MOSH_PREDICTION_DISPLAY' } = $predict; - $ENV{ 'MOSH_NO_TERM_INIT' } = '1' if !$term_init; -- exec {$client} ("$client @cmdline |", $ip, $port); -+ -+ my @client_av = (); -+ if ( $forward_agent ) { -+ push @client_av, ( '-A' ); -+ } -+ push @client_av, ( $ip, $port ); -+ -+ exec {$client} ("$client @cmdline |", @client_av); - } - - sub shell_quote { join ' ', map {(my $a = $_) =~ s/'/'\\''/g; "'$a'"} @_ } -diff --git a/src/Makefile.am b/src/Makefile.am -index 2390f7c..332bb2c 100644 ---- a/src/Makefile.am -+++ b/src/Makefile.am -@@ -1 +1 @@ --SUBDIRS = protobufs util crypto terminal network statesync frontend examples tests -+SUBDIRS = protobufs util crypto terminal network statesync agent frontend examples tests -diff --git a/src/agent/Makefile.am b/src/agent/Makefile.am -new file mode 100644 -index 0000000..7ec93ea ---- /dev/null -+++ b/src/agent/Makefile.am -@@ -0,0 +1,7 @@ -+AM_CPPFLAGS = -I$(srcdir)/../util -I$(srcdir)/../network -I$(srcdir)/../protobufs -I$(srcdir)/../crypto $(TINFO_CFLAGS) -+AM_CXXFLAGS = $(WARNING_CXXFLAGS) $(PICKY_CXXFLAGS) $(HARDEN_CFLAGS) $(MISC_CXXFLAGS) -+ -+noinst_LIBRARIES = libmoshagent.a -+ -+libmoshagent_a_SOURCES = agent.h agent.cc -+ -diff --git a/src/agent/agent.cc b/src/agent/agent.cc -new file mode 100644 -index 0000000..12cd31b ---- /dev/null -+++ b/src/agent/agent.cc -@@ -0,0 +1,486 @@ -+/* -+ Mosh: the mobile shell -+ Copyright 2012 Keith Winstein -+ -+ SSH Agent forwarding for Mosh -+ Copyright 2013 Timo J. Rinne -+ -+ This program is free software: you can redistribute it and/or modify -+ it under the terms of the GNU General Public License as published by -+ the Free Software Foundation, either version 3 of the License, or -+ (at your option) any later version. -+ -+ This program is distributed in the hope that it will be useful, -+ but WITHOUT ANY WARRANTY; without even the implied warranty of -+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ GNU General Public License for more details. -+ -+ You should have received a copy of the GNU General Public License -+ along with this program. If not, see <http://www.gnu.org/licenses/>. -+ -+ In addition, as a special exception, the copyright holders give -+ permission to link the code of portions of this program with the -+ OpenSSL library under certain conditions as described in each -+ individual source file, and distribute linked combinations including -+ the two. -+ -+ You must obey the GNU General Public License in all respects for all -+ of the code used other than OpenSSL. If you modify file(s) with this -+ exception, you may extend this exception to your version of the -+ file(s), but you are not obligated to do so. If you do not wish to do -+ so, delete this exception statement from your version. If you delete -+ this exception statement from all source files in the program, then -+ also delete it here. -+*/ -+ -+#include "config.h" -+ -+#include <stdlib.h> -+#include <string.h> -+#include <unistd.h> -+#include <fcntl.h> -+#include <errno.h> -+#include <sys/stat.h> -+#ifdef HAVE_SYS_TYPES_H -+#include <sys/types.h> -+#endif -+#include <sys/socket.h> -+ -+#ifdef SUPPORT_AGENT_FORWARDING -+#ifdef HAVE_SYS_UN_H -+#include <sys/un.h> -+#else -+#undef SUPPORT_AGENT_FORWARDING -+#endif -+#endif -+ -+#include "prng.h" -+#include "network.h" -+#include "swrite.h" -+#include "select.h" -+#include "outofband.h" -+#include "agent.h" -+#include "agent.pb.h" -+#include "fatal_assert.h" -+ -+using namespace Agent; -+using std::string; -+using std::map; -+using Network::OutOfBand; -+using Network::OutOfBandCommunicator; -+ -+ProxyAgent::ProxyAgent( bool is_server, bool dummy ) { -+ server = is_server; -+ ok = false; -+ l_sock = -1; -+ l_dir = ""; -+ l_path = ""; -+ cnt = 0; -+ oob_ctl_ptr = NULL; -+ comm = NULL; -+#ifdef SUPPORT_AGENT_FORWARDING -+ if ( dummy ) { -+ return; -+ } -+ if (server) { -+ PRNG prng; -+ string dir("/tmp/ma-"); -+ string voc = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; -+ int i; -+ for ( i = 0; i < 10; i++ ) { -+ dir += voc.substr( prng.uint32() % voc.length(), 1 ); -+ } -+ if ( mkdir( dir.c_str(), 0700 ) != 0 ) { -+ return; -+ } -+ string path(dir + "/"); -+ for ( i = 0; i < 12; i++ ) { -+ path += voc.substr( prng.uint32() % voc.length(), 1 ); -+ } -+ int sock = socket( AF_UNIX, SOCK_STREAM, 0 ); -+ if ( sock < 0 ) { -+ (void) rmdir( dir.c_str() ); -+ return; -+ } -+ if ( fcntl( sock, F_SETFD, FD_CLOEXEC ) != 0 ) { -+ (void) rmdir( dir.c_str() ); -+ return; -+ } -+ struct sockaddr_un sunaddr; -+ memset( &sunaddr, 0, sizeof (sunaddr) ); -+ sunaddr.sun_family = AF_UNIX; -+ if ( path.length() >= sizeof (sunaddr.sun_path) ) { -+ (void) close( sock ); -+ (void) rmdir( dir.c_str() ); -+ return; -+ } -+ strncpy( sunaddr.sun_path, path.c_str(), sizeof (sunaddr.sun_path) ); -+ if ( bind( sock, (struct sockaddr *) &sunaddr, sizeof (sunaddr) ) < 0 ) { -+ (void) close( sock ); -+ (void) rmdir( dir.c_str() ); -+ return; -+ } -+ if ( listen( sock, AGENT_PROXY_LISTEN_QUEUE_LENGTH ) < 0) { -+ (void) close( sock ); -+ (void) unlink( path.c_str() ); -+ (void) rmdir( dir.c_str() ); -+ return; -+ } -+ l_sock = sock; -+ l_path = path; -+ l_dir = dir; -+ } -+ ok = true; -+#endif -+} -+ -+ProxyAgent::~ProxyAgent( void ) { -+#ifdef SUPPORT_AGENT_FORWARDING -+ shutdown_server(); -+#endif -+} -+ -+void ProxyAgent::close_sessions( void ) { -+#ifdef SUPPORT_AGENT_FORWARDING -+ map< uint64_t, AgentConnection * >::iterator i = agent_sessions.begin(); -+ while ( i != agent_sessions.end() ) { -+ AgentConnection *ac = i->second; -+ agent_sessions.erase( i ); -+ delete ac; -+ i = agent_sessions.begin(); -+ } -+#endif -+} -+ -+void ProxyAgent::shutdown_server( void ) { -+#ifdef SUPPORT_AGENT_FORWARDING -+ detach_oob(); -+ if (ok) { -+ if ( server && l_sock >= 0 ) { -+ (void) close( l_sock ); -+ (void) unlink( l_path.c_str() ); -+ (void) rmdir( l_dir.c_str() ); -+ l_sock = -1; -+ l_path = ""; -+ l_dir = ""; -+ } -+ close_sessions(); -+ ok = false; -+ } -+#endif -+} -+ -+void ProxyAgent::attach_oob(OutOfBand *oob_ctl) { -+ detach_oob(); -+ fatal_assert(oob_ctl != NULL); -+ oob_ctl_ptr = oob_ctl; -+ comm = oob_ctl_ptr->init(AGENT_FORWARD_OOB_NAME, Network::OOB_MODE_RELIABLE_DATAGRAM); -+ fatal_assert(comm != NULL); -+} -+ -+void ProxyAgent::detach_oob(void) { -+ if (oob_ctl_ptr != NULL) { -+ oob_ctl_ptr->uninit(AGENT_FORWARD_OOB_NAME); -+ } -+ oob_ctl_ptr = NULL; -+} -+ -+void ProxyAgent::pre_poll( void ) { -+#ifdef SUPPORT_AGENT_FORWARDING -+ if ( ! ok ) { -+ return; -+ } -+ Select &sel = Select::get_instance(); -+ if ( server && l_sock >= 0 ) { -+ sel.add_fd( l_sock ); -+ } -+ for ( map< uint64_t, AgentConnection * >::iterator i = agent_sessions.begin(); i != agent_sessions.end(); i++ ) { -+ AgentConnection *ac = i->second; -+ if ( ac->sock() >= 0 ) { -+ sel.add_fd( ac->sock() ); -+ ac->mark_in_read_set(true); -+ } else { -+ ac->mark_in_read_set(false); -+ } -+ } -+#endif -+} -+ -+void ProxyAgent::post_poll( void ) { -+#ifdef SUPPORT_AGENT_FORWARDING -+ if ( ! ok ) { -+ return; -+ } -+ Select &sel = Select::get_instance(); -+ // First handle possible incoming data from local sockets -+ map< uint64_t, AgentConnection * >::iterator i = agent_sessions.begin(); -+ while ( ((! server) || (l_sock >= 0)) && i != agent_sessions.end() ) { -+ AgentConnection *ac = i->second; -+ if ( (comm == NULL) || (oob_ctl_ptr == NULL) || ac->eof() || (ac->idle_time() > AGENT_IDLE_TIMEOUT) ) { -+ agent_sessions.erase( i++ ); -+ delete ac; -+ continue; -+ } -+ -+ if ( ac->in_read_set() && sel.read( ac->sock() ) ) { -+ while ( true ) { -+ string packet = ac->recv_packet(); -+ if ( ! packet.empty() ) { -+ AgentBuffers::Instruction inst; -+ inst.set_agent_id(ac->s_id); -+ inst.set_agent_data(packet); -+ string pb_packet; -+ fatal_assert(inst.SerializeToString(&pb_packet)); -+ comm->send(pb_packet); -+ continue; -+ } -+ if ( ac->eof() ) { -+ notify_eof(ac->s_id); -+ agent_sessions.erase( i++ ); -+ delete ac; -+ break; -+ } -+ i++; -+ break; -+ } -+ } else { -+ i++; -+ } -+ } -+ if ( ! server ) { -+ return; -+ } -+ // Then see if we have mysteriously died in between. -+ if ( l_sock < 0 ) { -+ return; -+ } -+ // Then check for new incoming connections. -+ if ( sel.read( l_sock ) ) { -+ AgentConnection *new_as = get_session(); -+ if ( new_as != NULL ) { -+ agent_sessions[new_as->s_id] = new_as; -+ } -+ } -+#endif -+} -+ -+void ProxyAgent::post_tick( void ) { -+#ifdef SUPPORT_AGENT_FORWARDING -+ if ( (! ok) || (comm == NULL) ) { -+ return; -+ } -+ while (comm->readable()) { -+ string pb_packet = comm->recv(); -+ AgentBuffers::Instruction inst; -+ fatal_assert( inst.ParseFromString(pb_packet) ); -+ uint64_t agent_id = inst.agent_id(); -+ string agent_data = inst.has_agent_data() ? inst.agent_data() : ""; -+ if (agent_data.empty()) { -+ map < uint64_t, AgentConnection* >::iterator i = agent_sessions.find(agent_id); -+ if (i != agent_sessions.end()) { -+ AgentConnection *ac = i->second; -+ agent_sessions.erase( i ); -+ delete ac; -+ } -+ } else { -+ map < uint64_t, AgentConnection* >::iterator i = agent_sessions.find(agent_id); -+ if (i == agent_sessions.end()) { -+ AgentConnection *new_as = NULL; -+ if (! server) { -+ const char *ap = getenv( "SSH_AUTH_SOCK" ); -+ if ( ap != NULL ) { -+ string agent_path(ap); -+ if ( ! agent_path.empty() ) { -+ new_as = new AgentConnection ( agent_path, agent_id, this ); -+ } -+ } -+ } -+ if (new_as == NULL) { -+ notify_eof(agent_id); -+ } else { -+ agent_sessions[agent_id] = new_as; -+ } -+ i = agent_sessions.find(agent_id); -+ } -+ if (i != agent_sessions.end()) { -+ AgentConnection *ac = i->second; -+ uint64_t idle = ac->idle_time(); -+ uint64_t timeout = idle < AGENT_IDLE_TIMEOUT ? (AGENT_IDLE_TIMEOUT - idle) * 1000 : 1; -+ if ( swrite_timeout( ac->sock(), timeout, agent_data.c_str(), agent_data.length() ) != 0 ) { -+ agent_sessions.erase( i ); -+ delete ac; -+ notify_eof(agent_id); -+ } -+ } -+ } -+ } -+#endif -+} -+ -+void ProxyAgent::notify_eof(uint64_t agent_id) { -+#ifdef SUPPORT_AGENT_FORWARDING -+ if (comm == NULL) { -+ return; -+ } -+ AgentBuffers::Instruction inst; -+ inst.set_agent_id(agent_id); -+ string pb_packet; -+ fatal_assert(inst.SerializeToString(&pb_packet)); -+ comm->send(pb_packet); -+#endif -+} -+ -+ -+AgentConnection *ProxyAgent::get_session() { -+#ifdef SUPPORT_AGENT_FORWARDING -+ if ( (! server) || l_sock < 0) { -+ return NULL; -+ } -+ struct sockaddr_un sunaddr; -+ socklen_t slen = sizeof ( sunaddr ); -+ memset( &sunaddr, 0, slen ); -+ int sock = accept ( l_sock, (struct sockaddr *)&sunaddr, &slen ); -+ if ( sock < 0 ) { -+ return NULL; -+ } -+ -+ if ( (comm == NULL) || (oob_ctl_ptr == NULL) ) { -+ (void) close( sock ); -+ return NULL; -+ } -+ -+ /* Here we should check that peer effective uid matches with the -+ euid of this process. Skipping however and trusting the file -+ system to protect the socket. This would basically catch root -+ accessing the socket, but root can change its effective uid to -+ match the socket anyways, so it doesn't really help at all. */ -+ -+ /* If can't set the socket mode, discard it. */ -+ if ( fcntl( sock, F_SETFD, FD_CLOEXEC ) != 0 || fcntl( sock, F_SETFL, O_NONBLOCK ) != 0 ) { -+ (void) close( sock ); -+ return NULL; -+ } -+ return new AgentConnection ( sock, ++cnt, this ); -+#else -+ return NULL; -+#endif -+} -+ -+AgentConnection::AgentConnection(int sock, uint64_t id, ProxyAgent *s_agent_ptr) { -+ agent_ptr = s_agent_ptr; -+ s_sock = sock; -+ s_id = id; -+ s_in_read_set = false; -+#ifndef SUPPORT_AGENT_FORWARDING -+ if (sock >= 0) { -+ (void) close( sock ); -+ } -+ s_sock = -1; -+#endif -+ idle_start = Network::timestamp(); -+ packet_buf = ""; -+ packet_len = 0; -+} -+ -+AgentConnection::AgentConnection(std::string agent_path, uint64_t id, ProxyAgent *s_agent_ptr) { -+ agent_ptr = s_agent_ptr; -+ s_sock = -1; -+ s_id = id; -+ s_in_read_set = false; -+#ifdef SUPPORT_AGENT_FORWARDING -+ int sock = socket( AF_UNIX, SOCK_STREAM, 0 ); -+ struct sockaddr_un sunaddr; -+ memset( &sunaddr, 0, sizeof (sunaddr) ); -+ sunaddr.sun_family = AF_UNIX; -+ if ( agent_path.length() >= sizeof (sunaddr.sun_path) ) { -+ (void) close( sock ); -+ return; -+ } -+ if ( fcntl( sock, F_SETFD, FD_CLOEXEC ) != 0 ) { -+ (void) close( sock ); -+ return; -+ } -+ strncpy( sunaddr.sun_path, agent_path.c_str(), sizeof (sunaddr.sun_path) ); -+ if ( connect(sock, (struct sockaddr *)&sunaddr, sizeof (sunaddr)) < 0 ) { -+ (void) close( sock ); -+ return; -+ } -+ if ( fcntl( sock, F_SETFL, O_NONBLOCK ) != 0 ) { -+ (void) close( sock ); -+ return; -+ } -+ s_sock = sock; -+#endif -+ idle_start = Network::timestamp(); -+ packet_buf = ""; -+ packet_len = 0; -+} -+ -+AgentConnection::~AgentConnection() { -+ if ( s_sock >= 0 ) { -+ (void) close ( s_sock ); -+ } -+} -+ -+uint64_t AgentConnection::idle_time() { -+ return (Network::timestamp() - idle_start) / 1000; -+} -+ -+string AgentConnection::recv_packet() { -+#ifdef SUPPORT_AGENT_FORWARDING -+ if (eof()) { -+ return ""; -+ } -+ ssize_t rv; -+ if (packet_len < 1) { -+ unsigned char buf[4]; -+ rv = read( s_sock, buf, 4 ); -+ if ( (rv < 0) && ( errno == EAGAIN || errno == EWOULDBLOCK ) ) { -+ return ""; -+ } -+ if ( rv != 4 ) { -+ (void) close(s_sock); -+ s_sock = -1; -+ return ""; -+ } -+ if ( buf[0] != 0 ) { -+ (void) close(s_sock); -+ s_sock = -1; -+ return ""; -+ } -+ -+ packet_len = (((size_t)buf[1]) << 16) | (((size_t)buf[2]) << 8) | ((size_t)buf[3]); -+ if ( packet_len < 1 || packet_len > AGENT_MAXIMUM_PACKET_LENGTH ) { -+ (void) close(s_sock); -+ s_sock = -1; -+ return ""; -+ } -+ packet_buf.append((char *)buf, 4); -+ idle_start = Network::timestamp(); -+ } -+ /* read in loop until the entire packet is read or EAGAIN happens */ -+ do { -+ unsigned char buf[1024]; -+ size_t len = packet_len + 4 - packet_buf.length(); -+ if (len > sizeof (buf)) { -+ len = sizeof (buf); -+ } -+ rv = read(s_sock, buf, len); -+ if ( (rv < 0) && ( errno == EAGAIN || errno == EWOULDBLOCK ) ) { -+ return ""; -+ } -+ if ( rv < 1 ) { -+ (void) close(s_sock); -+ s_sock = -1; -+ return ""; -+ } -+ packet_buf.append((char *)buf, rv); -+ idle_start = Network::timestamp(); -+ } while (packet_buf.length() < (packet_len + 4)); -+ string packet(packet_buf); -+ packet_buf = ""; -+ packet_len = 0; -+ return packet; -+#endif -+ return ""; -+} -diff --git a/src/agent/agent.h b/src/agent/agent.h -new file mode 100644 -index 0000000..d8f98d4 ---- /dev/null -+++ b/src/agent/agent.h -@@ -0,0 +1,110 @@ -+/* -+ Mosh: the mobile shell -+ Copyright 2012 Keith Winstein -+ -+ SSH Agent forwarding for Mosh -+ Copyright 2013 Timo J. Rinne -+ -+ This program is free software: you can redistribute it and/or modify -+ it under the terms of the GNU General Public License as published by -+ the Free Software Foundation, either version 3 of the License, or -+ (at your option) any later version. -+ -+ This program is distributed in the hope that it will be useful, -+ but WITHOUT ANY WARRANTY; without even the implied warranty of -+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ GNU General Public License for more details. -+ -+ You should have received a copy of the GNU General Public License -+ along with this program. If not, see <http://www.gnu.org/licenses/>. -+ -+ In addition, as a special exception, the copyright holders give -+ permission to link the code of portions of this program with the -+ OpenSSL library under certain conditions as described in each -+ individual source file, and distribute linked combinations including -+ the two. -+ -+ You must obey the GNU General Public License in all respects for all -+ of the code used other than OpenSSL. If you modify file(s) with this -+ exception, you may extend this exception to your version of the -+ file(s), but you are not obligated to do so. If you do not wish to do -+ so, delete this exception statement from your version. If you delete -+ this exception statement from all source files in the program, then -+ also delete it here. -+*/ -+ -+#ifndef AGENT_HPP -+#define AGENT_HPP -+ -+#include <string> -+#include <map> -+ -+#include "outofband.h" -+ -+#define AGENT_MAXIMUM_PACKET_LENGTH 32768 // Not counting the length field. -+#define AGENT_MAXIMUM_OUTPUT_BUFFER_LENGTH (AGENT_MAXIMUM_PACKET_LENGTH * 4) // Counting all data -+#define AGENT_IDLE_TIMEOUT 30 // In seconds. Must be enforced by the caller. -+#define AGENT_PROXY_LISTEN_QUEUE_LENGTH 4 -+#define AGENT_FORWARD_OOB_NAME "ssh-agent-forward" -+ -+namespace Agent { -+ -+ class ProxyAgent; -+ -+ class AgentConnection -+ { -+ private: -+ bool s_in_read_set; -+ int s_sock; -+ uint64_t s_id; -+ uint64_t idle_start; -+ string packet_buf; -+ size_t packet_len; -+ AgentConnection(int sock, uint64_t id, ProxyAgent *s_agent_ptr); -+ AgentConnection(std::string agent_path, uint64_t id, ProxyAgent *s_agent_ptr); -+ ~AgentConnection(); -+ int sock() { return s_sock; } -+ bool eof() { return (s_sock < 0); } -+ std::string recv_packet(); -+ uint64_t idle_time(); -+ void mark_in_read_set(bool val) { s_in_read_set = val; } -+ bool in_read_set( void ) { return s_in_read_set; } -+ ProxyAgent *agent_ptr; -+ -+ public: -+ friend ProxyAgent; -+ }; -+ -+ class ProxyAgent { -+ private: -+ Network::OutOfBand *oob_ctl_ptr; -+ Network::OutOfBand *oob( void ) { return oob_ctl_ptr; } -+ void detach_oob(void); -+ void notify_eof(uint64_t agent_id); -+ AgentConnection *get_session(); -+ bool server; -+ bool ok; -+ int l_sock; -+ string l_dir; -+ string l_path; -+ uint64_t cnt; -+ std::map< uint64_t, AgentConnection * > agent_sessions; -+ Network::OutOfBandCommunicator *comm; -+ public: -+ ProxyAgent( bool is_server, bool dummy = false ); -+ ~ProxyAgent( void ); -+ void attach_oob(Network::OutOfBand *oob_ctl); -+ bool active() { return ok && ((! server) || (l_sock >= 0)); } -+ std::string listener_path( void ) { if ( ok && server && l_sock >= 0 ) return l_path; return ""; } -+ void pre_poll( void ); -+ void post_poll( void ); -+ void post_tick( void ); -+ void close_sessions( void ); -+ void shutdown_server( void ); -+ -+ friend AgentConnection; -+ }; -+ -+} -+ -+#endif -diff --git a/src/frontend/Makefile.am b/src/frontend/Makefile.am -index a0345ae..91b5a7b 100644 ---- a/src/frontend/Makefile.am -+++ b/src/frontend/Makefile.am -@@ -1,7 +1,7 @@ --AM_CPPFLAGS = -I$(srcdir)/../statesync -I$(srcdir)/../terminal -I$(srcdir)/../network -I$(srcdir)/../crypto -I../protobufs -I$(srcdir)/../util $(TINFO_CFLAGS) $(protobuf_CFLAGS) $(OPENSSL_CFLAGS) -+AM_CPPFLAGS = -I$(srcdir)/../statesync -I$(srcdir)/../terminal -I$(srcdir)/../network -I$(srcdir)/../crypto -I$(srcdir)/../protobufs -I$(srcdir)/../agent -I$(srcdir)/../util $(TINFO_CFLAGS) $(protobuf_CFLAGS) $(OPENSSL_CFLAGS) - AM_CXXFLAGS = $(WARNING_CXXFLAGS) $(PICKY_CXXFLAGS) $(HARDEN_CFLAGS) $(MISC_CXXFLAGS) - AM_LDFLAGS = $(HARDEN_LDFLAGS) --LDADD = ../crypto/libmoshcrypto.a ../network/libmoshnetwork.a ../statesync/libmoshstatesync.a ../terminal/libmoshterminal.a ../util/libmoshutil.a ../protobufs/libmoshprotos.a -lm $(TINFO_LIBS) $(protobuf_LIBS) $(OPENSSL_LIBS) -+LDADD = ../crypto/libmoshcrypto.a ../network/libmoshnetwork.a ../statesync/libmoshstatesync.a ../terminal/libmoshterminal.a ../agent/libmoshagent.a ../util/libmoshutil.a ../protobufs/libmoshprotos.a -lm $(TINFO_LIBS) $(protobuf_LIBS) $(OPENSSL_LIBS) - - mosh_server_LDADD = $(LDADD) $(LIBUTIL) - -diff --git a/src/frontend/mosh-client.cc b/src/frontend/mosh-client.cc -index e338682..970cd22 100644 ---- a/src/frontend/mosh-client.cc -+++ b/src/frontend/mosh-client.cc -@@ -101,10 +101,15 @@ int main( int argc, char *argv[] ) - /* Detect edge case */ - fatal_assert( argc > 0 ); - -+ bool forward_agent = false; -+ - /* Get arguments */ - int opt; -- while ( (opt = getopt( argc, argv, "c" )) != -1 ) { -+ while ( (opt = getopt( argc, argv, "cA" )) != -1 ) { - switch ( opt ) { -+ case 'A': -+ forward_agent = true; -+ break; - case 'c': - print_colorcount(); - exit( 0 ); -@@ -170,7 +175,7 @@ int main( int argc, char *argv[] ) - set_native_locale(); - - try { -- STMClient client( ip, port, key, predict_mode ); -+ STMClient client( ip, port, key, predict_mode, forward_agent ); - client.init(); - - try { -diff --git a/src/frontend/mosh-server.cc b/src/frontend/mosh-server.cc -index ae2505f..0c54547 100644 ---- a/src/frontend/mosh-server.cc -+++ b/src/frontend/mosh-server.cc -@@ -80,6 +80,7 @@ - #include "locale_utils.h" - #include "pty_compat.h" - #include "select.h" -+#include "agent.h" - #include "timestamp.h" - #include "fatal_assert.h" - -@@ -93,11 +94,13 @@ typedef Network::Transport< Terminal::Complete, Network::UserStream > ServerConn - - void serve( int host_fd, - Terminal::Complete &terminal, -- ServerConnection &network ); -+ ServerConnection &network, -+ Agent::ProxyAgent &agent ); - - int run_server( const char *desired_ip, const char *desired_port, - const string &command_path, char *command_argv[], -- const int colors, bool verbose, bool with_motd ); -+ const int colors, bool verbose, bool with_motd, -+ bool with_agent_fwd ); - - using namespace std; - -@@ -166,6 +169,7 @@ int main( int argc, char *argv[] ) - string command_path; - char **command_argv = NULL; - int colors = 0; -+ bool with_agent_fwd = false; - bool verbose = false; /* don't close stdin/stdout/stderr */ - /* Will cause mosh-server not to correctly detach on old versions of sshd. */ - list<string> locale_vars; -@@ -186,8 +190,11 @@ int main( int argc, char *argv[] ) - && (strcmp( argv[ 1 ], "new" ) == 0) ) { - /* new option syntax */ - int opt; -- while ( (opt = getopt( argc - 1, argv + 1, "i:p:c:svl:" )) != -1 ) { -+ while ( (opt = getopt( argc - 1, argv + 1, "i:p:c:svl:A" )) != -1 ) { - switch ( opt ) { -+ case 'A': -+ with_agent_fwd = true; -+ break; - case 'i': - desired_ip = optarg; - break; -@@ -316,7 +323,7 @@ int main( int argc, char *argv[] ) - } - - try { -- return run_server( desired_ip, desired_port, command_path, command_argv, colors, verbose, with_motd ); -+ return run_server( desired_ip, desired_port, command_path, command_argv, colors, verbose, with_motd, with_agent_fwd ); - } catch ( const Network::NetworkException& e ) { - fprintf( stderr, "Network exception: %s: %s\n", - e.function.c_str(), strerror( e.the_errno ) ); -@@ -330,7 +337,8 @@ int main( int argc, char *argv[] ) - - int run_server( const char *desired_ip, const char *desired_port, - const string &command_path, char *command_argv[], -- const int colors, bool verbose, bool with_motd ) { -+ const int colors, bool verbose, bool with_motd, -+ bool with_agent_fwd ) { - /* get initial window size */ - struct winsize window_size; - if ( ioctl( STDIN_FILENO, TIOCGWINSZ, &window_size ) < 0 ) { -@@ -383,6 +391,13 @@ int run_server( const char *desired_ip, const char *desired_port, - - fprintf( stderr, "[mosh-server detached, pid = %d]\n", (int)getpid() ); - -+ /* initialize agent listener if requested */ -+ Agent::ProxyAgent agent( true, ! with_agent_fwd ); -+ if ( with_agent_fwd && (! agent.active()) ) { -+ fprintf( stderr, "Warning: Agent listener initialization failed. Disabling agent forwarding.\n" ); -+ with_agent_fwd = false; -+ } -+ - int master; - - #ifdef HAVE_IUTF8 -@@ -453,6 +468,14 @@ int run_server( const char *desired_ip, const char *desired_port, - exit( 1 ); - } - -+ /* set SSH_AUTH_SOCK */ -+ if ( agent.active() ) { -+ if ( setenv( "SSH_AUTH_SOCK", agent.listener_path().c_str(), true ) < 0 ) { -+ perror( "setenv" ); -+ exit( 1 ); -+ } -+ } -+ - /* ask ncurses to send UTF-8 instead of ISO 2022 for line-drawing chars */ - if ( setenv( "NCURSES_NO_UTF8_ACS", "1", true ) < 0 ) { - perror( "setenv" ); -@@ -487,7 +510,7 @@ int run_server( const char *desired_ip, const char *desired_port, - #endif - - try { -- serve( master, terminal, *network ); -+ serve( master, terminal, *network, agent ); - } catch ( const Network::NetworkException& e ) { - fprintf( stderr, "Network exception: %s: %s\n", - e.function.c_str(), strerror( e.the_errno ) ); -@@ -513,7 +536,7 @@ int run_server( const char *desired_ip, const char *desired_port, - return 0; - } - --void serve( int host_fd, Terminal::Complete &terminal, ServerConnection &network ) -+void serve( int host_fd, Terminal::Complete &terminal, ServerConnection &network, Agent::ProxyAgent &agent ) - { - /* prepare to poll for events */ - Select &sel = Select::get_instance(); -@@ -529,6 +552,10 @@ void serve( int host_fd, Terminal::Complete &terminal, ServerConnection &network - saved_addr.s_addr = 0; - #endif - -+ if ( agent.active() ) { -+ agent.attach_oob( network.oob() ); -+ } -+ - while ( 1 ) { - try { - uint64_t now = Network::timestamp(); -@@ -550,6 +577,10 @@ void serve( int host_fd, Terminal::Complete &terminal, ServerConnection &network - sel.add_fd( host_fd ); - } - -+ if ( agent.active() ) { -+ agent.pre_poll(); -+ } -+ - int active_fds = sel.select( timeout ); - if ( active_fds < 0 ) { - perror( "select" ); -@@ -635,6 +666,7 @@ void serve( int host_fd, Terminal::Complete &terminal, ServerConnection &network - /* If the pty slave is closed, reading from the master can fail with - EIO (see #264). So we treat errors on read() like EOF. */ - if ( bytes_read <= 0 ) { -+ agent.shutdown_server(); - network.start_shutdown(); - } else { - string terminal_to_host = terminal.act( string( buf, bytes_read ) ); -@@ -652,6 +684,7 @@ void serve( int host_fd, Terminal::Complete &terminal, ServerConnection &network - if ( sel.any_signal() ) { - /* shutdown signal */ - if ( network.has_remote_addr() && (!network.shutdown_in_progress()) ) { -+ agent.shutdown_server(); - network.start_shutdown(); - } else { - break; -@@ -665,6 +698,7 @@ void serve( int host_fd, Terminal::Complete &terminal, ServerConnection &network - - if ( (!network.shutdown_in_progress()) && sel.error( host_fd ) ) { - /* host problem */ -+ agent.shutdown_server(); - network.start_shutdown(); - } - -@@ -709,15 +743,29 @@ void serve( int host_fd, Terminal::Complete &terminal, ServerConnection &network - && time_since_remote_state >= uint64_t( timeout_if_no_client ) ) { - fprintf( stderr, "No connection within %d seconds.\n", - timeout_if_no_client / 1000 ); -+ agent.shutdown_server(); - break; - } - -+ if ( agent.active() ) { -+ if ( time_since_remote_state > (AGENT_IDLE_TIMEOUT * 1000) || time_since_remote_state > 30000 ) { -+ agent.close_sessions(); -+ } -+ agent.post_poll(); -+ } -+ - network.tick(); -+ -+ if ( agent.active() ) { -+ agent.post_tick(); -+ } -+ - } catch ( const Network::NetworkException& e ) { - fprintf( stderr, "%s: %s\n", e.function.c_str(), strerror( e.the_errno ) ); - spin(); - } catch ( const Crypto::CryptoException& e ) { - if ( e.fatal ) { -+ agent.shutdown_server(); - throw; - } else { - fprintf( stderr, "Crypto exception: %s\n", e.text.c_str() ); -diff --git a/src/frontend/stmclient.cc b/src/frontend/stmclient.cc -index 7d68ab1..791300c 100644 ---- a/src/frontend/stmclient.cc -+++ b/src/frontend/stmclient.cc -@@ -59,6 +59,7 @@ - #include "pty_compat.h" - #include "select.h" - #include "timestamp.h" -+#include "agent.h" - - #include "networktransport.cc" - -@@ -346,6 +347,11 @@ void STMClient::main( void ) - /* initialize signal handling and structures */ - main_init(); - -+ Agent::ProxyAgent agent( false, ! forward_agent ); -+ if ( agent.active() ) { -+ agent.attach_oob( network->oob() ); -+ } -+ - /* prepare to poll for events */ - Select &sel = Select::get_instance(); - -@@ -371,6 +377,10 @@ void STMClient::main( void ) - } - sel.add_fd( STDIN_FILENO ); - -+ if ( agent.active() ) { -+ agent.pre_poll(); -+ } -+ - int active_fds = sel.select( wait_time ); - if ( active_fds < 0 ) { - perror( "select" ); -@@ -390,6 +400,7 @@ void STMClient::main( void ) - - if ( sel.error( *it ) ) { - /* network problem */ -+ agent.shutdown_server(); - break; - } - } -@@ -403,9 +414,12 @@ void STMClient::main( void ) - if ( !process_user_input( STDIN_FILENO ) ) { - if ( !network->has_remote_addr() ) { - break; -- } else if ( !network->shutdown_in_progress() ) { -- overlays.get_notification_engine().set_notification_string( wstring( L"Exiting..." ), true ); -- network->start_shutdown(); -+ } else { -+ agent.shutdown_server(); -+ if ( !network->shutdown_in_progress() ) { -+ overlays.get_notification_engine().set_notification_string( wstring( L"Exiting..." ), true ); -+ network->start_shutdown(); -+ } - } - } - } -@@ -428,6 +442,7 @@ void STMClient::main( void ) - break; - } else if ( !network->shutdown_in_progress() ) { - overlays.get_notification_engine().set_notification_string( wstring( L"Signal received, shutting down..." ), true ); -+ agent.shutdown_server(); - network->start_shutdown(); - } - } -@@ -438,6 +453,7 @@ void STMClient::main( void ) - break; - } else if ( !network->shutdown_in_progress() ) { - overlays.get_notification_engine().set_notification_string( wstring( L"Exiting..." ), true ); -+ agent.shutdown_server(); - network->start_shutdown(); - } - } -@@ -466,6 +482,7 @@ void STMClient::main( void ) - if ( timestamp() - network->get_latest_remote_state().timestamp > 15000 ) { - if ( !network->shutdown_in_progress() ) { - overlays.get_notification_engine().set_notification_string( wstring( L"Timed out waiting for server..." ), true ); -+ agent.shutdown_server(); - network->start_shutdown(); - } - } else { -@@ -477,8 +494,16 @@ void STMClient::main( void ) - overlays.get_notification_engine().set_notification_string( L"" ); - } - -+ if ( agent.active() ) { -+ agent.post_poll(); -+ } -+ - network->tick(); - -+ if ( agent.active() ) { -+ agent.post_tick(); -+ } -+ - const Network::NetworkException *exn = network->get_send_exception(); - if ( exn ) { - overlays.get_notification_engine().set_network_exception( *exn ); -diff --git a/src/frontend/stmclient.h b/src/frontend/stmclient.h -index 51150c6..6946243 100644 ---- a/src/frontend/stmclient.h -+++ b/src/frontend/stmclient.h -@@ -47,6 +47,7 @@ class STMClient { - std::string ip; - int port; - std::string key; -+ bool forward_agent; - - struct termios saved_termios, raw_termios; - -@@ -77,8 +78,8 @@ 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 ) -- : ip( s_ip ), port( s_port ), key( s_key ), -+ STMClient( const char *s_ip, int s_port, const char *s_key, const char *predict_mode, bool s_forward_agent ) -+ : ip( s_ip ), port( s_port ), key( s_key ), forward_agent( s_forward_agent ), - saved_termios(), raw_termios(), - window_size(), - local_framebuffer( NULL ), -diff --git a/src/network/Makefile.am b/src/network/Makefile.am -index 3143cc4..fe8e3ed 100644 ---- a/src/network/Makefile.am -+++ b/src/network/Makefile.am -@@ -3,4 +3,4 @@ AM_CXXFLAGS = $(WARNING_CXXFLAGS) $(PICKY_CXXFLAGS) $(HARDEN_CFLAGS) $(MISC_CXXF - - noinst_LIBRARIES = libmoshnetwork.a - --libmoshnetwork_a_SOURCES = network.cc network.h networktransport.cc networktransport.h transportfragment.cc transportfragment.h transportsender.cc transportsender.h transportstate.h compressor.cc compressor.h -+libmoshnetwork_a_SOURCES = network.cc network.h networktransport.cc networktransport.h transportfragment.cc transportfragment.h transportsender.cc transportsender.h transportstate.h compressor.cc compressor.h outofband.h outofband.cc -diff --git a/src/network/networktransport.cc b/src/network/networktransport.cc -index 127e80c..6b62e2d 100644 ---- a/src/network/networktransport.cc -+++ b/src/network/networktransport.cc -@@ -130,6 +130,11 @@ void Transport<MyState, RemoteState>::recv( void ) - } - } - -+ /* Deliver out of band data */ -+ if (inst.has_oob()) { -+ oob()->input(inst.oob()); -+ } -+ - /* apply diff to reference state */ - TimestampedState<RemoteState> new_state = *reference_state; - new_state.timestamp = timestamp(); -diff --git a/src/network/networktransport.h b/src/network/networktransport.h -index c23d0bd..aca5d38 100644 ---- a/src/network/networktransport.h -+++ b/src/network/networktransport.h -@@ -83,6 +83,9 @@ namespace Network { - /* Find diff between last receiver state and current remote state, then rationalize states. */ - string get_remote_diff( void ); - -+ /* Get refenrece to out of band control object */ -+ OutOfBand *oob( void ) { return sender.oob(); } -+ - /* Shut down other side of connection. */ - /* Illegal to change current_state after this. */ - void start_shutdown( void ) { sender.start_shutdown(); } -diff --git a/src/network/outofband.cc b/src/network/outofband.cc -new file mode 100644 -index 0000000..41511ef ---- /dev/null -+++ b/src/network/outofband.cc -@@ -0,0 +1,265 @@ -+/* -+ Mosh: the mobile shell -+ Copyright 2012 Keith Winstein -+ -+ Out of band protocol extension for Mosh -+ Copyright 2013 Timo J. Rinne -+ -+ This program is free software: you can redistribute it and/or modify -+ it under the terms of the GNU General Public License as published by -+ the Free Software Foundation, either version 3 of the License, or -+ (at your option) any later version. -+ -+ This program is distributed in the hope that it will be useful, -+ but WITHOUT ANY WARRANTY; without even the implied warranty of -+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ GNU General Public License for more details. -+ -+ You should have received a copy of the GNU General Public License -+ along with this program. If not, see <http://www.gnu.org/licenses/>. -+ -+ In addition, as a special exception, the copyright holders give -+ permission to link the code of portions of this program with the -+ OpenSSL library under certain conditions as described in each -+ individual source file, and distribute linked combinations including -+ the two. -+ -+ You must obey the GNU General Public License in all respects for all -+ of the code used other than OpenSSL. If you modify file(s) with this -+ exception, you may extend this exception to your version of the -+ file(s), but you are not obligated to do so. If you do not wish to do -+ so, delete this exception statement from your version. If you delete -+ this exception statement from all source files in the program, then -+ also delete it here. -+*/ -+ -+#include <list> -+#include <stdio.h> -+#include <stdlib.h> -+#include <time.h> -+ -+#include "fatal_assert.h" -+ -+#include "outofband.h" -+#include "oob.pb.h" -+ -+#include <limits.h> -+ -+using namespace Network; -+using namespace OutOfBandBuffers; -+using namespace std; -+ -+ -+OutOfBand::OutOfBand() { -+ seq_num_out = 0; -+ ack_num_out = 0; -+} -+ -+OutOfBandCommunicator *OutOfBand::init(string name, OutOfBandMode mode) { -+ map < string, OutOfBandCommunicator * >::iterator i = comms.find(name); -+ if (i != comms.end()) { -+ return NULL; -+ } -+ OutOfBandCommunicator *comm = new OutOfBandCommunicator(mode, name, this); -+ comms[name] = comm; -+ return comm; -+} -+ -+void OutOfBand::uninit(string name) { -+ map < string, OutOfBandCommunicator * >::iterator i = comms.find(name); -+ if (i == comms.end()) { -+ return; -+ } -+ OutOfBandCommunicator *comm = i->second; -+ comms.erase(i); -+ delete comm; -+} -+ -+void OutOfBand::uninit(OutOfBandCommunicator *comm) { -+ uninit(comm->name); -+} -+ -+void OutOfBand::uninit(void) { -+ map < string, OutOfBandCommunicator * >::iterator i; -+ while ((i = comms.begin()) != comms.end()) { -+ OutOfBandCommunicator *comm = i->second; -+ comms.erase(i); -+ delete comm; -+ } -+} -+ -+void OutOfBand::input(string data) { -+ Instruction inst; -+ fatal_assert( inst.ParseFromString(data) ); -+ if (inst.has_ack_num()) { -+ uint64_t ack_num = inst.ack_num(); -+ if (ack_num != 0) { -+ list < OutOfBandBuffers::Instruction >::iterator i = reliable_instruction_out_sent.begin(); -+ while (i != reliable_instruction_out_sent.end()) { -+ fatal_assert((*i).has_seq_num()); -+ if ((*i).seq_num() <= ack_num) { -+ i = reliable_instruction_out_sent.erase(i); -+ continue; -+ } -+ break; -+ } -+ } -+ } -+ -+ bool ack = false; -+ -+ if (inst.has_payload_type() && inst.has_payload_data()) { -+ string payload_type = inst.payload_type(); -+ string payload_data = inst.payload_data(); -+ uint64_t seq_num = inst.has_seq_num() ? inst.seq_num() : 0; -+ uint64_t oob_mode = inst.has_oob_mode() ? inst.oob_mode() : 0; -+ OutOfBandCommunicator *comm = NULL; -+ map < string, OutOfBandCommunicator * >::iterator i = comms.find(payload_type); -+ if (i != comms.end()) { -+ comm = i->second; -+ fatal_assert(oob_mode == (uint64_t)comm->mode); -+ } -+ if (seq_num == 0) { -+ fatal_assert(oob_mode == (uint64_t)OOB_MODE_DATAGRAM); -+ if (comm != NULL) { -+ comm->datagram_queue.push(payload_data); -+ } -+ } else { -+ fatal_assert(oob_mode == (uint64_t)OOB_MODE_STREAM || oob_mode == (uint64_t)OOB_MODE_RELIABLE_DATAGRAM); -+ if (seq_num == next_seq_num(ack_num_out)) { -+ if (comm != NULL) { -+ switch (comm->mode) { -+ case OOB_MODE_STREAM: -+ comm->stream_buf += payload_data; -+ break; -+ case OOB_MODE_RELIABLE_DATAGRAM: -+ comm->datagram_queue.push(payload_data); -+ break; -+ default: -+ //NOTREACHED -+ fatal_assert(comm->mode == OOB_MODE_STREAM || comm->mode == OOB_MODE_RELIABLE_DATAGRAM); -+ } -+ } -+ ack_num_out = seq_num; -+ } -+ ack = true; -+ } -+ } -+ -+ if (ack && (! has_unsent_output())) { -+ Instruction inst; -+ datagram_instruction_out.push(inst); -+ } -+} -+ -+bool OutOfBand::has_output(void) { -+ return (! (datagram_instruction_out.empty() && reliable_instruction_out_sent.empty() && reliable_instruction_out_unsent.empty())); -+} -+ -+bool OutOfBand::has_unsent_output(void) { -+ return (! (datagram_instruction_out.empty() && reliable_instruction_out_unsent.empty())); -+} -+ -+string OutOfBand::output(void) { -+ string rv(""); -+ if (! datagram_instruction_out.empty()) { -+ Instruction inst = datagram_instruction_out.front(); -+ if (ack_num_out != 0) { -+ inst.set_ack_num(ack_num_out); -+ } -+ fatal_assert(inst.SerializeToString(&rv)); -+ datagram_instruction_out.pop(); -+ return rv; -+ } -+ if (! reliable_instruction_out_sent.empty()) { -+ Instruction inst = reliable_instruction_out_sent.front(); -+ if (ack_num_out != 0) { -+ inst.set_ack_num(ack_num_out); -+ } -+ fatal_assert(inst.SerializeToString(&rv)); -+ return rv; -+ } -+ if (! reliable_instruction_out_unsent.empty()) { -+ Instruction inst = reliable_instruction_out_unsent.front(); -+ reliable_instruction_out_sent.push_back(inst); -+ reliable_instruction_out_unsent.pop_front(); -+ if (ack_num_out != 0) { -+ inst.set_ack_num(ack_num_out); -+ } -+ fatal_assert(inst.SerializeToString(&rv)); -+ return rv; -+ } -+ return ""; -+} -+ -+OutOfBandCommunicator::OutOfBandCommunicator(OutOfBandMode oob_mode, string oob_name, OutOfBand *oob_ctl) { -+ mode = oob_mode; -+ name = oob_name; -+ oob_ctl_ptr = oob_ctl; -+ stream_buf = ""; -+} -+ -+void OutOfBandCommunicator::send(string data) { -+ Instruction inst; -+ if (oob()->ack_num_out != 0) { -+ inst.set_ack_num(oob()->ack_num_out); -+ } -+ inst.set_payload_type(name); -+ inst.set_payload_data(data); -+ inst.set_oob_mode((uint64_t)mode); -+ switch (mode) { -+ case OOB_MODE_STREAM: -+ case OOB_MODE_RELIABLE_DATAGRAM: -+ inst.set_seq_num(oob()->increment_seq_num_out()); -+ oob()->reliable_instruction_out_unsent.push_back(inst); -+ break; -+ //FALLTHROUGH -+ case OOB_MODE_DATAGRAM: -+ oob()->datagram_instruction_out.push(inst); -+ } -+} -+ -+bool OutOfBandCommunicator::readable(void) { -+ switch (mode) { -+ case OOB_MODE_STREAM: -+ return (! stream_buf.empty()); -+ case OOB_MODE_DATAGRAM: -+ case OOB_MODE_RELIABLE_DATAGRAM: -+ return (! datagram_queue.empty()); -+ } -+ //NOTREACHED -+ return false; -+} -+ -+string OutOfBandCommunicator::recv(void) { -+ string rv(""); -+ switch (mode) { -+ case OOB_MODE_STREAM: -+ if (stream_buf.empty()) { -+ return rv; -+ } -+ rv = stream_buf; -+ stream_buf = ""; -+ return rv; -+ case OOB_MODE_RELIABLE_DATAGRAM: -+ case OOB_MODE_DATAGRAM: -+ if (datagram_queue.empty()) { -+ return rv; -+ } -+ rv = datagram_queue.front(); -+ datagram_queue.pop(); -+ return rv; -+ } -+ //NOTREACHED -+ return ""; -+} -+ -+string OutOfBandCommunicator::read(size_t len) { -+ fatal_assert(mode == OOB_MODE_STREAM); -+ if (stream_buf.length() < len) { -+ return ""; -+ } -+ string rv = stream_buf.substr(0, len); -+ stream_buf = stream_buf.substr(len); -+ return rv; -+} -diff --git a/src/network/outofband.h b/src/network/outofband.h -new file mode 100644 -index 0000000..1c95ed5 ---- /dev/null -+++ b/src/network/outofband.h -@@ -0,0 +1,107 @@ -+/* -+ Mosh: the mobile shell -+ Copyright 2012 Keith Winstein -+ -+ Out of band protocol extension for Mosh -+ Copyright 2013 Timo J. Rinne -+ -+ This program is free software: you can redistribute it and/or modify -+ it under the terms of the GNU General Public License as published by -+ the Free Software Foundation, either version 3 of the License, or -+ (at your option) any later version. -+ -+ This program is distributed in the hope that it will be useful, -+ but WITHOUT ANY WARRANTY; without even the implied warranty of -+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ GNU General Public License for more details. -+ -+ You should have received a copy of the GNU General Public License -+ along with this program. If not, see <http://www.gnu.org/licenses/>. -+ -+ In addition, as a special exception, the copyright holders give -+ permission to link the code of portions of this program with the -+ OpenSSL library under certain conditions as described in each -+ individual source file, and distribute linked combinations including -+ the two. -+ -+ You must obey the GNU General Public License in all respects for all -+ of the code used other than OpenSSL. If you modify file(s) with this -+ exception, you may extend this exception to your version of the -+ file(s), but you are not obligated to do so. If you do not wish to do -+ so, delete this exception statement from your version. If you delete -+ this exception statement from all source files in the program, then -+ also delete it here. -+*/ -+ -+ -+#ifndef OUT_OF_BAND_HPP -+#define OUT_OF_BAND_HPP -+ -+#include <string> -+#include <queue> -+#include <list> -+#include <map> -+ -+#include "oob.pb.h" -+ -+using std::string; -+using std::queue; -+using std::list; -+using std::map; -+ -+namespace Network { -+ -+ enum OutOfBandMode { OOB_MODE_STREAM = 1, OOB_MODE_DATAGRAM = 2, OOB_MODE_RELIABLE_DATAGRAM = 3 }; -+ -+ class OutOfBand; -+ -+ class OutOfBandCommunicator -+ { -+ private: -+ OutOfBandMode mode; -+ string name; -+ string stream_buf; -+ queue < string > datagram_queue; -+ OutOfBand *oob_ctl_ptr; -+ OutOfBand *oob(void) { return oob_ctl_ptr; } -+ OutOfBandCommunicator(OutOfBandMode oob_mode, string oob_name, OutOfBand *oob_ctl); -+ -+ public: -+ void send(string data); -+ bool readable(void); -+ string recv(void); -+ string read(size_t len); -+ -+ friend class OutOfBand; -+ }; -+ -+ class OutOfBand -+ { -+ private: -+ map < string, OutOfBandCommunicator * > comms; -+ queue < OutOfBandBuffers::Instruction > datagram_instruction_out; -+ list < OutOfBandBuffers::Instruction > reliable_instruction_out_sent; -+ list < OutOfBandBuffers::Instruction > reliable_instruction_out_unsent; -+ uint64_t seq_num_out; -+ uint64_t ack_num_out; -+ uint64_t next_seq_num(uint64_t sn) { sn++; if (sn == 0) sn++; return sn; } -+ uint64_t increment_seq_num_out(void) { seq_num_out = next_seq_num(seq_num_out); return seq_num_out; } -+ -+ public: -+ OutOfBand(); -+ ~OutOfBand() { uninit(); } -+ OutOfBandCommunicator *init(string name, OutOfBandMode mode); -+ void uninit(string name); -+ void uninit(OutOfBandCommunicator *comm); -+ void uninit(void); -+ bool has_output(void); -+ bool has_unsent_output(void); -+ // input and output are to be called from transport code only -+ void input(string data); -+ string output(void); -+ -+ friend class OutOfBandCommunicator; -+ }; -+} -+ -+#endif -diff --git a/src/network/transportsender.cc b/src/network/transportsender.cc -index e641655..816b76d 100644 ---- a/src/network/transportsender.cc -+++ b/src/network/transportsender.cc -@@ -96,14 +96,18 @@ void TransportSender<MyState>::calculate_timers( void ) - next_ack_time = now + ACK_DELAY; - } - -- if ( !(current_state == sent_states.back().state) ) { -+ if ( oob()->has_unsent_output() ) { -+ next_send_time = sent_states.back().timestamp + send_interval(); -+ if ( mindelay_clock != uint64_t( -1 ) ) { -+ next_send_time = max( next_send_time, mindelay_clock + SEND_MINDELAY ); -+ } -+ } else if ( !(current_state == sent_states.back().state) ) { - if ( mindelay_clock == uint64_t( -1 ) ) { - mindelay_clock = now; - } -- - next_send_time = max( mindelay_clock + SEND_MINDELAY, - sent_states.back().timestamp + send_interval() ); -- } else if ( !(current_state == assumed_receiver_state->state) -+ } else if ( ((!(current_state == assumed_receiver_state->state)) || (oob()->has_output())) - && (last_heard + ACTIVE_RETRY_TIMEOUT > now) ) { - next_send_time = sent_states.back().timestamp + send_interval(); - if ( mindelay_clock != uint64_t( -1 ) ) { -@@ -181,11 +185,12 @@ void TransportSender<MyState>::tick( void ) - if ( diff.empty() && (now >= next_ack_time) ) { - send_empty_ack(); - mindelay_clock = uint64_t( -1 ); -- } else if ( !diff.empty() && ( (now >= next_send_time) -- || (now >= next_ack_time) ) ) { -+ } else if ( !diff.empty() && ((now >= next_send_time) || (now >= next_ack_time)) ) { - /* Send diffs or ack */ - send_to_receiver( diff ); - mindelay_clock = uint64_t( -1 ); -+ } else if ( oob()->has_output() && ((now >= next_send_time) || (now >= next_ack_time)) ) { -+ send_empty_ack(); - } - } - -@@ -194,7 +199,7 @@ void TransportSender<MyState>::send_empty_ack( void ) - { - uint64_t now = timestamp(); - -- assert( now >= next_ack_time ); -+ assert( now >= next_ack_time || oob()->has_output() ); - - uint64_t new_num = sent_states.back().num + 1; - -@@ -316,6 +321,10 @@ void TransportSender<MyState>::send_in_fragments( string diff, uint64_t new_num - inst.set_diff( diff ); - inst.set_chaff( make_chaff() ); - -+ if (oob()->has_output()) { -+ inst.set_oob(oob()->output()); -+ } -+ - if ( new_num == uint64_t(-1) ) { - shutdown_tries++; - } -diff --git a/src/network/transportsender.h b/src/network/transportsender.h -index 572c47f..3d4285b 100644 ---- a/src/network/transportsender.h -+++ b/src/network/transportsender.h -@@ -42,6 +42,7 @@ - #include "transportstate.h" - #include "transportfragment.h" - #include "prng.h" -+#include "outofband.h" - - using std::list; - using std::pair; -@@ -104,6 +105,8 @@ namespace Network { - - uint64_t last_heard; /* last time received new state */ - -+ OutOfBand oob_ctl; /* out of band protocol object */ -+ - /* chaff to disguise instruction length */ - PRNG prng; - const string make_chaff( void ); -@@ -133,7 +136,10 @@ namespace Network { - void remote_heard( uint64_t ts ) { last_heard = ts; } - - /* Starts shutdown sequence */ -- void start_shutdown( void ) { if ( !shutdown_in_progress ) { shutdown_start = timestamp(); shutdown_in_progress = true; } } -+ void start_shutdown( void ) { if ( !shutdown_in_progress ) { oob_ctl.uninit(); shutdown_start = timestamp(); shutdown_in_progress = true; } } -+ -+ /* Get refenrece to out of band control object */ -+ OutOfBand *oob( void ) { return &oob_ctl; } - - /* Misc. getters and setters */ - /* Cannot modify current_state while shutdown in progress */ -diff --git a/src/protobufs/Makefile.am b/src/protobufs/Makefile.am -index 131ec4e..419525a 100644 ---- a/src/protobufs/Makefile.am -+++ b/src/protobufs/Makefile.am -@@ -1,4 +1,4 @@ --source = userinput.proto hostinput.proto transportinstruction.proto -+source = userinput.proto hostinput.proto transportinstruction.proto oob.proto agent.proto - - AM_CPPFLAGS = $(protobuf_CFLAGS) - AM_CXXFLAGS = $(WARNING_CXXFLAGS) $(HARDEN_CFLAGS) $(MISC_CXXFLAGS) -diff --git a/src/protobufs/agent.proto b/src/protobufs/agent.proto -new file mode 100644 -index 0000000..b350331 ---- /dev/null -+++ b/src/protobufs/agent.proto -@@ -0,0 +1,8 @@ -+option optimize_for = LITE_RUNTIME; -+ -+package AgentBuffers; -+ -+message Instruction { -+ required uint64 agent_id = 1; -+ optional bytes agent_data = 2; -+} -diff --git a/src/protobufs/oob.proto b/src/protobufs/oob.proto -new file mode 100644 -index 0000000..561ca31 ---- /dev/null -+++ b/src/protobufs/oob.proto -@@ -0,0 +1,11 @@ -+option optimize_for = LITE_RUNTIME; -+ -+package OutOfBandBuffers; -+ -+message Instruction { -+ optional uint64 seq_num = 1; -+ optional uint64 ack_num = 2; -+ optional uint64 oob_mode = 3; -+ optional bytes payload_type = 4; -+ optional bytes payload_data = 5; -+} -diff --git a/src/protobufs/transportinstruction.proto b/src/protobufs/transportinstruction.proto -index d4a76f7..bb22a8c 100644 ---- a/src/protobufs/transportinstruction.proto -+++ b/src/protobufs/transportinstruction.proto -@@ -13,4 +13,6 @@ message Instruction { - optional bytes diff = 6; - - optional bytes chaff = 7; -+ -+ optional bytes oob = 8; - } -diff --git a/src/util/Makefile.am b/src/util/Makefile.am -index 25dc3dd..ea41058 100644 ---- a/src/util/Makefile.am -+++ b/src/util/Makefile.am -@@ -2,4 +2,4 @@ AM_CXXFLAGS = $(WARNING_CXXFLAGS) $(PICKY_CXXFLAGS) $(HARDEN_CFLAGS) $(MISC_CXXF - - noinst_LIBRARIES = libmoshutil.a - --libmoshutil_a_SOURCES = locale_utils.cc locale_utils.h swrite.cc swrite.h dos_assert.h fatal_assert.h select.h select.cc timestamp.h timestamp.cc pty_compat.cc pty_compat.h -+libmoshutil_a_SOURCES = locale_utils.cc locale_utils.h swrite.cc swrite.h dos_assert.h fatal_assert.h select.h select.cc timestamp.h timestamp.cc pty_compat.cc pty_compat.h -diff --git a/src/util/swrite.cc b/src/util/swrite.cc -index 64a772c..458477c 100644 ---- a/src/util/swrite.cc -+++ b/src/util/swrite.cc -@@ -30,11 +30,17 @@ - also delete it here. - */ - -+#include "config.h" -+ - #include <unistd.h> - #include <string.h> - #include <stdio.h> -+#include <errno.h> -+#include <time.h> - -+#include "timestamp.h" - #include "swrite.h" -+#include "fatal_assert.h" - - int swrite( int fd, const char *str, ssize_t len ) - { -@@ -53,3 +59,42 @@ int swrite( int fd, const char *str, ssize_t len ) - - return 0; - } -+ -+ -+int swrite_timeout( int fd, uint64_t timeout_ms, const char *str, ssize_t len ) -+{ -+ size_t total_bytes_written = 0; -+ size_t bytes_to_write = ( len >= 0 ) ? len : (ssize_t) strlen( str ); -+ uint64_t t0 = frozen_timestamp(); -+ uint64_t iteration = 0; -+ -+ while ( total_bytes_written < bytes_to_write ) { -+ iteration++; -+ ssize_t rv = write( fd, str + total_bytes_written, bytes_to_write - total_bytes_written); -+ if ( rv > 0 ) { -+ total_bytes_written += rv; -+ continue; -+ } else if ( rv < 0 && ( errno == EAGAIN || errno == EWOULDBLOCK ) ) { -+ uint64_t t1 = frozen_timestamp(); -+ fatal_assert( t1 >= t0 ); -+ uint64_t total_time_spent = t1 - t0; -+ if ( total_time_spent > timeout_ms ) { -+ perror( "write" ); -+ return -1; -+ } -+ uint64_t time_left = timeout_ms - total_time_spent; -+ uint64_t sleep_time = 999; -+ if ( time_left < sleep_time ) sleep_time = time_left; -+ if ( iteration * 50 < sleep_time ) sleep_time = iteration * 50; -+ fatal_assert( sleep_time > 0 ); -+ struct timespec req; -+ req.tv_sec = 0; -+ req.tv_nsec = sleep_time * 1000000; -+ nanosleep( &req, NULL ); -+ continue; -+ } else { -+ perror( "write" ); -+ } -+ } -+ return 0; -+} -diff --git a/src/util/swrite.h b/src/util/swrite.h -index e75bf7e..2f72782 100644 ---- a/src/util/swrite.h -+++ b/src/util/swrite.h -@@ -33,6 +33,9 @@ - #ifndef SWRITE_HPP - #define SWRITE_HPP - -+#include <stdint.h> -+ - int swrite( int fd, const char *str, ssize_t len = -1 ); -+int swrite_timeout( int fd, uint64_t timeout_ms, const char *str, ssize_t len = -1 ); - - #endif --- -1.9.1 - -From 42da0db244f79be4218fb7aa0edf801bf4d2e1f3 Mon Sep 17 00:00:00 2001 -From: "Timo J. Rinne" <tri@iki.fi> -Date: Tue, 14 May 2013 07:57:44 +0000 -Subject: [PATCH] Fixed broken friend declaration. - -Signed-off-by: Timo J. Rinne <tri@iki.fi> ---- - src/agent/agent.h | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/src/agent/agent.h b/src/agent/agent.h -index d8f98d4..835e29c 100644 ---- a/src/agent/agent.h -+++ b/src/agent/agent.h -@@ -72,7 +72,7 @@ namespace Agent { - ProxyAgent *agent_ptr; - - public: -- friend ProxyAgent; -+ friend class ProxyAgent; - }; - - class ProxyAgent { -@@ -102,7 +102,7 @@ namespace Agent { - void close_sessions( void ); - void shutdown_server( void ); - -- friend AgentConnection; -+ friend class AgentConnection; - }; - - } --- -1.9.1 - -From 3052df6e3cb41db932e7dd940ab39b83a14b4f29 Mon Sep 17 00:00:00 2001 -From: "Timo J. Rinne" <tri@iki.fi> -Date: Tue, 14 May 2013 12:18:51 +0000 -Subject: [PATCH] Added some generalization to handling oob objects in event - loop. Does not add any new functionality as such, but makes it easier to add - new stuff in future. - -Signed-off-by: Timo J. Rinne <tri@iki.fi> ---- - src/agent/agent.cc | 6 ++--- - src/agent/agent.h | 23 ++++++++++++-------- - src/frontend/mosh-server.cc | 30 ++++++++++--------------- - src/frontend/stmclient.cc | 27 +++++++++-------------- - src/network/outofband.cc | 53 ++++++++++++++++++++++++++++++++++++++++++--- - src/network/outofband.h | 29 +++++++++++++++++++++++-- - 6 files changed, 115 insertions(+), 53 deletions(-) - -diff --git a/src/agent/agent.cc b/src/agent/agent.cc -index 12cd31b..5316f05 100644 ---- a/src/agent/agent.cc -+++ b/src/agent/agent.cc -@@ -136,7 +136,7 @@ ProxyAgent::ProxyAgent( bool is_server, bool dummy ) { - - ProxyAgent::~ProxyAgent( void ) { - #ifdef SUPPORT_AGENT_FORWARDING -- shutdown_server(); -+ shutdown(); - #endif - } - -@@ -152,7 +152,7 @@ void ProxyAgent::close_sessions( void ) { - #endif - } - --void ProxyAgent::shutdown_server( void ) { -+void ProxyAgent::shutdown( void ) { - #ifdef SUPPORT_AGENT_FORWARDING - detach_oob(); - if (ok) { -@@ -174,7 +174,7 @@ void ProxyAgent::attach_oob(OutOfBand *oob_ctl) { - detach_oob(); - fatal_assert(oob_ctl != NULL); - oob_ctl_ptr = oob_ctl; -- comm = oob_ctl_ptr->init(AGENT_FORWARD_OOB_NAME, Network::OOB_MODE_RELIABLE_DATAGRAM); -+ comm = oob_ctl_ptr->init(AGENT_FORWARD_OOB_NAME, Network::OOB_MODE_RELIABLE_DATAGRAM, this); - fatal_assert(comm != NULL); - } - -diff --git a/src/agent/agent.h b/src/agent/agent.h -index 835e29c..64bbf8a 100644 ---- a/src/agent/agent.h -+++ b/src/agent/agent.h -@@ -75,11 +75,13 @@ namespace Agent { - friend class ProxyAgent; - }; - -- class ProxyAgent { -+ class ProxyAgent : public Network::OutOfBandPlugin -+ { - private: -+ void detach_oob(void); -+ Network::OutOfBandCommunicator *comm; - Network::OutOfBand *oob_ctl_ptr; - Network::OutOfBand *oob( void ) { return oob_ctl_ptr; } -- void detach_oob(void); - void notify_eof(uint64_t agent_id); - AgentConnection *get_session(); - bool server; -@@ -89,18 +91,21 @@ namespace Agent { - string l_path; - uint64_t cnt; - std::map< uint64_t, AgentConnection * > agent_sessions; -- Network::OutOfBandCommunicator *comm; -+ - public: -- ProxyAgent( bool is_server, bool dummy = false ); -- ~ProxyAgent( void ); -- void attach_oob(Network::OutOfBand *oob_ctl); -- bool active() { return ok && ((! server) || (l_sock >= 0)); } -- std::string listener_path( void ) { if ( ok && server && l_sock >= 0 ) return l_path; return ""; } -+ // Required by parent class -+ bool active( void ) { return ok && ((! server) || (l_sock >= 0)); } - void pre_poll( void ); - void post_poll( void ); - void post_tick( void ); - void close_sessions( void ); -- void shutdown_server( void ); -+ void shutdown( void ); -+ void attach_oob(Network::OutOfBand *oob_ctl); -+ -+ // Class specific stuff -+ ProxyAgent( bool is_server, bool dummy = false ); -+ ~ProxyAgent( void ); -+ std::string listener_path( void ) { if ( ok && server && l_sock >= 0 ) return l_path; return ""; } - - friend class AgentConnection; - }; -diff --git a/src/frontend/mosh-server.cc b/src/frontend/mosh-server.cc -index 0c54547..65c18f7 100644 ---- a/src/frontend/mosh-server.cc -+++ b/src/frontend/mosh-server.cc -@@ -552,9 +552,7 @@ void serve( int host_fd, Terminal::Complete &terminal, ServerConnection &network - saved_addr.s_addr = 0; - #endif - -- if ( agent.active() ) { -- agent.attach_oob( network.oob() ); -- } -+ agent.attach_oob(network.oob()); - - while ( 1 ) { - try { -@@ -577,9 +575,7 @@ void serve( int host_fd, Terminal::Complete &terminal, ServerConnection &network - sel.add_fd( host_fd ); - } - -- if ( agent.active() ) { -- agent.pre_poll(); -- } -+ network.oob()->pre_poll(); - - int active_fds = sel.select( timeout ); - if ( active_fds < 0 ) { -@@ -666,7 +662,7 @@ void serve( int host_fd, Terminal::Complete &terminal, ServerConnection &network - /* If the pty slave is closed, reading from the master can fail with - EIO (see #264). So we treat errors on read() like EOF. */ - if ( bytes_read <= 0 ) { -- agent.shutdown_server(); -+ network.oob()->shutdown(); - network.start_shutdown(); - } else { - string terminal_to_host = terminal.act( string( buf, bytes_read ) ); -@@ -684,7 +680,7 @@ void serve( int host_fd, Terminal::Complete &terminal, ServerConnection &network - if ( sel.any_signal() ) { - /* shutdown signal */ - if ( network.has_remote_addr() && (!network.shutdown_in_progress()) ) { -- agent.shutdown_server(); -+ network.oob()->shutdown(); - network.start_shutdown(); - } else { - break; -@@ -698,7 +694,7 @@ void serve( int host_fd, Terminal::Complete &terminal, ServerConnection &network - - if ( (!network.shutdown_in_progress()) && sel.error( host_fd ) ) { - /* host problem */ -- agent.shutdown_server(); -+ network.oob()->shutdown(); - network.start_shutdown(); - } - -@@ -743,29 +739,25 @@ void serve( int host_fd, Terminal::Complete &terminal, ServerConnection &network - && time_since_remote_state >= uint64_t( timeout_if_no_client ) ) { - fprintf( stderr, "No connection within %d seconds.\n", - timeout_if_no_client / 1000 ); -- agent.shutdown_server(); -+ network.oob()->shutdown(); - break; - } - -- if ( agent.active() ) { -- if ( time_since_remote_state > (AGENT_IDLE_TIMEOUT * 1000) || time_since_remote_state > 30000 ) { -- agent.close_sessions(); -- } -- agent.post_poll(); -+ if ( time_since_remote_state > (AGENT_IDLE_TIMEOUT * 1000) || time_since_remote_state > 30000 ) { -+ network.oob()->close_sessions(); - } -+ network.oob()->post_poll(); - - network.tick(); - -- if ( agent.active() ) { -- agent.post_tick(); -- } -+ network.oob()->post_tick(); - - } catch ( const Network::NetworkException& e ) { - fprintf( stderr, "%s: %s\n", e.function.c_str(), strerror( e.the_errno ) ); - spin(); - } catch ( const Crypto::CryptoException& e ) { - if ( e.fatal ) { -- agent.shutdown_server(); -+ network.oob()->shutdown(); - throw; - } else { - fprintf( stderr, "Crypto exception: %s\n", e.text.c_str() ); -diff --git a/src/frontend/stmclient.cc b/src/frontend/stmclient.cc -index 791300c..ceba763 100644 ---- a/src/frontend/stmclient.cc -+++ b/src/frontend/stmclient.cc -@@ -348,9 +348,8 @@ void STMClient::main( void ) - main_init(); - - Agent::ProxyAgent agent( false, ! forward_agent ); -- if ( agent.active() ) { -- agent.attach_oob( network->oob() ); -- } -+ -+ agent.attach_oob(network->oob()); - - /* prepare to poll for events */ - Select &sel = Select::get_instance(); -@@ -377,9 +376,7 @@ void STMClient::main( void ) - } - sel.add_fd( STDIN_FILENO ); - -- if ( agent.active() ) { -- agent.pre_poll(); -- } -+ network->oob()->pre_poll(); - - int active_fds = sel.select( wait_time ); - if ( active_fds < 0 ) { -@@ -400,7 +397,7 @@ void STMClient::main( void ) - - if ( sel.error( *it ) ) { - /* network problem */ -- agent.shutdown_server(); -+ network->oob()->shutdown(); - break; - } - } -@@ -415,7 +412,7 @@ void STMClient::main( void ) - if ( !network->has_remote_addr() ) { - break; - } else { -- agent.shutdown_server(); -+ network->oob()->shutdown(); - if ( !network->shutdown_in_progress() ) { - overlays.get_notification_engine().set_notification_string( wstring( L"Exiting..." ), true ); - network->start_shutdown(); -@@ -442,7 +439,7 @@ void STMClient::main( void ) - break; - } else if ( !network->shutdown_in_progress() ) { - overlays.get_notification_engine().set_notification_string( wstring( L"Signal received, shutting down..." ), true ); -- agent.shutdown_server(); -+ network->oob()->shutdown(); - network->start_shutdown(); - } - } -@@ -453,7 +450,7 @@ void STMClient::main( void ) - break; - } else if ( !network->shutdown_in_progress() ) { - overlays.get_notification_engine().set_notification_string( wstring( L"Exiting..." ), true ); -- agent.shutdown_server(); -+ network->oob()->shutdown(); - network->start_shutdown(); - } - } -@@ -482,7 +479,7 @@ void STMClient::main( void ) - if ( timestamp() - network->get_latest_remote_state().timestamp > 15000 ) { - if ( !network->shutdown_in_progress() ) { - overlays.get_notification_engine().set_notification_string( wstring( L"Timed out waiting for server..." ), true ); -- agent.shutdown_server(); -+ network->oob()->shutdown(); - network->start_shutdown(); - } - } else { -@@ -494,15 +491,11 @@ void STMClient::main( void ) - overlays.get_notification_engine().set_notification_string( L"" ); - } - -- if ( agent.active() ) { -- agent.post_poll(); -- } -+ network->oob()->post_poll(); - - network->tick(); - -- if ( agent.active() ) { -- agent.post_tick(); -- } -+ network->oob()->post_tick(); - - const Network::NetworkException *exn = network->get_send_exception(); - if ( exn ) { -diff --git a/src/network/outofband.cc b/src/network/outofband.cc -index 41511ef..d03a689 100644 ---- a/src/network/outofband.cc -+++ b/src/network/outofband.cc -@@ -55,16 +55,62 @@ OutOfBand::OutOfBand() { - ack_num_out = 0; - } - --OutOfBandCommunicator *OutOfBand::init(string name, OutOfBandMode mode) { -+OutOfBandCommunicator *OutOfBand::init(string name, OutOfBandMode mode, OutOfBandPlugin *plugin) { - map < string, OutOfBandCommunicator * >::iterator i = comms.find(name); - if (i != comms.end()) { - return NULL; - } -- OutOfBandCommunicator *comm = new OutOfBandCommunicator(mode, name, this); -+ OutOfBandCommunicator *comm = new OutOfBandCommunicator(mode, name, this, plugin); - comms[name] = comm; - return comm; - } - -+void OutOfBand::pre_poll( void ) { -+ map < string, OutOfBandCommunicator * >::iterator i = comms.begin(); -+ while (i != comms.end()) { -+ OutOfBandCommunicator *comm = (i++)->second; -+ if (comm->plugin_ptr->active()) { -+ comm->plugin_ptr->pre_poll(); -+ } -+ } -+} -+ -+void OutOfBand::post_poll( void ) { -+ map < string, OutOfBandCommunicator * >::iterator i = comms.begin(); -+ while (i != comms.end()) { -+ OutOfBandCommunicator *comm = (i++)->second; -+ if (comm->plugin_ptr->active()) { -+ comm->plugin_ptr->post_poll(); -+ } -+ } -+} -+ -+void OutOfBand::post_tick( void ) { -+ map < string, OutOfBandCommunicator * >::iterator i = comms.begin(); -+ while (i != comms.end()) { -+ OutOfBandCommunicator *comm = (i++)->second; -+ if (comm->plugin_ptr->active()) { -+ comm->plugin_ptr->post_tick(); -+ } -+ } -+} -+ -+void OutOfBand::close_sessions( void ) { -+ map < string, OutOfBandCommunicator * >::iterator i = comms.begin(); -+ while (i != comms.end()) { -+ OutOfBandCommunicator *comm = (i++)->second; -+ comm->plugin_ptr->close_sessions(); -+ } -+} -+ -+void OutOfBand::shutdown( void ) { -+ map < string, OutOfBandCommunicator * >::iterator i = comms.begin(); -+ while (i != comms.end()) { -+ OutOfBandCommunicator *comm = (i++)->second; -+ comm->plugin_ptr->shutdown(); -+ } -+} -+ - void OutOfBand::uninit(string name) { - map < string, OutOfBandCommunicator * >::iterator i = comms.find(name); - if (i == comms.end()) { -@@ -192,10 +238,11 @@ string OutOfBand::output(void) { - return ""; - } - --OutOfBandCommunicator::OutOfBandCommunicator(OutOfBandMode oob_mode, string oob_name, OutOfBand *oob_ctl) { -+OutOfBandCommunicator::OutOfBandCommunicator(OutOfBandMode oob_mode, string oob_name, OutOfBand *oob_ctl, OutOfBandPlugin *plugin) { - mode = oob_mode; - name = oob_name; - oob_ctl_ptr = oob_ctl; -+ plugin_ptr = plugin; - stream_buf = ""; - } - -diff --git a/src/network/outofband.h b/src/network/outofband.h -index 1c95ed5..e43b271 100644 ---- a/src/network/outofband.h -+++ b/src/network/outofband.h -@@ -54,6 +54,8 @@ namespace Network { - enum OutOfBandMode { OOB_MODE_STREAM = 1, OOB_MODE_DATAGRAM = 2, OOB_MODE_RELIABLE_DATAGRAM = 3 }; - - class OutOfBand; -+ class OutOfBandPlugin; -+ class OutOfBandCommunicator; - - class OutOfBandCommunicator - { -@@ -62,9 +64,10 @@ namespace Network { - string name; - string stream_buf; - queue < string > datagram_queue; -+ OutOfBandPlugin *plugin_ptr; - OutOfBand *oob_ctl_ptr; - OutOfBand *oob(void) { return oob_ctl_ptr; } -- OutOfBandCommunicator(OutOfBandMode oob_mode, string oob_name, OutOfBand *oob_ctl); -+ OutOfBandCommunicator(OutOfBandMode oob_mode, string oob_name, OutOfBand *oob_ctl, OutOfBandPlugin *plugin); - - public: - void send(string data); -@@ -90,7 +93,14 @@ namespace Network { - public: - OutOfBand(); - ~OutOfBand() { uninit(); } -- OutOfBandCommunicator *init(string name, OutOfBandMode mode); -+ -+ void pre_poll( void ); -+ void post_poll( void ); -+ void post_tick( void ); -+ void close_sessions( void ); -+ void shutdown( void ); -+ -+ OutOfBandCommunicator *init(string name, OutOfBandMode mode, OutOfBandPlugin *plugin); - void uninit(string name); - void uninit(OutOfBandCommunicator *comm); - void uninit(void); -@@ -102,6 +112,21 @@ namespace Network { - - friend class OutOfBandCommunicator; - }; -+ -+ class OutOfBandPlugin -+ { -+ public: -+ virtual bool active( void ) = 0; -+ virtual void pre_poll( void ) = 0; -+ virtual void post_poll( void ) = 0; -+ virtual void post_tick( void ) = 0; -+ virtual void close_sessions( void ) = 0; -+ virtual void shutdown( void ) = 0; -+ virtual void attach_oob(Network::OutOfBand *oob_ctl) = 0; -+ -+ friend class OutOfBand; -+ }; -+ - } - - #endif --- -1.9.1 - |