monotone

monotone Mtn Source Tree

Root/packet.cc

1// Copyright (C) 2002 Graydon Hoare <graydon@pobox.com>
2//
3// This program is made available under the GNU GPL version 2.0 or
4// greater. See the accompanying file COPYING for details.
5//
6// This program is distributed WITHOUT ANY WARRANTY; without even the
7// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
8// PURPOSE.
9
10
11#include "base.hh"
12#include <boost/regex.hpp>
13#include "lexical_cast.hh"
14
15#include "app_state.hh"
16#include "cset.hh"
17#include "constants.hh"
18#include "packet.hh"
19#include "revision.hh"
20#include "sanity.hh"
21#include "transforms.hh"
22#include "simplestring_xform.hh"
23#include "keys.hh"
24#include "cert.hh"
25
26using std::istream;
27using std::make_pair;
28using std::map;
29using std::ostream;
30using std::pair;
31using std::string;
32
33using boost::lexical_cast;
34using boost::match_default;
35using boost::match_results;
36using boost::regex;
37using boost::shared_ptr;
38
39// --- packet writer ---
40
41packet_writer::packet_writer(ostream & o) : ost(o) {}
42
43void
44packet_writer::consume_file_data(file_id const & ident,
45 file_data const & dat)
46{
47 base64<gzip<data> > packed;
48 pack(dat.inner(), packed);
49 ost << "[fdata " << ident.inner()() << "]\n"
50 << trim_ws(packed()) << '\n'
51 << "[end]\n";
52}
53
54void
55packet_writer::consume_file_delta(file_id const & old_id,
56 file_id const & new_id,
57 file_delta const & del)
58{
59 base64<gzip<delta> > packed;
60 pack(del.inner(), packed);
61 ost << "[fdelta " << old_id.inner()() << '\n'
62 << " " << new_id.inner()() << "]\n"
63 << trim_ws(packed()) << '\n'
64 << "[end]\n";
65}
66
67void
68packet_writer::consume_revision_data(revision_id const & ident,
69 revision_data const & dat)
70{
71 base64<gzip<data> > packed;
72 pack(dat.inner(), packed);
73 ost << "[rdata " << ident.inner()() << "]\n"
74 << trim_ws(packed()) << '\n'
75 << "[end]\n";
76}
77
78void
79packet_writer::consume_revision_cert(revision<cert> const & t)
80{
81 ost << "[rcert " << t.inner().ident() << '\n'
82 << " " << t.inner().name() << '\n'
83 << " " << t.inner().key() << '\n'
84 << " " << trim_ws(t.inner().value()) << "]\n"
85 << trim_ws(t.inner().sig()) << '\n'
86 << "[end]\n";
87}
88
89void
90packet_writer::consume_public_key(rsa_keypair_id const & ident,
91 base64< rsa_pub_key > const & k)
92{
93 ost << "[pubkey " << ident() << "]\n"
94 << trim_ws(k()) << '\n'
95 << "[end]\n";
96}
97
98void
99packet_writer::consume_key_pair(rsa_keypair_id const & ident,
100 keypair const & kp)
101{
102 ost << "[keypair " << ident() << "]\n"
103 << trim_ws(kp.pub()) <<"#\n" <<trim_ws(kp.priv()) << '\n'
104 << "[end]\n";
105}
106
107
108// -- remainder just deals with the regexes for reading packets off streams
109
110struct
111feed_packet_consumer
112{
113 app_state & app;
114 size_t & count;
115 packet_consumer & cons;
116 string ident;
117 string key;
118 string certname;
119 string base;
120 string sp;
121 feed_packet_consumer(size_t & count, packet_consumer & c, app_state & app_)
122 : app(app_), count(count), cons(c),
123 ident(constants::regex_legal_id_bytes),
124 key(constants::regex_legal_key_name_bytes),
125 certname(constants::regex_legal_cert_name_bytes),
126 base(constants::regex_legal_packet_bytes),
127 sp("[[:space:]]+")
128 {}
129 void require(bool x) const
130 {
131 E(x, F("malformed packet"));
132 }
133 bool operator()(match_results<string::const_iterator> const & res) const
134 {
135 if (res.size() != 4)
136 throw oops("matched impossible packet with "
137 + lexical_cast<string>(res.size()) + " matching parts: " +
138 string(res[0].first, res[0].second));
139 I(res[1].matched);
140 I(res[2].matched);
141 I(res[3].matched);
142 string type(res[1].first, res[1].second);
143 string args(res[2].first, res[2].second);
144 string body(res[3].first, res[3].second);
145 if (regex_match(type, regex("[fr]data")))
146 {
147 L(FL("read data packet"));
148 require(regex_match(args, regex(ident)));
149 require(regex_match(body, regex(base)));
150 base64<gzip<data> > body_packed(trim_ws(body));
151 data contents;
152 unpack(body_packed, contents);
153 if (type == "rdata")
154 cons.consume_revision_data(revision_id(hexenc<id>(args)),
155 revision_data(contents));
156 else if (type == "fdata")
157 cons.consume_file_data(file_id(hexenc<id>(args)),
158 file_data(contents));
159 else
160 throw oops("matched impossible data packet with head '" + type + "'");
161 }
162 else if (type == "fdelta")
163 {
164 L(FL("read delta packet"));
165 match_results<string::const_iterator> matches;
166 require(regex_match(args, matches, regex(ident + sp + ident)));
167 string src_id(matches[1].first, matches[1].second);
168 string dst_id(matches[2].first, matches[2].second);
169 require(regex_match(body, regex(base)));
170 base64<gzip<delta> > body_packed(trim_ws(body));
171 delta contents;
172 unpack(body_packed, contents);
173 cons.consume_file_delta(file_id(hexenc<id>(src_id)),
174 file_id(hexenc<id>(dst_id)),
175 file_delta(contents));
176 }
177 else if (type == "rcert")
178 {
179 L(FL("read cert packet"));
180 match_results<string::const_iterator> matches;
181 require(regex_match(args, matches, regex(ident + sp + certname
182 + sp + key + sp + base)));
183 string certid(matches[1].first, matches[1].second);
184 string name(matches[2].first, matches[2].second);
185 string keyid(matches[3].first, matches[3].second);
186 string val(matches[4].first, matches[4].second);
187 string contents(trim_ws(body));
188
189 // canonicalize the base64 encodings to permit searches
190 cert t = cert(hexenc<id>(certid),
191 cert_name(name),
192 base64<cert_value>(canonical_base64(val)),
193 rsa_keypair_id(keyid),
194 base64<rsa_sha1_signature>(canonical_base64(contents)));
195 cons.consume_revision_cert(revision<cert>(t));
196 }
197 else if (type == "pubkey")
198 {
199 L(FL("read pubkey data packet"));
200 require(regex_match(args, regex(key)));
201 require(regex_match(body, regex(base)));
202 string contents(trim_ws(body));
203 cons.consume_public_key(rsa_keypair_id(args),
204 base64<rsa_pub_key>(contents));
205 }
206 else if (type == "keypair")
207 {
208 L(FL("read keypair data packet"));
209 require(regex_match(args, regex(key)));
210 match_results<string::const_iterator> matches;
211 require(regex_match(body, matches, regex(base + "#" + base)));
212 base64<rsa_pub_key> pub_dat(trim_ws(string(matches[1].first, matches[1].second)));
213 base64<rsa_priv_key> priv_dat(trim_ws(string(matches[2].first, matches[2].second)));
214 cons.consume_key_pair(rsa_keypair_id(args), keypair(pub_dat, priv_dat));
215 }
216 else if (type == "privkey")
217 {
218 L(FL("read pubkey data packet"));
219 require(regex_match(args, regex(key)));
220 require(regex_match(body, regex(base)));
221 string contents(trim_ws(body));
222 keypair kp;
223 migrate_private_key(app,
224 rsa_keypair_id(args),
225 base64<arc4<rsa_priv_key> >(contents),
226 kp);
227 cons.consume_key_pair(rsa_keypair_id(args), kp);
228 }
229 else
230 {
231 W(F("unknown packet type: '%s'") % type);
232 return true;
233 }
234 ++count;
235 return true;
236 }
237};
238
239static size_t
240extract_packets(string const & s, packet_consumer & cons, app_state & app)
241{
242 static string const head("\\[([a-z]+)[[:space:]]+([^\\[\\]]+)\\]");
243 static string const body("([^\\[\\]]+)");
244 static string const tail("\\[end\\]");
245 static string const whole = head + body + tail;
246 regex expr(whole);
247 size_t count = 0;
248 regex_grep(feed_packet_consumer(count, cons, app), s, expr, match_default);
249 return count;
250}
251
252
253size_t
254read_packets(istream & in, packet_consumer & cons, app_state & app)
255{
256 string accum, tmp;
257 size_t count = 0;
258 size_t const bufsz = 0xff;
259 char buf[bufsz];
260 static string const end("[end]");
261 while(in)
262 {
263 in.read(buf, bufsz);
264 accum.append(buf, in.gcount());
265 string::size_type endpos = string::npos;
266 endpos = accum.rfind(end);
267 if (endpos != string::npos)
268 {
269 endpos += end.size();
270 string tmp = accum.substr(0, endpos);
271 count += extract_packets(tmp, cons, app);
272 if (endpos < accum.size() - 1)
273 accum = accum.substr(endpos+1);
274 else
275 accum.clear();
276 }
277 }
278 return count;
279}
280
281
282#ifdef BUILD_UNIT_TESTS
283#include "unit_tests.hh"
284#include "transforms.hh"
285
286using std::istringstream;
287using std::ostringstream;
288
289UNIT_TEST(packet, roundabout)
290{
291 string tmp;
292
293 {
294 ostringstream oss;
295 packet_writer pw(oss);
296
297 // an fdata packet
298 file_data fdata(data("this is some file data"));
299 file_id fid;
300 calculate_ident(fdata, fid);
301 pw.consume_file_data(fid, fdata);
302
303 // an fdelta packet
304 file_data fdata2(data("this is some file data which is not the same as the first one"));
305 file_id fid2;
306 calculate_ident(fdata2, fid2);
307 delta del;
308 diff(fdata.inner(), fdata2.inner(), del);
309 pw.consume_file_delta(fid, fid2, file_delta(del));
310
311 // a rdata packet
312 revision_t rev;
313 rev.new_manifest = manifest_id(string("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"));
314 shared_ptr<cset> cs(new cset);
315 cs->dirs_added.insert(file_path_internal(""));
316 rev.edges.insert(make_pair(revision_id(string("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb")),
317 cs));
318 revision_data rdat;
319 write_revision(rev, rdat);
320 revision_id rid;
321 calculate_ident(rdat, rid);
322 pw.consume_revision_data(rid, rdat);
323
324 // a cert packet
325 base64<cert_value> val;
326 encode_base64(cert_value("peaches"), val);
327 base64<rsa_sha1_signature> sig;
328 encode_base64(rsa_sha1_signature("blah blah there is no way this is a valid signature"), sig);
329 // should be a type violation to use a file id here instead of a revision
330 // id, but no-one checks...
331 cert c(fid.inner(), cert_name("smell"), val,
332 rsa_keypair_id("fun@moonman.com"), sig);
333 pw.consume_revision_cert(revision<cert>(c));
334
335 keypair kp;
336 // a public key packet
337 encode_base64(rsa_pub_key("this is not a real rsa key"), kp.pub);
338 pw.consume_public_key(rsa_keypair_id("test@lala.com"), kp.pub);
339
340 // a keypair packet
341 encode_base64(rsa_priv_key("this is not a real rsa key either!"), kp.priv);
342
343 pw.consume_key_pair(rsa_keypair_id("test@lala.com"), kp);
344
345 tmp = oss.str();
346 }
347
348 // read_packets needs this to convert privkeys to keypairs.
349 // This doesn't test privkey packets (theres a tests/ test for that),
350 // so we don't actually use the app_state for anything. So a default one
351 // is ok.
352 app_state aaa;
353 for (int i = 0; i < 10; ++i)
354 {
355 // now spin around sending and receiving this a few times
356 ostringstream oss;
357 packet_writer pw(oss);
358 istringstream iss(tmp);
359 read_packets(iss, pw, aaa);
360 UNIT_TEST_CHECK(oss.str() == tmp);
361 tmp = oss.str();
362 }
363}
364
365#endif // BUILD_UNIT_TESTS
366
367// Local Variables:
368// mode: C++
369// fill-column: 76
370// c-file-style: "gnu"
371// indent-tabs-mode: nil
372// End:
373// vim: et:sw=2:sts=2:ts=2:cino=>2s,{s,\:s,+s,t0,g0,^-2,e-2,n-2,p2s,(0,=s:

Archive Download this file

Branches

Tags

Quick Links:     www.monotone.ca    -     Downloads    -     Documentation    -     Wiki    -     Code Forge    -     Build Status