예제 #1
0
def get_via_dtn(ni_url, dtn_eid, file_name, verbose, lax):
    """
    @brief Perform a NetInf 'get' from the http_host for the ni_url.
    @param ni_url object instance of NIname with ni name to be retrieved
    @param dtn_eid string EID for node to be accessed
                            (FQDN or IP address with optional port number)
    @param file_name string path to save content if returned
    @param verbose boolean if True print error messages, otherwise be quiet
    @param lax boolean if True return content file even if it doesn't verify
    @return 3-tuple with:
                dictionary containing returned JSON metadata decoded
                boolean indicating if content was obtained (and is in file_name
                boolean indicating if contenmt failed to verify if lax was True
                
    Assume that ni_url has a valid ni URI
    """
    # Record if content failed to verify (for lax case)
    faulty = False

    # Record if content was retrieved at all
    got_content = False

    # Must be a complete ni: URL with non-empty params field
    rv = ni_url.validate_ni_url(has_params = True)
    if (rv != ni_errs.niSUCCESS):
        if verbose:
            print("Error: %s is not a complete, valid ni scheme URL: %s" %
                  (ni_url.get_url(), ni_errs_txt[rv]))
            sys.exit(-10)

    # Generate canonical form (no netloc, ni scheme) URI for ni name
    # This goes in the BPQ block - The canonical form ensures that the
    # BPQ EXACT_MATCH will find the NDO is it is cached along the DTN path.
    # If there is a netloc in here it is sent as a Metadats item.
    ni_url_str = ni_url.get_canonical_ni_url()

    # Generate EID + service tag for service to be accessed via DTN
    remote_service_eid = "dtn://" + dtn_eid + "/netinfproto/service/get"

    # Create a connection to the DTN daemon
    dtn_handle = dtnapi.dtn_open()
    if dtn_handle == -1:
        if verbose:
            print("Error: unable to open connection with DTN daemon")
        sys.exit(-20)

    # Generate the EID and service tag for this service
    local_service_eid = dtnapi.dtn_build_local_eid(dtn_handle,
                                                   "netinfproto/app/response")
    debug("Service EID: %s" % local_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_QUERY)
    bpq.set_matching_rule(BPQ.BPQ_MATCHING_RULE_EXACT)
    bpq.set_src_eid(local_service_eid)
    sent_msgid = str(random.randint(1, 32000))
    print sent_msgid
    bpq.set_bpq_id(sent_msgid)
    bpq.set_bpq_val(ni_url_str)
    bpq.clear_frag_desc()

    # Only need to send metadata if there is a non-empty netloc in
    # the ni_url.
    netloc = ni_url.get_netloc()
    meta_blocks = None
    if netloc != "":
        # Create a JSON dictionary with netloc in it
        json_dict = { "http_auth" : netloc }
        
        # Build an extension blocks structure to hold the metadata block
        meta_blocks =  dtnapi.dtn_extension_block_list(1)
                
        # Build a metadata block for JSON data
        md = Metadata()
        md.set_ontology(Metadata.ONTOLOGY_JSON)
        md.set_ontology_data(json.dumps(json_dict))
        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)

    # Don't need to send any payload placeholder

    # Payload is the empty string sent via memory
    pt = dtnapi.DTN_PAYLOAD_MEM
    pv = ""

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

    # Build an extension blocks structure to hold the BPQ 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)

    # 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, "", "")

    # Wait for a reponse - maybe aalso 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))

                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:
                if verbose:
                    print("Received bundle payload not in file - ignoring bundle")
                sys.exit(-21)
            
            # 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 in file %s" % pfn)

            # Does the bundle have a BPQ block
            bpq_data = None
            if bpq_bundle.extension_cnt == 0:
                if verbose:
                    print("Error: Received bundle with no extension block.")
                os.remove(pfn)
                sys.exit(-22)
                          
            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):
                        if verbose:
                          print("Error: Bad BPQ block received")
                        os.remove(pfn)
                        sys.exit(-23)

            if bpq_data is None:
                if verbose:
                    print("Error: Received bundle with no BPQ block in extension blocks")
                os.remove(pfn)
                sys.exit(-23)

            debug(bpq_data)

            # Does the bundle have a Metadata block of type JSON and optionally
            # a payload placeholder
            json_data = None
            got_content = True
            if bpq_bundle.metadata_cnt > 0:
                debug("Metadata count for bundle is %d" %
                      bpq_bundle.metadata_cnt)
                for blk in bpq_bundle.metadata_blks:
                    if blk.type == METADATA_BLOCK:
                        md = Metadata()
                        if not md.init_from_net(blk.data):
                            if verbose:
                                print("Error: Bad Metadata block received")
                            os.remove(pfn)
                            sys.exit(-24)
                        if md.ontology == Metadata.ONTOLOGY_JSON:
                            json_data = md
                        elif md.ontology == Metadata.ONTOLOGY_PAYLOAD_PLACEHOLDER:
                            got_content = False
                            debug("Have placeholder: %s" % md.ontology_data)
                        else:
                            if verbose:
                                print("Warning: Metadata (type %d) block not processed" %
                                      md.ontology)

            if json_data is not None:
                debug("JSON data: %s" % json_data)
                od = json_data.ontology_data
                if od[-1:] == '\x00':
                    od = od[:-1]
                json_dict = json.loads(od)
            else:
                json_dict = None

            # Check if bundle has a (non-empty) payload even if it has a placeholder
            if (bpq_bundle.payload_len > 0) and not got_content:
                if verbose:
                    print("Error: Bundle has payload placeholder and non-empty payload")
                os.remove(pfn)
                sys.exit(-25)
            

            # Validate the digest if there is content
            faulty = False
            if got_content:
                # Unfortunately there is no easy way to do this without reading the file again.
                # But that lets us copy it into the final destination at the same time.
                # Digest output
                bin_dgst = None

                h = ni_url.get_hash_function()()

                # Open the bundle payload file
                try:
                    fr = open(pfn, "rb")
                except Exception, e :
                    if verbose:
                        print("Error: Cannot open payload file %s: Reason: %s" %
                              (pfn, str(e)))
                    os.remove(pfn)
                    sys.exit(-26)

                # Open the destination file
                try:
                    fw = open(file_name, "wb")
                except Exception, e :
                    if verbose:
                        print("Error: Cannot open destination file %s: Reason: %s" %
                              (file_name, str(e)))
                    fr.close()
                    os.remove(pfn)
                    sys.exit(-26)

                while True:

                    try:
                        l = fr.read(1024)
                    except Exception, e :
                        if verbose:
                            print("Error: Cannot read payload file %s: Reason: %s" %
                                  (fn, str(e)))
                        fr.close()
                        os.remove(pfn)
                        fw.close(0)
                        os.remove(file_name)
                        sys.exit(-27)

                    if len(l) == 0:
                        fr.close()
                        fw.close()
                        break
                     
                    h.update(l)
                    try:
                        fw.write(l)
                    except Exception, e :
                        if verbose:
                            print("Error: Cannot write destination file %s: Reason: %s" %
                                  (file_name, str(e)))
                        fr.close()
                        os.remove(pfn)
                        fw.close(0)
                        os.remove(file_name)
                        sys.exit(-28)