diff options
Diffstat (limited to 'rpki')
-rw-r--r-- | rpki/left_right.py | 134 | ||||
-rw-r--r-- | rpki/publication_control.py | 20 | ||||
-rw-r--r-- | rpki/xml_utils.py | 75 |
3 files changed, 176 insertions, 53 deletions
diff --git a/rpki/left_right.py b/rpki/left_right.py index 55f893b8..c7517e26 100644 --- a/rpki/left_right.py +++ b/rpki/left_right.py @@ -23,6 +23,7 @@ RPKI "left-right" protocol. import base64 import logging +import collections import rpki.resource_set import rpki.x509 import rpki.sql @@ -179,10 +180,13 @@ class self_elt(data_elt): element_name = "self" attributes = ("action", "tag", "self_handle", "crl_interval", "regen_margin") - elements = ("bpki_cert", "bpki_glue") booleans = ("rekey", "reissue", "revoke", "run_now", "publish_world_now", "revoke_forgotten", "clear_replay_protection") + elements = collections.OrderedDict(( + ("bpki_cert", rpki.x509.X509), + ("bpki_glue", rpki.x509.X509))) + sql_template = rpki.sql.template( "self", "self_id", @@ -489,9 +493,13 @@ class bsc_elt(data_elt): element_name = "bsc" attributes = ("action", "tag", "self_handle", "bsc_handle", "key_type", "hash_alg", "key_length") - elements = ("signing_cert", "signing_cert_crl", "pkcs10_request") booleans = ("generate_keypair",) + elements = collections.OrderedDict(( + ("signing_cert", rpki.x509.X509), + ("signing_cert_crl", rpki.x509.CRL), + ("pkcs10_request", rpki.x509.PKCS10))) + sql_template = rpki.sql.template( "bsc", "bsc_id", @@ -557,9 +565,12 @@ class repository_elt(data_elt): element_name = "repository" attributes = ("action", "tag", "self_handle", "repository_handle", "bsc_handle", "peer_contact_uri") - elements = ("bpki_cert", "bpki_glue") booleans = ("clear_replay_protection",) + elements = collections.OrderedDict(( + ("bpki_cert", rpki.x509.X509), + ("bpki_glue", rpki.x509.X509))) + sql_template = rpki.sql.template( "repository", "repository_id", @@ -677,9 +688,12 @@ class parent_elt(data_elt): element_name = "parent" attributes = ("action", "tag", "self_handle", "parent_handle", "bsc_handle", "repository_handle", "peer_contact_uri", "sia_base", "sender_name", "recipient_name") - elements = ("bpki_cms_cert", "bpki_cms_glue") booleans = ("rekey", "reissue", "revoke", "revoke_forgotten", "clear_replay_protection") + elements = collections.OrderedDict(( + ("bpki_cms_cert", rpki.x509.X509), + ("bpki_cms_glue", rpki.x509.X509))) + sql_template = rpki.sql.template( "parent", "parent_id", @@ -969,9 +983,12 @@ class child_elt(data_elt): element_name = "child" attributes = ("action", "tag", "self_handle", "child_handle", "bsc_handle") - elements = ("bpki_cert", "bpki_glue") booleans = ("reissue", "clear_replay_protection") + elements = collections.OrderedDict(( + ("bpki_cert", rpki.x509.X509), + ("bpki_glue", rpki.x509.X509))) + sql_template = rpki.sql.template( "child", "child_id", @@ -1271,14 +1288,11 @@ class list_resources_elt(rpki.xml_utils.base_elt, left_right_namespace): def __repr__(self): return rpki.log.log_repr(self, self.self_handle, self.child_handle, self.asn, self.ipv4, self.ipv6) - def startElement(self, stack, name, attrs): + def fix_attribute_types(self): """ - Handle <list_resources/> element. This requires special handling - due to the data types of some of the attributes. + Fix data types for certain attributes. """ - assert name == "list_resources", "Unexpected name %s, stack %s" % (name, stack) - self.read_attrs(attrs) if isinstance(self.valid_until, str): self.valid_until = rpki.sundial.datetime.fromXMLtime(self.valid_until) if self.asn is not None: @@ -1288,6 +1302,27 @@ class list_resources_elt(rpki.xml_utils.base_elt, left_right_namespace): if self.ipv6 is not None: self.ipv6 = rpki.resource_set.resource_set_ipv6(self.ipv6) + def startElement(self, stack, name, attrs): + """ + Handle <list_resources/> element. This requires special handling + due to the data types of some of the attributes. + """ + + assert name == "list_resources", "Unexpected name %s, stack %s" % (name, stack) + self.read_attrs(attrs) + self.fix_attribute_types() + + @classmethod + def fromXML(cls, elt): + """ + Handle <list_resources/> element. This requires special handling + due to the data types of some of the attributes. + """ + + self = super(list_resources_elt, cls).fromXML(elt) + self.fix_attribute_types() + return self + def toXML(self): """ Generate <list_resources/> element. This requires special @@ -1307,6 +1342,16 @@ class list_roa_requests_elt(rpki.xml_utils.base_elt, left_right_namespace): element_name = "list_roa_requests" attributes = ("self_handle", "tag", "asn", "ipv4", "ipv6") + def fix_attribute_types(self): + """ + Fix data types for certain attributes. + """ + + if self.ipv4 is not None: + self.ipv4 = rpki.resource_set.roa_prefix_set_ipv4(self.ipv4) + if self.ipv6 is not None: + self.ipv6 = rpki.resource_set.roa_prefix_set_ipv6(self.ipv6) + def startElement(self, stack, name, attrs): """ Handle <list_roa_requests/> element. This requires special handling @@ -1315,10 +1360,18 @@ class list_roa_requests_elt(rpki.xml_utils.base_elt, left_right_namespace): assert name == "list_roa_requests", "Unexpected name %s, stack %s" % (name, stack) self.read_attrs(attrs) - if self.ipv4 is not None: - self.ipv4 = rpki.resource_set.roa_prefix_set_ipv4(self.ipv4) - if self.ipv6 is not None: - self.ipv6 = rpki.resource_set.roa_prefix_set_ipv6(self.ipv6) + self.fix_attribute_types() + + @classmethod + def fromXML(self, elt): + """ + Handle <list_roa_requests/> element. This requires special handling + due to the data types of some of the attributes. + """ + + self = super(list_roa_requests_elt, cls).fromXML(elt) + self.fix_attribute_types() + return self def __repr__(self): return rpki.log.log_repr(self, self.self_handle, self.asn, self.ipv4, self.ipv6) @@ -1344,7 +1397,9 @@ class list_ee_certificate_requests_elt(rpki.xml_utils.base_elt, left_right_names element_name = "list_ee_certificate_requests" attributes = ("self_handle", "tag", "gski", "valid_until", "asn", "ipv4", "ipv6", "cn", "sn", "eku") - elements = ("pkcs10",) + + elements = collections.OrderedDict(( + ("pkcs10", rpki.x509.PKCS10),)) pkcs10 = None valid_until = None @@ -1353,6 +1408,22 @@ class list_ee_certificate_requests_elt(rpki.xml_utils.base_elt, left_right_names def __repr__(self): return rpki.log.log_repr(self, self.self_handle, self.gski, self.cn, self.sn, self.asn, self.ipv4, self.ipv6) + def fix_attribute_types(self): + """ + Fix data types for certain attributes. + """ + + if isinstance(self.valid_until, str): + self.valid_until = rpki.sundial.datetime.fromXMLtime(self.valid_until) + if self.asn is not None: + self.asn = rpki.resource_set.resource_set_as(self.asn) + if self.ipv4 is not None: + self.ipv4 = rpki.resource_set.resource_set_ipv4(self.ipv4) + if self.ipv6 is not None: + self.ipv6 = rpki.resource_set.resource_set_ipv6(self.ipv6) + if self.eku is not None: + self.eku = self.eku.split(",") + def startElement(self, stack, name, attrs): """ Handle <list_ee_certificate_requests/> element. This requires special @@ -1362,16 +1433,18 @@ class list_ee_certificate_requests_elt(rpki.xml_utils.base_elt, left_right_names if name not in self.elements: assert name == self.element_name, "Unexpected name %s, stack %s" % (name, stack) self.read_attrs(attrs) - if isinstance(self.valid_until, str): - self.valid_until = rpki.sundial.datetime.fromXMLtime(self.valid_until) - if self.asn is not None: - self.asn = rpki.resource_set.resource_set_as(self.asn) - if self.ipv4 is not None: - self.ipv4 = rpki.resource_set.resource_set_ipv4(self.ipv4) - if self.ipv6 is not None: - self.ipv6 = rpki.resource_set.resource_set_ipv6(self.ipv6) - if self.eku is not None: - self.eku = self.eku.split(",") + self.fix_attribute_types() + + @classmethod + def fromXML(cls, elt): + """ + Handle <list_ee_certificate_requests/> element. This requires special + handling due to the data types of some of the attributes. + """ + + self = super(list_ee_certificate_requests_elt, cls).fromXML(elt) + self.fix_attribute_types() + return self def endElement(self, stack, name, text): """ @@ -1569,14 +1642,6 @@ class msg(rpki.xml_utils.msg, left_right_namespace): rpki.async.iterator(self, loop, done) -class sax_handler(rpki.xml_utils.sax_handler): - """ - SAX handler for Left-Right protocol. - """ - - pdu = msg - name = "msg" - version = rpki.relaxng.left_right.version class cms_msg(rpki.x509.XML_CMS_object): """ @@ -1585,7 +1650,8 @@ class cms_msg(rpki.x509.XML_CMS_object): encoding = "us-ascii" schema = rpki.relaxng.left_right - saxify = sax_handler.saxify + saxify = msg.fromXML + class cms_msg_no_sax(cms_msg): """ diff --git a/rpki/publication_control.py b/rpki/publication_control.py index 478f183b..b814346e 100644 --- a/rpki/publication_control.py +++ b/rpki/publication_control.py @@ -25,6 +25,7 @@ protocol itself. """ import logging +import collections import rpki.resource_set import rpki.x509 import rpki.sql @@ -87,9 +88,12 @@ class client_elt(rpki.xml_utils.data_elt, rpki.sql.sql_persistent, publication_c element_name = "client" attributes = ("action", "tag", "client_handle", "base_uri") - elements = ("bpki_cert", "bpki_glue") booleans = ("clear_replay_protection",) + elements = collections.OrderedDict(( + ("bpki_cert", rpki.x509.X509), + ("bpki_glue", rpki.x509.X509))) + sql_template = rpki.sql.template( "client", "client_id", @@ -239,16 +243,6 @@ class msg(rpki.xml_utils.msg, publication_control_namespace): return cb(r_msg) -class sax_handler(rpki.xml_utils.sax_handler): - """ - SAX handler for publication control protocol. - """ - - pdu = msg - name = "msg" - version = rpki.relaxng.publication_control.version - - class cms_msg(rpki.x509.XML_CMS_object): """ Class to hold a CMS-signed publication control PDU. @@ -256,9 +250,9 @@ class cms_msg(rpki.x509.XML_CMS_object): encoding = "us-ascii" schema = rpki.relaxng.publication_control - saxify = sax_handler.saxify - + saxify = msg.fromXML # Not really SAX anymore + class cms_msg_no_sax(cms_msg): """ Class to hold a CMS-signed publication control PDU without legacy diff --git a/rpki/xml_utils.py b/rpki/xml_utils.py index 9b443d0b..99377e17 100644 --- a/rpki/xml_utils.py +++ b/rpki/xml_utils.py @@ -56,6 +56,8 @@ class sax_handler(xml.sax.handler.ContentHandler): many XML namespace games. """ + # .pdu, .name, and .version are provided by subclass + def __init__(self): """ Initialize SAX handler. @@ -145,8 +147,7 @@ class sax_handler(xml.sax.handler.ContentHandler): class base_elt(object): """ Virtual base class for XML message elements. The left-right and - publication protocols use this. At least for now, the up-down - protocol does not, due to different design assumptions. + publication_control protocols use this. """ ## @var attributes @@ -161,6 +162,10 @@ class base_elt(object): # Boolean attributes (value "yes" or "no") for this element. booleans = () + ## @var text_attribute + # Name of class attribute that tells us where to put text values, if any. + text_attribute = None + def startElement(self, stack, name, attrs): """ Default startElement() handler: just process attributes. @@ -178,6 +183,47 @@ class base_elt(object): assert name == self.element_name, "Unexpected name %s, stack %s" % (name, stack) stack.pop() + @classmethod + def fromXML(cls, elt): + """ + First cut at non-SAX message unpacker. This will probably change. + """ + + logger.warning("base_elt(): Element %r (len %s)", elt, len(elt)) + + self = cls() + + for key in self.attributes: + val = elt.get(key, None) + if val is not None: + val = val.encode("ascii") + if val.isdigit() and not key.endswith("_handle"): + val = long(val) + setattr(self, key, val) + for key in self.booleans: + setattr(self, key, elt.get(key, False)) + + # This test could go in an extended method in text_elt. Then + # again, perhaps spreading the logic in as many places as we + # possibly can is not really helping matters.... + + if self.text_attribute is not None: + setattr(self, self.text_attribute, elt.text) + + # In the long run, we probably want the key for that to include + # the namespace, but that would break the current .toXML() code, + # so kludge it for now. + + for b64 in elt: + # XXX + logger.warning("base_elt(): XML tag %r, XML namespace %r", b64.tag, self.xmlns) + assert b64.tag.startswith(self.xmlns) + ename = b64.tag[len(self.xmlns):] + etype = self.elements[ename] + setattr(self, ename, etype(Base64 = b64.text)) + + return self + def toXML(self): """ Default toXML() element generator. @@ -246,10 +292,6 @@ class text_elt(base_elt): Virtual base class for XML message elements that contain text. """ - ## @var text_attribute - # Name of the class attribute that holds the text value. - text_attribute = None - def endElement(self, stack, name, text): """ Extract text from parsed XML. @@ -530,3 +572,24 @@ class msg(list): """ return self.type == "reply" + + @classmethod + def fromXML(cls, elt): + """ + First cut at non-SAX message unpacker. This will probably change. + """ + + assert cls.version == int(elt.get("version")) + self = cls() + self.type = elt.get("type") + + # This could be simplified by including the namespace name in the .pdus[] key. + + for sub in elt: + # XXX + logger.warning("msg(): XML tag %r, XML namespace %r", sub.tag, self.xmlns) + assert sub.tag.startswith(self.xmlns) + self.append(self.pdus[sub.tag[len(self.xmlns):]].fromXML(sub)) + + logger.warning("msg(): parsed %r", self) + return self |