added in existing BPQ code
authoraidan
Tue, 26 Apr 2011 14:43:43 +0100
changeset 1 44c5e3fa6d30
parent 0 2b3e5ec03512
child 2 9daf757dfa63
child 3 c244287035f5
added in existing BPQ code
applib/dtn_types.h
apps/Makefile
apps/dtnquery/.state
apps/dtnquery/dtnquery.1
apps/dtnquery/dtnquery.c
apps/dtnquery/dtnquery_pseudo
apps/dtnrespond/dtnmatch.1
apps/dtnrespond/dtnrespond.1
apps/dtnrespond/dtnrespond.c
apps/dtnrespond/dtnrespond_pseudo
apps/dtnrespond/matching_file.csv
daemon/dtn.conf
servlib/bundling/BundleProtocol.h
--- a/applib/dtn_types.h	Thu Apr 21 14:57:45 2011 +0100
+++ b/applib/dtn_types.h	Tue Apr 26 14:43:43 2011 +0100
@@ -248,6 +248,21 @@
 };
 typedef struct dtn_extension_block_t dtn_extension_block_t;
 
+struct dtn_bpq_extension_block_data_t {
+    u_int kind;
+    u_int matching_rule;
+    struct {
+        u_int query_len;
+        char* query_val;
+    } query;
+    struct {
+        u_int num_frag_returned;
+        u_int *frag_offsets;
+        u_int *frag_lenghts;
+    } fragments;
+};
+typedef struct dtn_bpq_extension_block_data_t dtn_bpq_extension_block_data_t;
+
 /**
  * A Sequence ID is a vector of (EID, counter) values in the following
  * text format:
@@ -409,6 +424,7 @@
 extern  bool_t xdr_dtn_bundle_delivery_opts_t (XDR *, dtn_bundle_delivery_opts_t*);
 extern  bool_t xdr_dtn_extension_block_flags_t (XDR *, dtn_extension_block_flags_t*);
 extern  bool_t xdr_dtn_extension_block_t (XDR *, dtn_extension_block_t*);
+extern  bool_t xdr_dtn_bpq_extension_block_data_t (XDR *, dtn_bpq_extension_block_data_t*);
 extern  bool_t xdr_dtn_sequence_id_t (XDR *, dtn_sequence_id_t*);
 extern  bool_t xdr_dtn_bundle_spec_t (XDR *, dtn_bundle_spec_t*);
 extern  bool_t xdr_dtn_bundle_id_t (XDR *, dtn_bundle_id_t*);
@@ -430,6 +446,7 @@
 extern bool_t xdr_dtn_bundle_delivery_opts_t ();
 extern bool_t xdr_dtn_extension_block_flags_t ();
 extern bool_t xdr_dtn_extension_block_t ();
+extern bool_t xdr_dtn_bpq_extension_block_data_t ();
 extern bool_t xdr_dtn_sequence_id_t ();
 extern bool_t xdr_dtn_bundle_spec_t ();
 extern bool_t xdr_dtn_bundle_id_t ();
--- a/apps/Makefile	Thu Apr 21 14:57:45 2011 +0100
+++ b/apps/Makefile	Tue Apr 26 14:43:43 2011 +0100
@@ -23,27 +23,29 @@
 SRCDIR := ..
 endif
 
-SINGLE_SOURCE_APPS := 			\
+SINGLE_SOURCE_APPS := 		\
 	dtncat/dtncat			\
-	dtncp/dtncp			\
+	dtncp/dtncp			    \
 	dtncpd/dtncpd			\
 	dtnping/dtnping			\
-	dtnping/dtntraceroute		\
+	dtnping/dtntraceroute	\
 	dtnping/dtnreporter		\
-	dtnperf/dtnperf-server		\
+	dtnperf/dtnperf-server	\
 	dtnrecv/dtnrecv			\
+    dtnquery/dtnquery       \
+    dtnrespond/dtnrespond   \
 	dtnsink/dtnsink			\
 	num2sdnv/num2sdnv		\
 
-OTHER_APPS :=				\
+OTHER_APPS :=				    \
 	dtnmoteproxy/dtnmoteproxy	\
-	dtntest/dtntest			\
-	dtntunnel/dtntunnel		\
+	dtntest/dtntest			    \
+	dtntunnel/dtntunnel		    \
 	dtnperf/dtnperf-client		\
-	tca_admin/tca_admin		\
-	dtnsend/dtnsend			\
-	dtnsource/dtnsource		\
-	num2sdnv/sdnv2num		\
+	tca_admin/tca_admin		    \
+	dtnsend/dtnsend			    \
+	dtnsource/dtnsource		    \
+	num2sdnv/sdnv2num		    \
 
 APPS := $(SINGLE_SOURCE_APPS) $(OTHER_APPS)
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/apps/dtnquery/.state	Tue Apr 26 14:43:43 2011 +0100
@@ -0,0 +1,10 @@
+#id, type, query, date, expiry, file_name, state, frag_offset, frag_length
+
+000, q, test, 2011-01-01 14:20:00, 3600, test_dest, expired, ,
+
+001, q, foo, 2011-01-10 14:20:00, 36000, foo_dest, pending, ,
+002, r, foo, 2011-01-11 14:20:00, , , complete, ,
+ 
+003, q, bar, 2011-01-10 14:30:00, 36000, bar_dest, pending, ,
+004, r, bar, 2011-01-11 14:30:00, , , partial, 0, 512
+005, r, bar, 2011-01-11 14:35:00, , , partial, 512, 512
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/apps/dtnquery/dtnquery.1	Tue Apr 26 14:43:43 2011 +0100
@@ -0,0 +1,131 @@
+.\"
+.\"    Copyright 2004-2006 Intel 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.
+.\"
+
+.TH dtnquery 1 "November 16, 2010"
+.LO 1
+.SH NAME
+dtnquery \- send DTN queries 
+.SH SYNOPSIS
+.B dtnquery 
+.RB -s 
+.IR src
+.RB [ -d 
+.IR dest ]
+.RB [ -f 
+.IR filename ]
+.RB [ -t
+.IR literal | base64 | file ]
+.RB [ -q 
+.IR query ]
+.RB [ -r
+.IR exact ]
+.RB [ -m
+.IR send | receive | both ]
+.RB [ -E
+.IR seconds ]
+.RB [ -e 
+.IR seconds ]
+.RB [ -hv ]
+
+.SH DESCRIPTION
+.B dtnquery
+injects bundle queries into a DTN. To do this, it connects
+to a dtnd daemon using the DTN API. 
+.PP
+If dtnquery is started in asynchronous mode to send a query
+.B -m send
+it will close once all outstanding queries have been sent.
+.PP
+It should then be run again in asynchronous receive mode
+.B -m receive
+to receive the query response.
+.PP
+If this connection is synchronous (default)
+.B -m both
+dtnquery will both send the query and wait to receive the 
+response.
+.PP
+On receiving a response, it will be stored in the directory
+specified by the
+.B -r <response_path>.
+
+.SH RETURN VALUES
+Returns 1 if there was an error processing the arguments.
+Otherwise 
+.B dtnquery
+returns 0.
+
+.SH OPTIONS
+.TP
+.B \-\^s
+The source EID for the bundle query. It should be an endpoint for which
+the dtnd is responsible, but that is not currently enforced.
+(Required)
+.TP
+.B \-\^d
+The destination EID for the bundle.
+.TP
+.B \-\^f
+The file name to save the response as.d)
+.TP
+.B \-\^t
+Type of query. Can be `literal' (default), `base64' or `file'. 
+.TP
+.B \-\^q
+The query value, or if the type of query is file (-t file), the matching
+file path. Values/paths must be enclosed in quotes e.g. -p "some
+literal query" or -p "my file.txt". Query files must be comma
+separated containing see
+.B dtnmatch.
+.TP
+.B \-\^r
+The matching rule. Currently only `exact' is supported.
+.TP
+.B \-\^m
+Mode to run in. Can be asynchronous: `send' or `receive' or synchronous
+`both' (default).
+.TP
+.B \-\^E
+The number of seconds to wait for a query response. Default 0: listen
+forever
+.TP
+.B \-\^e
+The number of seconds until the bundle expires from the DTN. Default
+is 3600 (one hour).
+.TP
+.B \-\^h
+Print a help message and exit.
+.TP
+.B \-\^v
+Be verbose. Adds diagnostic information to the standard output.
+.TP
+
+.SH ENVIRONMENT
+.TP
+.B DTNAPI_ADDR
+If
+.B DTNAPI_ADDR
+is set, 
+.B dtnsend
+uses it as the hostname to connect to for DTN API operations.
+.TP
+.B DTNAPI_PORT
+If
+.B DTNAPI_PORT
+is set, its value is used as the TCP port to connect to
+for DTN API operations.
+.SH "SEE ALSO"
+dtnrespond(1), dtnmatch(1), dtnd(8).
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/apps/dtnquery/dtnquery.c	Tue Apr 26 14:43:43 2011 +0100
@@ -0,0 +1,964 @@
+/*
+ *    Copyright 2004-2006 Intel 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include <dtn-config.h>
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <strings.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "dtn_api.h"
+#include "sdnv-c.h"
+
+#define BUFSIZE 16
+#define BLOCKSIZE 8192
+#define COUNTER_MAX_DIGITS 9
+
+// todo: move these out to a header file
+#define DTN_BPQ_LITERAL 1
+#define DTN_BPQ_BASE64 2
+#define DTN_BPQ_FILE 3
+
+#define DTN_BPQ_EXACT 1
+
+#define DTN_BPQ_SEND 1
+#define DTN_BPQ_RECV 2
+#define DTN_BPQ_SEND_RECV 3
+
+#define DTN_BPQ_BLOCK_TYPE 0xC8
+#define DTN_BPQ_BLOCK_FLAGS 0x00
+
+#define DTN_BPQ_KIND_QUERY 0x00
+#define DTN_BPQ_KIND_RESPONSE 0x01
+
+// Find the maximum commandline length
+#ifdef __FreeBSD__
+/* Needed for PATH_MAX, Linux doesn't need it */
+#include <sys/syslimits.h>
+#endif
+
+#ifndef PATH_MAX
+/* A conservative fallback */
+#define PATH_MAX 1024
+#endif
+
+//global variables
+const char* progname;
+
+/*******************************************************************************
+* usage:
+* display cmd line options to user.
+*******************************************************************************/
+int
+usage()
+{
+    fprintf(stderr, "usage: %s -s < src endpoint > -d < dest endpoint > "
+                    "[opts]\n", progname);
+    fprintf(stderr, "options:\n");
+    fprintf(stderr, " -f  < filename > response filename\n");
+    fprintf(stderr, " -q  < query > query or matching file\n");
+    fprintf(stderr, " -t  < literal | base64 | file > query type\n");
+    fprintf(stderr, " -r  < exact > matching rule\n");
+    fprintf(stderr, " -m  < send | receive | both > mode\n");
+    fprintf(stderr, " -o  < seconds > timeout\n");
+    fprintf(stderr, " -e  < seconds > bundle expiry time\n");
+    fprintf(stderr, " -i  < regid > existing registration id\n");
+    fprintf(stderr, " -E  < seconds > registration expiry time\n");
+    fprintf(stderr, " -A  < defer | drop | exec > failure action\n");
+    fprintf(stderr, " -S  < script > failure script for exec action\n");
+    fprintf(stderr, " -P  < bulk | normal | expedited | reserved > priority\n");
+    fprintf(stderr, " -D  request end-to-end delivery receipt\n");
+    fprintf(stderr, " -X  request deletion receipt\n");
+    fprintf(stderr, " -F  request bundle forwarding receipts\n");
+    fprintf(stderr, " -R  request bundle reception receipts\n");
+    fprintf(stderr, " -c  request custody transfer\n");
+    fprintf(stderr, " -C  request custody transfer receipts\n");
+    fprintf(stderr, " -1  assert destination endpoint is a singleton\n");
+    fprintf(stderr, " -N  assert destination endpoint is not a singleton\n");
+    fprintf(stderr, " -W  set the do not fragment option\n");
+    fprintf(stderr, " -h  help\n");
+    fprintf(stderr, " -v  verbose\n");
+
+    return 0;
+}
+
+/*******************************************************************************
+* parse matching file
+* if matching file is passed in rather than cml line literal query,
+* extract relevant information by reading the csv file and splitting
+* it into tokens.
+*
+* Matching File Format:
+* [ matching_rule, encoding, query, response_path, expiry ]
+*******************************************************************************/
+int
+parse_matching_file(const char * filename,
+    int * matching_rule,
+    int * query_type,
+    char * query,
+    dtn_timeval_t * bundle_expiry,
+    int verbose)
+{
+    FILE * file;
+    char * token;
+    char line[PATH_MAX];
+
+    memset(line, 0, sizeof(char) * PATH_MAX);
+    
+    if (verbose) fprintf(stdout, "openning matching file...\n");
+    file = fopen(filename, "r");
+    if (file == NULL) {
+        fprintf(stderr, "Error opening file: %s", filename);
+        return 1;    
+    }
+
+    fgets(line, PATH_MAX, file);
+    if (verbose) fprintf(stdout, "matching file %s contains [ %s ]\n", filename, line);
+
+    fclose(file);
+    if (verbose) fprintf(stdout, "closed matching file...\n");
+
+    //matching rule
+    token = strtok (line, ",");
+    if (token == NULL)  return 1;
+    *matching_rule = atoi(token);        
+
+    //encoding
+    token = strtok (NULL, ",");
+    if (token == NULL)  return 1;
+    *query_type = atoi(token);
+
+    //query
+    token = strtok (NULL, ",");
+    if (token == NULL)  return 1;
+    strncpy(query, token, PATH_MAX);
+
+    //response path - to be ignored
+    token = strtok (NULL, ",");
+    if (token == NULL)  return 1;
+
+    //expiry
+    token = strtok (NULL, ",");
+    if (token == NULL)  return 1;
+    *bundle_expiry = atoi(token);
+
+    //ensure there are no more tokens
+    token = strtok (NULL, ",");
+    if (token != NULL)  return 1;
+
+    return 0;
+}
+
+/*******************************************************************************
+* parse options:
+* set internal variables based on cmd line args.
+* calls parse matching file if required.
+* returns success or exits on failure.
+*******************************************************************************/
+int
+parse_options(int argc, char** argv,
+    char * src_eid_name,                // s
+    char * dest_eid_name,               // d
+    char * filename,                    // f
+    char * query,                       // q
+    int * query_type,                   // t
+    int * matching_rule,                // r
+    int * mode,                         // m
+    dtn_timeval_t * timeout,            // o
+    dtn_timeval_t * bundle_expiry,      // e
+    dtn_reg_id_t  * regid,              // i
+    dtn_timeval_t * reg_expiry,         // E
+    int  * reg_fail_action,             // A
+    char * reg_fail_script,             // S
+    dtn_bundle_priority_t * priority,   // P
+    int * delivery_options,             // D X F R c C 1 N W
+    int * verbose)                      // v
+{
+    int c, done = 0;
+    char matching_file[PATH_MAX];
+
+    progname = argv[0];
+
+    //initialize strings
+    memset(src_eid_name,    0, sizeof(char) * PATH_MAX);
+    memset(dest_eid_name,   0, sizeof(char) * PATH_MAX);
+    memset(filename,        0, sizeof(char) * PATH_MAX);
+    memset(query,           0, sizeof(char) * PATH_MAX);
+    memset(matching_file,   0, sizeof(char) * PATH_MAX);
+    memset(reg_fail_script, 0, sizeof(char) * PATH_MAX);
+
+    while( !done )
+    {
+        c = getopt(argc, argv, "s:d:f:q:t:r:m:o:e:i:E:A:S:P:DXFcC1NWvhH");
+        switch(c)
+        {
+        case 's':            
+            strncpy(src_eid_name, optarg, PATH_MAX);
+            break;
+        case 'd':    
+            strncpy(dest_eid_name, optarg, PATH_MAX);
+            break;
+        case 'f':    
+            strncpy(filename, optarg, PATH_MAX);
+            break;
+        case 'q':            
+            strncpy(query, optarg, PATH_MAX);
+            break;
+        case 't':
+            if (!strcasecmp(optarg, "literal")) {
+                *query_type = DTN_BPQ_LITERAL;
+                break;
+            } else if (!strcasecmp(optarg, "base64")) {
+                *query_type = DTN_BPQ_BASE64;
+                break;
+            } else if (!strcasecmp(optarg, "file")) {
+                *query_type = DTN_BPQ_FILE;
+                break;
+            } else {
+                fprintf(stderr, "invalid query type '%s'\n", optarg);
+                usage();
+                exit(1);
+            }
+        case 'r':
+            if (!strcasecmp(optarg, "exact")) {
+                *matching_rule = DTN_BPQ_EXACT;
+                break;
+            } else {
+                fprintf(stderr, "invalid query type '%s'\n", optarg);
+                usage();
+                exit(1);
+            }
+        case 'm':
+            if (!strcasecmp(optarg, "send")) {
+                *mode = DTN_BPQ_SEND;
+                break;
+            } else if (!strcasecmp(optarg, "receive")) {
+                *mode = DTN_BPQ_RECV;
+                break;
+            } else if (!strcasecmp(optarg, "both")) {
+                *mode = DTN_BPQ_SEND_RECV;
+                break;
+            } else {
+                fprintf(stderr, "invalid mode '%s'\n", optarg);
+                usage();
+                exit(1);
+            }
+        case 'o':
+            *timeout = atoi(optarg);
+            break;
+        case 'e':
+            *bundle_expiry = atoi(optarg);  
+            break;
+        case 'i':
+            *regid = atoi(optarg);
+            break;
+        case 'E':
+            *reg_expiry = atoi(optarg);
+            break;
+        case 'A':
+            if (!strcasecmp(optarg, "defer")) {
+                *reg_fail_action = DTN_REG_DEFER;
+
+            } else if (!strcasecmp(optarg, "drop")) {
+                *reg_fail_action = DTN_REG_DROP;
+
+            } else if (!strcasecmp(optarg, "exec")) {
+                *reg_fail_action = DTN_REG_EXEC;
+
+            } else {
+                fprintf(stderr, "invalid failure action '%s'\n", optarg);
+                usage();
+                exit(1);
+            }
+            break;
+        case 'S':
+            strncpy(reg_fail_script, optarg, PATH_MAX);
+            break;
+        case 'P':
+            if (!strcasecmp(optarg, "bulk"))   {
+                *priority = COS_BULK;
+            } else if (!strcasecmp(optarg, "normal")) {
+                *priority = COS_NORMAL;
+            } else if (!strcasecmp(optarg, "expedited")) {
+                *priority = COS_EXPEDITED;
+            } else if (!strcasecmp(optarg, "reserved")) {
+                *priority = COS_RESERVED;
+            } else {
+                fprintf(stderr, "invalid priority value %s\n", optarg);
+                usage();
+                exit(1);
+            }
+            break;
+        case 'D':
+            *delivery_options |= DOPTS_DELIVERY_RCPT;
+            break;
+        case 'X':
+            *delivery_options |= DOPTS_DELETE_RCPT;
+            break;
+        case 'F':
+            *delivery_options |= DOPTS_FORWARD_RCPT;
+            break;
+        case 'R':
+            *delivery_options |= DOPTS_RECEIVE_RCPT;
+            break;
+        case 'c':
+            *delivery_options |= DOPTS_CUSTODY;
+            break;
+        case 'C':
+            *delivery_options |= DOPTS_CUSTODY_RCPT;
+            break;
+        case '1':
+            *delivery_options |= DOPTS_SINGLETON_DEST;
+            break;
+        case 'N':
+            *delivery_options |= DOPTS_MULTINODE_DEST;
+            break;
+        case 'W':
+            *delivery_options |= DOPTS_DO_NOT_FRAGMENT;
+            break;
+        case 'v':
+            *verbose = 1;
+            break;
+        case 'h':
+        case 'H':
+            usage();
+
+            exit(0);
+        case -1:
+            done = 1;
+            break;
+        default:
+            // getopt already prints error message for unknown option characters
+            usage();
+            exit(1);
+        }
+        
+        // now set matching file if required
+        if (*query_type == DTN_BPQ_FILE) {
+            strncpy(matching_file, query, PATH_MAX);
+            memset(query, 0, sizeof(char) * PATH_MAX);
+
+            int ret = parse_matching_file(matching_file, matching_rule,
+                                          query_type, query, bundle_expiry, *verbose);
+            if (ret != DTN_SUCCESS) {
+                fprintf(stderr, "error parsing matching file, "
+                                "see man page dtnmatch(1)\n");
+                usage(1);
+                exit(1);
+            }
+        }
+    }
+    return 0;
+}
+
+/*******************************************************************************
+* validate options:
+* as there are different requirements depending on the mode,
+* the validation will differ accordingly.
+* returns success or exits on failure
+*******************************************************************************/
+int
+validate_options(const char * src_eid_name,
+    const char * dest_eid_name,
+    const char * filename,
+    const char * query,
+    int query_type,
+    int matching_rule,
+    int mode,
+    dtn_timeval_t timeout,
+    dtn_timeval_t bundle_expiry)
+{
+//todo: add new options
+#define REQUIRE(test, err_msg) \
+    if(!test) { \
+        fprintf(stderr, err_msg); \
+        usage(); \
+        exit(1); \
+    }
+
+    switch (mode)
+    {
+    case DTN_BPQ_SEND:  //requires src, dest, query
+        REQUIRE(strlen(src_eid_name) > 0, "-s <src eid> required\n");
+        REQUIRE(strlen(dest_eid_name) > 0, "-d <dest eid> required\n");
+        REQUIRE(strlen(query) > 0, "-q <query> required\n");
+        break;
+
+    case DTN_BPQ_RECV:  //requires src, filename
+        REQUIRE(strlen(src_eid_name) > 0, "-s <src eid> required\n");
+        REQUIRE(strlen(filename) > 0, "-f <filename> required\n");
+        break;
+
+    case DTN_BPQ_SEND_RECV: //requires src, dest, query, filename
+        REQUIRE(strlen(src_eid_name) > 0, "-s <src eid> required\n");
+        REQUIRE(strlen(dest_eid_name) > 0, "-d <dest eid> required\n");
+        REQUIRE(strlen(filename) > 0, "-f <filename> required\n");
+        REQUIRE(strlen(query) > 0, "-q <query> required\n");
+        break;
+    default:  
+        REQUIRE(mode == DTN_BPQ_SEND ||
+                mode == DTN_BPQ_RECV ||
+                mode == DTN_BPQ_SEND_RECV, "-m <mode> invalid mode\n")
+    }
+    REQUIRE(query_type == DTN_BPQ_LITERAL ||
+            query_type == DTN_BPQ_BASE64  ||
+            query_type == DTN_BPQ_FILE, "-t <query type> invalid type\n");
+
+    REQUIRE(matching_rule == DTN_BPQ_EXACT, "-r <matching rule> invalid rule\n");
+fprintf(stdout, "timeout: %d, REQUIRE(timeout >= -1): %d\n", timeout, timeout >= -1);
+//    REQUIRE(timeout >= -1, "-o <timeout> must ba a positive integer or -1: forever\n");
+    REQUIRE(bundle_expiry > 0, "-e <expiry> must be a positive integer\n");
+#undef REQUIRE
+//todo: check this is ok
+    return 0;
+}
+
+/*******************************************************************************
+* register with dtn:
+* 
+*******************************************************************************/
+int
+register_with_dtn(dtn_handle_t handle,
+    dtn_endpoint_id_t * src_eid,
+    dtn_reg_id_t * regid,
+    dtn_timeval_t reg_expiration,
+    int reg_fail_action,
+    char * reg_fail_script)
+{
+    int call_bind = 0;
+    dtn_reg_info_t reginfo;
+
+    // if no regid has been given we need to create a new registration
+    if (*regid == DTN_REGID_NONE) {
+        memset(&reginfo, 0, sizeof(dtn_reg_info_t));
+
+        // create a new registration based on this eid
+        dtn_copy_eid(&reginfo.endpoint, src_eid);
+        reginfo.regid             = *regid;
+        reginfo.expiration        = reg_expiration;
+        reginfo.flags             = reg_fail_action;
+        reginfo.script.script_val = reg_fail_script;
+        reginfo.script.script_len = strlen(reg_fail_script) + 1;
+    }
+
+    // try to see if there is an existing registration that matches
+    // the given endpoint, in which case we'll use that one.
+    if (*regid == DTN_REGID_NONE) {
+        fprintf(stdout, "### src_eid: %s, regid: %d  ###\n", src_eid->uri, *regid);
+        if (dtn_find_registration(handle, src_eid, regid) != DTN_SUCCESS &&
+            dtn_errno(handle) != DTN_ENOTFOUND) {
+            fprintf(stderr, "error finding registration: %s\n",
+                    dtn_strerror(dtn_errno(handle)));
+            dtn_close(handle);
+            exit(1);
+        }
+        call_bind = 1;
+    }
+    // if the user didn't give us a registration to use, get a new one
+    if (*regid == DTN_REGID_NONE) {
+        if (dtn_register(handle, &reginfo, regid) != DTN_SUCCESS) {
+            fprintf(stderr, "error registering: %s\n",
+                    dtn_strerror(dtn_errno(handle)));
+            dtn_close(handle);
+            exit(1);
+        }
+        call_bind = 0;
+    } else {
+        call_bind = 1;
+    }
+
+    if (call_bind) {
+        // bind the current handle to the found registration
+        if (dtn_bind(handle, *regid) != DTN_SUCCESS) {
+            fprintf(stderr, "error binding to registration: %s\n",
+                    dtn_strerror(dtn_errno(handle)));
+            dtn_close(handle);
+            exit(1);
+        }
+    }
+
+    return DTN_SUCCESS;
+}
+
+/*******************************************************************************
+* parse eid:
+*  
+* code lifted from dtnsend
+* todo: check this
+*******************************************************************************/
+dtn_endpoint_id_t *
+parse_eid(dtn_handle_t handle,
+    dtn_endpoint_id_t * eid,
+    const char * str,
+    int verbose)
+{
+    // try the string as an actual dtn eid
+    if (dtn_parse_eid_string(eid, str) == DTN_SUCCESS) {
+        if (verbose) fprintf(stdout, "%s (literal)\n", str);
+        return eid;
+    }
+
+    // build a local eid based on the configuration of our dtn
+    // router plus the str as demux string
+    else if (dtn_build_local_eid(handle, eid, str) == DTN_SUCCESS) {
+        if (verbose) fprintf(stdout, "%s (local)\n", str);
+        return eid;
+    }
+    else {
+        fprintf(stderr, "invalid eid string '%s'\n", str);
+        exit(1);
+    }
+}
+
+/*******************************************************************************
+* handle file transfer:
+* 
+*******************************************************************************/
+int
+handle_file_transfer(dtn_bundle_spec_t bundle_spec,
+    dtn_bundle_payload_t payload,
+    const char * filename,
+    int verbose)
+{
+    int i;
+    char line[PATH_MAX];
+    char new_filename[PATH_MAX];
+    FILE * output_file = NULL;
+    FILE * input_file = NULL;
+
+    strncpy(new_filename, filename, PATH_MAX);
+
+    // try to open file (need to find a filename + version that does not already exist)
+    // if it does exist, close, create new filename + version and try to reopen
+    // return error if can't find a valid filename after 100 attempts
+    output_file = fopen(new_filename, "r");
+    for (i=0; output_file != NULL; ++i) {
+        fclose(output_file);
+
+        if (i >= 100)
+            return -1;
+
+        snprintf(new_filename, PATH_MAX, "%s.%02d", filename, i);
+        output_file = fopen(new_filename, "r");
+    }
+
+    if (verbose) fprintf(stdout, "saving payload file as: %s\n", new_filename);
+
+    // new_filename now contains the name of the 
+    // new file in which to store the payload
+    if ((output_file = fopen(new_filename, "w")) == NULL) {
+        fprintf(stderr, "error opening file in which to store received payload\n");
+        return -1;
+    }
+  
+    if ((input_file = fopen(payload.filename.filename_val, "r")) == NULL) {
+        fprintf(stderr, "error opening the received payload file\n");
+        return -1;
+    }
+
+    // copy payload to file
+    while (fgets(line, PATH_MAX, input_file) != NULL) 
+        fprintf(output_file, "%s", line);
+        
+    fclose(input_file);
+    fclose(output_file);
+
+    return 0;
+}
+
+/*******************************************************************************
+* bpq to char array:
+* encode as SDNVs,
+*   BPQ-kind             1-byte
+*   matching rule type   1-byte
+*   BPQ-value-length     SDNV
+*   BPQ-value            n-bytes
+*   number of fragments  SDNV
+*   fragment offsets     SDNV
+*   fragment lengths     SDNV
+*
+* @return The number of bytes or -1 on error
+*******************************************************************************/
+int
+bpq_to_char_array(const dtn_bpq_extension_block_data_t * bpq, char* buf, size_t buf_len)
+{
+    int i=0, j=0, k=0;
+    int encoding_len;    
+    char encoding[PATH_MAX];
+
+    memset(buf, 0, buf_len);
+
+    // BPQ-kind             1-byte
+    if (i < buf_len)    buf[i++] = (char) bpq->kind;
+
+    // matching rule type   1-byte
+    if (i < buf_len)    buf[i++] = (char) bpq->matching_rule;
+
+    // BPQ-value-length     SDNV
+    if ( (encoding_len = sdnv_encode (bpq->query.query_len, encoding, PATH_MAX)) == -1 )
+        return -1;
+    for (j=0; i<buf_len && j<encoding_len; ++j)
+        buf[i++] = encoding[j];
+
+    // BPQ-value            n-bytes
+    for (j=0; i<buf_len && j<bpq->query.query_len; ++j)
+        buf[i++] = bpq->query.query_val[j];
+
+    // number of fragments  SDNV
+    if ( (encoding_len = sdnv_encode (bpq->fragments.num_frag_returned, encoding, PATH_MAX)) == -1 )
+        return -1;
+    for (j=0; i<buf_len && j<encoding_len; ++j)
+        buf[i++] = encoding[j];
+
+    
+    for (k=0; k<bpq->fragments.num_frag_returned; ++k) {
+
+        // fragment offsets     SDNV
+        if ( (encoding_len = sdnv_encode (bpq->fragments.frag_offsets[k], encoding, PATH_MAX)) == -1 )
+            return -1;
+        for (j=0; i<buf_len && j<encoding_len; ++j)
+            buf[i++] = encoding[j];
+
+        // fragment lengths     SDNV
+        if ( (encoding_len = sdnv_encode (bpq->fragments.frag_lenghts[k], encoding, PATH_MAX)) == -1 )
+            return -1;
+        for (j=0; i<buf_len && j<encoding_len; ++j)
+            buf[i++] = encoding[j];
+    }
+
+    
+    return i;
+}
+/*******************************************************************************
+* char array to bpq:
+* decode as SDNVs,
+*   BPQ-kind             1-byte
+*   matching rule type   1-byte
+*   BPQ-value-length     SDNV
+*   BPQ-value            n-bytes
+*   number of fragments  SDNV
+*   fragment offsets     SDNV
+*   fragment lengths     SDNV
+*
+* @return DTN_SUCCESS or -1 on error
+*******************************************************************************/
+int
+char_array_to_bpq(const char* buf, size_t buf_len, dtn_bpq_extension_block_data_t * bpq) 
+{
+    int i=0, j=0;
+    int decoding_len=0;
+
+    // BPQ-kind             1-byte
+    if (i<buf_len) bpq->kind = (u_int) buf[i++];
+
+    // matching rule type   1-byte
+    if (i<buf_len) bpq->matching_rule = (u_int) buf[i++];
+
+    // BPQ-value-length     SDNV
+    if ( (decoding_len = sdnv_decode (buf[i], buf_len - i, &(bpq->query.query_len))) == -1 )
+        return -1;
+    i += decoding_len;
+
+    // BPQ-value            n-bytes
+    if (i<buf_len) bpq->query.query_val = &(buf[i]);
+    i += bpq->query.query_len;
+
+    // number of fragments  SDNV
+    if ( (decoding_len = sdnv_decode (buf[i], buf_len - i, &(bpq->fragments.num_frag_returned))) == -1 )
+        return -1;
+    i += decoding_len;
+
+    for (j=0; i<buf_len && j<bpq->fragments.num_frag_returned; ++j) {
+
+        // fragment offsets     SDNV
+        if ( (decoding_len = sdnv_decode (buf[i], buf_len - i, &(bpq->fragments.frag_offsets[j]))) == -1 )
+            return -1;
+        i += decoding_len;
+
+        // fragment lengths     SDNV
+        if ( (decoding_len = sdnv_decode (buf[i], buf_len - i, &(bpq->fragments.frag_lenghts[j]))) == -1 )
+            return -1;
+        i += decoding_len;
+    }
+
+    if (i != buf_len)
+        return -1;
+
+    return DTN_SUCCESS;
+}
+
+/*******************************************************************************
+* send bpq:
+* given a registration handle, build a bundle with
+* a BPQ extension block and an empty payload and 
+* send it to the destination.
+*******************************************************************************/
+int
+send_bpq(dtn_handle_t handle,
+    dtn_reg_id_t regid,
+    const dtn_endpoint_id_t * src_eid,
+    const dtn_endpoint_id_t * dest_eid,
+    char * query,
+    int matching_rule,
+    int bundle_expiry,
+    dtn_bundle_priority_t  priority,
+    int delivery_options,
+    int verbose)
+{
+    int  ret = 0;
+    char buf [PATH_MAX];
+    size_t buf_len = 0;
+    dtn_bundle_id_t                 bundle_id;
+    dtn_bundle_spec_t               bundle_spec;
+    dtn_extension_block_t           bpq_block;
+    dtn_bpq_extension_block_data_t  bpq_block_data;
+    dtn_bundle_payload_t            payload;
+
+    memset(buf,             0, PATH_MAX);
+    memset(&bundle_spec,    0, sizeof(dtn_bundle_spec_t));
+    memset(&bpq_block,      0, sizeof(dtn_extension_block_t));
+    memset(&bpq_block_data, 0, sizeof(dtn_bpq_extension_block_data_t));
+    memset(&payload,        0, sizeof(dtn_bundle_payload_t));
+    
+    // set the bpq block data
+    bpq_block_data.kind = DTN_BPQ_KIND_QUERY;
+    bpq_block_data.matching_rule = matching_rule;
+    bpq_block_data.query.query_len = strlen(query) + 1;     // include the null char at the end
+    bpq_block_data.query.query_val = query; 
+    bpq_block_data.fragments.num_frag_returned = 0;
+    bpq_block_data.fragments.frag_offsets = NULL;
+    bpq_block_data.fragments.frag_lenghts = NULL;
+
+    buf_len = bpq_to_char_array(&bpq_block_data, buf, PATH_MAX);
+
+    // set the bpq block
+    bpq_block.type = DTN_BPQ_BLOCK_TYPE;
+    bpq_block.flags = DTN_BPQ_BLOCK_FLAGS;
+    bpq_block.data.data_len = buf_len;
+    bpq_block.data.data_val = buf;
+    
+    // set the payload (empty)
+    dtn_set_payload(&payload, DTN_PAYLOAD_MEM, NULL, 0);    
+
+    // set the bundle spec eids
+    if (verbose) fprintf(stdout, "Source: %s\n", src_eid->uri);
+    if (verbose) fprintf(stdout, "Destination: %s\n", dest_eid->uri);
+    bundle_spec.source = *src_eid;
+    bundle_spec.dest = *dest_eid;
+
+    dtn_copy_eid(&bundle_spec.replyto, &bundle_spec.source);// add support for this to be set differently
+
+    // set the bundle spec dtn options
+    bundle_spec.expiration = bundle_expiry;
+    bundle_spec.dopts = delivery_options;
+    bundle_spec.priority = priority;
+
+    // set the bundle extension
+    bundle_spec.blocks.blocks_len = 1;
+    bundle_spec.blocks.blocks_val = &bpq_block;
+
+    // send the bundle, bpq extension and empty payload
+    if (verbose) fprintf(stdout, "Sending bundle to: %s\n", dest_eid->uri);
+    ret = dtn_send(handle, regid, &bundle_spec, &payload, &bundle_id);
+    if (ret != DTN_SUCCESS) {
+        fprintf(stderr, "error sending bundle: %d (%s)\n",
+                    ret, dtn_strerror(dtn_errno(handle)));
+    } else if (verbose) {
+        fprintf(stdout, "bundle sent successfully: id %s,%llu.%llu\n",
+                    bundle_id.source.uri,
+                    bundle_id.creation_ts.secs,
+                    bundle_id.creation_ts.seqno); 
+    }
+    return ret;   
+}
+
+/*******************************************************************************
+* recv bpq:
+* todo.
+*******************************************************************************/
+int
+recv_bpq(dtn_handle_t handle,
+    dtn_timeval_t timeout,
+    const char * filename,
+    int verbose)
+{
+    int ret = 0;
+    dtn_bundle_spec_t bundle_spec;
+    dtn_bundle_payload_t payload;
+
+    memset(&bundle_spec, 0, sizeof(bundle_spec));
+    memset(&payload, 0, sizeof(payload));
+
+    // recv the bpq bundle
+    if (verbose) fprintf(stdout, "blocking waiting for dtn_recv\n");
+    ret = dtn_recv(handle, &bundle_spec, DTN_PAYLOAD_FILE, &payload, timeout);
+    if (ret != DTN_SUCCESS) {
+        fprintf(stderr, "error receiving bundle: %d (%s)\n",
+                    ret, dtn_strerror(dtn_errno(handle)));
+        return ret;
+    } else if (verbose) {
+            fprintf(stdout, "bundle received successfully: id %s,%llu.%llu\n",
+                             bundle_spec.source.uri,
+                             bundle_spec.creation_ts.secs,
+                             bundle_spec.creation_ts.seqno);
+    }
+
+    // handle the payload file
+    ret = handle_file_transfer(bundle_spec, payload, filename, verbose);
+    if (ret != DTN_SUCCESS) {
+        fprintf(stderr, "error handling file transfer: %d\n", ret);
+    } else if (verbose) {
+        fprintf(stdout, "sucessfully handled file transfer\n");        
+    }
+
+    dtn_free_payload(&payload);  
+    return ret;
+}
+
+/*******************************************************************************
+* main:
+*******************************************************************************/
+int
+main(int argc, char** argv)
+{
+    dtn_endpoint_id_t src_eid;
+    dtn_endpoint_id_t dest_eid;
+    char src_eid_name[PATH_MAX];
+    char dest_eid_name[PATH_MAX];
+    char filename[PATH_MAX];
+    char query[PATH_MAX];
+    int query_type = DTN_BPQ_LITERAL;
+    int matching_rule = DTN_BPQ_EXACT;
+    int mode = DTN_BPQ_SEND_RECV;
+    dtn_timeval_t timeout = DTN_TIMEOUT_INF;    //forever
+    dtn_timeval_t bundle_expiry = 3600;         //one hour
+    dtn_reg_id_t regid = DTN_REGID_NONE;
+    dtn_timeval_t reg_expiry = 30;
+    int reg_fail_action = DTN_REG_DEFER;
+    char reg_fail_script[PATH_MAX];
+    dtn_bundle_priority_t priority = COS_NORMAL; 
+    int delivery_options = 0;               
+    int verbose = 0;
+    int err = 0;
+    dtn_handle_t handle;
+
+    parse_options(argc, argv,
+        src_eid_name,
+        dest_eid_name,
+        filename,
+        query,
+        &query_type,
+        &matching_rule,
+        &mode,
+        &timeout,
+        &bundle_expiry,
+        &regid,
+        &reg_expiry,
+        &reg_fail_action,
+        reg_fail_script,
+        &priority,
+        &delivery_options,
+        &verbose);
+
+    validate_options(src_eid_name,
+        dest_eid_name,
+        filename,
+        query,
+        query_type,
+        matching_rule,
+        mode,
+        timeout,
+        bundle_expiry);
+
+    // open the ipc handle
+    if (verbose) printf("opening connection to dtn router...\n");
+    if ((err = dtn_open(&handle)) != DTN_SUCCESS) {
+        fprintf(stderr, "fatal error opening dtn handle: %s\n",
+                dtn_strerror(err));
+        exit(1);
+    }
+    if (verbose) fprintf(stdout, "opened connection to dtn router...\n");
+
+    // parse eids
+    parse_eid(handle, &src_eid, src_eid_name, verbose);
+    parse_eid(handle, &dest_eid, dest_eid_name, verbose);
+    if (verbose) fprintf(stdout, "parsed src_eid: %s\n", src_eid.uri);
+    if (verbose) fprintf(stdout, "parsed dest_eid: %s\n", dest_eid.uri);
+    if (verbose) fprintf(stdout, "regid: %d\n", regid);
+
+    // get dtn registration
+    if (verbose) fprintf(stdout, "registering with dtn...\n");
+    register_with_dtn(handle,
+        &src_eid,
+        &regid,
+        reg_expiry,
+        reg_fail_action,
+        reg_fail_script);
+    if (verbose) fprintf(stdout, "registered with dtn, "
+                                 "regid: %d local eid: %s\n",
+                                 regid, src_eid.uri);
+    
+    //get to work
+    switch (mode)
+    {
+#define TRY(fn, err_msg) \
+    if (fn != DTN_SUCCESS) { \
+        fprintf(stderr, err_msg); \
+        dtn_close(handle); \
+        exit(1); \
+    }
+    case DTN_BPQ_SEND:
+        TRY( send_bpq(handle, regid, &src_eid, &dest_eid, query,
+                      matching_rule, bundle_expiry, priority,
+                      delivery_options, verbose), "error sending query\n" );
+        break;
+
+    case DTN_BPQ_RECV:
+        TRY( recv_bpq(handle, timeout, filename, verbose), 
+             "error receiving query\n" );
+        break;
+
+    case DTN_BPQ_SEND_RECV:
+        TRY( send_bpq(handle, regid, &src_eid, &dest_eid, query,
+                      matching_rule, bundle_expiry, priority,
+                      delivery_options, verbose), "error sending query\n" );
+
+        TRY( recv_bpq(handle, timeout, filename, verbose), 
+             "error receiving query\n" );
+        break;
+
+    default:
+        fprintf(stderr, "invalid mode '%d'\n", mode);
+        dtn_close(handle);
+        exit(1);
+#undef TRY
+    }
+
+    // close the ipc handle
+    if (verbose) fprintf(stdout, "closing connection to dtn router...\n");
+    dtn_close(handle);
+    if (verbose) fprintf(stdout, "closed connection to dtn router...\n");
+
+    return 0;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/apps/dtnquery/dtnquery_pseudo	Tue Apr 26 14:43:43 2011 +0100
@@ -0,0 +1,66 @@
+DTNQUERY
+
+- inputs
+    - src eid (req)
+    - dest eid (req)
+    - resp path (req)
+    - query type
+    - query
+    - matching rule
+    - mode
+    - receive time
+    - bundle expiry time
+    - help
+    - verbose
+
+- outputs
+    - 0: success
+    - 1: error
+
+--------------------------------------------------------------------------------
+
+- main
+    - parse cmd line args
+        - validate cmd line args
+            - if invalid, print usage & exit
+
+    - create registration
+        - validate response
+    - open handle
+        - validate response
+    - switch mode
+        - send
+            - do_send
+        - receive
+            - do_recv
+        - both
+            - do_send
+            - do_recv
+
+    - end switch
+    - close handle
+        - validate response
+- end main
+
+--------------------------------------------------------------------------------
+
+- do_send
+    - create & initialize bundle_spec, empty payload, bundle_id, bpq_ext
+    - parse src / dest eids
+        - assert valid
+    - dtn_send()
+        - assert response
+- end do_send
+
+--------------------------------------------------------------------------------
+
+- do_recv
+    - create & initialize bundle_spec 
+    - dtn_recv()
+        - assert response
+    - handle file transfer
+        - build file name
+            - if exists append .01, .02, ...
+    - notify user that file was received
+- end do_recv
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/apps/dtnrespond/dtnmatch.1	Tue Apr 26 14:43:43 2011 +0100
@@ -0,0 +1,51 @@
+.\"
+.\"    Copyright 2004-2006 Intel 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.
+.\"
+
+.TH dtnmatch 1 "November 18, 2010"
+.LO 1
+.SH NAME
+dtnmatch \- describe dtn query matching 
+.SH CONTENTS
+.RB [
+.RB  matching_rule,
+.RB encoding, 
+.RB query, 
+.RB response_path, 
+.RB expiry 
+.RB ]
+
+.SH DESCRIPTION
+.B dtnmatch
+is a comma seperated value file used by both 
+.B dtnquery
+to describe the query to be sent (response path is
+ignored) and
+.B dtnrespond
+to describe the files to match against (matching rule,
+encoding and query are ignored).
+.PP
+When sending a query a matching file may be specified containing
+the matching rule to use, the encoding, query itself and the bundle
+expiry time.
+.PP
+When 
+.B dtnrespond
+is listening for query bundle it uses the response path to find
+files to match against. The expiry contained in this file is used 
+to set the bundle expiry time.
+
+.SH "SEE ALSO"
+dtnquery(1), dtnrespond(1), dtnd(8).
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/apps/dtnrespond/dtnrespond.1	Tue Apr 26 14:43:43 2011 +0100
@@ -0,0 +1,99 @@
+.\"
+.\"    Copyright 2004-2006 Intel 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.
+.\"
+
+.TH dtnrespond 1 "November 16, 2010"
+.LO 1
+.SH NAME
+dtnrespond \- receive DTN queries 
+.SH SYNOPSIS
+.B dtnrespond
+.RB -l
+.IR eid
+.RB -f
+.IR matching_file
+.RB [ -n
+.IR num ]
+.RB [ -e
+.IR seconds ]
+.RB [ \-hv ]
+
+.SH DESCRIPTION
+.B dtnrespond
+listens for incoming query bundles. To do this,
+it connects to a dtnd daemon using the DTN API.
+It registers to receive bundles with the 
+.IR end‐point
+from the command line. It then blocks, waiting on
+bundles to arrive from dtnd. It terminates on SIGTERM (Ctrl-C)
+or after n query bundles have been received.
+.PP
+On receiving a query bundle
+.B dtnrespond
+will attempt to match the query based on the contents of the 
+.IR matching_file
+and will return the result (if available) to the source eid
+of the query bundle.
+
+.SH RETURN VALUES
+Returns 1 if there was an error processing the arguments.
+Otherwise 
+.B dtnrespond
+returns 0.
+
+.SH OPTIONS
+.TP
+.B \-\^l
+The EID on which to listen for the query bundles. It should be
+an endpoint for which the dtnd is responsible, but that
+is not currently enforced. (Required)
+.TP
+.B \-\^f
+The matching_file is a comma seperated value file that contains 
+information on the files to match against. See 
+.B dtnmatch.
+.TP
+.B \-\^n
+The number of queries to listen for before closing. Default: 0
+indicates
+.B dtnrespond
+should listen forever.
+.TP
+.B \-\^e
+The number of seconds until the response bundle expires from the DTN.
+Default is 3600 (one hour).
+.TP
+.B \-\^h
+Print a help message and exit.
+.TP
+.B \-\^v
+Be verbose. Adds diagnostic information to the standard output.
+
+.SH ENVIRONMENT
+.TP
+.B DTNAPI_ADDR
+If
+.B DTNAPI_ADDR
+is set, 
+.B dtnqueryrecv
+uses it as the hostname to connect to for DTN API operations.
+.TP
+.B DTNAPI_PORT
+If
+.B DTNAPI_PORT
+is set, its value is used as the TCP port to connect to
+for DTN API operations.
+.SH "SEE ALSO"
+dtnquery(1), dtnmatch(1), dtnd(8).
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/apps/dtnrespond/dtnrespond.c	Tue Apr 26 14:43:43 2011 +0100
@@ -0,0 +1,897 @@
+/*
+ *    Copyright 2004-2006 Intel 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include <dtn-config.h>
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <strings.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "dtn_api.h"
+#include "sdnv-c.h"
+
+#define BUFSIZE 16
+#define BLOCKSIZE 8192
+#define COUNTER_MAX_DIGITS 9
+
+#define DTN_BPQ_BLOCK_TYPE 0xC8
+#define DTN_BPQ_BLOCK_FLAGS 0x00
+
+#define DTN_BPQ_KIND_QUERY 0x00
+#define DTN_BPQ_KIND_RESPONSE 0x01
+
+// Find the maximum commandline length
+#ifdef __FreeBSD__
+/* Needed for PATH_MAX, Linux doesn't need it */
+#include <sys/syslimits.h>
+#endif
+
+#ifndef PATH_MAX
+/* A conservative fallback */
+#define PATH_MAX 1024
+#endif
+
+//global variables
+const char* progname;
+
+/*******************************************************************************
+* usage:
+* display cmd line options to user.
+*******************************************************************************/
+int
+usage()
+{
+    fprintf(stderr, "usage: %s -l < local endpoint > -f < matching filename > "
+                    "[opts]\n", progname);
+    fprintf(stderr, "options:\n");
+    fprintf(stderr, " -n  < count > exit after count bundles received\n");
+    fprintf(stderr, " -e  < seconds > bundle expiry time\n");
+    fprintf(stderr, " -i  < regid > existing registration id\n");
+    fprintf(stderr, " -E  < seconds > registration expiry time\n");
+    fprintf(stderr, " -A  < defer | drop | exec > failure action\n");
+    fprintf(stderr, " -S  < script > failure script for exec action\n");
+    fprintf(stderr, " -P  < bulk | normal | expedited | reserved > priority\n");
+    fprintf(stderr, " -D  request end-to-end delivery receipt\n");
+    fprintf(stderr, " -X  request deletion receipt\n");
+    fprintf(stderr, " -F  request bundle forwarding receipts\n");
+    fprintf(stderr, " -R  request bundle reception receipts\n");
+    fprintf(stderr, " -c  request custody transfer\n");
+    fprintf(stderr, " -C  request custody transfer receipts\n");
+    fprintf(stderr, " -1  assert destination endpoint is a singleton\n");
+    fprintf(stderr, " -N  assert destination endpoint is not a singleton\n");
+    fprintf(stderr, " -W  set the do not fragment option\n");
+    fprintf(stderr, " -h  help\n");
+    fprintf(stderr, " -v  verbose\n");
+
+    return 0;
+}
+
+/*******************************************************************************
+* parse options:
+* set internal variables based on cmd line args.
+* calls parse matching file if required.
+* returns success or exits on failure.
+*******************************************************************************/
+int
+parse_options(int argc, char** argv,
+    char * local_eid_name,              // l
+    char * matching_filename,           // f
+    int * count,                        // n
+    dtn_timeval_t * bundle_expiry,      // e
+    dtn_reg_id_t * regid,               // i
+    dtn_timeval_t * reg_expiry,         // E
+    int * reg_fail_action,              // A
+    char * reg_fail_script,             // S
+    dtn_bundle_priority_t * priority,   // P
+    int * delivery_options,             // D X F R c C 1 N W
+    int * verbose)                      // v
+{
+    int c, done = 0;
+
+    progname = argv[0];
+
+    //initialize strings
+    memset(local_eid_name,    0, sizeof(char) * PATH_MAX);
+    memset(matching_filename, 0, sizeof(char) * PATH_MAX);
+    memset(reg_fail_script,   0, sizeof(char) * PATH_MAX);
+
+    while( !done )
+    {
+        c = getopt(argc, argv, "l:f:n:e:i:E:A:S:P:DXFRcC1NWvhH");
+        switch(c)
+        {
+        case 'l':
+            strncpy(local_eid_name, optarg, PATH_MAX);
+            break;
+        case 'f':
+            strncpy(matching_filename, optarg, PATH_MAX);
+            break;
+        case 'n':
+            *count = atoi(optarg);
+            break;
+        case 'e':
+            *bundle_expiry = atoi(optarg);
+            break;
+        case 'i':
+            *regid = atoi(optarg);
+            break;
+        case 'E':
+            *reg_expiry = atoi(optarg);
+            break;
+        case 'A':
+            if (!strcasecmp(optarg, "defer")) {
+                *reg_fail_action = DTN_REG_DEFER;
+
+            } else if (!strcasecmp(optarg, "drop")) {
+                *reg_fail_action = DTN_REG_DROP;
+
+            } else if (!strcasecmp(optarg, "exec")) {
+                *reg_fail_action = DTN_REG_EXEC;
+
+            } else {
+                fprintf(stderr, "invalid failure action '%s'\n", optarg);
+                usage();
+                exit(1);
+            }
+            break;
+        case 'S':
+            strncpy(reg_fail_script, optarg, PATH_MAX);
+            break;
+        case 'P':
+            if (!strcasecmp(optarg, "bulk"))   {
+                *priority = COS_BULK;
+            } else if (!strcasecmp(optarg, "normal")) {
+                *priority = COS_NORMAL;
+            } else if (!strcasecmp(optarg, "expedited")) {
+                *priority = COS_EXPEDITED;
+            } else if (!strcasecmp(optarg, "reserved")) {
+                *priority = COS_RESERVED;
+            } else {
+                fprintf(stderr, "invalid priority value %s\n", optarg);
+                usage();
+                exit(1);
+            }
+            break;
+        case 'D':
+            *delivery_options |= DOPTS_DELIVERY_RCPT;
+            break;
+        case 'X':
+            *delivery_options |= DOPTS_DELETE_RCPT;
+            break;
+        case 'F':
+            *delivery_options |= DOPTS_FORWARD_RCPT;
+            break;
+        case 'R':
+            *delivery_options |= DOPTS_RECEIVE_RCPT;
+            break;
+        case 'c':
+            *delivery_options |= DOPTS_CUSTODY;
+            break;
+        case 'C':
+            *delivery_options |= DOPTS_CUSTODY_RCPT;
+            break;
+        case '1':
+            *delivery_options |= DOPTS_SINGLETON_DEST;
+            break;
+        case 'N':
+            *delivery_options |= DOPTS_MULTINODE_DEST;
+            break;
+        case 'W':
+            *delivery_options |= DOPTS_DO_NOT_FRAGMENT;
+            break;
+        case 'v':
+            *verbose = 1;
+            break;
+        case 'h':
+        case 'H':
+            usage();
+            exit(0);
+        case -1:
+            done = 1;
+            break;
+        default:
+            // getopt already prints error message for unknown option characters
+            usage();
+            exit(1);
+        }             
+    }
+    return 0;
+}
+
+/*******************************************************************************
+* validate options:
+* returns success or exits on failure 
+*******************************************************************************/
+int
+validate_options(const char * local_eid_name, const char * matching_filename)
+{
+#define REQUIRE(test, err_msg) \
+    if(!test) { \
+        fprintf(stderr, err_msg); \
+        usage(); \
+        exit(1); \
+    }
+
+    REQUIRE(strlen(local_eid_name) > 0, "-l <local eid> required\n");
+    REQUIRE(strlen(matching_filename) > 0, "-f <matching filename> required\n");
+    
+    return 0;
+}
+
+/*******************************************************************************
+* register with dtn:
+* 
+*******************************************************************************/
+int
+register_with_dtn(dtn_handle_t handle,
+    dtn_endpoint_id_t * local_eid,
+    const char * local_eid_name,
+    dtn_reg_id_t * regid,
+    dtn_timeval_t reg_expiration,
+    int reg_fail_action,
+    char * reg_fail_script)
+{
+    int call_bind = 0;
+    dtn_reg_info_t reginfo;
+
+    memset(local_eid, 0, sizeof(dtn_endpoint_id_t));
+
+    // if no regid has been given we need to create a new registration
+    if (*regid == DTN_REGID_NONE) {
+        if (local_eid_name[0] == '/') {
+            if (dtn_build_local_eid(handle, local_eid, local_eid_name) != DTN_SUCCESS) {
+                fprintf(stderr, "error building local eid: %s\n",
+                        dtn_strerror(dtn_errno(handle)));
+                dtn_close(handle); 
+                exit(1);
+            }
+        } else {
+            if (dtn_parse_eid_string(local_eid, local_eid_name) != DTN_SUCCESS) {
+                fprintf(stderr, "error parsing eid string: %s\n",
+                        dtn_strerror(dtn_errno(handle)));
+                dtn_close(handle);
+                exit(1);
+            }
+        }
+
+        memset(&reginfo, 0, sizeof(dtn_reg_info_t));
+
+        // create a new registration based on this eid
+        dtn_copy_eid(&reginfo.endpoint, local_eid);
+        reginfo.regid             = *regid;
+        reginfo.expiration        = reg_expiration;
+        reginfo.flags             = reg_fail_action;
+        reginfo.script.script_val = reg_fail_script;
+        reginfo.script.script_len = strlen(reg_fail_script) + 1;
+    }
+
+    // try to see if there is an existing registration that matches
+    // the given endpoint, in which case we'll use that one.
+    if (*regid == DTN_REGID_NONE) {
+        if (dtn_find_registration(handle, local_eid, regid) != DTN_SUCCESS &&
+            dtn_errno(handle) != DTN_ENOTFOUND) {
+            fprintf(stderr, "error finding registration: %s\n",
+                    dtn_strerror(dtn_errno(handle)));
+            dtn_close(handle);
+            exit(1);
+        }
+        call_bind = 1;
+    }
+
+    // if the user didn't give us a registration to use, get a new one
+    if (*regid == DTN_REGID_NONE) {
+        if (dtn_register(handle, &reginfo, regid) != DTN_SUCCESS) {
+            fprintf(stderr, "error registering: %s\n",
+                    dtn_strerror(dtn_errno(handle)));
+            dtn_close(handle);
+            exit(1); 
+        }
+        call_bind = 0;
+    } else {
+        call_bind = 1;
+    }
+
+    if (call_bind) {
+        // bind the current handle to the found registration
+        if (dtn_bind(handle, *regid) != DTN_SUCCESS) {
+            fprintf(stderr, "error binding to registration: %s\n",
+                    dtn_strerror(dtn_errno(handle)));
+            dtn_close(handle);
+            exit(1);
+        }
+    }
+
+    return DTN_SUCCESS;
+}
+
+/*******************************************************************************
+* trim whitespace:
+* first move past any leading whitespace 
+* after the first non-whitespace char bef=gin copying to output
+* finish by setting any trailing whitespace to 0
+* @return 0 on success or -1 if input not completely read
+*******************************************************************************/
+int
+trim_whitespace(const char * in, char * out, int out_len)
+{
+    int i=0, j=0;
+    char space = ' ';
+    char tab = '\t';
+
+    memset(out, 0, out_len);
+
+    // move past any leading whitespace
+    while (i<strlen(in) && in[i] == space ||
+           i<strlen(in) && in[i] == tab)
+        ++i;
+    
+    // body case
+    for ( ; i<strlen(in) && j<out_len-2; ++i){
+            out[j++] = in[i];
+    }
+
+    // remove any trailing whitespace
+    // out[j] now points to the null char that terminates the string
+    j--;
+    while (j >= 0 && out[j] == space ||
+           j >= 0 && out[j] == tab)
+        out[j--] = 0;
+
+    if (i <  strlen(in))
+        return -1;
+    else
+        return 0;
+}   
+
+/*******************************************************************************
+* escape spaces:
+* first move past any leading whitespace 
+* after the first non-whitespace char escape any whitespace
+* @return 0 on success or -1 if input not completely read
+*******************************************************************************/
+int
+escape_spaces(const char * in, char * out, int out_len)
+{
+    int i=0, j=0;
+    char escape = '\\';
+    char space = ' ';
+
+    memset(out, 0, out_len);
+
+    // move past any leading whitespace
+    while (i<strlen(in) && in[i] == space)
+        ++i;
+    
+    // body case
+    for ( ; i<strlen(in) && j<out_len-2; ++i){
+        if (in[i] == space && in[i-1] != escape) {
+            out[j++] = escape;
+            out[j++] = space;
+        } else {
+            out[j++] = in[i];
+        }
+    }
+
+    if (i <  strlen(in))
+        return -1;
+    else
+        return 0;
+}   
+
+/*******************************************************************************
+* find query
+* search all files in 'dir' for a query match
+* test against query len & query val and update found
+* @return 0 or -1 if error
+*******************************************************************************/
+int
+find_query(const char * dir, const char * query, int * found)
+{
+    FILE *file;
+    int i, status;
+    char ls_result[PATH_MAX];
+    char ls_cmd[PATH_MAX];
+    char esc_dir[PATH_MAX];
+
+    *found = 0;
+
+    // trim & escape spaces in dir before calling ls
+    if (escape_spaces(dir, esc_dir, PATH_MAX) != DTN_SUCCESS)
+        return -1;
+    
+    snprintf (ls_cmd, PATH_MAX, "ls %s", esc_dir);
+    file = popen (ls_cmd, "r");
+
+    if (file == NULL)
+        return -1;
+
+    for (i=0; fgets(ls_result, PATH_MAX, file) != NULL; ++i) {
+        if (ls_result[strlen(ls_result)-1] == '\n')
+            ls_result[strlen(ls_result)-1] = 0;
+
+        if (strlen(query) == strlen(ls_result) &&
+            strncmp (query, ls_result, strlen(query)) == 0) {
+            *found = 1;
+            break;
+        }
+    }
+
+    status = pclose(file);
+    if (status == -1) 
+        return -1;
+
+    return 0;
+}
+
+
+/*******************************************************************************
+* match bpq:
+* read in paths to search for query in from 'matching file'
+* for each record in the matching file, extract the response path
+* matching file format: [matching_rule, encoding, query, response_path, expiry]
+*******************************************************************************/
+int
+match_bpq(const dtn_bpq_extension_block_data_t * bpq,
+    const char * matching_filename,
+    char * pathname,
+    int * found)
+{
+    char line[PATH_MAX];
+    char trim_response_path[PATH_MAX];
+    char * response_path;
+    FILE * file;
+    
+    *found = 0;
+
+    if ((file = fopen(matching_filename, "r")) == 0)
+        return -1;
+    
+    memset(line, 0 , PATH_MAX);
+    while (fgets(line, PATH_MAX, file) != NULL) {
+        strtok(line, ",");
+        strtok(NULL, ",");
+        strtok(NULL, ",");
+        response_path = strtok(NULL, ",");  
+//      expiry = strtok(NULL, ",");                 
+
+        // trim whitespace from response path
+        trim_whitespace(response_path, trim_response_path, PATH_MAX);
+
+        if (find_query(trim_response_path, bpq->query.query_val, found) != DTN_SUCCESS)
+            return -1;
+
+        // if found build pathname and stop looking
+        if (*found == 1) {
+            // ensure path ends in slash
+            if (trim_response_path[strlen(trim_response_path)-1] == '/'){
+                snprintf(pathname, PATH_MAX, "%s%s", trim_response_path, bpq->query.query_val);
+            } else {
+                snprintf(pathname, PATH_MAX, "%s/%s", trim_response_path, bpq->query.query_val);
+            }
+
+            break;
+        }
+        memset(line, 0 , PATH_MAX);
+    }
+    fclose (file);
+
+    return 0;
+}
+
+/*******************************************************************************
+* bpq to char array:
+* encode as SDNVs,
+*   BPQ-kind             1-byte
+*   matching rule type   1-byte
+*   BPQ-value-length     SDNV
+*   BPQ-value            n-bytes
+*   number of fragments  SDNV
+*   fragment offsets     SDNV
+*   fragment lengths     SDNV
+*
+* @return The number of bytes or -1 on error
+*******************************************************************************/
+int
+bpq_to_char_array(const dtn_bpq_extension_block_data_t * bpq,
+    char* buf,
+    size_t buf_len)
+{
+    int i=0, j=0, k=0;
+    int encoding_len;
+    char encoding[PATH_MAX];
+
+    // BPQ-kind             1-byte
+    if (i < buf_len)    buf[i++] = (char) bpq->kind;
+
+    // matching rule type   1-byte
+    if (i < buf_len)    buf[i++] = (char) bpq->matching_rule;
+
+    // BPQ-value-length     SDNV
+    if ( (encoding_len = sdnv_encode (bpq->query.query_len, encoding, PATH_MAX)) == -1 )
+        return -1;
+    for (j=0; i<buf_len && j<encoding_len; ++j)
+        buf[i++] = encoding[j];
+
+    // BPQ-value            n-bytes
+    for (j=0; i<buf_len && j<bpq->query.query_len; ++j)
+        buf[i++] = bpq->query.query_val[j];
+
+    // number of fragments  SDNV
+    if ( (encoding_len = sdnv_encode (bpq->fragments.num_frag_returned, encoding, PATH_MAX)) == -1 )
+        return -1;
+    for (j=0; i<buf_len && j<encoding_len; ++j)
+        buf[i++] = encoding[j];
+
+    for (k=0; k<bpq->fragments.num_frag_returned; ++k) {
+
+        // fragment offsets     SDNV
+        if ( (encoding_len = sdnv_encode (bpq->fragments.frag_offsets[k], encoding, PATH_MAX)) == -1 )
+            return -1;
+        for (j=0; i<buf_len && j<encoding_len; ++j)
+            buf[i++] = encoding[j];
+
+        // fragment lengths     SDNV
+        if ( (encoding_len = sdnv_encode (bpq->fragments.frag_lenghts[k], encoding, PATH_MAX)) == -1 )
+            return -1;
+        for (j=0; i<buf_len && j<encoding_len; ++j)
+            buf[i++] = encoding[j];
+    }
+
+
+    return i;
+}
+
+/*******************************************************************************
+* char array to bpq:
+* decode as SDNVs,
+*   BPQ-kind             1-byte
+*   matching rule type   1-byte
+*   BPQ-value-length     SDNV
+*   BPQ-value            n-bytes
+*   number of fragments  SDNV
+*   fragment offsets     SDNV
+*   fragment lengths     SDNV
+*
+* @return DTN_SUCCESS or -1 on error
+*******************************************************************************/
+int
+char_array_to_bpq(const char* buf,
+    size_t buf_len,
+    dtn_bpq_extension_block_data_t * bpq)
+{
+    int i=0, j=0;
+    int decoding_len=0;
+
+    // BPQ-kind             1-byte
+    if (i<buf_len) bpq->kind = (u_int) buf[i++];
+
+    // matching rule type   1-byte
+    if (i<buf_len) bpq->matching_rule = (u_int) buf[i++];
+
+    // BPQ-value-length     SDNV
+    if ( (decoding_len = sdnv_decode (&(buf[i]), buf_len - i, &(bpq->query.query_len))) == -1 )
+        return -1;
+    i += decoding_len;
+
+    // BPQ-value            n-bytes
+    if (i<buf_len) bpq->query.query_val = &(buf[i]);
+    i += bpq->query.query_len;
+
+    // number of fragments  SDNV
+    if ( (decoding_len = sdnv_decode (&(buf[i]), buf_len - i, &(bpq->fragments.num_frag_returned))) == -1 )
+        return -1;
+    i += decoding_len;
+
+    for (j=0; i<buf_len && j<bpq->fragments.num_frag_returned; ++j) {
+
+        // fragment offsets     SDNV
+        if ( (decoding_len = sdnv_decode (&(buf[i]), buf_len - i, &(bpq->fragments.frag_offsets[j]))) == -1 )
+            return -1;
+        i += decoding_len;
+
+        // fragment lengths     SDNV
+        if ( (decoding_len = sdnv_decode (&(buf[i]), buf_len - i, &(bpq->fragments.frag_lenghts[j]))) == -1 )
+            return -1;
+        i += decoding_len;
+    }
+
+    if (i != buf_len)
+        return -1;
+
+    return DTN_SUCCESS;
+}
+
+/*******************************************************************************
+* send response bpq:
+* build a response bundle containing the queried object file as the payload
+* attach a BPQ extension describing the query & object
+* and send to the source of the query
+* @return 0 if successful or -1 if error
+*******************************************************************************/
+int
+send_response_bpq(dtn_handle_t * handle,
+    dtn_reg_id_t regid,
+    dtn_bundle_spec_t * query_bundle_spec,
+    dtn_bpq_extension_block_data_t * query_bpq_block_data,
+    const char * pathname,
+    int bundle_expiry,
+    dtn_bundle_priority_t  priority,
+    int delivery_options,
+    int verbose)
+{
+    int  ret = 0;
+    char buf [PATH_MAX];
+    size_t buf_len = 0;
+    dtn_bundle_id_t                 response_bundle_id;
+    dtn_bundle_spec_t               response_bundle_spec;
+    dtn_extension_block_t           response_bpq_block;
+    dtn_bpq_extension_block_data_t  response_bpq_block_data;
+    dtn_bundle_payload_t            response_payload;
+    u_int offsets[1];
+    u_int lengths[1];
+
+    memset(buf,                      0, PATH_MAX);
+    memset(&response_bundle_spec,    0, sizeof(dtn_bundle_spec_t));
+    memset(&response_bpq_block,      0, sizeof(dtn_extension_block_t));
+    memset(&response_bpq_block_data, 0, sizeof(dtn_bpq_extension_block_data_t));
+    memset(&response_payload,        0, sizeof(dtn_bundle_payload_t));
+
+    // set the payload
+    dtn_set_payload(&response_payload, DTN_PAYLOAD_FILE, pathname, strlen(pathname)); 
+
+    // set the bpq block data
+    response_bpq_block_data.kind = DTN_BPQ_KIND_RESPONSE;
+    response_bpq_block_data.matching_rule = query_bpq_block_data->matching_rule;
+    response_bpq_block_data.query.query_len = query_bpq_block_data->query.query_len;
+    response_bpq_block_data.query.query_val = query_bpq_block_data->query.query_val;
+
+    offsets[0] = 0;
+    lengths[0] = 100;   //todo: add payload length here
+
+    response_bpq_block_data.fragments.num_frag_returned = 1;
+    response_bpq_block_data.fragments.frag_offsets = offsets;
+    response_bpq_block_data.fragments.frag_lenghts = lengths; 
+
+    if ( (buf_len = bpq_to_char_array(&response_bpq_block_data, buf, PATH_MAX)) == -1 ) {
+        fprintf (stderr, "error encoding bpq: %d", buf_len);
+        return -1;
+    }
+
+    // set the bpq block
+    response_bpq_block.type = DTN_BPQ_BLOCK_TYPE;
+    response_bpq_block.flags = DTN_BPQ_BLOCK_FLAGS;
+    response_bpq_block.data.data_len = buf_len;
+    response_bpq_block.data.data_val = buf;
+
+    // copy dest src
+    dtn_copy_eid(&response_bundle_spec.dest,    &(query_bundle_spec->source));
+    dtn_copy_eid(&response_bundle_spec.source,  &(query_bundle_spec->dest));
+    dtn_copy_eid(&response_bundle_spec.replyto, &(query_bundle_spec->dest));
+
+    // set the bundle spec dtn options
+    response_bundle_spec.expiration = bundle_expiry;
+    response_bundle_spec.dopts = delivery_options;
+    response_bundle_spec.priority = priority;
+
+    // set the bundle extension
+    response_bundle_spec.blocks.blocks_len = 1;
+    response_bundle_spec.blocks.blocks_val = &response_bpq_block;
+
+
+    // send the bundle, bpq extension and empty payload
+    ret = dtn_send(*handle, regid, &response_bundle_spec, &response_payload, &response_bundle_id);
+    if (ret != DTN_SUCCESS) {
+        fprintf(stderr, "error sending response bundle: %d (%s)\n",
+                    ret, dtn_strerror(dtn_errno(handle)));
+    } else if (verbose) {
+        fprintf(stdout, "bundle sent successfully: id %s,%llu.%llu\n",
+                    response_bundle_id.source.uri,
+                    response_bundle_id.creation_ts.secs,
+                    response_bundle_id.creation_ts.seqno);
+    }
+    return ret;
+}
+
+/*******************************************************************************
+* receive bpq:
+* listen for incoming bundles, 
+* upon receipt of a new bundle ckeck for bpq expension block. 
+* Attempt to match the query in the bpq.
+* If a match is found, send a response containing the queied object to the 
+* source of the query.
+*******************************************************************************/
+int
+receive_bpq(dtn_handle_t * handle,
+    dtn_reg_id_t regid,
+    const char * matching_filename,
+    int  count,
+    int bundle_expiry,
+    dtn_bundle_priority_t  priority,
+    int delivery_options,
+    int verbose)
+{
+    int i, j, num_blocks, found, ret = 0;
+    char pathname[PATH_MAX];
+    dtn_bundle_spec_t              bundle_spec;
+    dtn_extension_block_t          * bpq_blocks;
+    dtn_bpq_extension_block_data_t bpq_block_data;
+    dtn_bundle_payload_t           payload;
+
+    // start listening for bpq bundles
+    for (i = 0; count == -1 || i < count; ++i) {
+        found = 0;
+        memset(&bundle_spec,    0, sizeof(dtn_bundle_spec_t));
+        memset(&bpq_block_data, 0, sizeof(dtn_bpq_extension_block_data_t));
+        memset(&payload,        0, sizeof(dtn_bundle_payload_t));
+        memset(pathname,        0, PATH_MAX);
+        pathname[0] = '\0';
+
+        if (verbose) fprintf(stdout, "blocking waiting for dtn_recv\n");
+        ret = dtn_recv(*handle,
+                       &bundle_spec,
+                       DTN_PAYLOAD_FILE,
+                       &payload,
+                       DTN_TIMEOUT_INF);
+        if (ret != DTN_SUCCESS) {
+            fprintf(stderr, "error receiving bundle: %d (%s)\n",
+                    ret, dtn_strerror(dtn_errno(*handle)));
+            return ret;
+        } else if (verbose) {
+            fprintf(stdout, "bundle %d received successfully: id %s,%llu.%llu\n",
+                             i,
+                             bundle_spec.source.uri,
+                             bundle_spec.creation_ts.secs,
+                             bundle_spec.creation_ts.seqno);
+        }
+
+        // extract the bpq
+        num_blocks = bundle_spec.blocks.blocks_len;
+        bpq_blocks = bundle_spec.blocks.blocks_val;    
+
+        for (j = 0; j < num_blocks; ++j) {
+            if (bpq_blocks[j].type == DTN_BPQ_BLOCK_TYPE) {
+
+                if (verbose) fprintf(stdout, "bundle %d contains a "
+                                             "BPQ extension block\n", i);
+
+                ret = char_array_to_bpq(bpq_blocks[j].data.data_val, 
+                                        bpq_blocks[j].data.data_len, 
+                                        &bpq_block_data);
+                if (ret != DTN_SUCCESS) {
+                    fprintf(stderr, "error decoding query bundle: %d\n", ret);
+                    return ret;
+                }
+
+                match_bpq(&bpq_block_data, matching_filename, pathname, &found);
+                break;
+            }                
+        }
+
+        // if found respond and continue listening
+        if (found) {
+            if (verbose) fprintf(stdout, "BPQ match found for query: %s\n",
+                                         bpq_block_data.query.query_val);
+
+            ret = send_response_bpq(handle,
+                                    regid,
+                                    &bundle_spec,
+                                    &bpq_block_data,
+                                    pathname,
+                                    bundle_expiry,
+                                    priority,
+                                    delivery_options,
+                                    verbose);
+            if (ret != DTN_SUCCESS) {
+                fprintf(stderr, "error sending response bundle: %d (%s)\n",
+                        ret, dtn_strerror(dtn_errno(*handle)));
+                return ret;
+            }
+        }
+        dtn_free_payload(&payload);
+    }
+
+    return ret;
+}
+
+/*******************************************************************************
+* main:
+*
+*******************************************************************************/
+int
+main(int argc, char** argv)
+{
+    dtn_endpoint_id_t local_eid;
+    char local_eid_name[PATH_MAX];
+    char matching_filename[PATH_MAX];
+    int count = -1;                                     //forever
+    dtn_timeval_t bundle_expiry = 3600;                 //one hour
+    dtn_reg_id_t regid = DTN_REGID_NONE;
+    dtn_timeval_t reg_expiry = 30;
+    int reg_fail_action = DTN_REG_DEFER;
+    char reg_fail_script[PATH_MAX]; 
+    dtn_bundle_priority_t priority = COS_NORMAL;
+    int delivery_options = 0;
+    int verbose = 0;
+    int err = 0;
+    dtn_handle_t handle;
+
+    parse_options(argc, argv,
+        local_eid_name,
+        matching_filename,
+        &count,
+        &bundle_expiry,
+        &regid,
+        &reg_expiry,
+        &reg_fail_action,
+        reg_fail_script,
+        &priority,
+        &delivery_options,
+        &verbose);
+
+    validate_options(local_eid_name, matching_filename);
+
+    // open the ipc handle
+    if (verbose) fprintf(stdout, "opening connection to dtn router...\n");
+    if ((err = dtn_open(&handle)) != DTN_SUCCESS) {
+        fprintf(stderr, "fatal error opening dtn handle: %s\n",
+                dtn_strerror(err));
+        exit(1);
+    }
+    if (verbose) fprintf(stdout, "opened connection to dtn router...\n");
+
+    // get dtn registration
+    if (verbose) fprintf(stdout, "registering with dtn...\n");
+    register_with_dtn(handle,
+        &local_eid,
+        local_eid_name,
+        &regid,
+        reg_expiry,
+        reg_fail_action,
+        reg_fail_script);
+    if (verbose) fprintf(stdout, "registered with dtn, "
+                                 "regid: %d local eid: %s\n",
+                                 regid, local_eid.uri);
+
+    // get to work
+    // this fn will likely never exit so the handle won't be closed...
+    receive_bpq(&handle,
+        regid,
+        matching_filename,
+        count,
+        bundle_expiry,
+        priority,
+        delivery_options,
+        verbose);
+
+// UNREACHABLE CODE if count = -1 //////////////////////////////////////////////
+
+    // close the ipc handle
+    if (verbose) fprintf(stdout, "closing connection to dtn router...\n");
+    dtn_close(handle);
+    if (verbose) fprintf(stdout, "closed connection to dtn router...\n");
+
+    return 0;
+
+// UNREACHABLE CODE if count = -1 //////////////////////////////////////////////
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/apps/dtnrespond/dtnrespond_pseudo	Tue Apr 26 14:43:43 2011 +0100
@@ -0,0 +1,84 @@
+DTNRESPOND
+
+- inputs
+    - listen eid (req)
+    - config file (req)
+    - number of bundles to receive
+    - bundle expiry time
+    - help
+    - verbose
+
+- outputs
+    - 0: success
+    - 1: error
+
+--------------------------------------------------------------------------------
+
+- main
+    - parse cmd line args
+        - validate cmd line args
+            - if invalid, print usage & exit
+
+    - parse config file - format: {encoding, pathname}
+        - validate config file
+    
+    - create matcher
+        - set encoding from config
+        - set path from config
+ 
+    - create registration
+        - validate response
+    - open handle
+        - validate response
+
+    - do_recv
+        - validate response
+
+    - close handle
+        - validate response
+- end main
+
+--------------------------------------------------------------------------------
+
+- do_recv
+    - create bundle_spec
+
+    - loop forever or until received enough bundles
+        - (re)initialize bundle_spec
+
+        - dtn_recv()
+            - assert response
+
+        - extract bpq matching rule
+        - extract bpq query
+
+        - match query using matcher
+        - if found
+            - do_send_response
+        - else
+            - do nothing for now
+- end do_recv
+
+--------------------------------------------------------------------------------
+
+- do_send_response
+    - create & initialize bundle_spec, response payload, bundle_id, bpq_ext
+    - set src = current eid
+    - set dest = bpq source eid
+    - set payload = file at 'found location'
+
+    - dtn_send()
+        - assert response
+- end do_send
+
+--------------------------------------------------------------------------------
+
+- match
+    - create list of files in 'path'
+    - match based on matching rule
+    - if found
+        - update location
+        - return 0
+    - else
+        - return 1
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/apps/dtnrespond/matching_file.csv	Tue Apr 26 14:43:43 2011 +0100
@@ -0,0 +1,1 @@
+matching_rule,encoding,query,/home/aidan/Desktop,300
--- a/daemon/dtn.conf	Thu Apr 21 14:57:45 2011 +0100
+++ b/daemon/dtn.conf	Tue Apr 26 14:43:43 2011 +0100
@@ -32,7 +32,7 @@
 # (this interprets user commands)
 #
 console set addr 127.0.0.1
-console set port 5050
+console set port 5051
 
 #
 # console set prompt <prompt>
@@ -174,6 +174,38 @@
 #
 # e.g. route add dtn://host.domain/* tcp0
 
+set localhost [lindex [split [info hostname] .] 0]
+switch -exact $localhost {
+    stewie {
+        route local_eid                 dtn://stewie.dtn
+
+        link  add l-audrey              134.226.36.152:4557 ALWAYSON tcp
+        route add dtn://audrey.dtn      l-audrey
+
+        link add l-basil                134.226.36.138:4557 ALWAYSON tcp
+        route add dtn://basil.dtn       l-basil
+
+        route add dtn://terry.dtn/*     dtn://audrey.dtn
+    }
+    audrey {
+        route local_eid                 dtn://audrey.dtn
+
+        route add dtn://terry.dtn/*     dtn://terry.dtn
+
+        route add dtn://stewie.dtn/*    dtn://stewie.dtn
+    }
+    terry {
+        route local_eid                 dtn://terry.dtn
+
+        link  add l-audrey              134.226.36.152:4557 ALWAYSON tcp
+        route add dtn://audrey.dtn      l-audrey
+        route add dtn://audrey.dtn/*    dtn://audrey.dtn
+
+        route add dtn://stewie.dtn/*    dtn://audrey.dtn
+    }
+}
+
+
 ########################################
 #
 # Service discovery
--- a/servlib/bundling/BundleProtocol.h	Thu Apr 21 14:57:45 2011 +0100
+++ b/servlib/bundling/BundleProtocol.h	Tue Apr 26 14:43:43 2011 +0100
@@ -184,6 +184,7 @@
         SESSION_BLOCK               = 0x009, ///< NOT IN SPEC YET
         SEQUENCE_ID_BLOCK           = 0x010, ///< NOT IN SPEC YET
         OBSOLETES_ID_BLOCK          = 0x011, ///< NOT IN SPEC YET
+        QUERY_EXTENSION_BLOCK       = 0x0C8, ///< NOT IN SPEC YET
         API_EXTENSION_BLOCK         = 0x100, ///< INTERNAL ONLY -- NOT IN SPEC
         UNKNOWN_BLOCK               = 0x101, ///< INTERNAL ONLY -- NOT IN SPEC
     } bundle_block_type_t;