Beispiel #1
0
/*
 * Append Scope=0 to partially constructed Interest, meaning
 * to address only the local ndnd.
 */
static void
local_scope(struct ndn_charbuf *templ)
{
    ndn_charbuf_append_tt(templ, NDN_DTAG_Scope, NDN_DTAG);
    ndn_charbuf_append_tt(templ, 1, NDN_UDATA);
    ndn_charbuf_append(templ, "0", 1);
    ndn_charbuf_append_closer(templ); /* </Scope> */
}
Beispiel #2
0
/**
 * Append AnswerOriginKind=1 to partially constructed Interest, meaning
 * do not generate new content.
 */
static void
answer_passive(struct ndn_charbuf *templ)
{
    ndn_charbuf_append_tt(templ, NDN_DTAG_AnswerOriginKind, NDN_DTAG);
    ndn_charbuf_append_tt(templ, 1, NDN_UDATA);
    ndn_charbuf_append(templ, "1", 1);
    ndn_charbuf_append_closer(templ); /* </AnswerOriginKind> */
}
Beispiel #3
0
static void
append_future_vcomp(struct ndn_charbuf *templ)
{
    /* One beyond a distant future version stamp */
    unsigned char b[7] = {NDN_MARKER_VERSION + 1, 0, 0, 0, 0, 0, 0};
    ndn_charbuf_append_tt(templ, NDN_DTAG_Component, NDN_DTAG);
    ndn_charbuf_append_tt(templ, sizeof(b), NDN_BLOB);
    ndn_charbuf_append(templ, b, sizeof(b));
    ndn_charbuf_append_closer(templ); /* </Component> */
}
Beispiel #4
0
struct ndn_charbuf *make_template(int allow_caching) {
    if (NDN_API_VERSION >= 8000 && !allow_caching) {
        struct ndn_charbuf *templ = ndn_charbuf_create();
        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>
        ndn_charbuf_append_tt(templ, NDN_DTAG_AnswerOriginKind, NDN_DTAG);
        ndnb_append_number(templ, NDN_AOK_NEW);
        ndn_charbuf_append_closer(templ); // </AnswerOriginKind>
        ndn_charbuf_append_closer(templ); // </Interest>
        return(templ);
    }

    return NULL;
}
Beispiel #5
0
PyObject *
_pyndn_cmd_KeyLocator_to_ndn(PyObject *UNUSED(self), PyObject *args,
                             PyObject *kwds)
{
    static char *kwlist[] = {"name", "digest", "key", "cert", NULL};
    PyObject *py_name = Py_None, *py_digest = Py_None, *py_key = Py_None,
              *py_cert = Py_None;
    struct ndn_charbuf *keylocator;
    int r;

    if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOOO", kwlist, &py_name,
                                     &py_digest, &py_key, &py_cert))
        return NULL;

    keylocator = ndn_charbuf_create();
    JUMP_IF_NULL_MEM(keylocator, error);

    r = ndn_charbuf_append_tt(keylocator, NDN_DTAG_KeyLocator, NDN_DTAG);
    JUMP_IF_NEG_MEM(r, error);

    if (py_name != Py_None) {
        //TODO: add digest as well
        r = KeyLocator_name_to_ndn(keylocator, py_name);
        JUMP_IF_NEG(r, error);
    } else if (py_key != Py_None) {
        r = KeyLocator_key_to_ndn(keylocator, py_key);
        JUMP_IF_NEG(r, error);
    } else if (py_cert != Py_None) {
#if 0
        ndn_charbuf_append_tt(keylocator, NDN_DTAG_Certificate, NDN_DTAG);
        // TODO: How to handle certificate?  ** Not supported here
        ndn_charbuf_append_closer(keylocator); /* </Certificate> */
#endif

        PyErr_SetString(PyExc_NotImplementedError, "Certificate key locator is"
                        " not implemented");
        goto error;
    }

    r = ndn_charbuf_append_closer(keylocator); /* </KeyLocator> */
    JUMP_IF_NEG_MEM(r, error);

    return NDNObject_New(KEY_LOCATOR, keylocator);
