#
#
# add_dir "network"
#
# add_file "network/automate_listener.cc"
# content [e3cc9c6951bd37f909ca6d1000475595d3f5c545]
#
# add_file "network/automate_listener.hh"
# content [bf2a8f5463556c862f5d6dcb5e9ec542c827f8db]
#
# add_file "network/automate_session.cc"
# content [ebc7328238ef4bf127528a736a0ec9bf46efa340]
#
# add_file "network/automate_session.hh"
# content [c37e12bc01cccc8c1f91b1d7c0e51dcd54aff231]
#
# add_file "network/listener_base.cc"
# content [1cf9255975a5c99f20a755f7e8401ebeae553aed]
#
# add_file "network/listener_base.hh"
# content [db29dc7652f074e21b800e57a4a3c7ec79a5a24a]
#
# add_file "network/make_server.cc"
# content [7344c6d4b47bb6d975442542b9447ecd1582d7e1]
#
# add_file "network/make_server.hh"
# content [25b6e2faa53607d06e4b9b2e14ec1ab29c68b67e]
#
# add_file "network/netsync.txt"
# content [c8668e67fac1556012731568df6069dcc3576608]
#
# add_file "network/netsync_listener.cc"
# content [84a4bee07620fef712963e19322e623f3c083fe0]
#
# add_file "network/netsync_listener.hh"
# content [5974625ca14d0f63d7bf803bb908a4dd665b0d40]
#
# add_file "network/netsync_session.cc"
# content [6fadb167fe426ef9f30c87eabd0368277b21f100]
#
# add_file "network/netsync_session.hh"
# content [204b7a7956048dded4548927a1abd2a3642f6a72]
#
# add_file "network/reactable.cc"
# content [92f41ef3a46d350882dcaf8fe3d8d84b3cabd3c2]
#
# add_file "network/reactable.hh"
# content [88bc81d80ae4d3eeef01fb06e5bf7aaff9014955]
#
# add_file "network/reactor.cc"
# content [706efdfe2fec0191b90c598fbbc8c1d661bb98c0]
#
# add_file "network/reactor.hh"
# content [ca7a364e2a3113f4a51993741149588bd56b7d12]
#
# add_file "network/session_base.cc"
# content [157c8a8f35cea36433729d2876f0a4bfb4dd5937]
#
# add_file "network/session_base.hh"
# content [0ffdf1741c214d0144014df839a534dc06a4b23e]
#
# patch "Makefile.am"
# from [5f8f59e524bb434d34ff260e025310df86936981]
# to [c6a102401efb2ea9778faf7a908371f682f4ace1]
#
# patch "netsync.cc"
# from [0591fcf92046d9bfa90811f8525586db32de83c2]
# to [d3b06a6a452c1d8bfae75f4742b0b0ac0ef1ed6d]
#
============================================================
--- network/automate_listener.cc e3cc9c6951bd37f909ca6d1000475595d3f5c545
+++ network/automate_listener.cc e3cc9c6951bd37f909ca6d1000475595d3f5c545
@@ -0,0 +1,78 @@
+// Copyright (C) 2008 Timothy Brownawell
+//
+// This program is made available under the GNU GPL version 2.0 or
+// greater. See the accompanying file COPYING for details.
+//
+// This program is distributed WITHOUT ANY WARRANTY; without even the
+// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+// PURPOSE.
+
+#include "base.hh"
+#include "network/automate_listener.hh"
+
+#include "netxx/sockopt.h"
+#include "netxx/stream.h"
+#include "netxx/streamserver.h"
+
+#include "app_state.hh"
+#include "constants.hh"
+#include "network/automate_session.hh"
+#include "network/make_server.hh"
+#include "network/reactor.hh"
+
+using std::string;
+
+using boost::lexical_cast;
+using boost::shared_ptr;
+
+automate_listener::automate_listener(app_state & app,
+ boost::shared_ptr & guard,
+ reactor & react,
+ bool use_ipv6) :
+ listener_base(shared_ptr()),
+ app(app), guard(guard), addr(use_ipv6),
+ timeout(static_cast(constants::netsync_timeout_seconds)),
+ react(react)
+{
+ srv = make_server(app.opts.bind_automate_uris, 0, timeout, use_ipv6, addr);
+}
+bool automate_listener::do_io(Netxx::Probe::ready_type event)
+{
+ L(FL("accepting new automate connection on %s : %s")
+ % (addr.get_name()?addr.get_name():"") % lexical_cast(addr.get_port()));
+ Netxx::Peer client = srv->accept_connection();
+
+ if (!client)
+ {
+ L(FL("accept() returned a dead client"));
+ }
+ else
+ {
+ P(F("accepted new client connection from %s : %s")
+ % client.get_address() % lexical_cast(client.get_port()));
+
+ // 'false' here means not to revert changes when the SockOpt
+ // goes out of scope.
+ Netxx::SockOpt socket_options(client.get_socketfd(), false);
+ socket_options.set_non_blocking();
+
+ shared_ptr str =
+ shared_ptr
+ (new Netxx::Stream(client.get_socketfd(), timeout));
+
+ shared_ptr sess(new automate_session(app,
+ lexical_cast(client),
+ str));
+ I(guard);
+ react.add(sess, *guard);
+ }
+ return true;
+}
+
+// Local Variables:
+// mode: C++
+// fill-column: 76
+// c-file-style: "gnu"
+// indent-tabs-mode: nil
+// End:
+// vim: et:sw=2:sts=2:ts=2:cino=>2s,{s,\:s,+s,t0,g0,^-2,e-2,n-2,p2s,(0,=s:
============================================================
--- network/automate_listener.hh bf2a8f5463556c862f5d6dcb5e9ec542c827f8db
+++ network/automate_listener.hh bf2a8f5463556c862f5d6dcb5e9ec542c827f8db
@@ -0,0 +1,42 @@
+// Copyright (C) 2008 Timothy Brownawell
+//
+// This program is made available under the GNU GPL version 2.0 or
+// greater. See the accompanying file COPYING for details.
+//
+// This program is distributed WITHOUT ANY WARRANTY; without even the
+// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+// PURPOSE.
+
+#ifndef __AUTOMATE_LISTENER_HH__
+#define __AUTOMATE_LISTENER_HH__
+
+#include "network/listener_base.hh"
+
+class app_state;
+class transaction_guard;
+class reactor;
+
+class automate_listener : public listener_base
+{
+ app_state & app;
+ boost::shared_ptr &guard;
+ Netxx::Address addr;
+ Netxx::Timeout timeout;
+ reactor & react;
+public:
+ automate_listener(app_state & app,
+ boost::shared_ptr & guard,
+ reactor & react,
+ bool use_ipv6);
+ bool do_io(Netxx::Probe::ready_type event);
+};
+
+#endif
+
+// Local Variables:
+// mode: C++
+// fill-column: 76
+// c-file-style: "gnu"
+// indent-tabs-mode: nil
+// End:
+// vim: et:sw=2:sts=2:ts=2:cino=>2s,{s,\:s,+s,t0,g0,^-2,e-2,n-2,p2s,(0,=s:
============================================================
--- network/automate_session.cc ebc7328238ef4bf127528a736a0ec9bf46efa340
+++ network/automate_session.cc ebc7328238ef4bf127528a736a0ec9bf46efa340
@@ -0,0 +1,245 @@
+// Copyright (C) 2008 Timothy Brownawell
+//
+// This program is made available under the GNU GPL version 2.0 or
+// greater. See the accompanying file COPYING for details.
+//
+// This program is distributed WITHOUT ANY WARRANTY; without even the
+// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+// PURPOSE.
+
+#include "base.hh"
+#include "network/automate_session.hh"
+
+#include "app_state.hh"
+#include "work.hh"
+#include "vocab_cast.hh"
+
+using std::make_pair;
+using std::pair;
+using std::set;
+using std::string;
+using std::vector;
+
+using boost::shared_ptr;
+
+CMD_FWD_DECL(automate);
+
+bool automate_session::skip_ws(size_t & pos, size_t len)
+{
+ static string whitespace(" \r\n\t");
+ while (pos < len && whitespace.find(inbuf[pos]) != string::npos)
+ {
+ ++pos;
+ }
+ if (pos == len)
+ return false;
+ return true;
+}
+
+bool automate_session::read_str(size_t & pos, size_t len, string & out)
+{
+ if (pos >= len)
+ return false;
+ if (!skip_ws(pos, len))
+ return false;
+ size_t size = 0;
+ char c = inbuf[pos++];
+ while (pos < len && c <= '9' && c >= '0')
+ {
+ size = (size * 10) + (c - '0');
+ c = inbuf[pos++];
+ }
+ if (pos == len && c <= '9' && c >= '0')
+ return false;
+
+ if (c != ':')
+ throw bad_decode(F("bad automate stdio input; cannot read string"));
+
+ if (pos + size > len)
+ return false;
+
+ out = inbuf.substr(pos, size);
+ pos += size;
+ return true;
+}
+
+bool automate_session::read_cmd(Command & cmd)
+{
+ cmd.opts.clear();
+ cmd.args.clear();
+
+ size_t len = inbuf.size();
+ if (len < 2)
+ return false;
+ size_t pos = 0;
+ if (!skip_ws(pos, len))
+ return false;
+ if (inbuf[pos] == 'o')
+ {
+ ++pos;
+ while (inbuf[pos] != 'e')
+ {
+ string opt, val;
+ if (!read_str(pos, len, opt))
+ return false;
+ if (!read_str(pos, len, val))
+ return false;
+ cmd.opts.push_back(make_pair(opt, val));
+ if (!skip_ws(pos, len))
+ return false;
+ if (pos == len)
+ return false;
+ };
+ ++pos;
+ }
+ if (inbuf[pos] == 'l')
+ {
+ ++pos;
+ while (inbuf[pos] != 'e')
+ {
+ string arg;
+ if (!read_str(pos, len, arg))
+ return false;
+ cmd.args.push_back(arg);
+ if (!skip_ws(pos, len))
+ return false;
+ if (pos == len)
+ return false;
+ }
+ ++pos;
+ }
+ else
+ throw bad_decode(F("bad automate stdio input; cannot find command"));
+
+ if (cmd.args.empty())
+ throw bad_decode(F("bad automate stdio input: empty command"));
+ inbuf.pop_front(pos);
+ return true;
+}
+
+void automate_session::note_bytes_in(int count)
+{
+ protocol_state = working_state;
+}
+
+void automate_session::note_bytes_out(int count)
+{
+ size_t len = inbuf.size();
+ size_t pos = 0;
+ if (output_empty() && !skip_ws(pos, len))
+ {
+ protocol_state = confirmed_state;
+ }
+}
+
+automate_session::automate_session(app_state & app,
+ string const & peer_id,
+ shared_ptr str) :
+ session_base(peer_id, str),
+ app(app), armed(false),
+ os(oss, app.opts.automate_stdio_size)
+{ }
+
+bool automate_session::arm()
+{
+ if (!armed)
+ {
+ if (output_overfull())
+ {
+ return false;
+ }
+ armed = read_cmd(cmd);
+ }
+ return armed;
+}
+
+bool automate_session::do_work(transaction_guard & guard)
+{
+ try
+ {
+ if (!arm())
+ return true;
+ }
+ catch (bad_decode & bd)
+ {
+ W(F("stdio protocol error processing %s : '%s'")
+ % peer_id % bd.what);
+ return false;
+ }
+ armed = false;
+
+ args_vector args;
+ for (vector::iterator i = cmd.args.begin();
+ i != cmd.args.end(); ++i)
+ {
+ args.push_back(arg_type(*i, origin::user));
+ }
+
+ oss.str(string());
+
+ try
+ {
+ options::options_type opts;
+ opts = options::opts::all_options() - options::opts::globals();
+ opts.instantiate(&app.opts).reset();
+
+ command_id id;
+ for (args_vector::const_iterator iter = args.begin();
+ iter != args.end(); iter++)
+ id.push_back(typecast_vocab(*iter));
+
+ set< command_id > matches =
+ CMD_REF(automate)->complete_command(id);
+
+ if (matches.empty())
+ {
+ E(false, origin::user,
+ F("no completions for this command"));
+ }
+ else if (matches.size() > 1)
+ {
+ E(false, origin::user,
+ F("multiple completions possible for this command"));
+ }
+
+ id = *matches.begin();
+
+ I(args.size() >= id.size());
+ for (command_id::size_type i = 0; i < id.size(); i++)
+ args.erase(args.begin());
+
+ command const * cmd = CMD_REF(automate)->find_command(id);
+ I(cmd != NULL);
+ automate const * acmd = reinterpret_cast< automate const * >(cmd);
+
+ opts = options::opts::globals() | acmd->opts();
+
+ if (cmd->use_workspace_options())
+ {
+ // Re-read the ws options file, rather than just copying
+ // the options from the previous apts.opts object, because
+ // the file may have changed due to user activity.
+ workspace::check_format();
+ workspace::get_options(app.opts);
+ }
+
+ opts.instantiate(&app.opts).from_key_value_pairs(this->cmd.opts);
+ acmd->exec_from_automate(app, id, args, os);
+ }
+ catch (recoverable_failure & f)
+ {
+ os.set_err(2);
+ os << f.what();
+ }
+ os.end_cmd();
+ queue_output(oss.str());
+ return true;
+}
+
+// Local Variables:
+// mode: C++
+// fill-column: 76
+// c-file-style: "gnu"
+// indent-tabs-mode: nil
+// End:
+// vim: et:sw=2:sts=2:ts=2:cino=>2s,{s,\:s,+s,t0,g0,^-2,e-2,n-2,p2s,(0,=s:
============================================================
--- network/automate_session.hh c37e12bc01cccc8c1f91b1d7c0e51dcd54aff231
+++ network/automate_session.hh c37e12bc01cccc8c1f91b1d7c0e51dcd54aff231
@@ -0,0 +1,54 @@
+// Copyright (C) 2008 Timothy Brownawell
+//
+// This program is made available under the GNU GPL version 2.0 or
+// greater. See the accompanying file COPYING for details.
+//
+// This program is distributed WITHOUT ANY WARRANTY; without even the
+// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+// PURPOSE.
+
+#ifndef __AUTOMATE_SESSION_HH__
+#define __AUTOMATE_SESSION_HH__
+
+#include "automate_ostream.hh"
+#include "cmd.hh"
+#include "network/session_base.hh"
+
+class automate_session : public session_base
+{
+ app_state & app;
+ typedef commands::command_id command_id;
+ typedef commands::command command;
+ typedef commands::automate automate;
+ struct Command
+ {
+ std::vector > opts;
+ std::vector args;
+ };
+ bool skip_ws(size_t & pos, size_t len);
+ bool read_str(size_t & pos, size_t len, std::string & out);
+ bool read_cmd(Command & cmd);
+ bool armed;
+ Command cmd;
+
+ void note_bytes_in(int count);
+ void note_bytes_out(int count);
+ std::ostringstream oss;
+ automate_ostream os;
+public:
+ automate_session(app_state & app,
+ std::string const & peer_id,
+ boost::shared_ptr str);
+ bool arm();
+ bool do_work(transaction_guard & guard);
+};
+
+#endif
+
+// Local Variables:
+// mode: C++
+// fill-column: 76
+// c-file-style: "gnu"
+// indent-tabs-mode: nil
+// End:
+// vim: et:sw=2:sts=2:ts=2:cino=>2s,{s,\:s,+s,t0,g0,^-2,e-2,n-2,p2s,(0,=s:
============================================================
--- network/listener_base.cc 1cf9255975a5c99f20a755f7e8401ebeae553aed
+++ network/listener_base.cc 1cf9255975a5c99f20a755f7e8401ebeae553aed
@@ -0,0 +1,60 @@
+// Copyright (C) 2004 Graydon Hoare
+// 2008 Stephen Leake
+//
+// This program is made available under the GNU GPL version 2.0 or
+// greater. See the accompanying file COPYING for details.
+//
+// This program is distributed WITHOUT ANY WARRANTY; without even the
+// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+// PURPOSE.
+
+#include "base.hh"
+#include "network/listener_base.hh"
+
+#include "netxx/streamserver.h"
+
+#include "constants.hh"
+
+using std::string;
+using std::vector;
+
+using boost::shared_ptr;
+
+listener_base::listener_base(shared_ptr srv)
+ : srv(srv)
+{
+}
+listener_base::~listener_base()
+{
+}
+bool listener_base::timed_out(time_t now) { return false; }
+bool listener_base::do_work(transaction_guard & guard) { return true; }
+bool listener_base::arm() { return false; }
+bool listener_base::can_timeout() { return false; }
+
+string listener_base::name() { return ""; } // FIXME
+
+bool listener_base::is_pipe_pair()
+{
+ return false;
+}
+vector listener_base::get_sockets()
+{
+ return srv->get_probe_info()->get_sockets();
+}
+void listener_base::add_to_probe(Netxx::PipeCompatibleProbe & probe)
+{
+ if (num_reactables() >= constants::netsync_connection_limit)
+ {
+ W(F("session limit %d reached, some connections "
+ "will be refused") % constants::netsync_connection_limit);
+ }
+ else
+ {
+ probe.add(*srv);
+ }
+}
+void listener_base::remove_from_probe(Netxx::PipeCompatibleProbe & probe)
+{
+ probe.remove(*srv);
+}
============================================================
--- network/listener_base.hh db29dc7652f074e21b800e57a4a3c7ec79a5a24a
+++ network/listener_base.hh db29dc7652f074e21b800e57a4a3c7ec79a5a24a
@@ -0,0 +1,47 @@
+// Copyright (C) 2004 Graydon Hoare
+// 2008 Stephen Leake
+//
+// This program is made available under the GNU GPL version 2.0 or
+// greater. See the accompanying file COPYING for details.
+//
+// This program is distributed WITHOUT ANY WARRANTY; without even the
+// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+// PURPOSE.
+
+#ifndef __LISTENER_BASE_HH__
+#define __LISTENER_BASE_HH__
+
+#include "network/reactable.hh"
+
+#include
+
+class listener_base : public reactable
+{
+protected:
+ boost::shared_ptr srv;
+public:
+ listener_base(boost::shared_ptr srv);
+ virtual ~listener_base();
+ virtual bool do_io(Netxx::Probe::ready_type event) = 0;
+ bool timed_out(time_t now);
+ bool do_work(transaction_guard & guard);
+ bool arm();
+ bool can_timeout();
+
+ std::string name();
+
+ bool is_pipe_pair();
+ std::vector get_sockets();
+ void add_to_probe(Netxx::PipeCompatibleProbe & probe);
+ void remove_from_probe(Netxx::PipeCompatibleProbe & probe);
+};
+
+#endif
+
+// Local Variables:
+// mode: C++
+// fill-column: 76
+// c-file-style: "gnu"
+// indent-tabs-mode: nil
+// End:
+// vim: et:sw=2:sts=2:ts=2:cino=>2s,{s,\:s,+s,t0,g0,^-2,e-2,n-2,p2s,(0,=s:
============================================================
--- network/make_server.cc 7344c6d4b47bb6d975442542b9447ecd1582d7e1
+++ network/make_server.cc 7344c6d4b47bb6d975442542b9447ecd1582d7e1
@@ -0,0 +1,95 @@
+// Copyright (C) 2004 Graydon Hoare
+// 2008 Stephen Leake
+//
+// This program is made available under the GNU GPL version 2.0 or
+// greater. See the accompanying file COPYING for details.
+//
+// This program is distributed WITHOUT ANY WARRANTY; without even the
+// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+// PURPOSE.
+
+#include "base.hh"
+#include "network/make_server.hh"
+
+#include "lexical_cast.hh"
+#include "vocab.hh"
+
+using std::list;
+using std::string;
+
+using boost::lexical_cast;
+using boost::shared_ptr;
+
+shared_ptr
+make_server(list const & addresses,
+ Netxx::port_type default_port,
+ Netxx::Timeout timeout,
+ bool use_ipv6,
+ Netxx::Address & addr)
+{
+ try
+ {
+ addr = Netxx::Address(use_ipv6);
+
+ if (addresses.empty())
+ addr.add_all_addresses(default_port);
+ else
+ {
+ for (std::list::const_iterator it = addresses.begin();
+ it != addresses.end(); ++it)
+ {
+ const utf8 & address = *it;
+ if (!address().empty())
+ {
+ size_t l_colon = address().find(':');
+ size_t r_colon = address().rfind(':');
+
+ if (l_colon == r_colon && l_colon == 0)
+ {
+ // can't be an IPv6 address as there is only one colon
+ // must be a : followed by a port
+ string port_str = address().substr(1);
+ addr.add_all_addresses(std::atoi(port_str.c_str()));
+ }
+ else
+ addr.add_address(address().c_str(), default_port);
+ }
+ }
+ }
+ shared_ptr ret(new Netxx::StreamServer(addr, timeout));
+
+ char const * name;
+ name = addr.get_name();
+ P(F("beginning service on %s : %s")
+ % (name != NULL ? name : _(""))
+ % lexical_cast(addr.get_port()));
+
+ return ret;
+ }
+ // If we use IPv6 and the initialisation of server fails, we want
+ // to try again with IPv4. The reason is that someone may have
+ // downloaded a IPv6-enabled monotone on a system that doesn't
+ // have IPv6, and which might fail therefore.
+ catch(Netxx::NetworkException & e)
+ {
+ if (use_ipv6)
+ return make_server(addresses, default_port, timeout, false, addr);
+ else
+ throw;
+ }
+ catch(Netxx::Exception & e)
+ {
+ if (use_ipv6)
+ return make_server(addresses, default_port, timeout, false, addr);
+ else
+ throw;
+ }
+}
+
+// Local Variables:
+// mode: C++
+// fill-column: 76
+// c-file-style: "gnu"
+// indent-tabs-mode: nil
+// End:
+// vim: et:sw=2:sts=2:ts=2:cino=>2s,{s,\:s,+s,t0,g0,^-2,e-2,n-2,p2s,(0,=s:
============================================================
--- network/make_server.hh 25b6e2faa53607d06e4b9b2e14ec1ab29c68b67e
+++ network/make_server.hh 25b6e2faa53607d06e4b9b2e14ec1ab29c68b67e
@@ -0,0 +1,37 @@
+// Copyright (C) 2004 Graydon Hoare
+// 2008 Stephen Leake
+//
+// This program is made available under the GNU GPL version 2.0 or
+// greater. See the accompanying file COPYING for details.
+//
+// This program is distributed WITHOUT ANY WARRANTY; without even the
+// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+// PURPOSE.
+
+#ifndef __MAKE_SERVER_HH__
+#define __MAKE_SERVER_HH__
+
+#include
+
+#include
+
+#include "netxx/streamserver.h"
+
+class utf8;
+
+boost::shared_ptr
+make_server(std::list const & addresses,
+ Netxx::port_type default_port,
+ Netxx::Timeout timeout,
+ bool use_ipv6,
+ Netxx::Address & addr);
+
+#endif
+
+// Local Variables:
+// mode: C++
+// fill-column: 76
+// c-file-style: "gnu"
+// indent-tabs-mode: nil
+// End:
+// vim: et:sw=2:sts=2:ts=2:cino=>2s,{s,\:s,+s,t0,g0,^-2,e-2,n-2,p2s,(0,=s:
============================================================
--- network/netsync.txt c8668e67fac1556012731568df6069dcc3576608
+++ network/netsync.txt c8668e67fac1556012731568df6069dcc3576608
@@ -0,0 +1,204 @@
+// TODO: things to do that will break protocol compatibility
+// -- need some way to upgrade anonymous to keyed pull, without user having
+// to explicitly specify which they want
+// just having a way to respond "access denied, try again" might work
+// but perhaps better to have the anonymous command include a note "I
+// _could_ use key <...> if you prefer", and if that would lead to more
+// access, could reply "I do prefer". (Does this lead to too much
+// information exposure? Allows anonymous people to probe what branches
+// a key has access to.)
+// -- "warning" packet type?
+// -- Richard Levitte wants, when you (e.g.) request '*' but don't have
+// access to all of it, you just get the parts you have access to
+// (maybe with warnings about skipped branches). to do this right,
+// should have a way for the server to send back to the client "right,
+// you're not getting the following branches: ...", so the client will
+// not include them in its merkle trie.
+// -- add some sort of vhost field to the client's first packet, saying who
+// they expect to talk to
+
+//
+// This is the "new" network synchronization (netsync) system in
+// monotone. It is based on synchronizing pairs of merkle trees over an
+// interactive connection.
+//
+// A netsync process between peers treats each peer as either a source, a
+// sink, or both. When a peer is only a source, it will not write any new
+// items to its database. when a peer is only a sink, it will not send any
+// items from its database. When a peer is both a source and sink, it may
+// send and write items freely.
+//
+// The post-state of a netsync is that each sink contains a superset of the
+// items in its corresponding source; when peers are behaving as both
+// source and sink, this means that the post-state of the sync is for the
+// peers to have identical item sets.
+//
+//
+// Data structure
+// --------------
+//
+// Each node in a merkle tree contains a fixed number of slots. this number
+// is derived from a global parameter of the protocol -- the tree fanout --
+// such that the number of slots is 2^fanout. For now we will assume that
+// fanout is 4 thus there are 16 slots in a node, because this makes
+// illustration easier. The other parameter of the protocol is the size of
+// a hash; we use SHA1 so the hash is 20 bytes (160 bits) long.
+//
+// Each slot in a merkle tree node is in one of 3 states:
+//
+// - empty
+// - leaf
+// - subtree
+//
+// In addition, each leaf contains a hash code which identifies an element
+// of the set being synchronized. Each subtree slot contains a hash code of
+// the node immediately beneath it in the merkle tree. Empty slots contain
+// no hash codes.
+//
+// Since empty slots have no hash code, they are represented implicitly by
+// a bitmap at the head of each merkle tree node. As an additional
+// integrity check, each merkle tree node contains a label indicating its
+// prefix in the tree, and a hash of its own contents.
+//
+// In total, then, the byte-level representation of a <160,4> merkle tree
+// node is as follows:
+//
+// 20 bytes - hash of the remaining bytes in the node
+// 1 byte - type of this node (manifest, file, key, mcert, fcert)
+// 1-N bytes - level of this node in the tree (0 == "root", uleb128)
+// 0-20 bytes - the prefix of this node, 4 bits * level,
+// rounded up to a byte
+// 1-N bytes - number of leaves under this node (uleb128)
+// 4 bytes - slot-state bitmap of the node
+// 0-320 bytes - between 0 and 16 live slots in the node
+//
+// So, in the worst case such a node is 367 bytes, with these parameters.
+//
+//
+// Protocol
+// --------
+//
+// The protocol is a binary command-packet system over TCP; each packet
+// consists of a single byte which identifies the protocol version, a byte
+// which identifies the command name inside that version, a size_t sent as
+// a uleb128 indicating the length of the packet, that many bytes of
+// payload, and finally 20 bytes of SHA-1 HMAC calculated over the payload.
+// The key for the SHA-1 HMAC is 20 bytes of 0 during authentication, and a
+// 20-byte random key chosen by the client after authentication (discussed
+// below). Decoding involves simply buffering until a sufficient number of
+// bytes are received, then advancing the buffer pointer. Any time an
+// integrity check (the HMAC) fails, the protocol is assumed to have lost
+// synchronization, and the connection is dropped. The parties are free to
+// drop the TCP stream at any point, if too much data is received or too
+// much idle time passes; no commitments or transactions are made.
+//
+// Version Negotiation
+// -------------------
+//
+// Before the exchange begin, the client may receive one or more
+// "usher " packets, any number sent by "usher" proxies and
+// one sent by more recent servers. It ignores the protocol version
+// field on these packets, but replys with a "usher_reply "
+// packet containing its own maximum supported protocol version.
+//
+// Older server begin by sending a "hello" packet (see below) that contains
+// their only supported protocol version. New servers first send a "usher"
+// packet and use the response to determine the client's maximum protocol
+// version, and then use the lesser of that or their own maximum version
+// in the "hello" packet and all later packets.
+//
+// When the client receive the "hello" packet it uses that version as the
+// protocol version for all remaining packets.
+//
+// If the "usher_reply" packet indicates a version that's older than the
+// minimum version supported by the server, the server sends an error packet
+// and closes the connection.
+//
+// If the "hello" packet indicates a version not supported by the client,
+// it sends an error packet and closes the connection.
+//
+// Authentication and setup
+// ------------------------
+//
+// The exchange begins in a non-authenticated state. The server sends a
+// "hello " command, which identifies the server's RSA key and
+// issues a nonce which must be used for a subsequent authentication.
+//
+// The client then responds with either:
+//
+// An "auth (source|sink|both)
+// " command, which identifies its RSA key, notes the
+// role it wishes to play in the synchronization, identifies the pattern it
+// wishes to sync with, signs the previous nonce with its own key, and informs
+// the server of the HMAC key it wishes to use for this session (encrypted
+// with the server's public key); or
+//
+// An "anonymous (source|sink|both)
+// " command, which identifies the role it wishes to play in the
+// synchronization, the pattern it wishes to sync with, and the HMAC key it
+// wishes to use for this session (also encrypted with the server's public
+// key).
+//
+// The server then replies with a "confirm" command, which contains no
+// other data but will only have the correct HMAC integrity code if the
+// server received and properly decrypted the HMAC key offered by the
+// client. This transitions the peers into an authenticated state and
+// begins epoch refinement. If epoch refinement and epoch transmission
+// succeed, the peers switch to data refinement and data transmission.
+//
+//
+// Refinement
+// ----------
+//
+// Refinement is executed by "refiners"; there is a refiner for each
+// set of 'items' being exchanged: epochs, keys, certs, and revisions.
+// When refinement starts, each party knows only their own set of
+// items; when refinement completes, each party has learned of the
+// complete set of items it needs to send, and a count of items it's
+// expecting to receive.
+//
+// For more details on the refinement process, see refiner.cc.
+//
+//
+// Transmission
+// ------------
+//
+// Once the set of items to send has been determined (for keys, certs, and
+// revisions) each peer switches into a transmission mode. This mode
+// involves walking the revision graph in ancestry-order and sending all
+// the items the local peer has which the remote one does not. Since the
+// remote and local peers both know all the items which need to be
+// transferred (they learned during refinement) they know what to wait for
+// and what to send. The mechanisms of the transmission phase (notably,
+// enumerator.cc) simply ensure that things are sent in the proper order,
+// and without over-filling the output buffer too much.
+//
+//
+// Shutdown
+// --------
+//
+// After transmission completes, one special command, "bye", is used to
+// shut down a connection gracefully. The shutdown sequence based on "bye"
+// commands is documented below in session::process_bye_cmd.
+//
+//
+// Note on epochs
+// --------------
+//
+// One refinement and transmission phase preceeds all the others: epochs.
+// Epochs are exchanged and compared in order to be sure that further
+// refinement and transmission (on certs and revisions) makes sense; they
+// are a sort of "immune system" to prevent incompatible databases (say
+// between rebuilds due to bugs in monotone) from cross-contaminating. The
+// later refinements are only kicked off *after* all epochs are received
+// and compare correctly.
+//
+//
+// Note on dense coding
+// --------------------
+//
+// This protocol is "raw binary" (non-text) because coding density is
+// actually important here, and each packet consists of very
+// information-dense material that you wouldn't have a hope of typing in,
+// or interpreting manually anyways.
+//
============================================================
--- network/netsync_listener.cc 84a4bee07620fef712963e19322e623f3c083fe0
+++ network/netsync_listener.cc 84a4bee07620fef712963e19322e623f3c083fe0
@@ -0,0 +1,92 @@
+// Copyright (C) 2004 Graydon Hoare
+// 2008 Stephen Leake
+//
+// This program is made available under the GNU GPL version 2.0 or
+// greater. See the accompanying file COPYING for details.
+//
+// This program is distributed WITHOUT ANY WARRANTY; without even the
+// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+// PURPOSE.
+
+#include "base.hh"
+#include "network/netsync_listener.hh"
+
+#include "netxx/sockopt.h"
+#include "netxx/stream.h"
+#include "netxx/streamserver.h"
+
+#include "lexical_cast.hh"
+#include "network/make_server.hh"
+#include "network/netsync_session.hh"
+#include "network/reactor.hh"
+
+using std::list;
+using std::string;
+
+using boost::lexical_cast;
+using boost::shared_ptr;
+
+listener::listener(options & opts,
+ lua_hooks & lua,
+ project_t & project,
+ key_store & keys,
+ reactor & react,
+ protocol_role role,
+ list const & addresses,
+ shared_ptr &guard,
+ bool use_ipv6)
+ : listener_base(shared_ptr()),
+ opts(opts), lua(lua), project(project), keys(keys),
+ react(react), role(role),
+ timeout(static_cast(constants::netsync_timeout_seconds)),
+ guard(guard),
+ addr(use_ipv6)
+{
+ srv = make_server(addresses, constants::netsync_default_port,
+ timeout, use_ipv6, addr);
+}
+
+bool
+listener::do_io(Netxx::Probe::ready_type event)
+{
+ L(FL("accepting new connection on %s : %s")
+ % (addr.get_name()?addr.get_name():"") % lexical_cast(addr.get_port()));
+ Netxx::Peer client = srv->accept_connection();
+
+ if (!client)
+ {
+ L(FL("accept() returned a dead client"));
+ }
+ else
+ {
+ P(F("accepted new client connection from %s : %s")
+ % client.get_address() % lexical_cast(client.get_port()));
+
+ // 'false' here means not to revert changes when the SockOpt
+ // goes out of scope.
+ Netxx::SockOpt socket_options(client.get_socketfd(), false);
+ socket_options.set_non_blocking();
+
+ shared_ptr str =
+ shared_ptr
+ (new Netxx::Stream(client.get_socketfd(), timeout));
+
+ shared_ptr sess(new session(opts, lua, project, keys,
+ role, server_voice,
+ globish("*", origin::internal),
+ globish("", origin::internal),
+ lexical_cast(client), str));
+ sess->begin_service();
+ I(guard);
+ react.add(sess, *guard);
+ }
+ return true;
+}
+
+// Local Variables:
+// mode: C++
+// fill-column: 76
+// c-file-style: "gnu"
+// indent-tabs-mode: nil
+// End:
+// vim: et:sw=2:sts=2:ts=2:cino=>2s,{s,\:s,+s,t0,g0,^-2,e-2,n-2,p2s,(0,=s:
============================================================
--- network/netsync_listener.hh 5974625ca14d0f63d7bf803bb908a4dd665b0d40
+++ network/netsync_listener.hh 5974625ca14d0f63d7bf803bb908a4dd665b0d40
@@ -0,0 +1,66 @@
+// Copyright (C) 2004 Graydon Hoare
+// 2008 Stephen Leake
+//
+// This program is made available under the GNU GPL version 2.0 or
+// greater. See the accompanying file COPYING for details.
+//
+// This program is distributed WITHOUT ANY WARRANTY; without even the
+// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+// PURPOSE.
+
+#ifndef __NETSYNC_LISTENER_HH__
+#define __NETSYNC_LISTENER_HH__
+
+#include
+
+#include "netcmd.hh"
+#include "network/listener_base.hh"
+#include "vocab.hh"
+
+//#include
+
+class options;
+class lua_hooks;
+class key_store;
+class project_t;
+class reactor;
+class transaction_guard;
+
+class listener : public listener_base
+{
+ options & opts;
+ lua_hooks & lua;
+ project_t & project;
+ key_store & keys;
+
+ reactor & react;
+
+ protocol_role role;
+ Netxx::Timeout timeout;
+
+ boost::shared_ptr & guard;
+ Netxx::Address addr;
+public:
+
+ listener(options & opts,
+ lua_hooks & lua,
+ project_t & project,
+ key_store & keys,
+ reactor & react,
+ protocol_role role,
+ std::list const & addresses,
+ boost::shared_ptr &guard,
+ bool use_ipv6);
+
+ bool do_io(Netxx::Probe::ready_type event);
+};
+
+#endif
+
+// Local Variables:
+// mode: C++
+// fill-column: 76
+// c-file-style: "gnu"
+// indent-tabs-mode: nil
+// End:
+// vim: et:sw=2:sts=2:ts=2:cino=>2s,{s,\:s,+s,t0,g0,^-2,e-2,n-2,p2s,(0,=s:
============================================================
--- network/netsync_session.cc 6fadb167fe426ef9f30c87eabd0368277b21f100
+++ network/netsync_session.cc 6fadb167fe426ef9f30c87eabd0368277b21f100
@@ -0,0 +1,2222 @@
+// Copyright (C) 2004 Graydon Hoare
+// 2008 Stephen Leake
+//
+// This program is made available under the GNU GPL version 2.0 or
+// greater. See the accompanying file COPYING for details.
+//
+// This program is distributed WITHOUT ANY WARRANTY; without even the
+// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+// PURPOSE.
+
+#include "base.hh"
+#include "network/netsync_session.hh"
+
+#include