--- /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