Exemple #1
0
int
main(int argc, char *argv[])
{
	int res;
	struct ccn *ccn = NULL;
	struct ccn_charbuf *name = NULL;
	struct ccn_closure *incoming;

	if (argc != 2)
		usage(argv[0]);

	name = ccn_charbuf_create();

	res = ccn_name_from_uri(name, argv[1]);
	if (res < 0) {
		fprintf(stderr, "invalid ccn URI: %s\n", argv[1]);
		usage(argv[0]);
	}
	ccn = ccn_create();

	res = ccn_connect(ccn, NULL);
	if (res < 0) {
		fprintf(stderr, "can't connect to ccn: %d\n", res);
		ccn_perror(ccn, "ccn_connect");
		exit(1);
	}

	incoming = calloc(1, sizeof(*incoming));
	incoming->p = incoming_handler;
	g_working = true;
	gettimeofday(&g_start, NULL);
	res = ccn_express_interest(ccn, name, incoming, NULL);

	while (g_working && res >= 0)
		res = ccn_run(ccn, 100);

	free(incoming);

	if (res < 0) {
		ccn_perror(ccn, "ccn_run");
		exit(1);
	}

	ccn_charbuf_destroy(&name);
	ccn_destroy(&ccn);

	return 0;
}
Exemple #2
0
enum ccn_upcall_res
ccn_proxy_server_decap_interest(
    struct ccn_closure *selfp,
    enum ccn_upcall_kind kind,
    struct ccn_upcall_info *info)
{
    enum ccn_upcall_res upcall_res = CCN_UPCALL_RESULT_ERR;
    struct ccn_proxy_server* server = selfp->data;
    struct ccn_proxy* proxy = server->proxy;
    // struct ccn_proxy *proxy = (selfp->data)->proxy;
    int i, res;
    unsigned char* random = NULL;
    struct ccn_charbuf* request_name;
    struct ccn_indexbuf* request_comps;
    struct ccn_charbuf* signed_info;
    struct ccn_signing_params sp = CCN_SIGNING_PARAMS_INIT;

    random = (unsigned char*)malloc(RESP_SIZE * sizeof(unsigned char));
    for (i = 0; i < RESP_SIZE; i++) random[i] = rand() % 256;

    handled++;
    printf("random buffer created - creating signed object: %d\n", handled);

    // Create a valid, signed ContentObject
    request_name = NULL;
    request_comps = NULL;
    res = ccn_util_extract_name(info->interest_ccnb, info->interest_comps, &request_name, &request_comps);
    signed_info = ccn_charbuf_create();
    sp.type = CCN_CONTENT_DATA;
    res = ccn_sign_content(proxy->handle, signed_info, request_name, &sp, random, RESP_SIZE);

    // Drop the content object into the socket (handle) to be sent back downstream
    res = ccn_put(proxy->handle, signed_info->buf, signed_info->length);
    if (res < 0)
    {
        ccn_perror(proxy->handle, NULL);
        printf("WTF?!?!?!\n");
    }
    else
    {
        printf("successfully put content object in the socket\n");
    }

    // // TODO: need to fill up random buffer and then inject it using ccn_put

