/** * Parses content object and sets content->flatname */ static int r_store_set_flatname(struct ccnr_handle *h, struct content_entry *content, struct ccn_parsed_ContentObject *pco) { int res; struct ccn_charbuf *flatname = NULL; const unsigned char *msg = NULL; size_t size; msg = r_store_content_base(h, content); size = content->size; if (msg == NULL) goto Bail; flatname = ccn_charbuf_create(); if (flatname == NULL) goto Bail; res = ccn_parse_ContentObject(msg, size, pco, NULL); if (res < 0) { ccnr_msg(h, "error parsing ContentObject - code %d", res); goto Bail; } ccn_digest_ContentObject(msg, pco); if (pco->digest_bytes != 32) goto Bail; res = ccn_flatname_from_ccnb(flatname, msg, size); if (res < 0) goto Bail; res = ccn_flatname_append_component(flatname, pco->digest, pco->digest_bytes); if (res < 0) goto Bail; content->flatname = flatname; flatname = NULL; return(0); Bail: ccn_charbuf_destroy(&flatname); return(-1); }
/** @returns 2 if content was added to index, 1 if it was there but had no accession, 0 if it was already there, -1 for error */ static int r_store_content_btree_insert(struct ccnr_handle *h, struct content_entry *content, struct ccn_parsed_ContentObject *pco, ccnr_accession *accp) { const unsigned char *content_base = NULL; struct ccn_btree *btree = NULL; struct ccn_btree_node *leaf = NULL; struct ccn_btree_node *node = NULL; struct ccn_charbuf *flat = NULL; int i; int limit; int res; btree = h->btree; if (btree == NULL) return(-1); flat = content->flatname; if (flat == NULL) return(-1); res = ccn_btree_lookup(h->btree, flat->buf, flat->length, &leaf); if (res < 0) return(-1); i = CCN_BT_SRCH_INDEX(res); if (CCN_BT_SRCH_FOUND(res)) { *accp = ccnr_accession_decode(h, ccn_btree_content_cobid(leaf, i)); return(*accp == CCNR_NULL_ACCESSION); } else { content_base = r_store_content_base(h, content); if (content_base == NULL) return(-1); res = ccn_btree_prepare_for_update(h->btree, leaf); if (res < 0) return(-1); res = ccn_btree_insert_content(leaf, i, ccnr_accession_encode(h, content->accession), content_base, pco, content->flatname); if (res < 0) return(-1); if (ccn_btree_oversize(btree, leaf)) { res = ccn_btree_split(btree, leaf); for (limit = 100; res >= 0 && btree->nextsplit != 0; limit--) { if (limit == 0) abort(); node = ccn_btree_getnode(btree, btree->nextsplit, 0); if (node == NULL) return(-1); res = ccn_btree_split(btree, node); } } r_store_index_needs_cleaning(h); *accp = content->accession; return(2); } }
PUBLIC struct content_entry * r_store_content_from_accession(struct ccnr_handle *h, ccnr_accession accession) { struct ccn_parsed_ContentObject obj = {0}; struct content_entry *content = NULL; struct content_by_accession_entry *entry; const unsigned char *content_base = NULL; int res; ccnr_accession acc; if (accession == CCNR_NULL_ACCESSION) return(NULL); entry = hashtb_lookup(h->content_by_accession_tab, &accession, sizeof(accession)); if (entry != NULL) { h->content_from_accession_hits++; return(entry->content); } h->content_from_accession_misses++; content = calloc(1, sizeof(*content)); CHKPTR(content); content->cookie = 0; content->accession = accession; content->cob = NULL; content->size = 0; content_base = r_store_content_base(h, content); if (content_base == NULL || content->size == 0) goto Bail; res = r_store_set_flatname(h, content, &obj); if (res < 0) goto Bail; r_store_enroll_content(h, content); res = r_store_content_btree_insert(h, content, &obj, &acc); if (res < 0) goto Bail; if (res == 1 || CCNSHOULDLOG(h, sdf, CCNL_FINEST)) ccnr_debug_content(h, __LINE__, "content/accession", NULL, content); return(content); Bail: ccnr_msg(h, "r_store_content_from_accession.%d failed 0x%jx", __LINE__, ccnr_accession_encode(h, accession)); r_store_forget_content(h, &content); return(content); }
PUBLIC void r_store_send_content(struct ccnr_handle *h, struct fdholder *fdholder, struct content_entry *content) { const unsigned char *content_msg = NULL; off_t offset; if (CCNSHOULDLOG(h, LM_4, CCNL_FINE)) ccnr_debug_content(h, __LINE__, "content_to", fdholder, content); content_msg = r_store_content_base(h, content); r_link_stuff_and_send(h, fdholder, content_msg, content->size, NULL, 0, &offset); if (offset != (off_t)-1 && content->accession == CCNR_NULL_ACCESSION) { int res; res = r_store_set_accession_from_offset(h, content, fdholder, offset); if (res == 0) if (CCNSHOULDLOG(h, LM_4, CCNL_FINE)) ccnr_debug_content(h, __LINE__, "content_stored", r_io_fdholder_from_fd(h, h->active_out_fd), content); } }
enum ndn_upcall_res hash_store_handle_proto_sha256(struct hash_store* self, struct ndn_upcall_info* info) { int res; // extract hash const uint8_t* hash; size_t hashsz; res = ndn_name_comp_get(info->interest_ndnb, info->interest_comps, 1, &hash, &hashsz); if (res != 0 || hashsz != SEGMENT_HASHSZ) return NDN_UPCALL_RESULT_ERR; LOG("hash_store_handle_proto_sha256("); LOG_hash(hash, hashsz); LOG(") "); // find content ndnr_accession accession = hash_store_find(self, hash); if (accession == NDNR_NULL_ACCESSION) { LOG("MISS\n"); return NDN_UPCALL_RESULT_OK; } struct content_entry* orig_content = r_store_content_from_accession(self->h, accession); if (orig_content == NULL) { LOG("LOST\n"); return NDN_UPCALL_RESULT_OK; } if (orig_content->cob == NULL && r_store_content_base(self->h, orig_content) == NULL) { LOG("LOST\n"); return NDN_UPCALL_RESULT_OK; } LOG("HIT %" PRIx64 ", ", (uint64_t)ndnr_accession_encode(self->h, accession)); // extract payload struct ndn_parsed_ContentObject orig_pco = {0}; res = ndn_parse_ContentObject(orig_content->cob->buf, orig_content->cob->length, &orig_pco, NULL); if (res != 0) { LOG("cannot parse\n"); return NDN_UPCALL_RESULT_OK; } const uint8_t* payload; size_t payloadsz; res = ndn_content_get_value(orig_content->cob->buf, orig_content->cob->length, &orig_pco, &payload, &payloadsz); if (res != 0) { LOG("cannot extract payload\n"); return NDN_UPCALL_RESULT_OK; } // verify hash if (!hash_store_verify_hash(self, payload, payloadsz, hash)) { LOG("hash mismatch\n"); return NDN_UPCALL_RESULT_OK; } // build reply struct ndn_charbuf* reply = ndn_charbuf_create(); hash_store_build_reply(self, reply, hash, payload, payloadsz); // send reply TODO use queues res = ndn_put(info->h, reply->buf, reply->length); if (res != 0) { LOG("cannot send\n"); ndn_charbuf_destroy(&reply); return NDN_UPCALL_RESULT_OK; } ndn_charbuf_destroy(&reply); LOG("OK\n"); return NDN_UPCALL_RESULT_INTEREST_CONSUMED; }
/** * 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); }
PUBLIC int r_store_content_field_access(struct ccnr_handle *h, struct content_entry *content, enum ccn_dtag dtag, const unsigned char **bufp, size_t *sizep) { int res = -1; const unsigned char *content_msg; struct ccn_parsed_ContentObject pco = {0}; content_msg = r_store_content_base(h, content); if (content_msg == NULL) return(-1); res = ccn_parse_ContentObject(content_msg, content->size, &pco, NULL); if (res < 0) return(-1); if (dtag == CCN_DTAG_Content) res = ccn_ref_tagged_BLOB(CCN_DTAG_Content, content_msg, pco.offset[CCN_PCO_B_Content], pco.offset[CCN_PCO_E_Content], bufp, sizep); return(res); }
/** * 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 NDNR_GLOBAL_PREFIX or the system * default value of ndn:/named-data.net/ndn/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 ndnr_handle *ndnr) { int fd; ssize_t res; struct content_entry *content = NULL; const unsigned char *content_msg = NULL; struct ndn_parsed_ContentObject pco = {0}; struct ndn_parsed_Link pl = {0}; struct ndn_indexbuf *nc = NULL; struct ndn_charbuf *basename = NULL; struct ndn_charbuf *policy = NULL; struct ndn_charbuf *policy_cob = NULL; struct ndn_charbuf *policyFileName; const char *global_prefix; const unsigned char *buf = NULL; size_t length = 0; int segment = 0; int final = 0; struct ndn_buf_decoder decoder; struct ndn_buf_decoder *d; policyFileName = ndn_charbuf_create(); ndn_charbuf_putf(policyFileName, "%s/repoPolicy", ndnr->directory); ndnr->parsed_policy = ndnr_parsed_policy_create(); fd = open(ndn_charbuf_as_string(policyFileName), O_RDONLY); if (fd >= 0) { ndnr->policy_link_cob = ndn_charbuf_create(); ndn_charbuf_reserve(ndnr->policy_link_cob, 4096); // limits the size of the policy link ndnr->policy_link_cob->length = 0; // clear the buffer res = read(fd, ndnr->policy_link_cob->buf, ndnr->policy_link_cob->limit - ndnr->policy_link_cob->length); close(fd); if (res == -1) { r_init_fail(ndnr, __LINE__, "Error reading repoPolicy file.", errno); ndn_charbuf_destroy(&ndnr->policy_link_cob); ndn_charbuf_destroy(&policyFileName); return(-1); } ndnr->policy_link_cob->length = res; nc = ndn_indexbuf_create(); res = ndn_parse_ContentObject(ndnr->policy_link_cob->buf, ndnr->policy_link_cob->length, &pco, nc); res = ndn_ref_tagged_BLOB(NDN_DTAG_Content, ndnr->policy_link_cob->buf, pco.offset[NDN_PCO_B_Content], pco.offset[NDN_PCO_E_Content], &buf, &length); d = ndn_buf_decoder_start(&decoder, buf, length); res = ndn_parse_Link(d, &pl, NULL); if (res <= 0) { ndnr_msg(ndnr, "Policy link is malformed."); goto CreateNewPolicy; } basename = ndn_charbuf_create(); ndn_charbuf_append(basename, buf + pl.offset[NDN_PL_B_Name], pl.offset[NDN_PL_E_Name] - pl.offset[NDN_PL_B_Name]); ndnr->policy_name = ndn_charbuf_create(); // to detect writes to this name ndn_charbuf_append_charbuf(ndnr->policy_name, basename); // has version ndn_name_chop(ndnr->policy_name, NULL, -1); // get rid of version policy = ndn_charbuf_create(); // if we fail to retrieve the link target, report and then create a new one do { ndn_name_append_numeric(basename, NDN_MARKER_SEQNUM, segment++); content = r_store_lookup_ndnb(ndnr, basename->buf, basename->length); if (content == NULL) { ndnr_debug_ndnb(ndnr, __LINE__, "policy lookup failed for", NULL, basename->buf, basename->length); break; } ndn_name_chop(basename, NULL, -1); content_msg = r_store_content_base(ndnr, content); if (content_msg == NULL) { ndnr_debug_ndnb(ndnr, __LINE__, "Unable to read policy object", NULL, basename->buf, basename->length); break; } res = ndn_parse_ContentObject(content_msg, r_store_content_size(ndnr, content), &pco, nc); res = ndn_ref_tagged_BLOB(NDN_DTAG_Content, content_msg, pco.offset[NDN_PCO_B_Content], pco.offset[NDN_PCO_E_Content], &buf, &length); ndn_charbuf_append(policy, buf, length); final = ndn_is_final_pco(content_msg, &pco, nc); } while (!final && segment < 100);
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); }