00001 """
00002 Command line IR back-end control program for rpkid and pubd.
00003
00004 $Id: irbe_cli.py 2452 2009-05-27 02:54:24Z 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, rpki.publication
00037
00038 pem_out = None
00039
00040 class UsageWrapper(textwrap.TextWrapper):
00041 """
00042 Call interface around Python textwrap.Textwrapper class.
00043 """
00044
00045 def __call__(self, *args):
00046 """Format arguments, with TextWrapper indentation."""
00047 return self.fill(textwrap.dedent(" ".join(args)))
00048
00049 usage_fill = UsageWrapper(subsequent_indent = " " * 4)
00050
00051 class cmd_elt_mixin(object):
00052 """
00053 Protocol mix-in for command line client element PDUs.
00054 """
00055
00056
00057
00058
00059
00060
00061 excludes = ()
00062
00063 @classmethod
00064 def usage(cls):
00065 """
00066 Generate usage message for this PDU.
00067 """
00068 args = " ".join("--" + x + "=" for x in cls.attributes + cls.elements if x not in cls.excludes)
00069 bools = " ".join("--" + x for x in cls.booleans)
00070 if args and bools:
00071 return args + " " + bools
00072 else:
00073 return args or bools
00074
00075 def client_getopt(self, argv):
00076 """
00077 Parse options for this class.
00078 """
00079 opts, argv = getopt.getopt(argv, "", [x + "=" for x in self.attributes + self.elements if x not in self.excludes] + list(self.booleans))
00080 for o, a in opts:
00081 o = o[2:]
00082 handler = getattr(self, "client_query_" + o, None)
00083 if handler is not None:
00084 handler(a)
00085 elif o in self.booleans:
00086 setattr(self, o, True)
00087 else:
00088 assert o in self.attributes
00089 setattr(self, o, a)
00090 return argv
00091
00092 def client_query_bpki_cert(self, arg):
00093 """Special handler for --bpki_cert option."""
00094 self.bpki_cert = rpki.x509.X509(Auto_file = arg)
00095
00096 def client_query_glue(self, arg):
00097 """Special handler for --bpki_glue option."""
00098 self.bpki_glue = rpki.x509.X509(Auto_file = arg)
00099
00100 def client_query_bpki_cms_cert(self, arg):
00101 """Special handler for --bpki_cms_cert option."""
00102 self.bpki_cms_cert = rpki.x509.X509(Auto_file = arg)
00103
00104 def client_query_cms_glue(self, arg):
00105 """Special handler for --bpki_cms_glue option."""
00106 self.bpki_cms_glue = rpki.x509.X509(Auto_file = arg)
00107
00108 def client_query_bpki_https_cert(self, arg):
00109 """Special handler for --bpki_https_cert option."""
00110 self.bpki_https_cert = rpki.x509.X509(Auto_file = arg)
00111
00112 def client_query_https_glue(self, arg):
00113 """Special handler for --bpki_https_glue option."""
00114 self.bpki_https_glue = rpki.x509.X509(Auto_file = arg)
00115
00116 def client_reply_decode(self):
00117 pass
00118
00119 def client_reply_show(self):
00120 print self.element_name
00121 for i in self.attributes + self.elements:
00122 if getattr(self, i) is not None:
00123 print " %s: %s" % (i, getattr(self, i))
00124
00125 class cmd_msg_mixin(object):
00126 """
00127 Protocol mix-in for command line client message PDUs.
00128 """
00129
00130 @classmethod
00131 def usage(cls):
00132 """
00133 Generate usage message for this PDU.
00134 """
00135 for k, v in cls.pdus.items():
00136 print usage_fill(k, v.usage())
00137
00138
00139
00140 class self_elt(cmd_elt_mixin, rpki.left_right.self_elt):
00141 pass
00142
00143 class bsc_elt(cmd_elt_mixin, rpki.left_right.bsc_elt):
00144
00145 excludes = ("pkcs10_request",)
00146
00147 def client_query_signing_cert(self, arg):
00148 """--signing_cert option."""
00149 self.signing_cert = rpki.x509.X509(Auto_file = arg)
00150
00151 def client_query_signing_cert_crl(self, arg):
00152 """--signing_cert_crl option."""
00153 self.signing_cert_crl = rpki.x509.CRL(Auto_file = arg)
00154
00155 def client_reply_decode(self):
00156 global pem_out
00157 if pem_out is not None and self.pkcs10_request is not None:
00158 if isinstance(pem_out, str):
00159 pem_out = open(pem_out, "w")
00160 pem_out.write(self.pkcs10_request.get_PEM())
00161
00162 class parent_elt(cmd_elt_mixin, rpki.left_right.parent_elt):
00163 pass
00164
00165 class child_elt(cmd_elt_mixin, rpki.left_right.child_elt):
00166 pass
00167
00168 class repository_elt(cmd_elt_mixin, rpki.left_right.repository_elt):
00169 pass
00170
00171 class route_origin_elt(cmd_elt_mixin, rpki.left_right.route_origin_elt):
00172
00173 def client_query_as_number(self, arg):
00174 """Handle autonomous sequence numbers."""
00175 self.as_number = long(arg)
00176
00177 def client_query_ipv4(self, arg):
00178 """Handle IPv4 addresses."""
00179 self.ipv4 = rpki.resource_set.roa_prefix_set_ipv4(arg)
00180
00181 def client_query_ipv6(self, arg):
00182 """Handle IPv6 addresses."""
00183 self.ipv6 = rpki.resource_set.roa_prefix_set_ipv6(arg)
00184
00185 class left_right_msg(cmd_msg_mixin, rpki.left_right.msg):
00186 pdus = dict((x.element_name, x)
00187 for x in (self_elt, bsc_elt, parent_elt, child_elt, repository_elt, route_origin_elt))
00188
00189 class left_right_sax_handler(rpki.left_right.sax_handler):
00190 pdu = left_right_msg
00191
00192 class left_right_cms_msg(rpki.left_right.cms_msg):
00193 saxify = left_right_sax_handler.saxify
00194
00195
00196
00197 class config_elt(cmd_elt_mixin, rpki.publication.config_elt):
00198
00199 def client_query_bpki_crl(self, arg):
00200 """Special handler for --bpki_crl option."""
00201 self.bpki_crl = rpki.x509.CRL(Auto_file = arg)
00202
00203 class client_elt(cmd_elt_mixin, rpki.publication.client_elt):
00204 pass
00205
00206 class certificate_elt(cmd_elt_mixin, rpki.publication.certificate_elt):
00207 pass
00208
00209 class crl_elt(cmd_elt_mixin, rpki.publication.crl_elt):
00210 pass
00211
00212 class manifest_elt(cmd_elt_mixin, rpki.publication.manifest_elt):
00213 pass
00214
00215 class roa_elt(cmd_elt_mixin, rpki.publication.roa_elt):
00216 pass
00217
00218 class publication_msg(cmd_msg_mixin, rpki.publication.msg):
00219 pdus = dict((x.element_name, x)
00220 for x in (config_elt, client_elt, certificate_elt, crl_elt, manifest_elt, roa_elt))
00221
00222 class publication_sax_handler(rpki.publication.sax_handler):
00223 pdu = publication_msg
00224
00225 class publication_cms_msg(rpki.publication.cms_msg):
00226 saxify = publication_sax_handler.saxify
00227
00228
00229
00230 top_opts = ["config=", "help", "pem_out=", "verbose"]
00231
00232 def usage(code = 1):
00233 print __doc__.strip()
00234 print
00235 print "Usage:"
00236 print
00237 print "# Top-level options:"
00238 print usage_fill(*["--" + x for x in top_opts])
00239 print
00240 print "# left-right protocol:"
00241 left_right_msg.usage()
00242 print
00243 print "# publication protocol:"
00244 publication_msg.usage()
00245 sys.exit(code)
00246
00247
00248
00249 def call_daemon(cms_class, client_key, client_cert, server_ta, url, q_msg):
00250 q_cms, q_xml = cms_class.wrap(q_msg, client_key, client_cert, pretty_print = True)
00251 if verbose:
00252 print q_xml
00253
00254 def done(der):
00255 r_msg, r_xml = cms_class.unwrap(der, server_ta, pretty_print = True)
00256 print r_xml
00257 for r_pdu in r_msg:
00258 r_pdu.client_reply_decode()
00259
00260 def fail(e):
00261 print "Failed: %s" % e
00262
00263 rpki.https.client(client_key = client_key,
00264 client_cert = client_cert,
00265 server_ta = server_ta,
00266 url = url,
00267 msg = q_cms,
00268 callback = done,
00269 errback = fail)
00270
00271
00272
00273 rpki.log.init("irbe_cli")
00274
00275 argv = sys.argv[1:]
00276
00277 if not argv:
00278 usage(0)
00279
00280 cfg_file = "irbe.conf"
00281 verbose = False
00282
00283 opts, argv = getopt.getopt(argv, "c:hpv?", top_opts)
00284 for o, a in opts:
00285 if o in ("-?", "-h", "--help"):
00286 usage(0)
00287 elif o in ("-c", "--config"):
00288 cfg_file = a
00289 elif o in ("-p", "--pem_out"):
00290 pem_out = a
00291 elif o in ("-v", "--verbose"):
00292 verbose = True
00293
00294 if not argv:
00295 usage(1)
00296
00297 cfg = rpki.config.parser(cfg_file, "irbe_cli")
00298
00299 q_msg_left_right = left_right_msg()
00300 q_msg_left_right.type = "query"
00301
00302 q_msg_publication = publication_msg()
00303 q_msg_publication.type = "query"
00304
00305 while argv:
00306 if argv[0] in left_right_msg.pdus:
00307 q_pdu = left_right_msg.pdus[argv[0]]()
00308 q_msg = q_msg_left_right
00309 elif argv[0] in publication_msg.pdus:
00310 q_pdu = publication_msg.pdus[argv[0]]()
00311 q_msg = q_msg_publication
00312 else:
00313 usage(1)
00314 argv = q_pdu.client_getopt(argv[1:])
00315 q_msg.append(q_pdu)
00316
00317 if q_msg_left_right:
00318 call_daemon(
00319 cms_class = left_right_cms_msg,
00320 client_key = rpki.x509.RSA( Auto_file = cfg.get("rpkid-irbe-key")),
00321 client_cert = rpki.x509.X509(Auto_file = cfg.get("rpkid-irbe-cert")),
00322 server_ta = (rpki.x509.X509(Auto_file = cfg.get("rpkid-bpki-ta")),
00323 rpki.x509.X509(Auto_file = cfg.get("rpkid-cert"))),
00324 url = cfg.get("rpkid-url"),
00325 q_msg = q_msg_left_right)
00326
00327 if q_msg_publication:
00328 call_daemon(
00329 cms_class = publication_cms_msg,
00330 client_key = rpki.x509.RSA( Auto_file = cfg.get("pubd-irbe-key")),
00331 client_cert = rpki.x509.X509(Auto_file = cfg.get("pubd-irbe-cert")),
00332 server_ta = (rpki.x509.X509(Auto_file = cfg.get("pubd-bpki-ta")),
00333 rpki.x509.X509(Auto_file = cfg.get("pubd-cert"))),
00334 url = cfg.get("pubd-url"),
00335 q_msg = q_msg_publication)