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