예제 #1
0
/**
 * 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)));
}
예제 #2
0
/**
 * 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));
}
예제 #3
0
/**
 * 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);
}
예제 #4
0
파일: ndn_header.c 프로젝트: cawka/ndnd-tlv
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);
}
예제 #5
0
/**
 * 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);
}
예제 #6
0
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);
}
예제 #7
0
파일: ndn_versioning.c 프로젝트: cawka/ndnx
/**
 * 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);
}
예제 #8
0
파일: ndnsendchunks.c 프로젝트: cawka/ndnx
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);
}
예제 #9
0
/**
 * 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)));
}
예제 #10
0
/*
 * 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);
}
예제 #11
0
파일: ndn_uri.c 프로젝트: cawka/ndnd-tlv
/**
 * 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);
}