/** * Add nonce Component to ndnb-encoded Name * * Uses %C1.N namespace. * @returns 0, or -1 for error * see doc/technical/NameConventions.html */ int ndn_name_append_nonce(struct ndn_charbuf *c) { const unsigned char pre[4] = { NDN_MARKER_CONTROL, '.', 'N', 0 }; unsigned char b[15]; memcpy(b, pre, sizeof(pre)); ndn_random_bytes(b + sizeof(pre), sizeof(b) - sizeof(pre)); return(ndn_name_append(c, b, sizeof(b))); }
/** * Add a binary Component to a ndnb-encoded Name * * These are special components used for marking versions, fragments, etc. * @returns 0, or -1 for error * see doc/technical/NameConventions.html */ int ndn_name_append_numeric(struct ndn_charbuf *c, enum ndn_marker marker, uintmax_t value) { uintmax_t v; int i; char b[32]; for (v = value, i = sizeof(b); v != 0 && i > 0; i--, v >>= 8) b[i-1] = v & 0xff; if (i < 1) return(-1); if (marker >= 0) b[--i] = marker; return(ndn_name_append(c, b + i, sizeof(b) - i)); }
/** * should probably return a new cob, rather than reusing one. * should publish link as: * NDNRID_POLICY_URI("ndn:/%C1.M.S.localhost/%C1.M.SRV/repository/POLICY)/%C1.M.K--pubid--/--version--/%00 * should have key locator which is the key name of the repository */ PUBLIC struct ndn_charbuf * ndnr_init_policy_link_cob(struct ndnr_handle *ndnr, struct ndn *h, struct ndn_charbuf *targetname) { struct ndn_signing_params sp = NDN_SIGNING_PARAMS_INIT; struct ndn_charbuf *name = ndn_charbuf_create(); struct ndn_charbuf *pubid = ndn_charbuf_create(); struct ndn_charbuf *pubkey = ndn_charbuf_create(); struct ndn_charbuf *keyid = ndn_charbuf_create(); struct ndn_charbuf *content = ndn_charbuf_create(); struct ndn_charbuf *cob = ndn_charbuf_create(); struct ndn_charbuf *answer = NULL; int res; res = ndn_get_public_key(h, NULL, pubid, pubkey); if (res < 0) goto Bail; if (ndn_name_from_uri(name, NDNRID_POLICY_URI) < 0) goto Bail; res |= ndn_charbuf_append_value(keyid, NDN_MARKER_CONTROL, 1); res |= ndn_charbuf_append_string(keyid, ".M.K"); res |= ndn_charbuf_append_value(keyid, 0, 1); res |= ndn_charbuf_append_charbuf(keyid, pubid); res |= ndn_name_append(name, keyid->buf, keyid->length); res |= ndn_create_version(h, name, NDN_V_NOW, 0, 0); if (ndn_name_from_uri(name, "%00") < 0) goto Bail; sp.sp_flags |= NDN_SP_FINAL_BLOCK; sp.type = NDN_CONTENT_LINK; res |= ndnb_append_Link(content, targetname, "Repository Policy", NULL); if (res != 0) goto Bail; res |= ndn_sign_content(h, cob, name, &sp, content->buf, content->length); if (res != 0) goto Bail; answer = cob; cob = NULL; Bail: ndn_charbuf_destroy(&name); ndn_charbuf_destroy(&pubid); ndn_charbuf_destroy(&pubkey); ndn_charbuf_destroy(&keyid); ndn_charbuf_destroy(&content); ndn_charbuf_destroy(&cob); return (answer); }
struct ndn_header * ndn_get_header(struct ndn *h, struct ndn_charbuf *name, int timeout) { struct ndn_charbuf *hn; struct ndn_header *result = NULL; int res; hn = ndn_charbuf_create(); ndn_charbuf_append_charbuf(hn, name); /* * Requires consistency with metadata profile in * javasrc/src/main/org/ndnx/ndn-tlv/profiles/metadata/MetadataProfile.java */ ndn_name_append(hn, meta, sizeof(meta)); ndn_name_append_str(hn, ".header"); res = ndn_resolve_version(h, hn, NDN_V_HIGHEST, timeout); if (res <= 0) { /* Version not found: try old header name from prior to 04/2010 */ ndn_charbuf_reset(hn); ndn_charbuf_append_charbuf(hn, name); ndn_name_append_str(hn, "_meta_"); ndn_name_append_str(hn, ".header"); res = ndn_resolve_version(h, hn, NDN_V_HIGHEST, timeout); } /* headers must be versioned */ if (res > 0) { struct ndn_charbuf *ho = ndn_charbuf_create(); struct ndn_parsed_ContentObject pcobuf = { 0 }; const unsigned char *hc; size_t hcs; res = ndn_get(h, hn, NULL, timeout, ho, &pcobuf, NULL, 0); if (res == 0) { hc = ho->buf; hcs = ho->length; ndn_content_get_value(hc, hcs, &pcobuf, &hc, &hcs); result = ndn_header_parse(hc, hcs); } ndn_charbuf_destroy(&ho); } ndn_charbuf_destroy(&hn); return (result); }
/** * Advance the last Component of a Name to the next possible value. * @param c contains a ndnb-encoded Name to be updated. * @returns -1 for error, otherwise the number of Components */ int ndn_name_next_sibling(struct ndn_charbuf *c) { int res = -1; struct ndn_indexbuf *ndx; unsigned char *lastcomp = NULL; size_t lastcompsize = 0; size_t i; int carry; struct ndn_charbuf *newcomp; ndx = ndn_indexbuf_create(); if (ndx == NULL) goto Finish; res = ndn_name_split(c, ndx); if (res <= 0) { res = -1; goto Finish; } res = ndn_ref_tagged_BLOB(NDN_DTAG_Component, c->buf, ndx->buf[res-1], ndx->buf[res], (const unsigned char **)&lastcomp, &lastcompsize); if (res < 0) goto Finish; for (carry = 1, i = lastcompsize; carry && i > 0; i--) { carry = (((++lastcomp[i-1]) & 0xFF) == 0x00); } if (carry) { newcomp = ndn_charbuf_create(); res |= ndn_charbuf_append_value(newcomp, 0, 1); res |= ndn_charbuf_append(newcomp, lastcomp, lastcompsize); res |= ndn_name_chop(c, ndx, ndx->n - 2); res |= ndn_name_append(c, newcomp->buf, newcomp->length); ndn_charbuf_destroy(&newcomp); if (res < 0) goto Finish; } res = ndx->n - 1; Finish: ndn_indexbuf_destroy(&ndx); return(res); }
static void express_bulkdata_interest(struct ndn *h, struct pending *p) { int res; struct bulkdata *b = NULL; struct ndn_charbuf *name = NULL; struct ndn_charbuf *templ = NULL; struct ndn_charbuf *seq = NULL; b = p->parent; if (b == NULL) return; name = ndn_charbuf_create(); templ = ndn_charbuf_create(); seq = ndn_charbuf_create(); ndn_charbuf_append(name, b->name_prefix->buf, b->name_prefix->length); seq->length = 0; (*b->seqfunc)(p->x, b->seqfunc_param, seq); ndn_name_append(name, seq->buf, seq->length); ndn_charbuf_append_tt(templ, NDN_DTAG_Interest, NDN_DTAG); ndn_charbuf_append_tt(templ, NDN_DTAG_Name, NDN_DTAG); ndn_charbuf_append_closer(templ); /* </Name> */ // XXX - may want to set Min/MaxSuffixComponents ndn_charbuf_append_closer(templ); /* </Interest> */ res = ndn_express_interest(h, name, &p->closure, templ); assert(res >= 0); // XXX - handle this better ndn_charbuf_destroy(&name); ndn_charbuf_destroy(&templ); ndn_charbuf_destroy(&seq); }
/** * Resolve the version, based on existing ndn content. * @param h is the the ndn handle; it may be NULL, but it is preferable to * use the handle that the client probably already has. * @param name is a ndnb-encoded Name prefix. It gets extended in-place with * one additional Component such that it names highest extant * version that can be found, subject to the supplied timeout. * @param versioning_flags presently must be NDN_V_HIGH or NDN_V_HIGHEST, * possibly combined with NDN_V_NESTOK. If NDN_V_NESTOK is not present * and the ending component appears to be a version, the routine * returns 0 immediately, on the assumption that an explicit * version has already been provided. * @param timeout_ms is a time value in milliseconds. This is the total time * that the caller can wait. * @returns -1 for error, 0 if name was not extended, 1 if was. */ int ndn_resolve_version(struct ndn *h, struct ndn_charbuf *name, int versioning_flags, int timeout_ms) { int res; int myres = -1; struct ndn_parsed_ContentObject pco_space = { 0 }; struct ndn_charbuf *templ = NULL; struct ndn_charbuf *prefix = ndn_charbuf_create(); struct ndn_charbuf *cobj = ndn_charbuf_create(); struct ndn_parsed_ContentObject *pco = &pco_space; struct ndn_indexbuf *ndx = ndn_indexbuf_create(); const unsigned char *vers = NULL; size_t vers_size = 0; struct timeval start, prev, now; int n; int rtt_max = 0; int rtt; int ttimeout; struct ndn_indexbuf *nix = ndn_indexbuf_create(); unsigned char lowtime[7] = {NDN_MARKER_VERSION, 0, FF, FF, FF, FF, FF}; if ((versioning_flags & ~NDN_V_NESTOK & ~NDN_V_EST) != NDN_V_HIGH) { ndn_seterror(h, EINVAL); ndn_perror(h, "ndn_resolve_version is only implemented for versioning_flags = NDN_V_HIGH(EST)"); goto Finish; } n = ndn_name_split(name, nix); if (n < 0) goto Finish; if ((versioning_flags & NDN_V_NESTOK) == 0) { res = ndn_name_comp_get(name->buf, nix, n - 1, &vers, &vers_size); if (res >= 0 && vers_size == 7 && vers[0] == NDN_MARKER_VERSION) { myres = 0; goto Finish; } } templ = resolve_templ(templ, lowtime, sizeof(lowtime), ms_to_tu(timeout_ms) * 7 / 8, versioning_flags); ndn_charbuf_append(prefix, name->buf, name->length); /* our copy */ cobj->length = 0; gettimeofday(&start, NULL); prev = start; /* * the algorithm for NDN_V_HIGHEST is to send the initial Interest with * a lifetime that will ensure 1 resend before the timeout, and to keep * keep sending an Interest, excluding earlier versions, tracking the * maximum round trip time and using a timeout of 4*RTT, and an interest * lifetime that should get a retransmit. If there is no response, * return the highest version found so far. */ myres = 0; res = ndn_get(h, prefix, templ, timeout_ms, cobj, pco, ndx, 0); while (cobj->length != 0) { if (pco->type == NDN_CONTENT_NACK) // XXX - also check for number of components break; res = ndn_name_comp_get(cobj->buf, ndx, n, &vers, &vers_size); if (res < 0) break; if (vers_size == 7 && vers[0] == NDN_MARKER_VERSION) { /* Looks like we have versions. */ name->length = 0; ndn_charbuf_append(name, prefix->buf, prefix->length); ndn_name_append(name, vers, vers_size); myres = 1; if ((versioning_flags & NDN_V_EST) == 0) break; gettimeofday(&now, NULL); rtt = (now.tv_sec - prev.tv_sec) * 1000000 + (now.tv_usec - prev.tv_usec); if (rtt > rtt_max) rtt_max = rtt; prev = now; timeout_ms -= (now.tv_sec - start.tv_sec) * 1000 + (now.tv_usec - start.tv_usec) / 1000; if (timeout_ms <= 0) break; ttimeout = timeout_ms < (rtt_max/250) ? timeout_ms : (rtt_max/250); templ = resolve_templ(templ, vers, vers_size, ms_to_tu(ttimeout) * 7 / 8, versioning_flags); if (templ == NULL) break; cobj->length = 0; res = ndn_get(h, prefix, templ, ttimeout, cobj, pco, ndx, NDN_GET_NOKEYWAIT); } else break; } Finish: ndn_charbuf_destroy(&prefix); ndn_charbuf_destroy(&cobj); ndn_indexbuf_destroy(&ndx); ndn_indexbuf_destroy(&nix); ndn_charbuf_destroy(&templ); return(myres); }
int main(int argc, char **argv) { const char *progname = argv[0]; struct ndn *ndn = NULL; struct ndn_charbuf *root = NULL; struct ndn_charbuf *name = NULL; struct ndn_charbuf *temp = NULL; struct ndn_charbuf *templ = NULL; struct ndn_signing_params sp = NDN_SIGNING_PARAMS_INIT; long expire = -1; long blocksize = 1024; int i; int status = 0; int res; ssize_t read_res; unsigned char *buf = NULL; struct mydata mydata = { 0 }; struct ndn_closure in_content = {.p=&incoming_content, .data=&mydata}; struct ndn_closure in_interest = {.p=&incoming_interest, .data=&mydata}; while ((res = getopt(argc, argv, "hx:b:")) != -1) { switch (res) { case 'x': expire = atol(optarg); if (expire <= 0) usage(progname); break; case 'b': blocksize = atol(optarg); break; default: case 'h': usage(progname); break; } } argc -= optind; argv += optind; if (argc != 1) usage(progname); name = ndn_charbuf_create(); res = ndn_name_from_uri(name, argv[0]); if (res < 0) { fprintf(stderr, "%s: bad NDN URI: %s\n", progname, argv[0]); exit(1); } ndn = ndn_create(); if (ndn_connect(ndn, NULL) == -1) { perror("Could not connect to ndnd"); exit(1); } buf = calloc(1, blocksize); root = name; name = ndn_charbuf_create(); temp = ndn_charbuf_create(); templ = ndn_charbuf_create(); /* Set up a handler for interests */ ndn_charbuf_append(name, root->buf, root->length); ndn_set_interest_filter(ndn, name, &in_interest); /* Initiate check to see whether there is already something there. */ ndn_charbuf_reset(temp); ndn_charbuf_putf(temp, "%d", 0); ndn_name_append(name, temp->buf, temp->length); ndn_charbuf_reset(templ); ndnb_element_begin(templ, NDN_DTAG_Interest); ndnb_element_begin(templ, NDN_DTAG_Name); ndnb_element_end(templ); /* </Name> */ ndnb_tagged_putf(templ, NDN_DTAG_MaxSuffixComponents, "%d", 1); // XXX - use pubid ndnb_element_end(templ); /* </Interest> */ res = ndn_express_interest(ndn, name, &in_content, templ); if (res < 0) abort(); sp.freshness = expire; for (i = 0;; i++) { read_res = read_full(0, buf, blocksize); if (read_res < 0) { perror("read"); read_res = 0; status = 1; } if (read_res < blocksize) { sp.sp_flags |= NDN_SP_FINAL_BLOCK; } ndn_charbuf_reset(name); ndn_charbuf_append(name, root->buf, root->length); ndn_charbuf_reset(temp); ndn_charbuf_putf(temp, "%d", i); ndn_name_append(name, temp->buf, temp->length); ndn_charbuf_reset(temp); ndn_charbuf_append(temp, buf, read_res); ndn_charbuf_reset(temp); res = ndn_sign_content(ndn, temp, name, &sp, buf, read_res); if (res != 0) { fprintf(stderr, "Failed to sign ContentObject (res == %d)\n", res); exit(1); } /* Put the keylocator in the first block only. */ sp.sp_flags |= NDN_SP_OMIT_KEY_LOCATOR; if (i == 0) { /* Finish check for old content */ if (mydata.content_received == 0) ndn_run(ndn, 100); if (mydata.content_received > 0) { fprintf(stderr, "%s: name is in use: %s\n", progname, argv[0]); exit(1); } mydata.outstanding++; /* the first one is free... */ } res = ndn_put(ndn, temp->buf, temp->length); if (res < 0) { fprintf(stderr, "ndn_put failed (res == %d)\n", res); exit(1); } if (read_res < blocksize) break; if (mydata.outstanding > 0) mydata.outstanding--; else res = 10; res = ndn_run(ndn, res * 100); if (res < 0) { status = 1; break; } } free(buf); buf = NULL; ndn_charbuf_destroy(&root); ndn_charbuf_destroy(&name); ndn_charbuf_destroy(&temp); ndn_destroy(&ndn); exit(status); }
/** * Add a Component that is a NUL-terminated string. * * The component added consists of the bytes of the string without the NUL. * This function is convenient for those applications that construct * component names from simple strings. * @returns 0, or -1 for error. */ int ndn_name_append_str(struct ndn_charbuf *c, const char *s) { return(ndn_name_append(c, s, strlen(s))); }
/* * 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 ndn_upcall_res incoming_content( struct ndn_closure *selfp, enum ndn_upcall_kind kind, struct ndn_upcall_info *info) { struct ndn_charbuf *c = NULL; struct ndn_charbuf *comp = NULL; struct ndn_charbuf *uri = NULL; const unsigned char *ndnb = NULL; size_t ndnb_size = 0; struct ndn_indexbuf *comps = NULL; int matched_comps = 0; int res; int i; struct ndn_traversal *data = get_my_data(selfp); if (kind == NDN_UPCALL_FINAL) { for (i = 0; i < data->n_excl; i++) ndn_charbuf_destroy(&(data->excl[i])); if (data->excl != NULL) free(data->excl); free(data); free(selfp); return(0); } if (kind == NDN_UPCALL_INTEREST_TIMED_OUT) return(0); if (kind == NDN_UPCALL_CONTENT_BAD) return(0); if (kind == NDN_UPCALL_CONTENT_UNVERIFIED) { if ((data->flags & MUST_VERIFY) != 0) return(NDN_UPCALL_RESULT_VERIFY); } if (kind != NDN_UPCALL_CONTENT && kind != NDN_UPCALL_CONTENT_UNVERIFIED) abort(); ndnb = info->content_ndnb; ndnb_size = info->pco->offset[NDN_PCO_E]; comps = info->content_comps; matched_comps = info->pi->prefix_comps; c = ndn_charbuf_create(); uri = ndn_charbuf_create(); if (matched_comps + 1 > comps->n) { ndn_uri_append(c, ndnb, ndnb_size, 1); fprintf(stderr, "How did this happen? %s\n", ndn_charbuf_as_string(uri)); exit(1); } data->counter[0]++; /* Tell main that something new came in */ /* Recover the same prefix as before */ ndn_name_init(c); ndn_name_append_components(c, ndnb, comps->buf[0], comps->buf[matched_comps]); comp = ndn_charbuf_create(); ndn_name_init(comp); if (matched_comps + 1 == comps->n) { /* Reconstruct the implicit content digest component */ ndn_digest_ContentObject(ndnb, info->pco); ndn_name_append(comp, info->pco->digest, info->pco->digest_bytes); } else { ndn_name_append_components(comp, ndnb, 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 ndn_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 ndn_traversal *newdat = NULL; struct ndn_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; ndn_name_init(c); ndn_name_append_components(c, ndnb, comps->buf[0], comps->buf[matched_comps + 1]); express_my_interest(info->h, cl, c); } else { res = ndn_uri_append(uri, info->content_ndnb, info->pco->offset[NDN_PCO_E], 1); if (res < 0) fprintf(stderr, "*** Error: ndn_traverse line %d res=%d\n", __LINE__, res); else printf("%s\n", ndn_charbuf_as_string(uri)); } ndn_charbuf_destroy(&c); ndn_charbuf_destroy(&uri); return(0); }
/** * Convert a ndnx-scheme URI to a ndnb-encoded Name. * The converted result is placed in c. * On input, c may contain a base name, in which case relative URIs are allowed. * Otherwise c should start out empty, and the URI must be absolute. * @returns -1 if an error is found, otherwise returns the number of characters * that were processed. */ int ndn_name_from_uri(struct ndn_charbuf *c, const char *uri) { int res = 0; struct ndn_charbuf *compbuf = NULL; const char *stop = uri + strlen(uri); const char *s = uri; size_t cont = 0; compbuf = ndn_charbuf_create(); if (compbuf == NULL) return(-1); if (s[0] != '/') { res = ndn_append_uri_component(compbuf, s, stop - s, &cont); if (res < -2) goto Done; ndn_charbuf_reserve(compbuf, 1)[0] = 0; if (s[cont-1] == ':') { if ((0 == strcasecmp((const char *)(compbuf->buf), "ndn:") || 0 == strcasecmp((const char *)(compbuf->buf), "ndn:"))) { s += cont; cont = 0; } else return (-1); } } if (s[0] == '/') { ndn_name_init(c); if (s[1] == '/') { /* Skip over hostname part - not used in ndnx scheme */ s += 2; compbuf->length = 0; res = ndn_append_uri_component(compbuf, s, stop - s, &cont); if (res < 0 && res != -2) goto Done; s += cont; cont = 0; } } while (s[0] != 0 && s[0] != '?' && s[0] != '#') { if (s[0] == '/') s++; compbuf->length = 0; res = ndn_append_uri_component(compbuf, s, stop - s, &cont); s += cont; cont = 0; if (res < -2) goto Done; if (res == -2) { res = 0; /* process . or equiv in URI */ continue; } if (res == -1) { /* process .. in URI - discard last name component */ res = ndn_name_last_component_offset(c->buf, c->length); if (res < 0) goto Done; c->length = res; ndn_charbuf_append_closer(c); continue; } res = ndn_name_append(c, compbuf->buf, compbuf->length); if (res < 0) goto Done; } Done: ndn_charbuf_destroy(&compbuf); if (res < 0) return(-1); if (c->length < 2 || c->buf[c->length-1] != NDN_CLOSE) return(-1); return(s - uri); }