    return(CCN_UPCALL_RESULT_OK);
}
Exemple #3
0
int NdnMediaProcess::startThread() {
    struct ccn *h;

    /* Shut down any lingering session */
    while (ndnState.ccn != NULL) {
        ndnState.active = 0;
        printf("waiting for old session to die\n");
    }
    h = ccn_create();
    if (ccn_connect(h, NULL) == -1) {
        ccn_perror(h, "Failed to contact ccnd");
        ccn_destroy(&h);
        return (-1);
    }
	pthread_mutexattr_init(&ccn_attr);
	pthread_mutexattr_settype(&ccn_attr, PTHREAD_MUTEX_RECURSIVE);     
	pthread_mutex_init(&ccn_mutex, &ccn_attr);

    ndnState.ccn = h; 
	pfds[0].fd = ccn_get_connection_fd(ndnState.ccn);
	pfds[0].events = POLLIN;
	clock = new QTimer(this);
	connect(clock, SIGNAL(timeout()), this, SLOT(tick()));
	clock->start(PER_PACKET_LEN);
	
    if (! isRunning()) {
        fprintf(stderr, "Starting voice thread in media_pro\n"); 
        ndnState.active = true;

        start(QThread::HighestPriority);
#ifdef Q_OS_LINUX
        int policy;
        struct sched_param param;
        if (pthread_getschedparam(pthread_self(), &policy, &param) == 0) {
            if (policy == SCHED_OTHER) {
                policy = SCHED_FIFO;
                param.sched_priority = 1;
                pthread_setschedparam(pthread_self(), policy, &param);
            }
        }
#endif
    }
	else
		fprintf(stderr, "what the hell\n");
    return 0;
}
Exemple #4
0
int main(void) {
	int res;
	struct ccn *ccn= NULL;
	struct ccn_charbuf *name = NULL;
	struct ccn_charbuf *result = NULL;
	struct ccn_closure in_interest = {.p=&incoming_interest};
	struct ccn_signing_params sp = CCN_SIGNING_PARAMS_INIT; // Parameters for creating signed content objects.
	char rawcontentbuf[1024] = "";
	size_t content_size;

	// 1. Name
	name = ccn_charbuf_create();
	res = ccn_name_from_uri(name, URI);
	if (res < 0) {
		fprintf(stderr, "bad ccn URI: %s\n", URI);
		exit(1);
	}

	// 2. ccn_create, and ccn_connect
	ccn = ccn_create();
	res = ccn_connect(ccn, NULL);
	if (res < 0) {
		fprintf(stderr, "can't connect to ccnd: %d\n", res);
		ccn_perror(ccn, "ccn_connect");
		exit(1);
	}

	// 3. Create the signed content object, set up &sp - signing params;

	// Extend a Name with a new version stamp if requested
	/*int versioned = 0;
	const char *postver = NULL;
	memcmp(postver, "%00", 3);
	if (versioned) {
		res = ccn_create_version(ccn, name, CCN_V_REPLACE | CCN_V_NOW | CCN_V_HIGH, 0, 0);
		if (res < 0) {
			fprintf(stderr, "%s: ccn_create_version() failed\n");
			exit(1);
		}
		if (postver != NULL) {
			res = ccn_name_from_uri(name, postver);
			if (res < 0) {
				fprintf(stderr, "-V %s: invalid name suffix\n", postver);
				exit(0);
			}
		}
	} */

	// Ask for a FinalBlockID if appropriate
	/*int setfinal = 0;
	if (setfinal)
		sp.sp_flags |= CCN_SP_FINAL_BLOCK;
	*/

	// Set content type
	//sp.type = content_type;

	// Set freshness
	/* The template_ccnb may contain a ccnb-encoded SignedInfo to supply selected fields from under
	 * the direction of sp_flags.
	 */
	/*int expire = -1;
	if (expire >= 0) {
		if (sp.template_ccnb == NULL) {
			sp.template_ccnb = ccn_charbuf_create();
			ccn_charbuf_append_tt(sp.template_ccnb, CCN_DTAG_SignedInfo, CCN_DTAG);
			}
		else if (sp.template_ccnb -> length > 0) {
			sp.template_ccnb -> length--;
		}
		ccnb_tagged_putf(sp.template_ccnb, CCN_DTAG_FreshnessSeconds, "%ld", expire);
		sp.sp_flags |= CCN_SP_TEMPL_FRESHNESS;
		ccn_charbuf_append_closer(sp.template_ccnb);
	}
	*/

	// Set key locator, if supplied
	/*const char *key_uri = NULL;
	//key_uri = optarg;
	if (key_uri != NULL) {
		struct ccn_charbuf *c = ccn_charbuf_create();
		res = ccn_name_from_uri(c, key_uri);
		if (res < 0) {
			fprintf(stderr, "%s is not a valid ccnx URI\n", key_uri);
			exit(1);
		}
		if (sp.template_ccnb == NULL) {
			sp.template_ccnb = ccn_charbuf_create();
			ccn_charbuf_append_tt(sp.template_ccnb, CCN_DTAG_SignedInfo, CCN_DTAG);
		}
		else if (sp.template_ccnb -> length > 0) {
			sp.template_ccnb -> length--;
		}
		ccn_charbuf_append_tt(sp.template_ccnb, CCN_DTAG_KeyLocator, CCN_DTAG);
		ccn_charbuf_append_tt(sp.template_ccnb, CCN_DTAG_KeyName, CCN_DTAG);
		ccn_charbuf_append(sp.template_ccnb, c->buf, c->length);
		ccn_charbuf_append_closer(sp.template_ccnb);
		ccn_charbuf_append_closer(sp.template_ccnb);
		sp.sp_flags |= CCN_SP_TEMPL_KEY_LOCATOR;
		ccn_charbuf_append_closer(sp.template_ccnb);
		ccn_charbuf_destroy(&c);
	}
	*/

	// Set up a handler for interest
	ccn_set_interest_filter(ccn, name, &in_interest);


	// 4. Use ccn_sign_content to create the signed content object with composed &sp,
	// if (force) { ccn_put; } # ccn_get means send ccn binary. For normal clients, this should be a ContentObject sent in response to an Interest.
	// else { ccn_set_interest_filter; ccn_run; }

	result = ccn_charbuf_create();
	result -> length = 0;
	strcat(rawcontentbuf, CONTENTDATA);
	content_size = strlen(rawcontentbuf);
	//sp.sp_flags |= CCN_SP_FINAL_BLOCK;
	res = ccn_sign_content(ccn, result, name, &sp, rawcontentbuf, content_size);
	if (res < 0) {
		ccn_perror(ccn, "ccn_sign_content");
		exit(1);
	}

	printf("Content signed, trying to send the data...\n");
	res = ccn_put(ccn, result->buf, result->length);
	if (res < 0) {
		ccn_perror(ccn, "ccn_put");
		printf("Failed to send content object: res = %d\n", res);
		exit(1);
	}
	else
		printf("ccn_put done, content object sent.\n");

	// ccn_run serves as the event loop when timeout = -1
	res = ccn_run(ccn, -1);  // Loop here all the time until it is killed, then "Event loop..."
	printf("Event loop...\n");
	if (res < 0) {
		ccn_perror(ccn, "ccn_run");
		printf("Error: ccn_run\n");
		exit(1);
	}

	ccn_destroy(&ccn);
	ccn_charbuf_destroy(&name);
	ccn_charbuf_destroy(&result);
	ccn_charbuf_destroy(&sp.template_ccnb);
	printf("ccn handle is destroyed... \n");

	return 0;

}
Exemple #5
0
int
main(int argc, char **argv)
{
    struct ccn *h = NULL;
    struct ccn_charbuf *name = NULL;
    struct ccn_charbuf *null_name = NULL;
    struct ccn_charbuf *name_prefix = NULL;
    struct ccn_charbuf *newface = NULL;
    struct ccn_charbuf *prefixreg = NULL;
    struct ccn_charbuf *resultbuf = NULL;
    struct ccn_charbuf *temp = NULL;
    struct ccn_charbuf *templ = NULL;
    const unsigned char *ptr = NULL;
    size_t length = 0;
    const char *arg = NULL;
    const char *progname = NULL;
    struct ccn_parsed_ContentObject pcobuf = {0};
    struct ccn_face_instance face_instance_storage = {0};
    struct ccn_face_instance *face_instance = &face_instance_storage;
    struct ccn_forwarding_entry forwarding_entry_storage = {0};
    struct ccn_forwarding_entry *forwarding_entry = &forwarding_entry_storage;
    struct ccn_signing_params sp = CCN_SIGNING_PARAMS_INIT;
    struct ccn_charbuf *keylocator_templ = NULL;
    struct ccn_keystore *keystore = NULL;
    long expire = -1;
    int ipproto;
    unsigned char ccndid_storage[32] = {0};
    const unsigned char *ccndid = NULL;
    size_t ccndid_size = 0;
    int res;
    int opt;

    progname = argv[0];
    while ((opt = getopt(argc, argv, "h")) != -1) {
        switch (opt) {
        case 'h':
        default:
            usage(progname);
        }
    }

    /* Sanity check the URI and argument count */
    arg = argv[optind];
    if (arg == NULL)
        usage(progname);
    name = ccn_charbuf_create();
    res = ccn_name_from_uri(name, arg);
    if (res < 0) {
        fprintf(stderr, "%s: bad ccn URI: %s\n", progname, arg);
        exit(1);
    }
    if (argc - optind < 3 || argc - optind > 4)
        usage(progname);

    h = ccn_create();
    res = ccn_connect(h, NULL);
    if (res < 0) {
        ccn_perror(h, "ccn_connect");
        exit(1);
    }

    newface = ccn_charbuf_create();
    temp = ccn_charbuf_create();
    templ = ccn_charbuf_create();
    keylocator_templ = ccn_charbuf_create();

    resultbuf = ccn_charbuf_create();
    name_prefix = ccn_charbuf_create();
    null_name = ccn_charbuf_create();
    CHKRES(ccn_name_init(null_name));

    keystore = ccn_keystore_create();

    /* We need to figure out our local ccnd's CCIDID */
    /* Set up our Interest template to indicate scope 1 */
    ccn_charbuf_reset(templ);
    ccnb_element_begin(templ, CCN_DTAG_Interest);
    ccnb_element_begin(templ, CCN_DTAG_Name);
    ccnb_element_end(templ);	/* </Name> */
    ccnb_tagged_putf(templ, CCN_DTAG_Scope, "1");
    ccnb_element_end(templ);	/* </Interest> */

    ccn_charbuf_reset(name);
    CHKRES(res = ccn_name_from_uri(name, "ccnx:/%C1.M.S.localhost/%C1.M.SRV/ccnd/KEY"));
    CHKRES(res = ccn_get(h, name, templ, 200, resultbuf, &pcobuf, NULL, 0));
    res = ccn_ref_tagged_BLOB(CCN_DTAG_PublisherPublicKeyDigest,
                              resultbuf->buf,
                              pcobuf.offset[CCN_PCO_B_PublisherPublicKeyDigest],
                              pcobuf.offset[CCN_PCO_E_PublisherPublicKeyDigest],
                              &ccndid, &ccndid_size);
    CHKRES(res);
    if (ccndid_size > sizeof(ccndid_storage))
        CHKRES(-1);
    memcpy(ccndid_storage, ccndid, ccndid_size);
    ccndid = ccndid_storage;

    face_instance->action = "newface";
    face_instance->ccnd_id = ccndid;
    face_instance->ccnd_id_size = ccndid_size;
    if (strcmp(argv[optind + 1], "tcp") == 0)
        ipproto = 6;
    else if (strcmp(argv[optind + 1], "udp") == 0)
        ipproto = 17;
    else
        ipproto = atoi(argv[optind + 1]);
    face_instance->descr.ipproto = ipproto; // XXX - 6 = tcp or 17 = udp
    face_instance->descr.address = argv[optind + 2];
    face_instance->descr.port = argv[optind + 3];
    if (face_instance->descr.port == NULL)
        face_instance->descr.port = CCN_DEFAULT_UNICAST_PORT;
    face_instance->descr.mcast_ttl = -1;
    face_instance->lifetime = (~0U) >> 1;

    CHKRES(res = ccnb_append_face_instance(newface, face_instance));
    temp->length = 0;
    CHKRES(ccn_charbuf_putf(temp, "%s/.ccnx/.ccnx_keystore", getenv("HOME")));
    res = ccn_keystore_init(keystore,
                            ccn_charbuf_as_string(temp),
                            "Th1s1sn0t8g00dp8ssw0rd.");
    CHKRES(res);

    ccnb_element_begin(keylocator_templ, CCN_DTAG_SignedInfo);
    ccnb_element_begin(keylocator_templ, CCN_DTAG_KeyLocator);
    ccnb_element_begin(keylocator_templ, CCN_DTAG_Key);
    CHKRES(ccn_append_pubkey_blob(keylocator_templ, ccn_keystore_public_key(keystore)));
    ccnb_element_end(keylocator_templ);	/* </Key> */
    ccnb_element_end(keylocator_templ);	/* </KeyLocator> */
    ccnb_element_end(keylocator_templ);    /* </SignedInfo> */
    sp.template_ccnb = keylocator_templ;
    sp.sp_flags |= CCN_SP_TEMPL_KEY_LOCATOR;
    sp.freshness = expire;
    ccn_charbuf_reset(temp);
    res = ccn_sign_content(h, temp, null_name, &sp,
                           newface->buf, newface->length);
    CHKRES(res);

    /* Create the new face */
    CHKRES(ccn_name_init(name));
    CHKRES(ccn_name_append_str(name, "ccnx"));
    CHKRES(ccn_name_append(name, ccndid, ccndid_size));
    CHKRES(ccn_name_append(name, "newface", 7));
    CHKRES(ccn_name_append(name, temp->buf, temp->length));
    res = ccn_get(h, name, templ, 1000, resultbuf, &pcobuf, NULL, 0);
    if (res < 0) {
        fprintf(stderr, "no response from face creation request\n");
        exit(1);
    }
    ptr = resultbuf->buf;
    length = resultbuf->length;
    res = ccn_content_get_value(resultbuf->buf, resultbuf->length, &pcobuf, &ptr, &length);
    CHKRES(res);
    face_instance = ccn_face_instance_parse(ptr, length);
    if (face_instance == NULL)
        CHKRES(res = -1);
    CHKRES(face_instance->faceid);

    /* Finally, register the prefix */
    ccn_charbuf_reset(name_prefix);
    CHKRES(ccn_name_from_uri(name_prefix, arg));
    forwarding_entry->action = "prefixreg";
    forwarding_entry->name_prefix = name_prefix;
    forwarding_entry->ccnd_id = ccndid;
    forwarding_entry->ccnd_id_size = ccndid_size;
    forwarding_entry->faceid = face_instance->faceid;
    forwarding_entry->flags = -1; /* let ccnd decide */
    forwarding_entry->lifetime = (~0U) >> 1;
    prefixreg = ccn_charbuf_create();
    CHKRES(res = ccnb_append_forwarding_entry(prefixreg, forwarding_entry));
    ccn_charbuf_reset(temp);
    res = ccn_sign_content(h, temp, null_name, &sp,
                           prefixreg->buf, prefixreg->length);
    CHKRES(res);
    CHKRES(ccn_name_init(name));
    CHKRES(ccn_name_append_str(name, "ccnx"));
    CHKRES(ccn_name_append(name, ccndid, ccndid_size));
    CHKRES(ccn_name_append_str(name, "prefixreg"));
    CHKRES(ccn_name_append(name, temp->buf, temp->length));
    res = ccn_get(h, name, templ, 1000, resultbuf, &pcobuf, NULL, 0);
    if (res < 0) {
        fprintf(stderr, "no response from prefix registration request\n");
        exit(1);
    }
    fprintf(stderr, "Prefix %s will be forwarded to face %d\n", arg, face_instance->faceid);

    /* We're about to exit, so don't bother to free everything. */
    ccn_destroy(&h);
    exit(res < 0);
}
Exemple #6
0
/**
 * 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);
}
Exemple #7
0
int
main(int argc, char **argv)
{
    struct ccn *h = NULL;
    struct ccn_charbuf *name = NULL;
    struct ccn_charbuf *templ = NULL;
    struct ccn_charbuf *resultbuf = NULL;
    const char *arg = NULL;
    struct ccn_parsed_ContentObject pcobuf = { 0 };
    int res;
    char ch;
    int allow_stale = 0;
    int content_only = 0;
    const unsigned char *ptr;
    size_t length;
    int resolve_version = 0;
    const char *env_timeout = getenv("CCN_LINGER");
    int timeout_ms = 3000;
    
    while ((ch = getopt(argc, argv, "hacv")) != -1) {
        switch (ch) {
            case 'a':
                allow_stale = 1;
                break;
            case 'c':
                content_only = 1;
                break;
            case 'v':
                if (resolve_version == 0)
                    resolve_version = CCN_V_HIGHEST;
                else
                    resolve_version = CCN_V_HIGH;
                break;
            case 'h':
            default:
                usage(argv[0]);
        }
    }
    arg = argv[optind];
    if (arg == NULL)
        usage(argv[0]);
    name = ccn_charbuf_create();
    res = ccn_name_from_uri(name, arg);
    if (res < 0) {
        fprintf(stderr, "%s: bad ccn URI: %s\n", argv[0], arg);
        exit(1);
    }
    if (argv[optind + 1] != NULL)
        fprintf(stderr, "%s warning: extra arguments ignored\n", argv[0]);
    h = ccn_create();
    res = ccn_connect(h, NULL);
    if (res < 0) {
        ccn_perror(h, "ccn_connect");
        exit(1);
    }
    if (res < 0) {
        fprintf(stderr, "%s: bad ccn URI: %s\n", argv[0], arg);
        exit(1);
    }
    if (env_timeout != NULL && (res = atoi(env_timeout)) > 0) {
		timeout_ms = res * 1000;
    }
	if (allow_stale || env_timeout != NULL) {
        templ = ccn_charbuf_create();
        ccn_charbuf_append_tt(templ, CCN_DTAG_Interest, CCN_DTAG);
        ccn_charbuf_append_tt(templ, CCN_DTAG_Name, CCN_DTAG);
        ccn_charbuf_append_closer(templ); /* </Name> */
		if (allow_stale) {
			ccn_charbuf_append_tt(templ, CCN_DTAG_AnswerOriginKind, CCN_DTAG);
			ccnb_append_number(templ,
							   CCN_AOK_DEFAULT | CCN_AOK_STALE);
			ccn_charbuf_append_closer(templ); /* </AnswerOriginKind> */
		}
		if (env_timeout != NULL) {
			/*
			 * Choose the interest lifetime so there are at least 3
			 * expressions (in the unsatisfied case).
			 */
			unsigned char buf[3] = { 0 };
			unsigned lifetime;
			int i;
			if (timeout_ms > 60000)
				lifetime = 30 << 12;
			else {
				lifetime = timeout_ms * 2 / 5 * 4096 / 1000;
			}
			for (i = sizeof(buf) - 1; i >= 0; i--, lifetime >>= 8)
				buf[i] = lifetime & 0xff;
			ccnb_append_tagged_blob(templ, CCN_DTAG_InterestLifetime, buf, sizeof(buf));
		}