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