diff options
-rw-r--r-- | rpkid/irbe-setup.py | 22 | ||||
-rwxr-xr-x | rpkid/irbe_cli.py | 24 | ||||
-rwxr-xr-x | rpkid/irdbd.py | 9 | ||||
-rwxr-xr-x | rpkid/pubd.py | 25 | ||||
-rw-r--r-- | rpkid/resource-cert-samples.py | 36 | ||||
-rw-r--r-- | rpkid/rpki/async.py | 54 | ||||
-rw-r--r-- | rpkid/rpki/config.py | 16 | ||||
-rw-r--r-- | rpkid/rpki/exceptions.py | 185 | ||||
-rw-r--r-- | rpkid/rpki/https.py | 17 | ||||
-rw-r--r-- | rpkid/rpki/ipaddrs.py | 13 | ||||
-rw-r--r-- | rpkid/rpki/left_right.py | 208 | ||||
-rw-r--r-- | rpkid/rpki/log.py | 19 | ||||
-rw-r--r-- | rpkid/rpki/manifest.py | 3 | ||||
-rw-r--r-- | rpkid/rpki/oids.py | 3 | ||||
-rw-r--r-- | rpkid/rpki/publication.py | 116 | ||||
-rw-r--r-- | rpkid/rpki/resource_set.py | 247 | ||||
-rw-r--r-- | rpkid/rpki/roa.py | 3 | ||||
-rw-r--r-- | rpkid/rpki/rpki_engine.py | 166 | ||||
-rw-r--r-- | rpkid/rpki/sql.py | 48 | ||||
-rw-r--r-- | rpkid/rpki/sundial.py | 46 | ||||
-rw-r--r-- | rpkid/rpki/up_down.py | 193 | ||||
-rw-r--r-- | rpkid/rpki/x509.py | 278 | ||||
-rw-r--r-- | rpkid/rpki/xml_utils.py | 127 | ||||
-rw-r--r-- | rpkid/testbed.py | 152 |
24 files changed, 1446 insertions, 564 deletions
diff --git a/rpkid/irbe-setup.py b/rpkid/irbe-setup.py index e04f4abe..7b551316 100644 --- a/rpkid/irbe-setup.py +++ b/rpkid/irbe-setup.py @@ -1,7 +1,13 @@ -"""Set up the relationship between an IRBE and an RPKI engine given an +""" +Set up the relationship between an IRBE and an RPKI engine given an IRDB. Our main task here is to create child objects in the RPKI engine for every registrant object in the IRDB. +NB: This code is badly out of date, and has been kept only because +some of what it's doing might be useful in other tools that haven't +been written yet. Don't believe anything you see here. + + $Id$ Copyright (C) 2007--2008 American Registry for Internet Numbers ("ARIN") @@ -39,7 +45,8 @@ irbe_key = rpki.x509.RSA( Auto_file = cfg.get("irbe-key")) https_url = cfg.get("https-url") def call_rpkid(pdu): - """Hand a PDU to rpkid and get back the response. Just throw an + """ + Hand a PDU to rpkid and get back the response. Just throw an exception if anything bad happens, no fancy error handling. """ @@ -102,7 +109,10 @@ for registrant_id, subject_name in registrants: print "Attempting to bind", registrant_id, subject_name pdu = call_rpkid(rpki.left_right.child_elt.make_pdu(action = "create", self_id = self_id, bsc_id = bsc_id, bpki_cms_cert = cer)) print "Attempting to bind", registrant_id, subject_name, pdu.child_id - cur.execute("""UPDATE registrant - SET rpki_self_id = %d, rpki_child_id = %d - WHERE registrant_id = %d - """, (self_id, pdu.child_id, registrant_id)) + cur.execute( + """ + UPDATE registrant + SET rpki_self_id = %d, rpki_child_id = %d + WHERE registrant_id = %d + """, + (self_id, pdu.child_id, registrant_id)) diff --git a/rpkid/irbe_cli.py b/rpkid/irbe_cli.py index 06c6c63a..992f8c84 100755 --- a/rpkid/irbe_cli.py +++ b/rpkid/irbe_cli.py @@ -24,7 +24,9 @@ import rpki.left_right, rpki.https, rpki.x509, rpki.config, rpki.log, rpki.publi pem_out = None class UsageWrapper(textwrap.TextWrapper): - """Call interface around Python textwrap.Textwrapper class.""" + """ + Call interface around Python textwrap.Textwrapper class. + """ def __call__(self, *args): """Format arguments, with TextWrapper indentation.""" @@ -33,7 +35,9 @@ class UsageWrapper(textwrap.TextWrapper): usage_fill = UsageWrapper(subsequent_indent = " " * 4) class cmd_elt_mixin(object): - """Protocol mix-in for command line client element PDUs.""" + """ + Protocol mix-in for command line client element PDUs. + """ ## @var excludes # XML attributes and elements that should not be allowed as command @@ -44,7 +48,9 @@ class cmd_elt_mixin(object): @classmethod def usage(cls): - """Generate usage message for this PDU.""" + """ + Generate usage message for this PDU. + """ args = " ".join("--" + x + "=" for x in cls.attributes + cls.elements if x not in cls.excludes) bools = " ".join("--" + x for x in cls.booleans) if args and bools: @@ -53,7 +59,9 @@ class cmd_elt_mixin(object): return args or bools def client_getopt(self, argv): - """Parse options for this class.""" + """ + Parse options for this class. + """ opts, argv = getopt.getopt(argv, "", [x + "=" for x in self.attributes + self.elements if x not in self.excludes] + list(self.booleans)) for o, a in opts: o = o[2:] @@ -101,11 +109,15 @@ class cmd_elt_mixin(object): print " %s: %s" % (i, getattr(self, i)) class cmd_msg_mixin(object): - """Protocol mix-in for command line client message PDUs.""" + """ + Protocol mix-in for command line client message PDUs. + """ @classmethod def usage(cls): - """Generate usage message for this PDU.""" + """ + Generate usage message for this PDU. + """ for k, v in cls.pdus.items(): print usage_fill(k, v.usage()) diff --git a/rpkid/irdbd.py b/rpkid/irdbd.py index cda8c02d..e49da68a 100755 --- a/rpkid/irdbd.py +++ b/rpkid/irdbd.py @@ -50,9 +50,12 @@ def handler(query, path, cb): r_pdu.self_id = q_pdu.self_id r_pdu.child_id = q_pdu.child_id - cur.execute("""SELECT registrant_id, subject_name, valid_until FROM registrant - WHERE registrant.rpki_self_id = %s AND registrant.rpki_child_id = %s - """, (q_pdu.self_id, q_pdu.child_id)) + cur.execute( + """ + SELECT registrant_id, subject_name, valid_until FROM registrant + WHERE registrant.rpki_self_id = %s AND registrant.rpki_child_id = %s + """, + (q_pdu.self_id, q_pdu.child_id)) if cur.rowcount != 1: raise rpki.exceptions.NotInDatabase, \ "This query should have produced a single exact match, something's messed up (rowcount = %d, self_id = %s, child_id = %s)" \ diff --git a/rpkid/pubd.py b/rpkid/pubd.py index 05b17ced..5fb04dcb 100755 --- a/rpkid/pubd.py +++ b/rpkid/pubd.py @@ -30,7 +30,9 @@ import rpki.https, rpki.config, rpki.exceptions, rpki.relaxng, rpki.log import rpki.publication class pubd_context(object): - """A container for various pubd parameters.""" + """ + A container for various pubd parameters. + """ def __init__(self, cfg): @@ -47,7 +49,9 @@ class pubd_context(object): self.publication_base = cfg.get("publication-base", "publication/") def handler_common(self, query, client, cb, certs, crl = None): - """Common PDU handler code.""" + """ + Common PDU handler code. + """ def done(r_msg): reply = rpki.publication.cms_msg.wrap(r_msg, self.pubd_key, self.pubd_cert, crl) @@ -58,7 +62,9 @@ class pubd_context(object): q_msg.serve_top_level(self, client, done) def control_handler(self, query, path, cb): - """Process one PDU from the IRBE.""" + """ + Process one PDU from the IRBE. + """ rpki.log.trace() try: self.sql.ping() @@ -70,7 +76,9 @@ class pubd_context(object): cb(500, "Unhandled exception %s" % data) def client_handler(self, query, path, cb): - """Process one PDU from a client.""" + """ + Process one PDU from a client. + """ rpki.log.trace() try: self.sql.ping() @@ -95,14 +103,17 @@ class pubd_context(object): https_ta_cache = None def clear_https_ta_cache(self): - """Clear dynamic TLS trust anchors.""" - + """ + Clear dynamic TLS trust anchors. + """ if self.https_ta_cache is not None: rpki.log.debug("Clearing HTTPS trusted cert cache") self.https_ta_cache = None def build_https_ta_cache(self): - """Build dynamic TLS trust anchors.""" + """ + Build dynamic TLS trust anchors. + """ if self.https_ta_cache is None: clients = rpki.publication.client_elt.sql_fetch_all(self) self.https_ta_cache = rpki.https.build_https_ta_cache( diff --git a/rpkid/resource-cert-samples.py b/rpkid/resource-cert-samples.py index fd940194..978f3554 100644 --- a/rpkid/resource-cert-samples.py +++ b/rpkid/resource-cert-samples.py @@ -33,7 +33,9 @@ openssl = "../../openssl/openssl/apps/openssl" keybits = 2048 def main(): - """Main program, including the toy database itself.""" + """ + Main program, including the toy database itself. + """ db = allocation_db() db.add("ISP1", ipv4 = "192.0.2.1-192.0.2.33", asn = "64533") @@ -59,8 +61,9 @@ def main(): "".join([i.makefile_rules() for i in db])) def write_maybe(name, new_content): - """Write a file if and only if its contents have changed. - This simplifies interactions with "make". + """ + Write a file if and only if its contents have changed. This + simplifies interactions with "make". """ old_content = None if os.path.isfile(name): @@ -74,19 +77,23 @@ def write_maybe(name, new_content): f.close() class allocation_db(list): - """Class to represent an allocation database.""" + """ + Class to represent an allocation database. + """ def __init__(self): self.allocation_map = {} def add(self, name, **kw): - """Add a new entry to this allocation database. - All arguments passed through to the allocation constructor. + """ + Add a new entry to this allocation database. All arguments passed + through to the allocation constructor. """ self.insert(0, allocation(name = name, allocation_map = self.allocation_map, **kw)) class allocation(object): - """Class representing one entity holding allocated resources. + """ + Class representing one entity holding allocated resources. In order to simplify configuration, this class automatically computes the set of resources that this entity must hold in order to @@ -96,7 +103,8 @@ class allocation(object): parent = None def __init__(self, name, asn = None, ipv4 = None, ipv6 = None, children = (), allocation_map = None): - """Create a new allocation entry. + """ + Create a new allocation entry. This binds the parent attributes of any children, and computes the transitive closure of the set of resources this entity needs. @@ -112,7 +120,9 @@ class allocation(object): allocation_map[name] = self def summarize(self, attrname, seed = None): - """Compute the transitive resource closure for one resource attribute.""" + """ + Compute the transitive resource closure for one resource attribute. + """ if seed is None: seed = getattr(self, attrname) for child in self.children: @@ -123,7 +133,9 @@ class allocation(object): return "%s\n ASN: %s\n IPv4: %s\n IPv6: %s" % (self.name, self.asn, self.ipv4, self.ipv6) def cfg_string(self): - """Generate the OpenSSL configuration file needed for this entity.""" + """ + Generate the OpenSSL configuration file needed for this entity. + """ keys = { "self" : self.name, "keybits" : keybits, "no_parent" : "#", @@ -144,7 +156,9 @@ class allocation(object): return openssl_cfg_fmt % keys def makefile_rules(self): - """Generate the makefile rules needed for this entity.""" + """ + Generate the makefile rules needed for this entity. + """ keys = { "self" : self.name, "keybits" : keybits, "openssl" : openssl } diff --git a/rpkid/rpki/async.py b/rpkid/rpki/async.py index cb2541b0..c581dbb0 100644 --- a/rpkid/rpki/async.py +++ b/rpkid/rpki/async.py @@ -24,7 +24,8 @@ import rpki.log, rpki.sundial ExitNow = asyncore.ExitNow class iterator(object): - """Iteration construct for event-driven code. Takes three + """ + Iteration construct for event-driven code. Takes three arguments: - Some kind of iterable object @@ -62,7 +63,8 @@ class iterator(object): class timer(object): - """Timer construct for event-driven code. It can be used in either of two ways: + """ + Timer construct for event-driven code. It can be used in either of two ways: - As a virtual class, in which case the subclass should provide a handler() method to receive the wakup event when the timer expires; or @@ -87,10 +89,11 @@ class timer(object): self.set_errback(errback) def set(self, when): - """Set a timer. Argument can be a datetime, to specify an - absolute time, a timedelta, to specify an offset time, or None, to - indicate that the timer should expire immediately, which can be - useful in avoiding an excessively deep call stack. + """ + Set a timer. Argument can be a datetime, to specify an absolute + time, a timedelta, to specify an offset time, or None, to indicate + that the timer should expire immediately, which can be useful in + avoiding an excessively deep call stack. """ if when is None: self.when = rpki.sundial.now() @@ -107,7 +110,9 @@ class timer(object): return cmp(self.when, other.when) def cancel(self): - """Cancel a timer, if it was set.""" + """ + Cancel a timer, if it was set. + """ try: self.queue.remove(self) except ValueError: @@ -118,13 +123,15 @@ class timer(object): return self in self.queue def handler(self): - """Handle a timer that has expired. This must either be overriden - by a subclass or set dynamically by set_handler(). + """ + Handle a timer that has expired. This must either be overriden by + a subclass or set dynamically by set_handler(). """ raise NotImplementedError def set_handler(self, handler): - """Set timer's expiration handler. This is an alternative to + """ + Set timer's expiration handler. This is an alternative to subclassing the timer class, and may be easier to use when integrating timers into other classes (eg, the handler can be a bound method to an object in a class representing a network @@ -133,7 +140,9 @@ class timer(object): self.handler = handler def errback(self, e): - """Error callback. May be overridden, or set with set_errback().""" + """ + Error callback. May be overridden, or set with set_errback(). + """ rpki.log.error("Unhandled exception from timer: %s" % e) rpki.log.error(traceback.format_exc()) @@ -143,7 +152,8 @@ class timer(object): @classmethod def runq(cls): - """Run the timer queue: for each timer whose call time has passed, + """ + Run the timer queue: for each timer whose call time has passed, pull the timer off the queue and call its handler() method. """ while cls.queue and rpki.sundial.now() >= cls.queue[0].when: @@ -160,12 +170,13 @@ class timer(object): @classmethod def seconds_until_wakeup(cls): - """Calculate delay until next timer expires, or None if no timers - are set and we should wait indefinitely. Rounds up to avoid - spinning in select() or poll(). We could calculate fractional - seconds in the right units instead, but select() and poll() don't - even take the same units (argh!), and we're not doing anything - that hair-triggered, so rounding up is simplest. + """ + Calculate delay until next timer expires, or None if no timers are + set and we should wait indefinitely. Rounds up to avoid spinning + in select() or poll(). We could calculate fractional seconds in + the right units instead, but select() and poll() don't even take + the same units (argh!), and we're not doing anything that + hair-triggered, so rounding up is simplest. """ if not cls.queue: return None @@ -181,7 +192,8 @@ class timer(object): @classmethod def clear(cls): - """Cancel every timer on the queue. We could just throw away the + """ + Cancel every timer on the queue. We could just throw away the queue content, but this way we can notify subclasses that provide their own cancel() method. """ @@ -193,7 +205,9 @@ def _raiseExitNow(signum, frame): raise ExitNow def event_loop(catch_signals = (signal.SIGINT, signal.SIGTERM)): - """Replacement for asyncore.loop(), adding timer and signal support.""" + """ + Replacement for asyncore.loop(), adding timer and signal support. + """ old_signal_handlers = {} try: for sig in catch_signals: diff --git a/rpkid/rpki/config.py b/rpkid/rpki/config.py index 72bbc589..c233179b 100644 --- a/rpkid/rpki/config.py +++ b/rpkid/rpki/config.py @@ -1,5 +1,6 @@ -"""Configuration file parsing utilities, layered on top of stock -Python ConfigParser module. +""" +Configuration file parsing utilities, layered on top of stock Python +ConfigParser module. $Id$ @@ -23,14 +24,17 @@ import ConfigParser class parser(ConfigParser.RawConfigParser): def __init__(self, filename = None, section = None): - """Initialize this parser.""" + """ + Initialize this parser. + """ ConfigParser.RawConfigParser.__init__(self) if filename: self.read(filename) self.default_section = section def multiget(self, option, section = None): - """Parse OpenSSL-style foo.0, foo.1, ... subscripted options. + """ + Parse OpenSSL-style foo.0, foo.1, ... subscripted options. Returns a list of values matching the specified option name. """ @@ -47,7 +51,9 @@ class parser(ConfigParser.RawConfigParser): return [match[1] for match in matches] def get(self, option, default = None, section = None): - """Get an option, perhaps with a default value.""" + """ + Get an option, perhaps with a default value. + """ if section is None: section = self.default_section if default is None or self.has_option(section, option): diff --git a/rpkid/rpki/exceptions.py b/rpkid/rpki/exceptions.py index d2ff92ec..8a79d680 100644 --- a/rpkid/rpki/exceptions.py +++ b/rpkid/rpki/exceptions.py @@ -1,4 +1,5 @@ -"""Exception definitions for RPKI modules. +""" +Exception definitions for RPKI modules. $Id$ @@ -18,136 +19,228 @@ PERFORMANCE OF THIS SOFTWARE. """ class RPKI_Exception(Exception): - """Base class for RPKI exceptions.""" + """ + Base class for RPKI exceptions. + """ class NotInDatabase(RPKI_Exception): - """Lookup failed for an object expected to be in the database.""" + """ + Lookup failed for an object expected to be in the database. + """ class BadURISyntax(RPKI_Exception): - """Illegal syntax for a URI.""" + """ + Illegal syntax for a URI. + """ class BadStatusCode(RPKI_Exception): - """Unrecognized protocol status code.""" + """ + Unrecognized protocol status code. + """ class BadQuery(RPKI_Exception): - """Unexpected protocol query.""" + """ + Unexpected protocol query. + """ class DBConsistancyError(RPKI_Exception): - """Found multiple matches for a database query that shouldn't ever return that.""" + """ + Found multiple matches for a database query that shouldn't ever + return that. + """ class CMSVerificationFailed(RPKI_Exception): - """Verification of a CMS message failed.""" + """ + Verification of a CMS message failed. + """ class HTTPRequestFailed(RPKI_Exception): - """HTTP request failed.""" + """ + HTTP request failed. + """ class DERObjectConversionError(RPKI_Exception): - """Error trying to convert a DER-based object from one representation to another.""" + """ + Error trying to convert a DER-based object from one representation + to another. + """ class NotACertificateChain(RPKI_Exception): - """Certificates don't form a proper chain.""" + """ + Certificates don't form a proper chain. + """ class BadContactURL(RPKI_Exception): - """Error trying to parse contact URL.""" + """ + Error trying to parse contact URL. + """ class BadClassNameSyntax(RPKI_Exception): - """Illegal syntax for a class_name.""" + """ + Illegal syntax for a class_name. + """ class BadIssueResponse(RPKI_Exception): - """issue_response PDU with wrong number of classes or certificates.""" + """ + issue_response PDU with wrong number of classes or certificates. + """ class NotImplementedYet(RPKI_Exception): - """Internal error -- not implemented yet.""" + """ + Internal error -- not implemented yet. + """ class BadPKCS10(RPKI_Exception): - """Bad PKCS #10 object.""" + """ + Bad PKCS #10 object. + """ class UpstreamError(RPKI_Exception): - """Received an error from upstream.""" + """ + Received an error from upstream. + """ class ChildNotFound(RPKI_Exception): - """Could not find specified child in database.""" + """ + Could not find specified child in database. + """ class BSCNotFound(RPKI_Exception): - """Could not find specified BSC in database.""" + """ + Could not find specified BSC in database. + """ class BadSender(RPKI_Exception): - """Unexpected XML sender value.""" + """ + Unexpected XML sender value. + """ class ClassNameMismatch(RPKI_Exception): - """class_name does not match child context.""" + """ + class_name does not match child context. + """ class ClassNameUnknown(RPKI_Exception): - """Unknown class_name.""" + """ + Unknown class_name. + """ class SKIMismatch(RPKI_Exception): - """SKI value in response does not match request.""" + """ + SKI value in response does not match request. + """ class SubprocessError(RPKI_Exception): - """Subprocess returned unexpected error.""" + """ + Subprocess returned unexpected error. + """ class BadIRDBReply(RPKI_Exception): - """Unexpected reply to IRDB query.""" + """ + Unexpected reply to IRDB query. + """ class NotFound(RPKI_Exception): - """Object not found in database.""" + """ + Object not found in database. + """ class MustBePrefix(RPKI_Exception): - """Resource range cannot be expressed as a prefix.""" + """ + Resource range cannot be expressed as a prefix. + """ class TLSValidationError(RPKI_Exception): - """TLS certificate validation error.""" + """ + TLS certificate validation error. + """ class MultipleTLSEECert(TLSValidationError): - """Received more than one TLS EE certificate.""" + """ + Received more than one TLS EE certificate. + """ class ReceivedTLSCACert(TLSValidationError): - """Received CA certificate via TLS.""" + """ + Received CA certificate via TLS. + """ class WrongEContentType(RPKI_Exception): - """Received wrong CMS eContentType.""" + """ + Received wrong CMS eContentType. + """ class EmptyPEM(RPKI_Exception): - """Couldn't find PEM block to convert.""" + """ + Couldn't find PEM block to convert. + """ class UnexpectedCMSCerts(RPKI_Exception): - """Received CMS certs when not expecting any.""" + """ + Received CMS certs when not expecting any. + """ class UnexpectedCMSCRLs(RPKI_Exception): - """Received CMS CRLs when not expecting any.""" + """ + Received CMS CRLs when not expecting any. + """ class MissingCMSEEcert(RPKI_Exception): - """Didn't receive CMS EE cert when expecting one.""" + """ + Didn't receive CMS EE cert when expecting one. + """ class MissingCMSCRL(RPKI_Exception): - """Didn't receive CMS CRL when expecting one.""" + """ + Didn't receive CMS CRL when expecting one. + """ class UnparsableCMSDER(RPKI_Exception): - """Alleged CMS DER wasn't parsable.""" + """ + Alleged CMS DER wasn't parsable. + """ class CMSCRLNotSet(RPKI_Exception): - """CMS CRL has not been configured.""" + """ + CMS CRL has not been configured. + """ class ServerShuttingDown(RPKI_Exception): - """Server is shutting down.""" + """ + Server is shutting down. + """ class NoActiveCA(RPKI_Exception): - """No active ca_detail for specified class.""" + """ + No active ca_detail for specified class. + """ class BadClientURL(RPKI_Exception): - """URL given to HTTPS client does not match profile.""" + """ + URL given to HTTPS client does not match profile. + """ class ClientNotFound(RPKI_Exception): - """Could not find specified client in database.""" + """ + Could not find specified client in database. + """ class BadExtension(RPKI_Exception): - """Forbidden X.509 extension.""" + """ + Forbidden X.509 extension. + """ class ForbiddenURI(RPKI_Exception): - """Forbidden URI, does not start with correct base URI.""" + """ + Forbidden URI, does not start with correct base URI. + """ class HTTPSRetryFailure(RPKI_Exception): - """HTTPS connection failed and request has already been retried.""" + """ + HTTPS connection failed and request has already been retried. + """ class BadPublicationReply(RPKI_Exception): - """Unexpected reply to publication query.""" + """ + Unexpected reply to publication query. + """ diff --git a/rpkid/rpki/https.py b/rpkid/rpki/https.py index 47c5b81f..d4365db6 100644 --- a/rpkid/rpki/https.py +++ b/rpkid/rpki/https.py @@ -1,4 +1,5 @@ -"""HTTPS utilities, both client and server. +""" +HTTPS utilities, both client and server. At the moment this only knows how to use the PEM certs in my subversion repository; generalizing it would not be hard, but the more @@ -206,7 +207,9 @@ class http_stream(asynchat.async_chat): self.timer.cancel() def collect_incoming_data(self, data): - """Buffer the data""" + """ + Buffer the data + """ self.buffer.append(data) self.update_active_timeout() @@ -294,7 +297,9 @@ class http_server(http_stream): self.handle_message() def find_handler(self, path): - """Helper method to search self.handlers.""" + """ + Helper method to search self.handlers. + """ for s, h in self.handlers: if path.startswith(s): return h @@ -532,7 +537,8 @@ class http_queue(object): queues = {} def client(msg, client_key, client_cert, server_ta, url, callback, errback): - """Open client HTTPS connection, send a message, wait for response. + """ + Open client HTTPS connection, send a message, wait for response. THIS VERSION DOES NOT DO TLS. THIS IS EXPERIMENTAL CODE. DO NOT USE IN PRODUCTION UNTIL TLS SUPPORT HAS BEEN ADDED. @@ -567,7 +573,8 @@ def client(msg, client_key, client_cert, server_ta, url, callback, errback): queues[hostport].request(request) def server(handlers, server_key, server_cert, port, host ="", client_ta = None, dynamic_https_trust_anchor = None): - """Run an HTTPS server and wait (forever) for connections. + """ + Run an HTTPS server and wait (forever) for connections. THIS VERSION DOES NOT DO TLS. THIS IS EXPERIMENTAL CODE. DO NOT USE IN PRODUCTION UNTIL TLS SUPPORT HAS BEEN ADDED. diff --git a/rpkid/rpki/ipaddrs.py b/rpkid/rpki/ipaddrs.py index b79a8daa..f387452d 100644 --- a/rpkid/rpki/ipaddrs.py +++ b/rpkid/rpki/ipaddrs.py @@ -1,4 +1,5 @@ -"""Classes to represent IP addresses. +""" +Classes to represent IP addresses. Given some of the other operations we need to perform on them, it's most convenient to represent IP addresses as Python "long" values. @@ -44,7 +45,8 @@ PERFORMANCE OF THIS SOFTWARE. import socket, struct class v4addr(long): - """IPv4 address. + """ + IPv4 address. Derived from long, but supports IPv4 print syntax. """ @@ -52,7 +54,9 @@ class v4addr(long): bits = 32 def __new__(cls, x): - """Construct a v4addr object.""" + """ + Construct a v4addr object. + """ if isinstance(x, str): return cls.from_bytes(socket.inet_pton(socket.AF_INET, ".".join(str(int(i)) for i in x.split(".")))) else: @@ -72,7 +76,8 @@ class v4addr(long): return socket.inet_ntop(socket.AF_INET, self.to_bytes()) class v6addr(long): - """IPv6 address. + """ + IPv6 address. Derived from long, but supports IPv6 print syntax. """ diff --git a/rpkid/rpki/left_right.py b/rpkid/rpki/left_right.py index fd590b11..ef069fc9 100644 --- a/rpkid/rpki/left_right.py +++ b/rpkid/rpki/left_right.py @@ -1,4 +1,5 @@ -"""RPKI "left-right" protocol. +""" +RPKI "left-right" protocol. $Id$ @@ -26,13 +27,17 @@ import rpki.publication, rpki.async enforce_strict_up_down_xml_sender = False class left_right_namespace(object): - """XML namespace parameters for left-right protocol.""" + """ + XML namespace parameters for left-right protocol. + """ xmlns = "http://www.hactrn.net/uris/rpki/left-right-spec/" nsmap = { None : xmlns } class data_elt(rpki.xml_utils.data_elt, rpki.sql.sql_persistant, left_right_namespace): - """Virtual class for top-level left-right protocol data elements.""" + """ + Virtual class for top-level left-right protocol data elements. + """ def self(self): """Fetch self object to which this object links.""" @@ -47,7 +52,8 @@ class data_elt(rpki.xml_utils.data_elt, rpki.sql.sql_persistant, left_right_name r_pdu.self_id = self.self_id def serve_fetch_one(self): - """Find the object on which a get, set, or destroy method should + """ + Find the object on which a get, set, or destroy method should operate. """ where = self.sql_template.index + " = %s AND self_id = %s" @@ -62,13 +68,17 @@ class data_elt(rpki.xml_utils.data_elt, rpki.sql.sql_persistant, left_right_name return self.sql_fetch_where(self.gctx, "self_id = %s", (self.self_id,)) def unimplemented_control(self, *controls): - """Uniform handling for unimplemented control operations.""" + """ + Uniform handling for unimplemented control operations. + """ unimplemented = [x for x in controls if getattr(self, x, False)] if unimplemented: raise rpki.exceptions.NotImplementedYet, "Unimplemented control %s" % ", ".join(unimplemented) class self_elt(data_elt): - """<self/> element.""" + """ + <self/> element. + """ element_name = "self" attributes = ("action", "tag", "self_id", "crl_interval", "regen_margin") @@ -106,7 +116,9 @@ class self_elt(data_elt): return route_origin_elt.sql_fetch_where(self.gctx, "self_id = %s", (self.self_id,)) def serve_post_save_hook(self, q_pdu, r_pdu, cb, eb): - """Extra server actions for self_elt.""" + """ + Extra server actions for self_elt. + """ rpki.log.trace() if q_pdu.rekey: self.serve_rekey(cb, eb) @@ -117,7 +129,9 @@ class self_elt(data_elt): cb() def serve_rekey(self, cb, eb): - """Handle a left-right rekey action for this self.""" + """ + Handle a left-right rekey action for this self. + """ rpki.log.trace() def loop(iterator, parent): @@ -126,7 +140,9 @@ class self_elt(data_elt): rpki.async.iterator(self.parents(), loop, cb) def serve_revoke(self, cb, eb): - """Handle a left-right revoke action for this self.""" + """ + Handle a left-right revoke action for this self. + """ rpki.log.trace() def loop(iterator, parent): @@ -135,7 +151,8 @@ class self_elt(data_elt): rpki.async.iterator(self.parents(), loop, cb) def serve_fetch_one(self): - """Find the self object upon which a get, set, or destroy action + """ + Find the self object upon which a get, set, or destroy action should operate. """ r = self.sql_fetch(self.gctx, self.self_id) @@ -144,14 +161,18 @@ class self_elt(data_elt): return r def serve_fetch_all(self): - """Find the self objects upon which a list action should operate. + """ + Find the self objects upon which a list action should operate. This is different from the list action for all other objects, where list only works within a given self_id context. """ return self.sql_fetch_all(self.gctx) def client_poll(self, callback): - """Run the regular client poll cycle with each of this self's parents in turn.""" + """ + Run the regular client poll cycle with each of this self's parents + in turn. + """ rpki.log.trace() @@ -198,7 +219,8 @@ class self_elt(data_elt): rpki.async.iterator(self.parents(), parent_loop, callback) def update_children(self, cb): - """Check for updated IRDB data for all of this self's children and + """ + Check for updated IRDB data for all of this self's children and issue new certs as necessary. Must handle changes both in resources and in expiration date. """ @@ -275,7 +297,8 @@ class self_elt(data_elt): def regenerate_crls_and_manifests(self, cb): - """Generate new CRLs and manifests as necessary for all of this + """ + Generate new CRLs and manifests as necessary for all of this self's CAs. Extracting nextUpdate from a manifest is hard at the moment due to implementation silliness, so for now we generate a new manifest whenever we generate a new CRL @@ -324,7 +347,9 @@ class self_elt(data_elt): def update_roas(self, cb): - """Generate or update ROAs for this self's route_origin objects.""" + """ + Generate or update ROAs for this self's route_origin objects. + """ def loop(iterator, route_origin): route_origin.update_roa(iterator) @@ -333,7 +358,9 @@ class self_elt(data_elt): class bsc_elt(data_elt): - """<bsc/> (Business Signing Context) element.""" + """ + <bsc/> (Business Signing Context) element. + """ element_name = "bsc" attributes = ("action", "tag", "self_id", "bsc_id", "key_type", "hash_alg", "key_length") @@ -364,8 +391,9 @@ class bsc_elt(data_elt): return child_elt.sql_fetch_where(self.gctx, "bsc_id = %s", (self.bsc_id,)) def serve_pre_save_hook(self, q_pdu, r_pdu, cb, eb): - """Extra server actions for bsc_elt -- handle key generation. - For now this only allows RSA with SHA-256. + """ + Extra server actions for bsc_elt -- handle key generation. For + now this only allows RSA with SHA-256. """ if q_pdu.generate_keypair: assert q_pdu.key_type in (None, "rsa") and q_pdu.hash_alg in (None, "sha256") @@ -375,7 +403,9 @@ class bsc_elt(data_elt): cb() class parent_elt(data_elt): - """<parent/> element.""" + """ + <parent/> element. + """ element_name = "parent" attributes = ("action", "tag", "self_id", "parent_id", "bsc_id", "repository_id", @@ -402,7 +432,9 @@ class parent_elt(data_elt): return rpki.rpki_engine.ca_obj.sql_fetch_where(self.gctx, "parent_id = %s", (self.parent_id,)) def serve_post_save_hook(self, q_pdu, r_pdu, cb, eb): - """Extra server actions for parent_elt.""" + """ + Extra server actions for parent_elt. + """ if q_pdu.rekey: self.serve_rekey(cb, eb) elif q_pdu.revoke: @@ -412,7 +444,9 @@ class parent_elt(data_elt): cb() def serve_rekey(self, cb, eb): - """Handle a left-right rekey action for this parent.""" + """ + Handle a left-right rekey action for this parent. + """ def loop(iterator, ca): ca.rekey(iterator, eb) @@ -420,7 +454,9 @@ class parent_elt(data_elt): rpki.async.iterator(self.cas(), loop, cb) def serve_revoke(self, cb, eb): - """Handle a left-right revoke action for this parent.""" + """ + Handle a left-right revoke action for this parent. + """ def loop(iterator, ca): ca.revoke(iterator, eb) @@ -428,7 +464,9 @@ class parent_elt(data_elt): rpki.async.iterator(self.cas(), loop, cb) def query_up_down(self, q_pdu, cb, eb): - """Client code for sending one up-down query PDU to this parent.""" + """ + Client code for sending one up-down query PDU to this parent. + """ rpki.log.trace() @@ -463,7 +501,9 @@ class parent_elt(data_elt): errback = eb) class child_elt(data_elt): - """<child/> element.""" + """ + <child/> element. + """ element_name = "child" attributes = ("action", "tag", "self_id", "child_id", "bsc_id") @@ -487,7 +527,9 @@ class child_elt(data_elt): return parent_elt.sql_fetch_where(self.gctx, "self_id = %s", (self.self_id,)) def ca_from_class_name(self, class_name): - """Fetch the CA corresponding to an up-down class_name.""" + """ + Fetch the CA corresponding to an up-down class_name. + """ if not class_name.isdigit(): raise rpki.exceptions.BadClassNameSyntax, "Bad class name %s" % class_name ca = rpki.rpki_engine.ca_obj.sql_fetch(self.gctx, long(class_name)) @@ -499,7 +541,9 @@ class child_elt(data_elt): return ca def serve_post_save_hook(self, q_pdu, r_pdu, cb, eb): - """Extra server actions for child_elt.""" + """ + Extra server actions for child_elt. + """ self.unimplemented_control("reissue") if self.clear_https_ta_cache: self.gctx.clear_https_ta_cache() @@ -507,7 +551,8 @@ class child_elt(data_elt): cb() def endElement(self, stack, name, text): - """Handle subelements of <child/> element. These require special + """ + Handle subelements of <child/> element. These require special handling because modifying them invalidates the HTTPS trust anchor cache. """ @@ -516,7 +561,9 @@ class child_elt(data_elt): self.clear_https_ta_cache = True def serve_up_down(self, query, callback): - """Outer layer of server handling for one up-down PDU from this child.""" + """ + Outer layer of server handling for one up-down PDU from this child. + """ rpki.log.trace() @@ -551,7 +598,9 @@ class child_elt(data_elt): done(q_msg.serve_error(data)) class repository_elt(data_elt): - """<repository/> element.""" + """ + <repository/> element. + """ element_name = "repository" attributes = ("action", "tag", "self_id", "repository_id", "bsc_id", "peer_contact_uri") @@ -571,7 +620,9 @@ class repository_elt(data_elt): return parent_elt.sql_fetch_where(self.gctx, "repository_id = %s", (self.repository_id,)) def call_pubd(self, callback, errback, *pdus): - """Send a message to publication daemon and return the response.""" + """ + Send a message to publication daemon and return the response. + """ rpki.log.trace() bsc = self.bsc() q_msg = rpki.publication.msg(pdus) @@ -600,19 +651,25 @@ class repository_elt(data_elt): errback = errback) def publish(self, obj, uri, callback, errback): - """Publish one object in the repository.""" + """ + Publish one object in the repository. + """ rpki.log.trace() rpki.log.info("Publishing %s as %s" % (repr(obj), repr(uri))) self.call_pubd(callback, errback, rpki.publication.obj2elt[type(obj)].make_pdu(action = "publish", uri = uri, payload = obj)) def withdraw(self, obj, uri, callback, errback): - """Withdraw one object from the repository.""" + """ + Withdraw one object from the repository. + """ rpki.log.trace() rpki.log.info("Withdrawing %s from at %s" % (repr(obj), repr(uri))) self.call_pubd(callback, errback, rpki.publication.obj2elt[type(obj)].make_pdu(action = "withdraw", uri = uri)) class route_origin_elt(data_elt): - """<route_origin/> element.""" + """ + <route_origin/> element. + """ element_name = "route_origin" attributes = ("action", "tag", "self_id", "route_origin_id", "as_number", "ipv4", "ipv6") @@ -632,7 +689,9 @@ class route_origin_elt(data_elt): publish_ee_separately = False def sql_fetch_hook(self): - """Extra SQL fetch actions for route_origin_elt -- handle prefix list.""" + """ + Extra SQL fetch actions for route_origin_elt -- handle prefix list. + """ self.ipv4 = rpki.resource_set.roa_prefix_set_ipv4.from_sql( self.gctx.sql, """ @@ -647,16 +706,24 @@ class route_origin_elt(data_elt): """, (self.route_origin_id,)) def sql_insert_hook(self): - """Extra SQL insert actions for route_origin_elt -- handle address ranges.""" + """ + Extra SQL insert actions for route_origin_elt -- handle address + ranges. + """ if self.ipv4 or self.ipv6: - self.gctx.sql.executemany(""" - INSERT route_origin_prefix (route_origin_id, address, prefixlen, max_prefixlen) - VALUES (%s, %s, %s, %s)""", - ((self.route_origin_id, x.address, x.prefixlen, x.max_prefixlen) - for x in (self.ipv4 or []) + (self.ipv6 or []))) + self.gctx.sql.executemany( + """ + INSERT route_origin_prefix (route_origin_id, address, prefixlen, max_prefixlen) + VALUES (%s, %s, %s, %s) + """, + ((self.route_origin_id, x.address, x.prefixlen, x.max_prefixlen) + for x in (self.ipv4 or []) + (self.ipv6 or []))) def sql_delete_hook(self): - """Extra SQL delete actions for route_origin_elt -- handle address ranges.""" + """ + Extra SQL delete actions for route_origin_elt -- handle address + ranges. + """ self.gctx.sql.execute("DELETE FROM route_origin_prefix WHERE route_origin_id = %s", (self.route_origin_id,)) def ca_detail(self): @@ -664,13 +731,16 @@ class route_origin_elt(data_elt): return rpki.rpki_engine.ca_detail_obj.sql_fetch(self.gctx, self.ca_detail_id) def serve_post_save_hook(self, q_pdu, r_pdu, cb, eb): - """Extra server actions for route_origin_elt.""" + """ + Extra server actions for route_origin_elt. + """ self.unimplemented_control("suppress_publication") cb() def startElement(self, stack, name, attrs): - """Handle <route_origin/> element. This requires special - processing due to the data types of some of the attributes. + """ + Handle <route_origin/> element. This requires special processing + due to the data types of some of the attributes. """ assert name == "route_origin", "Unexpected name %s, stack %s" % (name, stack) self.read_attrs(attrs) @@ -682,7 +752,9 @@ class route_origin_elt(data_elt): self.ipv6 = rpki.resource_set.roa_prefix_set_ipv6(self.ipv6) def update_roa(self, callback): - """Bring this route_origin's ROA up to date if necesssary.""" + """ + Bring this route_origin's ROA up to date if necesssary. + """ def lose(e): rpki.log.warn("Could not update ROA %r, skipping: %s" % (self, e)) @@ -722,7 +794,8 @@ class route_origin_elt(data_elt): callback() def generate_roa(self, callback, errback): - """Generate a ROA based on this <route_origin/> object. + """ + Generate a ROA based on this <route_origin/> object. At present this does not support ROAs with multiple signatures (neither does the current CMS code). @@ -796,7 +869,8 @@ class route_origin_elt(data_elt): errback) def withdraw_roa(self, callback, errback, regenerate = False): - """Withdraw ROA associated with this route_origin. + """ + Withdraw ROA associated with this route_origin. In order to preserve make-before-break properties without duplicating code, this method also handles generating a @@ -837,7 +911,9 @@ class route_origin_elt(data_elt): one() def regenerate_roa(self, callback, errback): - """Reissue ROA associated with this route_origin.""" + """ + Reissue ROA associated with this route_origin. + """ if self.ca_detail() is None: self.generate_roa(callback, errback) else: @@ -860,15 +936,18 @@ class route_origin_elt(data_elt): return ca.sia_uri + self.ee_uri_tail() class list_resources_elt(rpki.xml_utils.base_elt, left_right_namespace): - """<list_resources/> element.""" + """ + <list_resources/> element. + """ element_name = "list_resources" attributes = ("self_id", "tag", "child_id", "valid_until", "asn", "ipv4", "ipv6", "subject_name") valid_until = None def startElement(self, stack, name, attrs): - """Handle <list_resources/> element. This requires special - handling due to the data types of some of the attributes. + """ + 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) @@ -882,7 +961,8 @@ class list_resources_elt(rpki.xml_utils.base_elt, left_right_namespace): self.ipv6 = rpki.resource_set.resource_set_ipv6(self.ipv6) def toXML(self): - """Generate <list_resources/> element. This requires special + """ + Generate <list_resources/> element. This requires special handling due to the data types of some of the attributes. """ elt = self.make_elt() @@ -891,14 +971,18 @@ class list_resources_elt(rpki.xml_utils.base_elt, left_right_namespace): return elt class report_error_elt(rpki.xml_utils.base_elt, left_right_namespace): - """<report_error/> element.""" + """ + <report_error/> element. + """ element_name = "report_error" attributes = ("tag", "self_id", "error_code") @classmethod def from_exception(cls, e, self_id = None): - """Generate a <report_error/> element from an exception.""" + """ + Generate a <report_error/> element from an exception. + """ self = cls() self.self_id = self_id self.error_code = e.__class__.__name__ @@ -906,7 +990,9 @@ class report_error_elt(rpki.xml_utils.base_elt, left_right_namespace): return self class msg(rpki.xml_utils.msg, left_right_namespace): - """Left-right PDU.""" + """ + Left-right PDU. + """ ## @var version # Protocol version @@ -919,7 +1005,9 @@ class msg(rpki.xml_utils.msg, left_right_namespace): route_origin_elt, list_resources_elt, report_error_elt)) def serve_top_level(self, gctx, cb): - """Serve one msg PDU.""" + """ + Serve one msg PDU. + """ r_msg = self.__class__() r_msg.type = "reply" @@ -944,14 +1032,18 @@ 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.""" + """ + SAX handler for Left-Right protocol. + """ pdu = msg name = "msg" version = "1" class cms_msg(rpki.x509.XML_CMS_object): - """Class to hold a CMS-signed left-right PDU.""" + """ + Class to hold a CMS-signed left-right PDU. + """ encoding = "us-ascii" schema = rpki.relaxng.left_right diff --git a/rpkid/rpki/log.py b/rpkid/rpki/log.py index c19db594..83cb18bc 100644 --- a/rpkid/rpki/log.py +++ b/rpkid/rpki/log.py @@ -1,4 +1,5 @@ -"""Logging facilities for RPKI libraries. +""" +Logging facilities for RPKI libraries. $Id$ @@ -33,7 +34,9 @@ tag = "" pid = 0 def init(ident = "rpki", flags = syslog.LOG_PID | syslog.LOG_PERROR, facility = syslog.LOG_DAEMON): - """Initialize logging system.""" + """ + Initialize logging system. + """ if use_syslog: return syslog.openlog(ident, flags, facility) @@ -43,13 +46,17 @@ def init(ident = "rpki", flags = syslog.LOG_PID | syslog.LOG_PERROR, facility = pid = os.getpid() def set_trace(enable): - """Enable or disable call tracing.""" + """ + Enable or disable call tracing. + """ global enable_trace enable_trace = enable class logger(object): - """Closure for logging.""" + """ + Closure for logging. + """ def __init__(self, priority): self.priority = priority @@ -67,7 +74,9 @@ info = logger(syslog.LOG_INFO) debug = logger(syslog.LOG_DEBUG) def trace(): - """Execution trace -- where are we now, and whence came we here?""" + """ + Execution trace -- where are we now, and whence came we here? + """ if enable_trace: bt = traceback.extract_stack(limit = 3) diff --git a/rpkid/rpki/manifest.py b/rpkid/rpki/manifest.py index 97b1cc71..8581f3a0 100644 --- a/rpkid/rpki/manifest.py +++ b/rpkid/rpki/manifest.py @@ -1,4 +1,5 @@ -"""Signed manifests. This is just the ASN.1 encoder, the rest is in +""" +Signed manifests. This is just the ASN.1 encoder, the rest is in rpki.x509 with the rest of the DER_object code. Note that rpki.x509.SignedManifest implements the signed manifest; diff --git a/rpkid/rpki/oids.py b/rpkid/rpki/oids.py index 59c36cf8..90e486b7 100644 --- a/rpkid/rpki/oids.py +++ b/rpkid/rpki/oids.py @@ -1,4 +1,5 @@ -"""OID database. +""" +OID database. $Id$ diff --git a/rpkid/rpki/publication.py b/rpkid/rpki/publication.py index b43fa60c..ad4ce866 100644 --- a/rpkid/rpki/publication.py +++ b/rpkid/rpki/publication.py @@ -1,4 +1,5 @@ -"""RPKI "publication" protocol. +""" +RPKI "publication" protocol. $Id$ @@ -22,24 +23,30 @@ import rpki.resource_set, rpki.x509, rpki.sql, rpki.exceptions, rpki.xml_utils import rpki.https, rpki.up_down, rpki.relaxng, rpki.sundial, rpki.log, rpki.roa class publication_namespace(object): - """XML namespace parameters for publication protocol.""" + """ + XML namespace parameters for publication protocol. + """ xmlns = "http://www.hactrn.net/uris/rpki/publication-spec/" nsmap = { None : xmlns } class control_elt(rpki.xml_utils.data_elt, rpki.sql.sql_persistant, publication_namespace): - """Virtual class for control channel objects.""" + """ + Virtual class for control channel objects. + """ def serve_dispatch(self, r_msg, cb, eb): - """Action dispatch handler. This needs special handling because - we need to make sure that this PDU arrived via the control channel. + """ + Action dispatch handler. This needs special handling because we + need to make sure that this PDU arrived via the control channel. """ if self.client is not None: raise rpki.exceptions.BadQuery, "Control query received on client channel" rpki.xml_utils.data_elt.serve_dispatch(self, r_msg, cb, eb) class config_elt(control_elt): - """<config/> element. This is a little weird because there should + """ + <config/> element. This is a little weird because there should never be more than one row in the SQL config table, but we have to put the BPKI CRL somewhere and SQL is the least bad place available. @@ -57,21 +64,24 @@ class config_elt(control_elt): wired_in_config_id = 1 def startElement(self, stack, name, attrs): - """StartElement() handler for config object. This requires - special handling because of the weird way we treat config_id. + """ + StartElement() handler for config object. This requires special + handling because of the weird way we treat config_id. """ control_elt.startElement(self, stack, name, attrs) self.config_id = self.wired_in_config_id @classmethod def fetch(cls, gctx): - """Fetch the config object from SQL. This requires special - handling because of the weird way we treat config_id. + """ + Fetch the config object from SQL. This requires special handling + because of the weird way we treat config_id. """ return cls.sql_fetch(gctx, cls.wired_in_config_id) def serve_set(self, r_msg, cb, eb): - """Handle a set action. This requires special handling because + """ + Handle a set action. This requires special handling because config doesn't support the create method. """ if self.sql_fetch(self.gctx, self.config_id) is None: @@ -80,7 +90,8 @@ class config_elt(control_elt): control_elt.serve_set(self, r_msg, cb, eb) def serve_fetch_one(self): - """Find the config object on which a get or set method should + """ + Find the config object on which a get or set method should operate. """ r = self.sql_fetch(self.gctx, self.config_id) @@ -89,7 +100,9 @@ class config_elt(control_elt): return r class client_elt(control_elt): - """<client/> element.""" + """ + <client/> element. + """ element_name = "client" attributes = ("action", "tag", "client_id", "base_uri") @@ -104,7 +117,8 @@ class client_elt(control_elt): clear_https_ta_cache = False def endElement(self, stack, name, text): - """Handle subelements of <client/> element. These require special + """ + Handle subelements of <client/> element. These require special handling because modifying them invalidates the HTTPS trust anchor cache. """ @@ -113,14 +127,17 @@ class client_elt(control_elt): self.clear_https_ta_cache = True def serve_post_save_hook(self, q_pdu, r_pdu, cb, eb): - """Extra server actions for client_elt.""" + """ + Extra server actions for client_elt. + """ if self.clear_https_ta_cache: self.gctx.clear_https_ta_cache() self.clear_https_ta_cache = False cb() def serve_fetch_one(self): - """Find the client object on which a get, set, or destroy method + """ + Find the client object on which a get, set, or destroy method should operate. """ r = self.sql_fetch(self.gctx, self.client_id) @@ -137,7 +154,8 @@ class client_elt(control_elt): raise rpki.exceptions.ForbiddenURI class publication_object_elt(rpki.xml_utils.base_elt, publication_namespace): - """Virtual class for publishable objects. These have very similar + """ + Virtual class for publishable objects. These have very similar syntax, differences lie in underlying datatype and methods. XML methods are a little different from the pattern used for objects that support the create/set/get/list/destroy actions, but @@ -149,21 +167,27 @@ class publication_object_elt(rpki.xml_utils.base_elt, publication_namespace): payload = None def endElement(self, stack, name, text): - """Handle a publishable element element.""" + """ + Handle a publishable element element. + """ assert name == self.element_name, "Unexpected name %s, stack %s" % (name, stack) if text: self.payload = self.payload_type(Base64 = text) stack.pop() def toXML(self): - """Generate XML element for publishable object.""" + """ + Generate XML element for publishable object. + """ elt = self.make_elt() if self.payload: elt.text = base64.b64encode(self.payload.get_DER()) return elt def serve_dispatch(self, r_msg, cb, eb): - """Action dispatch handler.""" + """ + Action dispatch handler. + """ if self.client is None: raise rpki.exceptions.BadQuery, "Client query received on control channel" dispatch = { "publish" : self.serve_publish, @@ -180,7 +204,9 @@ class publication_object_elt(rpki.xml_utils.base_elt, publication_namespace): cb() def serve_publish(self): - """Publish an object.""" + """ + Publish an object. + """ rpki.log.info("Publishing %s as %s" % (repr(self.payload), repr(self.uri))) filename = self.uri_to_filename() dirname = os.path.dirname(filename) @@ -191,12 +217,16 @@ class publication_object_elt(rpki.xml_utils.base_elt, publication_namespace): f.close() def serve_withdraw(self): - """Withdraw an object.""" + """ + Withdraw an object. + """ rpki.log.info("Withdrawing %s" % repr(self.uri)) os.remove(self.uri_to_filename()) def uri_to_filename(self): - """Convert a URI to a local filename.""" + """ + Convert a URI to a local filename. + """ if not self.uri.startswith("rsync://"): raise rpki.exceptions.BadURISyntax filename = self.gctx.publication_base + self.uri[len("rsync://"):] @@ -205,25 +235,33 @@ class publication_object_elt(rpki.xml_utils.base_elt, publication_namespace): return filename class certificate_elt(publication_object_elt): - """<certificate/> element.""" + """ + <certificate/> element. + """ element_name = "certificate" payload_type = rpki.x509.X509 class crl_elt(publication_object_elt): - """<crl/> element.""" + """ + <crl/> element. + """ element_name = "crl" payload_type = rpki.x509.CRL class manifest_elt(publication_object_elt): - """<manifest/> element.""" + """ + <manifest/> element. + """ element_name = "manifest" payload_type = rpki.x509.SignedManifest class roa_elt(publication_object_elt): - """<roa/> element.""" + """ + <roa/> element. + """ element_name = "roa" payload_type = rpki.x509.ROA @@ -234,20 +272,26 @@ class roa_elt(publication_object_elt): obj2elt = dict((e.payload_type, e) for e in (certificate_elt, crl_elt, manifest_elt, roa_elt)) class report_error_elt(rpki.xml_utils.base_elt, publication_namespace): - """<report_error/> element.""" + """ + <report_error/> element. + """ element_name = "report_error" attributes = ("tag", "error_code") @classmethod def from_exception(cls, exc): - """Generate a <report_error/> element from an exception.""" + """ + Generate a <report_error/> element from an exception. + """ self = cls() self.error_code = exc.__class__.__name__ return self class msg(rpki.xml_utils.msg, publication_namespace): - """Publication PDU.""" + """ + Publication PDU. + """ ## @var version # Protocol version @@ -259,7 +303,9 @@ class msg(rpki.xml_utils.msg, publication_namespace): for x in (config_elt, client_elt, certificate_elt, crl_elt, manifest_elt, roa_elt, report_error_elt)) def serve_top_level(self, gctx, client, cb): - """Serve one msg PDU.""" + """ + Serve one msg PDU. + """ if self.type != "query": raise rpki.exceptions.BadQuery, "Message type is not query" r_msg = self.__class__() @@ -287,14 +333,18 @@ class msg(rpki.xml_utils.msg, publication_namespace): rpki.async.iterator(self, loop, done) class sax_handler(rpki.xml_utils.sax_handler): - """SAX handler for publication protocol.""" + """ + SAX handler for publication protocol. + """ pdu = msg name = "msg" version = "1" class cms_msg(rpki.x509.XML_CMS_object): - """Class to hold a CMS-signed publication PDU.""" + """ + Class to hold a CMS-signed publication PDU. + """ encoding = "us-ascii" schema = rpki.relaxng.publication diff --git a/rpkid/rpki/resource_set.py b/rpkid/rpki/resource_set.py index 10736812..5aeab3bc 100644 --- a/rpkid/rpki/resource_set.py +++ b/rpkid/rpki/resource_set.py @@ -1,4 +1,5 @@ -"""Classes dealing with sets of resources. +""" +Classes dealing with sets of resources. The basic mechanics of a resource set are the same for any of the resources we handle (ASNs, IPv4 addresses, or IPv6 addresses), so we @@ -33,21 +34,26 @@ import rpki.ipaddrs, rpki.oids, rpki.exceptions inherit_token = "<inherit>" class resource_range(object): - """Generic resource range type. Assumes underlying type is some - kind of integer. + """ + Generic resource range type. Assumes underlying type is some kind + of integer. This is a virtual class. You probably don't want to use this type directly. """ def __init__(self, min, max): - """Initialize and sanity check a resource_range.""" + """ + Initialize and sanity check a resource_range. + """ assert min <= max, "Mis-ordered range: %s before %s" % (str(min), str(max)) self.min = min self.max = max def __cmp__(self, other): - """Compare two resource_range objects.""" + """ + Compare two resource_range objects. + """ assert self.__class__ is other.__class__ c = self.min - other.min if c == 0: c = self.max - other.max @@ -56,9 +62,11 @@ class resource_range(object): return c class resource_range_as(resource_range): - """Range of Autonomous System Numbers. + """ + Range of Autonomous System Numbers. - Denotes a single ASN by a range whose min and max values are identical. + Denotes a single ASN by a range whose min and max values are + identical. """ ## @var datum_type @@ -67,30 +75,39 @@ class resource_range_as(resource_range): datum_type = long def __str__(self): - """Convert a resource_range_as to string format.""" + """ + Convert a resource_range_as to string format. + """ if self.min == self.max: return str(self.min) else: return str(self.min) + "-" + str(self.max) def to_rfc3779_tuple(self): - """Convert a resource_range_as to tuple format for RFC 3779 ASN.1 encoding.""" + """ + Convert a resource_range_as to tuple format for RFC 3779 ASN.1 encoding. + """ if self.min == self.max: return ("id", self.min) else: return ("range", (self.min, self.max)) class resource_range_ip(resource_range): - """Range of (generic) IP addresses. + """ + Range of (generic) IP addresses. Prefixes are converted to ranges on input, and ranges that can be represented as prefixes are written as prefixes on output. - This is a virtual class. You probably don't want to use it directly. + This is a virtual class. You probably don't want to use it + directly. """ def _prefixlen(self): - """Determine whether a resource_range_ip can be expressed as a prefix.""" + """ + Determine whether a resource_range_ip can be expressed as a + prefix. + """ mask = self.min ^ self.max if self.min & mask != 0: return -1 @@ -104,7 +121,9 @@ class resource_range_ip(resource_range): return prefixlen def __str__(self): - """Convert a resource_range_ip to string format.""" + """ + Convert a resource_range_ip to string format. + """ prefixlen = self._prefixlen() if prefixlen < 0: return str(self.min) + "-" + str(self.max) @@ -112,7 +131,10 @@ class resource_range_ip(resource_range): return str(self.min) + "/" + str(prefixlen) def to_rfc3779_tuple(self): - """Convert a resource_range_ip to tuple format for RFC 3779 ASN.1 encoding.""" + """ + Convert a resource_range_ip to tuple format for RFC 3779 ASN.1 + encoding. + """ prefixlen = self._prefixlen() if prefixlen < 0: return ("addressRange", (_long2bs(self.min, self.datum_type.bits, strip = 0), @@ -122,7 +144,9 @@ class resource_range_ip(resource_range): @classmethod def make_prefix(cls, address, prefixlen): - """Construct a resource range corresponding to a prefix.""" + """ + Construct a resource range corresponding to a prefix. + """ assert isinstance(address, cls.datum_type) and isinstance(prefixlen, (int, long)) assert prefixlen >= 0 and prefixlen <= cls.datum_type.bits, "Nonsensical prefix length: %s" % prefixlen mask = (1 << (cls.datum_type.bits - prefixlen)) - 1 @@ -130,7 +154,9 @@ class resource_range_ip(resource_range): return cls(cls.datum_type(address), cls.datum_type(address | mask)) class resource_range_ipv4(resource_range_ip): - """Range of IPv4 addresses.""" + """ + Range of IPv4 addresses. + """ ## @var datum_type # Type of underlying data (min and max). @@ -138,7 +164,9 @@ class resource_range_ipv4(resource_range_ip): datum_type = rpki.ipaddrs.v4addr class resource_range_ipv6(resource_range_ip): - """Range of IPv6 addresses.""" + """ + Range of IPv6 addresses. + """ ## @var datum_type # Type of underlying data (min and max). @@ -146,7 +174,9 @@ class resource_range_ipv6(resource_range_ip): datum_type = rpki.ipaddrs.v6addr def _rsplit(rset, that): - """Utility function to split a resource range into two resource ranges.""" + """ + Utility function to split a resource range into two resource ranges. + """ this = rset.pop(0) cell_type = type(this.min) assert type(this) is type(that) and type(this.max) is cell_type and \ @@ -160,8 +190,8 @@ def _rsplit(rset, that): rset.insert(1, type(this)(cell_type(that.max + 1), this.max)) class resource_set(list): - """Generic resource set. - This is a list subclass containing resource ranges. + """ + Generic resource set, a list subclass containing resource ranges. This is a virtual class. You probably don't want to use it directly. @@ -173,7 +203,9 @@ class resource_set(list): inherit = False def __init__(self, ini = None): - """Initialize a resource_set.""" + """ + Initialize a resource_set. + """ if isinstance(ini, (int, long)): ini = str(ini) if ini == inherit_token: @@ -197,14 +229,17 @@ class resource_set(list): assert self[i].max < self[i+1].min, "Resource overlap: %s %s" % (self[i], self[i+1]) def __str__(self): - """Convert a resource_set to string format.""" + """ + Convert a resource_set to string format. + """ if self.inherit: return inherit_token else: return ",".join(str(x) for x in self) def _comm(self, other): - """Like comm(1), sort of. + """ + Like comm(1), sort of. Returns a tuple of three resource sets: resources only in self, resources only in other, and resources in both. Used (not very @@ -236,7 +271,9 @@ class resource_set(list): return type(self)(only1), type(self)(only2), type(self)(both) def union(self, other): - """Set union for resource sets.""" + """ + Set union for resource sets. + """ assert not self.inherit assert type(self) is type(other), "Type mismatch: %s %s" % (repr(type(self)), repr(type(other))) set1 = self[:] @@ -272,7 +309,9 @@ class resource_set(list): return com[0].union(com[1]) def contains(self, item): - """Set membership test for resource sets.""" + """ + Set membership test for resource sets. + """ assert not self.inherit for i in self: if isinstance(item, type(i)) and i.min <= item.min and i.max >= item.max: @@ -284,7 +323,9 @@ class resource_set(list): return False def issubset(self, other): - """Test whether self is a subset (possibly improper) of other.""" + """ + Test whether self is a subset (possibly improper) of other. + """ for i in self: if not other.contains(i): return False @@ -296,7 +337,8 @@ class resource_set(list): @classmethod def from_sql(cls, sql, query, args = None): - """Create resource set from an SQL query. + """ + Create resource set from an SQL query. sql is an object that supports execute() and fetchall() methods like a DB API 2.0 cursor object. @@ -310,7 +352,9 @@ class resource_set(list): for (b, e) in sql.fetchall()]) class resource_set_as(resource_set): - """ASN resource set.""" + """ + Autonomous System Number resource set. + """ ## @var range_type # Type of range underlying this type of resource_set. @@ -318,7 +362,9 @@ class resource_set_as(resource_set): range_type = resource_range_as def parse_str(self, x): - """Parse ASN resource sets from text (eg, XML attributes).""" + """ + Parse ASN resource sets from text (eg, XML attributes). + """ r = re.match("^([0-9]+)-([0-9]+)$", x) if r: return resource_range_as(long(r.group(1)), long(r.group(2))) @@ -326,7 +372,10 @@ class resource_set_as(resource_set): return resource_range_as(long(x), long(x)) def parse_rfc3779_tuple(self, x): - """Parse ASN resource from tuple format generated by RFC 3779 ASN.1 decoder.""" + """ + Parse ASN resource from tuple format generated by RFC 3779 ASN.1 + decoder. + """ if x[0] == "asIdsOrRanges": for aor in x[1]: if aor[0] == "range": @@ -341,7 +390,10 @@ class resource_set_as(resource_set): self.inherit = True def to_rfc3779_tuple(self): - """Convert ASN resource set into tuple format used for RFC 3779 ASN.1 encoding.""" + """ + Convert ASN resource set into tuple format used for RFC 3779 ASN.1 + encoding. + """ if self: return ("asIdsOrRanges", tuple(a.to_rfc3779_tuple() for a in self)) elif self.inherit: @@ -350,14 +402,17 @@ class resource_set_as(resource_set): return None class resource_set_ip(resource_set): - """(Generic) IP address resource set. + """ + (Generic) IP address resource set. This is a virtual class. You probably don't want to use it directly. """ def parse_str(self, x): - """Parse IP address resource sets from text (eg, XML attributes).""" + """ + Parse IP address resource sets from text (eg, XML attributes). + """ r = re.match("^([0-9:.a-fA-F]+)-([0-9:.a-fA-F]+)$", x) if r: return self.range_type(self.range_type.datum_type(r.group(1)), self.range_type.datum_type(r.group(2))) @@ -367,7 +422,10 @@ class resource_set_ip(resource_set): raise RuntimeError, 'Bad IP resource "%s"' % (x) def parse_rfc3779_tuple(self, x): - """Parse IP address resource sets from tuple format generated by RFC 3779 ASN.1 decoder.""" + """ + Parse IP address resource sets from tuple format generated by RFC + 3779 ASN.1 decoder. + """ if x[0] == "addressesOrRanges": for aor in x[1]: if aor[0] == "addressRange": @@ -382,7 +440,10 @@ class resource_set_ip(resource_set): self.inherit = True def to_rfc3779_tuple(self): - """Convert IP resource set into tuple format used by RFC 3779 ASN.1 encoder.""" + """ + Convert IP resource set into tuple format used by RFC 3779 ASN.1 + encoder. + """ if self: return (self.afi, ("addressesOrRanges", tuple(a.to_rfc3779_tuple() for a in self))) elif self.inherit: @@ -391,7 +452,9 @@ class resource_set_ip(resource_set): return None class resource_set_ipv4(resource_set_ip): - """IPv4 address resource set.""" + """ + IPv4 address resource set. + """ ## @var range_type # Type of range underlying this type of resource_set. @@ -404,7 +467,9 @@ class resource_set_ipv4(resource_set_ip): afi = "\x00\x01" class resource_set_ipv6(resource_set_ip): - """IPv6 address resource set.""" + """ + IPv6 address resource set. + """ ## @var range_type # Type of range underlying this type of resource_set. @@ -417,7 +482,8 @@ class resource_set_ipv6(resource_set_ip): afi = "\x00\x02" def _bs2long(bs, addrlen, fill): - """Utility function to convert a bitstring (POW.pkix tuple + """ + Utility function to convert a bitstring (POW.pkix tuple representation) into a Python long. """ x = 0L @@ -428,7 +494,8 @@ def _bs2long(bs, addrlen, fill): return x def _long2bs(number, addrlen, prefixlen = None, strip = None): - """Utility function to convert a Python long into a POW.pkix tuple + """ + Utility function to convert a Python long into a POW.pkix tuple bitstring. This is a bit complicated because it supports the fiendishly compact encoding used in RFC 3779. """ @@ -448,8 +515,9 @@ def _long2bs(number, addrlen, prefixlen = None, strip = None): return tuple(bs) class resource_bag(object): - """Container to simplify passing around the usual triple of ASN, - IPv4, and IPv6 resource sets. + """ + Container to simplify passing around the usual triple of ASN, IPv4, + and IPv6 resource sets. """ ## @var asn @@ -471,25 +539,32 @@ class resource_bag(object): self.valid_until = valid_until def oversized(self, other): - """True iff self is oversized with respect to other.""" + """ + True iff self is oversized with respect to other. + """ return not self.asn.issubset(other.asn) or \ not self.v4.issubset(other.v4) or \ not self.v6.issubset(other.v6) def undersized(self, other): - """True iff self is undersized with respect to other.""" + """ + True iff self is undersized with respect to other. + """ return not other.asn.issubset(self.asn) or \ not other.v4.issubset(self.v4) or \ not other.v6.issubset(self.v6) @classmethod def from_rfc3779_tuples(cls, exts): - """Build a resource_bag from intermediate form generated by RFC 3779 ASN.1 decoder.""" + """ + Build a resource_bag from intermediate form generated by RFC 3779 + ASN.1 decoder. + """ asn = None v4 = None v6 = None for x in exts: - if x[0] == rpki.oids.name2oid["sbgp-autonomousSysNum"]: # + if x[0] == rpki.oids.name2oid["sbgp-autonomousSysNum"]: assert len(x[2]) == 1 or x[2][1] is None, "RDI not implemented: %s" % (str(x)) assert asn is None asn = resource_set_as(x[2][0]) @@ -504,7 +579,7 @@ class resource_bag(object): return cls(asn, v4, v6) def empty(self): - """Return True iff all resource sets in this bag are empty.""" + """True iff all resource sets in this bag are empty.""" return not self.asn and not self.v4 and not self.v6 def __eq__(self, other): @@ -517,8 +592,9 @@ class resource_bag(object): return not (self == other) def intersection(self, other): - """Compute intersection with another resource_bag. - valid_until attribute (if any) inherits from self. + """ + Compute intersection with another resource_bag. valid_until + attribute (if any) inherits from self. """ return self.__class__(self.asn.intersection(other.asn), self.v4.intersection(other.v4), @@ -526,8 +602,9 @@ class resource_bag(object): self.valid_until) def union(self, other): - """Compute union with another resource_bag. - valid_until attribute (if any) inherits from self. + """ + Compute union with another resource_bag. valid_until attribute + (if any) inherits from self. """ return self.__class__(self.asn.union(other.asn), self.v4.union(other.v4), @@ -557,7 +634,8 @@ class resource_bag(object): # worth. class roa_prefix(object): - """ROA prefix. This is similar to the resource_range_ip class, but + """ + ROA prefix. This is similar to the resource_range_ip class, but differs in that it only represents prefixes, never ranges, and includes the maximum prefix length as an additional value. @@ -574,9 +652,9 @@ class roa_prefix(object): # Maxmimum prefix length. def __init__(self, address, prefixlen, max_prefixlen = None): - """Initialize a ROA prefix. max_prefixlen is optional and - defaults to prefixlen. max_prefixlen must not be smaller than - prefixlen. + """ + Initialize a ROA prefix. max_prefixlen is optional and defaults + to prefixlen. max_prefixlen must not be smaller than prefixlen. """ if max_prefixlen is None: max_prefixlen = prefixlen @@ -586,8 +664,9 @@ class roa_prefix(object): self.max_prefixlen = max_prefixlen def __cmp__(self, other): - """Compare two ROA prefix objects. Comparision is based on - address, prefixlen, and max_prefixlen, in that order. + """ + Compare two ROA prefix objects. Comparision is based on address, + prefixlen, and max_prefixlen, in that order. """ assert self.__class__ is other.__class__ c = self.address - other.address @@ -598,14 +677,17 @@ class roa_prefix(object): return c def __str__(self): - """Convert a ROA prefix to string format.""" + """ + Convert a ROA prefix to string format. + """ if self.prefixlen == self.max_prefixlen: return str(self.address) + "/" + str(self.prefixlen) else: return str(self.address) + "/" + str(self.prefixlen) + "-" + str(self.max_prefixlen) def to_resource_range(self): - """Convert this ROA prefix to the equivilent resource_range_ip + """ + Convert this ROA prefix to the equivilent resource_range_ip object. This is an irreversable transformation because it loses the max_prefixlen attribute, nothing we can do about that. """ @@ -616,17 +698,24 @@ class roa_prefix(object): return self.address def max(self): - """Return highest address covered by prefix.""" + """ + Return highest address covered by prefix. + """ t = self.range_type.datum_type return t(self.address | ((1 << (t.bits - self.prefixlen)) - 1)) def to_roa_tuple(self): - """Convert a resource_range_ip to tuple format for ROA ASN.1 encoding.""" + """ + Convert a resource_range_ip to tuple format for ROA ASN.1 + encoding. + """ return (_long2bs(self.address, self.range_type.datum_type.bits, prefixlen = self.prefixlen), None if self.prefixlen == self.max_prefixlen else self.max_prefixlen) class roa_prefix_ipv4(roa_prefix): - """IPv4 ROA prefix.""" + """ + IPv4 ROA prefix. + """ ## @var range_type # Type of corresponding resource_range_ip. @@ -634,7 +723,9 @@ class roa_prefix_ipv4(roa_prefix): range_type = resource_range_ipv4 class roa_prefix_ipv6(roa_prefix): - """IPv6 ROA prefix.""" + """ + IPv6 ROA prefix. + """ ## @var range_type # Type of corresponding resource_range_ip. @@ -642,10 +733,14 @@ class roa_prefix_ipv6(roa_prefix): range_type = resource_range_ipv6 class roa_prefix_set(list): - """Set of ROA prefixes, analogous to the resource_set_ip class.""" + """ + Set of ROA prefixes, analogous to the resource_set_ip class. + """ def __init__(self, ini = None): - """Initialize a ROA prefix set.""" + """ + Initialize a ROA prefix set. + """ if isinstance(ini, str) and len(ini): self.extend(self.parse_str(s) for s in ini.split(",")) elif isinstance(ini, (list, tuple)): @@ -662,7 +757,9 @@ class roa_prefix_set(list): return ",".join(str(x) for x in self) def parse_str(self, x): - """Parse ROA prefix from text (eg, an XML attribute).""" + """ + Parse ROA prefix from text (eg, an XML attribute). + """ r = re.match("^([0-9:.a-fA-F]+)/([0-9]+)-([0-9]+)$", x) if r: return self.prefix_type(self.prefix_type.range_type.datum_type(r.group(1)), int(r.group(2)), int(r.group(3))) @@ -672,14 +769,16 @@ class roa_prefix_set(list): raise RuntimeError, 'Bad ROA prefix "%s"' % (x) def to_resource_set(self): - """Convert a ROA prefix set to a resource set. This is an + """ + Convert a ROA prefix set to a resource set. This is an irreversable transformation. """ return self.resource_set_type([p.to_resource_range() for p in self]) @classmethod def from_sql(cls, sql, query, args = None): - """Create ROA prefix set from an SQL query. + """ + Create ROA prefix set from an SQL query. sql is an object that supports execute() and fetchall() methods like a DB API 2.0 cursor object. @@ -693,15 +792,19 @@ class roa_prefix_set(list): for (x, y, z) in sql.fetchall()]) def to_roa_tuple(self): - """Convert ROA prefix set into tuple format used by ROA ASN.1 encoder. - This is a variation on the format used in RFC 3779.""" + """ + Convert ROA prefix set into tuple format used by ROA ASN.1 + encoder. This is a variation on the format used in RFC 3779. + """ if self: return (self.resource_set_type.afi, tuple(a.to_roa_tuple() for a in self)) else: return None class roa_prefix_set_ipv4(roa_prefix_set): - """Set of IPv4 ROA prefixes.""" + """ + Set of IPv4 ROA prefixes. + """ ## @var prefix_type # Type of underlying roa_prefix. @@ -714,7 +817,9 @@ class roa_prefix_set_ipv4(roa_prefix_set): resource_set_type = resource_set_ipv4 class roa_prefix_set_ipv6(roa_prefix_set): - """Set of IPv6 ROA prefixes.""" + """ + Set of IPv6 ROA prefixes. + """ ## @var prefix_type # Type of underlying roa_prefix. diff --git a/rpkid/rpki/roa.py b/rpkid/rpki/roa.py index ab178db0..9f7c1849 100644 --- a/rpkid/rpki/roa.py +++ b/rpkid/rpki/roa.py @@ -1,4 +1,5 @@ -"""ROA (Route Origin Authorization). +""" +ROA (Route Origin Authorization). At the moment this is just the ASN.1 encoder. diff --git a/rpkid/rpki/rpki_engine.py b/rpkid/rpki/rpki_engine.py index ed03ad29..0f78bf31 100644 --- a/rpkid/rpki/rpki_engine.py +++ b/rpkid/rpki/rpki_engine.py @@ -1,4 +1,5 @@ -"""Global context for rpkid. +""" +Global context for rpkid. $Id$ @@ -22,7 +23,9 @@ import rpki.resource_set, rpki.up_down, rpki.left_right, rpki.x509, rpki.sql import rpki.https, rpki.config, rpki.exceptions, rpki.relaxng, rpki.log, rpki.async class rpkid_context(object): - """A container for various global rpkid parameters.""" + """ + A container for various global rpkid parameters. + """ def __init__(self, cfg): @@ -42,7 +45,9 @@ class rpkid_context(object): self.publication_kludge_base = cfg.get("publication-kludge-base", "publication/") def irdb_query(self, self_id, child_id, callback, errback): - """Perform an IRDB callback query.""" + """ + Perform an IRDB callback query. + """ rpki.log.trace() @@ -75,7 +80,10 @@ class rpkid_context(object): errback = errback) def left_right_handler(self, query, path, cb): - """Process one left-right PDU.""" + """ + Process one left-right PDU. + """ + rpki.log.trace() def done(r_msg): @@ -96,7 +104,10 @@ class rpkid_context(object): cb(500, "Unhandled exception %s" % data) def up_down_handler(self, query, path, cb): - """Process one up-down PDU.""" + """ + Process one up-down PDU. + """ + rpki.log.trace() def done(reply): @@ -119,7 +130,11 @@ class rpkid_context(object): cb(400, "Could not process PDU: %s" % data) def cronjob_handler(self, query, path, cb): - """Periodic tasks. This will need another rewrite once we have internal timers.""" + """ + Periodic tasks. This is somewhat obsolete now that we have + internal timers, but the test framework still uses this, and I + haven't yet refactored this code to use the new timers. + """ rpki.log.trace() self.sql.ping() @@ -155,14 +170,18 @@ class rpkid_context(object): https_ta_cache = None def clear_https_ta_cache(self): - """Clear dynamic TLS trust anchors.""" + """ + Clear dynamic TLS trust anchors. + """ if self.https_ta_cache is not None: rpki.log.debug("Clearing HTTPS trusted cert cache") self.https_ta_cache = None def build_https_ta_cache(self): - """Build dynamic TLS trust anchors.""" + """ + Build dynamic TLS trust anchors. + """ if self.https_ta_cache is None: @@ -180,7 +199,9 @@ class rpkid_context(object): class ca_obj(rpki.sql.sql_persistant): - """Internal CA object.""" + """ + Internal CA object. + """ sql_template = rpki.sql.template( "ca", @@ -220,7 +241,8 @@ class ca_obj(rpki.sql.sql_persistant): return ca_detail_obj.sql_fetch_where(self.gctx, "ca_id = %s AND state = 'revoked'", (self.ca_id,)) def construct_sia_uri(self, parent, rc): - """Construct the sia_uri value for this CA given configured + """ + Construct the sia_uri value for this CA given configured information and the parent's up-down protocol list_response PDU. """ @@ -232,7 +254,8 @@ class ca_obj(rpki.sql.sql_persistant): return sia_uri + str(self.ca_id) + "/" def check_for_updates(self, parent, rc, cb, eb): - """Parent has signaled continued existance of a resource class we + """ + Parent has signaled continued existance of a resource class we already knew about, so we need to check for an updated certificate, changes in resource coverage, revocation and reissue with the same key, etc. @@ -289,8 +312,9 @@ class ca_obj(rpki.sql.sql_persistant): @classmethod def create(cls, parent, rc, cb, eb): - """Parent has signaled existance of a new resource class, so we - need to create and set up a corresponding CA object. + """ + Parent has signaled existance of a new resource class, so we need + to create and set up a corresponding CA object. """ self = cls() @@ -312,9 +336,10 @@ class ca_obj(rpki.sql.sql_persistant): rpki.up_down.issue_pdu.query(parent, self, ca_detail, done, eb) def delete(self, parent, callback): - """The list of current resource classes received from parent does - not include the class corresponding to this CA, so we need to - delete it (and its little dog too...). + """ + The list of current resource classes received from parent does not + include the class corresponding to this CA, so we need to delete + it (and its little dog too...). All certs published by this CA are now invalid, so need to withdraw them, the CRL, and the manifest from the repository, @@ -338,28 +363,35 @@ class ca_obj(rpki.sql.sql_persistant): rpki.async.iterator(self.ca_details(), loop, done) def next_serial_number(self): - """Allocate a certificate serial number.""" + """ + Allocate a certificate serial number. + """ self.last_issued_sn += 1 self.sql_mark_dirty() return self.last_issued_sn def next_manifest_number(self): - """Allocate a manifest serial number.""" + """ + Allocate a manifest serial number. + """ self.last_manifest_sn += 1 self.sql_mark_dirty() return self.last_manifest_sn def next_crl_number(self): - """Allocate a CRL serial number.""" + """ + Allocate a CRL serial number. + """ self.last_crl_sn += 1 self.sql_mark_dirty() return self.last_crl_sn def rekey(self, cb, eb): - """Initiate a rekey operation for this ca. Generate a new - keypair. Request cert from parent using new keypair. Mark result - as our active ca_detail. Reissue all child certs issued by this - ca using the new ca_detail. + """ + Initiate a rekey operation for this ca. Generate a new keypair. + Request cert from parent using new keypair. Mark result as our + active ca_detail. Reissue all child certs issued by this ca using + the new ca_detail. """ rpki.log.trace() @@ -380,7 +412,9 @@ class ca_obj(rpki.sql.sql_persistant): rpki.up_down.issue_pdu.query(parent, self, new_detail, done, eb) def revoke(self, cb, eb): - """Revoke deprecated ca_detail objects associated with this ca.""" + """ + Revoke deprecated ca_detail objects associated with this ca. + """ rpki.log.trace() @@ -390,7 +424,9 @@ class ca_obj(rpki.sql.sql_persistant): rpki.async.iterator(self.fetch_deprecated(), loop, cb) class ca_detail_obj(rpki.sql.sql_persistant): - """Internal CA detail object.""" + """ + Internal CA detail object. + """ sql_template = rpki.sql.template( "ca_detail", @@ -408,7 +444,9 @@ class ca_detail_obj(rpki.sql.sql_persistant): "ca_id") def sql_decode(self, vals): - """Extra assertions for SQL decode of a ca_detail_obj.""" + """ + Extra assertions for SQL decode of a ca_detail_obj. + """ rpki.sql.sql_persistant.sql_decode(self, vals) assert (self.public_key is None and self.private_key_id is None) or \ self.public_key.get_DER() == self.private_key_id.get_public_DER() @@ -444,7 +482,9 @@ class ca_detail_obj(rpki.sql.sql_persistant): return ca.sia_uri + self.public_key.gSKI() + ".mnf" def activate(self, ca, cert, uri, callback, errback, predecessor = None): - """Activate this ca_detail.""" + """ + Activate this ca_detail. + """ self.latest_ca_cert = cert self.ca_cert_uri = uri.rsync() @@ -475,7 +515,9 @@ class ca_detail_obj(rpki.sql.sql_persistant): self.generate_crl(callback = did_crl, errback = errback) def delete(self, ca, repository, cb, eb): - """Delete this ca_detail and all of the certs it issued.""" + """ + Delete this ca_detail and all of the certs it issued. + """ def withdraw_one_child(iterator, child_cert): repository.withdraw(child_cert.cert, child_cert.uri(ca), iterator, eb) @@ -501,7 +543,9 @@ class ca_detail_obj(rpki.sql.sql_persistant): rpki.async.iterator(self.child_certs(), withdraw_one_child, child_certs_done) def revoke(self, cb, eb): - """Request revocation of all certificates whose SKI matches the key for this ca_detail. + """ + Request revocation of all certificates whose SKI matches the key + for this ca_detail. Tasks: @@ -517,7 +561,8 @@ class ca_detail_obj(rpki.sql.sql_persistant): - Destroy old keypairs. - - Leave final CRL and manifest in place until their nextupdate time has passed. + - Leave final CRL and manifest in place until their nextupdate + time has passed. """ def parent_revoked(r_msg): @@ -562,8 +607,9 @@ class ca_detail_obj(rpki.sql.sql_persistant): rpki.up_down.revoke_pdu.query(self, parent_revoked, eb) def update(self, parent, ca, rc, sia_uri_changed, old_resources, callback, errback): - """Need to get a new certificate for this ca_detail and perhaps - frob children of this ca_detail. + """ + Need to get a new certificate for this ca_detail and perhaps frob + children of this ca_detail. """ def issued(issue_response): @@ -590,7 +636,9 @@ class ca_detail_obj(rpki.sql.sql_persistant): @classmethod def create(cls, ca): - """Create a new ca_detail object for a specified CA.""" + """ + Create a new ca_detail object for a specified CA. + """ self = cls() self.gctx = ca.gctx self.ca_id = ca.ca_id @@ -606,7 +654,9 @@ class ca_detail_obj(rpki.sql.sql_persistant): return self def issue_ee(self, ca, resources, subject_key, sia = None): - """Issue a new EE certificate.""" + """ + Issue a new EE certificate. + """ return self.latest_ca_cert.issue( keypair = self.private_key_id, @@ -621,7 +671,9 @@ class ca_detail_obj(rpki.sql.sql_persistant): def generate_manifest_cert(self, ca): - """Generate a new manifest certificate for this ca_detail.""" + """ + Generate a new manifest certificate for this ca_detail. + """ resources = rpki.resource_set.resource_bag( asn = rpki.resource_set.resource_set_as("<inherit>"), @@ -631,10 +683,11 @@ class ca_detail_obj(rpki.sql.sql_persistant): self.latest_manifest_cert = self.issue_ee(ca, resources, self.manifest_public_key) def issue(self, ca, child, subject_key, sia, resources, callback, errback, child_cert = None): - """Issue a new certificate to a child. Optional child_cert - argument specifies an existing child_cert object to update in - place; if not specified, we create a new one. Returns the - child_cert object containing the newly issued cert. + """ + Issue a new certificate to a child. Optional child_cert argument + specifies an existing child_cert object to update in place; if not + specified, we create a new one. Returns the child_cert object + containing the newly issued cert. """ assert child_cert is None or (child_cert.child_id == child.child_id and @@ -674,7 +727,8 @@ class ca_detail_obj(rpki.sql.sql_persistant): ca.parent().repository().publish(child_cert.cert, child_cert.uri(ca), published, errback) def generate_crl(self, callback, errback, nextUpdate = None): - """Generate a new CRL for this ca_detail. At the moment this is + """ + Generate a new CRL for this ca_detail. At the moment this is unconditional, that is, it is up to the caller to decide whether a new CRL is needed. """ @@ -707,7 +761,9 @@ class ca_detail_obj(rpki.sql.sql_persistant): repository.publish(self.latest_crl, self.crl_uri(ca), callback = callback, errback = errback) def generate_manifest(self, callback, errback, nextUpdate = None): - """Generate a new manifest for this ca_detail.""" + """ + Generate a new manifest for this ca_detail. + """ ca = self.ca() parent = ca.parent() @@ -739,7 +795,9 @@ class ca_detail_obj(rpki.sql.sql_persistant): repository.publish(self.latest_manifest, self.manifest_uri(ca), callback = callback, errback = errback) class child_cert_obj(rpki.sql.sql_persistant): - """Certificate that has been issued to a child.""" + """ + Certificate that has been issued to a child. + """ sql_template = rpki.sql.template( "child_cert", @@ -750,7 +808,9 @@ class child_cert_obj(rpki.sql.sql_persistant): "ski") def __init__(self, gctx = None, child_id = None, ca_detail_id = None, cert = None): - """Initialize a child_cert_obj.""" + """ + Initialize a child_cert_obj. + """ self.gctx = gctx self.child_id = child_id self.ca_detail_id = ca_detail_id @@ -775,7 +835,9 @@ class child_cert_obj(rpki.sql.sql_persistant): return ca.sia_uri + self.uri_tail() def revoke(self, callback, errback): - """Revoke a child cert.""" + """ + Revoke a child cert. + """ rpki.log.debug("Revoking %s" % repr(self)) ca_detail = self.ca_detail() @@ -791,8 +853,9 @@ class child_cert_obj(rpki.sql.sql_persistant): repository.withdraw(self.cert, self.uri(ca), done, errback) def reissue(self, ca_detail, callback = None, errback = None, resources = None, sia = None): - """Reissue an existing cert, reusing the public key. If the cert - we would generate is identical to the one we already have, we just + """ + Reissue an existing cert, reusing the public key. If the cert we + would generate is identical to the one we already have, we just return the one we already have. If we have to revoke the old certificate when generating the new one, we have to generate a new child_cert_obj, so calling code that needs the updated @@ -854,7 +917,8 @@ class child_cert_obj(rpki.sql.sql_persistant): @classmethod def fetch(cls, gctx = None, child = None, ca_detail = None, ski = None, unique = False): - """Fetch all child_cert objects matching a particular set of + """ + Fetch all child_cert objects matching a particular set of parameters. This is a wrapper to consolidate various queries that would otherwise be inline SQL WHERE expressions. In most cases code calls this indirectly, through methods in other classes. @@ -885,7 +949,9 @@ class child_cert_obj(rpki.sql.sql_persistant): return cls.sql_fetch_where(gctx, where, args) class revoked_cert_obj(rpki.sql.sql_persistant): - """Tombstone for a revoked certificate.""" + """ + Tombstone for a revoked certificate. + """ sql_template = rpki.sql.template( "revoked_cert", @@ -911,7 +977,9 @@ class revoked_cert_obj(rpki.sql.sql_persistant): @classmethod def revoke(cls, cert, ca_detail): - """Revoke a certificate.""" + """ + Revoke a certificate. + """ return cls( serial = cert.getSerial(), expires = cert.getNotAfter(), diff --git a/rpkid/rpki/sql.py b/rpkid/rpki/sql.py index 61c1ed8f..fc8cc809 100644 --- a/rpkid/rpki/sql.py +++ b/rpkid/rpki/sql.py @@ -1,4 +1,5 @@ -"""SQL interface code. +""" +SQL interface code. $Id$ @@ -21,7 +22,9 @@ import MySQLdb, warnings, _mysql_exceptions import rpki.x509, rpki.resource_set, rpki.sundial, rpki.log class session(object): - """SQL session layer.""" + """ + SQL session layer. + """ _exceptions_enabled = False @@ -84,7 +87,9 @@ class session(object): assert not self.dirty, "Dirty objects in SQL cache: %s" % self.dirty def sweep(self): - """Write any dirty objects out to SQL.""" + """ + Write any dirty objects out to SQL. + """ for s in self.dirty.copy(): rpki.log.debug("Sweeping %s" % repr(s)) if s.sql_deleted: @@ -94,7 +99,10 @@ class session(object): self.assert_pristine() class template(object): - """SQL template generator.""" + """ + SQL template generator. + """ + def __init__(self, table_name, index_column, *data_columns): """Build a SQL template.""" type_map = dict((x[0], x[1]) for x in data_columns if isinstance(x, tuple)) @@ -113,7 +121,8 @@ class template(object): self.delete = "DELETE FROM %s WHERE %s = %%s" % (table_name, index_column) class sql_persistant(object): - """Mixin for persistant class that needs to be stored in SQL. + """ + Mixin for persistant class that needs to be stored in SQL. """ ## @var sql_in_db @@ -133,7 +142,8 @@ class sql_persistant(object): @classmethod def sql_fetch(cls, gctx, id): - """Fetch one object from SQL, based on its primary key. + """ + Fetch one object from SQL, based on its primary key. Since in this one case we know that the primary index is also the cache key, we check for a cache hit directly in the hope of @@ -155,7 +165,9 @@ class sql_persistant(object): @classmethod def sql_fetch_where1(cls, gctx, where, args = None): - """Fetch one object from SQL, based on an arbitrary SQL WHERE expression.""" + """ + Fetch one object from SQL, based on an arbitrary SQL WHERE expression. + """ results = cls.sql_fetch_where(gctx, where, args) if len(results) == 0: return None @@ -173,7 +185,9 @@ class sql_persistant(object): @classmethod def sql_fetch_where(cls, gctx, where, args = None): - """Fetch objects of this type matching an arbitrary SQL WHERE expression.""" + """ + Fetch objects of this type matching an arbitrary SQL WHERE expression. + """ if where is None: assert args is None if cls.sql_debug: @@ -195,7 +209,9 @@ class sql_persistant(object): @classmethod def sql_init(cls, gctx, row, key): - """Initialize one Python object from the result of a SQL query.""" + """ + Initialize one Python object from the result of a SQL query. + """ self = cls() self.gctx = gctx self.sql_decode(dict(zip(cls.sql_template.columns, row))) @@ -221,7 +237,9 @@ class sql_persistant(object): self.sql_deleted = True def sql_store(self): - """Store this object to SQL.""" + """ + Store this object to SQL. + """ args = self.sql_encode() if not self.sql_in_db: if self.sql_debug: @@ -241,7 +259,9 @@ class sql_persistant(object): self.sql_in_db = True def sql_delete(self): - """Delete this object from SQL.""" + """ + Delete this object from SQL. + """ if self.sql_in_db: id = getattr(self, self.sql_template.index) self.gctx.sql.execute(self.sql_template.delete, id) @@ -253,7 +273,8 @@ class sql_persistant(object): self.sql_mark_clean() def sql_encode(self): - """Convert object attributes into a dict for use with canned SQL + """ + Convert object attributes into a dict for use with canned SQL queries. This is a default version that assumes a one-to-one mapping between column names in SQL and attribute names in Python. If you need something fancier, override this. @@ -265,7 +286,8 @@ class sql_persistant(object): return d def sql_decode(self, vals): - """Initialize an object with values returned by self.sql_fetch(). + """ + Initialize an object with values returned by self.sql_fetch(). This is a default version that assumes a one-to-one mapping between column names in SQL and attribute names in Python. If you need something fancier, override this. diff --git a/rpkid/rpki/sundial.py b/rpkid/rpki/sundial.py index 9eeea86e..f89e1297 100644 --- a/rpkid/rpki/sundial.py +++ b/rpkid/rpki/sundial.py @@ -1,4 +1,5 @@ -"""Unified RPKI date/time handling, based on the standard Python datetime module. +""" +Unified RPKI date/time handling, based on the standard Python datetime module. Module name chosen to sidestep a nightmare of import-related errors that occur with the more obvious module names. @@ -28,12 +29,14 @@ def now(): return datetime.utcnow() class datetime(pydatetime.datetime): - """RPKI extensions to standard datetime.datetime class. All work - here is in UTC, so we use naive datetime objects. + """ + RPKI extensions to standard datetime.datetime class. All work here + is in UTC, so we use naive datetime objects. """ def totimestamp(self): - """Convert to seconds from epoch (like time.time()). Conversion + """ + Convert to seconds from epoch (like time.time()). Conversion method is a bit silly, but avoids time module timezone whackiness. """ return int(self.strftime("%s")) @@ -58,7 +61,9 @@ class datetime(pydatetime.datetime): @classmethod def fromASN1tuple(cls, x): - """Convert from ASN.1 tuple representation.""" + """ + Convert from ASN.1 tuple representation. + """ assert isinstance(x, tuple) and len(x) == 2 and x[0] in ("utcTime", "generalTime") if x[0] == "utcTime": return cls.fromUTCTime(x[1]) @@ -71,7 +76,9 @@ class datetime(pydatetime.datetime): PKIX_threshhold = pydatetime.datetime(2050, 1, 1) def toASN1tuple(self): - """Convert to ASN.1 tuple representation.""" + """ + Convert to ASN.1 tuple representation. + """ if self < self.PKIX_threshhold: return "utcTime", self.toUTCTime() else: @@ -79,7 +86,9 @@ class datetime(pydatetime.datetime): @classmethod def fromXMLtime(cls, x): - """Convert from XML time representation.""" + """ + Convert from XML time representation. + """ if x is None: return None else: @@ -94,13 +103,16 @@ class datetime(pydatetime.datetime): @classmethod def fromdatetime(cls, x): - """Convert a datetime.datetime object into this subclass. - This is whacky due to the weird constructors for datetime. + """ + Convert a datetime.datetime object into this subclass. This is + whacky due to the weird constructors for datetime. """ return cls.combine(x.date(), x.time()) def __add__(self, other): - """Force correct class for timedelta results.""" + """ + Force correct class for timedelta results. + """ x = pydatetime.datetime.__add__(self, other) if isinstance(x, pydatetime.timedelta): return timedelta.fromtimedelta(x) @@ -108,7 +120,9 @@ class datetime(pydatetime.datetime): return datetime.fromdatetime(x) def __sub__(self, other): - """Force correct class for timedelta results.""" + """ + Force correct class for timedelta results. + """ x = pydatetime.datetime.__sub__(self, other) if isinstance(x, pydatetime.timedelta): return timedelta.fromtimedelta(x) @@ -121,7 +135,8 @@ class datetime(pydatetime.datetime): return cls.fromdatetime(x) def to_sql(self): - """Convert to SQL storage format. + """ + Convert to SQL storage format. There's something whacky going on in the MySQLdb module, it throws range errors when storing a derived type into a DATETIME column. @@ -143,7 +158,8 @@ class datetime(pydatetime.datetime): return other if other < self else self class timedelta(pydatetime.timedelta): - """Timedelta with text parsing. This accepts two input formats: + """ + Timedelta with text parsing. This accepts two input formats: - A simple integer, indicating a number of seconds. @@ -168,7 +184,9 @@ class timedelta(pydatetime.timedelta): @classmethod def parse(cls, arg): - """Parse text into a timedelta object.""" + """ + Parse text into a timedelta object. + """ if not isinstance(arg, str): return cls(seconds = arg) elif arg.isdigit(): diff --git a/rpkid/rpki/up_down.py b/rpkid/rpki/up_down.py index 825a5d14..8b93f5a8 100644 --- a/rpkid/rpki/up_down.py +++ b/rpkid/rpki/up_down.py @@ -1,4 +1,5 @@ -"""RPKI "up-down" protocol. +""" +RPKI "up-down" protocol. $Id$ @@ -26,13 +27,15 @@ xmlns = "http://www.apnic.net/specs/rescerts/up-down/" nsmap = { None : xmlns } class base_elt(object): - """Generic PDU object. + """ + Generic PDU object. Virtual class, just provides some default methods. """ def startElement(self, stack, name, attrs): - """Ignore startElement() if there's no specific handler. + """ + Ignore startElement() if there's no specific handler. Some elements have no attributes and we only care about their text content. @@ -40,14 +43,17 @@ class base_elt(object): pass def endElement(self, stack, name, text): - """Ignore endElement() if there's no specific handler. + """ + Ignore endElement() if there's no specific handler. If we don't need to do anything else, just pop the stack. """ stack.pop() def make_elt(self, name, *attrs): - """Construct a element, copying over a set of attributes.""" + """ + Construct a element, copying over a set of attributes. + """ elt = lxml.etree.Element("{%s}%s" % (xmlns, name), nsmap=nsmap) for key in attrs: val = getattr(self, key, None) @@ -56,7 +62,9 @@ class base_elt(object): return elt def make_b64elt(self, elt, name, value=None): - """Construct a sub-element with Base64 text content.""" + """ + Construct a sub-element with Base64 text content. + """ if value is None: value = getattr(self, name, None) if value is not None: @@ -71,10 +79,14 @@ class base_elt(object): pass class multi_uri(list): - """Container for a set of URIs.""" + """ + Container for a set of URIs. + """ def __init__(self, ini): - """Initialize a set of URIs, which includes basic some syntax checking.""" + """ + Initialize a set of URIs, which includes basic some syntax checking. + """ if isinstance(ini, (list, tuple)): self[:] = ini elif isinstance(ini, str): @@ -90,17 +102,23 @@ class multi_uri(list): return ",".join(self) def rsync(self): - """Find first rsync://... URI in self.""" + """ + Find first rsync://... URI in self. + """ for s in self: if s.startswith("rsync://"): return s return None class certificate_elt(base_elt): - """Up-Down protocol representation of an issued certificate.""" + """ + Up-Down protocol representation of an issued certificate. + """ def startElement(self, stack, name, attrs): - """Handle attributes of <certificate/> element.""" + """ + Handle attributes of <certificate/> element. + """ assert name == "certificate", "Unexpected name %s, stack %s" % (name, stack) self.cert_url = multi_uri(attrs["cert_url"]) self.req_resource_set_as = rpki.resource_set.resource_set_as(attrs.get("req_resource_set_as")) @@ -108,20 +126,26 @@ class certificate_elt(base_elt): self.req_resource_set_ipv6 = rpki.resource_set.resource_set_ipv6(attrs.get("req_resource_set_ipv6")) def endElement(self, stack, name, text): - """Handle text content of a <certificate/> element.""" + """ + Handle text content of a <certificate/> element. + """ assert name == "certificate", "Unexpected name %s, stack %s" % (name, stack) self.cert = rpki.x509.X509(Base64=text) stack.pop() def toXML(self): - """Generate a <certificate/> element.""" + """ + Generate a <certificate/> element. + """ elt = self.make_elt("certificate", "cert_url", "req_resource_set_as", "req_resource_set_ipv4", "req_resource_set_ipv6") elt.text = self.cert.get_Base64() return elt class class_elt(base_elt): - """Up-Down protocol representation of a resource class.""" + """ + Up-Down protocol representation of a resource class. + """ issuer = None @@ -130,7 +154,9 @@ class class_elt(base_elt): self.certs = [] def startElement(self, stack, name, attrs): - """Handle <class/> elements and their children.""" + """ + Handle <class/> elements and their children. + """ if name == "certificate": cert = certificate_elt() self.certs.append(cert) @@ -147,7 +173,9 @@ class class_elt(base_elt): self.resource_set_notafter = rpki.sundial.datetime.fromXMLtime(attrs.get("resource_set_notafter")) def endElement(self, stack, name, text): - """Handle <class/> elements and their children.""" + """ + Handle <class/> elements and their children. + """ if name == "issuer": self.issuer = rpki.x509.X509(Base64=text) else: @@ -155,7 +183,9 @@ class class_elt(base_elt): stack.pop() def toXML(self): - """Generate a <class/> element.""" + """ + Generate a <class/> element. + """ elt = self.make_elt("class", "class_name", "cert_url", "resource_set_as", "resource_set_ipv4", "resource_set_ipv6", "resource_set_notafter", "suggested_sia_head") @@ -165,28 +195,36 @@ class class_elt(base_elt): return elt def to_resource_bag(self): - """Build a resource_bag from from this <class/> element.""" + """ + Build a resource_bag from from this <class/> element. + """ return rpki.resource_set.resource_bag(self.resource_set_as, self.resource_set_ipv4, self.resource_set_ipv6, self.resource_set_notafter) def from_resource_bag(self, bag): - """Set resources of this class element from a resource_bag.""" + """ + Set resources of this class element from a resource_bag. + """ self.resource_set_as = bag.asn self.resource_set_ipv4 = bag.v4 self.resource_set_ipv6 = bag.v6 self.resource_set_notafter = bag.valid_until class list_pdu(base_elt): - """Up-Down protocol "list" PDU.""" + """ + Up-Down protocol "list" PDU. + """ def toXML(self): """Generate (empty) payload of "list" PDU.""" return [] def serve_pdu(self, q_msg, r_msg, child, callback, errback): - """Serve one "list" PDU.""" + """ + Serve one "list" PDU. + """ def handle(irdb_resources): @@ -223,14 +261,18 @@ class list_pdu(base_elt): parent.query_up_down(cls(), cb, eb) class class_response_syntax(base_elt): - """Syntax for Up-Down protocol "list_response" and "issue_response" PDUs.""" + """ + Syntax for Up-Down protocol "list_response" and "issue_response" PDUs. + """ def __init__(self): """Initialize class_response_syntax.""" self.classes = [] def startElement(self, stack, name, attrs): - """Handle "list_response" and "issue_response" PDUs.""" + """ + Handle "list_response" and "issue_response" PDUs. + """ assert name == "class", "Unexpected name %s, stack %s" % (name, stack) c = class_elt() self.classes.append(c) @@ -242,14 +284,20 @@ class class_response_syntax(base_elt): return [c.toXML() for c in self.classes] class list_response_pdu(class_response_syntax): - """Up-Down protocol "list_response" PDU.""" + """ + Up-Down protocol "list_response" PDU. + """ pass class issue_pdu(base_elt): - """Up-Down protocol "issue" PDU.""" + """ + Up-Down protocol "issue" PDU. + """ def startElement(self, stack, name, attrs): - """Handle "issue" PDU.""" + """ + Handle "issue" PDU. + """ assert name == "request", "Unexpected name %s, stack %s" % (name, stack) self.class_name = attrs["class_name"] self.req_resource_set_as = rpki.resource_set.resource_set_as(attrs.get("req_resource_set_as")) @@ -257,20 +305,26 @@ class issue_pdu(base_elt): self.req_resource_set_ipv6 = rpki.resource_set.resource_set_ipv6(attrs.get("req_resource_set_ipv6")) def endElement(self, stack, name, text): - """Handle "issue" PDU.""" + """ + Handle "issue" PDU. + """ assert name == "request", "Unexpected name %s, stack %s" % (name, stack) self.pkcs10 = rpki.x509.PKCS10(Base64=text) stack.pop() def toXML(self): - """Generate payload of "issue" PDU.""" + """ + Generate payload of "issue" PDU. + """ elt = self.make_elt("request", "class_name", "req_resource_set_as", "req_resource_set_ipv4", "req_resource_set_ipv6") elt.text = self.pkcs10.get_Base64() return [elt] def serve_pdu(self, q_msg, r_msg, child, callback, errback): - """Serve one issue request PDU.""" + """ + Serve one issue request PDU. + """ # Subsetting not yet implemented, this is the one place where we # have to handle it, by reporting that we're lame. @@ -336,7 +390,9 @@ class issue_pdu(base_elt): @classmethod def query(cls, parent, ca, ca_detail, callback, errback): - """Send an "issue" request to parent associated with ca.""" + """ + Send an "issue" request to parent associated with ca. + """ assert ca_detail is not None and ca_detail.state in ("pending", "active") sia = ((rpki.oids.name2oid["id-ad-caRepository"], ("uri", ca.sia_uri)), (rpki.oids.name2oid["id-ad-rpkiManifest"], ("uri", ca_detail.manifest_uri(ca)))) @@ -346,17 +402,22 @@ class issue_pdu(base_elt): parent.query_up_down(self, callback, errback) class issue_response_pdu(class_response_syntax): - """Up-Down protocol "issue_response" PDU.""" + """ + Up-Down protocol "issue_response" PDU. + """ def check_response(self): - """Check whether this looks like a reasonable issue_response PDU. + """ + Check whether this looks like a reasonable issue_response PDU. XML schema should be tighter for this response. """ if len(self.classes) != 1 or len(self.classes[0].certs) != 1: raise rpki.exceptions.BadIssueResponse class revoke_syntax(base_elt): - """Syntax for Up-Down protocol "revoke" and "revoke_response" PDUs.""" + """ + Syntax for Up-Down protocol "revoke" and "revoke_response" PDUs. + """ def startElement(self, stack, name, attrs): """Handle "revoke" PDU.""" @@ -368,14 +429,18 @@ class revoke_syntax(base_elt): return [self.make_elt("key", "class_name", "ski")] class revoke_pdu(revoke_syntax): - """Up-Down protocol "revoke" PDU.""" + """ + Up-Down protocol "revoke" PDU. + """ def get_SKI(self): """Convert g(SKI) encoding from PDU back to raw SKI.""" return base64.urlsafe_b64decode(self.ski + "=") def serve_pdu(self, q_msg, r_msg, child, cb, eb): - """Serve one revoke request PDU.""" + """ + Serve one revoke request PDU. + """ def loop1(iterator1, ca_detail): @@ -395,7 +460,9 @@ class revoke_pdu(revoke_syntax): @classmethod def query(cls, ca_detail, cb, eb): - """Send a "revoke" request to parent associated with ca_detail.""" + """ + Send a "revoke" request to parent associated with ca_detail. + """ ca = ca_detail.ca() parent = ca.parent() self = cls() @@ -404,12 +471,16 @@ class revoke_pdu(revoke_syntax): parent.query_up_down(self, cb, eb) class revoke_response_pdu(revoke_syntax): - """Up-Down protocol "revoke_response" PDU.""" + """ + Up-Down protocol "revoke_response" PDU. + """ pass class error_response_pdu(base_elt): - """Up-Down protocol "error_response" PDU.""" + """ + Up-Down protocol "error_response" PDU. + """ codes = { 1101 : "Already processing request", @@ -426,13 +497,17 @@ class error_response_pdu(base_elt): rpki.exceptions.NoActiveCA : 1202 } def __init__(self, exception = None): - """Initialize an error_response PDU from an exception object.""" + """ + Initialize an error_response PDU from an exception object. + """ if exception is not None: self.status = self.exceptions.get(type(exception), 2001) self.description = str(exception) def endElement(self, stack, name, text): - """Handle "error_response" PDU.""" + """ + Handle "error_response" PDU. + """ if name == "status": code = int(text) if code not in self.codes: @@ -446,7 +521,9 @@ class error_response_pdu(base_elt): stack[-1].endElement(stack, name, text) def toXML(self): - """Generate payload of "error_response" PDU.""" + """ + Generate payload of "error_response" PDU. + """ assert self.status in self.codes elt = self.make_elt("status") elt.text = str(self.status) @@ -459,13 +536,16 @@ class error_response_pdu(base_elt): return payload def check_response(self): - """Handle an error response. For now, just raise an exception, + """ + Handle an error response. For now, just raise an exception, perhaps figure out something more clever to do later. """ raise rpki.exceptions.UpstreamError, self.codes[self.status] class message_pdu(base_elt): - """Up-Down protocol message wrapper PDU.""" + """ + Up-Down protocol message wrapper PDU. + """ version = 1 @@ -481,13 +561,16 @@ class message_pdu(base_elt): type2name = dict((v, k) for k, v in name2type.items()) def toXML(self): - """Generate payload of message PDU.""" + """ + Generate payload of message PDU. + """ elt = self.make_elt("message", "version", "sender", "recipient", "type") elt.extend(self.payload.toXML()) return elt def startElement(self, stack, name, attrs): - """Handle message PDU. + """ + Handle message PDU. Payload of the <message/> element varies depending on the "type" attribute, so after some basic checks we have to instantiate the @@ -506,7 +589,9 @@ class message_pdu(base_elt): lxml.etree.tostring(self.toXML(), pretty_print = True, encoding = "UTF-8") def serve_top_level(self, child, callback): - """Serve one message request PDU.""" + """ + Serve one message request PDU. + """ r_msg = message_pdu() r_msg.sender = self.recipient @@ -528,7 +613,9 @@ class message_pdu(base_elt): lose(edata) def serve_error(self, exception): - """Generate an error_response message PDU.""" + """ + Generate an error_response message PDU. + """ r_msg = message_pdu() r_msg.sender = self.recipient r_msg.recipient = self.sender @@ -538,7 +625,9 @@ class message_pdu(base_elt): @classmethod def make_query(cls, payload, sender, recipient): - """Construct one message PDU.""" + """ + Construct one message PDU. + """ assert not cls.type2name[type(payload)].endswith("_response") if sender is None: sender = "tweedledee" @@ -552,14 +641,18 @@ class message_pdu(base_elt): return self class sax_handler(rpki.xml_utils.sax_handler): - """SAX handler for Up-Down protocol.""" + """ + SAX handler for Up-Down protocol. + """ pdu = message_pdu name = "message" version = "1" class cms_msg(rpki.x509.XML_CMS_object): - """Class to hold a CMS-signed up-down PDU.""" + """ + Class to hold a CMS-signed up-down PDU. + """ encoding = "UTF-8" schema = rpki.relaxng.up_down diff --git a/rpkid/rpki/x509.py b/rpkid/rpki/x509.py index 9825e609..91ab48bd 100644 --- a/rpkid/rpki/x509.py +++ b/rpkid/rpki/x509.py @@ -1,4 +1,5 @@ -"""One X.509 implementation to rule them all... +""" +One X.509 implementation to rule them all... ...and in the darkness hide the twisty maze of partially overlapping X.509 support packages in Python. @@ -47,7 +48,8 @@ import rpki.exceptions, rpki.resource_set, rpki.oids, rpki.sundial import rpki.manifest, rpki.roa, rpki.log, rpki.async def calculate_SKI(public_key_der): - """Calculate the SKI value given the DER representation of a public + """ + Calculate the SKI value given the DER representation of a public key, which requires first peeling the ASN.1 wrapper off the key. """ k = POW.pkix.SubjectPublicKeyInfo() @@ -57,20 +59,28 @@ def calculate_SKI(public_key_der): return d.digest() class PEM_converter(object): - """Convert between DER and PEM encodings for various kinds of ASN.1 data.""" + """ + Convert between DER and PEM encodings for various kinds of ASN.1 data. + """ def __init__(self, kind): # "CERTIFICATE", "RSA PRIVATE KEY", ... - """Initialize PEM_converter.""" + """ + Initialize PEM_converter. + """ self.b = "-----BEGIN %s-----" % kind self.e = "-----END %s-----" % kind def looks_like_PEM(self, text): - """Guess whether text looks like a PEM encoding.""" + """ + Guess whether text looks like a PEM encoding. + """ b = text.find(self.b) return b >= 0 and text.find(self.e) > b + len(self.b) def to_DER(self, pem): - """Convert from PEM to DER.""" + """ + Convert from PEM to DER. + """ lines = [line.strip() for line in pem.splitlines(0)] while lines and lines.pop(0) != self.b: pass @@ -81,7 +91,9 @@ class PEM_converter(object): return base64.b64decode("".join(lines)) def to_PEM(self, der): - """Convert from DER to PEM.""" + """ + Convert from DER to PEM. + """ b64 = base64.b64encode(der) pem = self.b + "\n" while len(b64) > 64: @@ -90,7 +102,9 @@ class PEM_converter(object): return pem + b64 + "\n" + self.e + "\n" class DER_object(object): - """Virtual class to hold a generic DER object.""" + """ + Virtual class to hold a generic DER object. + """ ## Formats supported in this object formats = ("DER",) @@ -105,31 +119,39 @@ class DER_object(object): ## DER value of this object def empty(self): - """Test whether this object is empty.""" + """ + Test whether this object is empty. + """ for a in self.formats: if getattr(self, a, None) is not None: return False return True def clear(self): - """Make this object empty.""" + """ + Make this object empty. + """ for a in self.formats + self.other_clear: setattr(self, a, None) def __init__(self, **kw): - """Initialize a DER_object.""" + """ + Initialize a DER_object. + """ self.clear() if len(kw): self.set(**kw) def set(self, **kw): - """Set this object by setting one of its known formats. + """ + Set this object by setting one of its known formats. This method only allows one to set one format at a time. Subsequent calls will clear the object first. The point of all this is to let the object's internal converters handle mustering the object into whatever format you need at the moment. """ + if len(kw) == 1: name = kw.keys()[0] if name in self.formats: @@ -156,7 +178,8 @@ class DER_object(object): raise rpki.exceptions.DERObjectConversionError, "Can't honor conversion request %s" % repr(kw) def get_DER(self): - """Get the DER value of this object. + """ + Get the DER value of this object. Subclasses will almost certainly override this method. """ @@ -178,60 +201,83 @@ class DER_object(object): return cmp(self.get_DER(), other.get_DER()) def hSKI(self): - """Return hexadecimal string representation of SKI for this - object. Only work for subclasses that implement get_SKI(). + """ + Return hexadecimal string representation of SKI for this object. + Only work for subclasses that implement get_SKI(). """ ski = self.get_SKI() return ":".join(("%02X" % ord(i) for i in ski)) if ski else "" def gSKI(self): - """Calculate g(SKI) for this object. Only work for subclasses + """ + Calculate g(SKI) for this object. Only work for subclasses that implement get_SKI(). """ return base64.urlsafe_b64encode(self.get_SKI()).rstrip("=") def hAKI(self): - """Return hexadecimal string representation of AKI for this + """ + Return hexadecimal string representation of AKI for this object. Only work for subclasses that implement get_AKI(). """ aki = self.get_AKI() return ":".join(("%02X" % ord(i) for i in aki)) if aki else "" def gAKI(self): - """Calculate g(AKI) for this object. Only work for subclasses + """ + Calculate g(AKI) for this object. Only work for subclasses that implement get_AKI(). """ return base64.urlsafe_b64encode(self.get_AKI()).rstrip("=") def get_AKI(self): - """Get the AKI extension from this object. Only works for subclasses that support getExtension().""" + """ + Get the AKI extension from this object. Only works for subclasses + that support getExtension(). + """ aki = (self.get_POWpkix().getExtension(rpki.oids.name2oid["authorityKeyIdentifier"]) or ((), 0, None))[2] return aki[0] if isinstance(aki, tuple) else aki def get_SKI(self): - """Get the SKI extension from this object. Only works for subclasses that support getExtension().""" + """ + Get the SKI extension from this object. Only works for subclasses + that support getExtension(). + """ return (self.get_POWpkix().getExtension(rpki.oids.name2oid["subjectKeyIdentifier"]) or ((), 0, None))[2] def get_SIA(self): - """Get the SIA extension from this object. Only works for subclasses that support getExtension().""" + """ + Get the SIA extension from this object. Only works for subclasses + that support getExtension(). + """ return (self.get_POWpkix().getExtension(rpki.oids.name2oid["subjectInfoAccess"]) or ((), 0, None))[2] def get_AIA(self): - """Get the SIA extension from this object. Only works for subclasses that support getExtension().""" + """ + Get the SIA extension from this object. Only works for subclasses + that support getExtension(). + """ return (self.get_POWpkix().getExtension(rpki.oids.name2oid["subjectInfoAccess"]) or ((), 0, None))[2] def get_basicConstraints(self): - """Get the basicConstraints extension from this object. Only works for subclasses that support getExtension().""" + """ + Get the basicConstraints extension from this object. Only works + for subclasses that support getExtension(). + """ return (self.get_POWpkix().getExtension(rpki.oids.name2oid["basicConstraints"]) or ((), 0, None))[2] def is_CA(self): - """Return True if and only if object has the basicConstraints extension and its cA value is true.""" + """ + Return True if and only if object has the basicConstraints + extension and its cA value is true. + """ basicConstraints = self.get_basicConstraints() return basicConstraints and basicConstraints[0] != 0 def get_3779resources(self): - """Get RFC 3779 resources as rpki.resource_set objects. - Only works for subclasses that support getExtensions(). + """ + Get RFC 3779 resources as rpki.resource_set objects. Only works + for subclasses that support getExtensions(). """ resources = rpki.resource_set.resource_bag.from_rfc3779_tuples(self.get_POWpkix().getExtensions()) try: @@ -250,7 +296,8 @@ class DER_object(object): return self.get_DER() def dumpasn1(self): - """Pretty print an ASN.1 DER object using cryptlib dumpasn1 tool. + """ + Pretty print an ASN.1 DER object using cryptlib dumpasn1 tool. Use a temporary file rather than popen4() because dumpasn1 uses seek() when decoding ASN.1 content nested in OCTET STRING values. """ @@ -269,7 +316,8 @@ class DER_object(object): return ret class X509(DER_object): - """X.509 certificates. + """ + X.509 certificates. This class is designed to hold all the different representations of X.509 certs we're using and convert between them. X.509 support in @@ -282,7 +330,9 @@ class X509(DER_object): pem_converter = PEM_converter("CERTIFICATE") def get_DER(self): - """Get the DER value of this certificate.""" + """ + Get the DER value of this certificate. + """ assert not self.empty() if self.DER: return self.DER @@ -301,14 +351,18 @@ class X509(DER_object): raise rpki.exceptions.DERObjectConversionError, "No conversion path to DER available" def get_POW(self): - """Get the POW value of this certificate.""" + """ + Get the POW value of this certificate. + """ assert not self.empty() if not self.POW: self.POW = POW.derRead(POW.X509_CERTIFICATE, self.get_DER()) return self.POW def get_POWpkix(self): - """Get the POW.pkix value of this certificate.""" + """ + Get the POW.pkix value of this certificate. + """ assert not self.empty() if not self.POWpkix: cert = POW.pkix.Certificate() @@ -317,7 +371,9 @@ class X509(DER_object): return self.POWpkix def get_tlslite(self): - """Get the tlslite value of this certificate.""" + """ + Get the tlslite value of this certificate. + """ assert not self.empty() if not self.tlslite: cert = tlslite.api.X509() @@ -355,7 +411,9 @@ class X509(DER_object): def issue(self, keypair, subject_key, serial, sia, aia, crldp, notAfter, cn = None, resources = None, is_ca = True): - """Issue a certificate.""" + """ + Issue a certificate. + """ now = rpki.sundial.now() aki = self.get_SKI() @@ -408,7 +466,8 @@ class X509(DER_object): @classmethod def normalize_chain(cls, chain): - """Normalize a chain of certificates into a tuple of X509 objects. + """ + Normalize a chain of certificates into a tuple of X509 objects. Given all the glue certificates needed for BPKI cross certification, it's easiest to allow sloppy arguments to the HTTPS and CMS validation methods and provide a single method that @@ -420,13 +479,17 @@ class X509(DER_object): return tuple(x for x in chain if x is not None) class PKCS10(DER_object): - """Class to hold a PKCS #10 request.""" + """ + Class to hold a PKCS #10 request. + """ formats = ("DER", "POWpkix") pem_converter = PEM_converter("CERTIFICATE REQUEST") def get_DER(self): - """Get the DER value of this certification request.""" + """ + Get the DER value of this certification request. + """ assert not self.empty() if self.DER: return self.DER @@ -436,7 +499,9 @@ class PKCS10(DER_object): raise rpki.exceptions.DERObjectConversionError, "No conversion path to DER available" def get_POWpkix(self): - """Get the POW.pkix value of this certification request.""" + """ + Get the POW.pkix value of this certification request. + """ assert not self.empty() if not self.POWpkix: req = POW.pkix.CertificationRequest() @@ -449,7 +514,8 @@ class PKCS10(DER_object): return RSApublic(DER = self.get_POWpkix().certificationRequestInfo.subjectPublicKeyInfo.toString()) def check_valid_rpki(self): - """Check this certification request to see whether it's a valid + """ + Check this certification request to see whether it's a valid request for an RPKI certificate. This is broken out of the up-down protocol code because it's somewhat involved and the up-down code doesn't need to know the details. @@ -495,7 +561,9 @@ class PKCS10(DER_object): @classmethod def create_ca(cls, keypair, sia = None): - """Create a new request for a given keypair, including given SIA value.""" + """ + Create a new request for a given keypair, including given SIA value. + """ exts = [["basicConstraints", True, (1, None)], ["keyUsage", True, (0, 0, 0, 0, 0, 1, 1)]] if sia is not None: @@ -506,7 +574,9 @@ class PKCS10(DER_object): @classmethod def create(cls, keypair, exts = None): - """Create a new request for a given keypair, including given extensions.""" + """ + Create a new request for a given keypair, including given extensions. + """ cn = "".join(("%02X" % ord(i) for i in keypair.get_SKI())) req = POW.pkix.CertificationRequest() req.certificationRequestInfo.version.set(0) @@ -518,13 +588,17 @@ class PKCS10(DER_object): return cls(POWpkix = req) class RSA(DER_object): - """Class to hold an RSA key pair.""" + """ + Class to hold an RSA key pair. + """ formats = ("DER", "POW", "tlslite") pem_converter = PEM_converter("RSA PRIVATE KEY") def get_DER(self): - """Get the DER value of this keypair.""" + """ + Get the DER value of this keypair. + """ assert not self.empty() if self.DER: return self.DER @@ -534,14 +608,18 @@ class RSA(DER_object): raise rpki.exceptions.DERObjectConversionError, "No conversion path to DER available" def get_POW(self): - """Get the POW value of this keypair.""" + """ + Get the POW value of this keypair. + """ assert not self.empty() if not self.POW: self.POW = POW.derRead(POW.RSA_PRIVATE_KEY, self.get_DER()) return self.POW def get_tlslite(self): - """Get the tlslite value of this keypair.""" + """ + Get the tlslite value of this keypair. + """ assert not self.empty() if not self.tlslite: self.tlslite = tlslite.api.parsePEMKey(self.get_PEM(), private=True) @@ -565,13 +643,17 @@ class RSA(DER_object): return RSApublic(DER = self.get_public_DER()) class RSApublic(DER_object): - """Class to hold an RSA public key.""" + """ + Class to hold an RSA public key. + """ formats = ("DER", "POW") pem_converter = PEM_converter("RSA PUBLIC KEY") def get_DER(self): - """Get the DER value of this public key.""" + """ + Get the DER value of this public key. + """ assert not self.empty() if self.DER: return self.DER @@ -581,7 +663,9 @@ class RSApublic(DER_object): raise rpki.exceptions.DERObjectConversionError, "No conversion path to DER available" def get_POW(self): - """Get the POW value of this public key.""" + """ + Get the POW value of this public key. + """ assert not self.empty() if not self.POW: self.POW = POW.derRead(POW.RSA_PUBLIC_KEY, self.get_DER()) @@ -592,8 +676,9 @@ class RSApublic(DER_object): return calculate_SKI(self.get_DER()) def POWify_OID(oid): - """Utility function to convert tuple form of an OID to - the dotted-decimal string form that POW uses. + """ + Utility function to convert tuple form of an OID to the + dotted-decimal string form that POW uses. """ if isinstance(oid, str): return POWify_OID(rpki.oids.name2oid[oid]) @@ -601,7 +686,8 @@ def POWify_OID(oid): return ".".join(str(i) for i in oid) class CMS_object(DER_object): - """Class to hold a CMS-wrapped object. + """ + Class to hold a CMS-wrapped object. CMS-wrapped objects are a little different from the other DER_object types because the signed object is CMS wrapping inner content that's @@ -641,7 +727,9 @@ class CMS_object(DER_object): print_on_der_error = True def get_DER(self): - """Get the DER value of this CMS_object.""" + """ + Get the DER value of this CMS_object. + """ assert not self.empty() if self.DER: return self.DER @@ -651,24 +739,32 @@ class CMS_object(DER_object): raise rpki.exceptions.DERObjectConversionError, "No conversion path to DER available" def get_POW(self): - """Get the POW value of this CMS_object.""" + """ + Get the POW value of this CMS_object. + """ assert not self.empty() if not self.POW: self.POW = POW.derRead(POW.CMS_MESSAGE, self.get_DER()) return self.POW def get_content(self): - """Get the inner content of this CMS_object.""" + """ + Get the inner content of this CMS_object. + """ assert self.content is not None return self.content def set_content(self, content): - """Set the (inner) content of this CMS_object, clearing the wrapper.""" + """ + Set the (inner) content of this CMS_object, clearing the wrapper. + """ self.clear() self.content = content def verify(self, ta): - """Verify CMS wrapper and store inner content.""" + """ + Verify CMS wrapper and store inner content. + """ try: cms = self.get_POW() @@ -741,8 +837,9 @@ class CMS_object(DER_object): return self.get_content() def extract(self): - """Extract and store inner content from CMS wrapper without - verifying the CMS. + """ + Extract and store inner content from CMS wrapper without verifying + the CMS. DANGER WILL ROBINSON!!! @@ -768,7 +865,9 @@ class CMS_object(DER_object): return self.get_content() def sign(self, keypair, certs, crls = None, no_certs = False): - """Sign and wrap inner content.""" + """ + Sign and wrap inner content. + """ rpki.log.trace() @@ -797,20 +896,26 @@ class CMS_object(DER_object): self.POW = cms class DER_CMS_object(CMS_object): - """Class to hold CMS objects with DER-based content.""" + """ + Class to hold CMS objects with DER-based content. + """ def encode(self): """Encode inner content for signing.""" return self.get_content().toString() def decode(self, der): - """Decode DER and set inner content.""" + """ + Decode DER and set inner content. + """ obj = self.content_class() obj.fromString(der) self.content = obj class SignedManifest(DER_CMS_object): - """Class to hold a signed manifest.""" + """ + Class to hold a signed manifest. + """ pem_converter = PEM_converter("RPKI MANIFEST") content_class = rpki.manifest.Manifest @@ -826,7 +931,9 @@ class SignedManifest(DER_CMS_object): @classmethod def build(cls, serial, thisUpdate, nextUpdate, names_and_objs, keypair, certs, version = 0): - """Build a signed manifest.""" + """ + Build a signed manifest. + """ self = cls() filelist = [] for name, obj in names_and_objs: @@ -846,7 +953,9 @@ class SignedManifest(DER_CMS_object): return self class ROA(DER_CMS_object): - """Class to hold a signed ROA.""" + """ + Class to hold a signed ROA. + """ pem_converter = PEM_converter("ROUTE ORIGIN ATTESTATION") content_class = rpki.roa.RouteOriginAttestation @@ -854,7 +963,9 @@ class ROA(DER_CMS_object): @classmethod def build(cls, as_number, ipv4, ipv6, keypair, certs, version = 0): - """Build a ROA.""" + """ + Build a ROA. + """ self = cls() r = rpki.roa.RouteOriginAttestation() r.version.set(version) @@ -865,7 +976,9 @@ class ROA(DER_CMS_object): return self class XML_CMS_object(CMS_object): - """Class to hold CMS-wrapped XML protocol data.""" + """ + Class to hold CMS-wrapped XML protocol data. + """ econtent_oid = POWify_OID("id-ct-xml") @@ -896,7 +1009,9 @@ class XML_CMS_object(CMS_object): return lxml.etree.tostring(self.get_content(), pretty_print = True, encoding = self.encoding, xml_declaration = True) def schema_check(self): - """Handle XML RelaxNG schema check.""" + """ + Handle XML RelaxNG schema check. + """ try: self.schema.assertValid(self.get_content()) except lxml.etree.DocumentInvalid: @@ -904,14 +1019,18 @@ class XML_CMS_object(CMS_object): raise def dump_to_disk(self, prefix): - """Write DER of current message to disk, for debugging.""" + """ + Write DER of current message to disk, for debugging. + """ f = open(prefix + rpki.sundial.now().isoformat() + "Z.cms", "wb") f.write(self.get_DER()) f.close() @classmethod def wrap(cls, msg, keypair, certs, crls = None, pretty_print = False): - """Build a CMS-wrapped XML PDU and return its DER encoding.""" + """ + Build a CMS-wrapped XML PDU and return its DER encoding. + """ rpki.log.trace() self = cls() self.set_content(msg.toXML()) @@ -926,7 +1045,9 @@ class XML_CMS_object(CMS_object): @classmethod def unwrap(cls, der, ta, pretty_print = False): - """Unwrap a CMS-wrapped XML PDU and return Python objects.""" + """ + Unwrap a CMS-wrapped XML PDU and return Python objects. + """ self = cls(DER = der) if self.dump_inbound_cms: self.dump_to_disk(self.dump_inbound_cms) @@ -939,13 +1060,17 @@ class XML_CMS_object(CMS_object): return msg class CRL(DER_object): - """Class to hold a Certificate Revocation List.""" + """ + Class to hold a Certificate Revocation List. + """ formats = ("DER", "POW", "POWpkix") pem_converter = PEM_converter("X509 CRL") def get_DER(self): - """Get the DER value of this CRL.""" + """ + Get the DER value of this CRL. + """ assert not self.empty() if self.DER: return self.DER @@ -958,14 +1083,18 @@ class CRL(DER_object): raise rpki.exceptions.DERObjectConversionError, "No conversion path to DER available" def get_POW(self): - """Get the POW value of this CRL.""" + """ + Get the POW value of this CRL. + """ assert not self.empty() if not self.POW: self.POW = POW.derRead(POW.X509_CRL, self.get_DER()) return self.POW def get_POWpkix(self): - """Get the POW.pkix value of this CRL.""" + """ + Get the POW.pkix value of this CRL. + """ assert not self.empty() if not self.POWpkix: crl = POW.pkix.CertificateList() @@ -987,6 +1116,9 @@ class CRL(DER_object): @classmethod def generate(cls, keypair, issuer, serial, thisUpdate, nextUpdate, revokedCertificates, version = 1, digestType = "sha256WithRSAEncryption"): + """ + Generate a new CRL. + """ crl = POW.pkix.CertificateList() crl.setVersion(version) crl.setIssuer(issuer.get_POWpkix().getSubject()) diff --git a/rpkid/rpki/xml_utils.py b/rpkid/rpki/xml_utils.py index 1487cdd3..f4bff859 100644 --- a/rpkid/rpki/xml_utils.py +++ b/rpkid/rpki/xml_utils.py @@ -1,4 +1,5 @@ -"""XML utilities. +""" +XML utilities. $Id$ @@ -21,7 +22,8 @@ import xml.sax, lxml.sax, lxml.etree, base64 import rpki.exceptions class sax_handler(xml.sax.handler.ContentHandler): - """SAX handler for RPKI protocols. + """ + SAX handler for RPKI protocols. This class provides some basic amenities for parsing protocol XML of the kind we use in the RPKI protocols, including whacking all the @@ -35,7 +37,9 @@ class sax_handler(xml.sax.handler.ContentHandler): """ def __init__(self): - """Initialize SAX handler.""" + """ + Initialize SAX handler. + """ xml.sax.handler.ContentHandler.__init__(self) self.text = "" self.stack = [] @@ -53,7 +57,8 @@ class sax_handler(xml.sax.handler.ContentHandler): self.text += content def startElement(self, name, attrs): - """Handle startElement() events. + """ + Handle startElement() events. We maintain a stack of nested elements under construction so that we can feed events directly to the current element rather than @@ -63,6 +68,7 @@ class sax_handler(xml.sax.handler.ContentHandler): we call a virtual method to create the corresponding object and that's the object we'll be returning as our final result. """ + a = dict() for k, v in attrs.items(): if isinstance(k, tuple): @@ -79,9 +85,9 @@ class sax_handler(xml.sax.handler.ContentHandler): self.stack[-1].startElement(self.stack, name, a) def endElement(self, name): - """Handle endElement() events. - - Mostly this means handling any accumulated element text. + """ + Handle endElement() events. Mostly this means handling any + accumulated element text. """ text = self.text.encode("ascii").strip() self.text = "" @@ -89,19 +95,23 @@ class sax_handler(xml.sax.handler.ContentHandler): @classmethod def saxify(cls, elt): - """Create a one-off SAX parser, parse an ETree, return the result. + """ + Create a one-off SAX parser, parse an ETree, return the result. """ self = cls() lxml.sax.saxify(elt, self) return self.result def create_top_level(self, name, attrs): - """Handle top-level PDU for this protocol.""" + """ + Handle top-level PDU for this protocol. + """ assert name == self.name and attrs["version"] == self.version return self.pdu() class base_elt(object): - """Virtual base class for XML message elements. The left-right and + """ + 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. """ @@ -119,22 +129,30 @@ class base_elt(object): booleans = () def startElement(self, stack, name, attrs): - """Default startElement() handler: just process attributes.""" + """ + Default startElement() handler: just process attributes. + """ if name not in self.elements: assert name == self.element_name, "Unexpected name %s, stack %s" % (name, stack) self.read_attrs(attrs) def endElement(self, stack, name, text): - """Default endElement() handler: just pop the stack.""" + """ + Default endElement() handler: just pop the stack. + """ assert name == self.element_name, "Unexpected name %s, stack %s" % (name, stack) stack.pop() def toXML(self): - """Default toXML() element generator.""" + """ + Default toXML() element generator. + """ return self.make_elt() def read_attrs(self, attrs): - """Template-driven attribute reader.""" + """ + Template-driven attribute reader. + """ for key in self.attributes: val = attrs.get(key, None) if isinstance(val, str) and val.isdigit(): @@ -144,7 +162,9 @@ class base_elt(object): setattr(self, key, attrs.get(key, False)) def make_elt(self): - """XML element constructor.""" + """ + XML element constructor. + """ elt = lxml.etree.Element("{%s}%s" % (self.xmlns, self.element_name), nsmap = self.nsmap) for key in self.attributes: val = getattr(self, key, None) @@ -156,19 +176,25 @@ class base_elt(object): return elt def make_b64elt(self, elt, name, value = None): - """Constructor for Base64-encoded subelement.""" + """ + Constructor for Base64-encoded subelement. + """ if value is None: value = getattr(self, name, None) if value is not None: lxml.etree.SubElement(elt, "{%s}%s" % (self.xmlns, name), nsmap = self.nsmap).text = base64.b64encode(value) def __str__(self): - """Convert a base_elt object to string format.""" + """ + Convert a base_elt object to string format. + """ lxml.etree.tostring(self.toXML(), pretty_print = True, encoding = "us-ascii") @classmethod def make_pdu(cls, **kargs): - """Generic PDU constructor.""" + """ + Generic PDU constructor. + """ self = cls() for k, v in kargs.items(): if isinstance(v, bool): @@ -177,14 +203,16 @@ class base_elt(object): return self class data_elt(base_elt): - """Virtual base class for PDUs that map to SQL objects. These - objects all implement the create/set/get/list/destroy action - attribute. + """ + Virtual base class for PDUs that map to SQL objects. These objects + all implement the create/set/get/list/destroy action attribute. """ def endElement(self, stack, name, text): - """Default endElement handler for SQL-based objects. This assumes - that sub-elements are Base64-encoded using the sql_template mechanism. + """ + Default endElement handler for SQL-based objects. This assumes + that sub-elements are Base64-encoded using the sql_template + mechanism. """ if name in self.elements: elt_type = self.sql_template.map.get(name) @@ -195,7 +223,8 @@ class data_elt(base_elt): stack.pop() def toXML(self): - """Default element generator for SQL-based objects. This assumes + """ + Default element generator for SQL-based objects. This assumes that sub-elements are Base64-encoded DER objects. """ elt = self.make_elt() @@ -206,7 +235,9 @@ class data_elt(base_elt): return elt def make_reply(self, r_pdu = None): - """Construct a reply PDU.""" + """ + Construct a reply PDU. + """ if r_pdu is None: r_pdu = self.__class__() self.make_reply_clone_hook(r_pdu) @@ -231,7 +262,10 @@ class data_elt(base_elt): cb() def serve_create(self, r_msg, cb, eb): - """Handle a create action.""" + """ + Handle a create action. + """ + r_pdu = self.make_reply() def one(): @@ -246,7 +280,10 @@ class data_elt(base_elt): self.serve_pre_save_hook(self, r_pdu, one, eb) def serve_set(self, r_msg, cb, eb): - """Handle a set action.""" + """ + Handle a set action. + """ + db_pdu = self.serve_fetch_one() r_pdu = self.make_reply() for a in db_pdu.sql_template.columns[1:]: @@ -266,28 +303,36 @@ class data_elt(base_elt): db_pdu.serve_pre_save_hook(self, r_pdu, one, eb) def serve_get(self, r_msg, cb, eb): - """Handle a get action.""" + """ + Handle a get action. + """ r_pdu = self.serve_fetch_one() self.make_reply(r_pdu) r_msg.append(r_pdu) cb() def serve_list(self, r_msg, cb, eb): - """Handle a list action for non-self objects.""" + """ + Handle a list action for non-self objects. + """ for r_pdu in self.serve_fetch_all(): self.make_reply(r_pdu) r_msg.append(r_pdu) cb() def serve_destroy(self, r_msg, cb, eb): - """Handle a destroy action.""" + """ + Handle a destroy action. + """ db_pdu = self.serve_fetch_one() db_pdu.sql_delete() r_msg.append(self.make_reply()) cb() def serve_dispatch(self, r_msg, cb, eb): - """Action dispatch handler.""" + """ + Action dispatch handler. + """ dispatch = { "create" : self.serve_create, "set" : self.serve_set, "get" : self.serve_get, @@ -298,16 +343,22 @@ class data_elt(base_elt): dispatch[self.action](r_msg, cb, eb) def unimplemented_control(self, *controls): - """Uniform handling for unimplemented control operations.""" + """ + Uniform handling for unimplemented control operations. + """ 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.""" + """ + Generic top-level PDU. + """ def startElement(self, stack, name, attrs): - """Handle top-level PDU.""" + """ + Handle top-level PDU. + """ if name == "msg": assert self.version == int(attrs["version"]) self.type = attrs["type"] @@ -318,7 +369,9 @@ class msg(list): elt.startElement(stack, name, attrs) def endElement(self, stack, name, text): - """Handle top-level PDU.""" + """ + Handle top-level PDU. + """ assert name == "msg", "Unexpected name %s, stack %s" % (name, stack) assert len(stack) == 1 stack.pop() @@ -328,7 +381,9 @@ class msg(list): lxml.etree.tostring(self.toXML(), pretty_print = True, encoding = "us-ascii") def toXML(self): - """Generate top-level PDU.""" + """ + Generate top-level PDU. + """ elt = lxml.etree.Element("{%s}msg" % (self.xmlns), nsmap = self.nsmap, version = str(self.version), type = self.type) elt.extend([i.toXML() for i in self]) return elt diff --git a/rpkid/testbed.py b/rpkid/testbed.py index 3be69ec3..786017ea 100644 --- a/rpkid/testbed.py +++ b/rpkid/testbed.py @@ -93,7 +93,9 @@ except: # Define port allocator early, so we can use it while reading config def allocate_port(): - """Allocate a TCP port number.""" + """ + Allocate a TCP port number. + """ global base_port p = base_port base_port += 1 @@ -141,8 +143,9 @@ pub_sql_file = cfg.get("pub_sql_file", "pubd.sql") startup_delay = int(cfg.get("startup_delay", "10")) class main(object): - """Main program, implemented as a class to handle asynchronous I/O - in underlying libraries. + """ + Main program, implemented as a class to handle asynchronous I/O in + underlying libraries. """ def __init__(self): @@ -306,7 +309,9 @@ def wakeup(signum, frame): rpki.log.info("Wakeup call received, continuing") def cmd_sleep(interval = None): - """Set an alarm, then wait for it to go off.""" + """ + Set an alarm, then wait for it to go off. + """ if interval is None: rpki.log.info("Pausing indefinitely, send a SIGALRM to wake me up") else: @@ -316,7 +321,9 @@ def cmd_sleep(interval = None): signal.pause() def cmd_shell(*cmd): - """Run a shell command.""" + """ + Run a shell command. + """ cmd = " ".join(cmd) status = subprocess.call(cmd, shell = True) rpki.log.info("Shell command returned status %d" % status) @@ -333,7 +340,9 @@ cmds = { "sleep" : cmd_sleep, "echo" : cmd_echo } class route_origin(object): - """Representation for a route_origin object.""" + """ + Representation for a route_origin object. + """ def __init__(self, asn, ipv4, ipv6): self.asn = asn @@ -359,12 +368,16 @@ class route_origin(object): return cls(yaml.get("asn"), yaml.get("ipv4"), yaml.get("ipv6")) class allocation_db(list): - """Representation of all the entities and allocations in the test system. - Almost everything is generated out of this database. + """ + Representation of all the entities and allocations in the test + system. Almost everything is generated out of this database. """ def __init__(self, yaml): - """Initialize database from the (first) YAML document.""" + """ + Initialize database from the (first) YAML document. + """ + self.root = allocation(yaml, self) assert self.root.is_root() if self.root.crl_interval is None: @@ -388,7 +401,9 @@ class allocation_db(list): a.set_engine_number(i) def apply_delta(self, delta, cb): - """Apply a delta or run a command.""" + """ + Apply a delta or run a command. + """ def each(iterator, d): if isinstance(d, str): @@ -408,7 +423,9 @@ class allocation_db(list): rpki.async.iterator(delta, each, done) def dump(self): - """Print content of the database.""" + """ + Print content of the database. + """ for a in self: print a @@ -450,7 +467,9 @@ class allocation(object): self.extra_conf = yaml.get("extra_conf", []) def closure(self): - """Compute the transitive resource closure.""" + """ + Compute the transitive resource closure. + """ resources = self.base for kid in self.kids: resources = resources.union(kid.closure()) @@ -458,7 +477,9 @@ class allocation(object): return resources def apply_delta(self, yaml, cb): - """Apply deltas to this entity.""" + """ + Apply deltas to this entity. + """ rpki.log.info("Applying delta: %s" % yaml) @@ -557,14 +578,18 @@ class allocation(object): def is_twig(self): return not self.is_leaf() and not self.is_root() def set_engine_number(self, n): - """Set the engine number for this entity.""" + """ + Set the engine number for this entity. + """ self.irdb_db_name = "irdb%d" % n self.irdb_port = allocate_port() self.rpki_db_name = "rpki%d" % n self.rpki_port = allocate_port() def setup_bpki_certs(self): - """Create BPKI certificates for this entity.""" + """ + Create BPKI certificates for this entity. + """ rpki.log.info("Constructing BPKI keys and certs for %s" % self.name) if self.is_leaf(): setup_bpki_cert_chain(self.name, ee = ("RPKI",)) @@ -576,7 +601,9 @@ class allocation(object): self.rpkid_cert = rpki.x509.X509(PEM_file = self.name + "-RPKI.cer") def setup_conf_file(self): - """Write config files for this entity.""" + """ + Write config files for this entity. + """ rpki.log.info("Writing config files for %s" % self.name) d = { "my_name" : self.name, "testbed_name" : testbed_name, @@ -593,7 +620,9 @@ class allocation(object): f.close() def setup_sql(self, rpki_sql, irdb_sql): - """Set up this entity's IRDB.""" + """ + Set up this entity's IRDB. + """ rpki.log.info("Setting up MySQL for %s" % self.name) db = MySQLdb.connect(user = "rpki", db = self.rpki_db_name, passwd = rpki_db_pass) cur = db.cursor() @@ -605,13 +634,15 @@ class allocation(object): for sql in irdb_sql: cur.execute(sql) for kid in self.kids: - cur.execute("INSERT registrant (IRBE_mapped_id, subject_name, valid_until) VALUES (%s, %s, %s)", (kid.name, kid.name, kid.resources.valid_until.to_sql())) + cur.execute("INSERT registrant (IRBE_mapped_id, subject_name, valid_until) VALUES (%s, %s, %s)", + (kid.name, kid.name, kid.resources.valid_until.to_sql())) db.close() def sync_sql(self): - """Whack this entity's IRDB to match our master database. We do - this once during setup, then do it again every time we apply a - delta to this entity. + """ + Whack this entity's IRDB to match our master database. We do this + once during setup, then do it again every time we apply a delta to + this entity. """ rpki.log.info("Updating MySQL data for IRDB %s" % self.name) db = MySQLdb.connect(user = "irdb", db = self.irdb_db_name, passwd = irdb_db_pass) @@ -631,13 +662,17 @@ class allocation(object): db.close() def run_daemons(self): - """Run daemons for this entity.""" + """ + Run daemons for this entity. + """ rpki.log.info("Running daemons for %s" % self.name) self.rpkid_process = subprocess.Popen((prog_python, prog_rpkid, "-c", self.name + ".conf") + (("-p", self.name + ".prof") if profile else ())) self.irdbd_process = subprocess.Popen((prog_python, prog_irdbd, "-c", self.name + ".conf")) def kill_daemons(self): - """Kill daemons for this entity.""" + """ + Kill daemons for this entity. + """ rpki.log.info("Killing daemons for %s" % self.name) for proc in (self.rpkid_process, self.irdbd_process): try: @@ -648,8 +683,9 @@ class allocation(object): proc.wait() def call_rpkid(self, pdu, cb): - """Send a left-right message to this entity's RPKI daemon and - return the response. + """ + Send a left-right message to this entity's RPKI daemon and return + the response. """ rpki.log.info("Calling rpkid for %s" % self.name) msg = rpki.left_right.msg([pdu]) @@ -685,7 +721,9 @@ class allocation(object): self.call_rpkid_caller_cb(msg[0] if len(msg) == 1 else msg) def cross_certify(self, certificant, reverse = False): - """Cross-certify and return the resulting certificate.""" + """ + Cross-certify and return the resulting certificate. + """ if reverse: certifier = certificant @@ -713,7 +751,8 @@ class allocation(object): return rpki.x509.X509(Auto_file = certfile) def create_rpki_objects(self, cb): - """Create RPKI engine objects for this engine. + """ + Create RPKI engine objects for this engine. Parent and child objects are tricky: @@ -851,7 +890,8 @@ class allocation(object): start() def setup_yaml_leaf(self): - """Generate certificates and write YAML scripts for leaf nodes. + """ + Generate certificates and write YAML scripts for leaf nodes. We're cheating a bit here: properly speaking, we can't generate issue or revoke requests without knowing the class, which is generated on the fly, but at the moment the test case is @@ -880,7 +920,9 @@ class allocation(object): f.close() def run_cron(self, cb): - """Trigger cron run for this engine.""" + """ + Trigger cron run for this engine. + """ rpki.log.info("Running cron for %s" % self.name) rpki.https.client(client_key = self.irbe_key, @@ -892,10 +934,11 @@ class allocation(object): errback = cb) def run_yaml(self): - """Run YAML scripts for this leaf entity. Since we're not - bothering to check the class list returned by the list command, - the issue command may fail, so we treat failure of the list - command as an error, but only issue a warning when issue fails. + """ + Run YAML scripts for this leaf entity. Since we're not bothering + to check the class list returned by the list command, the issue + command may fail, so we treat failure of the list command as an + error, but only issue a warning when issue fails. """ rpki.log.info("Running YAML for %s" % self.name) @@ -904,7 +947,9 @@ class allocation(object): rpki.log.warn("YAML issue command failed for %s, continuing" % self.name) def setup_bpki_cert_chain(name, ee = (), ca = ()): - """Build a set of BPKI certificates.""" + """ + Build a set of BPKI certificates. + """ s = "exec >/dev/null 2>&1\n" for kind in ("TA",) + ee + ca: d = { "name" : name, @@ -928,7 +973,9 @@ def setup_bpki_cert_chain(name, ee = (), ca = ()): subprocess.check_call(s, shell = True) def setup_rootd(rpkid_name, rpkid_tag, rootd_yaml): - """Write the config files for rootd.""" + """ + Write the config files for rootd. + """ rpki.log.info("Writing config files for %s" % rootd_name) d = { "rootd_name" : rootd_name, "rootd_port" : rootd_port, @@ -948,7 +995,9 @@ def setup_rootd(rpkid_name, rpkid_tag, rootd_yaml): subprocess.check_call(s, shell = True) def setup_rcynic(): - """Write the config file for rcynic.""" + """ + Write the config file for rcynic. + """ rpki.log.info("Config file for rcynic") d = { "rcynic_name" : rcynic_name, "rootd_name" : rootd_name, @@ -958,7 +1007,9 @@ def setup_rcynic(): f.close() def setup_rsyncd(): - """Write the config file for rsyncd.""" + """ + Write the config file for rsyncd. + """ rpki.log.info("Config file for rsyncd") d = { "rsyncd_name" : rsyncd_name, "rsyncd_port" : rsyncd_port, @@ -969,7 +1020,9 @@ def setup_rsyncd(): f.close() def setup_publication(pubd_sql): - """Set up publication daemon.""" + """ + Set up publication daemon. + """ rpki.log.info("Configure publication daemon") pubd_dir = os.getcwd() + "/publication/" assert rootd_sia.startswith("rsync://") @@ -998,7 +1051,8 @@ def setup_publication(pubd_sql): pubd_pubd_cert = rpki.x509.X509(Auto_file = pubd_name + "-PUBD.cer") def call_pubd(pdu, cb): - """Send a publication message to publication daemon and return the + """ + Send a publication message to publication daemon and return the response. """ rpki.log.info("Calling pubd") @@ -1033,8 +1087,9 @@ def call_pubd_cb(val): call_pubd_caller_cb(msg[0] if len(msg) == 1 else msg) def set_pubd_crl(cb): - """Whack publication daemon's bpki_crl. This must be configured - before publication daemon starts talking to its clients, and must be + """ + Whack publication daemon's bpki_crl. This must be configured before + publication daemon starts talking to its clients, and must be updated whenever we update the CRL. """ rpki.log.info("Setting pubd's BPKI CRL") @@ -1042,7 +1097,9 @@ def set_pubd_crl(cb): cb = cb) def run_rcynic(): - """Run rcynic to see whether what was published makes sense.""" + """ + Run rcynic to see whether what was published makes sense. + """ rpki.log.info("Running rcynic") env = os.environ.copy() env["TZ"] = "" @@ -1050,10 +1107,13 @@ def run_rcynic(): subprocess.call(rcynic_stats, shell = True, env = env) def mangle_sql(filename): - """Mangle an SQL file into a sequence of SQL statements.""" - # - # There is no pretty way to do this. Just shut your eyes, it'll be over soon. - # + """ + Mangle an SQL file into a sequence of SQL statements. + """ + + # There is no pretty way to do this. Just shut your eyes, it'll be + # over soon. + f = open(filename) statements = " ".join(" ".join(word for word in line.expandtabs().split(" ") if word) for line in [line.strip(" \t\n") for line in f.readlines()] |