Esempio n. 1
0
            print("Error: Could not decode JSON report '%s': %s" % (json_msg.get_payload(),
                                                                    str(e)))
        sys.exit(-8)
    
    if options.view:
        print("Returned metadata for %s:" % args[0])
        print json.dumps(json_report, indent = 4)

    if options.metadata:
        print json.dumps(json_report, separators=(",", ":"))

    # If the content was also returned..
    if ct_msg != None:
        debug( ct_msg.__dict__)
        # Check the digest
        rv = NIproc.checknib(ni_url, ct_msg.get_payload())
        if (rv != ni_errs.niSUCCESS):
            verified = False
            if verbose:
                print("Error: digest of received data does not match digest in URL %s: %s" %
                      (ni_url.get_url(), ni_errs_txt[rv]))
            if not options.lax:
                sys.exit(-9)
        else:
            verified = True

        # Write to file
        try:
            f = open(options.file_name, "wb")
        except Exception, e:
            if verbose:
Esempio n. 2
0
    debug("Returned metadata for %s:" % ni_url_str)
    debug(json.dumps(json_report, indent = 4))

    msgid = json_report.get("msgid", "")
    if msgid != sent_msgid:
        if verbose:
            print("Returned msgid (%s) does not match request msgid (%s)" %
                  (msgid, sent_msgid))
        os.remove(file_name)
        sys.exit(-19)

    # If the content was also returned..
    if ct_msg != None:
        debug(ct_msg.__dict__)
        digest= digester.get_digest()[:ni_url.get_truncated_length()]
        digest = NIproc.make_b64_urldigest(digest)
        got_content = True
                                          
        # Check the digest
        #print digest, ni_url.get_digest()
        if (digest != ni_url.get_digest()):
            if verbose:
                print("Warning: Digest of %s did not verify" % ni_url.get_url())
            if not lax:
                os.remove(file_name)
                sys.exit(-20)
            faulty = True
    else:
        # Clean up unused temporary file
        os.remove(file_name)
        file_name = None
