Below is the file 'netcmd.cc' from this revision. You can also download the file.
// copyright (C) 2004 graydon hoare <graydon@pobox.com> // all rights reserved. // licensed to the public under the terms of the GNU GPL (>= 2) // see the file COPYING for details #include <string> #include <vector> #include <utility> #include "adler32.hh" #include "constants.hh" #include "netcmd.hh" #include "netio.hh" #include "numeric_vocab.hh" #include "sanity.hh" #include "transforms.hh" #include "hmac.hh" using namespace std; static netcmd_item_type read_netcmd_item_type(string const & in, size_t & pos, string const & name) { u8 tmp = extract_datum_lsb<u8>(in, pos, name); switch (tmp) { case static_cast<u8>(revision_item): return revision_item; case static_cast<u8>(manifest_item): return manifest_item; case static_cast<u8>(file_item): return file_item; case static_cast<u8>(cert_item): return cert_item; case static_cast<u8>(key_item): return key_item; case static_cast<u8>(epoch_item): return epoch_item; default: throw bad_decode(F("unknown item type 0x%x for '%s'") % static_cast<int>(tmp) % name); } } netcmd::netcmd() : version(constants::netcmd_current_protocol_version), cmd_code(bye_cmd) {} size_t netcmd::encoded_size() { string tmp; insert_datum_uleb128<size_t>(payload.size(), tmp); return 1 + 1 + tmp.size() + payload.size() + 4; } bool netcmd::operator==(netcmd const & other) const { return version == other.version && cmd_code == other.cmd_code && payload == other.payload; } // note: usher_reply_cmd does not get included in the hmac. void netcmd::write(string & out, chained_hmac & hmac) const { size_t oldlen = out.size(); out += static_cast<char>(version); out += static_cast<char>(cmd_code); insert_variable_length_string(payload, out); if (cmd_code != usher_reply_cmd) { string digest = hmac.process(out, oldlen); I(hmac.hmac_length == constants::netsync_hmac_value_length_in_bytes); out.append(digest); } } // note: usher_cmd does not get included in the hmac. bool netcmd::read(string_queue & inbuf, chained_hmac & hmac) { size_t pos = 0; if (inbuf.size() < constants::netcmd_minsz) return false; u8 extracted_ver = extract_datum_lsb<u8>(inbuf, pos, "netcmd protocol number"); if (extracted_ver != version) throw bad_decode(F("protocol version mismatch: wanted '%d' got '%d'") % widen<u32,u8>(version) % widen<u32,u8>(extracted_ver)); version = extracted_ver; u8 cmd_byte = extract_datum_lsb<u8>(inbuf, pos, "netcmd code"); switch (cmd_byte) { case static_cast<u8>(hello_cmd): case static_cast<u8>(anonymous_cmd): case static_cast<u8>(auth_cmd): case static_cast<u8>(error_cmd): case static_cast<u8>(bye_cmd): case static_cast<u8>(confirm_cmd): case static_cast<u8>(refine_cmd): case static_cast<u8>(done_cmd): case static_cast<u8>(send_data_cmd): case static_cast<u8>(send_delta_cmd): case static_cast<u8>(data_cmd): case static_cast<u8>(delta_cmd): case static_cast<u8>(nonexistant_cmd): case static_cast<u8>(usher_cmd): cmd_code = static_cast<netcmd_code>(cmd_byte); break; default: throw bad_decode(F("unknown netcmd code 0x%x") % widen<u32,u8>(cmd_byte)); } // check to see if we have even enough bytes for a complete uleb128 size_t payload_len = 0; if (!try_extract_datum_uleb128<size_t>(inbuf, pos, "netcmd payload length", payload_len)) return false; // they might have given us a bogus size if (payload_len > constants::netcmd_payload_limit) throw bad_decode(F("oversized payload of '%d' bytes") % payload_len); // there might not be enough data yet in the input buffer unsigned int minsize; if (cmd_code == usher_cmd) minsize = pos + payload_len; else minsize = pos + payload_len + constants::netsync_hmac_value_length_in_bytes; if (inbuf.size() < minsize) { return false; } // grab it before the data gets munged I(hmac.hmac_length == constants::netsync_hmac_value_length_in_bytes); string digest; if (cmd_code != usher_cmd) digest = hmac.process(inbuf, 0, pos + payload_len); payload = extract_substring(inbuf, pos, payload_len, "netcmd payload"); // they might have given us bogus data string cmd_digest; if (cmd_code != usher_cmd) cmd_digest = extract_substring(inbuf, pos, constants::netsync_hmac_value_length_in_bytes, "netcmd HMAC"); inbuf.pop_front(pos); if (cmd_digest != digest) throw bad_decode(F("bad HMAC checksum (got %s, wanted %s)\n" "this suggests data was corrupted in transit\n") % encode_hexenc(cmd_digest) % encode_hexenc(digest)); return true; } //////////////////////////////////////////// // payload reader/writer functions follow // //////////////////////////////////////////// void netcmd::read_error_cmd(std::string & errmsg) const { size_t pos = 0; // syntax is: <errmsg:vstr> extract_variable_length_string(payload, errmsg, pos, "error netcmd, message"); assert_end_of_buffer(payload, pos, "error netcmd payload"); } void netcmd::write_error_cmd(std::string const & errmsg) { cmd_code = error_cmd; payload.clear(); insert_variable_length_string(errmsg, payload); } void netcmd::read_hello_cmd(rsa_keypair_id & server_keyname, rsa_pub_key & server_key, id & nonce) const { size_t pos = 0; // syntax is: <server keyname:vstr> <server pubkey:vstr> <nonce:20 random bytes> string skn_str, sk_str; extract_variable_length_string(payload, skn_str, pos, "hello netcmd, server key name"); server_keyname = rsa_keypair_id(skn_str); extract_variable_length_string(payload, sk_str, pos, "hello netcmd, server key"); server_key = rsa_pub_key(sk_str); nonce = id(extract_substring(payload, pos, constants::merkle_hash_length_in_bytes, "hello netcmd, nonce")); assert_end_of_buffer(payload, pos, "hello netcmd payload"); } void netcmd::write_hello_cmd(rsa_keypair_id const & server_keyname, rsa_pub_key const & server_key, id const & nonce) { cmd_code = hello_cmd; payload.clear(); I(nonce().size() == constants::merkle_hash_length_in_bytes); insert_variable_length_string(server_keyname(), payload); insert_variable_length_string(server_key(), payload); payload += nonce(); } void netcmd::read_anonymous_cmd(protocol_role & role, utf8 & include_pattern, utf8 & exclude_pattern, rsa_oaep_sha_data & hmac_key_encrypted) const { size_t pos = 0; // syntax is: <role:1 byte> <include_pattern: vstr> <exclude_pattern: vstr> <hmac_key_encrypted: vstr> u8 role_byte = extract_datum_lsb<u8>(payload, pos, "anonymous(hmac) netcmd, role"); if (role_byte != static_cast<u8>(source_role) && role_byte != static_cast<u8>(sink_role) && role_byte != static_cast<u8>(source_and_sink_role)) throw bad_decode(F("unknown role specifier %d") % widen<u32,u8>(role_byte)); role = static_cast<protocol_role>(role_byte); std::string pattern_string; extract_variable_length_string(payload, pattern_string, pos, "anonymous(hmac) netcmd, include_pattern"); include_pattern = utf8(pattern_string); extract_variable_length_string(payload, pattern_string, pos, "anonymous(hmac) netcmd, exclude_pattern"); exclude_pattern = utf8(pattern_string); string hmac_key_string; extract_variable_length_string(payload, hmac_key_string, pos, "anonymous(hmac) netcmd, hmac_key_encrypted"); hmac_key_encrypted = rsa_oaep_sha_data(hmac_key_string); assert_end_of_buffer(payload, pos, "anonymous(hmac) netcmd payload"); } void netcmd::write_anonymous_cmd(protocol_role role, utf8 const & include_pattern, utf8 const & exclude_pattern, rsa_oaep_sha_data const & hmac_key_encrypted) { cmd_code = anonymous_cmd; payload = static_cast<char>(role); insert_variable_length_string(include_pattern(), payload); insert_variable_length_string(exclude_pattern(), payload); insert_variable_length_string(hmac_key_encrypted(), payload); } void netcmd::read_auth_cmd(protocol_role & role, utf8 & include_pattern, utf8 & exclude_pattern, id & client, id & nonce1, rsa_oaep_sha_data & hmac_key_encrypted, string & signature) const { size_t pos = 0; // syntax is: <role:1 byte> <include_pattern: vstr> <exclude_pattern: vstr> // <client: 20 bytes sha1> <nonce1: 20 random bytes> // <hmac_key_encrypted: vstr> <signature: vstr> u8 role_byte = extract_datum_lsb<u8>(payload, pos, "auth netcmd, role"); if (role_byte != static_cast<u8>(source_role) && role_byte != static_cast<u8>(sink_role) && role_byte != static_cast<u8>(source_and_sink_role)) throw bad_decode(F("unknown role specifier %d") % widen<u32,u8>(role_byte)); role = static_cast<protocol_role>(role_byte); std::string pattern_string; extract_variable_length_string(payload, pattern_string, pos, "auth(hmac) netcmd, include_pattern"); include_pattern = utf8(pattern_string); extract_variable_length_string(payload, pattern_string, pos, "auth(hmac) netcmd, exclude_pattern"); exclude_pattern = utf8(pattern_string); client = id(extract_substring(payload, pos, constants::merkle_hash_length_in_bytes, "auth(hmac) netcmd, client identifier")); nonce1 = id(extract_substring(payload, pos, constants::merkle_hash_length_in_bytes, "auth(hmac) netcmd, nonce1")); string hmac_key; extract_variable_length_string(payload, hmac_key, pos, "auth(hmac) netcmd, hmac_key_encrypted"); hmac_key_encrypted = rsa_oaep_sha_data(hmac_key); extract_variable_length_string(payload, signature, pos, "auth(hmac) netcmd, signature"); assert_end_of_buffer(payload, pos, "auth(hmac) netcmd payload"); } void netcmd::write_auth_cmd(protocol_role role, utf8 const & include_pattern, utf8 const & exclude_pattern, id const & client, id const & nonce1, rsa_oaep_sha_data const & hmac_key_encrypted, string const & signature) { cmd_code = auth_cmd; I(client().size() == constants::merkle_hash_length_in_bytes); I(nonce1().size() == constants::merkle_hash_length_in_bytes); payload = static_cast<char>(role); insert_variable_length_string(include_pattern(), payload); insert_variable_length_string(exclude_pattern(), payload); payload += client(); payload += nonce1(); insert_variable_length_string(hmac_key_encrypted(), payload); insert_variable_length_string(signature, payload); } void netcmd::read_confirm_cmd() const { size_t pos = 0; assert_end_of_buffer(payload, pos, "confirm netcmd payload"); } void netcmd::write_confirm_cmd() { cmd_code = confirm_cmd; payload.clear(); } void netcmd::read_refine_cmd(merkle_node & node) const { // syntax is: <node: a merkle tree node> read_node(payload, node); } void netcmd::write_refine_cmd(merkle_node const & node) { cmd_code = refine_cmd; payload.clear(); write_node(node, payload); } void netcmd::read_done_cmd(size_t & level, netcmd_item_type & type) const { size_t pos = 0; // syntax is: <level: uleb128> <type: 1 byte> level = extract_datum_uleb128<size_t>(payload, pos, "done netcmd, level number"); type = read_netcmd_item_type(payload, pos, "done netcmd, item type"); assert_end_of_buffer(payload, pos, "done netcmd payload"); } void netcmd::write_done_cmd(size_t level, netcmd_item_type type) { cmd_code = done_cmd; payload.clear(); insert_datum_uleb128<size_t>(level, payload); payload += static_cast<char>(type); } void netcmd::read_send_data_cmd(netcmd_item_type & type, id & item) const { size_t pos = 0; // syntax is: <type: 1 byte> <id: 20 bytes sha1> type = read_netcmd_item_type(payload, pos, "send_data netcmd, item type"); item = id(extract_substring(payload, pos, constants::merkle_hash_length_in_bytes, "send_data netcmd, item identifier")); assert_end_of_buffer(payload, pos, "send_data netcmd payload"); } void netcmd::write_send_data_cmd(netcmd_item_type type, id const & item) { cmd_code = send_data_cmd; I(item().size() == constants::merkle_hash_length_in_bytes); payload = static_cast<char>(type); payload += item(); } void netcmd::read_send_delta_cmd(netcmd_item_type & type, id & base, id & ident) const { size_t pos = 0; // syntax is: <type: 1 byte> <src: 20 bytes sha1> <dst: 20 bytes sha1> type = read_netcmd_item_type(payload, pos, "send_delta netcmd, item type"); base = id(extract_substring(payload, pos, constants::merkle_hash_length_in_bytes, "send_delta netcmd, base item identifier")); ident = id(extract_substring(payload, pos, constants::merkle_hash_length_in_bytes, "send_delta netcmd, ident item identifier")); assert_end_of_buffer(payload, pos, "send_delta netcmd payload"); } void netcmd::write_send_delta_cmd(netcmd_item_type type, id const & base, id const & ident) { cmd_code = send_delta_cmd; I(base().size() == constants::merkle_hash_length_in_bytes); I(ident().size() == constants::merkle_hash_length_in_bytes); payload = static_cast<char>(type); payload += base(); payload += ident(); } void netcmd::read_data_cmd(netcmd_item_type & type, id & item, string & dat) const { size_t pos = 0; // syntax is: <type: 1 byte> <id: 20 bytes sha1> // <compressed_p1: 1 byte> <dat: vstr> type = read_netcmd_item_type(payload, pos, "data netcmd, item type"); item = id(extract_substring(payload, pos, constants::merkle_hash_length_in_bytes, "data netcmd, item identifier")); dat.clear(); u8 compressed_p = extract_datum_lsb<u8>(payload, pos, "data netcmd, compression flag"); extract_variable_length_string(payload, dat, pos, "data netcmd, data payload"); if (compressed_p == 1) { gzip<data> zdat(dat); data tdat; decode_gzip(zdat, tdat); dat = tdat(); } assert_end_of_buffer(payload, pos, "data netcmd payload"); } void netcmd::write_data_cmd(netcmd_item_type type, id const & item, string const & dat) { cmd_code = data_cmd; I(item().size() == constants::merkle_hash_length_in_bytes); payload = static_cast<char>(type); payload += item(); if (dat.size() > constants::netcmd_minimum_bytes_to_bother_with_gzip) { gzip<data> zdat; encode_gzip(dat, zdat); payload += static_cast<char>(1); // compressed flag insert_variable_length_string(zdat(), payload); } else { payload += static_cast<char>(0); // compressed flag insert_variable_length_string(dat, payload); } } void netcmd::read_delta_cmd(netcmd_item_type & type, id & base, id & ident, delta & del) const { size_t pos = 0; // syntax is: <type: 1 byte> <src: 20 bytes sha1> <dst: 20 bytes sha1> // <compressed_p: 1 byte> <del: vstr> type = read_netcmd_item_type(payload, pos, "delta netcmd, item type"); base = id(extract_substring(payload, pos, constants::merkle_hash_length_in_bytes, "delta netcmd, base identifier")); ident = id(extract_substring(payload, pos, constants::merkle_hash_length_in_bytes, "delta netcmd, ident identifier")); u8 compressed_p = extract_datum_lsb<u8>(payload, pos, "delta netcmd, compression flag"); string tmp; extract_variable_length_string(payload, tmp, pos, "delta netcmd, delta payload"); if (compressed_p == 1) { gzip<delta> zdel(tmp); decode_gzip(zdel, del); } else { del = tmp; } assert_end_of_buffer(payload, pos, "delta netcmd payload"); } void netcmd::write_delta_cmd(netcmd_item_type & type, id const & base, id const & ident, delta const & del) { cmd_code = delta_cmd; I(base().size() == constants::merkle_hash_length_in_bytes); I(ident().size() == constants::merkle_hash_length_in_bytes); payload = static_cast<char>(type); payload += base(); payload += ident(); string tmp; if (tmp.size() > constants::netcmd_minimum_bytes_to_bother_with_gzip) { payload += static_cast<char>(1); // compressed flag gzip<delta> zdel; encode_gzip(del, zdel); tmp = zdel(); } else { payload += static_cast<char>(0); // compressed flag tmp = del(); } I(tmp.size() <= constants::netcmd_payload_limit); insert_variable_length_string(tmp, payload); } void netcmd::read_nonexistant_cmd(netcmd_item_type & type, id & item) const { size_t pos = 0; // syntax is: <type: 1 byte> <id: 20 bytes sha1> type = read_netcmd_item_type(payload, pos, "nonexistant netcmd, item type"); item = id(extract_substring(payload, pos, constants::merkle_hash_length_in_bytes, "nonexistant netcmd, item identifier")); assert_end_of_buffer(payload, pos, "nonexistant netcmd payload"); } void netcmd::write_nonexistant_cmd(netcmd_item_type type, id const & item) { cmd_code = nonexistant_cmd; I(item().size() == constants::merkle_hash_length_in_bytes); payload = static_cast<char>(type); payload += item(); } void netcmd::read_usher_cmd(utf8 & greeting) const { size_t pos = 0; std::string str; extract_variable_length_string(payload, str, pos, "error netcmd, message"); greeting = utf8(str); assert_end_of_buffer(payload, pos, "error netcmd payload"); } void netcmd::write_usher_reply_cmd(utf8 const & pattern) { cmd_code = usher_reply_cmd; payload.clear(); insert_variable_length_string(pattern(), payload); } #ifdef BUILD_UNIT_TESTS #include "unit_tests.hh" #include "transforms.hh" #include <boost/lexical_cast.hpp> void test_netcmd_mac() { netcmd out_cmd, in_cmd; string buf; netsync_session_key key(constants::netsync_key_initializer); { chained_hmac mac(key); // mutates mac out_cmd.write(buf, mac); BOOST_CHECK_THROW(in_cmd.read_string(buf, mac), bad_decode); } { chained_hmac mac(key); out_cmd.write(buf, mac); } buf[0] ^= 0xff; { chained_hmac mac(key); BOOST_CHECK_THROW(in_cmd.read_string(buf, mac), bad_decode); } { chained_hmac mac(key); out_cmd.write(buf, mac); } buf[buf.size() - 1] ^= 0xff; { chained_hmac mac(key); BOOST_CHECK_THROW(in_cmd.read_string(buf, mac), bad_decode); } { chained_hmac mac(key); out_cmd.write(buf, mac); } buf += '\0'; { chained_hmac mac(key); BOOST_CHECK_THROW(in_cmd.read_string(buf, mac), bad_decode); } } static void do_netcmd_roundtrip(netcmd const & out_cmd, netcmd & in_cmd, string & buf) { netsync_session_key key(constants::netsync_key_initializer); { chained_hmac mac(key); out_cmd.write(buf, mac); } { chained_hmac mac(key); BOOST_CHECK(in_cmd.read_string(buf, mac)); } BOOST_CHECK(in_cmd == out_cmd); } void test_netcmd_functions() { try { // error_cmd { L(boost::format("checking i/o round trip on error_cmd\n")); netcmd out_cmd, in_cmd; string out_errmsg("your shoelaces are untied"), in_errmsg; string buf; out_cmd.write_error_cmd(out_errmsg); do_netcmd_roundtrip(out_cmd, in_cmd, buf); in_cmd.read_error_cmd(in_errmsg); BOOST_CHECK(in_errmsg == out_errmsg); L(boost::format("errmsg_cmd test done, buffer was %d bytes\n") % buf.size()); } // bye_cmd { L(boost::format("checking i/o round trip on bye_cmd\n")); netcmd out_cmd, in_cmd; string buf; do_netcmd_roundtrip(out_cmd, in_cmd, buf); L(boost::format("bye_cmd test done, buffer was %d bytes\n") % buf.size()); } // hello_cmd { L(boost::format("checking i/o round trip on hello_cmd\n")); netcmd out_cmd, in_cmd; string buf; rsa_keypair_id out_server_keyname("server@there"), in_server_keyname; rsa_pub_key out_server_key("9387938749238792874"), in_server_key; id out_nonce(raw_sha1("nonce it up")), in_nonce; out_cmd.write_hello_cmd(out_server_keyname, out_server_key, out_nonce); do_netcmd_roundtrip(out_cmd, in_cmd, buf); in_cmd.read_hello_cmd(in_server_keyname, in_server_key, in_nonce); BOOST_CHECK(in_server_keyname == out_server_keyname); BOOST_CHECK(in_server_key == out_server_key); BOOST_CHECK(in_nonce == out_nonce); L(boost::format("hello_cmd test done, buffer was %d bytes\n") % buf.size()); } // anonymous_cmd { L(boost::format("checking i/o round trip on anonymous_cmd\n")); netcmd out_cmd, in_cmd; protocol_role out_role = source_and_sink_role, in_role; string buf; // total cheat, since we don't actually verify that rsa_oaep_sha_data // is sensible anywhere here... rsa_oaep_sha_data out_key("nonce start my heart"), in_key; utf8 out_include_pattern("radishes galore!"), in_include_pattern; utf8 out_exclude_pattern("turnips galore!"), in_exclude_pattern; out_cmd.write_anonymous_cmd(out_role, out_include_pattern, out_exclude_pattern, out_key); do_netcmd_roundtrip(out_cmd, in_cmd, buf); in_cmd.read_anonymous_cmd(in_role, in_include_pattern, in_exclude_pattern, in_key); BOOST_CHECK(in_key == out_key); BOOST_CHECK(in_include_pattern == out_include_pattern); BOOST_CHECK(in_exclude_pattern == out_exclude_pattern); BOOST_CHECK(in_role == out_role); L(boost::format("anonymous_cmd test done, buffer was %d bytes\n") % buf.size()); } // auth_cmd { L(boost::format("checking i/o round trip on auth_cmd\n")); netcmd out_cmd, in_cmd; protocol_role out_role = source_and_sink_role, in_role; string buf; id out_client(raw_sha1("happy client day")), out_nonce1(raw_sha1("nonce me amadeus")), in_client, in_nonce1; // total cheat, since we don't actually verify that rsa_oaep_sha_data // is sensible anywhere here... rsa_oaep_sha_data out_key("nonce start my heart"), in_key; string out_signature(raw_sha1("burble") + raw_sha1("gorby")), in_signature; utf8 out_include_pattern("radishes galore!"), in_include_pattern; utf8 out_exclude_pattern("turnips galore!"), in_exclude_pattern; out_cmd.write_auth_cmd(out_role, out_include_pattern, out_exclude_pattern , out_client, out_nonce1, out_key, out_signature); do_netcmd_roundtrip(out_cmd, in_cmd, buf); in_cmd.read_auth_cmd(in_role, in_include_pattern, in_exclude_pattern, in_client, in_nonce1, in_key, in_signature); BOOST_CHECK(in_client == out_client); BOOST_CHECK(in_nonce1 == out_nonce1); BOOST_CHECK(in_key == out_key); BOOST_CHECK(in_signature == out_signature); BOOST_CHECK(in_role == out_role); BOOST_CHECK(in_include_pattern == out_include_pattern); BOOST_CHECK(in_exclude_pattern == out_exclude_pattern); L(boost::format("auth_cmd test done, buffer was %d bytes\n") % buf.size()); } // confirm_cmd { L(boost::format("checking i/o round trip on confirm_cmd\n")); netcmd out_cmd, in_cmd; string buf; out_cmd.write_confirm_cmd(); do_netcmd_roundtrip(out_cmd, in_cmd, buf); in_cmd.read_confirm_cmd(); L(boost::format("confirm_cmd test done, buffer was %d bytes\n") % buf.size()); } // refine_cmd { L(boost::format("checking i/o round trip on refine_cmd\n")); netcmd out_cmd, in_cmd; string buf; merkle_node out_node, in_node; out_node.set_raw_slot(0, id(raw_sha1("The police pulled Kris Kringle over"))); out_node.set_raw_slot(3, id(raw_sha1("Kris Kringle tried to escape from the police"))); out_node.set_raw_slot(8, id(raw_sha1("He was arrested for auto theft"))); out_node.set_raw_slot(15, id(raw_sha1("He was whisked away to jail"))); out_node.set_slot_state(0, subtree_state); out_node.set_slot_state(3, live_leaf_state); out_node.set_slot_state(8, dead_leaf_state); out_node.set_slot_state(15, subtree_state); out_cmd.write_refine_cmd(out_node); do_netcmd_roundtrip(out_cmd, in_cmd, buf); in_cmd.read_refine_cmd(in_node); BOOST_CHECK(in_node == out_node); L(boost::format("refine_cmd test done, buffer was %d bytes\n") % buf.size()); } // done_cmd { L(boost::format("checking i/o round trip on done_cmd\n")); netcmd out_cmd, in_cmd; size_t out_level(12), in_level; netcmd_item_type out_type(key_item), in_type(manifest_item); string buf; out_cmd.write_done_cmd(out_level, out_type); do_netcmd_roundtrip(out_cmd, in_cmd, buf); in_cmd.read_done_cmd(in_level, in_type); BOOST_CHECK(in_level == out_level); BOOST_CHECK(in_type == out_type); L(boost::format("done_cmd test done, buffer was %d bytes\n") % buf.size()); } // send_data_cmd { L(boost::format("checking i/o round trip on send_data_cmd\n")); netcmd out_cmd, in_cmd; netcmd_item_type out_type(file_item), in_type(key_item); id out_id(raw_sha1("avocado is the yummiest")), in_id; string buf; out_cmd.write_send_data_cmd(out_type, out_id); do_netcmd_roundtrip(out_cmd, in_cmd, buf); in_cmd.read_send_data_cmd(in_type, in_id); BOOST_CHECK(in_type == out_type); BOOST_CHECK(in_id == out_id); L(boost::format("send_data_cmd test done, buffer was %d bytes\n") % buf.size()); } // send_delta_cmd { L(boost::format("checking i/o round trip on send_delta_cmd\n")); netcmd out_cmd, in_cmd; netcmd_item_type out_type(file_item), in_type(key_item); id out_head(raw_sha1("when you board an airplane")), in_head; id out_base(raw_sha1("always check the exit locations")), in_base; string buf; out_cmd.write_send_delta_cmd(out_type, out_head, out_base); do_netcmd_roundtrip(out_cmd, in_cmd, buf); in_cmd.read_send_delta_cmd(in_type, in_head, in_base); BOOST_CHECK(in_type == out_type); BOOST_CHECK(in_head == out_head); BOOST_CHECK(in_base == out_base); L(boost::format("send_delta_cmd test done, buffer was %d bytes\n") % buf.size()); } // data_cmd { L(boost::format("checking i/o round trip on data_cmd\n")); netcmd out_cmd, in_cmd; netcmd_item_type out_type(file_item), in_type(key_item); id out_id(raw_sha1("tuna is not yummy")), in_id; string out_dat("thank you for flying northwest"), in_dat; string buf; out_cmd.write_data_cmd(out_type, out_id, out_dat); do_netcmd_roundtrip(out_cmd, in_cmd, buf); in_cmd.read_data_cmd(in_type, in_id, in_dat); BOOST_CHECK(in_id == out_id); BOOST_CHECK(in_dat == out_dat); L(boost::format("data_cmd test done, buffer was %d bytes\n") % buf.size()); } // delta_cmd { L(boost::format("checking i/o round trip on delta_cmd\n")); netcmd out_cmd, in_cmd; netcmd_item_type out_type(file_item), in_type(key_item); id out_head(raw_sha1("your seat cusion can be reused")), in_head; id out_base(raw_sha1("as a floatation device")), in_base; delta out_delta("goodness, this is not an xdelta"), in_delta; string buf; out_cmd.write_delta_cmd(out_type, out_head, out_base, out_delta); do_netcmd_roundtrip(out_cmd, in_cmd, buf); in_cmd.read_delta_cmd(in_type, in_head, in_base, in_delta); BOOST_CHECK(in_type == out_type); BOOST_CHECK(in_head == out_head); BOOST_CHECK(in_base == out_base); BOOST_CHECK(in_delta == out_delta); L(boost::format("delta_cmd test done, buffer was %d bytes\n") % buf.size()); } // nonexistant_cmd { L(boost::format("checking i/o round trip on nonexistant_cmd\n")); netcmd out_cmd, in_cmd; netcmd_item_type out_type(file_item), in_type(key_item); id out_id(raw_sha1("avocado is the yummiest")), in_id; string buf; out_cmd.write_nonexistant_cmd(out_type, out_id); do_netcmd_roundtrip(out_cmd, in_cmd, buf); in_cmd.read_nonexistant_cmd(in_type, in_id); BOOST_CHECK(in_type == out_type); BOOST_CHECK(in_id == out_id); L(boost::format("nonexistant_cmd test done, buffer was %d bytes\n") % buf.size()); } } catch (bad_decode & d) { L(boost::format("bad decode exception: '%s'\n") % d.what); throw; } } void add_netcmd_tests(test_suite * suite) { suite->add(BOOST_TEST_CASE(&test_netcmd_functions)); suite->add(BOOST_TEST_CASE(&test_netcmd_mac)); } #endif // BUILD_UNIT_TESTS