aboutsummaryrefslogtreecommitdiff
path: root/scripts
diff options
context:
space:
mode:
Diffstat (limited to 'scripts')
-rw-r--r--scripts/rpki/ipaddrs.py8
-rw-r--r--scripts/rpki/relaxng.py4
-rw-r--r--scripts/rpki/resource_set.py38
-rw-r--r--scripts/rpki/up_down.py40
4 files changed, 90 insertions, 0 deletions
diff --git a/scripts/rpki/ipaddrs.py b/scripts/rpki/ipaddrs.py
index e041e801..73e404ca 100644
--- a/scripts/rpki/ipaddrs.py
+++ b/scripts/rpki/ipaddrs.py
@@ -3,6 +3,10 @@
import socket, struct
class v4addr(long):
+ """
+ IPv4 address. Derived from long, but supports IPv4 print syntax.
+ """
+
bits = 32
def __new__(cls, x):
@@ -15,6 +19,10 @@ class v4addr(long):
return socket.inet_ntop(socket.AF_INET, struct.pack("!I", long(self)))
class v6addr(long):
+ """
+ IPv6 address. Derived from long, but supports IPv6 print syntax.
+ """
+
bits = 128
def __new__(cls, x):
diff --git a/scripts/rpki/relaxng.py b/scripts/rpki/relaxng.py
index 21f6d2b7..a4a6c00d 100644
--- a/scripts/rpki/relaxng.py
+++ b/scripts/rpki/relaxng.py
@@ -3,6 +3,10 @@
import os
def relaxng(xml, rng):
+ """
+ Validate a chunk of xml against a RelaxNG schema.
+ """
+
i, o = os.popen4(("xmllint", "--noout", "--relaxng", rng, "-"))
i.write(xml)
i.close()
diff --git a/scripts/rpki/resource_set.py b/scripts/rpki/resource_set.py
index bc1e7c16..a0e71566 100644
--- a/scripts/rpki/resource_set.py
+++ b/scripts/rpki/resource_set.py
@@ -3,6 +3,10 @@
import re, ipaddrs
class resource_range(object):
+ """
+ Generic resource range type. Assumes underlying type is some kind of integer.
+ You probably don't want to use this type directly.
+ """
def __init__(self, min, max):
assert min <= max, "Mis-ordered range: %s before %s" % (str(min), str(max))
@@ -16,6 +20,10 @@ class resource_range(object):
return c
class resource_range_as(resource_range):
+ """
+ Range of Autonomous System Numbers.
+ Denote a single ASN by a range whose min and max values are identical.
+ """
def __str__(self):
if self.min == self.max:
@@ -24,6 +32,11 @@ class resource_range_as(resource_range):
return str(self.min) + "-" + str(self.max)
class resource_range_ip(resource_range):
+ """
+ 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.
+ """
def __str__(self):
mask = self.min ^ self.max
@@ -37,12 +50,22 @@ class resource_range_ip(resource_range):
return str(self.min) + "/" + str(prefixlen)
class resource_range_ipv4(resource_range_ip):
+ """
+ Range of IPv4 addresses.
+ """
pass
class resource_range_ipv6(resource_range_ip):
+ """
+ Range of IPv6 addresses.
+ """
pass
class resource_set(list):
+ """
+ Generic resource set. List type containing resource ranges.
+ You probably don't want to use this type directly.
+ """
def __init__(self, s):
if s:
@@ -56,6 +79,9 @@ class resource_set(list):
return ",".join(map(str, self))
class resource_set_as(resource_set):
+ """
+ ASN resource set.
+ """
def parse(self, x):
r = re.match("^([0-9]+)-([0-9]+)$", x)
@@ -65,6 +91,10 @@ class resource_set_as(resource_set):
return resource_range_as(long(x), long(x))
class resource_set_ip(resource_set):
+ """
+ (Generic) IP address resource set.
+ You probably don't want to use this type directly.
+ """
def parse(self, x):
r = re.match("^([0-9:.a-fA-F]+)-([0-9:.a-fA-F]+)$", x)
@@ -81,9 +111,17 @@ class resource_set_ip(resource_set):
raise RuntimeError, 'Bad IP resource "%s"' % (x)
class resource_set_ipv4(resource_set_ip):
+ """
+ IPv4 address resource set.
+ """
+
addr_type = ipaddrs.v4addr
range_type = resource_range_ipv4
class resource_set_ipv6(resource_set_ip):
+ """
+ IPv6 address resource set.
+ """
+
addr_type = ipaddrs.v6addr
range_type = resource_range_ipv6
diff --git a/scripts/rpki/up_down.py b/scripts/rpki/up_down.py
index f595eb24..7435a1d5 100644
--- a/scripts/rpki/up_down.py
+++ b/scripts/rpki/up_down.py
@@ -3,6 +3,12 @@
import base64, xml.sax, resource_set
def snarf(obj, attrs, key, func=None):
+ """
+ Utility function to consolidate the steps needed to extract a field
+ from the SAX XML parse and insert it as an object attribute of the
+ same name.
+ """
+
try:
val = attrs.getValue(key).encode("ascii")
if func:
@@ -12,6 +18,9 @@ def snarf(obj, attrs, key, func=None):
setattr(obj, key, val)
class msg(object):
+ """
+ Base type for Up-Down protocol PDU.
+ """
def __str__(self):
return ('\
@@ -34,6 +43,9 @@ class msg(object):
pass
class cert(object):
+ """
+ Up-Down protocol representation of an issued certificate.
+ """
def __init__(self, attrs):
snarf(self, attrs, "cert_url")
@@ -53,6 +65,9 @@ class cert(object):
return xml
class klass(object):
+ """
+ Up-Down protocol representation of a resource class.
+ """
def __init__(self, attrs):
snarf(self, attrs, "class_name")
@@ -81,9 +96,15 @@ class klass(object):
return xml
class list(msg):
+ """
+ Up-Down protocol "list" PDU.
+ """
pass
class list_response(msg):
+ """
+ Up-Down protocol "list_response" PDU.
+ """
def __init__(self):
self.klasses = []
@@ -104,6 +125,9 @@ class list_response(msg):
return "".join(map(str, self.klasses))
class issue(msg):
+ """
+ Up-Down protocol "issue" PDU.
+ """
def startElement(self, name, attrs):
assert name == "request"
@@ -127,12 +151,18 @@ class issue(msg):
return xml + ">" + base64.b64encode(self.pkcs10) + "</request>\n"
class issue_response(list_response):
+ """
+ Up-Down protocol "issue_response" PDU.
+ """
def toXML(self):
assert len(self.klasses) == 1
return list_response.toXML(self)
class revoke(msg):
+ """
+ Up-Down protocol "revoke" PDU.
+ """
def startElement(self, name, attrs):
snarf(self, attrs, "class_name")
@@ -142,9 +172,15 @@ class revoke(msg):
return (' <key class_name="%s" ski="%s" />\n' % (self.class_name, self.ski))
class revoke_response(revoke):
+ """
+ Up-Down protocol "revoke_response" PDU.
+ """
pass
class error_response(msg):
+ """
+ Up-Down protocol "error_response" PDU.
+ """
def toXML(self):
return ' <status>%d</status>\n' % self.status
@@ -158,6 +194,10 @@ class error_response(msg):
self.description = text
class sax_handler(xml.sax.handler.ContentHandler):
+ """
+ SAX handler for Up-Down protocol. Builds message PDU then
+ dispatches to that class's handler for nested data.
+ """
def __init__(self):
self.text = ""
35130f92aaefd9339a3b06d3dbbd'>729456cb
b46deb14








429adae7











0c17de89



0c17de89
b46deb14





ac415cdd

b46deb14










9f6d6462
b46deb14













0c17de89

429adae7
b46deb14

429adae7
7f5e7518
b46deb14

429adae7
32d43381
7f5e7518

b46deb14

7f5e7518
429adae7
b46deb14

429adae7

b46deb14

429adae7

b46deb14

429adae7

b46deb14

429adae7

b46deb14

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206

      


                                                                       




                                                                        







                                                                      



                                


                                                                   

   

              
                            



                   

                                    

                                                  



                                                                      
 
                                       
 






                                                                                                                 
 


                                                                    
                               
                                                       
                                              
 

                                              


                                         


                                                     
 
                                                                         
 


                                                                    





                                                               
 




                                                    


                              

                                                             
 

                                                                               
 
                                                                   
 





                                                      
 






                                                                       








                                                                                  











                                                                    



                                                                     
                                   





                                                                      

                                    










                                                                      
                                                                    













                                                                       

 
                                 

                                     
 
                                   

                               
 
                             

                               

                                     
 
                         

                                               

                            

                                                

                              

                                          

                         

                               

                                 

                                       
# $Id$
#
# Copyright (C) 2015-2016  Parsons Government Services ("PARSONS")
# Portions copyright (C) 2013-2014  Dragon Research Labs ("DRL")
# Portions copyright (C) 2011-2012  Internet Systems Consortium ("ISC")
#
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notices and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND PARSONS, DRL, AND ISC DISCLAIM
# ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL
# PARSONS, DRL, OR ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
# CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
# OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

"""
Common Django ORM field classes.

Many of these are complex ASN.1 DER objects stored as binaray data,
since the only sane text representation would just be the Base64
encoding of the DER and thus would add no value.
"""

import logging

from django.db import models

import rpki.x509
import rpki.sundial

logger = logging.getLogger(__name__)


class EnumField(models.PositiveSmallIntegerField):
    """
    An enumeration type that uses strings in Python and small integers
    in SQL.
    """

    description = "An enumeration type"

    def __init__(self, *args, **kwargs):
        if isinstance(kwargs.get("choices"), (tuple, list)) and isinstance(kwargs["choices"][0], (str, unicode)):
            kwargs["choices"] = tuple(enumerate(kwargs["choices"], 1))
        # Might need something here to handle string-valued default parameter
        models.PositiveSmallIntegerField.__init__(self, *args, **kwargs)
        self.enum_i2s = dict(self.flatchoices)
        self.enum_s2i = dict((v, k) for k, v in self.flatchoices)

    def from_db_value(self, value, expression, connection, context):
        return self.enum_i2s.get(value, value)

    def to_python(self, value):
        value = super(EnumField, self).to_python(value)
        return self.enum_i2s.get(value, value)

    def get_prep_value(self, value):
        return self.enum_s2i.get(value, value)


class SundialField(models.DateTimeField):
    """
    A field type for our customized datetime objects.
    """

    description = "A datetime type using our customized datetime objects"

    def from_db_value(self, value, expression, connection, context):
        return self.to_python(value)

    def to_python(self, value):
        if isinstance(value, rpki.sundial.pydatetime.datetime):
            return rpki.sundial.datetime.from_datetime(
                models.DateTimeField.to_python(self, value))
        else:
            return value

    def get_prep_value(self, value):
        if isinstance(value, rpki.sundial.datetime):
            return value.to_datetime()
        else:
            return value


class BlobField(models.Field):
    """
    Old BLOB field type, predating Django's BinaryField type.

    Do not use, this is only here for backwards compatabilty during migrations.
    """

    description   = "Raw BLOB type without ASN.1 encoding/decoding"

    def __init__(self, *args, **kwargs):
        self.blob_type = kwargs.pop("blob_type", None)
        kwargs["serialize"] = False
        kwargs["blank"] = True
        kwargs["default"] = None
        models.Field.__init__(self, *args, **kwargs)

    def deconstruct(self):
        name, path, args, kwargs = super(BlobField, self).deconstruct()
        del kwargs["serialize"]
        del kwargs["blank"]
        del kwargs["default"]
        return name, path, args, kwargs

    def db_type(self, connection):
        if self.blob_type is not None:
            return self.blob_type
        elif connection.settings_dict['ENGINE'] == "django.db.backends.mysql":
            return "LONGBLOB"
        elif connection.settings_dict['ENGINE'] == "django.db.backends.posgresql":
            return "bytea"
        else:
            return "BLOB"


# For reasons which now escape me, I had a few fields in the old
# hand-coded SQL which used MySQL type BINARY(20) to hold SKIs.
# Presumably this was so that I could then use those SKIs in indexes
# and searches, but apparently I never got around to that part.
#
# SKIs probably would be better stored as hex strings anyway, so not
# bothering with a separate binary type model for this.  Deal with
# this if and when it ever becomes an issue.


# DERField used to be a subclass of BlobField.  Try changing it to be
# a subclass of BinaryField instead, leave BlobField (for now) for
# backwards compatability during migrations,

class DERField(models.BinaryField):
    """
    Field class for DER objects, with automatic translation between
    ASN.1 and Python types.  This is an abstract class, concrete field
    classes are derived from it.
    """

    rpki_type = rpki.x509.DER_object

    def __init__(self, *args, **kwargs):
        kwargs["blank"] = True
        kwargs["default"] = None
        super(DERField, self).__init__(*args, **kwargs)

    def deconstruct(self):
        name, path, args, kwargs = super(DERField, self).deconstruct()
        del kwargs["blank"]
        del kwargs["default"]
        return name, path, args, kwargs

    def from_db_value(self, value, expression, connection, context):
        if value is not None:
            value = self.rpki_type(DER = str(value))
        return value

    def to_python(self, value):
        value = super(DERField, self).to_python(value)
        if value is not None and not isinstance(value, self.rpki_type):
            value = self.rpki_type(DER = str(value))
        return value

    def get_prep_value(self, value):
        if value is not None:
            value = value.get_DER()
        return super(DERField, self).get_prep_value(value)


class CertificateField(DERField):
    description = "X.509 certificate"
    rpki_type   = rpki.x509.X509

class RSAPrivateKeyField(DERField):
    description = "RSA keypair"
    rpki_type   = rpki.x509.RSA

KeyField = RSAPrivateKeyField

class PublicKeyField(DERField):
    description = "RSA keypair"
    rpki_type   = rpki.x509.PublicKey

class CRLField(DERField):
    description = "Certificate Revocation List"
    rpki_type   = rpki.x509.CRL

class PKCS10Field(DERField):
    description = "PKCS #10 certificate request"
    rpki_type   = rpki.x509.PKCS10

class ManifestField(DERField):
    description = "RPKI Manifest"
    rpki_type   = rpki.x509.SignedManifest

class ROAField(DERField):
    description = "ROA"
    rpki_type   = rpki.x509.ROA

class GhostbusterField(DERField):
    description = "Ghostbuster Record"
    rpki_type   = rpki.x509.Ghostbuster