00001 """
00002 RPKI "up-down" protocol.
00003
00004 $Id: up_down.py 3449 2010-09-16 21:30:30Z sra $
00005
00006 Copyright (C) 2009--2010 Internet Systems Consortium ("ISC")
00007
00008 Permission to use, copy, modify, and distribute this software for any
00009 purpose with or without fee is hereby granted, provided that the above
00010 copyright notice and this permission notice appear in all copies.
00011
00012 THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
00013 REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
00014 AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
00015 INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
00016 LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
00017 OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
00018 PERFORMANCE OF THIS SOFTWARE.
00019
00020 Portions copyright (C) 2007--2008 American Registry for Internet Numbers ("ARIN")
00021
00022 Permission to use, copy, modify, and distribute this software for any
00023 purpose with or without fee is hereby granted, provided that the above
00024 copyright notice and this permission notice appear in all copies.
00025
00026 THE SOFTWARE IS PROVIDED "AS IS" AND ARIN DISCLAIMS ALL WARRANTIES WITH
00027 REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
00028 AND FITNESS. IN NO EVENT SHALL ARIN BE LIABLE FOR ANY SPECIAL, DIRECT,
00029 INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
00030 LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
00031 OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
00032 PERFORMANCE OF THIS SOFTWARE.
00033 """
00034
00035 import base64, lxml.etree
00036 import rpki.resource_set, rpki.x509, rpki.exceptions, rpki.log
00037 import rpki.xml_utils, rpki.relaxng
00038
00039 xmlns = "http://www.apnic.net/specs/rescerts/up-down/"
00040
00041 nsmap = { None : xmlns }
00042
00043 class base_elt(object):
00044 """
00045 Generic PDU object.
00046
00047 Virtual class, just provides some default methods.
00048 """
00049
00050 def startElement(self, stack, name, attrs):
00051 """
00052 Ignore startElement() if there's no specific handler.
00053
00054 Some elements have no attributes and we only care about their
00055 text content.
00056 """
00057 pass
00058
00059 def endElement(self, stack, name, text):
00060 """
00061 Ignore endElement() if there's no specific handler.
00062
00063 If we don't need to do anything else, just pop the stack.
00064 """
00065 stack.pop()
00066
00067 def make_elt(self, name, *attrs):
00068 """
00069 Construct a element, copying over a set of attributes.
00070 """
00071 elt = lxml.etree.Element("{%s}%s" % (xmlns, name), nsmap=nsmap)
00072 for key in attrs:
00073 val = getattr(self, key, None)
00074 if val is not None:
00075 elt.set(key, str(val))
00076 return elt
00077
00078 def make_b64elt(self, elt, name, value):
00079 """
00080 Construct a sub-element with Base64 text content.
00081 """
00082 if value is not None and not value.empty():
00083 lxml.etree.SubElement(elt, "{%s}%s" % (xmlns, name), nsmap=nsmap).text = value.get_Base64()
00084
00085 def serve_pdu(self, q_msg, r_msg, child, callback, errback):
00086 """Default PDU handler to catch unexpected types."""
00087 raise rpki.exceptions.BadQuery, "Unexpected query type %s" % q_msg.type
00088
00089 def check_response(self):
00090 """Placeholder for response checking."""
00091 pass
00092
00093 class multi_uri(list):
00094 """
00095 Container for a set of URIs.
00096 """
00097
00098 def __init__(self, ini):
00099 """
00100 Initialize a set of URIs, which includes basic some syntax checking.
00101 """
00102 list.__init__(self)
00103 if isinstance(ini, (list, tuple)):
00104 self[:] = ini
00105 elif isinstance(ini, str):
00106 self[:] = ini.split(",")
00107 for s in self:
00108 if s.strip() != s or "://" not in s:
00109 raise rpki.exceptions.BadURISyntax, "Bad URI \"%s\"" % s
00110 else:
00111 raise TypeError
00112
00113 def __str__(self):
00114 """Convert a multi_uri back to a string representation."""
00115 return ",".join(self)
00116
00117 def rsync(self):
00118 """
00119 Find first rsync://... URI in self.
00120 """
00121 for s in self:
00122 if s.startswith("rsync://"):
00123 return s
00124 return None
00125
00126 class certificate_elt(base_elt):
00127 """
00128 Up-Down protocol representation of an issued certificate.
00129 """
00130
00131 def startElement(self, stack, name, attrs):
00132 """
00133 Handle attributes of <certificate/> element.
00134 """
00135 assert name == "certificate", "Unexpected name %s, stack %s" % (name, stack)
00136 self.cert_url = multi_uri(attrs["cert_url"])
00137 self.req_resource_set_as = rpki.resource_set.resource_set_as(attrs.get("req_resource_set_as"))
00138 self.req_resource_set_ipv4 = rpki.resource_set.resource_set_ipv4(attrs.get("req_resource_set_ipv4"))
00139 self.req_resource_set_ipv6 = rpki.resource_set.resource_set_ipv6(attrs.get("req_resource_set_ipv6"))
00140
00141 def endElement(self, stack, name, text):
00142 """
00143 Handle text content of a <certificate/> element.
00144 """
00145 assert name == "certificate", "Unexpected name %s, stack %s" % (name, stack)
00146 self.cert = rpki.x509.X509(Base64 = text)
00147 stack.pop()
00148
00149 def toXML(self):
00150 """
00151 Generate a <certificate/> element.
00152 """
00153 elt = self.make_elt("certificate", "cert_url",
00154 "req_resource_set_as", "req_resource_set_ipv4", "req_resource_set_ipv6")
00155 elt.text = self.cert.get_Base64()
00156 return elt
00157
00158 class class_elt(base_elt):
00159 """
00160 Up-Down protocol representation of a resource class.
00161 """
00162
00163 issuer = None
00164
00165 def __init__(self):
00166 """Initialize class_elt."""
00167 base_elt.__init__(self)
00168 self.certs = []
00169
00170 def startElement(self, stack, name, attrs):
00171 """
00172 Handle <class/> elements and their children.
00173 """
00174 if name == "certificate":
00175 cert = certificate_elt()
00176 self.certs.append(cert)
00177 stack.append(cert)
00178 cert.startElement(stack, name, attrs)
00179 elif name != "issuer":
00180 assert name == "class", "Unexpected name %s, stack %s" % (name, stack)
00181 self.class_name = attrs["class_name"]
00182 self.cert_url = multi_uri(attrs["cert_url"])
00183 self.suggested_sia_head = attrs.get("suggested_sia_head")
00184 self.resource_set_as = rpki.resource_set.resource_set_as(attrs["resource_set_as"])
00185 self.resource_set_ipv4 = rpki.resource_set.resource_set_ipv4(attrs["resource_set_ipv4"])
00186 self.resource_set_ipv6 = rpki.resource_set.resource_set_ipv6(attrs["resource_set_ipv6"])
00187 self.resource_set_notafter = rpki.sundial.datetime.fromXMLtime(attrs.get("resource_set_notafter"))
00188
00189 def endElement(self, stack, name, text):
00190 """
00191 Handle <class/> elements and their children.
00192 """
00193 if name == "issuer":
00194 self.issuer = rpki.x509.X509(Base64 = text)
00195 else:
00196 assert name == "class", "Unexpected name %s, stack %s" % (name, stack)
00197 stack.pop()
00198
00199 def toXML(self):
00200 """
00201 Generate a <class/> element.
00202 """
00203 elt = self.make_elt("class", "class_name", "cert_url", "resource_set_as",
00204 "resource_set_ipv4", "resource_set_ipv6",
00205 "resource_set_notafter", "suggested_sia_head")
00206 elt.extend([i.toXML() for i in self.certs])
00207 self.make_b64elt(elt, "issuer", self.issuer)
00208 return elt
00209
00210 def to_resource_bag(self):
00211 """
00212 Build a resource_bag from from this <class/> element.
00213 """
00214 return rpki.resource_set.resource_bag(self.resource_set_as,
00215 self.resource_set_ipv4,
00216 self.resource_set_ipv6,
00217 self.resource_set_notafter)
00218
00219 def from_resource_bag(self, bag):
00220 """
00221 Set resources of this class element from a resource_bag.
00222 """
00223 self.resource_set_as = bag.asn
00224 self.resource_set_ipv4 = bag.v4
00225 self.resource_set_ipv6 = bag.v6
00226 self.resource_set_notafter = bag.valid_until
00227
00228 class list_pdu(base_elt):
00229 """
00230 Up-Down protocol "list" PDU.
00231 """
00232
00233 def toXML(self):
00234 """Generate (empty) payload of "list" PDU."""
00235 return []
00236
00237 def serve_pdu(self, q_msg, r_msg, child, callback, errback):
00238 """
00239 Serve one "list" PDU.
00240 """
00241
00242 def handle(irdb_resources):
00243
00244 r_msg.payload = list_response_pdu()
00245
00246 for parent in child.parents():
00247 for ca in parent.cas():
00248 ca_detail = ca.fetch_active()
00249 if not ca_detail:
00250 continue
00251 resources = ca_detail.latest_ca_cert.get_3779resources().intersection(irdb_resources)
00252 if resources.empty():
00253 continue
00254 rc = class_elt()
00255 rc.class_name = str(ca.ca_id)
00256 rc.cert_url = multi_uri(ca_detail.ca_cert_uri)
00257 rc.from_resource_bag(resources)
00258 for child_cert in child.child_certs(ca_detail = ca_detail):
00259 c = certificate_elt()
00260 c.cert_url = multi_uri(child_cert.uri(ca))
00261 c.cert = child_cert.cert
00262 rc.certs.append(c)
00263 rc.issuer = ca_detail.latest_ca_cert
00264 r_msg.payload.classes.append(rc)
00265 callback()
00266
00267 self.gctx.irdb_query_child_resources(child.self().self_handle, child.child_handle, handle, errback)
00268
00269 @classmethod
00270 def query(cls, parent, cb, eb):
00271 """
00272 Send a "list" query to parent.
00273 """
00274 try:
00275 rpki.log.info('Sending "list" request to parent %s' % parent.parent_handle)
00276 parent.query_up_down(cls(), cb, eb)
00277 except (rpki.async.ExitNow, SystemExit):
00278 raise
00279 except Exception, e:
00280 eb(e)
00281
00282 class class_response_syntax(base_elt):
00283 """
00284 Syntax for Up-Down protocol "list_response" and "issue_response" PDUs.
00285 """
00286
00287 def __init__(self):
00288 """
00289 Initialize class_response_syntax.
00290 """
00291 base_elt.__init__(self)
00292 self.classes = []
00293
00294 def startElement(self, stack, name, attrs):
00295 """
00296 Handle "list_response" and "issue_response" PDUs.
00297 """
00298 assert name == "class", "Unexpected name %s, stack %s" % (name, stack)
00299 c = class_elt()
00300 self.classes.append(c)
00301 stack.append(c)
00302 c.startElement(stack, name, attrs)
00303
00304 def toXML(self):
00305 """Generate payload of "list_response" and "issue_response" PDUs."""
00306 return [c.toXML() for c in self.classes]
00307
00308 class list_response_pdu(class_response_syntax):
00309 """
00310 Up-Down protocol "list_response" PDU.
00311 """
00312 pass
00313
00314 class issue_pdu(base_elt):
00315 """
00316 Up-Down protocol "issue" PDU.
00317 """
00318
00319 def startElement(self, stack, name, attrs):
00320 """
00321 Handle "issue" PDU.
00322 """
00323 assert name == "request", "Unexpected name %s, stack %s" % (name, stack)
00324 self.class_name = attrs["class_name"]
00325 self.req_resource_set_as = rpki.resource_set.resource_set_as(attrs.get("req_resource_set_as"))
00326 self.req_resource_set_ipv4 = rpki.resource_set.resource_set_ipv4(attrs.get("req_resource_set_ipv4"))
00327 self.req_resource_set_ipv6 = rpki.resource_set.resource_set_ipv6(attrs.get("req_resource_set_ipv6"))
00328
00329 def endElement(self, stack, name, text):
00330 """
00331 Handle "issue" PDU.
00332 """
00333 assert name == "request", "Unexpected name %s, stack %s" % (name, stack)
00334 self.pkcs10 = rpki.x509.PKCS10(Base64 = text)
00335 stack.pop()
00336
00337 def toXML(self):
00338 """
00339 Generate payload of "issue" PDU.
00340 """
00341 elt = self.make_elt("request", "class_name", "req_resource_set_as",
00342 "req_resource_set_ipv4", "req_resource_set_ipv6")
00343 elt.text = self.pkcs10.get_Base64()
00344 return [elt]
00345
00346 def serve_pdu(self, q_msg, r_msg, child, callback, errback):
00347 """
00348 Serve one issue request PDU.
00349 """
00350
00351
00352
00353
00354 if self.req_resource_set_as or \
00355 self.req_resource_set_ipv4 or \
00356 self.req_resource_set_ipv6:
00357 raise rpki.exceptions.NotImplementedYet, "req_* attributes not implemented yet, sorry"
00358
00359
00360 self.pkcs10.check_valid_rpki()
00361 ca = child.ca_from_class_name(self.class_name)
00362 ca_detail = ca.fetch_active()
00363 if ca_detail is None:
00364 raise rpki.exceptions.NoActiveCA, "No active CA for class %r" % self.class_name
00365
00366
00367
00368 def got_resources(irdb_resources):
00369
00370 resources = irdb_resources.intersection(ca_detail.latest_ca_cert.get_3779resources())
00371 req_key = self.pkcs10.getPublicKey()
00372 req_sia = self.pkcs10.get_SIA()
00373 child_cert = child.child_certs(ca_detail = ca_detail, ski = req_key.get_SKI(), unique = True)
00374
00375
00376
00377 publisher = rpki.rpki_engine.publication_queue()
00378
00379 if child_cert is None:
00380 child_cert = ca_detail.issue(
00381 ca = ca,
00382 child = child,
00383 subject_key = req_key,
00384 sia = req_sia,
00385 resources = resources,
00386 publisher = publisher)
00387 else:
00388 child_cert = child_cert.reissue(
00389 ca_detail = ca_detail,
00390 sia = req_sia,
00391 resources = resources,
00392 publisher = publisher)
00393
00394 def done():
00395 c = certificate_elt()
00396 c.cert_url = multi_uri(child_cert.uri(ca))
00397 c.cert = child_cert.cert
00398 rc = class_elt()
00399 rc.class_name = self.class_name
00400 rc.cert_url = multi_uri(ca_detail.ca_cert_uri)
00401 rc.from_resource_bag(resources)
00402 rc.certs.append(c)
00403 rc.issuer = ca_detail.latest_ca_cert
00404 r_msg.payload = issue_response_pdu()
00405 r_msg.payload.classes.append(rc)
00406 callback()
00407
00408 self.gctx.sql.sweep()
00409 assert child_cert and child_cert.sql_in_db
00410 publisher.call_pubd(done, errback)
00411
00412 self.gctx.irdb_query_child_resources(child.self().self_handle, child.child_handle, got_resources, errback)
00413
00414 @classmethod
00415 def query(cls, parent, ca, ca_detail, callback, errback):
00416 """
00417 Send an "issue" request to parent associated with ca.
00418 """
00419 assert ca_detail is not None and ca_detail.state in ("pending", "active")
00420 sia = ((rpki.oids.name2oid["id-ad-caRepository"], ("uri", ca.sia_uri)),
00421 (rpki.oids.name2oid["id-ad-rpkiManifest"], ("uri", ca_detail.manifest_uri(ca))))
00422 self = cls()
00423 self.class_name = ca.parent_resource_class
00424 self.pkcs10 = rpki.x509.PKCS10.create_ca(ca_detail.private_key_id, sia)
00425 rpki.log.info('Sending "issue" request to parent %s' % parent.parent_handle)
00426 parent.query_up_down(self, callback, errback)
00427
00428 class issue_response_pdu(class_response_syntax):
00429 """
00430 Up-Down protocol "issue_response" PDU.
00431 """
00432
00433 def check_response(self):
00434 """
00435 Check whether this looks like a reasonable issue_response PDU.
00436 XML schema should be tighter for this response.
00437 """
00438 if len(self.classes) != 1 or len(self.classes[0].certs) != 1:
00439 raise rpki.exceptions.BadIssueResponse
00440
00441 class revoke_syntax(base_elt):
00442 """
00443 Syntax for Up-Down protocol "revoke" and "revoke_response" PDUs.
00444 """
00445
00446 def startElement(self, stack, name, attrs):
00447 """Handle "revoke" PDU."""
00448 self.class_name = attrs["class_name"]
00449 self.ski = attrs["ski"]
00450
00451 def toXML(self):
00452 """Generate payload of "revoke" PDU."""
00453 return [self.make_elt("key", "class_name", "ski")]
00454
00455 class revoke_pdu(revoke_syntax):
00456 """
00457 Up-Down protocol "revoke" PDU.
00458 """
00459
00460 def get_SKI(self):
00461 """Convert g(SKI) encoding from PDU back to raw SKI."""
00462 return base64.urlsafe_b64decode(self.ski + "=")
00463
00464 def serve_pdu(self, q_msg, r_msg, child, cb, eb):
00465 """
00466 Serve one revoke request PDU.
00467 """
00468
00469 def done():
00470 r_msg.payload = revoke_response_pdu()
00471 r_msg.payload.class_name = self.class_name
00472 r_msg.payload.ski = self.ski
00473 cb()
00474
00475 ca = child.ca_from_class_name(self.class_name)
00476 publisher = rpki.rpki_engine.publication_queue()
00477 for ca_detail in ca.ca_details():
00478 for child_cert in child.child_certs(ca_detail = ca_detail, ski = self.get_SKI()):
00479 child_cert.revoke(publisher = publisher)
00480 self.gctx.sql.sweep()
00481 publisher.call_pubd(done, eb)
00482
00483 @classmethod
00484 def query(cls, ca, gski, cb, eb):
00485 """
00486 Send a "revoke" request for certificate(s) named by gski to parent associated with ca.
00487 """
00488 parent = ca.parent()
00489 self = cls()
00490 self.class_name = ca.parent_resource_class
00491 self.ski = gski
00492 rpki.log.info('Sending "revoke" request for SKI %s to parent %s' % (gski, parent.parent_handle))
00493 parent.query_up_down(self, cb, eb)
00494
00495 class revoke_response_pdu(revoke_syntax):
00496 """
00497 Up-Down protocol "revoke_response" PDU.
00498 """
00499
00500 pass
00501
00502 class error_response_pdu(base_elt):
00503 """
00504 Up-Down protocol "error_response" PDU.
00505 """
00506
00507 codes = {
00508 1101 : "Already processing request",
00509 1102 : "Version number error",
00510 1103 : "Unrecognised request type",
00511 1201 : "Request - no such resource class",
00512 1202 : "Request - no resources allocated in resource class",
00513 1203 : "Request - badly formed certificate request",
00514 1301 : "Revoke - no such resource class",
00515 1302 : "Revoke - no such key",
00516 2001 : "Internal Server Error - Request not performed" }
00517
00518 exceptions = {
00519 rpki.exceptions.NoActiveCA : 1202 }
00520
00521 def __init__(self, exception = None):
00522 """
00523 Initialize an error_response PDU from an exception object.
00524 """
00525 base_elt.__init__(self)
00526 if exception is not None:
00527 self.status = self.exceptions.get(type(exception), 2001)
00528 self.description = str(exception)
00529
00530 def endElement(self, stack, name, text):
00531 """
00532 Handle "error_response" PDU.
00533 """
00534 if name == "status":
00535 code = int(text)
00536 if code not in self.codes:
00537 raise rpki.exceptions.BadStatusCode, "%s is not a known status code" % code
00538 self.status = code
00539 elif name == "description":
00540 self.description = text
00541 else:
00542 assert name == "message", "Unexpected name %s, stack %s" % (name, stack)
00543 stack.pop()
00544 stack[-1].endElement(stack, name, text)
00545
00546 def toXML(self):
00547 """
00548 Generate payload of "error_response" PDU.
00549 """
00550 assert self.status in self.codes
00551 elt = self.make_elt("status")
00552 elt.text = str(self.status)
00553 payload = [elt]
00554 if self.description:
00555 elt = self.make_elt("description")
00556 elt.text = str(self.description)
00557 elt.set("{http://www.w3.org/XML/1998/namespace}lang", "en-US")
00558 payload.append(elt)
00559 return payload
00560
00561 def check_response(self):
00562 """
00563 Handle an error response. For now, just raise an exception,
00564 perhaps figure out something more clever to do later.
00565 """
00566 raise rpki.exceptions.UpstreamError, self.codes[self.status]
00567
00568 class message_pdu(base_elt):
00569 """
00570 Up-Down protocol message wrapper PDU.
00571 """
00572
00573 version = 1
00574
00575 name2type = {
00576 "list" : list_pdu,
00577 "list_response" : list_response_pdu,
00578 "issue" : issue_pdu,
00579 "issue_response" : issue_response_pdu,
00580 "revoke" : revoke_pdu,
00581 "revoke_response" : revoke_response_pdu,
00582 "error_response" : error_response_pdu }
00583
00584 type2name = dict((v, k) for k, v in name2type.items())
00585
00586 def toXML(self):
00587 """
00588 Generate payload of message PDU.
00589 """
00590 elt = self.make_elt("message", "version", "sender", "recipient", "type")
00591 elt.extend(self.payload.toXML())
00592 return elt
00593
00594 def startElement(self, stack, name, attrs):
00595 """
00596 Handle message PDU.
00597
00598 Payload of the <message/> element varies depending on the "type"
00599 attribute, so after some basic checks we have to instantiate the
00600 right class object to handle whatever kind of PDU this is.
00601 """
00602 assert name == "message", "Unexpected name %s, stack %s" % (name, stack)
00603 assert self.version == int(attrs["version"])
00604 self.sender = attrs["sender"]
00605 self.recipient = attrs["recipient"]
00606 self.type = attrs["type"]
00607 self.payload = self.name2type[attrs["type"]]()
00608 stack.append(self.payload)
00609
00610 def __str__(self):
00611 """Convert a message PDU to a string."""
00612 lxml.etree.tostring(self.toXML(), pretty_print = True, encoding = "UTF-8")
00613
00614 def serve_top_level(self, child, callback):
00615 """
00616 Serve one message request PDU.
00617 """
00618
00619 r_msg = message_pdu()
00620 r_msg.sender = self.recipient
00621 r_msg.recipient = self.sender
00622
00623 def done():
00624 r_msg.type = self.type2name[type(r_msg.payload)]
00625 callback(r_msg)
00626
00627 def lose(e):
00628 rpki.log.traceback()
00629 callback(self.serve_error(e))
00630
00631 try:
00632 self.log_query(child)
00633 self.payload.serve_pdu(self, r_msg, child, done, lose)
00634 except (rpki.async.ExitNow, SystemExit):
00635 raise
00636 except Exception, e:
00637 lose(e)
00638
00639 def log_query(self, child):
00640 """
00641 Log query we're handling. Separate method so rootd can override.
00642 """
00643 rpki.log.info("Serving %s query from child %s [sender %s, recipient %s]" % (self.type, child.child_handle, self.sender, self.recipient))
00644
00645 def serve_error(self, exception):
00646 """
00647 Generate an error_response message PDU.
00648 """
00649 r_msg = message_pdu()
00650 r_msg.sender = self.recipient
00651 r_msg.recipient = self.sender
00652 r_msg.payload = error_response_pdu(exception)
00653 r_msg.type = self.type2name[type(r_msg.payload)]
00654 return r_msg
00655
00656 @classmethod
00657 def make_query(cls, payload, sender, recipient):
00658 """
00659 Construct one message PDU.
00660 """
00661 assert not cls.type2name[type(payload)].endswith("_response")
00662 if sender is None:
00663 sender = "tweedledee"
00664 if recipient is None:
00665 recipient = "tweedledum"
00666 self = cls()
00667 self.sender = sender
00668 self.recipient = recipient
00669 self.payload = payload
00670 self.type = self.type2name[type(payload)]
00671 return self
00672
00673 class sax_handler(rpki.xml_utils.sax_handler):
00674 """
00675 SAX handler for Up-Down protocol.
00676 """
00677
00678 pdu = message_pdu
00679 name = "message"
00680 version = "1"
00681
00682 class cms_msg(rpki.x509.XML_CMS_object):
00683 """
00684 Class to hold a CMS-signed up-down PDU.
00685 """
00686
00687 encoding = "UTF-8"
00688 schema = rpki.relaxng.up_down
00689 saxify = sax_handler.saxify