error:
    ndn_charbuf_destroy(&keylocator);

    return NULL;
}
Beispiel #6
0
static int
KeyLocator_name_to_ndn(struct ndn_charbuf *keylocator, PyObject *py_name)
{
    struct ndn_charbuf *name;
    int r;

    if (!NDNObject_IsValid(NAME, py_name)) {
        PyErr_SetString(PyExc_TypeError, "Argument needs to be of type NDN Name");
        return -1;
    }
    name = NDNObject_Get(NAME, py_name);

    r = ndn_charbuf_append_tt(keylocator, NDN_DTAG_KeyName, NDN_DTAG);
    JUMP_IF_NEG_MEM(r, error);

    // Looks like name already has all necessary tags
    // r = ndnb_append_tagged_blob(keylocator, NDN_DTAG_Name, name->buf, name->length); // check
    r = ndn_charbuf_append_charbuf(keylocator, name);
    JUMP_IF_NEG_MEM(r, error);

    r = ndn_charbuf_append_closer(keylocator); /* </KeyName> */
    JUMP_IF_NEG_MEM(r, error);

    r = 0;

error:

    return r;
}
Beispiel #7
0
static int
KeyLocator_key_to_ndn(struct ndn_charbuf *keylocator, PyObject *py_key)
{
    struct ndn_pkey *key;
    int r;

    if (!NDNObject_IsValid(PKEY_PUB, py_key)) {
        PyErr_SetString(PyExc_TypeError, "Argument needs to be of type NDN Key");
        return -1;
    }

    key = NDNObject_Get(PKEY_PUB, py_key);

    r = ndn_charbuf_append_tt(keylocator, NDN_DTAG_Key, NDN_DTAG);
    JUMP_IF_NEG_MEM(r, error);

    r = ndn_append_pubkey_blob(keylocator, key);
    JUMP_IF_NEG_MEM(r, error);

    r = ndn_charbuf_append_closer(keylocator); /* </Key> */
    JUMP_IF_NEG_MEM(r, error);

    r = 0;

error:

    return r;
}
Beispiel #8
0
int
build_keylocator_from_key(struct ndn_charbuf** keylocator, struct ndn_pkey* key)
{
	int res = 0;

	*keylocator = ndn_charbuf_create();

	ndn_charbuf_append_tt(*keylocator, NDN_DTAG_KeyLocator, NDN_DTAG);
	ndn_charbuf_append_tt(*keylocator, NDN_DTAG_Key, NDN_DTAG);

	res = ndn_append_pubkey_blob(*keylocator, key);

	ndn_charbuf_append_closer(*keylocator); /* </Key> */
	ndn_charbuf_append_closer(*keylocator); /* </KeyLocator> */

	return(res);
}
Beispiel #9
0
/**
 * Add a Component to a Name.
 *
 * The component is an arbitrary string of n octets, no escaping required.
 * @returns 0, or -1 for error.
 */
int
ndn_name_append(struct ndn_charbuf *c, const void *component, size_t n)
{
    int res;
    const unsigned char closer[2] = {NDN_CLOSE, NDN_CLOSE};
    if (c->length < 2 || c->buf[c->length-1] != closer[1])
        return(-1);
    c->length -= 1;
    ndn_charbuf_reserve(c, n + 8);
    res = ndn_charbuf_append_tt(c, NDN_DTAG_Component, NDN_DTAG);
    if (res == -1) return(res);
    res = ndn_charbuf_append_tt(c, n, NDN_BLOB);
    if (res == -1) return(res);
    res = ndn_charbuf_append(c, component, n);
    if (res == -1) return(res);
    res = ndn_charbuf_append(c, closer, sizeof(closer));
    return(res);
}
Beispiel #10
0
/*
 * Construct and send a new interest that uses the exclusion list.
 * Return -1 if not sent because of packet size, 0 for success.
 */
