diff options
-rwxr-xr-x | potpourri/rrdp-test-tool | 99 | ||||
-rw-r--r-- | rpki/pubd.py | 7 |
2 files changed, 86 insertions, 20 deletions
diff --git a/potpourri/rrdp-test-tool b/potpourri/rrdp-test-tool index fa2de023..d83e188d 100755 --- a/potpourri/rrdp-test-tool +++ b/potpourri/rrdp-test-tool @@ -23,6 +23,7 @@ for testing. """ import rpki.relaxng +import rpki.x509 import lxml.etree import argparse import os @@ -37,41 +38,105 @@ tags = Tags("notification", "deltas", "delta", "snapshot", "publish", "withdraw" class main(object): def __init__(self): - parser = argparse.ArgumentParser(description = __doc__) parser.add_argument("--rcynic-tree", default = "rcynic-data/unauthenticated", help = "directory tree in which to write extracted RPKI objects") + parser.add_argument("--serial-filename", + help = "file name in which to store RRDP serial number") parser.add_argument("rrdp_file", nargs = "+", help = "RRDP snapshot or deltas file") self.args = parser.parse_args() - if not os.path.isdir(self.args.rcynic_tree): os.makedirs(self.args.rcynic_tree) - for rrdp_file in self.args.rrdp_file: xml = lxml.etree.ElementTree(file = rrdp_file).getroot() rpki.relaxng.rrdp.assertValid(xml) getattr(self, xml.tag[len(rpki.relaxng.rrdp.xmlns):])(xml) + @property + def serial_filename(self): + return self.args.serial_filename or os.path.join(self.args.rcynic_tree, "serial") - def snapshot(self, xml): - assert xml.tag == tags.snapshot + def get_serial(self): + with open(self.serial_filename, "r") as f: + return f.read().strip() + + def set_serial(self, value): + with open(self.serial_filename, "w") as f: + f.write("%s\n" % value) - print "Unpacking version %s session %s serial %s" % ( + def notification(self, xml): + print "Notification version %s session %s serial %s" % ( xml.get("version"), xml.get("session_id"), xml.get("serial")) + assert xml[0].tag == tags.snapshot + print " Snapshot URI %s hash %s" % ( + xml[0].get("uri"), xml[0].get("hash")) + for i, elt in enumerate(xml.iterchildren(tags.delta)): + print " Delta %3d from %6s to %6s URI %s hash %s" % ( + i, elt.get("from"), elt.get("to"), elt.get("uri"), elt.get("hash")) - for elt in xml: - assert elt.tag == tags.publish - uri = elt.get("uri") - print " ", uri - assert uri.startswith("rsync://") - fn = os.path.join(self.args.rcynic_tree, uri[len("rsync://"):]) - dn = os.path.dirname(fn) - if not os.path.isdir(dn): - os.makedirs(dn) - with open(fn, "wb") as f: - f.write(elt.text.decode("base64")) + def uri_to_filename(self, uri): + assert uri.startswith("rsync://") + return os.path.join(self.args.rcynic_tree, uri[len("rsync://"):]) + + def add_obj(self, uri, obj): + fn = self.uri_to_filename(uri) + dn = os.path.dirname(fn) + if not os.path.isdir(dn): + os.makedirs(dn) + with open(fn, "wb") as f: + f.write(obj) + + def del_obj(self, uri, hash): + fn = self.uri_to_filename(uri) + with open(fn, "rb") as f: + if hash != rpki.x509.sha256(f.read()).encode("hex"): + raise RuntimeError("Hash mismatch for URI %s" % uri) + os.unlink(fn) + dn = os.path.dirname(fn) + while True: + try: + os.rmdir(dn) + except OSError: + break + else: + dn = os.path.dirname(dn) + + def snapshot(self, xml): + print "Unpacking snapshot version %s session %s serial %6s" % ( + xml.get("version"), xml.get("session_id"), xml.get("serial")) + for elt in xml.iterchildren(tags.publish): + print " ", elt.get("uri") + self.add_obj(elt.get("uri"), elt.text.decode("base64")) + self.set_serial(xml.get("serial")) + def deltas(self, xml): + cur = int(self.get_serial()) + old = int(xml.get("from")) + new = int(xml.get("to")) + print "Unpacking deltas version %s session %s from %s to %s" % ( + xml.get("version"), xml.get("session_id"), old, new) + if cur != old: + raise RuntimeError("Can't apply deltas: current %s old %s new %s" % (cur, old, new)) + for i, delta in enumerate(xml.iterchildren(tags.delta)): + serial = int(delta.get("serial")) + print " Delta %3d serial %ds" % (i, serial) + if cur != serial - 1: + raise RuntimeError("Can't apply delta: current %s delta serial %s" % (cur, serial)) + for j, elt in enumerate(delta.iterchildren(tags.withdraw)): + uri = elt.get("uri") + hash = elt.get("hash") + print " %3d withdraw URI %s hash %s" % (j, uri, hash) + self.del_obj(uri, hash) + for j, elt in enumerate(delta.iterchildren(tags.publish)): + uri = elt.get("uri") + hash = elt.get("hash", None) + print " %3d publish URI %s hash %s" % (j, uri, hash) + if hash is not None: + self.del_obj(uri, hash) + self.add_obj(elt.get("uri"), elt.text.decode("base64")) + cur += 1 + self.set_serial(cur) if __name__ == "__main__": main() diff --git a/rpki/pubd.py b/rpki/pubd.py index 1849a0dd..21fd9298 100644 --- a/rpki/pubd.py +++ b/rpki/pubd.py @@ -428,9 +428,10 @@ class delta_obj(rpki.sql.sql_persistent): self.sql_mark_dirty() def publish(self, client, der, uri, hash): - if hash is not None: - self.withdraw(client, uri, hash) - elif object_obj.current_object_at_uri(client, self, uri) is not None: + obj = object_obj.current_object_at_uri(client, self, uri) + if obj is not None and obj.hash == hash: + obj.delete(self) + elif obj is not None: raise rpki.exceptions.ExistingObjectAtURI("Object already published at %s" % uri) logger.debug("Publishing %s", uri) object_obj.create(client, self, der, uri) |