aboutsummaryrefslogtreecommitdiff
path: root/rp/rcynic/rcynicng
diff options
context:
space:
mode:
Diffstat (limited to 'rp/rcynic/rcynicng')
-rwxr-xr-xrp/rcynic/rcynicng146
1 files changed, 127 insertions, 19 deletions
diff --git a/rp/rcynic/rcynicng b/rp/rcynic/rcynicng
index 096ed467..167a3b56 100755
--- a/rp/rcynic/rcynicng
+++ b/rp/rcynic/rcynicng
@@ -13,6 +13,7 @@ import shutil
import errno
import logging
import argparse
+import datetime
import subprocess
import tornado.gen
@@ -188,6 +189,20 @@ class X509(rpki.POW.X509):
return "<X509 at 0x{:x}>".format(id(self))
@classmethod
+ def store_if_new(cls, der, uri, retrieval):
+ self = cls.derRead(der)
+ aki = self.getAKI()
+ ski = self.getSKI()
+ return rpki.rcynicdb.models.RPKIObject.objects.get_or_create(
+ der = der,
+ defaults = dict(
+ uri = uri,
+ aki = "" if aki is None else aki.encode("hex"),
+ ski = "" if ski is None else ski.encode("hex"),
+ hash = sha256(der).encode("hex"),
+ retrieved = retrieval))
+
+ @classmethod
def derReadURI(cls, uri, generation, cms = None):
fn = uri_to_filename(uri, generation.tree)
if not os.path.exists(fn):
@@ -296,6 +311,19 @@ class CRL(rpki.POW.CRL):
return "<CRL at 0x{:x}>".format(id(self))
@classmethod
+ def store_if_new(cls, der, uri, retrieval):
+ self = cls.derRead(der)
+ aki = self.getAKI()
+ return rpki.rcynicdb.models.RPKIObject.objects.get_or_create(
+ der = der,
+ defaults = dict(
+ uri = uri,
+ aki = "" if aki is None else aki.encode("hex"),
+ ski = "",
+ hash = sha256(der).encode("hex"),
+ retrieved = retrieval))
+
+ @classmethod
def derReadURI(cls, uri, generation):
fn = uri_to_filename(uri, generation.tree)
if not os.path.exists(fn):
@@ -346,7 +374,25 @@ class CRL(rpki.POW.CRL):
return not any(s.kind == "bad" for s in status)
-class Ghostbuster(rpki.POW.CMS):
+class CMS_Mixin(object):
+
+ @classmethod
+ def store_if_new(cls, der, uri, retrieval):
+ self = cls.derRead(der)
+ cert = self.certs()[0]
+ aki = cert.getAKI()
+ ski = cert.getSKI()
+ return rpki.rcynicdb.models.RPKIObject.objects.get_or_create(
+ der = der,
+ defaults = dict(
+ uri = uri,
+ aki = "" if aki is None else aki.encode("hex"),
+ ski = "" if ski is None else ski.encode("hex"),
+ hash = sha256(der).encode("hex"),
+ retrieved = retrieval))
+
+
+class Ghostbuster(rpki.POW.CMS, CMS_Mixin):
def __repr__(self):
try:
@@ -384,7 +430,7 @@ class Ghostbuster(rpki.POW.CMS):
return not any(s.kind == "bad" for s in status)
-class Manifest(rpki.POW.Manifest):
+class Manifest(rpki.POW.Manifest, CMS_Mixin):
def __repr__(self):
try:
@@ -406,6 +452,7 @@ class Manifest(rpki.POW.Manifest):
self.ee = X509.derReadURI(uri, generation, self)
self.fah = None
self.generation = generation
+ self.sha256 = sha256(der)
self.thisUpdate = None
self.nextUpdate = None
self.number = None
@@ -443,7 +490,7 @@ class Manifest(rpki.POW.Manifest):
yield diruri + fn, digest
-class ROA(rpki.POW.ROA):
+class ROA(rpki.POW.ROA, CMS_Mixin):
def __repr__(self):
try:
@@ -483,6 +530,19 @@ class ROA(rpki.POW.ROA):
return not any(s.kind == "bad" for s in status)
+class_dispatch = dict(cer = X509,
+ crl = CRL,
+ gbr = Ghostbuster,
+ mft = Manifest,
+ roa = ROA)
+
+def uri_to_class(uri):
+ cls = class_dispatch.get(uri[-3:]) if len(uri) > 4 and uri[-4] == "." else None
+ if cls is None:
+ Status.add(uri, None, codes.UNKNOWN_OBJECT_TYPE_SKIPPED)
+ return cls
+
+
class WalkFrame(object):
"""
Certificate tree walk stack frame. This is basically just a
@@ -492,10 +552,6 @@ class WalkFrame(object):
after an rsync or RRDP fetch completes).
"""
- fns2 = dict(cer = X509,
- gbr = Ghostbuster,
- roa = ROA)
-
def __init__(self, cer):
self.cer = cer
self.state = self.initial
@@ -626,15 +682,19 @@ class WalkFrame(object):
yield tornado.gen.moment
uri = self.diruri + fn
- cls = self.fns2.get(uri[-3:])
# Need general URI validator here?
if uri == self.crl.uri:
continue
- if uri[-4] != "." or cls is None:
- Status.add(uri, None, codes.UNKNOWN_OBJECT_TYPE_SKIPPED)
+ cls = uri_to_class(uri)
+
+ if cls is None:
+ continue
+
+ if cls in (Manifest, CRL):
+ Status.add(uri, None, codes.INAPPROPRIATE_OBJECT_TYPE_SKIPPED)
continue
for generation in (Generation.current, Generation.backup):
@@ -706,12 +766,12 @@ class WalkTask(object):
def read_tals():
- for root, dirs, files in os.walk(args.tals):
+ for head, dirs, files in os.walk(args.tals):
for fn in files:
if fn.endswith(".tal"):
- furi = "file://" + os.path.abspath(os.path.join(root, fn))
+ furi = "file://" + os.path.abspath(os.path.join(head, fn))
try:
- with open(os.path.join(root, fn), "r") as f:
+ with open(os.path.join(head, fn), "r") as f:
lines = f.readlines()
uri = lines.pop(0).strip()
b64 = "".join(lines[lines.index("\n"):])
@@ -822,14 +882,15 @@ class Fetcher(object):
self._rsync_history[path] = self
try:
+ path = uri_to_filename(self.uri, args.unauthenticated)
cmd = ["rsync", "--update", "--times", "--copy-links", "--itemize-changes"]
if self.uri.endswith("/"):
cmd.append("--recursive")
cmd.append("--delete")
cmd.append(self.uri)
- cmd.append(uri_to_filename(self.uri, args.unauthenticated))
+ cmd.append(path)
- dn = os.path.dirname(cmd[-1])
+ dn = os.path.dirname(path)
if not os.path.exists(dn):
os.makedirs(dn)
@@ -862,11 +923,44 @@ class Fetcher(object):
# Should do something with rsync result and validation status database here.
+ # We probably don't want to yield in the middle of a
+ # transaction, and this doesn't really need to be wrapped
+ # in a transaction in any case, so leave well enough alone.
+ #
+ #from django.db import IntegrityError, transaction
+ #with transaction.atomic():
+
+ retrieval = rpki.rcynicdb.models.Retrieval.objects.create(
+ uri = self.uri,
+ started = datetime.datetime.fromtimestamp(t0),
+ finished = datetime.datetime.fromtimestamp(t1),
+ successful = self.status == 0)
+
+ for fn in self._rsync_walk(path):
+ yield tornado.gen.moment
+ uri = "rsync://" + fn[len(args.unauthenticated):].lstrip("/")
+ cls = uri_to_class(uri)
+ if cls is not None:
+ try:
+ with open(fn, "rb") as f:
+ cls.store_if_new(f.read(), uri, retrieval)
+ except:
+ Status.add(uri, Generation.current, codes.UNREADABLE_OBJECT)
+ logger.exception("Couldn't read %s from rsync tree", uri)
+
finally:
pending = self.pending
self.pending = None
pending.notify_all()
+ def _rsync_walk(self, path):
+ if self.uri.endswith("/"):
+ for head, dirs, files in os.walk(path):
+ for fn in files:
+ yield os.path.join(head, fn)
+ elif os.path.exists(path):
+ yield path
+
class CheckTALTask(object):
@@ -964,7 +1058,8 @@ class posint(int):
def main():
- os.putenv("TZ", "UTC")
+ os.environ.update(TZ = "UTC",
+ DJANGO_SETTINGS_MODULE = "rpki.django_settings.rcynic")
time.tzset()
parser = argparse.ArgumentParser(description = __doc__)
@@ -975,13 +1070,26 @@ def main():
parser.add_argument("--tals", default = "sample-trust-anchors")
- parser.add_argument("--workers", type = posint, default = 10)
- parser.add_argument("--no-fetch", action = "store_true")
- parser.add_argument("--no-spawn-on-fetch", action = "store_true")
+ parser.add_argument("--workers", type = posint, default = 10)
+
+ parser.add_argument("--no-fetch", action = "store_true")
+ parser.add_argument("--no-spawn-on-fetch", action = "store_true")
+ parser.add_argument("--no-migrate", action = "store_true")
global args
args = parser.parse_args()
+ import django
+ django.setup()
+
+ if not args.no_migrate:
+ # Not sure we should be doing this on every run, but sure simplifies things.
+ import django.core.management
+ django.core.management.call_command("migrate", verbosity = 0, interactive = False)
+
+ global rpki
+ import rpki.rcynicdb
+
global new_authenticated, old_authenticated
new_authenticated = args.authenticated.rstrip("/") + time.strftime(".%Y-%m-%dT%H:%M:%SZ")
old_authenticated = args.authenticated.rstrip("/")