aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--rpkid/irbe-setup.py22
-rwxr-xr-xrpkid/irbe_cli.py24
-rwxr-xr-xrpkid/irdbd.py9
-rwxr-xr-xrpkid/pubd.py25
-rw-r--r--rpkid/resource-cert-samples.py36
-rw-r--r--rpkid/rpki/async.py54
-rw-r--r--rpkid/rpki/config.py16
-rw-r--r--rpkid/rpki/exceptions.py185
-rw-r--r--rpkid/rpki/https.py17
-rw-r--r--rpkid/rpki/ipaddrs.py13
-rw-r--r--rpkid/rpki/left_right.py208
-rw-r--r--rpkid/rpki/log.py19
-rw-r--r--rpkid/rpki/manifest.py3
-rw-r--r--rpkid/rpki/oids.py3
-rw-r--r--rpkid/rpki/publication.py116
-rw-r--r--rpkid/rpki/resource_set.py247
-rw-r--r--rpkid/rpki/roa.py3
-rw-r--r--rpkid/rpki/rpki_engine.py166
-rw-r--r--rpkid/rpki/sql.py48
-rw-r--r--rpkid/rpki/sundial.py46
-rw-r--r--rpkid/rpki/up_down.py193
-rw-r--r--rpkid/rpki/x509.py278
-rw-r--r--rpkid/rpki/xml_utils.py127
-rw-r--r--rpkid/testbed.py152
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()]