RPKI Engine 1.0
|
00001 """ 00002 Prototype of an iterator class to parse the output of an rcynic run. 00003 This script will almost certainly move to the library package once 00004 it's stable. 00005 00006 $Id: rcynic.py 3884 2011-06-17 18:50:12Z sra $ 00007 00008 Copyright (C) 2010-2011 Internet Systems Consortium ("ISC") 00009 00010 Permission to use, copy, modify, and distribute this software for any 00011 purpose with or without fee is hereby granted, provided that the above 00012 copyright notice and this permission notice appear in all copies. 00013 00014 THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 00015 REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 00016 AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 00017 INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 00018 LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 00019 OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 00020 PERFORMANCE OF THIS SOFTWARE. 00021 """ 00022 00023 import sys, os, rpki.x509, rpki.exceptions 00024 from xml.etree.ElementTree import ElementTree 00025 00026 class UnknownObject(rpki.exceptions.RPKI_Exception): 00027 """ 00028 Unrecognized object in rcynic result cache. 00029 """ 00030 00031 class NotRsyncURI(rpki.exceptions.RPKI_Exception): 00032 """ 00033 URI is not an rsync URI. 00034 """ 00035 00036 class rcynic_object(object): 00037 """ 00038 An object read from rcynic cache. 00039 """ 00040 00041 def __init__(self, filename, **kwargs): 00042 self.filename = filename 00043 for k, v in kwargs.iteritems(): 00044 setattr(self, k, v) 00045 self.obj = self.obj_class(DER_file = filename) 00046 00047 def __repr__(self): 00048 return "<%s %s %s at 0x%x>" % (self.__class__.__name__, self.uri, self.resources, id(self)) 00049 00050 def show_attrs(self, *attrs): 00051 """ 00052 Print a bunch of object attributes, quietly ignoring any that 00053 might be missing. 00054 """ 00055 for a in attrs: 00056 try: 00057 print "%s: %s" % (a.capitalize(), getattr(self, a)) 00058 except AttributeError: 00059 pass 00060 00061 def show(self): 00062 """ 00063 Print common object attributes. 00064 """ 00065 self.show_attrs("filename", "uri", "status", "timestamp") 00066 00067 class rcynic_certificate(rcynic_object): 00068 """ 00069 A certificate from rcynic cache. 00070 """ 00071 00072 obj_class = rpki.x509.X509 00073 00074 def __init__(self, filename, **kwargs): 00075 rcynic_object.__init__(self, filename, **kwargs) 00076 self.notBefore = self.obj.getNotBefore() 00077 self.notAfter = self.obj.getNotAfter() 00078 self.aia_uri = self.obj.get_aia_uri() 00079 self.sia_directory_uri = self.obj.get_sia_directory_uri() 00080 self.manifest_uri = self.obj.get_sia_manifest_uri() 00081 self.resources = self.obj.get_3779resources() 00082 self.is_ca = self.obj.is_CA() 00083 self.serial = self.obj.getSerial() 00084 self.issuer = self.obj.getIssuer() 00085 self.subject = self.obj.getSubject() 00086 self.ski = self.obj.hSKI() 00087 self.aki = self.obj.hAKI() 00088 00089 def show(self): 00090 """ 00091 Print certificate attributes. 00092 """ 00093 rcynic_object.show(self) 00094 self.show_attrs("notBefore", "notAfter", "aia_uri", "sia_directory_uri", "resources") 00095 00096 class rcynic_roa(rcynic_object): 00097 """ 00098 A ROA from rcynic cache. 00099 """ 00100 00101 obj_class = rpki.x509.ROA 00102 00103 _afi_map = dict((cls.resource_set_type.afi, cls) 00104 for cls in (rpki.resource_set.roa_prefix_set_ipv4, 00105 rpki.resource_set.roa_prefix_set_ipv6)) 00106 00107 def __init__(self, filename, **kwargs): 00108 rcynic_object.__init__(self, filename, **kwargs) 00109 self.obj.extract() 00110 self.asID = self.obj.get_content().asID.get() 00111 self.prefix_sets = [] 00112 for fam in self.obj.get_content().ipAddrBlocks: 00113 prefix_set = self._afi_map[fam.addressFamily.get()]() 00114 addr_type = prefix_set.resource_set_type.range_type.datum_type 00115 self.prefix_sets.append(prefix_set) 00116 for addr in fam.addresses: 00117 prefix = addr.address.get() 00118 prefixlen = len(prefix) 00119 prefix = addr_type(rpki.resource_set._bs2long(prefix, addr_type.bits, 0)) 00120 maxprefixlen = addr.maxLength.get() 00121 prefix_set.append(prefix_set.prefix_type(prefix, prefixlen, maxprefixlen)) 00122 self.ee = rpki.x509.X509(POW = self.obj.get_POW().certs()[0]) 00123 self.notBefore = self.ee.getNotBefore() 00124 self.notAfter = self.ee.getNotAfter() 00125 self.aia_uri = self.ee.get_aia_uri() 00126 self.resources = self.ee.get_3779resources() 00127 self.issuer = self.ee.getIssuer() 00128 self.serial = self.ee.getSerial() 00129 self.subject = self.ee.getSubject() 00130 self.aki = self.ee.hAKI() 00131 self.ski = self.ee.hSKI() 00132 00133 def show(self): 00134 """ 00135 Print ROA attributes. 00136 """ 00137 rcynic_object.show(self) 00138 self.show_attrs("notBefore", "notAfter", "aia_uri", "resources", "asID") 00139 if self.prefix_sets: 00140 print "Prefixes:", ",".join(str(i) for i in self.prefix_sets) 00141 00142 class rcynic_ghostbuster(rcynic_object): 00143 """ 00144 Ghostbuster record from the rcynic cache. 00145 """ 00146 00147 obj_class = rpki.x509.Ghostbuster 00148 00149 def __init__(self, *args, **kwargs): 00150 rcynic_object.__init__(self, *args, **kwargs) 00151 self.obj.extract() 00152 self.vcard = self.obj.get_content() 00153 self.ee = rpki.x509.X509(POW = self.obj.get_POW().certs()[0]) 00154 self.notBefore = self.ee.getNotBefore() 00155 self.notAfter = self.ee.getNotAfter() 00156 self.aia_uri = self.ee.get_aia_uri() 00157 self.issuer = self.ee.getIssuer() 00158 self.serial = self.ee.getSerial() 00159 self.subject = self.ee.getSubject() 00160 self.aki = self.ee.hAKI() 00161 self.ski = self.ee.hSKI() 00162 00163 def show(self): 00164 rcynic_object.show(self) 00165 self.show_attrs("notBefore", "notAfter", "vcard") 00166 00167 file_name_classes = { 00168 ".cer" : rcynic_certificate, 00169 ".gbr" : rcynic_ghostbuster, 00170 ".roa" : rcynic_roa } 00171 00172 class rcynic_file_iterator(object): 00173 """ 00174 Iterate over files in an rcynic output tree, yielding a Python 00175 representation of each object found. 00176 """ 00177 00178 def __init__(self, rcynic_root, 00179 authenticated_subdir = "authenticated"): 00180 self.rcynic_dir = os.path.join(rcynic_root, authenticated_subdir) 00181 00182 def __iter__(self): 00183 for root, dirs, files in os.walk(self.rcynic_dir): 00184 for filename in files: 00185 filename = os.path.join(root, filename) 00186 ext = os.path.splitext(filename)[1] 00187 if ext in file_name_classes: 00188 yield file_name_classes[ext](filename) 00189 00190 class rcynic_xml_iterator(object): 00191 """ 00192 Iterate over validation_status entries in the XML output from an 00193 rcynic run. Yields a tuple for each entry: 00194 00195 URI, OK, status, timestamp, object 00196 00197 where URI, status, and timestamp are the corresponding values from 00198 the XML element, OK is a boolean indicating whether validation was 00199 considered succesful, and object is a Python representation of the 00200 object in question. If OK is True, object will be from rcynic's 00201 authenticated output tree; otherwise, object will be from rcynic's 00202 unauthenticated output tree. 00203 00204 Note that it is possible for the same URI to appear in more than one 00205 validation_status element; in such cases, the succesful case (OK 00206 True) should be the last entry (as rcynic will stop trying once it 00207 gets a good copy), but there may be multiple failures, which might 00208 or might not have different status codes. 00209 """ 00210 00211 def __init__(self, rcynic_root, xml_file, 00212 authenticated_subdir = "authenticated", 00213 authenticated_old_subdir = "authenticated.old", 00214 unauthenticated_subdir = "unauthenticated"): 00215 self.rcynic_root = rcynic_root 00216 self.xml_file = xml_file 00217 self.authenticated_subdir = os.path.join(rcynic_root, authenticated_subdir) 00218 self.authenticated_old_subdir = os.path.join(rcynic_root, authenticated_old_subdir) 00219 self.unauthenticated_subdir = os.path.join(rcynic_root, unauthenticated_subdir) 00220 00221 base_uri = "rsync://" 00222 00223 def uri_to_filename(self, uri): 00224 if uri.startswith(self.base_uri): 00225 return uri[len(self.base_uri):] 00226 else: 00227 raise NotRsyncURI, "Not an rsync URI %r" % uri 00228 00229 def __iter__(self): 00230 00231 for validation_status in ElementTree(file = self.xml_file).getroot().getiterator("validation_status"): 00232 timestamp = validation_status.get("timestamp") 00233 status = validation_status.get("status") 00234 uri = validation_status.text.strip() 00235 ok = status == "validation_ok" 00236 filename = os.path.join(self.authenticated_subdir if ok else self.unauthenticated_subdir, self.uri_to_filename(uri)) 00237 ext = os.path.splitext(filename)[1] 00238 if ext in file_name_classes: 00239 yield file_name_classes[ext](filename = filename, uri = uri, ok = ok, status = status, timestamp = timestamp) 00240 00241 def label_iterator(xml_file): 00242 """ 00243 Returns an iterator which contains all defined labels from an rcynic XML 00244 output file. Each item is a tuple of the form 00245 (label, kind, description). 00246 """ 00247 00248 for label in ElementTree(file=xml_file).find("labels"): 00249 yield label.tag, label.get("kind"), label.text.strip() 00250 00251 00252 if __name__ == "__main__": 00253 rcynic_dir = os.path.normpath(os.path.join(sys.path[0], "..", "rcynic")) 00254 if False: 00255 try: 00256 for i in rcynic_file_iterator(os.path.join(rcynic_dir, "rcynic-data")): 00257 print i 00258 except IOError: 00259 pass 00260 if True: 00261 try: 00262 for i in rcynic_xml_iterator(os.path.join(rcynic_dir, "rcynic-data"), 00263 os.path.join(rcynic_dir, "rcynic.xml")): 00264 #print i 00265 i.show() 00266 print 00267 except IOError: 00268 pass