--- /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;
+}
+