/* * Build a new closure to handle the high half of the excludes, and modify the * old closure to handle the low half. */ static struct ccn_closure * split_my_excludes(struct ccn_closure *selfp) { int i; int m; struct ccn_traversal *newdat = NULL; struct ccn_closure *cl; struct ccn_traversal *data = get_my_data(selfp); if (data->n_excl < 3) return NULL; m = data->n_excl / 2; newdat = calloc(1, sizeof(*newdat)); newdat->magic = 68955871; newdat->warn = 1492; newdat->counter = data->counter; newdat->n_excl = data->n_excl - m; newdat->excl = calloc(newdat->n_excl, sizeof(newdat->excl[0])); if (newdat->excl == NULL) { free(newdat); return(NULL); } newdat->excl[0] = ccn_charbuf_duplicate(data->excl[m]); newdat->flags = data->flags | EXCLUDE_LOW; for (i = 1; i < newdat->n_excl; i++) { newdat->excl[i] = data->excl[m + i]; data->excl[m + i] = NULL; } data->n_excl = m + 1; data->flags |= EXCLUDE_HIGH; cl = calloc(1, sizeof(*cl)); cl->p = &incoming_content; cl->data = newdat; return(cl); }
/* * Construct and send a new interest that uses the exclusion list. * Return -1 if not sent because of packet size, 0 for success. */ static int express_my_interest(struct ccn *h, struct ccn_closure *selfp, struct ccn_charbuf *name) { int ans; struct ccn_charbuf *templ = NULL; int i; struct ccn_traversal *data = get_my_data(selfp); templ = ccn_charbuf_create(); ccnb_element_begin(templ, CCN_DTAG_Interest); ccnb_element_begin(templ, CCN_DTAG_Name); ccnb_element_end(templ); /* </Name> */ if (data->n_excl != 0) { ccnb_element_begin(templ, CCN_DTAG_Exclude); if ((data->flags & EXCLUDE_LOW) != 0) append_Any_filter(templ); for (i = 0; i < data->n_excl; i++) { struct ccn_charbuf *comp = data->excl[i]; if (comp->length < 4) abort(); ccn_charbuf_append(templ, comp->buf + 1, comp->length - 2); } if ((data->flags & EXCLUDE_HIGH) != 0) append_Any_filter(templ); ccnb_element_end(templ); /* </Exclude> */ } answer_passive(templ, (data->flags & ALLOW_STALE) != 0); if ((data->flags & LOCAL_SCOPE) != 0) local_scope(templ); ccnb_element_end(templ); /* </Interest> */ if (templ->length + name->length > data->warn + 2) { fprintf(stderr, "*** Interest packet is %d bytes\n", (int)templ->length); data->warn = data->warn * 8 / 5; } if (templ->length + name->length > 1450 && data->n_excl > 3) ans = -1; else { ccn_express_interest(h, name, selfp, templ); ans = 0; } ccn_charbuf_destroy(&templ); return(ans); }
/* * Construct and send a new interest that uses the exclusion list. * Return -1 if not sent because of packet size, 0 for success. */ static int express_my_interest(struct ccn *h, struct ccn_closure *selfp, struct ccn_charbuf *name) { int ans; struct ccn_charbuf *templ = NULL; int i; struct upcalldata *data = get_my_data(selfp); templ = ccn_charbuf_create(); ccn_charbuf_append_tt(templ, CCN_DTAG_Interest, CCN_DTAG); ccn_charbuf_append_tt(templ, CCN_DTAG_Name, CCN_DTAG); ccn_charbuf_append_closer(templ); /* </Name> */ ccn_charbuf_append_tt(templ, CCN_DTAG_Exclude, CCN_DTAG); if ((data->flags & EXCLUDE_LOW) != 0) append_bf_all(templ); for (i = 0; i < data->n_excl; i++) { struct ccn_charbuf *comp = data->excl[i]; if (comp->length < 4) abort(); ccn_charbuf_append(templ, comp->buf + 1, comp->length - 2); } if ((data->flags & EXCLUDE_HIGH) != 0) append_bf_all(templ); ccn_charbuf_append_closer(templ); /* </Exclude> */ answer_passive(templ); ccn_charbuf_append_closer(templ); /* </Interest> */ if (templ->length + name->length > data->warn + 2) { fprintf(stderr, "*** Interest packet is %d bytes\n", (int)templ->length); data->warn = data->warn * 8 / 5; } if (templ->length + name->length > 1450 && data->n_excl > 3) ans = -1; else { ccn_express_interest(h, name, selfp, templ); ans = 0; } ccn_charbuf_destroy(&templ); return(ans); }
/* * This upcall gets called for each piece of incoming content that * matches one of our interests. We need to issue a new interest that * excludes another component at the current level, and perhaps also * and interest to start exploring the next level. Thus if the matched * interest is * /a/b/c exclude {d,e,f,i,j,k} * and we get * /a/b/c/g/h * we would issue a new interest * /a/b/c exclude {d,e,f,g,i,j,k} * to continue exploring the current level, plus a simple interest * /a/b/c/g * to start exploring the next level as well. * * This does end up fetching each piece of content multiple times, once for * each level in the name. The repeated requests will be answered from the local * content store, though, and so should not generate extra network traffic. * There is a lot of unanswerable interest generated, though. * * To prevent the interests from becoming too huge, we may need to split them. * Thus if the first new interest above were deemed too large, we could instead * issue the two interests * /a/b/c exclude {d,e,f,g,*} * /a/b/c exclude {*,g,i,j,k} * where * stands for a Bloom filter that excludes anything. Note the * repetition of g to ensure that these two interests cover disjoint portions * of the hierarchy. We need to keep track of the endpoint conditions * as well as the excluded set in our upcall data. * When a split happens, we need a new closure to track it, as we do when * we start exploring a new level. */ static enum ccn_upcall_res incoming_content( struct ccn_closure *selfp, enum ccn_upcall_kind kind, struct ccn_upcall_info *info) { struct ccn_charbuf *c = NULL; struct ccn_charbuf *comp = NULL; struct ccn_charbuf *uri = NULL; const unsigned char *ccnb = NULL; size_t ccnb_size = 0; struct ccn_indexbuf *comps = NULL; int matched_comps = 0; int res; int i; struct ccn_traversal *data = get_my_data(selfp); if (kind == CCN_UPCALL_FINAL) { for (i = 0; i < data->n_excl; i++) ccn_charbuf_destroy(&(data->excl[i])); if (data->excl != NULL) free(data->excl); free(data); free(selfp); return(0); } if (kind == CCN_UPCALL_INTEREST_TIMED_OUT) return(0); if (kind == CCN_UPCALL_CONTENT_BAD) return(0); if (kind == CCN_UPCALL_CONTENT_UNVERIFIED) { if ((data->flags & MUST_VERIFY) != 0) return(CCN_UPCALL_RESULT_VERIFY); } if (kind != CCN_UPCALL_CONTENT && kind != CCN_UPCALL_CONTENT_UNVERIFIED) abort(); ccnb = info->content_ccnb; ccnb_size = info->pco->offset[CCN_PCO_E]; comps = info->content_comps; matched_comps = info->pi->prefix_comps; c = ccn_charbuf_create(); uri = ccn_charbuf_create(); if (matched_comps + 1 > comps->n) { ccn_uri_append(c, ccnb, ccnb_size, 1); fprintf(stderr, "How did this happen? %s\n", ccn_charbuf_as_string(uri)); exit(1); } data->counter[0]++; /* Tell main that something new came in */ /* Recover the same prefix as before */ ccn_name_init(c); ccn_name_append_components(c, ccnb, comps->buf[0], comps->buf[matched_comps]); comp = ccn_charbuf_create(); ccn_name_init(comp); if (matched_comps + 1 == comps->n) { /* Reconstruct the implicit content digest component */ ccn_digest_ContentObject(ccnb, info->pco); ccn_name_append(comp, info->pco->digest, info->pco->digest_bytes); } else { ccn_name_append_components(comp, ccnb, comps->buf[matched_comps], comps->buf[matched_comps + 1]); } data->excl = realloc(data->excl, (data->n_excl + 1) * sizeof(data->excl[0])); data->excl[data->n_excl++] = comp; comp = NULL; qsort(data->excl, data->n_excl, sizeof(data->excl[0]), &namecompare); res = express_my_interest(info->h, selfp, c); if (res == -1) { struct ccn_closure *high = split_my_excludes(selfp); if (high == NULL) abort(); express_my_interest(info->h, selfp, c); express_my_interest(info->h, high, c); } /* Explore the next level, if there is one. */ if (matched_comps + 2 < comps->n) { struct ccn_traversal *newdat = NULL; struct ccn_closure *cl; newdat = calloc(1, sizeof(*newdat)); newdat->magic = 68955871; newdat->warn = 1492; newdat->counter = data->counter; newdat->flags = data->flags & ~(EXCLUDE_LOW | EXCLUDE_HIGH); newdat->n_excl = 0; newdat->excl = NULL; cl = calloc(1, sizeof(*cl)); cl->p = &incoming_content; cl->data = newdat; ccn_name_init(c); ccn_name_append_components(c, ccnb, comps->buf[0], comps->buf[matched_comps + 1]); express_my_interest(info->h, cl, c); } else { res = ccn_uri_append(uri, info->content_ccnb, info->pco->offset[CCN_PCO_E], 1); if (res < 0) fprintf(stderr, "*** Error: ccn_traverse line %d res=%d\n", __LINE__, res); else printf("%s\n", ccn_charbuf_as_string(uri)); } ccn_charbuf_destroy(&c); ccn_charbuf_destroy(&uri); return(0); }