--- 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(®info, 0, sizeof(dtn_reg_info_t));
+
+ // create a new registration based on this eid
+ dtn_copy_eid(®info.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, ®info, 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,
+ ®id,
+ ®_expiry,
+ ®_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,
+ ®id,
+ 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(®info, 0, sizeof(dtn_reg_info_t));
+
+ // create a new registration based on this eid
+ dtn_copy_eid(®info.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, ®info, 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,
+ ®id,
+ ®_expiry,
+ ®_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,
+ ®id,
+ 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;