# HG changeset patch # User aidan # Date 1303825423 -3600 # Node ID 44c5e3fa6d30d52844cf969839cb3dda78cc05c0 # Parent 2b3e5ec03512389ab2af7dd4657f86b211c83af5 added in existing BPQ code diff -r 2b3e5ec03512 -r 44c5e3fa6d30 applib/dtn_types.h --- a/applib/dtn_types.h Thu Apr 21 14:57:45 2011 +0100 +++ b/applib/dtn_types.h Tue Apr 26 14:43:43 2011 +0100 @@ -248,6 +248,21 @@ }; typedef struct dtn_extension_block_t dtn_extension_block_t; +struct dtn_bpq_extension_block_data_t { + u_int kind; + u_int matching_rule; + struct { + u_int query_len; + char* query_val; + } query; + struct { + u_int num_frag_returned; + u_int *frag_offsets; + u_int *frag_lenghts; + } fragments; +}; +typedef struct dtn_bpq_extension_block_data_t dtn_bpq_extension_block_data_t; + /** * A Sequence ID is a vector of (EID, counter) values in the following * text format: @@ -409,6 +424,7 @@ extern bool_t xdr_dtn_bundle_delivery_opts_t (XDR *, dtn_bundle_delivery_opts_t*); extern bool_t xdr_dtn_extension_block_flags_t (XDR *, dtn_extension_block_flags_t*); extern bool_t xdr_dtn_extension_block_t (XDR *, dtn_extension_block_t*); +extern bool_t xdr_dtn_bpq_extension_block_data_t (XDR *, dtn_bpq_extension_block_data_t*); extern bool_t xdr_dtn_sequence_id_t (XDR *, dtn_sequence_id_t*); extern bool_t xdr_dtn_bundle_spec_t (XDR *, dtn_bundle_spec_t*); extern bool_t xdr_dtn_bundle_id_t (XDR *, dtn_bundle_id_t*); @@ -430,6 +446,7 @@ extern bool_t xdr_dtn_bundle_delivery_opts_t (); extern bool_t xdr_dtn_extension_block_flags_t (); extern bool_t xdr_dtn_extension_block_t (); +extern bool_t xdr_dtn_bpq_extension_block_data_t (); extern bool_t xdr_dtn_sequence_id_t (); extern bool_t xdr_dtn_bundle_spec_t (); extern bool_t xdr_dtn_bundle_id_t (); diff -r 2b3e5ec03512 -r 44c5e3fa6d30 apps/Makefile --- 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) diff -r 2b3e5ec03512 -r 44c5e3fa6d30 apps/dtnquery/.state --- /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 diff -r 2b3e5ec03512 -r 44c5e3fa6d30 apps/dtnquery/dtnquery.1 --- /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 . + +.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). diff -r 2b3e5ec03512 -r 44c5e3fa6d30 apps/dtnquery/dtnquery.c --- /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 +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 +#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 required\n"); + REQUIRE(strlen(dest_eid_name) > 0, "-d required\n"); + REQUIRE(strlen(query) > 0, "-q required\n"); + break; + + case DTN_BPQ_RECV: //requires src, filename + REQUIRE(strlen(src_eid_name) > 0, "-s required\n"); + REQUIRE(strlen(filename) > 0, "-f required\n"); + break; + + case DTN_BPQ_SEND_RECV: //requires src, dest, query, filename + REQUIRE(strlen(src_eid_name) > 0, "-s required\n"); + REQUIRE(strlen(dest_eid_name) > 0, "-d required\n"); + REQUIRE(strlen(filename) > 0, "-f required\n"); + REQUIRE(strlen(query) > 0, "-q required\n"); + break; + default: + REQUIRE(mode == DTN_BPQ_SEND || + mode == DTN_BPQ_RECV || + mode == DTN_BPQ_SEND_RECV, "-m invalid mode\n") + } + REQUIRE(query_type == DTN_BPQ_LITERAL || + query_type == DTN_BPQ_BASE64 || + query_type == DTN_BPQ_FILE, "-t invalid type\n"); + + REQUIRE(matching_rule == DTN_BPQ_EXACT, "-r invalid rule\n"); +fprintf(stdout, "timeout: %d, REQUIRE(timeout >= -1): %d\n", timeout, timeout >= -1); +// REQUIRE(timeout >= -1, "-o must ba a positive integer or -1: forever\n"); + REQUIRE(bundle_expiry > 0, "-e 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; iquery.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; ifragments.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; ifragments.frag_lenghts[k], encoding, PATH_MAX)) == -1 ) + return -1; + for (j=0; ikind = (u_int) buf[i++]; + + // matching rule type 1-byte + if (imatching_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 (iquery.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; ifragments.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; +} + diff -r 2b3e5ec03512 -r 44c5e3fa6d30 apps/dtnquery/dtnquery_pseudo --- /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 + diff -r 2b3e5ec03512 -r 44c5e3fa6d30 apps/dtnrespond/dtnmatch.1 --- /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). diff -r 2b3e5ec03512 -r 44c5e3fa6d30 apps/dtnrespond/dtnrespond.1 --- /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). diff -r 2b3e5ec03512 -r 44c5e3fa6d30 apps/dtnrespond/dtnrespond.c --- /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 +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 +#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 required\n"); + REQUIRE(strlen(matching_filename) > 0, "-f 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= 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 (iquery.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; iquery.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; ifragments.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; ifragments.frag_lenghts[k], encoding, PATH_MAX)) == -1 ) + return -1; + for (j=0; ikind = (u_int) buf[i++]; + + // matching rule type 1-byte + if (imatching_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 (iquery.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; ifragments.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 ////////////////////////////////////////////// +} + diff -r 2b3e5ec03512 -r 44c5e3fa6d30 apps/dtnrespond/dtnrespond_pseudo --- /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 + diff -r 2b3e5ec03512 -r 44c5e3fa6d30 apps/dtnrespond/matching_file.csv --- /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 diff -r 2b3e5ec03512 -r 44c5e3fa6d30 daemon/dtn.conf --- 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 @@ -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 diff -r 2b3e5ec03512 -r 44c5e3fa6d30 servlib/bundling/BundleProtocol.h --- 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;