00001 """
00002 Command line IR back-end control program for rpkid and pubd.
00003
00004 $Id: irbe_cli.py 2799 2009-09-30 02:33:36Z sra $
00005
00006 Copyright (C) 2009 Internet Systems Consortium ("ISC")
00007
00008 Permission to use, copy, modify, and distribute this software for any
00009 purpose with or without fee is hereby granted, provided that the above
00010 copyright notice and this permission notice appear in all copies.
00011
00012 THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
00013 REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
00014 AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
00015 INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
00016 LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
00017 OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
00018 PERFORMANCE OF THIS SOFTWARE.
00019
00020 Portions copyright (C) 2007--2008 American Registry for Internet Numbers ("ARIN")
00021
00022 Permission to use, copy, modify, and distribute this software for any
00023 purpose with or without fee is hereby granted, provided that the above
00024 copyright notice and this permission notice appear in all copies.
00025
00026 THE SOFTWARE IS PROVIDED "AS IS" AND ARIN DISCLAIMS ALL WARRANTIES WITH
00027 REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
00028 AND FITNESS. IN NO EVENT SHALL ARIN BE LIABLE FOR ANY SPECIAL, DIRECT,
00029 INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
00030 LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
00031 OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
00032 PERFORMANCE OF THIS SOFTWARE.
00033 """
00034
00035 import getopt, sys, textwrap
00036 import rpki.left_right, rpki.https, rpki.x509, rpki.config, rpki.log
00037 import rpki.publication, rpki.async
00038
00039 pem_out = None
00040
00041 class caller(object):
00042 """
00043 Handle client-side mechanics for left-right and publication
00044 protocols.
00045 """
00046
00047 def __init__(self, cms_class, client_key, client_cert, server_ta, server_cert, url):
00048 self.cms_class = cms_class
00049 self.client_key = client_key
00050 self.client_cert = client_cert
00051 self.server_ta = server_ta
00052 self.server_cert = server_cert
00053 self.url = url
00054
00055 def __call__(self, cb, eb, msg):
00056
00057 def done(cms):
00058 msg, xml = self.cms_class.unwrap(cms, (self.server_ta, self.server_cert), pretty_print = True)
00059 if verbose:
00060 print "<!-- Reply -->"
00061 print xml
00062 cb(msg)
00063
00064 cms, xml = self.cms_class.wrap(msg, self.client_key, self.client_cert, pretty_print = True)
00065 if verbose:
00066 print "<!-- Query -->"
00067 print xml
00068
00069 rpki.https.client(
00070 client_key = self.client_key,
00071 client_cert = self.client_cert,
00072 server_ta = self.server_ta,
00073 url = self.url,
00074 msg = cms,
00075 callback = done,
00076 errback = eb)
00077
00078 class UsageWrapper(textwrap.TextWrapper):
00079 """
00080 Call interface around Python textwrap.Textwrapper class.
00081 """
00082
00083 def __call__(self, *args):
00084 """Format arguments, with TextWrapper indentation."""
00085 return self.fill(textwrap.dedent(" ".join(args)))
00086
00087 usage_fill = UsageWrapper(subsequent_indent = " " * 4)
00088
00089 class reply_elt_mixin(object):
00090 """
00091 Protocol mix-in for printout of reply PDUs.
00092 """
00093
00094 is_cmd = False
00095
00096 def client_reply_decode(self):
00097 pass
00098
00099 def client_reply_show(self):
00100 print self.element_name
00101 for i in self.attributes + self.elements:
00102 if getattr(self, i) is not None:
00103 print " %s: %s" % (i, getattr(self, i))
00104
00105 class cmd_elt_mixin(reply_elt_mixin):
00106 """
00107 Protocol mix-in for command line client element PDUs.
00108 """
00109
00110 is_cmd = True
00111
00112
00113
00114
00115 excludes = ()
00116
00117 @classmethod
00118 def usage(cls):
00119 """
00120 Generate usage message for this PDU.
00121 """
00122 args = " ".join("--" + x + "=" for x in cls.attributes + cls.elements if x not in cls.excludes)
00123 bools = " ".join("--" + x for x in cls.booleans)
00124 if args and bools:
00125 return args + " " + bools
00126 else:
00127 return args or bools
00128
00129 def client_getopt(self, argv):
00130 """
00131 Parse options for this class.
00132 """
00133 opts, argv = getopt.getopt(argv, "", [x + "=" for x in self.attributes + self.elements if x not in self.excludes] + list(self.booleans))
00134 for o, a in opts:
00135 o = o[2:]
00136 handler = getattr(self, "client_query_" + o, None)
00137 if handler is not None:
00138 handler(a)
00139 elif o in self.booleans:
00140 setattr(self, o, True)
00141 else:
00142 assert o in self.attributes
00143 setattr(self, o, a)
00144 return argv
00145
00146 def client_query_bpki_cert(self, arg):
00147 """Special handler for --bpki_cert option."""
00148 self.bpki_cert = rpki.x509.X509(Auto_file = arg)
00149
00150 def client_query_glue(self, arg):
00151 """Special handler for --bpki_glue option."""
00152 self.bpki_glue = rpki.x509.X509(Auto_file = arg)
00153
00154 def client_query_bpki_cms_cert(self, arg):
00155 """Special handler for --bpki_cms_cert option."""
00156 self.bpki_cms_cert = rpki.x509.X509(Auto_file = arg)
00157
00158 def client_query_cms_glue(self, arg):
00159 """Special handler for --bpki_cms_glue option."""
00160 self.bpki_cms_glue = rpki.x509.X509(Auto_file = arg)
00161
00162 def client_query_bpki_https_cert(self, arg):
00163 """Special handler for --bpki_https_cert option."""
00164 self.bpki_https_cert = rpki.x509.X509(Auto_file = arg)
00165
00166 def client_query_https_glue(self, arg):
00167 """Special handler for --bpki_https_glue option."""
00168 self.bpki_https_glue = rpki.x509.X509(Auto_file = arg)
00169
00170 class cmd_msg_mixin(object):
00171 """
00172 Protocol mix-in for command line client message PDUs.
00173 """
00174
00175 @classmethod
00176 def usage(cls):
00177 """
00178 Generate usage message for this PDU.
00179 """
00180 for k, v in cls.pdus.items():
00181 if v.is_cmd:
00182 print usage_fill(k, v.usage())
00183
00184
00185
00186 class self_elt(cmd_elt_mixin, rpki.left_right.self_elt):
00187 pass
00188
00189 class bsc_elt(cmd_elt_mixin, rpki.left_right.bsc_elt):
00190
00191 excludes = ("pkcs10_request",)
00192
00193 def client_query_signing_cert(self, arg):
00194 """--signing_cert option."""
00195 self.signing_cert = rpki.x509.X509(Auto_file = arg)
00196
00197 def client_query_signing_cert_crl(self, arg):
00198 """--signing_cert_crl option."""
00199 self.signing_cert_crl = rpki.x509.CRL(Auto_file = arg)
00200
00201 def client_reply_decode(self):
00202 global pem_out
00203 if pem_out is not None and self.pkcs10_request is not None:
00204 if isinstance(pem_out, str):
00205 pem_out = open(pem_out, "w")
00206 pem_out.write(self.pkcs10_request.get_PEM())
00207
00208 class parent_elt(cmd_elt_mixin, rpki.left_right.parent_elt):
00209 pass
00210
00211 class child_elt(cmd_elt_mixin, rpki.left_right.child_elt):
00212 pass
00213
00214 class repository_elt(cmd_elt_mixin, rpki.left_right.repository_elt):
00215 pass
00216
00217 class list_published_objects_elt(cmd_elt_mixin, rpki.left_right.list_published_objects_elt):
00218
00219 excludes = ("uri",)
00220
00221 class report_error_elt(reply_elt_mixin, rpki.left_right.report_error_elt):
00222 pass
00223
00224 class left_right_msg(cmd_msg_mixin, rpki.left_right.msg):
00225 pdus = dict((x.element_name, x)
00226 for x in (self_elt, bsc_elt, parent_elt, child_elt, repository_elt,
00227 list_published_objects_elt, report_error_elt))
00228
00229 class left_right_sax_handler(rpki.left_right.sax_handler):
00230 pdu = left_right_msg
00231
00232 class left_right_cms_msg(rpki.left_right.cms_msg):
00233 saxify = left_right_sax_handler.saxify
00234
00235
00236
00237 class config_elt(cmd_elt_mixin, rpki.publication.config_elt):
00238
00239 def client_query_bpki_crl(self, arg):
00240 """Special handler for --bpki_crl option."""
00241 self.bpki_crl = rpki.x509.CRL(Auto_file = arg)
00242
00243 class client_elt(cmd_elt_mixin, rpki.publication.client_elt):
00244 pass
00245
00246 class certificate_elt(cmd_elt_mixin, rpki.publication.certificate_elt):
00247 pass
00248
00249 class crl_elt(cmd_elt_mixin, rpki.publication.crl_elt):
00250 pass
00251
00252 class manifest_elt(cmd_elt_mixin, rpki.publication.manifest_elt):
00253 pass
00254
00255 class roa_elt(cmd_elt_mixin, rpki.publication.roa_elt):
00256 pass
00257
00258 class report_error_elt(reply_elt_mixin, rpki.publication.report_error_elt):
00259 pass
00260
00261 class publication_msg(cmd_msg_mixin, rpki.publication.msg):
00262 pdus = dict((x.element_name, x)
00263 for x in (config_elt, client_elt, certificate_elt, crl_elt, manifest_elt, roa_elt, report_error_elt))
00264
00265 class publication_sax_handler(rpki.publication.sax_handler):
00266 pdu = publication_msg
00267
00268 class publication_cms_msg(rpki.publication.cms_msg):
00269 saxify = publication_sax_handler.saxify
00270
00271
00272
00273 top_opts = ["config=", "help", "pem_out=", "quiet", "verbose"]
00274
00275 def usage(code = 1):
00276 print __doc__.strip()
00277 print
00278 print "Usage:"
00279 print
00280 print "# Top-level options:"
00281 print usage_fill(*["--" + x for x in top_opts])
00282 print
00283 print "# left-right protocol:"
00284 left_right_msg.usage()
00285 print
00286 print "# publication protocol:"
00287 publication_msg.usage()
00288 sys.exit(code)
00289
00290
00291
00292 rpki.log.init("irbe_cli")
00293
00294 argv = sys.argv[1:]
00295
00296 if not argv:
00297 usage(0)
00298
00299 cfg_file = "irbe.conf"
00300 verbose = True
00301
00302 opts, argv = getopt.getopt(argv, "c:hpqv?", top_opts)
00303 for o, a in opts:
00304 if o in ("-?", "-h", "--help"):
00305 usage(0)
00306 elif o in ("-c", "--config"):
00307 cfg_file = a
00308 elif o in ("-p", "--pem_out"):
00309 pem_out = a
00310 elif o in ("-q", "--quiet"):
00311 verbose = False
00312 elif o in ("-v", "--verbose"):
00313 verbose = True
00314
00315 if not argv:
00316 usage(1)
00317
00318 cfg = rpki.config.parser(cfg_file, "irbe_cli")
00319
00320 q_msg_left_right = left_right_msg.query()
00321 q_msg_publication = publication_msg.query()
00322
00323 while argv:
00324 if argv[0] in left_right_msg.pdus:
00325 q_pdu = left_right_msg.pdus[argv[0]]()
00326 q_msg = q_msg_left_right
00327 elif argv[0] in publication_msg.pdus:
00328 q_pdu = publication_msg.pdus[argv[0]]()
00329 q_msg = q_msg_publication
00330 else:
00331 usage(1)
00332 argv = q_pdu.client_getopt(argv[1:])
00333 q_msg.append(q_pdu)
00334
00335 if q_msg_left_right:
00336
00337 call_rpkid = rpki.async.sync_wrapper(caller(
00338 cms_class = left_right_cms_msg,
00339 client_key = rpki.x509.RSA( Auto_file = cfg.get("rpkid-irbe-key")),
00340 client_cert = rpki.x509.X509(Auto_file = cfg.get("rpkid-irbe-cert")),
00341 server_ta = rpki.x509.X509(Auto_file = cfg.get("rpkid-bpki-ta")),
00342 server_cert = rpki.x509.X509(Auto_file = cfg.get("rpkid-cert")),
00343 url = cfg.get("rpkid-url")))
00344
00345 call_rpkid(q_msg_left_right)
00346
00347 if q_msg_publication:
00348
00349 call_pubd = rpki.async.sync_wrapper(caller(
00350 cms_class = publication_cms_msg,
00351 client_key = rpki.x509.RSA( Auto_file = cfg.get("pubd-irbe-key")),
00352 client_cert = rpki.x509.X509(Auto_file = cfg.get("pubd-irbe-cert")),
00353 server_ta = rpki.x509.X509(Auto_file = cfg.get("pubd-bpki-ta")),
00354 server_cert = rpki.x509.X509(Auto_file = cfg.get("pubd-cert")),
00355 url = cfg.get("pubd-url")))
00356
00357 call_pubd(q_msg_publication)