Esempio n. 3
0
def py_nicl():
    """
    @brief Command line program to generate and validate digests in ni: URLs.
    
    Uses NIproc global instance of NI operations class

    Run:
    
    >  nicl.py --help

    to see usage and options.
    """
    
    # Options parsing and verification stuff
    usage = "%prog [-g|-w|-v] -n <name> -f <pathname of content file> [-V]\n"
    usage = usage + "       %prog -m -n <name> [-V]\n"
    usage = usage + "       %prog -b -s <suite_number> -f <pathname of content file> [-V]\n"
    usage = usage + "       The name can be either an ni: or nih: scheme URI\n"
    usage = usage + "       Return code: success 0, failure non-zero (-V for more info)\n"
    usage = usage + "       Available hashalg (suite number) options:\n"
    usage = usage + "       %s" % NIname.list_algs()
    parser = OptionParser(usage)
    
    parser.add_option("-g", "--generate", default=False,
                      action="store_true", dest="generate",
                      help="Generate hash based on content file, " + \
                           "and output name with encoded hash after the hashalg string")
    parser.add_option("-w", "--well-known", default=False,
                      action="store_true", dest="well_known",
                      help="Generate hash based on content file, " + \
                           "and output name with encoded hash in the .well_known URL " + \
                           "after the hashalg string. Applies to ni: scheme only.")
    parser.add_option("-v", "--verify", default=False,
                      action="store_true", dest="verify",
                      help="Verify hash in name is correct for content file")
    parser.add_option("-m", "--map", default=False,
                      action="store_true", dest="map_wkn",
                      help="Maps from an ni: name to a .well-known URL")
    parser.add_option("-b", "--binary", default=False,
                      action="store_true", dest="bin",
                      help="Outputs the name in binary format for a given suite number")
    parser.add_option("-V", "--verbose", default=False,
                      action="store_true", dest="verbose",
                      help="Be more long winded.")
    parser.add_option("-n", "--ni-name", dest="ni_name",
                      type="string",
                      help="The ni name template for (-g) or ni name matching (-v) content file.")
    parser.add_option("-f", "--file", dest="file_name",
                      type="string",
                      help="File with content data named by ni name.")
    parser.add_option("-s", "--suite-no", dest="suite_no",
                      type="int",
                      help="Suite number for hash algorithm to use.")

    (opts, args) = parser.parse_args()

    if not (opts.generate or opts.well_known or opts.verify or
            opts.map_wkn or opts.bin ):
        parser.error( "Must specify one of -g/--generate, -w/--well-known, -v/--verify, -m/--map or -b/--binary.")
    if opts.generate or opts.well_known or opts.verify:
        if (opts.ni_name == None) or (opts.file_name == None):
            parser.error("Must specify both name and content file name for -g, -w or -v.")
    if opts.map_wkn:
        if (opts.ni_name == None):
            parser.error("Must specify ni name for -m.")
    if opts.bin:
        if (opts.suite_no == None) or (opts.file_name == None):
            parser.error("Must specify both suite number and content file name for -b.")
    if len(args) != 0:
        parser.error("Too many or unrecognised arguments specified")

    # Execute requested action
    if opts.generate:
        n = NIname(opts.ni_name)
        ret = NIproc.makenif(n, opts.file_name)
        if ret == ni_errs.niSUCCESS:
            if opts.verbose:
                print("Name generated successfully.")
            print "%s" % n.get_url()
            sys.exit(0)
        if opts.verbose:
            print "Name could not be successfully generated."
    elif opts.well_known:
        n = NIname(opts.ni_name)
        if n.get_scheme() == "nih":
            if opts.verbose:
                print "Only applicable to ni: scheme names."
            sys.exit(1)
        ret = NIproc.makenif(n, opts.file_name)
        if ret == ni_errs.niSUCCESS:
            if opts.verbose:
                print("Name generated successfully.")
            print "%s" % n.get_wku_transform()
            sys.exit(0)
        if opts.verbose:
            print "Name could not be successfully generated"
    elif opts.verify:
        n = NIname(opts.ni_name)
        ret = NIproc.checknif(n, opts.file_name)
        if ret == ni_errs.niSUCCESS:
            if opts.verbose:
                print("Name matches content file.")
                print "%s" % n.get_url()
            sys.exit(0)
        if opts.verbose:
            print "Check of name against content failed."
    elif opts.map_wkn:
        n = NIname(opts.ni_name)
        ret = n.validate_ni_url(has_params = True)
        if ret == ni_errs.niSUCCESS:
            if n.get_scheme() == "nih":
                if opts.verbose:
                    print "Only applicable to ni: scheme names."
                sys.exit(1)
            if opts.verbose:
                print("Name validated successfully.")
            print "%s" % n.get_wku_transform()
            sys.exit(0)
        else:
            if opts.verbose:
                print "Name could not be successfully validated."
    elif opts.bin:
        (ret, bin_name) = NIproc.makebnf(opts.suite_no, opts.file_name)
        if ret == ni_errs.niSUCCESS:
            if opts.verbose:
                print("Name generated successfully.")
            print base64.b16encode(str(bin_name))
            sys.exit(0)
        else:
            if opts.verbose:
                print "Name could not be successfully generated."
    else:
        print"Should not have happened"
        sys.exit(2)

    # Print appropriate error message
    if opts.verbose:
        print "Error: %s" % ni_errs_txt[ret]
    sys.exit(1)
        
    sys.exit(0)