static int
express_my_interest(struct ndn *h,
                    struct ndn_closure *selfp,
                    struct ndn_charbuf *name)
{
    int ans;
    struct ndn_charbuf *templ = NULL;
    int i;
    struct ndn_traversal *data = get_my_data(selfp);

    templ = ndn_charbuf_create();
    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> */
    if (data->n_excl != 0) {
        ndn_charbuf_append_tt(templ, NDN_DTAG_Exclude, NDN_DTAG);
        if ((data->flags & EXCLUDE_LOW) != 0)
            append_Any_filter(templ);
        for (i = 0; i < data->n_excl; i++) {
            struct ndn_charbuf *comp = data->excl[i];
            if (comp->length < 4) abort();
            ndn_charbuf_append(templ, comp->buf + 1, comp->length - 2);
        }
        if ((data->flags & EXCLUDE_HIGH) != 0)
            append_Any_filter(templ);
        ndn_charbuf_append_closer(templ); /* </Exclude> */
    }
    answer_passive(templ, (data->flags & ALLOW_STALE) != 0);
    if ((data->flags & LOCAL_SCOPE) != 0)
        local_scope(templ);
    ndn_charbuf_append_closer(templ); /* </Interest> */
    if (templ->length + name->length > data->warn + 2) {
        fprintf(stderr, "*** Interest packet is %d bytes\n", (int)templ->length);
        data->warn = data->warn * 8 / 5;
    }
    if (templ->length + name->length > 1450 && data->n_excl > 3)
        ans = -1;
    else {
        ndn_express_interest(h, name, selfp, templ);
        ans = 0;
    }
    ndn_charbuf_destroy(&templ);
    return(ans);
}
Beispiel #11
0
/**
 * Construct a template suitable for use with ndn_express_interest
 * indicating at least one suffix component, and stale data if so
 * requested.
 */
struct ndn_charbuf *
make_template(struct mydata *md, struct ndn_upcall_info *info)
{
    struct ndn_charbuf *templ = ndn_charbuf_create();
    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 - use pubid if possible
    ndn_charbuf_append_tt(templ, NDN_DTAG_MinSuffixComponents, NDN_DTAG);
    ndnb_append_number(templ, 1);
    ndn_charbuf_append_closer(templ); /* </MinSuffixComponents> */
    if (md->allow_stale) {
        ndn_charbuf_append_tt(templ, NDN_DTAG_AnswerOriginKind, NDN_DTAG);
        ndnb_append_number(templ, NDN_AOK_DEFAULT | NDN_AOK_STALE);
        ndn_charbuf_append_closer(templ); /* </AnswerOriginKind> */
    }
    ndn_charbuf_append_closer(templ); /* </Interest> */
    return(templ);
}
Beispiel #12
0
/**
 * Reset charbuf to represent an empty Name in binary format.
 * 重置charbuf来代表一个空的*二进制*名字
 * @returns 0, or -1 for error.
 */
