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#include "base.hh"
11#include <sstream>
12
13#include "cset.hh"
14#include "constants.hh"
15#include "packet.hh"
16#include "revision.hh"
17#include "sanity.hh"
18#include "transforms.hh"
19#include "simplestring_xform.hh"
20#include "cert.hh"
21#include "key_store.hh" // for keypair
22#include "char_classifiers.hh"
23
24using std::istream;
25using std::istringstream;
26using std::make_pair;
27using std::map;
28using std::ostream;
29using std::pair;
30using std::string;
31
32using boost::shared_ptr;
33
34// --- packet writer ---
35
36packet_writer::packet_writer(ostream & o) : ost(o) {}
37
38void
39packet_writer::consume_file_data(file_id const & ident,
40 file_data const & dat)
41{
42 base64<gzip<data> > packed;
43 pack(dat.inner(), packed);
44 ost << "[fdata " << ident << "]\n"
45 << trim_ws(packed()) << '\n'
46 << "[end]\n";
47}
48
49void
50packet_writer::consume_file_delta(file_id const & old_id,
51 file_id const & new_id,
52 file_delta const & del)
53{
54 base64<gzip<delta> > packed;
55 pack(del.inner(), packed);
56 ost << "[fdelta " << old_id << '\n'
57 << " " << new_id << "]\n"
58 << trim_ws(packed()) << '\n'
59 << "[end]\n";
60}
61
62void
63packet_writer::consume_revision_data(revision_id const & ident,
64 revision_data const & dat)
65{
66 base64<gzip<data> > packed;
67 pack(dat.inner(), packed);
68 ost << "[rdata " << ident << "]\n"
69 << trim_ws(packed()) << '\n'
70 << "[end]\n";
71}
72
73void
74packet_writer::consume_revision_cert(revision<cert> const & t)
75{
76 ost << "[rcert " << encode_hexenc(t.inner().ident.inner()()) << '\n'
77 << " " << t.inner().name() << '\n'
78 << " " << t.inner().key() << '\n'
79 << " " << trim_ws(encode_base64(t.inner().value)()) << "]\n"
80 << trim_ws(encode_base64(t.inner().sig)()) << '\n'
81 << "[end]\n";
82}
83
84void
85packet_writer::consume_public_key(rsa_keypair_id const & ident,
86 rsa_pub_key const & k)
87{
88 ost << "[pubkey " << ident() << "]\n"
89 << trim_ws(encode_base64(k)()) << '\n'
90 << "[end]\n";
91}
92
93void
94packet_writer::consume_key_pair(rsa_keypair_id const & ident,
95 keypair const & kp)
96{
97 ost << "[keypair " << ident() << "]\n"
98 << trim_ws(encode_base64(kp.pub)()) << "#\n"
99 << trim_ws(encode_base64(kp.priv)()) << '\n'
100 << "[end]\n";
101}
102
103void
104packet_writer::consume_old_private_key(rsa_keypair_id const & ident,
105 old_arc4_rsa_priv_key const & k)
106{
107 ost << "[privkey " << ident() << "]\n"
108 << trim_ws(encode_base64(k)()) << '\n'
109 << "[end]\n";
110}
111
112
113// --- reading packets from streams ---
114namespace
115{
116 struct
117 feed_packet_consumer
118 {
119 size_t & count;
120 packet_consumer & cons;
121 feed_packet_consumer(size_t & count, packet_consumer & c)
122 : count(count), cons(c)
123 {}
124 void validate_id(string const & id) const
125 {
126 E(id.size() == constants::idlen
127 && id.find_first_not_of(constants::legal_id_bytes) == string::npos,
128 F("malformed packet: invalid identifier"));
129 }
130 void validate_base64(string const & s) const
131 {
132 E(!s.empty()
133 && s.find_first_not_of(constants::legal_base64_bytes) == string::npos,
134 F("malformed packet: invalid base64 block"));
135 }
136 void validate_arg_base64(string const & s) const
137 {
138 E(s.find_first_not_of(constants::legal_base64_bytes) == string::npos,
139 F("malformed packet: invalid base64 block"));
140 }
141 void validate_key(string const & k) const
142 {
143 E(!k.empty()
144 && k.find_first_not_of(constants::legal_key_name_bytes) == string::npos,
145 F("malformed packet: invalid key name"));
146 }
147 void validate_certname(string const & cn) const
148 {
149 E(!cn.empty()
150 && cn.find_first_not_of(constants::legal_cert_name_bytes) == string::npos,
151 F("malformed packet: invalid cert name"));
152 }
153 void validate_no_more_args(istringstream & iss) const
154 {
155 string next;
156 iss >> next;
157 E(next.empty(),
158 F("malformed packet: too many arguments in header"));
159 }
160
161 void data_packet(string const & args, string const & body,
162 bool is_revision) const
163 {
164 L(FL("read %s data packet") % (is_revision ? "revision" : "file"));
165 validate_id(args);
166 validate_base64(body);
167
168 id hash(decode_hexenc(args));
169 data contents;
170 unpack(base64<gzip<data> >(body), contents);
171 if (is_revision)
172 cons.consume_revision_data(revision_id(hash),
173 revision_data(contents));
174 else
175 cons.consume_file_data(file_id(hash),
176 file_data(contents));
177 }
178
179 void fdelta_packet(string const & args, string const & body) const
180 {
181 L(FL("read delta packet"));
182 istringstream iss(args);
183 string src_id; iss >> src_id; validate_id(src_id);
184 string dst_id; iss >> dst_id; validate_id(dst_id);
185 validate_no_more_args(iss);
186 validate_base64(body);
187
188 id src_hash(decode_hexenc(src_id)),
189 dst_hash(decode_hexenc(dst_id));
190 delta contents;
191 unpack(base64<gzip<delta> >(body), contents);
192 cons.consume_file_delta(file_id(src_hash),
193 file_id(dst_hash),
194 file_delta(contents));
195 }
196 static void read_rest(istream& in, string& dest)
197 {
198
199 while (true)
200 {
201 string t;
202 in >> t;
203 if (t.empty()) break;
204 dest += t;
205 }
206 }
207 void rcert_packet(string const & args, string const & body) const
208 {
209 L(FL("read cert packet"));
210 istringstream iss(args);
211 string certid; iss >> certid; validate_id(certid);
212 string name; iss >> name; validate_certname(name);
213 string keyid; iss >> keyid; validate_key(keyid);
214 string val;
215 read_rest(iss,val); validate_arg_base64(val);
216
217 revision_id hash(decode_hexenc(certid));
218 validate_base64(body);
219
220 // canonicalize the base64 encodings to permit searches
221 cert t = cert(hash,
222 cert_name(name),
223 decode_base64_as<cert_value>(val),
224 rsa_keypair_id(keyid),
225 decode_base64_as<rsa_sha1_signature>(body));
226 cons.consume_revision_cert(revision<cert>(t));
227 }
228
229 void pubkey_packet(string const & args, string const & body) const
230 {
231 L(FL("read pubkey packet"));
232 validate_key(args);
233 validate_base64(body);
234
235 cons.consume_public_key(rsa_keypair_id(args),
236 decode_base64_as<rsa_pub_key>(body));
237 }
238
239 void keypair_packet(string const & args, string const & body) const
240 {
241 L(FL("read keypair packet"));
242 string::size_type hashpos = body.find('#');
243 string pub(body, 0, hashpos);
244 string priv(body, hashpos+1);
245
246 validate_key(args);
247 validate_base64(pub);
248 validate_base64(priv);
249 cons.consume_key_pair(rsa_keypair_id(args),
250 keypair(decode_base64_as<rsa_pub_key>(pub),
251 decode_base64_as<rsa_priv_key>(priv)));
252 }
253
254 void privkey_packet(string const & args, string const & body) const
255 {
256 L(FL("read privkey packet"));
257 validate_key(args);
258 validate_base64(body);
259 cons.consume_old_private_key(rsa_keypair_id(args),
260 decode_base64_as<old_arc4_rsa_priv_key>(body));
261 }
262
263 void operator()(string const & type,
264 string const & args,
265 string const & body) const
266 {
267 if (type == "rdata")
268 data_packet(args, body, true);
269 else if (type == "fdata")
270 data_packet(args, body, false);
271 else if (type == "fdelta")
272 fdelta_packet(args, body);
273 else if (type == "rcert")
274 rcert_packet(args, body);
275 else if (type == "pubkey")
276 pubkey_packet(args, body);
277 else if (type == "keypair")
278 keypair_packet(args, body);
279 else if (type == "privkey")
280 privkey_packet(args, body);
281 else
282 {
283 W(F("unknown packet type: '%s'") % type);
284 return;
285 }
286 ++count;
287 }
288 };
289} // anonymous namespace
290
291static size_t
292extract_packets(string const & s, packet_consumer & cons)
293{
294 size_t count = 0;
295 feed_packet_consumer feeder(count, cons);
296
297 string::const_iterator p, tbeg, tend, abeg, aend, bbeg, bend;
298
299 enum extract_state {
300 skipping, open_bracket, scanning_type, found_type,
301 scanning_args, found_args, scanning_body,
302 end_1, end_2, end_3, end_4, end_5
303 } state = skipping;
304
305 for (p = s.begin(); p != s.end(); p++)
306 switch (state)
307 {
308 case skipping: if (*p == '[') state = open_bracket; break;
309 case open_bracket:
310 if (is_alpha (*p))
311 state = scanning_type;
312 else
313 state = skipping;
314 tbeg = p;
315 break;
316 case scanning_type:
317 if (!is_alpha (*p))
318 {
319 state = is_space(*p) ? found_type : skipping;
320 tend = p;
321 }
322 break;
323 case found_type:
324 if (!is_space (*p))
325 {
326 state = (*p != ']') ? scanning_args : skipping;
327 abeg = p;
328 }
329 break;
330 case scanning_args:
331 if (*p == ']')
332 {
333 state = found_args;
334 aend = p;
335 }
336 break;
337 case found_args:
338 state = (*p != '[' && *p != ']') ? scanning_body : skipping;
339 bbeg = p;
340 break;
341 case scanning_body:
342 if (*p == '[')
343 {
344 state = end_1;
345 bend = p;
346 }
347 else if (*p == ']')
348 state = skipping;
349 break;
350
351 case end_1: state = (*p == 'e') ? end_2 : skipping; break;
352 case end_2: state = (*p == 'n') ? end_3 : skipping; break;
353 case end_3: state = (*p == 'd') ? end_4 : skipping; break;
354 case end_4:
355 if (*p == ']')
356 feeder(string(tbeg, tend), string(abeg, aend), string(bbeg, bend));
357 state = skipping;
358 break;
359 default:
360 I(false);
361 }
362 return count;
363}
364
365size_t
366read_packets(istream & in, packet_consumer & cons)
367{
368 string accum, tmp;
369 size_t count = 0;
370 size_t const bufsz = 0xff;
371 char buf[bufsz];
372 static string const end("[end]");
373 while(in)
374 {
375 in.read(buf, bufsz);
376 accum.append(buf, in.gcount());
377 string::size_type endpos = string::npos;
378 endpos = accum.rfind(end);
379 if (endpos != string::npos)
380 {
381 endpos += end.size();
382 string tmp = accum.substr(0, endpos);
383 count += extract_packets(tmp, cons);
384 if (endpos < accum.size() - 1)
385 accum = accum.substr(endpos+1);
386 else
387 accum.clear();
388 }
389 }
390 return count;
391}
392
393
394#ifdef BUILD_UNIT_TESTS
395#include "unit_tests.hh"
396#include "xdelta.hh"
397
398using std::ostringstream;
399
400UNIT_TEST(packet, validators)
401{
402 ostringstream oss;
403 packet_writer pw(oss);
404 size_t count;
405 feed_packet_consumer f(count, pw);
406
407#define N_THROW(expr) UNIT_TEST_CHECK_NOT_THROW(expr, informative_failure)
408#define Y_THROW(expr) UNIT_TEST_CHECK_THROW(expr, informative_failure)
409
410 // validate_id
411 N_THROW(f.validate_id("5d7005fadff386039a8d066684d22d369c1e6c94"));
412 Y_THROW(f.validate_id(""));
413 Y_THROW(f.validate_id("5d7005fadff386039a8d066684d22d369c1e6c9"));
414 for (int i = 1; i < std::numeric_limits<unsigned char>::max(); i++)
415 if (!((i >= '0' && i <= '9')
416 || (i >= 'a' && i <= 'f')))
417 Y_THROW(f.validate_id(string("5d7005fadff386039a8d066684d22d369c1e6c9")
418 + char(i)));
419
420 // validate_base64
421 N_THROW(f.validate_base64("YmwK"));
422 N_THROW(f.validate_base64(" Y m x h a A o = "));
423 N_THROW(f.validate_base64("ABCD EFGH IJKL MNOP QRST UVWX YZ"
424 "abcd efgh ijkl mnop qrst uvwx yz"
425 "0123 4567 89/+ z\t=\r=\n="));
426
427 Y_THROW(f.validate_base64(""));
428 Y_THROW(f.validate_base64("!@#$"));
429
430 // validate_key
431 N_THROW(f.validate_key("graydon@venge.net"));
432 N_THROW(f.validate_key("dscherger+mtn"));
433 N_THROW(f.validate_key("ABCDEFGHIJKLMNOPQRSTUVWXYZ"
434 "abcdefghijklmnopqrstuvwxyz"
435 "0123456789-.@+_"));
436 Y_THROW(f.validate_key(""));
437 Y_THROW(f.validate_key("graydon at venge dot net"));
438
439 // validate_certname
440 N_THROW(f.validate_certname("graydon-at-venge-dot-net"));
441 N_THROW(f.validate_certname("ABCDEFGHIJKLMNOPQRSTUVWXYZ"
442 "abcdefghijklmnopqrstuvwxyz"
443 "0123456789-"));
444
445 Y_THROW(f.validate_certname(""));
446 Y_THROW(f.validate_certname("graydon@venge.net"));
447 Y_THROW(f.validate_certname("graydon at venge dot net"));
448
449 // validate_no_more_args
450 {
451 istringstream iss("a b");
452 string a; iss >> a; UNIT_TEST_CHECK(a == "a");
453 string b; iss >> b; UNIT_TEST_CHECK(b == "b");
454 N_THROW(f.validate_no_more_args(iss));
455 }
456 {
457 istringstream iss("a ");
458 string a; iss >> a; UNIT_TEST_CHECK(a == "a");
459 N_THROW(f.validate_no_more_args(iss));
460 }
461 {
462 istringstream iss("a b");
463 string a; iss >> a; UNIT_TEST_CHECK(a == "a");
464 Y_THROW(f.validate_no_more_args(iss));
465 }
466}
467
468UNIT_TEST(packet, roundabout)
469{
470 string tmp;
471
472 {
473 ostringstream oss;
474 packet_writer pw(oss);
475
476 // an fdata packet
477 file_data fdata(data("this is some file data"));
478 file_id fid;
479 calculate_ident(fdata, fid);
480 pw.consume_file_data(fid, fdata);
481
482 // an fdelta packet
483 file_data fdata2(data("this is some file data which is not the same as the first one"));
484 file_id fid2;
485 calculate_ident(fdata2, fid2);
486 delta del;
487 diff(fdata.inner(), fdata2.inner(), del);
488 pw.consume_file_delta(fid, fid2, file_delta(del));
489
490 // a rdata packet
491 revision_t rev;
492 rev.new_manifest = manifest_id(decode_hexenc(
493 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"));
494 shared_ptr<cset> cs(new cset);
495 cs->dirs_added.insert(file_path_internal(""));
496 rev.edges.insert(make_pair(revision_id(decode_hexenc(
497 "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb")), cs));
498 revision_data rdat;
499 write_revision(rev, rdat);
500 revision_id rid;
501 calculate_ident(rdat, rid);
502 pw.consume_revision_data(rid, rdat);
503
504 // a cert packet
505 cert_value val("peaches");
506 rsa_sha1_signature sig("blah blah there is no way this is a valid signature");
507
508 // cert now accepts revision_id exclusively, so we need to cast the
509 // file_id to create a cert to test the packet writer with.
510 cert c(revision_id(fid.inner()()), cert_name("smell"), val,
511 rsa_keypair_id("fun@moonman.com"), sig);
512 pw.consume_revision_cert(revision<cert>(c));
513
514 keypair kp;
515 // a public key packet
516 kp.pub = rsa_pub_key("this is not a real rsa key");
517 pw.consume_public_key(rsa_keypair_id("test@lala.com"), kp.pub);
518
519 // a keypair packet
520 kp.priv = rsa_priv_key("this is not a real rsa key either!");
521 pw.consume_key_pair(rsa_keypair_id("test@lala.com"), kp);
522
523 // an old privkey packet
524 old_arc4_rsa_priv_key oldpriv("and neither is this!");
525 pw.consume_old_private_key(rsa_keypair_id("test@lala.com"), oldpriv);
526
527 tmp = oss.str();
528 }
529
530 for (int i = 0; i < 10; ++i)
531 {
532 // now spin around sending and receiving this a few times
533 ostringstream oss;
534 packet_writer pw(oss);
535 istringstream iss(tmp);
536 read_packets(iss, pw);
537 UNIT_TEST_CHECK(oss.str() == tmp);
538 tmp = oss.str();
539 }
540}
541
542#endif // BUILD_UNIT_TESTS
543
544// Local Variables:
545// mode: C++
546// fill-column: 76
547// c-file-style: "gnu"
548// indent-tabs-mode: nil
549// End:
550// 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