Esempio n. 4
0
def publish_with_dtn(ni_name, destination, authority, hash_alg, ext_json, locs, 
                     scheme, full_put, file_name, rform, verbose):
    """
    @brief Action a NetInf publish request using the HTTP convergence layer
    @param ni_name NIname object instance or None - ni URI to publish if
                          given on comand line - otherwise will be constructed
    @param destination string netloc (FQDN or IP address with optional port)
                              indicating where to send publish request
    @param authority string netloc component to insert into ni name (may be "")
    @param hash_alg string name of hash algorithm used for ni URI
    @param ext_json dictionary additional information to send with request if any
                               in the form of a JSON dictionary or None
    @param locs list of strings with locators to publish - maybe None
    @param scheme URI scheme used for ni URI
    @param full_put boolean True if the file_name with the content was given
    @param file_name string name of file to publish or None if only doing metadata
    @param rform string request format of response
    @param verbose bolean indicates how much error message output is produced
    @return 2-tuple - target - string the actual ni name published
                      payload - string - the response received on publication
    """
    
    debug("Publishing via: %s" % destination)

    # Handle full_put = True cases - we have a file with the octets in it
    if full_put:
        if ni_name is None:
            # Make a ni_name template from specified components
            ni_name = NIname((scheme, authority, "/%s" % hash_alg))

            # Construct the digest from the file name and the template
            rv = NIproc.makenif(ni_name, file_name)
            if rv != ni_errs.niSUCCESS:
                raise PublishFailure("Unable to construct digest of file %s: %s" %
                                     (file_name, ni_errs_txt[rv]), -20)
        else:
            # Check the ni_name and the file match
            rv = Niproc.checknif(ni_name, file_name)
            if rv != ni_errs.niSUCCESS:
                raise PublishFailure("Digest of file %s does not match ni_name %s: %s" %
                                     (file_name,
                                      ni_name.get_url(),
                                      ni_errs_txt[rv]), -21)

        # Guess the mimetype of the file
        m = magic.Magic(mime=True)
        ctype = m.from_file(file_name)
        debug("Content-Type: %s" % ctype)
        if ctype is None:
            # Guessing didn't work - default
            ctype = "application/octet-stream"

    else:
        ctype = None

    target = ni_name.get_canonical_ni_url()
    debug("Using URI string: %s" % target)

    # Add extra items to ext_json to pass across as metadata
    ext_json["ni"] = target
    if ctype is not None:
        ext_json["ct"] = ctype
    if authority != "":
        ext_json["http_auth"] = authority
    # Send at most two locators as a list
    if (locs is not None):
        ext_json["loclist"] = locs[:2]
    ext_json["fullPut"] = full_put
    ext_json["rform"] = rform
    
    # Create a connection to the DTN daemon
    dtn_handle = dtnapi.dtn_open()
    if dtn_handle == -1:
        raise PublishFailure("Error: unable to open connection with DTN daemon",
                             -22)

    # Generate EID + service tag for service to be accessed via DTN
    if destination is None:
        remote_service_eid = \
                    dtnapi.dtn_build_local_eid(dtn_handle,
                                               "netinfproto/service/publish")
        i = remote_service_eid.find("/netinfproto")
        destination = remote_service_eid[:i]
    else:                           
        remote_service_eid = destination + "/netinfproto/service/publish"

    # Add destination to locs if it isn't there already
    if locs is None:
        locs = []
    if destination not in locs:
        locs.append(destination)
    
    # Generate the EID and service tag for this service
    local_service_eid = dtnapi.dtn_build_local_eid(dtn_handle,
                                                   "netinfproto/app/response")
    debug("Local Service EID: %s" % local_service_eid)
    debug("Remote Service EID: %s" % remote_service_eid)

    # Check if service_eid registration exists and register if not
    # Otherwise bind to the existing registration
    regid = dtnapi.dtn_find_registration(dtn_handle, local_service_eid)
    if (regid == -1):
        # Need to register the EID.. make it permanent with 'DEFER'
        # characteristics so that bundles are saved if they arrive
        # while the handler is inactive
        # Expire the registration an hour in the future
        exp = 60 * 60
        # The registration is immediately active
        passive = False
        # We don't want to execute a script
        script = ""
        
        regid = dtnapi.dtn_register(dtn_handle, local_service_eid,
                                    dtnapi.DTN_REG_DEFER,
                                    exp, passive, script)
    else:
        dtnapi.dtn_bind(dtn_handle, regid)

    # Build the bundle to send
    # First a suitable BPQ block
    bpq = BPQ()
    bpq.set_bpq_kind(BPQ.BPQ_BLOCK_KIND_PUBLISH)
    bpq.set_matching_rule(BPQ.BPQ_MATCHING_RULE_EXACT)
    bpq.set_src_eid(local_service_eid)
    sent_msgid = str(random.randint(1, 32000))
    bpq.set_bpq_id(sent_msgid)
    bpq.set_bpq_val(target)
    bpq.clear_frag_desc()

    # Build an extension blocks structure to hold the block
    ext_blocks =  dtnapi.dtn_extension_block_list(1)

    # Construct the extension block
    bpq_block = dtnapi.dtn_extension_block()
    bpq_block.type = QUERY_EXTENSION_BLOCK
    bpq_block.flags = 0
    bpq_block.data = bpq.build_for_net()
    ext_blocks.blocks.append(bpq_block)

    # Build an extension blocks structure to hold the block
    meta_blocks =  dtnapi.dtn_extension_block_list(2)
            
    # Build a metadata block for JSON data
    md = Metadata()
    md.set_ontology(Metadata.ONTOLOGY_JSON)
    md.set_ontology_data(json.dumps(ext_json))
    json_block = dtnapi.dtn_extension_block()
    json_block.type = METADATA_BLOCK
    json_block.flags = 0
    json_block.data = md.build_for_net()
    meta_blocks.blocks.append(json_block)

    # Set up payload and placeholder if needed
    if full_put:
        # No placeholder required (obviously!)        
        pt = dtnapi.DTN_PAYLOAD_FILE
        pv = file_name
    else:
        # DTN bundle always has a payload - distinguish
        # zero length file form no content available
        # Payload is the empty string sent via memory
        pt = dtnapi.DTN_PAYLOAD_MEM
        pv = ""
        # Add a payload placeholder metablock
        md = Metadata()
        md.set_ontology(Metadata.ONTOLOGY_PAYLOAD_PLACEHOLDER)
        md.set_ontology_data("No content supplied")
        pp_block = dtnapi.dtn_extension_block()
        pp_block.type = METADATA_BLOCK
        pp_block.flags = 0
        pp_block.data = md.build_for_net()
        meta_blocks.blocks.append(pp_block)

    # We want delivery reports and publication reports
    # (and maybe deletion reports?)
    dopts = dtnapi.DOPTS_DELIVERY_RCPT | dtnapi.DOPTS_PUBLICATION_RCPT
    # - Send with normal priority.
    pri = dtnapi.COS_NORMAL
    # NetInf bundles should last a while..
    exp = (24 *60 * 60)

    # Send the bundle
    bundle_id = dtnapi.dtn_send(dtn_handle, regid, local_service_eid,
                                remote_service_eid, local_service_eid,
                                pri, dopts, exp, pt, pv, 
                                ext_blocks, meta_blocks, "", "")
    if bundle_id == None:
        raise PublishFailure("dtn_send failed - %s" %
                             dtnapi.dtn_strerror(dtnapi.dtn_errno(dtn_handle)),
                             -23)

    # Wait for a reponse - maybe also some reports
    while(True):
        # NOTE: BUG in dtnapi - timeout is in msecs
        recv_timeout = 2000 * 60
        bpq_bundle = dtnapi.dtn_recv(dtn_handle, dtnapi.DTN_PAYLOAD_FILE,
                                     recv_timeout)
        # If bpq_bundle is None then either the dtn_recv timed out or
        # there was some other error.
        if bpq_bundle != None:
            # Filter out report bundles
            if bpq_bundle.status_report != None:
                debug("Received status report")
                if bpq_bundle.status_report.flags == dtnapi.STATUS_DELIVERED:
                    if verbose:
                        print("Received delivery report re from %s sent %d seq %d" %
                              (bpq_bundle.status_report.bundle_id.source,
                               bpq_bundle.status_report.bundle_id.creation_secs,
                               bpq_bundle.status_report.bundle_id.creation_seqno))

                elif bpq_bundle.status_report.flags == dtnapi.STATUS_DELETED:
                    if verbose:
                        print("Received deletion report re from %s sent %d seq %d" %
                              (bpq_bundle.status_report.bundle_id.source,
                               bpq_bundle.status_report.bundle_id.creation_secs,
                               bpq_bundle.status_report.bundle_id.creation_seqno))

                elif bpq_bundle.status_report.flags == dtnapi.STATUS_PUBLISHED:
                    if verbose:
                        print("Received publication report re from %s sent %d seq %d" %
                              (bpq_bundle.status_report.bundle_id.source,
                               bpq_bundle.status_report.bundle_id.creation_secs,
                               bpq_bundle.status_report.bundle_id.creation_seqno))

                else:
                    if verbose:
                        print("Received unexpected report: Flags: %d" %
                              bpq_bundle.status_report.flags)
                        
                # Wait for more status reports and incoming response
                continue

            # Check the payload really is in a file
            if not bpq_bundle.payload_file:
                raise PublishFailure("Received bundle payload not in file - "
                                     "ignoring bundle", -24)
            
            # Have to delete this file before an error exit or if empty
            pfn = bpq_bundle.payload
            l = len(pfn)
            if pfn[l-1] == "\x00":
                pfn = pfn[:-1]
            debug("Got incoming bundle with response in file %s" % pfn)

            # Does the bundle have a BPQ block
            bpq_data = None
            if bpq_bundle.extension_cnt == 0:
                os.remove(pfn)
                raise PublishFailure("Error: Received bundle with no "
                               "extension block.", -25)
                          
            for blk in bpq_bundle.extension_blks:
                if blk.type == QUERY_EXTENSION_BLOCK:
                    bpq_data = BPQ()
                    if not bpq_data.init_from_net(blk.data):
                        os.remove(pfn)
                        raise PublishFailure("Error: Bad BPQ block received",
                                             -26)
    
            if bpq_data is None:
                os.remove(pfn)
                raise PublishFailure("Error: Received bundle with no BPQ block "
                                     "in extension blocks", -27)

            debug(bpq_data)
            # OK.. got the response - finish with daemon
            break
                
        elif dtnapi.dtn_errno(dtn_handle) != dtnapi.DTN_ETIMEOUT:
            raise PublishFailure(dtnapi.dtn_strerror(dtnapi.dtn_errno(dtn_handle)),
                                 -28)
        else:
            raise PublishFailure("dtn_recv timed out without receiving "
                                 "response bundle", 1)
                           
    dtnapi.dtn_close(dtn_handle)

    # Check the BPQ data is right
    bpq.set_bpq_kind(BPQ.BPQ_BLOCK_KIND_PUBLISH)
    bpq.set_matching_rule(BPQ.BPQ_MATCHING_RULE_EXACT)
    if bpq_data.bpq_kind != BPQ.BPQ_BLOCK_KIND_PUBLISH:
        raise PublishFailure("Returned BPQ block is not PUBLISH kind: %d" %
                             bpq_data.bpq_kind, -29)
    if bpq_data.matching_rule != BPQ.BPQ_MATCHING_RULE_NEVER:
        raise PublishFailure("Returned BPQ block does not have NEVER matching rule: %d" %
                             bpq_data.matching_rule, -30)
    if bpq_data.bpq_id != sent_msgid:
        raise PublishFailure("Returned BPQ block has unmatched msgis %s vs %s" %
                             (bpq_data.bpq_id, sent_msgid), -31)

    # Verify the format of the response (a bit)
    try:
        pfd = open(pfn, "rb")
        payload = pfd.read()
        pfd.close()
        os.remove(pfn)
    except Exception, e:
        raise PublishFailure("Failed to read response from payload file %s" %
                             pfn, -32)