ccnslurp.c

Go to the documentation of this file.
00001 /**
00002  * @file ccnslurp.c
00003  * Attempts pull everything in a branch of the ccn name hierarchy.
00004  *
00005  * A CCNx command-line utility.
00006  *
00007  * Copyright (C) 2008-2011 Palo Alto Research Center, Inc.
00008  *
00009  * This work is free software; you can redistribute it and/or modify it under
00010  * the terms of the GNU General Public License version 2 as published by the
00011  * Free Software Foundation.
00012  * This work is distributed in the hope that it will be useful, but WITHOUT ANY
00013  * WARRANTY; without even the implied warranty of MERCHANTABILITY or
00014  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
00015  * for more details. You should have received a copy of the GNU General Public
00016  * License along with this program; if not, write to the
00017  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00018  * Boston, MA 02110-1301, USA.
00019  */
00020 #include <errno.h>
00021 #include <stdio.h>
00022 #include <stdlib.h>
00023 #include <string.h>
00024 #include <unistd.h>
00025 #include <ccn/bloom.h>
00026 #include <ccn/ccn.h>
00027 #include <ccn/charbuf.h>
00028 #include <ccn/uri.h>
00029 
00030 struct upcalldata {
00031     int magic; /* 856372 */
00032     long *counter;
00033     unsigned warn;
00034     int flags;
00035     int n_excl;
00036     struct ccn_charbuf **excl; /* Array of n_excl items */
00037 };
00038 #define EXCLUDE_LOW 1
00039 #define EXCLUDE_HIGH 2
00040 
00041 /* Prototypes */
00042 static int namecompare(const void *a, const void *b);
00043 static struct upcalldata *get_my_data(struct ccn_closure *selfp);
00044 static void append_bf_all(struct ccn_charbuf *c);
00045 static int express_my_interest(struct ccn *h,
00046                                struct ccn_closure *selfp,
00047                                struct ccn_charbuf *name);
00048 static struct ccn_closure *split_my_excludes(struct ccn_closure *selfp);
00049 static enum ccn_upcall_res incoming_content(struct ccn_closure *selfp,
00050                                             enum ccn_upcall_kind kind,
00051                                             struct ccn_upcall_info *);
00052 static struct ccn_charbuf *ccn_charbuf_duplicate(struct ccn_charbuf *);
00053 static void answer_passive(struct ccn_charbuf *templ);
00054 
00055 /* Global */
00056 static struct ccn_charbuf *passive_templ;
00057 static struct ccn_charbuf *
00058 create_passive_templ(void)
00059 {
00060     struct ccn_charbuf *templ = ccn_charbuf_create();
00061     ccn_charbuf_append_tt(templ, CCN_DTAG_Interest, CCN_DTAG);
00062     ccn_charbuf_append_tt(templ, CCN_DTAG_Name, CCN_DTAG);
00063     ccn_charbuf_append_closer(templ); /* </Name> */
00064     answer_passive(templ);
00065     ccn_charbuf_append_closer(templ); /* </Interest> */
00066     return(templ);
00067 }
00068 
00069 /*
00070  * Comparison operator for sorting the excl list with qsort.
00071  * For convenience, the items in the excl array are
00072  * charbufs containing ccnb-encoded Names of one component each.
00073  * (This is not the most efficient representation.)
00074  */
00075 static int /* for qsort */
00076 namecompare(const void *a, const void *b)
00077 {
00078     const struct ccn_charbuf *aa = *(const struct ccn_charbuf **)a;
00079     const struct ccn_charbuf *bb = *(const struct ccn_charbuf **)b;
00080     int ans = ccn_compare_names(aa->buf, aa->length, bb->buf, bb->length);
00081     if (ans == 0)
00082         fprintf(stderr, "wassat? %d\n", __LINE__);
00083     return (ans);
00084 }
00085 
00086 static struct upcalldata *get_my_data(struct ccn_closure *selfp)
00087 {
00088     struct upcalldata *data = selfp->data;
00089     if (data->magic != 856372) abort();
00090     return(data);
00091 }
00092 
00093 /*
00094  * This upcall gets called for each piece of incoming content that
00095  * matches one of our interests.  We need to issue a new interest that
00096  * excludes another component at the current level, and perhaps also
00097  * and interest to start exploring the next level.  Thus if the matched
00098  * interest is
00099  *   /a/b/c exclude {d,e,f,i,j,k}
00100  * and we get
00101  *   /a/b/c/g/h
00102  * we would issue a new interest
00103  *   /a/b/c exclude {d,e,f,g,i,j,k}
00104  * to continue exploring the current level, plus a simple interest
00105  *   /a/b/c/g
00106  * to start exploring the next level as well.
00107  *
00108  * This does end up fetching each piece of content multiple times, once for
00109  * each level in the name. The repeated requests will be answered from the local
00110  * content store, though, and so should not generate extra network traffic.
00111  * There is a lot of unanswerable interest generated, though.  
00112  *
00113  * To prevent the interests from becoming too huge, we may need to split them.
00114  * Thus if the first new interest above were deemed too large, we could instead
00115  * issue the two interests
00116  *   /a/b/c exclude {d,e,f,g,*}
00117  *   /a/b/c exclude {*,g,i,j,k}
00118  * where * stands for a Bloom filter that excludes anything.  Note the
00119  * repetiton of g to ensure that these two interests cover disjoint portions
00120  * of the hierarchy. We need to keep track of the endpoint conditions
00121  * as well as the excluded set in our upcall data.
00122  * When a split happens, we need a new closure to track it, as we do when
00123  * we start exploring a new level.
00124  */
00125 static enum ccn_upcall_res
00126 incoming_content(
00127     struct ccn_closure *selfp,
00128     enum ccn_upcall_kind kind,
00129     struct ccn_upcall_info *info)
00130 {
00131     struct ccn_charbuf *c = NULL;
00132     struct ccn_charbuf *comp = NULL;
00133     struct ccn_charbuf *uri = NULL;
00134     const unsigned char *ccnb = NULL;
00135     size_t ccnb_size = 0;
00136     struct ccn_indexbuf *comps = NULL;
00137     int matched_comps = 0;
00138     int res;
00139     int i;
00140     struct upcalldata *data = get_my_data(selfp);
00141     
00142     if (kind == CCN_UPCALL_FINAL) {
00143         for (i = 0; i < data->n_excl; i++)
00144             ccn_charbuf_destroy(&(data->excl[i]));
00145         if (data->excl != NULL)
00146             free(data->excl);
00147         free(data);
00148         free(selfp);
00149         return(0);
00150     }
00151     if (kind == CCN_UPCALL_INTEREST_TIMED_OUT)
00152         return(0);
00153     if (kind != CCN_UPCALL_CONTENT && kind != CCN_UPCALL_CONTENT_UNVERIFIED && kind != CCN_UPCALL_CONTENT_BAD)
00154         abort();
00155 
00156     ccnb = info->content_ccnb;
00157     ccnb_size = info->pco->offset[CCN_PCO_E];
00158     comps = info->content_comps;
00159     matched_comps = info->pi->prefix_comps;
00160     c = ccn_charbuf_create();
00161     uri = ccn_charbuf_create();
00162         
00163     if (matched_comps + 1 > comps->n) {
00164         ccn_uri_append(c, ccnb, ccnb_size, 1);
00165         fprintf(stderr, "How did this happen?  %s\n", ccn_charbuf_as_string(uri));
00166         exit(1);
00167     }
00168     
00169     if (kind == CCN_UPCALL_CONTENT_BAD) {
00170         ccn_uri_append(uri, ccnb, ccnb_size, 1);
00171         fprintf(stderr, "*** VERIFICATION FAILURE *** %s\n", ccn_charbuf_as_string(uri));
00172         uri->length = 0;
00173     }
00174     
00175     data->counter[0]++; /* Tell main that something new came in */
00176 
00177     /* Recover the same prefix as before */
00178     ccn_name_init(c);
00179     ccn_name_append_components(c, ccnb, comps->buf[0], comps->buf[matched_comps]);
00180     
00181     comp = ccn_charbuf_create();
00182     ccn_name_init(comp);
00183     if (matched_comps + 1 == comps->n) {
00184         /* Reconstruct the implicit ContentObject digest component */
00185         ccn_digest_ContentObject(ccnb, info->pco);
00186         ccn_name_append(comp, info->pco->digest, info->pco->digest_bytes);
00187     }
00188     else {
00189         ccn_name_append_components(comp, ccnb,
00190                                    comps->buf[matched_comps],
00191                                    comps->buf[matched_comps + 1]);
00192     }
00193     data->excl = realloc(data->excl, (data->n_excl + 1) * sizeof(data->excl[0]));
00194     data->excl[data->n_excl++] = comp;
00195     comp = NULL;
00196     qsort(data->excl, data->n_excl, sizeof(data->excl[0]), &namecompare);
00197     res = express_my_interest(info->h, selfp, c);
00198     if (res == -1) {
00199         struct ccn_closure *high = split_my_excludes(selfp);
00200         if (high == NULL) abort();
00201         express_my_interest(info->h, selfp, c);
00202         express_my_interest(info->h, high, c);
00203     }
00204     /* Explore the next level, if there is one. */
00205     if (matched_comps + 2 < comps->n) {
00206         struct upcalldata *newdat = NULL;
00207         struct ccn_closure *cl;
00208         newdat = calloc(1, sizeof(*newdat));
00209         newdat->magic = 856372;
00210         newdat->warn = 1492;
00211         newdat->counter = data->counter;
00212         newdat->flags = 0;
00213         newdat->n_excl = 0;
00214         newdat->excl = NULL;
00215         cl = calloc(1, sizeof(*cl));
00216         cl->p = &incoming_content;
00217         cl->data = newdat;
00218         ccn_name_init(c);
00219         ccn_name_append_components(c, ccnb,
00220                                    comps->buf[0],
00221                                    comps->buf[matched_comps + 1]);
00222         ccn_express_interest(info->h, c, cl, passive_templ);
00223     }
00224     else {
00225         res = ccn_uri_append(uri, info->content_ccnb, info->pco->offset[CCN_PCO_E], 1);
00226         if (res < 0)
00227             fprintf(stderr, "*** Error: ccnslurp line %d res=%d\n", __LINE__, res);
00228         else
00229             printf("%s\n", ccn_charbuf_as_string(uri));
00230     }
00231     ccn_charbuf_destroy(&c);
00232     ccn_charbuf_destroy(&uri);
00233     return(0);
00234 }
00235 
00236 /*
00237  * Append AnswerOriginKind=1 to partially constructed Interest, meaning
00238  * do not generate new content.
00239  */
00240 static void
00241 answer_passive(struct ccn_charbuf *templ)
00242 {
00243     ccn_charbuf_append_tt(templ, CCN_DTAG_AnswerOriginKind, CCN_DTAG);
00244     ccn_charbuf_append_tt(templ, 1, CCN_UDATA);
00245     ccn_charbuf_append(templ, "1", 1);
00246     ccn_charbuf_append_closer(templ); /* </AnswerOriginKind> */
00247 }
00248 
00249 /*
00250  * Construct and send a new interest that uses the exclusion list.
00251  * Return -1 if not sent because of packet size, 0 for success.
00252  */
00253 static int
00254 express_my_interest(struct ccn *h,
00255                     struct ccn_closure *selfp,
00256                     struct ccn_charbuf *name)
00257 {
00258     int ans;
00259     struct ccn_charbuf *templ = NULL;
00260     int i;
00261     struct upcalldata *data = get_my_data(selfp);
00262 
00263     templ = ccn_charbuf_create();
00264     ccn_charbuf_append_tt(templ, CCN_DTAG_Interest, CCN_DTAG);
00265     ccn_charbuf_append_tt(templ, CCN_DTAG_Name, CCN_DTAG);
00266     ccn_charbuf_append_closer(templ); /* </Name> */
00267     ccn_charbuf_append_tt(templ, CCN_DTAG_Exclude, CCN_DTAG);
00268     if ((data->flags & EXCLUDE_LOW) != 0)
00269         append_bf_all(templ);
00270     for (i = 0; i < data->n_excl; i++) {
00271         struct ccn_charbuf *comp = data->excl[i];
00272         if (comp->length < 4) abort();
00273         ccn_charbuf_append(templ, comp->buf + 1, comp->length - 2);
00274     }
00275     if ((data->flags & EXCLUDE_HIGH) != 0)
00276         append_bf_all(templ);
00277     ccn_charbuf_append_closer(templ); /* </Exclude> */
00278     answer_passive(templ);
00279     ccn_charbuf_append_closer(templ); /* </Interest> */
00280     if (templ->length + name->length > data->warn + 2) {
00281         fprintf(stderr, "*** Interest packet is %d bytes\n", (int)templ->length);
00282         data->warn = data->warn * 8 / 5;
00283     }
00284     if (templ->length + name->length > 1450 && data->n_excl > 3)
00285         ans = -1;
00286     else {
00287         ccn_express_interest(h, name, selfp, templ);
00288         ans = 0;
00289     }
00290     ccn_charbuf_destroy(&templ);
00291     return(ans);
00292 }
00293 
00294 /*
00295  * Build a new closure to handle the high half of the excludes, and modify the
00296  * old closure to handle the low half.
00297  */
00298 static struct ccn_closure *
00299 split_my_excludes(struct ccn_closure *selfp)
00300 {
00301     int i;
00302     int m;
00303     struct upcalldata *newdat = NULL;
00304     struct ccn_closure *cl;
00305     struct upcalldata *data = get_my_data(selfp);
00306     
00307     if (data->n_excl < 3)
00308         return NULL;
00309     m = data->n_excl / 2;
00310     newdat = calloc(1, sizeof(*newdat));
00311     newdat->magic = 856372;
00312     newdat->warn = 1492;
00313     newdat->counter = data->counter;
00314     newdat->n_excl = data->n_excl - m;
00315     newdat->excl = calloc(newdat->n_excl, sizeof(newdat->excl[0]));
00316     if (newdat->excl == NULL) {
00317         free(newdat);
00318         return(NULL);
00319     }
00320     newdat->excl[0] = ccn_charbuf_duplicate(data->excl[m]);
00321     newdat->flags = data->flags | EXCLUDE_LOW;
00322     for (i = 1; i < newdat->n_excl; i++) {
00323         newdat->excl[i] = data->excl[m + i];
00324         data->excl[m + i] = NULL;
00325     }
00326     data->n_excl = m + 1;
00327     data->flags |= EXCLUDE_HIGH;
00328     cl = calloc(1, sizeof(*cl));
00329     cl->p = &incoming_content;
00330     cl->data = newdat;
00331     return(cl);
00332 }
00333 
00334 /*
00335  * This appends a tagged, valid, fully-saturated Bloom filter, useful for
00336  * excluding everything between two 'fenceposts' in an Exclude construct.
00337  */
00338 static void
00339 append_bf_all(struct ccn_charbuf *c)
00340 {
00341     unsigned char bf_all[9] = { 3, 1, 'A', 0, 0, 0, 0, 0, 0xFF };
00342     const struct ccn_bloom_wire *b = ccn_bloom_validate_wire(bf_all, sizeof(bf_all));
00343     if (b == NULL) abort();
00344     ccn_charbuf_append_tt(c, CCN_DTAG_Bloom, CCN_DTAG);
00345     ccn_charbuf_append_tt(c, sizeof(bf_all), CCN_BLOB);
00346     ccn_charbuf_append(c, bf_all, sizeof(bf_all));
00347     ccn_charbuf_append_closer(c);
00348 }
00349 
00350 static struct ccn_charbuf *
00351 ccn_charbuf_duplicate(struct ccn_charbuf *c)
00352 {
00353     struct ccn_charbuf *ans = ccn_charbuf_create();
00354     ccn_charbuf_append(ans, c->buf, c->length);
00355     return(ans);
00356 }
00357 
00358 void
00359 usage(const char *prog)
00360 {
00361     fprintf(stderr,
00362             "%s [-h] URI\n"
00363             " Attempt to pull everything under given URI\n"
00364             " and print out names of found content to stdout\n"
00365             " -h Print this usage message.\n", prog);
00366     exit(1);
00367 }
00368 
00369 int
00370 main(int argc, char **argv)
00371 {
00372     const char *progname = argv[0];
00373     struct ccn *ccn = NULL;
00374     struct ccn_charbuf *c = NULL;
00375     struct upcalldata *data = NULL;
00376     int opt;
00377     int i;
00378     long n;
00379     int res;
00380     long counter = 0;
00381     struct ccn_closure *cl = NULL;
00382     
00383     while ((opt = getopt(argc, argv, "h")) != -1) {
00384         switch (opt) {
00385             case 'h':
00386             default:
00387                 usage(argv[0]);
00388         }
00389     }
00390 
00391     if (argv[optind] == NULL || argv[optind + 1] != NULL)
00392         usage(argv[0]);
00393 
00394     passive_templ = create_passive_templ();
00395     c = ccn_charbuf_create();
00396     res = ccn_name_from_uri(c, argv[optind]);
00397     if (res < 0) {
00398         fprintf(stderr, "%s: bad ccn URI: %s\n", progname, argv[optind]);
00399         exit(1);
00400     }
00401         
00402     ccn = ccn_create();
00403     if (ccn_connect(ccn, NULL) == -1) {
00404         perror("Could not connect to ccnd");
00405         exit(1);
00406     }
00407     
00408     data = calloc(1, sizeof(*data));
00409     data->magic = 856372;
00410     data->warn = 1492;
00411     data->counter = &counter;
00412     cl = calloc(1, sizeof(*cl));
00413     cl->p = &incoming_content;
00414     cl->data = data;
00415     ccn_express_interest(ccn, c, cl, passive_templ);
00416     cl = NULL;
00417     data = NULL;
00418     ccn_charbuf_destroy(&c);
00419     for (i = 0;; i++) {
00420         n = counter;
00421         ccn_run(ccn, 1000); /* stop if we run dry for 1 sec */
00422         fflush(stdout);
00423         if (counter == n)
00424             break;
00425     }
00426     ccn_destroy(&ccn);
00427     ccn_charbuf_destroy(&passive_templ);
00428     exit(0);
00429 }

Generated on Thu Feb 16 00:43:59 2012 for Content-Centric Networking in C by  doxygen 1.5.6