|
1 /* |
|
2 * Copyright 2004-2006 Intel Corporation |
|
3 * |
|
4 * Licensed under the Apache License, Version 2.0 (the "License"); |
|
5 * you may not use this file except in compliance with the License. |
|
6 * You may obtain a copy of the License at |
|
7 * |
|
8 * http://www.apache.org/licenses/LICENSE-2.0 |
|
9 * |
|
10 * Unless required by applicable law or agreed to in writing, software |
|
11 * distributed under the License is distributed on an "AS IS" BASIS, |
|
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
13 * See the License for the specific language governing permissions and |
|
14 * limitations under the License. |
|
15 */ |
|
16 |
|
17 #ifdef HAVE_CONFIG_H |
|
18 # include <dtn-config.h> |
|
19 #endif |
|
20 |
|
21 #include <stdio.h> |
|
22 #include <unistd.h> |
|
23 #include <errno.h> |
|
24 #include <strings.h> |
|
25 #include <string.h> |
|
26 #include <stdlib.h> |
|
27 #include <sys/time.h> |
|
28 #include <sys/stat.h> |
|
29 #include <fcntl.h> |
|
30 #include "dtn_api.h" |
|
31 #include "sdnv-c.h" |
|
32 |
|
33 #define BUFSIZE 16 |
|
34 #define BLOCKSIZE 8192 |
|
35 #define COUNTER_MAX_DIGITS 9 |
|
36 |
|
37 // todo: move these out to a header file |
|
38 #define DTN_BPQ_LITERAL 1 |
|
39 #define DTN_BPQ_BASE64 2 |
|
40 #define DTN_BPQ_FILE 3 |
|
41 |
|
42 #define DTN_BPQ_EXACT 1 |
|
43 |
|
44 #define DTN_BPQ_SEND 1 |
|
45 #define DTN_BPQ_RECV 2 |
|
46 #define DTN_BPQ_SEND_RECV 3 |
|
47 |
|
48 #define DTN_BPQ_BLOCK_TYPE 0xC8 |
|
49 #define DTN_BPQ_BLOCK_FLAGS 0x00 |
|
50 |
|
51 #define DTN_BPQ_KIND_QUERY 0x00 |
|
52 #define DTN_BPQ_KIND_RESPONSE 0x01 |
|
53 |
|
54 // Find the maximum commandline length |
|
55 #ifdef __FreeBSD__ |
|
56 /* Needed for PATH_MAX, Linux doesn't need it */ |
|
57 #include <sys/syslimits.h> |
|
58 #endif |
|
59 |
|
60 #ifndef PATH_MAX |
|
61 /* A conservative fallback */ |
|
62 #define PATH_MAX 1024 |
|
63 #endif |
|
64 |
|
65 //global variables |
|
66 const char* progname; |
|
67 |
|
68 /******************************************************************************* |
|
69 * usage: |
|
70 * display cmd line options to user. |
|
71 *******************************************************************************/ |
|
72 int |
|
73 usage() |
|
74 { |
|
75 fprintf(stderr, "usage: %s -s < src endpoint > -d < dest endpoint > " |
|
76 "[opts]\n", progname); |
|
77 fprintf(stderr, "options:\n"); |
|
78 fprintf(stderr, " -f < filename > response filename\n"); |
|
79 fprintf(stderr, " -q < query > query or matching file\n"); |
|
80 fprintf(stderr, " -t < literal | base64 | file > query type\n"); |
|
81 fprintf(stderr, " -r < exact > matching rule\n"); |
|
82 fprintf(stderr, " -m < send | receive | both > mode\n"); |
|
83 fprintf(stderr, " -o < seconds > timeout\n"); |
|
84 fprintf(stderr, " -e < seconds > bundle expiry time\n"); |
|
85 fprintf(stderr, " -i < regid > existing registration id\n"); |
|
86 fprintf(stderr, " -E < seconds > registration expiry time\n"); |
|
87 fprintf(stderr, " -A < defer | drop | exec > failure action\n"); |
|
88 fprintf(stderr, " -S < script > failure script for exec action\n"); |
|
89 fprintf(stderr, " -P < bulk | normal | expedited | reserved > priority\n"); |
|
90 fprintf(stderr, " -D request end-to-end delivery receipt\n"); |
|
91 fprintf(stderr, " -X request deletion receipt\n"); |
|
92 fprintf(stderr, " -F request bundle forwarding receipts\n"); |
|
93 fprintf(stderr, " -R request bundle reception receipts\n"); |
|
94 fprintf(stderr, " -c request custody transfer\n"); |
|
95 fprintf(stderr, " -C request custody transfer receipts\n"); |
|
96 fprintf(stderr, " -1 assert destination endpoint is a singleton\n"); |
|
97 fprintf(stderr, " -N assert destination endpoint is not a singleton\n"); |
|
98 fprintf(stderr, " -W set the do not fragment option\n"); |
|
99 fprintf(stderr, " -h help\n"); |
|
100 fprintf(stderr, " -v verbose\n"); |
|
101 |
|
102 return 0; |
|
103 } |
|
104 |
|
105 /******************************************************************************* |
|
106 * parse matching file |
|
107 * if matching file is passed in rather than cml line literal query, |
|
108 * extract relevant information by reading the csv file and splitting |
|
109 * it into tokens. |
|
110 * |
|
111 * Matching File Format: |
|
112 * [ matching_rule, encoding, query, response_path, expiry ] |
|
113 *******************************************************************************/ |
|
114 int |
|
115 parse_matching_file(const char * filename, |
|
116 int * matching_rule, |
|
117 int * query_type, |
|
118 char * query, |
|
119 dtn_timeval_t * bundle_expiry, |
|
120 int verbose) |
|
121 { |
|
122 FILE * file; |
|
123 char * token; |
|
124 char line[PATH_MAX]; |
|
125 |
|
126 memset(line, 0, sizeof(char) * PATH_MAX); |
|
127 |
|
128 if (verbose) fprintf(stdout, "openning matching file...\n"); |
|
129 file = fopen(filename, "r"); |
|
130 if (file == NULL) { |
|
131 fprintf(stderr, "Error opening file: %s", filename); |
|
132 return 1; |
|
133 } |
|
134 |
|
135 fgets(line, PATH_MAX, file); |
|
136 if (verbose) fprintf(stdout, "matching file %s contains [ %s ]\n", filename, line); |
|
137 |
|
138 fclose(file); |
|
139 if (verbose) fprintf(stdout, "closed matching file...\n"); |
|
140 |
|
141 //matching rule |
|
142 token = strtok (line, ","); |
|
143 if (token == NULL) return 1; |
|
144 *matching_rule = atoi(token); |
|
145 |
|
146 //encoding |
|
147 token = strtok (NULL, ","); |
|
148 if (token == NULL) return 1; |
|
149 *query_type = atoi(token); |
|
150 |
|
151 //query |
|
152 token = strtok (NULL, ","); |
|
153 if (token == NULL) return 1; |
|
154 strncpy(query, token, PATH_MAX); |
|
155 |
|
156 //response path - to be ignored |
|
157 token = strtok (NULL, ","); |
|
158 if (token == NULL) return 1; |
|
159 |
|
160 //expiry |
|
161 token = strtok (NULL, ","); |
|
162 if (token == NULL) return 1; |
|
163 *bundle_expiry = atoi(token); |
|
164 |
|
165 //ensure there are no more tokens |
|
166 token = strtok (NULL, ","); |
|
167 if (token != NULL) return 1; |
|
168 |
|
169 return 0; |
|
170 } |
|
171 |
|
172 /******************************************************************************* |
|
173 * parse options: |
|
174 * set internal variables based on cmd line args. |
|
175 * calls parse matching file if required. |
|
176 * returns success or exits on failure. |
|
177 *******************************************************************************/ |
|
178 int |
|
179 parse_options(int argc, char** argv, |
|
180 char * src_eid_name, // s |
|
181 char * dest_eid_name, // d |
|
182 char * filename, // f |
|
183 char * query, // q |
|
184 int * query_type, // t |
|
185 int * matching_rule, // r |
|
186 int * mode, // m |
|
187 dtn_timeval_t * timeout, // o |
|
188 dtn_timeval_t * bundle_expiry, // e |
|
189 dtn_reg_id_t * regid, // i |
|
190 dtn_timeval_t * reg_expiry, // E |
|
191 int * reg_fail_action, // A |
|
192 char * reg_fail_script, // S |
|
193 dtn_bundle_priority_t * priority, // P |
|
194 int * delivery_options, // D X F R c C 1 N W |
|
195 int * verbose) // v |
|
196 { |
|
197 int c, done = 0; |
|
198 char matching_file[PATH_MAX]; |
|
199 |
|
200 progname = argv[0]; |
|
201 |
|
202 //initialize strings |
|
203 memset(src_eid_name, 0, sizeof(char) * PATH_MAX); |
|
204 memset(dest_eid_name, 0, sizeof(char) * PATH_MAX); |
|
205 memset(filename, 0, sizeof(char) * PATH_MAX); |
|
206 memset(query, 0, sizeof(char) * PATH_MAX); |
|
207 memset(matching_file, 0, sizeof(char) * PATH_MAX); |
|
208 memset(reg_fail_script, 0, sizeof(char) * PATH_MAX); |
|
209 |
|
210 while( !done ) |
|
211 { |
|
212 c = getopt(argc, argv, "s:d:f:q:t:r:m:o:e:i:E:A:S:P:DXFcC1NWvhH"); |
|
213 switch(c) |
|
214 { |
|
215 case 's': |
|
216 strncpy(src_eid_name, optarg, PATH_MAX); |
|
217 break; |
|
218 case 'd': |
|
219 strncpy(dest_eid_name, optarg, PATH_MAX); |
|
220 break; |
|
221 case 'f': |
|
222 strncpy(filename, optarg, PATH_MAX); |
|
223 break; |
|
224 case 'q': |
|
225 strncpy(query, optarg, PATH_MAX); |
|
226 break; |
|
227 case 't': |
|
228 if (!strcasecmp(optarg, "literal")) { |
|
229 *query_type = DTN_BPQ_LITERAL; |
|
230 break; |
|
231 } else if (!strcasecmp(optarg, "base64")) { |
|
232 *query_type = DTN_BPQ_BASE64; |
|
233 break; |
|
234 } else if (!strcasecmp(optarg, "file")) { |
|
235 *query_type = DTN_BPQ_FILE; |
|
236 break; |
|
237 } else { |
|
238 fprintf(stderr, "invalid query type '%s'\n", optarg); |
|
239 usage(); |
|
240 exit(1); |
|
241 } |
|
242 case 'r': |
|
243 if (!strcasecmp(optarg, "exact")) { |
|
244 *matching_rule = DTN_BPQ_EXACT; |
|
245 break; |
|
246 } else { |
|
247 fprintf(stderr, "invalid query type '%s'\n", optarg); |
|
248 usage(); |
|
249 exit(1); |
|
250 } |
|
251 case 'm': |
|
252 if (!strcasecmp(optarg, "send")) { |
|
253 *mode = DTN_BPQ_SEND; |
|
254 break; |
|
255 } else if (!strcasecmp(optarg, "receive")) { |
|
256 *mode = DTN_BPQ_RECV; |
|
257 break; |
|
258 } else if (!strcasecmp(optarg, "both")) { |
|
259 *mode = DTN_BPQ_SEND_RECV; |
|
260 break; |
|
261 } else { |
|
262 fprintf(stderr, "invalid mode '%s'\n", optarg); |
|
263 usage(); |
|
264 exit(1); |
|
265 } |
|
266 case 'o': |
|
267 *timeout = atoi(optarg); |
|
268 break; |
|
269 case 'e': |
|
270 *bundle_expiry = atoi(optarg); |
|
271 break; |
|
272 case 'i': |
|
273 *regid = atoi(optarg); |
|
274 break; |
|
275 case 'E': |
|
276 *reg_expiry = atoi(optarg); |
|
277 break; |
|
278 case 'A': |
|
279 if (!strcasecmp(optarg, "defer")) { |
|
280 *reg_fail_action = DTN_REG_DEFER; |
|
281 |
|
282 } else if (!strcasecmp(optarg, "drop")) { |
|
283 *reg_fail_action = DTN_REG_DROP; |
|
284 |
|
285 } else if (!strcasecmp(optarg, "exec")) { |
|
286 *reg_fail_action = DTN_REG_EXEC; |
|
287 |
|
288 } else { |
|
289 fprintf(stderr, "invalid failure action '%s'\n", optarg); |
|
290 usage(); |
|
291 exit(1); |
|
292 } |
|
293 break; |
|
294 case 'S': |
|
295 strncpy(reg_fail_script, optarg, PATH_MAX); |
|
296 break; |
|
297 case 'P': |
|
298 if (!strcasecmp(optarg, "bulk")) { |
|
299 *priority = COS_BULK; |
|
300 } else if (!strcasecmp(optarg, "normal")) { |
|
301 *priority = COS_NORMAL; |
|
302 } else if (!strcasecmp(optarg, "expedited")) { |
|
303 *priority = COS_EXPEDITED; |
|
304 } else if (!strcasecmp(optarg, "reserved")) { |
|
305 *priority = COS_RESERVED; |
|
306 } else { |
|
307 fprintf(stderr, "invalid priority value %s\n", optarg); |
|
308 usage(); |
|
309 exit(1); |
|
310 } |
|
311 break; |
|
312 case 'D': |
|
313 *delivery_options |= DOPTS_DELIVERY_RCPT; |
|
314 break; |
|
315 case 'X': |
|
316 *delivery_options |= DOPTS_DELETE_RCPT; |
|
317 break; |
|
318 case 'F': |
|
319 *delivery_options |= DOPTS_FORWARD_RCPT; |
|
320 break; |
|
321 case 'R': |
|
322 *delivery_options |= DOPTS_RECEIVE_RCPT; |
|
323 break; |
|
324 case 'c': |
|
325 *delivery_options |= DOPTS_CUSTODY; |
|
326 break; |
|
327 case 'C': |
|
328 *delivery_options |= DOPTS_CUSTODY_RCPT; |
|
329 break; |
|
330 case '1': |
|
331 *delivery_options |= DOPTS_SINGLETON_DEST; |
|
332 break; |
|
333 case 'N': |
|
334 *delivery_options |= DOPTS_MULTINODE_DEST; |
|
335 break; |
|
336 case 'W': |
|
337 *delivery_options |= DOPTS_DO_NOT_FRAGMENT; |
|
338 break; |
|
339 case 'v': |
|
340 *verbose = 1; |
|
341 break; |
|
342 case 'h': |
|
343 case 'H': |
|
344 usage(); |
|
345 |
|
346 exit(0); |
|
347 case -1: |
|
348 done = 1; |
|
349 break; |
|
350 default: |
|
351 // getopt already prints error message for unknown option characters |
|
352 usage(); |
|
353 exit(1); |
|
354 } |
|
355 |
|
356 // now set matching file if required |
|
357 if (*query_type == DTN_BPQ_FILE) { |
|
358 strncpy(matching_file, query, PATH_MAX); |
|
359 memset(query, 0, sizeof(char) * PATH_MAX); |
|
360 |
|
361 int ret = parse_matching_file(matching_file, matching_rule, |
|
362 query_type, query, bundle_expiry, *verbose); |
|
363 if (ret != DTN_SUCCESS) { |
|
364 fprintf(stderr, "error parsing matching file, " |
|
365 "see man page dtnmatch(1)\n"); |
|
366 usage(1); |
|
367 exit(1); |
|
368 } |
|
369 } |
|
370 } |
|
371 return 0; |
|
372 } |
|
373 |
|
374 /******************************************************************************* |
|
375 * validate options: |
|
376 * as there are different requirements depending on the mode, |
|
377 * the validation will differ accordingly. |
|
378 * returns success or exits on failure |
|
379 *******************************************************************************/ |
|
380 int |
|
381 validate_options(const char * src_eid_name, |
|
382 const char * dest_eid_name, |
|
383 const char * filename, |
|
384 const char * query, |
|
385 int query_type, |
|
386 int matching_rule, |
|
387 int mode, |
|
388 dtn_timeval_t timeout, |
|
389 dtn_timeval_t bundle_expiry) |
|
390 { |
|
391 //todo: add new options |
|
392 #define REQUIRE(test, err_msg) \ |
|
393 if(!test) { \ |
|
394 fprintf(stderr, err_msg); \ |
|
395 usage(); \ |
|
396 exit(1); \ |
|
397 } |
|
398 |
|
399 switch (mode) |
|
400 { |
|
401 case DTN_BPQ_SEND: //requires src, dest, query |
|
402 REQUIRE(strlen(src_eid_name) > 0, "-s <src eid> required\n"); |
|
403 REQUIRE(strlen(dest_eid_name) > 0, "-d <dest eid> required\n"); |
|
404 REQUIRE(strlen(query) > 0, "-q <query> required\n"); |
|
405 break; |
|
406 |
|
407 case DTN_BPQ_RECV: //requires src, filename |
|
408 REQUIRE(strlen(src_eid_name) > 0, "-s <src eid> required\n"); |
|
409 REQUIRE(strlen(filename) > 0, "-f <filename> required\n"); |
|
410 break; |
|
411 |
|
412 case DTN_BPQ_SEND_RECV: //requires src, dest, query, filename |
|
413 REQUIRE(strlen(src_eid_name) > 0, "-s <src eid> required\n"); |
|
414 REQUIRE(strlen(dest_eid_name) > 0, "-d <dest eid> required\n"); |
|
415 REQUIRE(strlen(filename) > 0, "-f <filename> required\n"); |
|
416 REQUIRE(strlen(query) > 0, "-q <query> required\n"); |
|
417 break; |
|
418 default: |
|
419 REQUIRE(mode == DTN_BPQ_SEND || |
|
420 mode == DTN_BPQ_RECV || |
|
421 mode == DTN_BPQ_SEND_RECV, "-m <mode> invalid mode\n") |
|
422 } |
|
423 REQUIRE(query_type == DTN_BPQ_LITERAL || |
|
424 query_type == DTN_BPQ_BASE64 || |
|
425 query_type == DTN_BPQ_FILE, "-t <query type> invalid type\n"); |
|
426 |
|
427 REQUIRE(matching_rule == DTN_BPQ_EXACT, "-r <matching rule> invalid rule\n"); |
|
428 fprintf(stdout, "timeout: %d, REQUIRE(timeout >= -1): %d\n", timeout, timeout >= -1); |
|
429 // REQUIRE(timeout >= -1, "-o <timeout> must ba a positive integer or -1: forever\n"); |
|
430 REQUIRE(bundle_expiry > 0, "-e <expiry> must be a positive integer\n"); |
|
431 #undef REQUIRE |
|
432 //todo: check this is ok |
|
433 return 0; |
|
434 } |
|
435 |
|
436 /******************************************************************************* |
|
437 * register with dtn: |
|
438 * |
|
439 *******************************************************************************/ |
|
440 int |
|
441 register_with_dtn(dtn_handle_t handle, |
|
442 dtn_endpoint_id_t * src_eid, |
|
443 dtn_reg_id_t * regid, |
|
444 dtn_timeval_t reg_expiration, |
|
445 int reg_fail_action, |
|
446 char * reg_fail_script) |
|
447 { |
|
448 int call_bind = 0; |
|
449 dtn_reg_info_t reginfo; |
|
450 |
|
451 // if no regid has been given we need to create a new registration |
|
452 if (*regid == DTN_REGID_NONE) { |
|
453 memset(®info, 0, sizeof(dtn_reg_info_t)); |
|
454 |
|
455 // create a new registration based on this eid |
|
456 dtn_copy_eid(®info.endpoint, src_eid); |
|
457 reginfo.regid = *regid; |
|
458 reginfo.expiration = reg_expiration; |
|
459 reginfo.flags = reg_fail_action; |
|
460 reginfo.script.script_val = reg_fail_script; |
|
461 reginfo.script.script_len = strlen(reg_fail_script) + 1; |
|
462 } |
|
463 |
|
464 // try to see if there is an existing registration that matches |
|
465 // the given endpoint, in which case we'll use that one. |
|
466 if (*regid == DTN_REGID_NONE) { |
|
467 fprintf(stdout, "### src_eid: %s, regid: %d ###\n", src_eid->uri, *regid); |
|
468 if (dtn_find_registration(handle, src_eid, regid) != DTN_SUCCESS && |
|
469 dtn_errno(handle) != DTN_ENOTFOUND) { |
|
470 fprintf(stderr, "error finding registration: %s\n", |
|
471 dtn_strerror(dtn_errno(handle))); |
|
472 dtn_close(handle); |
|
473 exit(1); |
|
474 } |
|
475 call_bind = 1; |
|
476 } |
|
477 // if the user didn't give us a registration to use, get a new one |
|
478 if (*regid == DTN_REGID_NONE) { |
|
479 if (dtn_register(handle, ®info, regid) != DTN_SUCCESS) { |
|
480 fprintf(stderr, "error registering: %s\n", |
|
481 dtn_strerror(dtn_errno(handle))); |
|
482 dtn_close(handle); |
|
483 exit(1); |
|
484 } |
|
485 call_bind = 0; |
|
486 } else { |
|
487 call_bind = 1; |
|
488 } |
|
489 |
|
490 if (call_bind) { |
|
491 // bind the current handle to the found registration |
|
492 if (dtn_bind(handle, *regid) != DTN_SUCCESS) { |
|
493 fprintf(stderr, "error binding to registration: %s\n", |
|
494 dtn_strerror(dtn_errno(handle))); |
|
495 dtn_close(handle); |
|
496 exit(1); |
|
497 } |
|
498 } |
|
499 |
|
500 return DTN_SUCCESS; |
|
501 } |
|
502 |
|
503 /******************************************************************************* |
|
504 * parse eid: |
|
505 * |
|
506 * code lifted from dtnsend |
|
507 * todo: check this |
|
508 *******************************************************************************/ |
|
509 dtn_endpoint_id_t * |
|
510 parse_eid(dtn_handle_t handle, |
|
511 dtn_endpoint_id_t * eid, |
|
512 const char * str, |
|
513 int verbose) |
|
514 { |
|
515 // try the string as an actual dtn eid |
|
516 if (dtn_parse_eid_string(eid, str) == DTN_SUCCESS) { |
|
517 if (verbose) fprintf(stdout, "%s (literal)\n", str); |
|
518 return eid; |
|
519 } |
|
520 |
|
521 // build a local eid based on the configuration of our dtn |
|
522 // router plus the str as demux string |
|
523 else if (dtn_build_local_eid(handle, eid, str) == DTN_SUCCESS) { |
|
524 if (verbose) fprintf(stdout, "%s (local)\n", str); |
|
525 return eid; |
|
526 } |
|
527 else { |
|
528 fprintf(stderr, "invalid eid string '%s'\n", str); |
|
529 exit(1); |
|
530 } |
|
531 } |
|
532 |
|
533 /******************************************************************************* |
|
534 * handle file transfer: |
|
535 * |
|
536 *******************************************************************************/ |
|
537 int |
|
538 handle_file_transfer(dtn_bundle_spec_t bundle_spec, |
|
539 dtn_bundle_payload_t payload, |
|
540 const char * filename, |
|
541 int verbose) |
|
542 { |
|
543 int i; |
|
544 char line[PATH_MAX]; |
|
545 char new_filename[PATH_MAX]; |
|
546 FILE * output_file = NULL; |
|
547 FILE * input_file = NULL; |
|
548 |
|
549 strncpy(new_filename, filename, PATH_MAX); |
|
550 |
|
551 // try to open file (need to find a filename + version that does not already exist) |
|
552 // if it does exist, close, create new filename + version and try to reopen |
|
553 // return error if can't find a valid filename after 100 attempts |
|
554 output_file = fopen(new_filename, "r"); |
|
555 for (i=0; output_file != NULL; ++i) { |
|
556 fclose(output_file); |
|
557 |
|
558 if (i >= 100) |
|
559 return -1; |
|
560 |
|
561 snprintf(new_filename, PATH_MAX, "%s.%02d", filename, i); |
|
562 output_file = fopen(new_filename, "r"); |
|
563 } |
|
564 |
|
565 if (verbose) fprintf(stdout, "saving payload file as: %s\n", new_filename); |
|
566 |
|
567 // new_filename now contains the name of the |
|
568 // new file in which to store the payload |
|
569 if ((output_file = fopen(new_filename, "w")) == NULL) { |
|
570 fprintf(stderr, "error opening file in which to store received payload\n"); |
|
571 return -1; |
|
572 } |
|
573 |
|
574 if ((input_file = fopen(payload.filename.filename_val, "r")) == NULL) { |
|
575 fprintf(stderr, "error opening the received payload file\n"); |
|
576 return -1; |
|
577 } |
|
578 |
|
579 // copy payload to file |
|
580 while (fgets(line, PATH_MAX, input_file) != NULL) |
|
581 fprintf(output_file, "%s", line); |
|
582 |
|
583 fclose(input_file); |
|
584 fclose(output_file); |
|
585 |
|
586 return 0; |
|
587 } |
|
588 |
|
589 /******************************************************************************* |
|
590 * bpq to char array: |
|
591 * encode as SDNVs, |
|
592 * BPQ-kind 1-byte |
|
593 * matching rule type 1-byte |
|
594 * BPQ-value-length SDNV |
|
595 * BPQ-value n-bytes |
|
596 * number of fragments SDNV |
|
597 * fragment offsets SDNV |
|
598 * fragment lengths SDNV |
|
599 * |
|
600 * @return The number of bytes or -1 on error |
|
601 *******************************************************************************/ |
|
602 int |
|
603 bpq_to_char_array(const dtn_bpq_extension_block_data_t * bpq, char* buf, size_t buf_len) |
|
604 { |
|
605 int i=0, j=0, k=0; |
|
606 int encoding_len; |
|
607 char encoding[PATH_MAX]; |
|
608 |
|
609 memset(buf, 0, buf_len); |
|
610 |
|
611 // BPQ-kind 1-byte |
|
612 if (i < buf_len) buf[i++] = (char) bpq->kind; |
|
613 |
|
614 // matching rule type 1-byte |
|
615 if (i < buf_len) buf[i++] = (char) bpq->matching_rule; |
|
616 |
|
617 // BPQ-value-length SDNV |
|
618 if ( (encoding_len = sdnv_encode (bpq->query.query_len, encoding, PATH_MAX)) == -1 ) |
|
619 return -1; |
|
620 for (j=0; i<buf_len && j<encoding_len; ++j) |
|
621 buf[i++] = encoding[j]; |
|
622 |
|
623 // BPQ-value n-bytes |
|
624 for (j=0; i<buf_len && j<bpq->query.query_len; ++j) |
|
625 buf[i++] = bpq->query.query_val[j]; |
|
626 |
|
627 // number of fragments SDNV |
|
628 if ( (encoding_len = sdnv_encode (bpq->fragments.num_frag_returned, encoding, PATH_MAX)) == -1 ) |
|
629 return -1; |
|
630 for (j=0; i<buf_len && j<encoding_len; ++j) |
|
631 buf[i++] = encoding[j]; |
|
632 |
|
633 |
|
634 for (k=0; k<bpq->fragments.num_frag_returned; ++k) { |
|
635 |
|
636 // fragment offsets SDNV |
|
637 if ( (encoding_len = sdnv_encode (bpq->fragments.frag_offsets[k], encoding, PATH_MAX)) == -1 ) |
|
638 return -1; |
|
639 for (j=0; i<buf_len && j<encoding_len; ++j) |
|
640 buf[i++] = encoding[j]; |
|
641 |
|
642 // fragment lengths SDNV |
|
643 if ( (encoding_len = sdnv_encode (bpq->fragments.frag_lenghts[k], encoding, PATH_MAX)) == -1 ) |
|
644 return -1; |
|
645 for (j=0; i<buf_len && j<encoding_len; ++j) |
|
646 buf[i++] = encoding[j]; |
|
647 } |
|
648 |
|
649 |
|
650 return i; |
|
651 } |
|
652 /******************************************************************************* |
|
653 * char array to bpq: |
|
654 * decode as SDNVs, |
|
655 * BPQ-kind 1-byte |
|
656 * matching rule type 1-byte |
|
657 * BPQ-value-length SDNV |
|
658 * BPQ-value n-bytes |
|
659 * number of fragments SDNV |
|
660 * fragment offsets SDNV |
|
661 * fragment lengths SDNV |
|
662 * |
|
663 * @return DTN_SUCCESS or -1 on error |
|
664 *******************************************************************************/ |
|
665 int |
|
666 char_array_to_bpq(const char* buf, size_t buf_len, dtn_bpq_extension_block_data_t * bpq) |
|
667 { |
|
668 int i=0, j=0; |
|
669 int decoding_len=0; |
|
670 |
|
671 // BPQ-kind 1-byte |
|
672 if (i<buf_len) bpq->kind = (u_int) buf[i++]; |
|
673 |
|
674 // matching rule type 1-byte |
|
675 if (i<buf_len) bpq->matching_rule = (u_int) buf[i++]; |
|
676 |
|
677 // BPQ-value-length SDNV |
|
678 if ( (decoding_len = sdnv_decode (buf[i], buf_len - i, &(bpq->query.query_len))) == -1 ) |
|
679 return -1; |
|
680 i += decoding_len; |
|
681 |
|
682 // BPQ-value n-bytes |
|
683 if (i<buf_len) bpq->query.query_val = &(buf[i]); |
|
684 i += bpq->query.query_len; |
|
685 |
|
686 // number of fragments SDNV |
|
687 if ( (decoding_len = sdnv_decode (buf[i], buf_len - i, &(bpq->fragments.num_frag_returned))) == -1 ) |
|
688 return -1; |
|
689 i += decoding_len; |
|
690 |
|
691 for (j=0; i<buf_len && j<bpq->fragments.num_frag_returned; ++j) { |
|
692 |
|
693 // fragment offsets SDNV |
|
694 if ( (decoding_len = sdnv_decode (buf[i], buf_len - i, &(bpq->fragments.frag_offsets[j]))) == -1 ) |
|
695 return -1; |
|
696 i += decoding_len; |
|
697 |
|
698 // fragment lengths SDNV |
|
699 if ( (decoding_len = sdnv_decode (buf[i], buf_len - i, &(bpq->fragments.frag_lenghts[j]))) == -1 ) |
|
700 return -1; |
|
701 i += decoding_len; |
|
702 } |
|
703 |
|
704 if (i != buf_len) |
|
705 return -1; |
|
706 |
|
707 return DTN_SUCCESS; |
|
708 } |
|
709 |
|
710 /******************************************************************************* |
|
711 * send bpq: |
|
712 * given a registration handle, build a bundle with |
|
713 * a BPQ extension block and an empty payload and |
|
714 * send it to the destination. |
|
715 *******************************************************************************/ |
|
716 int |
|
717 send_bpq(dtn_handle_t handle, |
|
718 dtn_reg_id_t regid, |
|
719 const dtn_endpoint_id_t * src_eid, |
|
720 const dtn_endpoint_id_t * dest_eid, |
|
721 char * query, |
|
722 int matching_rule, |
|
723 int bundle_expiry, |
|
724 dtn_bundle_priority_t priority, |
|
725 int delivery_options, |
|
726 int verbose) |
|
727 { |
|
728 int ret = 0; |
|
729 char buf [PATH_MAX]; |
|
730 size_t buf_len = 0; |
|
731 dtn_bundle_id_t bundle_id; |
|
732 dtn_bundle_spec_t bundle_spec; |
|
733 dtn_extension_block_t bpq_block; |
|
734 dtn_bpq_extension_block_data_t bpq_block_data; |
|
735 dtn_bundle_payload_t payload; |
|
736 |
|
737 memset(buf, 0, PATH_MAX); |
|
738 memset(&bundle_spec, 0, sizeof(dtn_bundle_spec_t)); |
|
739 memset(&bpq_block, 0, sizeof(dtn_extension_block_t)); |
|
740 memset(&bpq_block_data, 0, sizeof(dtn_bpq_extension_block_data_t)); |
|
741 memset(&payload, 0, sizeof(dtn_bundle_payload_t)); |
|
742 |
|
743 // set the bpq block data |
|
744 bpq_block_data.kind = DTN_BPQ_KIND_QUERY; |
|
745 bpq_block_data.matching_rule = matching_rule; |
|
746 bpq_block_data.query.query_len = strlen(query) + 1; // include the null char at the end |
|
747 bpq_block_data.query.query_val = query; |
|
748 bpq_block_data.fragments.num_frag_returned = 0; |
|
749 bpq_block_data.fragments.frag_offsets = NULL; |
|
750 bpq_block_data.fragments.frag_lenghts = NULL; |
|
751 |
|
752 buf_len = bpq_to_char_array(&bpq_block_data, buf, PATH_MAX); |
|
753 |
|
754 // set the bpq block |
|
755 bpq_block.type = DTN_BPQ_BLOCK_TYPE; |
|
756 bpq_block.flags = DTN_BPQ_BLOCK_FLAGS; |
|
757 bpq_block.data.data_len = buf_len; |
|
758 bpq_block.data.data_val = buf; |
|
759 |
|
760 // set the payload (empty) |
|
761 dtn_set_payload(&payload, DTN_PAYLOAD_MEM, NULL, 0); |
|
762 |
|
763 // set the bundle spec eids |
|
764 if (verbose) fprintf(stdout, "Source: %s\n", src_eid->uri); |
|
765 if (verbose) fprintf(stdout, "Destination: %s\n", dest_eid->uri); |
|
766 bundle_spec.source = *src_eid; |
|
767 bundle_spec.dest = *dest_eid; |
|
768 |
|
769 dtn_copy_eid(&bundle_spec.replyto, &bundle_spec.source);// add support for this to be set differently |
|
770 |
|
771 // set the bundle spec dtn options |
|
772 bundle_spec.expiration = bundle_expiry; |
|
773 bundle_spec.dopts = delivery_options; |
|
774 bundle_spec.priority = priority; |
|
775 |
|
776 // set the bundle extension |
|
777 bundle_spec.blocks.blocks_len = 1; |
|
778 bundle_spec.blocks.blocks_val = &bpq_block; |
|
779 |
|
780 // send the bundle, bpq extension and empty payload |
|
781 if (verbose) fprintf(stdout, "Sending bundle to: %s\n", dest_eid->uri); |
|
782 ret = dtn_send(handle, regid, &bundle_spec, &payload, &bundle_id); |
|
783 if (ret != DTN_SUCCESS) { |
|
784 fprintf(stderr, "error sending bundle: %d (%s)\n", |
|
785 ret, dtn_strerror(dtn_errno(handle))); |
|
786 } else if (verbose) { |
|
787 fprintf(stdout, "bundle sent successfully: id %s,%llu.%llu\n", |
|
788 bundle_id.source.uri, |
|
789 bundle_id.creation_ts.secs, |
|
790 bundle_id.creation_ts.seqno); |
|
791 } |
|
792 return ret; |
|
793 } |
|
794 |
|
795 /******************************************************************************* |
|
796 * recv bpq: |
|
797 * todo. |
|
798 *******************************************************************************/ |
|
799 int |
|
800 recv_bpq(dtn_handle_t handle, |
|
801 dtn_timeval_t timeout, |
|
802 const char * filename, |
|
803 int verbose) |
|
804 { |
|
805 int ret = 0; |
|
806 dtn_bundle_spec_t bundle_spec; |
|
807 dtn_bundle_payload_t payload; |
|
808 |
|
809 memset(&bundle_spec, 0, sizeof(bundle_spec)); |
|
810 memset(&payload, 0, sizeof(payload)); |
|
811 |
|
812 // recv the bpq bundle |
|
813 if (verbose) fprintf(stdout, "blocking waiting for dtn_recv\n"); |
|
814 ret = dtn_recv(handle, &bundle_spec, DTN_PAYLOAD_FILE, &payload, timeout); |
|
815 if (ret != DTN_SUCCESS) { |
|
816 fprintf(stderr, "error receiving bundle: %d (%s)\n", |
|
817 ret, dtn_strerror(dtn_errno(handle))); |
|
818 return ret; |
|
819 } else if (verbose) { |
|
820 fprintf(stdout, "bundle received successfully: id %s,%llu.%llu\n", |
|
821 bundle_spec.source.uri, |
|
822 bundle_spec.creation_ts.secs, |
|
823 bundle_spec.creation_ts.seqno); |
|
824 } |
|
825 |
|
826 // handle the payload file |
|
827 ret = handle_file_transfer(bundle_spec, payload, filename, verbose); |
|
828 if (ret != DTN_SUCCESS) { |
|
829 fprintf(stderr, "error handling file transfer: %d\n", ret); |
|
830 } else if (verbose) { |
|
831 fprintf(stdout, "sucessfully handled file transfer\n"); |
|
832 } |
|
833 |
|
834 dtn_free_payload(&payload); |
|
835 return ret; |
|
836 } |
|
837 |
|
838 /******************************************************************************* |
|
839 * main: |
|
840 *******************************************************************************/ |
|
841 int |
|
842 main(int argc, char** argv) |
|
843 { |
|
844 dtn_endpoint_id_t src_eid; |
|
845 dtn_endpoint_id_t dest_eid; |
|
846 char src_eid_name[PATH_MAX]; |
|
847 char dest_eid_name[PATH_MAX]; |
|
848 char filename[PATH_MAX]; |
|
849 char query[PATH_MAX]; |
|
850 int query_type = DTN_BPQ_LITERAL; |
|
851 int matching_rule = DTN_BPQ_EXACT; |
|
852 int mode = DTN_BPQ_SEND_RECV; |
|
853 dtn_timeval_t timeout = DTN_TIMEOUT_INF; //forever |
|
854 dtn_timeval_t bundle_expiry = 3600; //one hour |
|
855 dtn_reg_id_t regid = DTN_REGID_NONE; |
|
856 dtn_timeval_t reg_expiry = 30; |
|
857 int reg_fail_action = DTN_REG_DEFER; |
|
858 char reg_fail_script[PATH_MAX]; |
|
859 dtn_bundle_priority_t priority = COS_NORMAL; |
|
860 int delivery_options = 0; |
|
861 int verbose = 0; |
|
862 int err = 0; |
|
863 dtn_handle_t handle; |
|
864 |
|
865 parse_options(argc, argv, |
|
866 src_eid_name, |
|
867 dest_eid_name, |
|
868 filename, |
|
869 query, |
|
870 &query_type, |
|
871 &matching_rule, |
|
872 &mode, |
|
873 &timeout, |
|
874 &bundle_expiry, |
|
875 ®id, |
|
876 ®_expiry, |
|
877 ®_fail_action, |
|
878 reg_fail_script, |
|
879 &priority, |
|
880 &delivery_options, |
|
881 &verbose); |
|
882 |
|
883 validate_options(src_eid_name, |
|
884 dest_eid_name, |
|
885 filename, |
|
886 query, |
|
887 query_type, |
|
888 matching_rule, |
|
889 mode, |
|
890 timeout, |
|
891 bundle_expiry); |
|
892 |
|
893 // open the ipc handle |
|
894 if (verbose) printf("opening connection to dtn router...\n"); |
|
895 if ((err = dtn_open(&handle)) != DTN_SUCCESS) { |
|
896 fprintf(stderr, "fatal error opening dtn handle: %s\n", |
|
897 dtn_strerror(err)); |
|
898 exit(1); |
|
899 } |
|
900 if (verbose) fprintf(stdout, "opened connection to dtn router...\n"); |
|
901 |
|
902 // parse eids |
|
903 parse_eid(handle, &src_eid, src_eid_name, verbose); |
|
904 parse_eid(handle, &dest_eid, dest_eid_name, verbose); |
|
905 if (verbose) fprintf(stdout, "parsed src_eid: %s\n", src_eid.uri); |
|
906 if (verbose) fprintf(stdout, "parsed dest_eid: %s\n", dest_eid.uri); |
|
907 if (verbose) fprintf(stdout, "regid: %d\n", regid); |
|
908 |
|
909 // get dtn registration |
|
910 if (verbose) fprintf(stdout, "registering with dtn...\n"); |
|
911 register_with_dtn(handle, |
|
912 &src_eid, |
|
913 ®id, |
|
914 reg_expiry, |
|
915 reg_fail_action, |
|
916 reg_fail_script); |
|
917 if (verbose) fprintf(stdout, "registered with dtn, " |
|
918 "regid: %d local eid: %s\n", |
|
919 regid, src_eid.uri); |
|
920 |
|
921 //get to work |
|
922 switch (mode) |
|
923 { |
|
924 #define TRY(fn, err_msg) \ |
|
925 if (fn != DTN_SUCCESS) { \ |
|
926 fprintf(stderr, err_msg); \ |
|
927 dtn_close(handle); \ |
|
928 exit(1); \ |
|
929 } |
|
930 case DTN_BPQ_SEND: |
|
931 TRY( send_bpq(handle, regid, &src_eid, &dest_eid, query, |
|
932 matching_rule, bundle_expiry, priority, |
|
933 delivery_options, verbose), "error sending query\n" ); |
|
934 break; |
|
935 |
|
936 case DTN_BPQ_RECV: |
|
937 TRY( recv_bpq(handle, timeout, filename, verbose), |
|
938 "error receiving query\n" ); |
|
939 break; |
|
940 |
|
941 case DTN_BPQ_SEND_RECV: |
|
942 TRY( send_bpq(handle, regid, &src_eid, &dest_eid, query, |
|
943 matching_rule, bundle_expiry, priority, |
|
944 delivery_options, verbose), "error sending query\n" ); |
|
945 |
|
946 TRY( recv_bpq(handle, timeout, filename, verbose), |
|
947 "error receiving query\n" ); |
|
948 break; |
|
949 |
|
950 default: |
|
951 fprintf(stderr, "invalid mode '%d'\n", mode); |
|
952 dtn_close(handle); |
|
953 exit(1); |
|
954 #undef TRY |
|
955 } |
|
956 |
|
957 // close the ipc handle |
|
958 if (verbose) fprintf(stdout, "closing connection to dtn router...\n"); |
|
959 dtn_close(handle); |
|
960 if (verbose) fprintf(stdout, "closed connection to dtn router...\n"); |
|
961 |
|
962 return 0; |
|
963 } |
|
964 |