diff options
-rw-r--r-- | rpki/left_right.py | 142 | ||||
-rw-r--r-- | rpki/rpkid.py | 162 | ||||
-rw-r--r-- | rpki/xml_utils.py | 91 |
3 files changed, 165 insertions, 230 deletions
diff --git a/rpki/left_right.py b/rpki/left_right.py index 09d8a5ca..653a2eaf 100644 --- a/rpki/left_right.py +++ b/rpki/left_right.py @@ -1373,100 +1373,6 @@ class list_ee_certificate_requests_elt(rpki.xml_utils.base_elt, left_right_names elt.set("valid_until", self.valid_until.toXMLtime()) return elt -class list_published_objects_elt(rpki.xml_utils.text_elt, left_right_namespace): - """ - <list_published_objects/> element. - """ - - element_name = "list_published_objects" - attributes = ("self_handle", "tag", "uri", "child_handle") - text_attribute = "obj" - - obj = None - child_handle = None - - def __repr__(self): - return rpki.log.log_repr(self, self.self_handle, self.child_handle, self.uri) - - def serve_dispatch(self, r_msg, cb, eb): - """ - Handle a <list_published_objects/> query. The method name is a - misnomer here, there's no action attribute and no dispatch, we - just dump every published object for the specified <self/> and return. - """ - - for parent in self_elt.serve_fetch_handle(self.gctx, None, self.self_handle).parents: - for ca in parent.cas: - ca_detail = ca.active_ca_detail - if ca_detail is not None: - r_msg.append(self.make_reply(ca_detail.crl_uri, ca_detail.latest_crl)) - r_msg.append(self.make_reply(ca_detail.manifest_uri, ca_detail.latest_manifest)) - r_msg.extend(self.make_reply(c.uri, c.cert, c.child.child_handle) - for c in ca_detail.child_certs) - r_msg.extend(self.make_reply(r.uri, r.roa) - for r in ca_detail.roas if r.roa is not None) - r_msg.extend(self.make_reply(g.uri, g.ghostbuster) - for g in ca_detail.ghostbusters) - r_msg.extend(self.make_reply(c.uri, c.cert) - for c in ca_detail.ee_certificates) - cb() - - def make_reply(self, uri, obj, child_handle = None): - """ - Generate one reply PDU. - """ - - r_pdu = self.make_pdu(tag = self.tag, self_handle = self.self_handle, - uri = uri, child_handle = child_handle) - r_pdu.obj = obj.get_Base64() - return r_pdu - -class list_received_resources_elt(rpki.xml_utils.base_elt, left_right_namespace): - """ - <list_received_resources/> element. - """ - - element_name = "list_received_resources" - attributes = ("self_handle", "tag", "parent_handle", - "notBefore", "notAfter", "uri", "sia_uri", "aia_uri", "asn", "ipv4", "ipv6") - - def __repr__(self): - return rpki.log.log_repr(self, self.self_handle, self.parent_handle, self.uri, self.notAfter) - - def serve_dispatch(self, r_msg, cb, eb): - """ - Handle a <list_received_resources/> query. The method name is a - misnomer here, there's no action attribute and no dispatch, we - just dump a bunch of data about every certificate issued to us by - one of our parents, then return. - """ - - for parent in self_elt.serve_fetch_handle(self.gctx, None, self.self_handle).parents: - for ca in parent.cas: - ca_detail = ca.active_ca_detail - if ca_detail is not None and ca_detail.latest_ca_cert is not None: - r_msg.append(self.make_reply(parent.parent_handle, ca_detail.ca_cert_uri, ca_detail.latest_ca_cert)) - cb() - - def make_reply(self, parent_handle, uri, cert): - """ - Generate one reply PDU. - """ - - resources = cert.get_3779resources() - return self.make_pdu( - tag = self.tag, - self_handle = self.self_handle, - parent_handle = parent_handle, - notBefore = str(cert.getNotBefore()), - notAfter = str(cert.getNotAfter()), - uri = uri, - sia_uri = cert.get_sia_directory_uri(), - aia_uri = cert.get_aia_uri(), - asn = resources.asn, - ipv4 = resources.v4, - ipv6 = resources.v6) - class report_error_elt(rpki.xml_utils.text_elt, left_right_namespace): """ <report_error/> element. @@ -1494,54 +1400,6 @@ class report_error_elt(rpki.xml_utils.text_elt, left_right_namespace): self.error_text = str(e) return self -class msg(rpki.xml_utils.msg, left_right_namespace): - """ - Left-right PDU. - """ - - ## @var version - # Protocol version - version = int(rpki.relaxng.left_right.version) - - ## @var pdus - # Dispatch table of PDUs for this protocol. - pdus = dict((x.element_name, x) - for x in (self_elt, child_elt, parent_elt, bsc_elt, - repository_elt, list_resources_elt, - list_roa_requests_elt, list_ghostbuster_requests_elt, - list_ee_certificate_requests_elt, - list_published_objects_elt, - list_received_resources_elt, report_error_elt)) - - def serve_top_level(self, gctx, cb): - """ - Serve one msg PDU. - """ - - r_msg = self.__class__.reply() - - def loop(iterator, q_pdu): - - def fail(e): - if not isinstance(e, rpki.exceptions.NotFound): - logger.exception("Unhandled exception serving left-right PDU %r", q_pdu) - r_msg.append(report_error_elt.from_exception( - e, self_handle = q_pdu.self_handle, tag = q_pdu.tag)) - cb(r_msg) - - try: - q_pdu.gctx = gctx - q_pdu.serve_dispatch(r_msg, iterator, fail) - except (rpki.async.ExitNow, SystemExit): - raise - except Exception, e: - fail(e) - - def done(): - cb(r_msg) - - rpki.async.iterator(self, loop, done) - class cms_msg(rpki.x509.XML_CMS_object): """ diff --git a/rpki/rpkid.py b/rpki/rpkid.py index f6f8b98f..ce7368df 100644 --- a/rpki/rpkid.py +++ b/rpki/rpkid.py @@ -42,7 +42,7 @@ import rpki.async import rpki.daemonize import rpki.rpkid_tasks -from lxml.etree import Element, SubElement +from lxml.etree import Element, SubElement, tostring as ElementToString logger = logging.getLogger(__name__) @@ -104,6 +104,12 @@ class main(object): if self.profile: logger.info("Running in profile mode with output to %s", self.profile) + import django + django.setup() + + global rpki + import rpki.rpkidb + self.sql = rpki.sql.session(self.cfg) self.bpki_ta = rpki.x509.X509(Auto_update = self.cfg.get("bpki-ta")) @@ -273,10 +279,92 @@ class main(object): return self._left_right_trivial_handlers except AttributeError: self._left_right_trivial_handlers = { - tag_list_published_objects : self.handle_list_published_objects, - tag_list_received_resources : self.handle_list_received_resources } + rpki.left_right.tag_list_published_objects : self.handle_list_published_objects, + rpki.left_right.tag_list_received_resources : self.handle_list_received_resources } return self._left_right_trivial_handlers + def handle_list_published_objects(self, q_pdu, r_msg): + """ + <list_published_objects/> server. + + This is written for the old SQL API, will need rewriting once we + switch rpkid to Django ORM. + """ + + logger.debug(".handle_list_published_objects() %s", ElementToString(q_pdu)) + + self_handle = q_pdu.get("self_handle") + msg_tag = q_pdu.get("tag") + + kw = dict(self_handle = self_handle) + if msg_tag is not None: + kw.update(tag = msg_tag) + + for parent in rpki.left_right.self_elt.serve_fetch_handle(self, None, self_handle).parents: + for ca in parent.cas: + ca_detail = ca.active_ca_detail + if ca_detail is not None: + + SubElement(r_msg, rpki.left_right.tag_list_published_objects, + uri = ca_detail.crl_uri, **kw).text = ca_detail.latest_crl.get_Base64() + + SubElement(r_msg, rpki.left_right.tag_list_published_objects, + uri = ca_detail.manifest_uri, **kw).text = ca_detail.latest_manifest.get_Base64() + + for c in ca_detail.child_certs: + SubElement(r_msg, rpki.left_right.tag_list_published_objects, + uri = c.uri, child_handle = c.child.child_handle, **kw).text = c.cert.get_Base64() + + for r in ca_detail.roas: + if r.roa is not None: + SubElement(r_msg, rpki.left_right.tag_list_published_objects, + uri = r.uri, **kw).text = r.roa.get_Base64() + + for g in ca_detail.ghostbusters: + SubElement(r_msg, rpki.left_right.tag_list_published_objects, + uri = g.uri, **kw).text = g.ghostbuster.get_Base64() + + for c in ca_detail.ee_certificates: + SubElement(r_msg, rpki.left_right.tag_list_published_objects, + uri = c.uri, **kw).text = c.cert.get_Base64() + + def handle_list_received_resources(self, q_pdu, r_msg): + """ + <list_received_resources/> server. + + This is written for the old SQL API, will need rewriting once we + switch rpkid to Django ORM. + """ + + logger.debug(".handle_list_received_resources() %s", ElementToString(q_pdu)) + + self_handle = q_pdu.get("self_handle") + msg_tag = q_pdu.get("tag") + + for parent in rpki.left_right.self_elt.serve_fetch_handle(self, None, self_handle).parents: + for ca in parent.cas: + ca_detail = ca.active_ca_detail + if ca_detail is not None and ca_detail.latest_ca_cert is not None: + + cert = ca_detail.latest_ca_cert + resources = cert.get_3779resources() + + r_pdu = SubElement(r_msg, rpki.left_right.tag_list_received_resources, + self_handle = self_handle, + parent_handle = parent.parent_handle, + uri = ca_detail.ca_cert_uri, + notBefore = str(cert.getNotBefore()), + notAfter = str(cert.getNotAfter()), + sia_uri = cert.get_sia_directory_uri(), + aia_uri = cert.get_aia_uri(), + asn = str(resources.asn), + ipv4 = str(resources.v4), + ipv6 = str(resources.v6)) + + if msg_tag is not None: + r_pdu.set("tag", msg_tag) + + def left_right_handler(self, query, path, cb): """ Process one left-right PDU. @@ -289,25 +377,71 @@ class main(object): # probably just become calls to ordinary methods of this # (rpki.rpkid.main) class. # - # Merge rpki.left_right.msg.serve_top_level() into this method, - # along with a generalization of rpki.pubd.main.control_handler(). - - def done(r_msg): - r_msg = r_msg.toXML() - reply = rpki.left_right.cms_msg().wrap(r_msg, self.rpkid_key, self.rpkid_cert) - self.sql.sweep() - cb(200, body = reply) + # Need to clone logic from rpki.pubd.main.control_handler(). try: q_cms = rpki.left_right.cms_msg(DER = query) q_msg = q_cms.unwrap((self.bpki_ta, self.irbe_cert)) - q_msg = rpki.left_right.msg.fromXML(q_msg) + r_msg = Element(rpki.left_right.tag_msg, nsmap = rpki.left_right.nsmap, + type = "reply", version = rpki.left_right.version) self.irbe_cms_timestamp = q_cms.check_replay(self.irbe_cms_timestamp, path) - if not q_msg.is_query(): + + assert q_msg.tag.startswith(rpki.left_right.xmlns) and all(q_pdu.tag.startswith(rpki.left_right.xmlns) for q_pdu in q_msg) + + if q_msg.get("version") != rpki.left_right.version: + raise rpki.exceptions.BadQuery("Unrecognized protocol version") + + if q_msg.get("type") != "query": raise rpki.exceptions.BadQuery("Message type is not query") - q_msg.serve_top_level(self, done) + + def done(): + self.sql.sweep() + cb(200, body = rpki.left_right.cms_msg().wrap(r_msg, self.rpkid_key, self.rpkid_cert)) + + def loop(iterator, q_pdu): + + def fail(e): + if not isinstance(e, rpki.exceptions.NotFound): + logger.exception("Unhandled exception serving left-right PDU %r", q_pdu) + + # Compatability kludge + if isinstance(q_pdu, rpki.left_right.data_elt): + r_msg.append(rpki.left_right.report_error_elt.from_exception( + e, self_handle = q_pdu.self_handle, tag = q_pdu.tag).toXML()) + else: + r_pdu = rpki.left_right.report_error_elt.from_exception(e, self_handle = q_pdu.get("self_handle")) + tag = q_pdu.get("tag") + if tag: + r_pdu.set("tag", tag) + r_msg.append(r_pdu.toXML()) + + self.sql.sweep() + + cb(200, body = rpki.left_right.cms_msg().wrap(r_msg, self.rpkid_key, self.rpkid_cert)) + + try: + if q_pdu.tag in self.left_right_trivial_handlers: + self.left_right_trivial_handlers[q_pdu.tag](q_pdu, r_msg) + iterator() + else: + q_map = { rpki.left_right.tag_self : rpki.left_right.self_elt, + rpki.left_right.tag_bsc : rpki.left_right.bsc_elt, + rpki.left_right.tag_parent : rpki.left_right.parent_elt, + rpki.left_right.tag_child : rpki.left_right.child_elt, + rpki.left_right.tag_repository : rpki.left_right.repository_elt } + q_pdu = q_map[q_pdu.tag].fromXML(q_pdu) + q_pdu.gctx = self + q_pdu.serve_dispatch(r_msg, iterator, fail) + except (rpki.async.ExitNow, SystemExit): + raise + except Exception, e: + fail(e) + + rpki.async.iterator(q_msg, loop, done) + except (rpki.async.ExitNow, SystemExit): raise + except Exception, e: logger.exception("Unhandled exception serving left-right request") cb(500, reason = "Unhandled exception %s: %s" % (e.__class__.__name__, e)) diff --git a/rpki/xml_utils.py b/rpki/xml_utils.py index b85a60c5..da907a0d 100644 --- a/rpki/xml_utils.py +++ b/rpki/xml_utils.py @@ -326,10 +326,26 @@ class data_elt(base_elt): Action dispatch handler. """ + # Transition hack: handle the .toXML() call for old handlers. + + fake_r_msg = [] + + def fake_convert(): + r_msg.extend(r_pdu.toXML() if isinstance(r_pdu, base_elt) else r_pdu + for r_pdu in fake_r_msg) + + def fake_cb(): + fake_convert() + cb() + + def fake_eb(e): + fake_convert() + eb(e) + method = getattr(self, "serve_" + self.action, None) if method is None: raise rpki.exceptions.BadQuery("Unexpected query: action %s" % self.action) - method(r_msg, cb, eb) + method(fake_r_msg, fake_cb, fake_eb) def unimplemented_control(self, *controls): """ @@ -339,76 +355,3 @@ class data_elt(base_elt): unimplemented = [x for x in controls if getattr(self, x, False)] if unimplemented: raise rpki.exceptions.NotImplementedYet("Unimplemented control %s" % ", ".join(unimplemented)) - -class msg(list): - """ - Generic top-level PDU. - """ - - def __str__(self): - """ - Convert msg object to string. - """ - - return lxml.etree.tostring(self.toXML(), pretty_print = True, encoding = "us-ascii") - - def toXML(self): - """ - Generate top-level PDU. - """ - - elt = lxml.etree.Element(self.xmlns + "msg", nsmap = self.nsmap, version = str(self.version), type = self.type) - elt.extend(i.toXML() for i in self) - return elt - - @classmethod - def query(cls, *args): - """ - Create a query PDU. - """ - - self = cls(args) - self.type = "query" - return self - - @classmethod - def reply(cls, *args): - """ - Create a reply PDU. - """ - - self = cls(args) - self.type = "reply" - return self - - def is_query(self): - """ - Is this msg a query? - """ - - return self.type == "query" - - def is_reply(self): - """ - Is this msg a reply? - """ - - 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: - assert sub.tag.startswith(self.xmlns) - self.append(self.pdus[sub.tag[len(self.xmlns):]].fromXML(sub)) - - return self |