|
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 #define DTN_BPQ_BLOCK_TYPE 0xC8 |
|
38 #define DTN_BPQ_BLOCK_FLAGS 0x00 |
|
39 |
|
40 #define DTN_BPQ_KIND_QUERY 0x00 |
|
41 #define DTN_BPQ_KIND_RESPONSE 0x01 |
|
42 |
|
43 // Find the maximum commandline length |
|
44 #ifdef __FreeBSD__ |
|
45 /* Needed for PATH_MAX, Linux doesn't need it */ |
|
46 #include <sys/syslimits.h> |
|
47 #endif |
|
48 |
|
49 #ifndef PATH_MAX |
|
50 /* A conservative fallback */ |
|
51 #define PATH_MAX 1024 |
|
52 #endif |
|
53 |
|
54 //global variables |
|
55 const char* progname; |
|
56 |
|
57 /******************************************************************************* |
|
58 * usage: |
|
59 * display cmd line options to user. |
|
60 *******************************************************************************/ |
|
61 int |
|
62 usage() |
|
63 { |
|
64 fprintf(stderr, "usage: %s -l < local endpoint > -f < matching filename > " |
|
65 "[opts]\n", progname); |
|
66 fprintf(stderr, "options:\n"); |
|
67 fprintf(stderr, " -n < count > exit after count bundles received\n"); |
|
68 fprintf(stderr, " -e < seconds > bundle expiry time\n"); |
|
69 fprintf(stderr, " -i < regid > existing registration id\n"); |
|
70 fprintf(stderr, " -E < seconds > registration expiry time\n"); |
|
71 fprintf(stderr, " -A < defer | drop | exec > failure action\n"); |
|
72 fprintf(stderr, " -S < script > failure script for exec action\n"); |
|
73 fprintf(stderr, " -P < bulk | normal | expedited | reserved > priority\n"); |
|
74 fprintf(stderr, " -D request end-to-end delivery receipt\n"); |
|
75 fprintf(stderr, " -X request deletion receipt\n"); |
|
76 fprintf(stderr, " -F request bundle forwarding receipts\n"); |
|
77 fprintf(stderr, " -R request bundle reception receipts\n"); |
|
78 fprintf(stderr, " -c request custody transfer\n"); |
|
79 fprintf(stderr, " -C request custody transfer receipts\n"); |
|
80 fprintf(stderr, " -1 assert destination endpoint is a singleton\n"); |
|
81 fprintf(stderr, " -N assert destination endpoint is not a singleton\n"); |
|
82 fprintf(stderr, " -W set the do not fragment option\n"); |
|
83 fprintf(stderr, " -h help\n"); |
|
84 fprintf(stderr, " -v verbose\n"); |
|
85 |
|
86 return 0; |
|
87 } |
|
88 |
|
89 /******************************************************************************* |
|
90 * parse options: |
|
91 * set internal variables based on cmd line args. |
|
92 * calls parse matching file if required. |
|
93 * returns success or exits on failure. |
|
94 *******************************************************************************/ |
|
95 int |
|
96 parse_options(int argc, char** argv, |
|
97 char * local_eid_name, // l |
|
98 char * matching_filename, // f |
|
99 int * count, // n |
|
100 dtn_timeval_t * bundle_expiry, // e |
|
101 dtn_reg_id_t * regid, // i |
|
102 dtn_timeval_t * reg_expiry, // E |
|
103 int * reg_fail_action, // A |
|
104 char * reg_fail_script, // S |
|
105 dtn_bundle_priority_t * priority, // P |
|
106 int * delivery_options, // D X F R c C 1 N W |
|
107 int * verbose) // v |
|
108 { |
|
109 int c, done = 0; |
|
110 |
|
111 progname = argv[0]; |
|
112 |
|
113 //initialize strings |
|
114 memset(local_eid_name, 0, sizeof(char) * PATH_MAX); |
|
115 memset(matching_filename, 0, sizeof(char) * PATH_MAX); |
|
116 memset(reg_fail_script, 0, sizeof(char) * PATH_MAX); |
|
117 |
|
118 while( !done ) |
|
119 { |
|
120 c = getopt(argc, argv, "l:f:n:e:i:E:A:S:P:DXFRcC1NWvhH"); |
|
121 switch(c) |
|
122 { |
|
123 case 'l': |
|
124 strncpy(local_eid_name, optarg, PATH_MAX); |
|
125 break; |
|
126 case 'f': |
|
127 strncpy(matching_filename, optarg, PATH_MAX); |
|
128 break; |
|
129 case 'n': |
|
130 *count = atoi(optarg); |
|
131 break; |
|
132 case 'e': |
|
133 *bundle_expiry = atoi(optarg); |
|
134 break; |
|
135 case 'i': |
|
136 *regid = atoi(optarg); |
|
137 break; |
|
138 case 'E': |
|
139 *reg_expiry = atoi(optarg); |
|
140 break; |
|
141 case 'A': |
|
142 if (!strcasecmp(optarg, "defer")) { |
|
143 *reg_fail_action = DTN_REG_DEFER; |
|
144 |
|
145 } else if (!strcasecmp(optarg, "drop")) { |
|
146 *reg_fail_action = DTN_REG_DROP; |
|
147 |
|
148 } else if (!strcasecmp(optarg, "exec")) { |
|
149 *reg_fail_action = DTN_REG_EXEC; |
|
150 |
|
151 } else { |
|
152 fprintf(stderr, "invalid failure action '%s'\n", optarg); |
|
153 usage(); |
|
154 exit(1); |
|
155 } |
|
156 break; |
|
157 case 'S': |
|
158 strncpy(reg_fail_script, optarg, PATH_MAX); |
|
159 break; |
|
160 case 'P': |
|
161 if (!strcasecmp(optarg, "bulk")) { |
|
162 *priority = COS_BULK; |
|
163 } else if (!strcasecmp(optarg, "normal")) { |
|
164 *priority = COS_NORMAL; |
|
165 } else if (!strcasecmp(optarg, "expedited")) { |
|
166 *priority = COS_EXPEDITED; |
|
167 } else if (!strcasecmp(optarg, "reserved")) { |
|
168 *priority = COS_RESERVED; |
|
169 } else { |
|
170 fprintf(stderr, "invalid priority value %s\n", optarg); |
|
171 usage(); |
|
172 exit(1); |
|
173 } |
|
174 break; |
|
175 case 'D': |
|
176 *delivery_options |= DOPTS_DELIVERY_RCPT; |
|
177 break; |
|
178 case 'X': |
|
179 *delivery_options |= DOPTS_DELETE_RCPT; |
|
180 break; |
|
181 case 'F': |
|
182 *delivery_options |= DOPTS_FORWARD_RCPT; |
|
183 break; |
|
184 case 'R': |
|
185 *delivery_options |= DOPTS_RECEIVE_RCPT; |
|
186 break; |
|
187 case 'c': |
|
188 *delivery_options |= DOPTS_CUSTODY; |
|
189 break; |
|
190 case 'C': |
|
191 *delivery_options |= DOPTS_CUSTODY_RCPT; |
|
192 break; |
|
193 case '1': |
|
194 *delivery_options |= DOPTS_SINGLETON_DEST; |
|
195 break; |
|
196 case 'N': |
|
197 *delivery_options |= DOPTS_MULTINODE_DEST; |
|
198 break; |
|
199 case 'W': |
|
200 *delivery_options |= DOPTS_DO_NOT_FRAGMENT; |
|
201 break; |
|
202 case 'v': |
|
203 *verbose = 1; |
|
204 break; |
|
205 case 'h': |
|
206 case 'H': |
|
207 usage(); |
|
208 exit(0); |
|
209 case -1: |
|
210 done = 1; |
|
211 break; |
|
212 default: |
|
213 // getopt already prints error message for unknown option characters |
|
214 usage(); |
|
215 exit(1); |
|
216 } |
|
217 } |
|
218 return 0; |
|
219 } |
|
220 |
|
221 /******************************************************************************* |
|
222 * validate options: |
|
223 * returns success or exits on failure |
|
224 *******************************************************************************/ |
|
225 int |
|
226 validate_options(const char * local_eid_name, const char * matching_filename) |
|
227 { |
|
228 #define REQUIRE(test, err_msg) \ |
|
229 if(!test) { \ |
|
230 fprintf(stderr, err_msg); \ |
|
231 usage(); \ |
|
232 exit(1); \ |
|
233 } |
|
234 |
|
235 REQUIRE(strlen(local_eid_name) > 0, "-l <local eid> required\n"); |
|
236 REQUIRE(strlen(matching_filename) > 0, "-f <matching filename> required\n"); |
|
237 |
|
238 return 0; |
|
239 } |
|
240 |
|
241 /******************************************************************************* |
|
242 * register with dtn: |
|
243 * |
|
244 *******************************************************************************/ |
|
245 int |
|
246 register_with_dtn(dtn_handle_t handle, |
|
247 dtn_endpoint_id_t * local_eid, |
|
248 const char * local_eid_name, |
|
249 dtn_reg_id_t * regid, |
|
250 dtn_timeval_t reg_expiration, |
|
251 int reg_fail_action, |
|
252 char * reg_fail_script) |
|
253 { |
|
254 int call_bind = 0; |
|
255 dtn_reg_info_t reginfo; |
|
256 |
|
257 memset(local_eid, 0, sizeof(dtn_endpoint_id_t)); |
|
258 |
|
259 // if no regid has been given we need to create a new registration |
|
260 if (*regid == DTN_REGID_NONE) { |
|
261 if (local_eid_name[0] == '/') { |
|
262 if (dtn_build_local_eid(handle, local_eid, local_eid_name) != DTN_SUCCESS) { |
|
263 fprintf(stderr, "error building local eid: %s\n", |
|
264 dtn_strerror(dtn_errno(handle))); |
|
265 dtn_close(handle); |
|
266 exit(1); |
|
267 } |
|
268 } else { |
|
269 if (dtn_parse_eid_string(local_eid, local_eid_name) != DTN_SUCCESS) { |
|
270 fprintf(stderr, "error parsing eid string: %s\n", |
|
271 dtn_strerror(dtn_errno(handle))); |
|
272 dtn_close(handle); |
|
273 exit(1); |
|
274 } |
|
275 } |
|
276 |
|
277 memset(®info, 0, sizeof(dtn_reg_info_t)); |
|
278 |
|
279 // create a new registration based on this eid |
|
280 dtn_copy_eid(®info.endpoint, local_eid); |
|
281 reginfo.regid = *regid; |
|
282 reginfo.expiration = reg_expiration; |
|
283 reginfo.flags = reg_fail_action; |
|
284 reginfo.script.script_val = reg_fail_script; |
|
285 reginfo.script.script_len = strlen(reg_fail_script) + 1; |
|
286 } |
|
287 |
|
288 // try to see if there is an existing registration that matches |
|
289 // the given endpoint, in which case we'll use that one. |
|
290 if (*regid == DTN_REGID_NONE) { |
|
291 if (dtn_find_registration(handle, local_eid, regid) != DTN_SUCCESS && |
|
292 dtn_errno(handle) != DTN_ENOTFOUND) { |
|
293 fprintf(stderr, "error finding registration: %s\n", |
|
294 dtn_strerror(dtn_errno(handle))); |
|
295 dtn_close(handle); |
|
296 exit(1); |
|
297 } |
|
298 call_bind = 1; |
|
299 } |
|
300 |
|
301 // if the user didn't give us a registration to use, get a new one |
|
302 if (*regid == DTN_REGID_NONE) { |
|
303 if (dtn_register(handle, ®info, regid) != DTN_SUCCESS) { |
|
304 fprintf(stderr, "error registering: %s\n", |
|
305 dtn_strerror(dtn_errno(handle))); |
|
306 dtn_close(handle); |
|
307 exit(1); |
|
308 } |
|
309 call_bind = 0; |
|
310 } else { |
|
311 call_bind = 1; |
|
312 } |
|
313 |
|
314 if (call_bind) { |
|
315 // bind the current handle to the found registration |
|
316 if (dtn_bind(handle, *regid) != DTN_SUCCESS) { |
|
317 fprintf(stderr, "error binding to registration: %s\n", |
|
318 dtn_strerror(dtn_errno(handle))); |
|
319 dtn_close(handle); |
|
320 exit(1); |
|
321 } |
|
322 } |
|
323 |
|
324 return DTN_SUCCESS; |
|
325 } |
|
326 |
|
327 /******************************************************************************* |
|
328 * trim whitespace: |
|
329 * first move past any leading whitespace |
|
330 * after the first non-whitespace char bef=gin copying to output |
|
331 * finish by setting any trailing whitespace to 0 |
|
332 * @return 0 on success or -1 if input not completely read |
|
333 *******************************************************************************/ |
|
334 int |
|
335 trim_whitespace(const char * in, char * out, int out_len) |
|
336 { |
|
337 int i=0, j=0; |
|
338 char space = ' '; |
|
339 char tab = '\t'; |
|
340 |
|
341 memset(out, 0, out_len); |
|
342 |
|
343 // move past any leading whitespace |
|
344 while (i<strlen(in) && in[i] == space || |
|
345 i<strlen(in) && in[i] == tab) |
|
346 ++i; |
|
347 |
|
348 // body case |
|
349 for ( ; i<strlen(in) && j<out_len-2; ++i){ |
|
350 out[j++] = in[i]; |
|
351 } |
|
352 |
|
353 // remove any trailing whitespace |
|
354 // out[j] now points to the null char that terminates the string |
|
355 j--; |
|
356 while (j >= 0 && out[j] == space || |
|
357 j >= 0 && out[j] == tab) |
|
358 out[j--] = 0; |
|
359 |
|
360 if (i < strlen(in)) |
|
361 return -1; |
|
362 else |
|
363 return 0; |
|
364 } |
|
365 |
|
366 /******************************************************************************* |
|
367 * escape spaces: |
|
368 * first move past any leading whitespace |
|
369 * after the first non-whitespace char escape any whitespace |
|
370 * @return 0 on success or -1 if input not completely read |
|
371 *******************************************************************************/ |
|
372 int |
|
373 escape_spaces(const char * in, char * out, int out_len) |
|
374 { |
|
375 int i=0, j=0; |
|
376 char escape = '\\'; |
|
377 char space = ' '; |
|
378 |
|
379 memset(out, 0, out_len); |
|
380 |
|
381 // move past any leading whitespace |
|
382 while (i<strlen(in) && in[i] == space) |
|
383 ++i; |
|
384 |
|
385 // body case |
|
386 for ( ; i<strlen(in) && j<out_len-2; ++i){ |
|
387 if (in[i] == space && in[i-1] != escape) { |
|
388 out[j++] = escape; |
|
389 out[j++] = space; |
|
390 } else { |
|
391 out[j++] = in[i]; |
|
392 } |
|
393 } |
|
394 |
|
395 if (i < strlen(in)) |
|
396 return -1; |
|
397 else |
|
398 return 0; |
|
399 } |
|
400 |
|
401 /******************************************************************************* |
|
402 * find query |
|
403 * search all files in 'dir' for a query match |
|
404 * test against query len & query val and update found |
|
405 * @return 0 or -1 if error |
|
406 *******************************************************************************/ |
|
407 int |
|
408 find_query(const char * dir, const char * query, int * found) |
|
409 { |
|
410 FILE *file; |
|
411 int i, status; |
|
412 char ls_result[PATH_MAX]; |
|
413 char ls_cmd[PATH_MAX]; |
|
414 char esc_dir[PATH_MAX]; |
|
415 |
|
416 *found = 0; |
|
417 |
|
418 // trim & escape spaces in dir before calling ls |
|
419 if (escape_spaces(dir, esc_dir, PATH_MAX) != DTN_SUCCESS) |
|
420 return -1; |
|
421 |
|
422 snprintf (ls_cmd, PATH_MAX, "ls %s", esc_dir); |
|
423 file = popen (ls_cmd, "r"); |
|
424 |
|
425 if (file == NULL) |
|
426 return -1; |
|
427 |
|
428 for (i=0; fgets(ls_result, PATH_MAX, file) != NULL; ++i) { |
|
429 if (ls_result[strlen(ls_result)-1] == '\n') |
|
430 ls_result[strlen(ls_result)-1] = 0; |
|
431 |
|
432 if (strlen(query) == strlen(ls_result) && |
|
433 strncmp (query, ls_result, strlen(query)) == 0) { |
|
434 *found = 1; |
|
435 break; |
|
436 } |
|
437 } |
|
438 |
|
439 status = pclose(file); |
|
440 if (status == -1) |
|
441 return -1; |
|
442 |
|
443 return 0; |
|
444 } |
|
445 |
|
446 |
|
447 /******************************************************************************* |
|
448 * match bpq: |
|
449 * read in paths to search for query in from 'matching file' |
|
450 * for each record in the matching file, extract the response path |
|
451 * matching file format: [matching_rule, encoding, query, response_path, expiry] |
|
452 *******************************************************************************/ |
|
453 int |
|
454 match_bpq(const dtn_bpq_extension_block_data_t * bpq, |
|
455 const char * matching_filename, |
|
456 char * pathname, |
|
457 int * found) |
|
458 { |
|
459 char line[PATH_MAX]; |
|
460 char trim_response_path[PATH_MAX]; |
|
461 char * response_path; |
|
462 FILE * file; |
|
463 |
|
464 *found = 0; |
|
465 |
|
466 if ((file = fopen(matching_filename, "r")) == 0) |
|
467 return -1; |
|
468 |
|
469 memset(line, 0 , PATH_MAX); |
|
470 while (fgets(line, PATH_MAX, file) != NULL) { |
|
471 strtok(line, ","); |
|
472 strtok(NULL, ","); |
|
473 strtok(NULL, ","); |
|
474 response_path = strtok(NULL, ","); |
|
475 // expiry = strtok(NULL, ","); |
|
476 |
|
477 // trim whitespace from response path |
|
478 trim_whitespace(response_path, trim_response_path, PATH_MAX); |
|
479 |
|
480 if (find_query(trim_response_path, bpq->query.query_val, found) != DTN_SUCCESS) |
|
481 return -1; |
|
482 |
|
483 // if found build pathname and stop looking |
|
484 if (*found == 1) { |
|
485 // ensure path ends in slash |
|
486 if (trim_response_path[strlen(trim_response_path)-1] == '/'){ |
|
487 snprintf(pathname, PATH_MAX, "%s%s", trim_response_path, bpq->query.query_val); |
|
488 } else { |
|
489 snprintf(pathname, PATH_MAX, "%s/%s", trim_response_path, bpq->query.query_val); |
|
490 } |
|
491 |
|
492 break; |
|
493 } |
|
494 memset(line, 0 , PATH_MAX); |
|
495 } |
|
496 fclose (file); |
|
497 |
|
498 return 0; |
|
499 } |
|
500 |
|
501 /******************************************************************************* |
|
502 * bpq to char array: |
|
503 * encode as SDNVs, |
|
504 * BPQ-kind 1-byte |
|
505 * matching rule type 1-byte |
|
506 * BPQ-value-length SDNV |
|
507 * BPQ-value n-bytes |
|
508 * number of fragments SDNV |
|
509 * fragment offsets SDNV |
|
510 * fragment lengths SDNV |
|
511 * |
|
512 * @return The number of bytes or -1 on error |
|
513 *******************************************************************************/ |
|
514 int |
|
515 bpq_to_char_array(const dtn_bpq_extension_block_data_t * bpq, |
|
516 char* buf, |
|
517 size_t buf_len) |
|
518 { |
|
519 int i=0, j=0, k=0; |
|
520 int encoding_len; |
|
521 char encoding[PATH_MAX]; |
|
522 |
|
523 // BPQ-kind 1-byte |
|
524 if (i < buf_len) buf[i++] = (char) bpq->kind; |
|
525 |
|
526 // matching rule type 1-byte |
|
527 if (i < buf_len) buf[i++] = (char) bpq->matching_rule; |
|
528 |
|
529 // BPQ-value-length SDNV |
|
530 if ( (encoding_len = sdnv_encode (bpq->query.query_len, encoding, PATH_MAX)) == -1 ) |
|
531 return -1; |
|
532 for (j=0; i<buf_len && j<encoding_len; ++j) |
|
533 buf[i++] = encoding[j]; |
|
534 |
|
535 // BPQ-value n-bytes |
|
536 for (j=0; i<buf_len && j<bpq->query.query_len; ++j) |
|
537 buf[i++] = bpq->query.query_val[j]; |
|
538 |
|
539 // number of fragments SDNV |
|
540 if ( (encoding_len = sdnv_encode (bpq->fragments.num_frag_returned, encoding, PATH_MAX)) == -1 ) |
|
541 return -1; |
|
542 for (j=0; i<buf_len && j<encoding_len; ++j) |
|
543 buf[i++] = encoding[j]; |
|
544 |
|
545 for (k=0; k<bpq->fragments.num_frag_returned; ++k) { |
|
546 |
|
547 // fragment offsets SDNV |
|
548 if ( (encoding_len = sdnv_encode (bpq->fragments.frag_offsets[k], encoding, PATH_MAX)) == -1 ) |
|
549 return -1; |
|
550 for (j=0; i<buf_len && j<encoding_len; ++j) |
|
551 buf[i++] = encoding[j]; |
|
552 |
|
553 // fragment lengths SDNV |
|
554 if ( (encoding_len = sdnv_encode (bpq->fragments.frag_lenghts[k], encoding, PATH_MAX)) == -1 ) |
|
555 return -1; |
|
556 for (j=0; i<buf_len && j<encoding_len; ++j) |
|
557 buf[i++] = encoding[j]; |
|
558 } |
|
559 |
|
560 |
|
561 return i; |
|
562 } |
|
563 |
|
564 /******************************************************************************* |
|
565 * char array to bpq: |
|
566 * decode as SDNVs, |
|
567 * BPQ-kind 1-byte |
|
568 * matching rule type 1-byte |
|
569 * BPQ-value-length SDNV |
|
570 * BPQ-value n-bytes |
|
571 * number of fragments SDNV |
|
572 * fragment offsets SDNV |
|
573 * fragment lengths SDNV |
|
574 * |
|
575 * @return DTN_SUCCESS or -1 on error |
|
576 *******************************************************************************/ |
|
577 int |
|
578 char_array_to_bpq(const char* buf, |
|
579 size_t buf_len, |
|
580 dtn_bpq_extension_block_data_t * bpq) |
|
581 { |
|
582 int i=0, j=0; |
|
583 int decoding_len=0; |
|
584 |
|
585 // BPQ-kind 1-byte |
|
586 if (i<buf_len) bpq->kind = (u_int) buf[i++]; |
|
587 |
|
588 // matching rule type 1-byte |
|
589 if (i<buf_len) bpq->matching_rule = (u_int) buf[i++]; |
|
590 |
|
591 // BPQ-value-length SDNV |
|
592 if ( (decoding_len = sdnv_decode (&(buf[i]), buf_len - i, &(bpq->query.query_len))) == -1 ) |
|
593 return -1; |
|
594 i += decoding_len; |
|
595 |
|
596 // BPQ-value n-bytes |
|
597 if (i<buf_len) bpq->query.query_val = &(buf[i]); |
|
598 i += bpq->query.query_len; |
|
599 |
|
600 // number of fragments SDNV |
|
601 if ( (decoding_len = sdnv_decode (&(buf[i]), buf_len - i, &(bpq->fragments.num_frag_returned))) == -1 ) |
|
602 return -1; |
|
603 i += decoding_len; |
|
604 |
|
605 for (j=0; i<buf_len && j<bpq->fragments.num_frag_returned; ++j) { |
|
606 |
|
607 // fragment offsets SDNV |
|
608 if ( (decoding_len = sdnv_decode (&(buf[i]), buf_len - i, &(bpq->fragments.frag_offsets[j]))) == -1 ) |
|
609 return -1; |
|
610 i += decoding_len; |
|
611 |
|
612 // fragment lengths SDNV |
|
613 if ( (decoding_len = sdnv_decode (&(buf[i]), buf_len - i, &(bpq->fragments.frag_lenghts[j]))) == -1 ) |
|
614 return -1; |
|
615 i += decoding_len; |
|
616 } |
|
617 |
|
618 if (i != buf_len) |
|
619 return -1; |
|
620 |
|
621 return DTN_SUCCESS; |
|
622 } |
|
623 |
|
624 /******************************************************************************* |
|
625 * send response bpq: |
|
626 * build a response bundle containing the queried object file as the payload |
|
627 * attach a BPQ extension describing the query & object |
|
628 * and send to the source of the query |
|
629 * @return 0 if successful or -1 if error |
|
630 *******************************************************************************/ |
|
631 int |
|
632 send_response_bpq(dtn_handle_t * handle, |
|
633 dtn_reg_id_t regid, |
|
634 dtn_bundle_spec_t * query_bundle_spec, |
|
635 dtn_bpq_extension_block_data_t * query_bpq_block_data, |
|
636 const char * pathname, |
|
637 int bundle_expiry, |
|
638 dtn_bundle_priority_t priority, |
|
639 int delivery_options, |
|
640 int verbose) |
|
641 { |
|
642 int ret = 0; |
|
643 char buf [PATH_MAX]; |
|
644 size_t buf_len = 0; |
|
645 dtn_bundle_id_t response_bundle_id; |
|
646 dtn_bundle_spec_t response_bundle_spec; |
|
647 dtn_extension_block_t response_bpq_block; |
|
648 dtn_bpq_extension_block_data_t response_bpq_block_data; |
|
649 dtn_bundle_payload_t response_payload; |
|
650 u_int offsets[1]; |
|
651 u_int lengths[1]; |
|
652 |
|
653 memset(buf, 0, PATH_MAX); |
|
654 memset(&response_bundle_spec, 0, sizeof(dtn_bundle_spec_t)); |
|
655 memset(&response_bpq_block, 0, sizeof(dtn_extension_block_t)); |
|
656 memset(&response_bpq_block_data, 0, sizeof(dtn_bpq_extension_block_data_t)); |
|
657 memset(&response_payload, 0, sizeof(dtn_bundle_payload_t)); |
|
658 |
|
659 // set the payload |
|
660 dtn_set_payload(&response_payload, DTN_PAYLOAD_FILE, pathname, strlen(pathname)); |
|
661 |
|
662 // set the bpq block data |
|
663 response_bpq_block_data.kind = DTN_BPQ_KIND_RESPONSE; |
|
664 response_bpq_block_data.matching_rule = query_bpq_block_data->matching_rule; |
|
665 response_bpq_block_data.query.query_len = query_bpq_block_data->query.query_len; |
|
666 response_bpq_block_data.query.query_val = query_bpq_block_data->query.query_val; |
|
667 |
|
668 offsets[0] = 0; |
|
669 lengths[0] = 100; //todo: add payload length here |
|
670 |
|
671 response_bpq_block_data.fragments.num_frag_returned = 1; |
|
672 response_bpq_block_data.fragments.frag_offsets = offsets; |
|
673 response_bpq_block_data.fragments.frag_lenghts = lengths; |
|
674 |
|
675 if ( (buf_len = bpq_to_char_array(&response_bpq_block_data, buf, PATH_MAX)) == -1 ) { |
|
676 fprintf (stderr, "error encoding bpq: %d", buf_len); |
|
677 return -1; |
|
678 } |
|
679 |
|
680 // set the bpq block |
|
681 response_bpq_block.type = DTN_BPQ_BLOCK_TYPE; |
|
682 response_bpq_block.flags = DTN_BPQ_BLOCK_FLAGS; |
|
683 response_bpq_block.data.data_len = buf_len; |
|
684 response_bpq_block.data.data_val = buf; |
|
685 |
|
686 // copy dest src |
|
687 dtn_copy_eid(&response_bundle_spec.dest, &(query_bundle_spec->source)); |
|
688 dtn_copy_eid(&response_bundle_spec.source, &(query_bundle_spec->dest)); |
|
689 dtn_copy_eid(&response_bundle_spec.replyto, &(query_bundle_spec->dest)); |
|
690 |
|
691 // set the bundle spec dtn options |
|
692 response_bundle_spec.expiration = bundle_expiry; |
|
693 response_bundle_spec.dopts = delivery_options; |
|
694 response_bundle_spec.priority = priority; |
|
695 |
|
696 // set the bundle extension |
|
697 response_bundle_spec.blocks.blocks_len = 1; |
|
698 response_bundle_spec.blocks.blocks_val = &response_bpq_block; |
|
699 |
|
700 |
|
701 // send the bundle, bpq extension and empty payload |
|
702 ret = dtn_send(*handle, regid, &response_bundle_spec, &response_payload, &response_bundle_id); |
|
703 if (ret != DTN_SUCCESS) { |
|
704 fprintf(stderr, "error sending response bundle: %d (%s)\n", |
|
705 ret, dtn_strerror(dtn_errno(handle))); |
|
706 } else if (verbose) { |
|
707 fprintf(stdout, "bundle sent successfully: id %s,%llu.%llu\n", |
|
708 response_bundle_id.source.uri, |
|
709 response_bundle_id.creation_ts.secs, |
|
710 response_bundle_id.creation_ts.seqno); |
|
711 } |
|
712 return ret; |
|
713 } |
|
714 |
|
715 /******************************************************************************* |
|
716 * receive bpq: |
|
717 * listen for incoming bundles, |
|
718 * upon receipt of a new bundle ckeck for bpq expension block. |
|
719 * Attempt to match the query in the bpq. |
|
720 * If a match is found, send a response containing the queied object to the |
|
721 * source of the query. |
|
722 *******************************************************************************/ |
|
723 int |
|
724 receive_bpq(dtn_handle_t * handle, |
|
725 dtn_reg_id_t regid, |
|
726 const char * matching_filename, |
|
727 int count, |
|
728 int bundle_expiry, |
|
729 dtn_bundle_priority_t priority, |
|
730 int delivery_options, |
|
731 int verbose) |
|
732 { |
|
733 int i, j, num_blocks, found, ret = 0; |
|
734 char pathname[PATH_MAX]; |
|
735 dtn_bundle_spec_t bundle_spec; |
|
736 dtn_extension_block_t * bpq_blocks; |
|
737 dtn_bpq_extension_block_data_t bpq_block_data; |
|
738 dtn_bundle_payload_t payload; |
|
739 |
|
740 // start listening for bpq bundles |
|
741 for (i = 0; count == -1 || i < count; ++i) { |
|
742 found = 0; |
|
743 memset(&bundle_spec, 0, sizeof(dtn_bundle_spec_t)); |
|
744 memset(&bpq_block_data, 0, sizeof(dtn_bpq_extension_block_data_t)); |
|
745 memset(&payload, 0, sizeof(dtn_bundle_payload_t)); |
|
746 memset(pathname, 0, PATH_MAX); |
|
747 pathname[0] = '\0'; |
|
748 |
|
749 if (verbose) fprintf(stdout, "blocking waiting for dtn_recv\n"); |
|
750 ret = dtn_recv(*handle, |
|
751 &bundle_spec, |
|
752 DTN_PAYLOAD_FILE, |
|
753 &payload, |
|
754 DTN_TIMEOUT_INF); |
|
755 if (ret != DTN_SUCCESS) { |
|
756 fprintf(stderr, "error receiving bundle: %d (%s)\n", |
|
757 ret, dtn_strerror(dtn_errno(*handle))); |
|
758 return ret; |
|
759 } else if (verbose) { |
|
760 fprintf(stdout, "bundle %d received successfully: id %s,%llu.%llu\n", |
|
761 i, |
|
762 bundle_spec.source.uri, |
|
763 bundle_spec.creation_ts.secs, |
|
764 bundle_spec.creation_ts.seqno); |
|
765 } |
|
766 |
|
767 // extract the bpq |
|
768 num_blocks = bundle_spec.blocks.blocks_len; |
|
769 bpq_blocks = bundle_spec.blocks.blocks_val; |
|
770 |
|
771 for (j = 0; j < num_blocks; ++j) { |
|
772 if (bpq_blocks[j].type == DTN_BPQ_BLOCK_TYPE) { |
|
773 |
|
774 if (verbose) fprintf(stdout, "bundle %d contains a " |
|
775 "BPQ extension block\n", i); |
|
776 |
|
777 ret = char_array_to_bpq(bpq_blocks[j].data.data_val, |
|
778 bpq_blocks[j].data.data_len, |
|
779 &bpq_block_data); |
|
780 if (ret != DTN_SUCCESS) { |
|
781 fprintf(stderr, "error decoding query bundle: %d\n", ret); |
|
782 return ret; |
|
783 } |
|
784 |
|
785 match_bpq(&bpq_block_data, matching_filename, pathname, &found); |
|
786 break; |
|
787 } |
|
788 } |
|
789 |
|
790 // if found respond and continue listening |
|
791 if (found) { |
|
792 if (verbose) fprintf(stdout, "BPQ match found for query: %s\n", |
|
793 bpq_block_data.query.query_val); |
|
794 |
|
795 ret = send_response_bpq(handle, |
|
796 regid, |
|
797 &bundle_spec, |
|
798 &bpq_block_data, |
|
799 pathname, |
|
800 bundle_expiry, |
|
801 priority, |
|
802 delivery_options, |
|
803 verbose); |
|
804 if (ret != DTN_SUCCESS) { |
|
805 fprintf(stderr, "error sending response bundle: %d (%s)\n", |
|
806 ret, dtn_strerror(dtn_errno(*handle))); |
|
807 return ret; |
|
808 } |
|
809 } |
|
810 dtn_free_payload(&payload); |
|
811 } |
|
812 |
|
813 return ret; |
|
814 } |
|
815 |
|
816 /******************************************************************************* |
|
817 * main: |
|
818 * |
|
819 *******************************************************************************/ |
|
820 int |
|
821 main(int argc, char** argv) |
|
822 { |
|
823 dtn_endpoint_id_t local_eid; |
|
824 char local_eid_name[PATH_MAX]; |
|
825 char matching_filename[PATH_MAX]; |
|
826 int count = -1; //forever |
|
827 dtn_timeval_t bundle_expiry = 3600; //one hour |
|
828 dtn_reg_id_t regid = DTN_REGID_NONE; |
|
829 dtn_timeval_t reg_expiry = 30; |
|
830 int reg_fail_action = DTN_REG_DEFER; |
|
831 char reg_fail_script[PATH_MAX]; |
|
832 dtn_bundle_priority_t priority = COS_NORMAL; |
|
833 int delivery_options = 0; |
|
834 int verbose = 0; |
|
835 int err = 0; |
|
836 dtn_handle_t handle; |
|
837 |
|
838 parse_options(argc, argv, |
|
839 local_eid_name, |
|
840 matching_filename, |
|
841 &count, |
|
842 &bundle_expiry, |
|
843 ®id, |
|
844 ®_expiry, |
|
845 ®_fail_action, |
|
846 reg_fail_script, |
|
847 &priority, |
|
848 &delivery_options, |
|
849 &verbose); |
|
850 |
|
851 validate_options(local_eid_name, matching_filename); |
|
852 |
|
853 // open the ipc handle |
|
854 if (verbose) fprintf(stdout, "opening connection to dtn router...\n"); |
|
855 if ((err = dtn_open(&handle)) != DTN_SUCCESS) { |
|
856 fprintf(stderr, "fatal error opening dtn handle: %s\n", |
|
857 dtn_strerror(err)); |
|
858 exit(1); |
|
859 } |
|
860 if (verbose) fprintf(stdout, "opened connection to dtn router...\n"); |
|
861 |
|
862 // get dtn registration |
|
863 if (verbose) fprintf(stdout, "registering with dtn...\n"); |
|
864 register_with_dtn(handle, |
|
865 &local_eid, |
|
866 local_eid_name, |
|
867 ®id, |
|
868 reg_expiry, |
|
869 reg_fail_action, |
|
870 reg_fail_script); |
|
871 if (verbose) fprintf(stdout, "registered with dtn, " |
|
872 "regid: %d local eid: %s\n", |
|
873 regid, local_eid.uri); |
|
874 |
|
875 // get to work |
|
876 // this fn will likely never exit so the handle won't be closed... |
|
877 receive_bpq(&handle, |
|
878 regid, |
|
879 matching_filename, |
|
880 count, |
|
881 bundle_expiry, |
|
882 priority, |
|
883 delivery_options, |
|
884 verbose); |
|
885 |
|
886 // UNREACHABLE CODE if count = -1 ////////////////////////////////////////////// |
|
887 |
|
888 // close the ipc handle |
|
889 if (verbose) fprintf(stdout, "closing connection to dtn router...\n"); |
|
890 dtn_close(handle); |
|
891 if (verbose) fprintf(stdout, "closed connection to dtn router...\n"); |
|
892 |
|
893 return 0; |
|
894 |
|
895 // UNREACHABLE CODE if count = -1 ////////////////////////////////////////////// |
|
896 } |
|
897 |