/** * 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); }
/** * Resolve the version, based on existing ccn content. * @param h is the the ccn handle; it may be NULL, but it is preferable to * use the handle that the client probably already has. * @param name is a ccnb-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 CCN_V_HIGH or CCN_V_HIGHEST, * possibly combined with CCN_V_NESTOK. If CCN_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 applied per * fetch attempt, so the total time may be longer by a factor that * depends on the number of (ccn) hops to the source(s). * @returns -1 for error, 0 if name could not be extended, 1 if was. */ int ccn_resolve_version(struct ccn *h, struct ccn_charbuf *name, int versioning_flags, int timeout_ms) { int res; int myres = -1; struct ccn_parsed_ContentObject pco_space = { 0 }; struct ccn_charbuf *templ = NULL; struct ccn_charbuf *prefix = ccn_charbuf_create(); struct ccn_charbuf *cobj = ccn_charbuf_create(); struct ccn_parsed_ContentObject *pco = &pco_space; struct ccn_indexbuf *ndx = ccn_indexbuf_create(); const unsigned char *vers = NULL; size_t vers_size = 0; int n; struct ccn_indexbuf *nix = ccn_indexbuf_create(); unsigned char lowtime[7] = {CCN_MARKER_VERSION, 0, FF, FF, FF, FF, FF}; if ((versioning_flags & ~CCN_V_NESTOK & ~CCN_V_EST) != CCN_V_HIGH) { ccn_seterror(h, EINVAL); ccn_perror(h, "ccn_resolve_version is only implemented for versioning_flags = CCN_V_HIGH(EST)"); goto Finish; } n = ccn_name_split(name, nix); if (n < 0) goto Finish; if ((versioning_flags & CCN_V_NESTOK) == 0) { res = ccn_name_comp_get(name->buf, nix, n - 1, &vers, &vers_size); if (res >= 0 && vers_size == 7 && vers[0] == CCN_MARKER_VERSION) { myres = 0; goto Finish; } } templ = resolve_templ(templ, lowtime, sizeof(lowtime)); ccn_charbuf_append(prefix, name->buf, name->length); /* our copy */ cobj->length = 0; res = ccn_get(h, prefix, templ, timeout_ms, cobj, pco, ndx, 0); while (cobj->length != 0) { if (pco->type == CCN_CONTENT_NACK) // XXX - also check for number of components break; res = ccn_name_comp_get(cobj->buf, ndx, n, &vers, &vers_size); if (res < 0) break; if (vers_size == 7 && vers[0] == CCN_MARKER_VERSION) { /* Looks like we have versions. */ name->length = 0; ccn_charbuf_append(name, prefix->buf, prefix->length); ccn_name_append(name, vers, vers_size); myres = 0; if ((versioning_flags & CCN_V_EST) == 0) break; templ = resolve_templ(templ, vers, vers_size); if (templ == NULL) break; cobj->length = 0; res = ccn_get(h, prefix, templ, timeout_ms, cobj, pco, ndx, CCN_GET_NOKEYWAIT); } else break; } Finish: ccn_charbuf_destroy(&prefix); ccn_charbuf_destroy(&cobj); ccn_indexbuf_destroy(&ndx); ccn_indexbuf_destroy(&nix); ccn_charbuf_destroy(&templ); return(myres); }