servlib/bundling/MetadataBlockProcessor.cc
changeset 0 2b3e5ec03512
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/servlib/bundling/MetadataBlockProcessor.cc	Thu Apr 21 14:57:45 2011 +0100
@@ -0,0 +1,437 @@
+/*
+ *    Copyright 2006-2007 The MITRE Corporation
+ * 
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ * 
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ *
+ *    The US Government will not be charged any license fee and/or royalties
+ *    related to this software. Neither name of The MITRE Corporation; nor the
+ *    names of its contributors may be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include <dtn-config.h>
+#endif
+
+#include "MetadataBlockProcessor.h"
+#include "MetadataBlock.h"
+#include "Bundle.h"
+#include "SDNV.h"
+
+namespace dtn {
+
+//----------------------------------------------------------------------
+MetadataBlockProcessor::MetadataBlockProcessor()
+    : BlockProcessor(BundleProtocol::METADATA_BLOCK)
+{
+}
+
+//----------------------------------------------------------------------
+int
+MetadataBlockProcessor::consume(Bundle*    bundle,
+                                BlockInfo* block,
+                                u_char*    buf,
+                                size_t     len)
+{
+    ASSERT(bundle != NULL);
+    ASSERT(block != NULL);
+
+    int cc = BlockProcessor::consume(bundle, block, buf, len);
+ 
+    if (cc == -1) {
+        return -1; // protocol error
+    }
+ 
+    if (!block->complete()) {
+        ASSERT(cc == (int)len);
+        return cc;
+    }
+
+    parse_metadata(bundle, block);
+
+    return cc;
+}
+
+//----------------------------------------------------------------------
+bool
+MetadataBlockProcessor::validate(const Bundle*           bundle,
+                                 BlockInfoVec*           block_list,
+                                 BlockInfo*              block,
+                                 status_report_reason_t* reception_reason,
+                                 status_report_reason_t* deletion_reason)
+{
+    static const char* log = "/dtn/bundle/protocol";
+    (void)log;
+    
+    ASSERT(bundle != NULL);
+    ASSERT(block != NULL);
+    ASSERT(block->owner() == this);
+    ASSERT(block->type() == BundleProtocol::METADATA_BLOCK);
+
+    MetadataBlock *metablock = dynamic_cast<MetadataBlock*>(block->locals());
+    ASSERT(metablock != NULL);
+
+    if (metablock->error()) {
+        log_debug_p(log, "MetadataBlockProcessor::validate: "
+                         "error in metadata block preamble");
+        return handle_error(block, reception_reason, deletion_reason);
+    }
+
+    // Check for generic block errors.
+    if (!BlockProcessor::validate(bundle, block_list, block,
+                                  reception_reason, deletion_reason)) {
+        metablock->set_block_error();
+        return false;
+    }
+
+    return true;
+}
+
+//----------------------------------------------------------------------
+int
+MetadataBlockProcessor::prepare(const Bundle*    bundle,
+                                BlockInfoVec*    xmit_blocks,
+                                const BlockInfo* source,
+                                const LinkRef&   link,
+                                list_owner_t     list)
+{
+    static const char* log = "/dtn/bundle/protocol";
+    (void)log;
+
+    ASSERT(bundle != NULL);
+    ASSERT(xmit_blocks != NULL);
+
+    // Do not include metadata unless there is a received source block.
+    if (source == NULL) {
+        return BP_FAIL;
+    }
+    
+    ASSERT(source != NULL);
+    ASSERT(source->owner() == this);
+    ASSERT(source->type() == BundleProtocol::METADATA_BLOCK);
+
+    MetadataBlock* source_metadata =
+        dynamic_cast<MetadataBlock*>(source->locals());
+
+    // if the source metadata locals is null just return 
+    // XXX this indicates a bug in the Ref class or a race elsewhere
+    if (source_metadata == NULL) {
+        log_debug_p(log, "MetadataBlockProcessor::prepare: "
+                         "invalid NULL source metadata");
+        return BP_FAIL;
+    }
+
+    oasys::ScopeLock metadata_lock(source_metadata->lock(), 
+                                   "MetadataBlockProcessor::prepare");
+
+    // Do not include invalid metadata if block flags indicate as such.
+    if (source_metadata->error() &&
+       (source->flags() & BundleProtocol::BLOCK_FLAG_DISCARD_BLOCK_ONERROR)) {
+        return BP_FAIL;
+    }
+
+    // Do not include metadata that has been marked for removal.
+    if (source_metadata->metadata_removed(link)) {
+        return BP_FAIL;
+    }
+
+    BlockProcessor::prepare(bundle, xmit_blocks, source, link, list);
+
+    return BP_SUCCESS;
+}
+
+//----------------------------------------------------------------------
+void
+MetadataBlockProcessor::prepare_generated_metadata(Bundle*        bundle,
+                                                   BlockInfoVec*  blocks,
+                                                   const LinkRef& link)
+{
+    ASSERT(bundle != NULL);
+    ASSERT(blocks != NULL);
+    
+    oasys::ScopeLock bundle_lock(bundle->lock(), 
+                      "MetadataBlockProcessor::prepare_generated_metadata");
+
+    // Include metadata generated specifically for the outgoing link.
+    const MetadataVec* metadata =
+        bundle->generated_metadata().find_blocks(link);
+    if (metadata != NULL) {
+        MetadataVec::const_iterator iter;
+        for (iter = metadata->begin(); iter != metadata->end(); ++iter) {
+            if ((*iter)->metadata_len() > 0) {
+                blocks->push_back(BlockInfo(this));
+                blocks->back().set_locals(iter->object());
+            }
+        }
+    }
+
+    // Include metadata generated for all outgoing links.
+    LinkRef null_link("MetadataBlockProcessor::prepare_generated_metadata");
+    const MetadataVec*
+        nulldata = bundle->generated_metadata().find_blocks(null_link);
+
+    if (nulldata != NULL) {
+        MetadataVec::const_iterator iter;
+        for (iter = nulldata->begin(); iter != nulldata->end(); ++iter) {
+            bool link_specific = false;
+            if (metadata != NULL) {
+                MetadataVec::const_iterator liter = metadata->begin();
+                for ( ; liter != metadata->end(); ++liter) {
+                    if ((*liter)->source() &&
+                       ((*liter)->source_id() == (*iter)->id())) {
+                        link_specific = true;
+                        break;
+                    }
+                }
+            }
+
+            if (link_specific) {
+                continue;
+            }
+	    
+            blocks->push_back(BlockInfo(this));
+            blocks->back().set_locals(iter->object());
+        }
+    }
+}
+
+//----------------------------------------------------------------------
+int
+MetadataBlockProcessor::generate(const Bundle*  bundle,
+                                 BlockInfoVec*  xmit_blocks,
+                                 BlockInfo*     block,
+                                 const LinkRef& link,
+                                 bool           last)
+{
+    (void)xmit_blocks;
+
+    ASSERT(bundle != NULL);
+    ASSERT(block != NULL);
+    ASSERT(block->owner() == this);
+
+    // Determine if the outgoing metadata block was received in the
+    // bundle or newly generated; however, both should not be true.
+    MetadataBlock* metadata = NULL;
+    bool received_block = false;
+    bool generated_block = false;
+    
+    if (block->source() != NULL) {
+        ASSERT(block->source()->owner() == this);
+        received_block = true;
+        metadata = dynamic_cast<MetadataBlock*>(block->source()->locals());
+        ASSERT(metadata != NULL);
+    }
+
+    if (block->locals() != NULL) {
+        generated_block = true;
+        metadata = dynamic_cast<MetadataBlock*>(block->locals());
+        ASSERT(metadata != NULL);
+    }
+
+    ASSERT(received_block || generated_block);
+    ASSERT(!(received_block && generated_block));
+    ASSERT(metadata != NULL);
+    
+    oasys::ScopeLock metadata_lock(metadata->lock(),
+                                   "MetadataBlockProcessor::generate");
+
+    if (received_block && metadata->error()) {
+        ASSERT((block->source()->flags() &
+                BundleProtocol::BLOCK_FLAG_DISCARD_BUNDLE_ONERROR) == 0);
+        ASSERT((block->source()->flags() &
+                BundleProtocol::BLOCK_FLAG_DISCARD_BLOCK_ONERROR) == 0);
+    }
+
+    // Buffer to the metadata block ontology-specific data.
+    u_char* buf = NULL;
+    u_int32_t len = 0;
+
+    // Determine if the outgoing metadata block was modified.
+    // NOTE: metadata_modified() will set buf and len if it returns true.
+    bool modified = (received_block ?
+                     metadata->metadata_modified(link, &buf, len) : false);
+
+    // Determine the outgoing metadata block length.
+    size_t block_data_len;
+    if (received_block && !modified) {
+        block_data_len = block->source()->data_length();
+
+    } else {
+        if (!modified) {
+            buf = metadata->metadata();
+            len = metadata->metadata_len();
+        }
+        
+        // If it is modified, len and buf were set by metadata_modified().
+
+        block_data_len = SDNV::encoding_len(metadata->ontology()) +
+                         SDNV::encoding_len(len) + len;
+    }
+
+    // Determine the preamble flags for the outgoing metadata block.
+    u_int8_t flags = 0;
+    if (received_block) {
+        flags = block->source()->flags();
+    } else {
+        flags |= metadata->flags();
+    }
+    if (last) {
+        flags |= BundleProtocol::BLOCK_FLAG_LAST_BLOCK;
+    } else {
+        flags &= ~BundleProtocol::BLOCK_FLAG_LAST_BLOCK;
+    }
+
+    // Generate the generic block preamble and reserve
+    // buffer space for the block-specific data.
+    generate_preamble(xmit_blocks, block, block_type(), flags, block_data_len);
+    block->writable_contents()->reserve(block->data_offset() + block_data_len);
+    block->writable_contents()->set_len(block->data_offset() + block_data_len);
+
+    // Simply copy the incoming metadata to the outgoing buffer
+    // if the block was originally received with the bundle and
+    // subsequently not modified.
+    if (received_block && !modified) {
+        memcpy(block->writable_contents()->buf() + block->data_offset(),
+               block->source()->contents().buf() + block->data_offset(),
+               block->data_length());
+        return BP_SUCCESS;
+    }
+
+    // Write the metadata block to the outgoing buffer.
+    u_char* outgoing_buf   = block->writable_contents()->buf() + 
+                             block->data_offset();
+    u_int32_t outgoing_len = block_data_len;
+
+    // Write the ontology type.
+    size_t sdnv_len = SDNV::encode(metadata->ontology(),
+                                   outgoing_buf, outgoing_len);
+    ASSERT(sdnv_len > 0);
+    outgoing_buf += sdnv_len;
+    outgoing_len -= sdnv_len;
+
+    // Write the ontology data length.
+    sdnv_len = SDNV::encode(len, outgoing_buf, outgoing_len);
+    ASSERT(sdnv_len > 0);
+    outgoing_buf += sdnv_len;
+    outgoing_len -= sdnv_len;
+
+    // Write the ontology data.
+    ASSERT(block->contents().nfree() >= len);
+    memcpy(outgoing_buf, buf, outgoing_len);
+
+    return BP_SUCCESS;
+}
+
+//----------------------------------------------------------------------
+bool
+MetadataBlockProcessor::parse_metadata(Bundle* bundle, BlockInfo* block)
+{
+    static const char* log = "/dtn/bundle/protocol";
+
+    ASSERT(bundle != NULL);
+    ASSERT(block != NULL);
+    ASSERT(block->owner() == this);
+    ASSERT(block->type() == BundleProtocol::METADATA_BLOCK);
+    ASSERT(block->complete());
+    ASSERT(block->data_offset() > 0);
+
+    // Generate metadata block state that is maintained by
+    // the bundle and referenced by the generic block state.
+    MetadataBlock* metadata = new MetadataBlock(block);
+    bundle->mutable_recv_metadata()->push_back(metadata);
+
+    block->set_locals(metadata);
+
+    // Parse the metadata block.
+    u_char *  buf = block->data();
+    u_int32_t len = block->data_length();
+
+    // Read the metadata block ontology.
+    int ontology_len = 0;
+    u_int64_t ontology = 0;
+    if ((ontology_len = SDNV::decode(buf, len, &ontology)) < 0) {
+        log_err_p(log, "MetadataBlockProcessor::parse_metadata_ontology: "
+                       "invalid ontology field length");
+        metadata->set_block_error();
+        return false;
+    }
+    buf += ontology_len;
+    len -= ontology_len;
+
+    metadata->set_ontology(ontology);
+
+    // XXX/demmer this doesn't seem to conform to the spec... instead
+    // the length should be whatever is left in len after the SDNV for
+    // the ontology
+    
+    // Read the metadata block data length.
+    int length_len = 0;
+    u_int64_t length = 0;
+    if ((length_len = SDNV::decode(buf, len, &length)) < 0) {
+        log_err_p(log, "MetadataBlockProcessor::parse_metadata_ontology: "
+                       "invalid ontology length field length");
+        metadata->set_block_error();
+        return false;
+    }
+    buf += length_len;
+    len -= length_len;
+
+    if (len != length) {
+        log_err_p(log, "MetadataBlockProcessor::parse_metadata_ontology: "
+                       "ontology length fails to match remaining block length");
+        metadata->set_block_error();
+        return false;
+    }
+
+    // Set the offset within the buffer to the metadata block ontology data.
+    metadata->set_metadata(buf, len);
+
+    return true;
+}
+
+//----------------------------------------------------------------------
+bool
+MetadataBlockProcessor::handle_error(const BlockInfo*        block,
+                                     status_report_reason_t* reception_reason,
+                                     status_report_reason_t* deletion_reason)
+{
+    if (block->flags() & BundleProtocol::BLOCK_FLAG_REPORT_ONERROR) {
+        *reception_reason = BundleProtocol::REASON_BLOCK_UNINTELLIGIBLE;
+    }
+
+    if (block->flags() & BundleProtocol::BLOCK_FLAG_DISCARD_BUNDLE_ONERROR) {
+        *deletion_reason = BundleProtocol::REASON_BLOCK_UNINTELLIGIBLE;
+        return false;
+    }
+
+    return true;
+}
+
+//----------------------------------------------------------------------
+void
+MetadataBlockProcessor::delete_generated_metadata(Bundle*        bundle,
+                                                  const LinkRef& link)
+{
+    ASSERT(bundle != NULL);
+
+    bundle->mutable_generated_metadata()->delete_blocks(link);
+
+    MetadataVec::const_iterator iter;
+    for (iter = bundle->recv_metadata().begin();
+         iter != bundle->recv_metadata().end(); ++iter)
+    {
+        (*iter)->delete_outgoing_metadata(link);
+    }
+}
+
+} // namespace dtn