int
ndn_name_init(struct ndn_charbuf *c)
{
    int res;
    c->length = 0;
    res = ndn_charbuf_append_tt(c, NDN_DTAG_Name, NDN_DTAG);
    if (res == -1) return(res);
    res = ndn_charbuf_append_closer(c);
    return(res);
}
Beispiel #13
0
static struct ndn_charbuf *
resolve_templ(struct ndn_charbuf *templ, unsigned const char *vcomp,
              int size, int lifetime, int versioning_flags)
{
    if (templ == NULL)
        templ = ndn_charbuf_create();
    if (size < 3 || size > 16) {
        ndn_charbuf_destroy(&templ);
        return(NULL);
    }
    templ->length = 0;
    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> */
    ndn_charbuf_append_tt(templ, NDN_DTAG_Exclude, NDN_DTAG);
    append_filter_all(templ);
    ndn_charbuf_append_tt(templ, NDN_DTAG_Component, NDN_DTAG);
    ndn_charbuf_append_tt(templ, size, NDN_BLOB);
    ndn_charbuf_append(templ, vcomp, size);
    ndn_charbuf_append_closer(templ); /* </Component> */
    append_future_vcomp(templ);
    append_filter_all(templ);
    ndn_charbuf_append_closer(templ); /* </Exclude> */
    answer_highest(templ);
    answer_passive(templ);
    if ((versioning_flags & NDN_V_SCOPE2) != 0)
        ndnb_tagged_putf(templ, NDN_DTAG_Scope, "%d", 2);
    else if ((versioning_flags & NDN_V_SCOPE1) != 0)
        ndnb_tagged_putf(templ, NDN_DTAG_Scope, "%d", 1);
    else if ((versioning_flags & NDN_V_SCOPE0) != 0)
        ndnb_tagged_putf(templ, NDN_DTAG_Scope, "%d", 0);
    if (lifetime > 0)
        ndnb_append_tagged_binary_number(templ, NDN_DTAG_InterestLifetime, lifetime);
    ndn_charbuf_append_closer(templ); /* </Interest> */
    return(templ);
}
Beispiel #14
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);
}
Beispiel #15
0
int
ndn_append_pubkey_blob(struct ndn_charbuf *c, const struct ndn_pkey *i_pubkey)
{
    int res;
    size_t bytes;
    unsigned char *p = NULL;
    res = i2d_PUBKEY((EVP_PKEY *)i_pubkey, NULL);
    if (res < 0)
        return(-1);
    bytes = res;
    res = ndn_charbuf_append_tt(c, bytes, NDN_BLOB);
    if (res < 0)
        return(-1);
    p = ndn_charbuf_reserve(c, bytes);
    if (p == NULL)
        return(-1);
    res = i2d_PUBKEY((EVP_PKEY *)i_pubkey, &p);
    if (res != (int)bytes)
        return(-1);
    c->length += bytes;
    return(bytes);
}
Beispiel #16
0
/**
 * Extend a Name with a new version stamp
 * @param h is the the ndn handle.
 *        May be NULL.  This procedure does not use the connection.
 * @param name is a ndnb-encoded Name prefix. By default it gets extended
 *        in-place with one additional Component that conforms to the
 *        versioning profile and is based on the supplied time, unless a
 *        version component is already present.
 * @param versioning_flags modifies the default behavior:
 *        NDN_V_REPLACE causes the last component to be replaced if it
 *        appears to be a version stamp.  If NDN_V_HIGH is set as well, an
 *        attempt will be made to generate a new version stamp that is
 *        later than the existing one, or to return an error.
 *        NDN_V_NOW bases the version on the current time rather than the
 *        supplied time.
 *        NDN_V_NESTOK will allow the new version component to be appended
 *        even if there is one there (this makes no difference if NDN_V_REPLACE
 *        is also set).
 * @param secs is the desired time, in seconds since epoch
 *        (ignored if NDN_V_NOW is set).
 * @param nsecs is the number of nanoseconds.
 * @returns -1 for error, 0 for success.
 */
