Beispiel #1
0
/*
 * deliver_content is used to deliver a previously-buffered
 * ContentObject to the client.
 */
static enum ndn_upcall_res
deliver_content(struct ndn *h, struct bulkdata *b)
{
    struct ndn_upcall_info info = {0};
    struct ndn_parsed_ContentObject obj = {0};
    struct pending *p = b->first;
    int res;
    enum ndn_upcall_res ans;
    assert(p != NULL && p->x == b->next_expected && p->content_ndnb != NULL);
    info.pco = &obj;
    info.content_comps = ndn_indexbuf_create();
    res = ndn_parse_ContentObject(p->content_ndnb, p->content_size,
                                  &obj, info.content_comps);
    assert(res >= 0);
    info.content_ndnb = p->content_ndnb;
    info.matched_comps = info.content_comps->n - 2;
    /* XXX - we have no matched interest to present */
    ans = (*b->client->p)(b->client, NDN_UPCALL_CONTENT, &info);
    // XXX - check for refusal
    info.content_ndnb = NULL;
    free(p->content_ndnb);
    p->content_ndnb = NULL;
    p->content_size = 0;
    ndn_indexbuf_destroy(&info.content_comps);
    if (ans == NDN_UPCALL_RESULT_OK) {
        struct ndn_closure *old = &p->closure;
        if ((--(old->refcount)) == 0) {
            info.pco = NULL;
            (old->p)(old, NDN_UPCALL_FINAL, &info);
        }
    }
    return(ans);
}
Beispiel #2
0
bool hash_store_insert(struct hash_store* self, ndnr_accession accession, struct ndn_charbuf* co, struct ndn_parsed_ContentObject* pco) {
  int res;
  // retrieve payload
  struct ndn_parsed_ContentObject pco2 = {0};
  if (pco == NULL) {
    res = ndn_parse_ContentObject(co->buf, co->length, pco = &pco2, NULL);
    if (res != 0) return false;
  }
  const uint8_t* payload; size_t payloadsz;
  ndn_content_get_value(co->buf, co->length, pco, &payload, &payloadsz);
  // calculate hash
  uint8_t hash[SEGMENT_HASHSZ];
  struct ndn_digest* digest = ndn_digest_create(NDN_DIGEST_SHA256);
  ndn_digest_init(digest);
  ndn_digest_update(digest, payload, payloadsz);
  ndn_digest_final(digest, hash, sizeof(hash));
  ndn_digest_destroy(&digest);

  LOG("hash_store_insert(%" PRIx64 ") ", (uint64_t)ndnr_accession_encode(self->h, accession));
  LOG_hash(hash, SEGMENT_HASHSZ);
  
  // find where to insert
  struct ndn_btree_node* leaf = NULL;
  res = ndn_btree_lookup(self->btree, hash, sizeof(hash), &leaf);
  int i = NDN_BT_SRCH_INDEX(res);
  if (NDN_BT_SRCH_FOUND(res)) {
    LOG(" duplicate(%u,%d)\n", leaf->nodeid, i);
    return true;// already have it
  }
  LOG(" insert(%u,%d)\n", leaf->nodeid, i);
  
  // prepare payload
  uint64_t accession_encoded = ndnr_accession_encode(self->h, accession);
  // insert index entry
  res = ndn_btree_prepare_for_update(self->btree, leaf);
  if (res < 0) return false;
  res = ndn_btree_insert_entry(leaf, i, hash, sizeof(hash), &accession_encoded, sizeof(accession_encoded));
  if (res < 0) return false;

  // btree maintenance
  if (ndn_btree_oversize(self->btree, leaf)) {
    res = ndn_btree_split(self->btree, leaf);
    for (int limit = 100; res >= 0 && self->btree->nextsplit != 0; --limit) {
      if (limit == 0) abort();
      struct ndn_btree_node* node = ndn_btree_getnode(self->btree, self->btree->nextsplit, 0);
      if (node == NULL) break;
      res = ndn_btree_split(self->btree, node);
    }
  }
  
  hash_store_clean(self);
  return true;
}
Beispiel #3
0
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;
}
Beispiel #4
0
/**
 * 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);
Beispiel #5
0
int
main (int argc, char *argv[]) {
    struct ndn *ndn = NULL;
    struct ndn_closure *action;
    struct ndn_charbuf *namebuf = NULL;
    struct ndn_charbuf *interestnamebuf = NULL;
    struct ndn_charbuf *interesttemplatebuf = NULL;
    struct ndn_buf_decoder decoder;
    struct ndn_buf_decoder *d;
    struct handlerstate *state;
    char *filename;
    char rawbuf[1024 * 1024];
    ssize_t rawlen;
    int i, n, res;
    int fd = -1;

    ndn = ndn_create();
    if (ndn_connect(ndn, NULL) == -1) {
        perror("ndn_connect");
        exit(1);
    }
    
    state = calloc(1, sizeof(struct handlerstate));
    action = calloc(1, sizeof(struct ndn_closure));
    action->p = interest_handler;

    namebuf = ndn_charbuf_create();
    if (namebuf == NULL) {
        fprintf(stderr, "ndn_charbuf_create\n");
        exit(1);
    }
    res = ndn_name_init(namebuf);
    if (res < 0) {
        fprintf(stderr, "ndn_name_init\n");
        exit(1);
    }

    interestnamebuf = ndn_charbuf_create();
    interesttemplatebuf = ndn_charbuf_create();
    if (interestnamebuf == NULL || interesttemplatebuf == NULL) {
        fprintf(stderr, "ndn_charbuf_create\n");
        exit(1);
    }
    res = ndn_name_init(interestnamebuf);
    if (res < 0) {
        fprintf(stderr, "ndn_name_init\n");
        exit(1);
    }

    n = 0;
    for (i = 1; i < argc; i++) {
        if (fd != -1) close(fd);
        filename = argv[i];
        if (0 == strcmp(filename, "-d")) {
            options.logging++;
            continue;
        }
        if (0 == strcmp(filename, "-nointerest")) {
            options.nointerest = 1;
            continue;
        }
        if (0 == strcmp(filename, "-reconnect")) {
            options.reconnect = 1;
            continue;
        }
        
        if (options.logging > 0) fprintf(stderr, "Processing %s ", filename);
        fd = open(filename, O_RDONLY);
        if (fd == -1) {
            perror("- open");
            continue;
        }

        rawlen = read(fd, rawbuf, sizeof(rawbuf));
        if (rawlen <= 0) {
            perror("- read");
            continue;
        }
        
        d = ndn_buf_decoder_start(&decoder, (unsigned char *)rawbuf, rawlen);

        if (ndn_buf_match_dtag(d, NDN_DTAG_ContentObject)) {
            state->items = realloc(state->items, (n + 1) * sizeof(*(state->items)));
            if (state->items == NULL) {
                perror(" - realloc failed");
                exit(1);
            }
            memset(&(state->items[n]), 0, sizeof(*(state->items)));
            state->items[n].components = ndn_indexbuf_create();
            res = ndn_parse_ContentObject((unsigned char *)rawbuf, rawlen, &(state->items[n].x), state->items[n].components);
            if (res < 0) {
                if (options.logging > 0) fprintf(stderr, "Processing %s ", filename);
                fprintf(stderr, "- skipping: ContentObject error %d\n", res);
                ndn_indexbuf_destroy(&state->items[n].components);
                continue;
            }
            if (options.logging > 0) fprintf(stderr, "- ok\n");
            state->items[n].filename = filename;
            state->items[n].contents = malloc(rawlen);
            state->items[n].size = rawlen;
            memcpy(state->items[n].contents, rawbuf, rawlen);
            n++;
        } else if (ndn_buf_match_dtag(d, NDN_DTAG_Interest)) {
            struct ndn_parsed_interest interest = {0};
            if (options.nointerest == 0) {
                size_t name_start;
                size_t name_size;
                interestnamebuf->length = 0;
                interesttemplatebuf->length = 0;
                res = ndn_parse_interest((unsigned char *)rawbuf, rawlen, &interest, NULL);
                name_start = interest.offset[NDN_PI_B_Name];
                name_size = interest.offset[NDN_PI_E_Name] - name_start;
                ndn_charbuf_append(interestnamebuf, rawbuf + name_start, name_size);
                ndn_charbuf_append(interesttemplatebuf, rawbuf, rawlen);
                res = ndn_express_interest(ndn, interestnamebuf, action, interesttemplatebuf);
            }
        } else {
            if (options.logging == 0) fprintf(stderr, "Processing %s ", filename);
            fprintf(stderr, "- skipping: unknown type\n");
        }
    }
    state->count = n;
    action->data = state;

    if (ndn_name_init(namebuf) == -1) {
        fprintf(stderr, "ndn_name_init\n");
        exit(1);
    }

    res = ndn_set_interest_filter(ndn, namebuf, action);
    for (;;) {
        res = ndn_run(ndn, -1);
        ndn_disconnect(ndn);
        if (!options.reconnect)
            break;
        sleep(2);
        ndn_connect(ndn, NULL);
    }
    ndn_destroy(&ndn);
    exit(0);
}
Beispiel #6
0
enum ndn_upcall_res
interest_handler(struct ndn_closure *selfp,
                 enum ndn_upcall_kind upcall_kind,
                 struct ndn_upcall_info *info)
{
    int i, c, mc, match, res;
    struct handlerstateitem item;
    struct handlerstate *state;
    size_t ndnb_size = 0;

    state = selfp->data;
    switch(upcall_kind) {
    case NDN_UPCALL_FINAL:
        fprintf(stderr, "Upcall final\n");
        return (0);

    case NDN_UPCALL_INTEREST_TIMED_OUT:
        fprintf(stderr, "refresh\n");
        return (NDN_UPCALL_RESULT_REEXPRESS);
        
    case NDN_UPCALL_CONTENT:
    case NDN_UPCALL_CONTENT_UNVERIFIED:
        ndnb_size = info->pco->offset[NDN_PCO_E];
        c = state->count;
        for (i = 0; i < c; i++) {
            if (info->content_comps->n == state->items[i].components->n) {
                mc = match_components((unsigned char *)info->content_ndnb, info->content_comps,
                                  state->items[i].contents, state->items[i].components);
                if (mc == (info->content_comps->n - 1)) {
                    fprintf(stderr, "Duplicate content\n");
                    return (0);
                }
            }
        }
        fprintf(stderr, "Storing content item %d ", c);
        state->items = realloc(state->items, (c + 1) * sizeof(*(state->items)));
        if (state->items == NULL) {
            perror("realloc failed");
            exit(1);
        }
        memset(&(state->items[c]), 0, sizeof(*(state->items)));
        state->items[c].components = ndn_indexbuf_create();
        /* XXX: probably should not have to do this re-parse of the content object */
        res = ndn_parse_ContentObject(info->content_ndnb, ndnb_size, &(state->items[c].x), state->items[c].components);
        if (res < 0) {
            fprintf(stderr, "- skipping: Not a ContentObject\n");
            ndn_indexbuf_destroy(&state->items[c].components);
            return (-1);
        }
        fprintf(stderr, "- ok\n");
        state->items[c].filename = "ephemeral";
        state->items[c].contents = malloc(ndnb_size);
        state->items[c].size = ndnb_size;
        memcpy(state->items[c].contents, info->content_ndnb, ndnb_size);
        state->count = c + 1;
        return (0);

    case NDN_UPCALL_CONTENT_BAD:
	fprintf(stderr, "Content signature verification failed! Discarding.\n");
	return (-1);

    case NDN_UPCALL_CONSUMED_INTEREST:
        fprintf(stderr, "Upcall consumed interest\n");
        return (-1); /* no data */

    case NDN_UPCALL_INTEREST:
        c = state->count;
        for (i = 0; i < c; i++) {
            match = ndn_content_matches_interest(state->items[i].contents,
                                                 state->items[i].size,
                                                 1,
                                                 NULL,
                                                 info->interest_ndnb,
                                                 info->pi->offset[NDN_PI_E],
                                                 info->pi);
            if (match) {
                ndn_put(info->h, state->items[i].contents, state->items[i].size);
                fprintf(stderr, "Sending %s\n", state->items[i].filename);
                if (i < c - 1) {
                    item = state->items[i];
                    memmove(&(state->items[i]), &(state->items[i+1]), sizeof(item) * ((c - 1) - i));
                    state->items[c - 1] = item;
                }
                return (1);
            }
        }
        return(0);
    case NDN_UPCALL_CONTENT_KEYMISSING:
    case NDN_UPCALL_CONTENT_RAW:
        /* should not happen */
        return (-1);
    }
    return (-1);
}
Beispiel #7
0
int
main(int argc, char **argv)
{
    char *arg = NULL;
    struct ndn *ndnH = NULL;
    struct ndn_parsed_interest interest = {0};
    struct ndn_charbuf *c = ndn_charbuf_create();
    struct ndn_charbuf *templ = ndn_charbuf_create();
    struct ndn_indexbuf *comps = ndn_indexbuf_create();
    int i;
    int millis = 0;
    int secs = 0;
    int opt;
    int pool = 0;
    int regflgs = (NDN_FORW_CHILD_INHERIT | NDN_FORW_ACTIVE);
    int res;
    int status = 0;
    int val = 0;
    
    while ((opt = getopt(argc, argv, "hv")) != -1) {
        switch (opt) {
            default:
            case 'h':
                usage();
                break;
            case 'v':
                verbose++;
                break;
        }
    }
    argc -= optind;
    argv += optind;
    ndnH = ndn_create();
    if (ndn_connect(ndnH, NULL) == -1) {
        ndn_perror(ndnH, "ndn_connect");
        exit(1);
    }
    for (i = 0; i < N_POOLS; i++) {
        store[i].me.p = &outgoing_content;
        store[i].me.data = &store[i];
        store[i].me.intdata = i;
        incoming_content_action[i].p = &incoming_content;
        incoming_content_action[i].intdata = i;
    }
    for (i = 0; i < argc; i++) {
        arg = argv[i];
        if (0 == strcmp(arg, "reconnect")) {
            if (argv[i+1] == NULL)
                usage();
            secs = atoi(argv[i+1]);
            if (secs <= 0 && strcmp(argv[i+1], "0") != 0)
                usage();
            i++;
            ndn_disconnect(ndnH);
            sleep(secs);
            if (ndn_connect(ndnH, NULL) == -1) {
                ndn_perror(ndnH, "ndn_connect");
                exit(1);
            }
            continue;
        }
        if (0 == strcmp(arg, "pool")) {
            if (argv[i+1] == NULL)
                usage();
            pool = argv[i+1][0] - '0';
            if (argv[i+1][1] || pool < 0 || pool >= N_POOLS)
                usage();
            fprintf(stderr, "Pool %d\n", pool);
            i++;
            continue;
        }
        if (0 == strcmp(arg, "prefix")) {
            if (argv[i+1] == NULL)
                usage();
            c->length = 0;
            res = ndn_name_from_uri(c, argv[i+1]);
            if (res < 0)
                usage();
            fprintf(stderr, "Prefix ff=%#x %s pool %d\n",
                    regflgs, argv[i+1], pool);
            if (store[pool].me.intdata != pool) {
                abort();
            }
            res = ndn_set_interest_filter_with_flags(ndnH, c, &store[pool].me, regflgs);
            if (res < 0) {
                ndn_perror(ndnH, "ndn_set_interest_filter_with_flags");
                status = 1;
            }
            res = ndn_run(ndnH, 2);
            if (res < 0)
                break;
            i++;
            continue;
        }
        if (0 == strcmp(arg, "flags")) {
            if (argv[i+1] == NULL)
                usage();
            regflgs = atoi(argv[i+1]);
            if (regflgs <= 0 && strcmp(argv[i+1], "0") != 0)
                usage();
            i++;
            continue;
        }
        if (0 == strcmp(arg, "mincob")) {
            if (argv[i+1] == NULL)
                usage();
            val = atoi(argv[i+1]);
            if (val <= 0 && strcmp(argv[i+1], "0") != 0)
                usage();
            i++;
            if (n_pool(pool) < val) {
                fprintf(stderr, "Pool %d has %d cobs, expected at least %d\n",
                        pool, n_pool(pool), val);
                exit(1);
            }
            continue;
        }
        if (0 == strcmp(arg, "maxcob")) {
            if (argv[i+1] == NULL)
                usage();
            val = atoi(argv[i+1]);
            if (val <= 0 && strcmp(argv[i+1], "0") != 0)
                usage();
            i++;
            if (n_pool(pool) > val) {
                fprintf(stderr, "Pool %d has %d cobs, expected at most %d\n",
                        pool, n_pool(pool), val);
                exit(1);
            }
            continue;
        }
        if (0 == strcmp(arg, "run")) {
            if (argv[i+1] == NULL)
                usage();
            millis = atoi(argv[i+1]);
            if (millis <= 0 && strcmp(argv[i+1], "0") != 0)
                usage();
            i++;
            res = ndn_run(ndnH, millis);
            if (res < 0) {
                ndn_perror(ndnH, "ndn_run");
                exit(1);
            }
            continue;
        }
        close(0);
        res = open(arg, O_RDONLY);
        if (res != 0) {
            perror(arg);
            exit(1);
        }
        fprintf(stderr, "Reading %s ... ", arg);
        rawlen = read(0, rawbuf, sizeof(rawbuf));
        if (rawlen < 0) {
            perror("skipping");
            // XXX - status
            continue;
        }
        // XXX - Should do a skeleton check before parse
        res = ndn_parse_interest(rawbuf, rawlen, &interest, NULL);
        if (res >= 0) {
            size_t name_start = interest.offset[NDN_PI_B_Name];
            size_t name_size = interest.offset[NDN_PI_E_Name] - name_start;
            templ->length = 0;
            ndn_charbuf_append(templ, rawbuf, rawlen);
            fprintf(stderr, "Expressing interest with %d name components\n", res);
            c->length = 0;
            ndn_charbuf_append(c, rawbuf + name_start, name_size);
            // XXX - res is currently ignored
            ndn_express_interest(ndnH, c, &(incoming_content_action[pool]), templ);
        }
        else {
            struct ndn_parsed_ContentObject obj = {0};
            int try;
            res = ndn_parse_ContentObject(rawbuf, rawlen, &obj, comps);
            if (res >= 0) {
                for (try = 0; try < 5; try++) {
                    res = add_to_pool(pool, rawbuf, rawlen);
                    if (res >= 0) {
                        fprintf(stderr, "Added to pool %d\n", pool);
                        break;
                    }
                    if (try == 5) {
                        fprintf(stderr, "No buffer for %s\n", arg);
                        status = 1;
                        break;
                    }
                    fprintf(stderr, "Pool %d full - wait for drain\n", pool);
                    if (ndn_run(ndnH, 1000) < 0)
                        break;
                }
                res = ndn_run(ndnH, 10);
            }
            else {
                fprintf(stderr, "What is that?\n");
                status = 1;
            }
        }
        res = ndn_run(ndnH, 10);
        if (res < 0) {
            ndn_perror(ndnH, "oops");
            status = 1;
        }
    }
    res = ndn_run(ndnH, 10);
    if (res < 0)
        status = 1;
    ndn_destroy(&ndnH);
    exit(status);
}