PUBLIC struct content_entry * r_store_next_child_at_level(struct ccnr_handle *h, struct content_entry *content, int level) { struct content_entry *next = NULL; struct ccn_charbuf *name; struct ccn_charbuf *flatname = NULL; int res; if (content == NULL) return(NULL); name = ccn_charbuf_create(); ccn_name_init(name); res = ccn_name_append_flatname(name, content->flatname->buf, content->flatname->length, 0, level + 1); if (res < level) goto Bail; if (res == level) res = ccn_name_append(name, NULL, 0); else if (res == level + 1) res = ccn_name_next_sibling(name); // XXX - would be nice to have a flatname version of this if (res < 0) goto Bail; if (CCNSHOULDLOG(h, LM_8, CCNL_FINER)) ccnr_debug_ccnb(h, __LINE__, "child_successor", NULL, name->buf, name->length); flatname = ccn_charbuf_create(); ccn_flatname_from_ccnb(flatname, name->buf, name->length); next = r_store_look(h, flatname->buf, flatname->length); if (next == content) { // XXX - I think this case should not occur, but just in case, avoid a loop. ccnr_debug_content(h, __LINE__, "urp", NULL, next); next = NULL; } Bail: ccn_charbuf_destroy(&name); ccn_charbuf_destroy(&flatname); return(next); }
/** * Consume matching interests * given a nameprefix_entry and a piece of content. * * If fdholder is not NULL, pay attention only to interests from that fdholder. * It is allowed to pass NULL for pc, but if you have a (valid) one it * will avoid a re-parse. * @returns number of matches found. */ PUBLIC int r_match_consume_matching_interests(struct ccnr_handle *h, struct nameprefix_entry *npe, struct content_entry *content, struct ccn_parsed_ContentObject *pc, struct fdholder *fdholder) { int matches = 0; struct propagating_entry *head; struct propagating_entry *next; struct propagating_entry *p; const unsigned char *content_msg; size_t content_size; struct fdholder *f; head = &npe->pe_head; // XXX - i do not think this is called in practice content_msg = r_store_content_base(h, content); content_size = r_store_content_size(h, content); f = fdholder; for (p = head->next; p != head; p = next) { next = p->next; if (p->interest_msg != NULL && ((fdholder == NULL && (f = r_io_fdholder_from_fd(h, p->filedesc)) != NULL) || (fdholder != NULL && p->filedesc == fdholder->filedesc))) { if (ccn_content_matches_interest(content_msg, content_size, 1, pc, p->interest_msg, p->size, NULL)) { r_sendq_face_send_queue_insert(h, f, content); if (CCNSHOULDLOG(h, (32 | 8), CCNL_FINE)) ccnr_debug_ccnb(h, __LINE__, "consume", f, p->interest_msg, p->size); matches += 1; r_match_consume_interest(h, p); } } } return(matches); }
/** * Common interest handler */ PUBLIC enum ccn_upcall_res ccnr_answer_req(struct ccn_closure *selfp, enum ccn_upcall_kind kind, struct ccn_upcall_info *info) { struct ccn_charbuf *msg = NULL; struct ccn_charbuf *name = NULL; struct ccn_charbuf *keylocator = NULL; struct ccn_charbuf *signed_info = NULL; struct ccn_charbuf *reply_body = NULL; struct ccnr_handle *ccnr = NULL; int res = 0; int morecomps = 0; const unsigned char *final_comp = NULL; size_t final_size = 0; switch (kind) { case CCN_UPCALL_FINAL: free(selfp); return(CCN_UPCALL_RESULT_OK); case CCN_UPCALL_INTEREST: break; case CCN_UPCALL_CONSUMED_INTEREST: return(CCN_UPCALL_RESULT_OK); default: return(CCN_UPCALL_RESULT_ERR); } ccnr = (struct ccnr_handle *)selfp->data; if (CCNSHOULDLOG(ccnr, LM_128, CCNL_FINE)) ccnr_debug_ccnb(ccnr, __LINE__, "ccnr_answer_req", NULL, info->interest_ccnb, info->pi->offset[CCN_PI_E]); morecomps = selfp->intdata & MORECOMPS_MASK; if ((info->pi->answerfrom & CCN_AOK_NEW) == 0 && selfp->intdata != OP_SERVICE) return(CCN_UPCALL_RESULT_OK); if (info->matched_comps >= info->interest_comps->n) goto Bail; if ((selfp->intdata & OPER_MASK) != OP_SERVICE && info->pi->prefix_comps != info->matched_comps + morecomps) goto Bail; if (morecomps == 1) { res = ccn_name_comp_get(info->interest_ccnb, info->interest_comps, info->matched_comps, &final_comp, &final_size); if (res < 0) goto Bail; } if ((selfp->intdata & MUST_VERIFY) != 0) { struct ccn_parsed_ContentObject pco = {0}; // XXX - probably should check for message origin BEFORE verify res = ccn_parse_ContentObject(final_comp, final_size, &pco, NULL); if (res < 0) { ccnr_debug_ccnb(ccnr, __LINE__, "co_parse_failed", NULL, info->interest_ccnb, info->pi->offset[CCN_PI_E]); goto Bail; } res = ccn_verify_content(info->h, final_comp, &pco); if (res != 0) { ccnr_debug_ccnb(ccnr, __LINE__, "co_verify_failed", NULL, info->interest_ccnb, info->pi->offset[CCN_PI_E]); goto Bail; } } switch (selfp->intdata & OPER_MASK) { case OP_SERVICE: if (ccnr->service_ccnb == NULL) ccnr->service_ccnb = ccnr_init_service_ccnb(ccnr, info->h, CCNRID_LOCAL_URI, 600); if (ccn_content_matches_interest( ccnr->service_ccnb->buf, ccnr->service_ccnb->length, 1, NULL, info->interest_ccnb, info->pi->offset[CCN_PI_E], info->pi )) { ccn_put(info->h, ccnr->service_ccnb->buf, ccnr->service_ccnb->length); res = CCN_UPCALL_RESULT_INTEREST_CONSUMED; goto Finish; } // XXX this needs refactoring. if (ccnr->neighbor_ccnb == NULL) ccnr->neighbor_ccnb = ccnr_init_service_ccnb(ccnr, info->h, CCNRID_NEIGHBOR_URI, 5); if (ccn_content_matches_interest( ccnr->neighbor_ccnb->buf, ccnr->neighbor_ccnb->length, 1, NULL, info->interest_ccnb, info->pi->offset[CCN_PI_E], info->pi )) { ccn_put(info->h, ccnr->neighbor_ccnb->buf, ccnr->neighbor_ccnb->length); res = CCN_UPCALL_RESULT_INTEREST_CONSUMED; goto Finish; } if (ccn_content_matches_interest( ccnr->policy_link_cob->buf, ccnr->policy_link_cob->length, 1, NULL, info->interest_ccnb, info->pi->offset[CCN_PI_E], info->pi )) { ccn_put(info->h, ccnr->policy_link_cob->buf, ccnr->policy_link_cob->length); res = CCN_UPCALL_RESULT_INTEREST_CONSUMED; goto Finish; } goto Bail; break; default: // No other OP_xxx are supported here goto Bail; } Bail: res = CCN_UPCALL_RESULT_ERR; Finish: ccn_charbuf_destroy(&msg); ccn_charbuf_destroy(&name); ccn_charbuf_destroy(&keylocator); ccn_charbuf_destroy(&reply_body); ccn_charbuf_destroy(&signed_info); return(res); }
PUBLIC struct content_entry * r_store_lookup(struct ccnr_handle *h, const unsigned char *msg, const struct ccn_parsed_interest *pi, struct ccn_indexbuf *comps) { struct content_entry *content = NULL; struct ccn_btree_node *leaf = NULL; ccnr_cookie last_match = 0; ccnr_accession last_match_acc = CCNR_NULL_ACCESSION; struct ccn_charbuf *scratch = NULL; size_t size = pi->offset[CCN_PI_E]; int ndx; int res; int try; content = r_store_find_first_match_candidate(h, msg, pi); if (content != NULL && CCNSHOULDLOG(h, LM_8, CCNL_FINER)) ccnr_debug_content(h, __LINE__, "first_candidate", NULL, content); if (content != NULL && !r_store_content_matches_interest_prefix(h, content, msg, size)) { if (CCNSHOULDLOG(h, LM_8, CCNL_FINER)) ccnr_debug_ccnb(h, __LINE__, "prefix_mismatch", NULL, msg, size); content = NULL; } scratch = ccn_charbuf_create(); for (try = 0; content != NULL; try++) { res = ccn_btree_lookup(h->btree, content->flatname->buf, content->flatname->length, &leaf); if (CCN_BT_SRCH_FOUND(res) == 0) { ccnr_debug_content(h, __LINE__, "impossible", NULL, content); content = NULL; break; } ndx = CCN_BT_SRCH_INDEX(res); res = ccn_btree_match_interest(leaf, ndx, msg, pi, scratch); if (res == -1) { ccnr_debug_ccnb(h, __LINE__, "match_error", NULL, msg, size); content = NULL; break; } if (res == 1) { if ((pi->orderpref & 1) == 0) // XXX - should be symbolic break; last_match = content->cookie; last_match_acc = content->accession; content = r_store_next_child_at_level(h, content, comps->n - 1); } else content = r_store_content_next(h, content); if (content != NULL && !r_store_content_matches_interest_prefix(h, content, msg, size)) content = NULL; } if (last_match != 0) { content = r_store_content_from_cookie(h, last_match); if (content == NULL) content = r_store_content_from_accession(h, last_match_acc); } ccn_charbuf_destroy(&scratch); return(content); } /** * Find the first content handle that matches the prefix given by the namish, * which may be a Name, Interest, ContentObject, ... * * Does not check the other parts of namish, in particular, does not generate * the digest component of a ContentObject. */ PUBLIC struct content_entry * r_store_lookup_ccnb(struct ccnr_handle *h, const unsigned char *namish, size_t size) { struct content_entry *content = NULL; struct ccn_charbuf *flatname = NULL; int res; flatname = ccn_charbuf_create(); if (flatname == NULL) goto Bail; res = ccn_flatname_from_ccnb(flatname, namish, size); if (res < 0) goto Bail; content = r_store_look(h, flatname->buf, flatname->length); if (content != NULL) { res = ccn_flatname_charbuf_compare(flatname, content->flatname); if (res == 0 || res == -9999) { /* prefix matches */ } else content = NULL; } Bail: ccn_charbuf_destroy(&flatname); return(content); } /** * Mark content as stale */ PUBLIC void r_store_mark_stale(struct ccnr_handle *h, struct content_entry *content) { ccnr_cookie cookie = content->cookie; if ((content->flags & CCN_CONTENT_ENTRY_STALE) != 0) return; if (CCNSHOULDLOG(h, LM_4, CCNL_FINE)) ccnr_debug_content(h, __LINE__, "stale", NULL, content); content->flags |= CCN_CONTENT_ENTRY_STALE; h->n_stale++; if (cookie < h->min_stale) h->min_stale = cookie; if (cookie > h->max_stale) h->max_stale = cookie; } /** * Scheduled event that makes content stale when its FreshnessSeconds * has expired. */ static int expire_content(struct ccn_schedule *sched, void *clienth, struct ccn_scheduled_event *ev, int flags) { struct ccnr_handle *h = clienth; ccnr_cookie cookie = ev->evint; struct content_entry *content = NULL; if ((flags & CCN_SCHEDULE_CANCEL) != 0) return(0); content = r_store_content_from_cookie(h, cookie); if (content != NULL) r_store_mark_stale(h, content); return(0); } /** * Schedules content expiration based on its FreshnessSeconds. * */ PUBLIC void r_store_set_content_timer(struct ccnr_handle *h, struct content_entry *content, struct ccn_parsed_ContentObject *pco) { int seconds = 0; int microseconds = 0; size_t start = pco->offset[CCN_PCO_B_FreshnessSeconds]; size_t stop = pco->offset[CCN_PCO_E_FreshnessSeconds]; const unsigned char *content_msg = NULL; if (start == stop) return; content_msg = r_store_content_base(h, content); seconds = ccn_fetch_tagged_nonNegativeInteger( CCN_DTAG_FreshnessSeconds, content_msg, start, stop); if (seconds <= 0) return; if (seconds > ((1U<<31) / 1000000)) { ccnr_debug_content(h, __LINE__, "FreshnessSeconds_too_large", NULL, content); return; } microseconds = seconds * 1000000; ccn_schedule_event(h->sched, microseconds, &expire_content, NULL, content->cookie); }
PUBLIC struct content_entry * r_store_find_first_match_candidate(struct ccnr_handle *h, const unsigned char *interest_msg, const struct ccn_parsed_interest *pi) { int res; size_t start = pi->offset[CCN_PI_B_Name]; size_t end = pi->offset[CCN_PI_E_Name]; struct ccn_charbuf *namebuf = NULL; struct ccn_charbuf *flatname = NULL; struct content_entry *content = NULL; flatname = ccn_charbuf_create(); ccn_flatname_from_ccnb(flatname, interest_msg, pi->offset[CCN_PI_E]); if (pi->offset[CCN_PI_B_Exclude] < pi->offset[CCN_PI_E_Exclude]) { /* Check for <Exclude><Any/><Component>... fast case */ struct ccn_buf_decoder decoder; struct ccn_buf_decoder *d; size_t ex1start; size_t ex1end; d = ccn_buf_decoder_start(&decoder, interest_msg + pi->offset[CCN_PI_B_Exclude], pi->offset[CCN_PI_E_Exclude] - pi->offset[CCN_PI_B_Exclude]); ccn_buf_advance(d); if (ccn_buf_match_dtag(d, CCN_DTAG_Any)) { ccn_buf_advance(d); ccn_buf_check_close(d); if (ccn_buf_match_dtag(d, CCN_DTAG_Component)) { ex1start = pi->offset[CCN_PI_B_Exclude] + d->decoder.token_index; ccn_buf_advance_past_element(d); ex1end = pi->offset[CCN_PI_B_Exclude] + d->decoder.token_index; if (d->decoder.state >= 0) { namebuf = ccn_charbuf_create(); ccn_charbuf_append(namebuf, interest_msg + start, end - start); namebuf->length--; ccn_charbuf_append(namebuf, interest_msg + ex1start, ex1end - ex1start); ccn_charbuf_append_closer(namebuf); res = ccn_flatname_append_from_ccnb(flatname, interest_msg + ex1start, ex1end - ex1start, 0, 1); if (res != 1) ccnr_debug_ccnb(h, __LINE__, "fastex_bug", NULL, namebuf->buf, namebuf->length); if (CCNSHOULDLOG(h, LM_8, CCNL_FINER)) ccnr_debug_ccnb(h, __LINE__, "fastex", NULL, namebuf->buf, namebuf->length); } } } } content = r_store_look(h, flatname->buf, flatname->length); ccn_charbuf_destroy(&namebuf); ccn_charbuf_destroy(&flatname); return(content); }
/** * Load a link to the repo policy from the repoPolicy file and load the link * target to extract the actual policy. * If a policy file does not exist a new one is created, with a link to a policy * based either on the environment variable CCNR_GLOBAL_PREFIX or the system * default value of ccnx:/parc.com/csl/ccn/Repos, plus the system defaults for * other fields. * This routine must be called after the btree code is initialized and capable * of returning content objects. * Sets the parsed_policy field of the handle to be the new policy. */ static int load_policy(struct ccnr_handle *ccnr) { int fd; ssize_t res; struct content_entry *content = NULL; const unsigned char *content_msg = NULL; struct ccn_parsed_ContentObject pco = {0}; struct ccn_parsed_Link pl = {0}; struct ccn_indexbuf *nc = NULL; struct ccn_charbuf *basename = NULL; struct ccn_charbuf *policy = NULL; struct ccn_charbuf *policy_cob = NULL; struct ccn_charbuf *policyFileName; const char *global_prefix; const unsigned char *buf = NULL; size_t length = 0; int segment = 0; int final = 0; struct ccn_buf_decoder decoder; struct ccn_buf_decoder *d; policyFileName = ccn_charbuf_create(); ccn_charbuf_putf(policyFileName, "%s/repoPolicy", ccnr->directory); ccnr->parsed_policy = ccnr_parsed_policy_create(); fd = open(ccn_charbuf_as_string(policyFileName), O_RDONLY); if (fd >= 0) { ccnr->policy_link_cob = ccn_charbuf_create(); ccn_charbuf_reserve(ccnr->policy_link_cob, 4096); // limits the size of the policy link ccnr->policy_link_cob->length = 0; // clear the buffer res = read(fd, ccnr->policy_link_cob->buf, ccnr->policy_link_cob->limit - ccnr->policy_link_cob->length); close(fd); if (res == -1) { r_init_fail(ccnr, __LINE__, "Error reading repoPolicy file.", errno); ccn_charbuf_destroy(&ccnr->policy_link_cob); ccn_charbuf_destroy(&policyFileName); return(-1); } ccnr->policy_link_cob->length = res; nc = ccn_indexbuf_create(); res = ccn_parse_ContentObject(ccnr->policy_link_cob->buf, ccnr->policy_link_cob->length, &pco, nc); res = ccn_ref_tagged_BLOB(CCN_DTAG_Content, ccnr->policy_link_cob->buf, pco.offset[CCN_PCO_B_Content], pco.offset[CCN_PCO_E_Content], &buf, &length); d = ccn_buf_decoder_start(&decoder, buf, length); res = ccn_parse_Link(d, &pl, NULL); if (res <= 0) { ccnr_msg(ccnr, "Policy link is malformed."); goto CreateNewPolicy; } basename = ccn_charbuf_create(); ccn_charbuf_append(basename, buf + pl.offset[CCN_PL_B_Name], pl.offset[CCN_PL_E_Name] - pl.offset[CCN_PL_B_Name]); ccnr->policy_name = ccn_charbuf_create(); // to detect writes to this name ccn_charbuf_append_charbuf(ccnr->policy_name, basename); // has version ccn_name_chop(ccnr->policy_name, NULL, -1); // get rid of version policy = ccn_charbuf_create(); // if we fail to retrieve the link target, report and then create a new one do { ccn_name_append_numeric(basename, CCN_MARKER_SEQNUM, segment++); content = r_store_lookup_ccnb(ccnr, basename->buf, basename->length); if (content == NULL) { ccnr_debug_ccnb(ccnr, __LINE__, "policy lookup failed for", NULL, basename->buf, basename->length); break; } ccn_name_chop(basename, NULL, -1); content_msg = r_store_content_base(ccnr, content); if (content_msg == NULL) { ccnr_debug_ccnb(ccnr, __LINE__, "Unable to read policy object", NULL, basename->buf, basename->length); break; } res = ccn_parse_ContentObject(content_msg, r_store_content_size(ccnr, content), &pco, nc); res = ccn_ref_tagged_BLOB(CCN_DTAG_Content, content_msg, pco.offset[CCN_PCO_B_Content], pco.offset[CCN_PCO_E_Content], &buf, &length); ccn_charbuf_append(policy, buf, length); final = ccn_is_final_pco(content_msg, &pco, nc); } while (!final && segment < 100);