int
ndn_create_version(struct ndn *h, struct ndn_charbuf *name,
                   int versioning_flags, intmax_t secs, int nsecs)
{
    size_t i;
    size_t j;
    size_t lc = 0;
    size_t oc = 0;
    int n;
    struct ndn_indexbuf *nix = NULL;
    int myres = -1;
    int already_versioned = 0;
    int ok_flags = (NDN_V_REPLACE | NDN_V_HIGH | NDN_V_NOW | NDN_V_NESTOK);
    // XXX - right now we ignore h, but in the future we may use it to try to avoid non-monotonicies in the versions.
    
    nix = ndn_indexbuf_create();
    n = ndn_name_split(name, nix);
    if (n < 0)
        goto Finish;
    if ((versioning_flags & ~ok_flags) != 0)
        goto Finish;        
    /* Check for existing version component */
    if (n >= 1) {
        oc = nix->buf[n-1];
        lc = nix->buf[n] - oc;
        if (lc <= 11 && lc >= 6 && name->buf[oc + 2] == NDN_MARKER_VERSION)
            already_versioned = 1;
    }
    myres = 0;
    if (already_versioned &&
        (versioning_flags & (NDN_V_REPLACE | NDN_V_NESTOK)) == 0)
        goto Finish;
    name->length -= 1; /* Strip name closer */
    i = name->length;
    myres |= ndn_charbuf_append_tt(name, NDN_DTAG_Component, NDN_DTAG);
    if ((versioning_flags & NDN_V_NOW) != 0)
        myres |= ndnb_append_now_blob(name, NDN_MARKER_VERSION);
    else {
        myres |= ndnb_append_timestamp_blob(name, NDN_MARKER_VERSION, secs, nsecs);
    }
    myres |= ndn_charbuf_append_closer(name); /* </Component> */
    if (myres < 0) {
        name->length = i;
        goto CloseName;
    }
    j = name->length;
    if (already_versioned && (versioning_flags & NDN_V_REPLACE) != 0) {
        oc = nix->buf[n-1];
        lc = nix->buf[n] - oc;
        if ((versioning_flags & NDN_V_HIGH) != 0 &&
            memcmp(name->buf + oc, name->buf + i, j - i) > 0) {
            /* Supplied version is in the future. */
            name->length = i;
            // XXX - we could try harder to make this work, for now just error out
            myres = -1;
            goto CloseName;
        }
        memmove(name->buf + oc, name->buf + i, j - i);
        name->length -= lc;
    }
CloseName:
    myres |= ndn_charbuf_append_closer(name); /* </Name> */
Finish:
    myres = (myres < 0) ? -1 : 0;
    ndn_indexbuf_destroy(&nix);
    return(myres);
}
Beispiel #17
0
/**
 * This appends a filter useful for
 * excluding everything between two 'fenceposts' in an Exclude construct.
 */
static void
append_filter_all(struct ndn_charbuf *c)
{
    ndn_charbuf_append_tt(c, NDN_DTAG_Any, NDN_DTAG);
    ndn_charbuf_append_closer(c);
}
Beispiel #18
0
int
main(int argc, char **argv)
{
    const char *progname = argv[0];
    struct ndn *ndn = NULL;
    struct ndn_charbuf *name = NULL;
    struct ndn_charbuf *pname = NULL;
    struct ndn_charbuf *temp = NULL;
    struct ndn_charbuf *extopt = NULL;
    long expire = -1;
    int versioned = 0;
    size_t blocksize = 8*1024;
    int status = 0;
    int res;
    ssize_t read_res;
    unsigned char *buf = NULL;
    enum ndn_content_type content_type = NDN_CONTENT_DATA;
    struct ndn_closure in_interest = {.p=&incoming_interest};
    const char *postver = NULL;
    const char *key_uri = NULL;
    int force = 0;
    int verbose = 0;
    int timeout = -1;
    int setfinal = 0;
    int prefixcomps = -1;
    int fd;
    struct ndn_signing_params sp = NDN_SIGNING_PARAMS_INIT;
    
    while ((res = getopt(argc, argv, "e:fhk:lvV:p:t:w:x:")) != -1) {
        switch (res) {
            case 'e':
                if (extopt == NULL)
                    extopt = ndn_charbuf_create();
                fd = open(optarg, O_RDONLY);
                if (fd < 0) {
                    perror(optarg);
                    exit(1);
                }
                for (;;) {
                    read_res = read(fd, ndn_charbuf_reserve(extopt, 64), 64);
                    if (read_res <= 0)
                        break;
                    extopt->length += read_res;
                }
                if (read_res < 0)
                    perror(optarg);
                close(fd);
                break;
            case 'f':
                force = 1;
                break;
            case 'l':
                setfinal = 1; // set FinalBlockID to last comp of name
                break;
            case 'k':
                key_uri = optarg;
                break;
            case 'p':
                prefixcomps = atoi(optarg);
                if (prefixcomps < 0)
                    usage(progname);
                break;
            case 'x':
                expire = atol(optarg);
                if (expire <= 0)
                    usage(progname);
                break;
            case 'v':
                verbose = 1;
                break;
            case 'V':
                versioned = 1;
                postver = optarg;
                if (0 == memcmp(postver, "%00", 3))
                    setfinal = 1;
                break;
            case 'w':
                timeout = atol(optarg);
                if (timeout <= 0)
                    usage(progname);
                timeout *= 1000;
                break;
            case 't':
                if (0 == strcasecmp(optarg, "DATA")) {
                    content_type = NDN_CONTENT_DATA;
                    break;
                }
                if (0 == strcasecmp(optarg, "ENCR")) {
                    content_type = NDN_CONTENT_ENCR;
                    break;
                }
                if (0 == strcasecmp(optarg, "GONE")) {
                    content_type = NDN_CONTENT_GONE;
                    break;
                }
                if (0 == strcasecmp(optarg, "KEY")) {
                    content_type = NDN_CONTENT_KEY;
                    break;
                }
                if (0 == strcasecmp(optarg, "LINK")) {
                    content_type = NDN_CONTENT_LINK;
                    break;
                }
                if (0 == strcasecmp(optarg, "NACK")) {
                    content_type = NDN_CONTENT_NACK;
                    break;
                }
                content_type = atoi(optarg);
                if (content_type > 0 && content_type <= 0xffffff)
                    break;
                fprintf(stderr, "Unknown content type %s\n", optarg);
                /* FALLTHRU */
            default:
            case 'h':
                usage(progname);
                break;
        }
    }
    argc -= optind;
    argv += optind;
    if (argv[0] == NULL)
        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);
    }
    if (argv[1] != NULL)
        fprintf(stderr, "%s warning: extra arguments ignored\n", progname);
    
    /* Preserve the original prefix, in case we add versioning,
     * but trim it down if requested for the interest filter registration
     */
    pname = ndn_charbuf_create();
    ndn_charbuf_append(pname, name->buf, name->length);
    if (prefixcomps >= 0) {
        res = ndn_name_chop(pname, NULL, prefixcomps);
        if (res < 0) {
            fprintf(stderr, "%s: unable to trim name to %d component%s.\n",
                    progname, prefixcomps, prefixcomps == 1 ? "" : "s");
            exit(1);
        }
    }
    /* Connect to ndnd */
    ndn = ndn_create();
    if (ndn_connect(ndn, NULL) == -1) {
        perror("Could not connect to ndnd");
        exit(1);
    }

    /* Read the actual user data from standard input */
    buf = calloc(1, blocksize);
    read_res = read_full(0, buf, blocksize);
    if (read_res < 0) {
        perror("read");
        read_res = 0;
        status = 1;
    }
        
    /* Tack on the version component if requested */
    if (versioned) {
        res = ndn_create_version(ndn, name, NDN_V_REPLACE | NDN_V_NOW | NDN_V_HIGH, 0, 0);
        if (res < 0) {
            fprintf(stderr, "%s: ndn_create_version() failed\n", progname);
            exit(1);
        }
        if (postver != NULL) {
            res = ndn_name_from_uri(name, postver);
            if (res < 0) {
                fprintf(stderr, "-V %s: invalid name suffix\n", postver);
                exit(0);
            }
        }
    }
    temp = ndn_charbuf_create();
    
    /* Ask for a FinalBlockID if appropriate. */
    if (setfinal)
        sp.sp_flags |= NDN_SP_FINAL_BLOCK;
    
    if (res < 0) {
        fprintf(stderr, "Failed to create signed_info (res == %d)\n", res);
        exit(1);
    }
    
    /* Set content type */
    sp.type = content_type;
    
    /* Set freshness */
    if (expire >= 0) {
        if (sp.template_ndnb == NULL) {
            sp.template_ndnb = ndn_charbuf_create();
            ndn_charbuf_append_tt(sp.template_ndnb, NDN_DTAG_SignedInfo, NDN_DTAG);
        }
        else if (sp.template_ndnb->length > 0) {
            sp.template_ndnb->length--;
        }
        ndnb_tagged_putf(sp.template_ndnb, NDN_DTAG_FreshnessSeconds, "%ld", expire);
        sp.sp_flags |= NDN_SP_TEMPL_FRESHNESS;
        ndn_charbuf_append_closer(sp.template_ndnb);
    }
    
    /* Set key locator, if supplied */
    if (key_uri != NULL) {
        struct ndn_charbuf *c = ndn_charbuf_create();
        res = ndn_name_from_uri(c, key_uri);
        if (res < 0) {
            fprintf(stderr, "%s is not a valid ndnx URI\n", key_uri);
            exit(1);
        }
        if (sp.template_ndnb == NULL) {
            sp.template_ndnb = ndn_charbuf_create();
            ndn_charbuf_append_tt(sp.template_ndnb, NDN_DTAG_SignedInfo, NDN_DTAG);
        }
        else if (sp.template_ndnb->length > 0) {
            sp.template_ndnb->length--;
        }
        ndn_charbuf_append_tt(sp.template_ndnb, NDN_DTAG_KeyLocator, NDN_DTAG);
        ndn_charbuf_append_tt(sp.template_ndnb, NDN_DTAG_KeyName, NDN_DTAG);
        ndn_charbuf_append(sp.template_ndnb, c->buf, c->length);
        ndn_charbuf_append_closer(sp.template_ndnb);
        ndn_charbuf_append_closer(sp.template_ndnb);
        sp.sp_flags |= NDN_SP_TEMPL_KEY_LOCATOR;
        ndn_charbuf_append_closer(sp.template_ndnb);
        ndn_charbuf_destroy(&c);
    }

    if (extopt != NULL && extopt->length > 0) {
        if (sp.template_ndnb == NULL) {
            sp.template_ndnb = ndn_charbuf_create();
            ndn_charbuf_append_tt(sp.template_ndnb, NDN_DTAG_SignedInfo, NDN_DTAG);
        }
        else if (sp.template_ndnb->length > 0) {
            sp.template_ndnb->length--;
        }
        ndnb_append_tagged_blob(sp.template_ndnb, NDN_DTAG_ExtOpt,
                                extopt->buf, extopt->length);
        sp.sp_flags |= NDN_SP_TEMPL_EXT_OPT;
        ndn_charbuf_append_closer(sp.template_ndnb);
    }
    
    /* Create the signed content object, ready to go */
    temp->length = 0;
    res = ndn_sign_content(ndn, temp, name, &sp, buf, read_res);
    if (res != 0) {
        fprintf(stderr, "Failed to encode ContentObject (res == %d)\n", res);
        exit(1);
    }
    if (read_res == blocksize) {
        read_res = read_full(0, buf, 1);
        if (read_res == 1) {
            fprintf(stderr, "%s: warning - truncated data\n", argv[0]);
            status = 1;
        }
    }
    free(buf);
    buf = NULL;
    if (force) {
        /* At user request, send without waiting to see an interest */
        res = ndn_put(ndn, temp->buf, temp->length);
        if (res < 0) {
            fprintf(stderr, "ndn_put failed (res == %d)\n", res);
            exit(1);
        }
    }
    else {
        in_interest.data = temp;
        /* Set up a handler for interests */
        res = ndn_set_interest_filter(ndn, pname, &in_interest);
        if (res < 0) {
            fprintf(stderr, "Failed to register interest (res == %d)\n", res);
            exit(1);
        }
        res = ndn_run(ndn, timeout);
        if (in_interest.intdata == 0) {
            if (verbose)
                fprintf(stderr, "Nobody's interested\n");
            exit(1);
        }
    }
    
    if (verbose) {
        struct ndn_charbuf *uri = ndn_charbuf_create();
        uri->length = 0;
        ndn_uri_append(uri, name->buf, name->length, 1);
        printf("wrote %s\n", ndn_charbuf_as_string(uri));
        ndn_charbuf_destroy(&uri);
    }
    ndn_destroy(&ndn);
    ndn_charbuf_destroy(&name);
    ndn_charbuf_destroy(&pname);
    ndn_charbuf_destroy(&temp);
    ndn_charbuf_destroy(&sp.template_ndnb);
    ndn_charbuf_destroy(&extopt);
    exit(status);
}