Esempio n. 1
0
isccc_sexpr_t *
isccc_alist_define(isccc_sexpr_t *alist, const char *key, isccc_sexpr_t *value)
{
	isccc_sexpr_t *kv, *k, *elt;

	kv = isccc_alist_assq(alist, key);
	if (kv == NULL) {
		/*
		 * New association.
		 */
		k = isccc_sexpr_fromstring(key);
		if (k == NULL)
			return (NULL);
		kv = isccc_sexpr_cons(k, value);
		if (kv == NULL) {
			isccc_sexpr_free(&kv);
			return (NULL);
		}
		elt = isccc_sexpr_addtolist(&alist, kv);
		if (elt == NULL) {
			isccc_sexpr_free(&kv);
			return (NULL);
		}
	} else {
		/*
		 * We've already got an entry for this key.  Replace it.
		 */
		isccc_sexpr_free(&CDR(kv));
		CDR(kv) = value;
	}

	return (kv);
}
Esempio n. 2
0
void
isccc_sexpr_free(isccc_sexpr_t **sexprp) {
	isccc_sexpr_t *sexpr;
	isccc_sexpr_t *item;

	sexpr = *sexprp;
	if (sexpr == NULL)
		return;
	switch (sexpr->type) {
	case ISCCC_SEXPRTYPE_STRING:
		free(sexpr->value.as_string);
		break;
	case ISCCC_SEXPRTYPE_DOTTEDPAIR:
		item = CAR(sexpr);
		if (item != NULL)
			isccc_sexpr_free(&item);
		item = CDR(sexpr);
		if (item != NULL)
			isccc_sexpr_free(&item);
		break;
	case ISCCC_SEXPRTYPE_BINARY:
		free(sexpr->value.as_region.rstart);
		break;
	}
	free(sexpr);

	*sexprp = NULL;
}
Esempio n. 3
0
static isc_result_t
list_fromwire(isccc_region_t *source, isccc_sexpr_t **listp)
{
	isccc_sexpr_t *list, *value;
	isc_result_t result;

	list = NULL;
	while (!REGION_EMPTY(*source)) {
		value = NULL;
		result = value_fromwire(source, &value);
		if (result != ISC_R_SUCCESS) {
			isccc_sexpr_free(&list);
			return (result);
		}
		if (isccc_sexpr_addtolist(&list, value) == NULL) {
			isccc_sexpr_free(&value);
			isccc_sexpr_free(&list);
			return (result);
		}
	}

	*listp = list;

	return (ISC_R_SUCCESS);
}
Esempio n. 4
0
static isc_result_t
createmessage(isc_uint32_t version, const char *from, const char *to,
	      isc_uint32_t serial, isccc_time_t now,
	      isccc_time_t expires, isccc_sexpr_t **alistp,
	      isc_boolean_t want_expires)
{
	isccc_sexpr_t *alist, *_ctrl, *_data;
	isc_result_t result;

	REQUIRE(alistp != NULL && *alistp == NULL);

	if (version != 1)
		return (ISCCC_R_UNKNOWNVERSION);

	alist = isccc_alist_create();
	if (alist == NULL)
		return (ISC_R_NOMEMORY);

	result = ISC_R_NOMEMORY;

	_ctrl = isccc_alist_create();
	if (_ctrl == NULL)
		goto bad;
	if (isccc_alist_define(alist, "_ctrl", _ctrl) == NULL) {
		isccc_sexpr_free(&_ctrl);
		goto bad;
	}

	_data = isccc_alist_create();
	if (_data == NULL)
		goto bad;
	if (isccc_alist_define(alist, "_data", _data) == NULL) {
		isccc_sexpr_free(&_data);
		goto bad;
	}

	if (isccc_cc_defineuint32(_ctrl, "_ser", serial) == NULL ||
	    isccc_cc_defineuint32(_ctrl, "_tim", now) == NULL ||
	    (want_expires &&
	     isccc_cc_defineuint32(_ctrl, "_exp", expires) == NULL))
		goto bad;
	if (from != NULL &&
	    isccc_cc_definestring(_ctrl, "_frm", from) == NULL)
		goto bad;
	if (to != NULL &&
	    isccc_cc_definestring(_ctrl, "_to", to) == NULL)
		goto bad;

	*alistp = alist;

	return (ISC_R_SUCCESS);

 bad:
	isccc_sexpr_free(&alist);

	return (result);
}
Esempio n. 5
0
isc_result_t
isccc_cc_createresponse(isccc_sexpr_t *message, isccc_time_t now,
			isccc_time_t expires, isccc_sexpr_t **alistp)
{
	char *_frm, *_to, *type = NULL;
	isc_uint32_t serial;
	isccc_sexpr_t *alist, *_ctrl, *_data;
	isc_result_t result;

	REQUIRE(alistp != NULL && *alistp == NULL);

	_ctrl = isccc_alist_lookup(message, "_ctrl");
	_data = isccc_alist_lookup(message, "_data");
	if (!isccc_alist_alistp(_ctrl) || !isccc_alist_alistp(_data) ||
	    isccc_cc_lookupuint32(_ctrl, "_ser", &serial) != ISC_R_SUCCESS ||
	    isccc_cc_lookupstring(_data, "type", &type) != ISC_R_SUCCESS)
		return (ISC_R_FAILURE);
	/*
	 * _frm and _to are optional.
	 */
	_frm = NULL;
	(void)isccc_cc_lookupstring(_ctrl, "_frm", &_frm);
	_to = NULL;
	(void)isccc_cc_lookupstring(_ctrl, "_to", &_to);
	/*
	 * Create the response.
	 */
	alist = NULL;
	result = isccc_cc_createmessage(1, _to, _frm, serial, now, expires,
					 &alist);
	if (result != ISC_R_SUCCESS)
		return (result);

	_ctrl = isccc_alist_lookup(alist, "_ctrl");
	if (_ctrl == NULL) {
		result = ISC_R_FAILURE;
		goto bad;
	}

	_data = isccc_alist_lookup(alist, "_data");
	if (_data == NULL) {
		result = ISC_R_FAILURE;
		goto bad;
	}

	if (isccc_cc_definestring(_ctrl, "_rpl", "1") == NULL ||
	    isccc_cc_definestring(_data, "type", type) == NULL)
	{
		result = ISC_R_NOMEMORY;
		goto bad;
	}

	*alistp = alist;

	return (ISC_R_SUCCESS);

 bad:
	isccc_sexpr_free(&alist);
	return (result);
}
Esempio n. 6
0
void
isccc_alist_delete(isccc_sexpr_t *alist, const char *key)
{
	isccc_sexpr_t *car, *caar, *rest, *prev;

	REQUIRE(isccc_alist_alistp(alist));

	prev = alist;
	rest = CDR(alist);
	while (rest != NULL) {
		INSIST(rest->type == ISCCC_SEXPRTYPE_DOTTEDPAIR);
		car = CAR(rest);
		INSIST(car != NULL && car->type == ISCCC_SEXPRTYPE_DOTTEDPAIR);
		caar = CAR(car);
		if (caar->type == ISCCC_SEXPRTYPE_STRING &&
		    strcmp(caar->value.as_string, key) == 0) {
			CDR(prev) = CDR(rest);
			CDR(rest) = NULL;
			isccc_sexpr_free(&rest);
			break;
		}
		prev = rest;
		rest = CDR(rest);
	}
}
Esempio n. 7
0
static isc_result_t
table_fromwire(isccc_region_t *source, isccc_region_t *secret,
	       isc_uint32_t algorithm, isccc_sexpr_t **alistp)
{
	char key[256];
	isc_uint32_t len;
	isc_result_t result;
	isccc_sexpr_t *alist, *value;
	isc_boolean_t first_tag;
	unsigned char *checksum_rstart;

	REQUIRE(alistp != NULL && *alistp == NULL);

	checksum_rstart = NULL;
	first_tag = ISC_TRUE;
	alist = isccc_alist_create();
	if (alist == NULL)
		return (ISC_R_NOMEMORY);

	while (!REGION_EMPTY(*source)) {
		GET8(len, source->rstart);
		if (REGION_SIZE(*source) < len) {
			result = ISC_R_UNEXPECTEDEND;
			goto bad;
		}
		GET_MEM(key, len, source->rstart);
		key[len] = '\0';	/* Ensure NUL termination. */
		value = NULL;
		result = value_fromwire(source, &value);
		if (result != ISC_R_SUCCESS)
			goto bad;
		if (isccc_alist_define(alist, key, value) == NULL) {
			result = ISC_R_NOMEMORY;
			goto bad;
		}
		if (first_tag && secret != NULL && strcmp(key, "_auth") == 0)
			checksum_rstart = source->rstart;
		first_tag = ISC_FALSE;
	}

	if (secret != NULL) {
		if (checksum_rstart != NULL)
			result = verify(alist, checksum_rstart,
					(unsigned int)
					(source->rend - checksum_rstart),
					algorithm, secret);
		else
			result = ISCCC_R_BADAUTH;
	} else
		result = ISC_R_SUCCESS;

 bad:
	if (result == ISC_R_SUCCESS)
		*alistp = alist;
	else
		isccc_sexpr_free(&alist);

	return (result);
}
Esempio n. 8
0
static void
rndc_recvdone(isc_task_t *task, isc_event_t *event) {
	isccc_sexpr_t *response = NULL;
	isccc_sexpr_t *data;
	isccc_region_t source;
	char *errormsg = NULL;
	char *textmsg = NULL;
	isc_result_t result;

	recvs--;

	if (ccmsg.result == ISC_R_EOF)
		fatal("connection to remote host closed\n"
		      "This may indicate that\n"
		      "* the remote server is using an older version of"
		      " the command protocol,\n"
		      "* this host is not authorized to connect,\n"
		      "* the clocks are not synchronized, or\n"
		      "* the key is invalid.");

	if (ccmsg.result != ISC_R_SUCCESS)
		fatal("recv failed: %s", isc_result_totext(ccmsg.result));

	source.rstart = isc_buffer_base(&ccmsg.buffer);
	source.rend = isc_buffer_used(&ccmsg.buffer);

	DO("parse message",
	   isccc_cc_fromwire(&source, &response, algorithm, &secret));

	data = isccc_alist_lookup(response, "_data");
	if (data == NULL)
		fatal("no data section in response");
	result = isccc_cc_lookupstring(data, "err", &errormsg);
	if (result == ISC_R_SUCCESS) {
		failed = ISC_TRUE;
		fprintf(stderr, "%s: '%s' failed: %s\n",
			progname, command, errormsg);
	}
	else if (result != ISC_R_NOTFOUND)
		fprintf(stderr, "%s: parsing response failed: %s\n",
			progname, isc_result_totext(result));

	result = isccc_cc_lookupstring(data, "text", &textmsg);
	if (result == ISC_R_SUCCESS) {
		if ((!quiet || failed) && strlen(textmsg) != 0U)
			fprintf(failed ? stderr : stdout, "%s\n", textmsg);
	} else if (result != ISC_R_NOTFOUND)
		fprintf(stderr, "%s: parsing response failed: %s\n",
			progname, isc_result_totext(result));

	isc_event_free(&event);
	isccc_sexpr_free(&response);
	if (sends == 0 && recvs == 0) {
		isc_socket_detach(&sock);
		isc_task_shutdown(task);
		RUNTIME_CHECK(isc_app_shutdown() == ISC_R_SUCCESS);
	}
}
Esempio n. 9
0
isc_result_t
isccc_cc_createack(isccc_sexpr_t *message, isc_boolean_t ok,
		   isccc_sexpr_t **ackp)
{
	char *_frm, *_to;
	isc_uint32_t serial;
	isccc_sexpr_t *ack, *_ctrl;
	isc_result_t result;
	isccc_time_t t;

	REQUIRE(ackp != NULL && *ackp == NULL);

	_ctrl = isccc_alist_lookup(message, "_ctrl");
	if (!isccc_alist_alistp(_ctrl) ||
	    isccc_cc_lookupuint32(_ctrl, "_ser", &serial) != ISC_R_SUCCESS ||
	    isccc_cc_lookupuint32(_ctrl, "_tim", &t) != ISC_R_SUCCESS)
		return (ISC_R_FAILURE);
	/*
	 * _frm and _to are optional.
	 */
	_frm = NULL;
	(void)isccc_cc_lookupstring(_ctrl, "_frm", &_frm);
	_to = NULL;
	(void)isccc_cc_lookupstring(_ctrl, "_to", &_to);
	/*
	 * Create the ack.
	 */
	ack = NULL;
	result = createmessage(1, _to, _frm, serial, t, 0, &ack, ISC_FALSE);
	if (result != ISC_R_SUCCESS)
		return (result);

	_ctrl = isccc_alist_lookup(ack, "_ctrl");
	if (_ctrl == NULL) {
		result = ISC_R_FAILURE;
		goto bad;
	}
	if (isccc_cc_definestring(ack, "_ack", (ok) ? "1" : "0") == NULL) {
		result = ISC_R_NOMEMORY;
		goto bad;
	}

	*ackp = ack;

	return (ISC_R_SUCCESS);

 bad:
	isccc_sexpr_free(&ack);

	return (result);
}
Esempio n. 10
0
isccc_sexpr_t *
isccc_alist_definebinary(isccc_sexpr_t *alist, const char *key, isccc_region_t *r)
{
	isccc_sexpr_t *v, *kv;

	v = isccc_sexpr_frombinary(r);
	if (v == NULL)
		return (NULL);
	kv = isccc_alist_define(alist, key, v);
	if (kv == NULL)
		isccc_sexpr_free(&v);

	return (kv);
}
Esempio n. 11
0
isccc_sexpr_t *
isccc_alist_definestring(isccc_sexpr_t *alist, const char *key, const char *str)
{
	isccc_sexpr_t *v, *kv;

	v = isccc_sexpr_fromstring(str);
	if (v == NULL)
		return (NULL);
	kv = isccc_alist_define(alist, key, v);
	if (kv == NULL)
		isccc_sexpr_free(&v);

	return (kv);
}
Esempio n. 12
0
isccc_sexpr_t *
isccc_alist_create(void)
{
	isccc_sexpr_t *alist, *tag;

	tag = isccc_sexpr_fromstring(ALIST_TAG);
	if (tag == NULL)
		return (NULL);
	alist = isccc_sexpr_cons(tag, NULL);
	if (alist == NULL) {
		isccc_sexpr_free(&tag);
		return (NULL);
	}

	return (alist);
}
Esempio n. 13
0
static void
control_recvmessage(isc_task_t *task, isc_event_t *event) {
	controlconnection_t *conn;
	controllistener_t *listener;
	controlkey_t *key;
	isccc_sexpr_t *request = NULL;
	isccc_sexpr_t *response = NULL;
	isc_uint32_t algorithm;
	isccc_region_t secret;
	isc_stdtime_t now;
	isc_buffer_t b;
	isc_region_t r;
	isc_buffer_t *text;
	isc_result_t result;
	isc_result_t eresult;
	isccc_sexpr_t *_ctrl;
	isccc_time_t sent;
	isccc_time_t exp;
	isc_uint32_t nonce;
	isccc_sexpr_t *data;

	REQUIRE(event->ev_type == ISCCC_EVENT_CCMSG);

	conn = event->ev_arg;
	listener = conn->listener;
	algorithm = DST_ALG_UNKNOWN;
	secret.rstart = NULL;
	text = NULL;

	/* Is the server shutting down? */
	if (listener->controls->shuttingdown)
		goto cleanup;

	if (conn->ccmsg.result != ISC_R_SUCCESS) {
		if (conn->ccmsg.result != ISC_R_CANCELED &&
		    conn->ccmsg.result != ISC_R_EOF)
			log_invalid(&conn->ccmsg, conn->ccmsg.result);
		goto cleanup;
	}

	request = NULL;

	for (key = ISC_LIST_HEAD(listener->keys);
	     key != NULL;
	     key = ISC_LIST_NEXT(key, link))
	{
		isccc_region_t ccregion;

		ccregion.rstart = isc_buffer_base(&conn->ccmsg.buffer);
		ccregion.rend = isc_buffer_used(&conn->ccmsg.buffer);
		secret.rstart = isc_mem_get(listener->mctx, key->secret.length);
		if (secret.rstart == NULL)
			goto cleanup;
		memmove(secret.rstart, key->secret.base, key->secret.length);
		secret.rend = secret.rstart + key->secret.length;
		algorithm = key->algorithm;
		result = isccc_cc_fromwire(&ccregion, &request,
					   algorithm, &secret);
		if (result == ISC_R_SUCCESS)
			break;
		isc_mem_put(listener->mctx, secret.rstart, REGION_SIZE(secret));
		if (result != ISCCC_R_BADAUTH) {
			log_invalid(&conn->ccmsg, result);
			goto cleanup;
		}
	}

	if (key == NULL) {
		log_invalid(&conn->ccmsg, ISCCC_R_BADAUTH);
		goto cleanup;
	}

	/* We shouldn't be getting a reply. */
	if (isccc_cc_isreply(request)) {
		log_invalid(&conn->ccmsg, ISC_R_FAILURE);
		goto cleanup_request;
	}

	isc_stdtime_get(&now);

	/*
	 * Limit exposure to replay attacks.
	 */
	_ctrl = isccc_alist_lookup(request, "_ctrl");
	if (_ctrl == NULL) {
		log_invalid(&conn->ccmsg, ISC_R_FAILURE);
		goto cleanup_request;
	}

	if (isccc_cc_lookupuint32(_ctrl, "_tim", &sent) == ISC_R_SUCCESS) {
		if ((sent + CLOCKSKEW) < now || (sent - CLOCKSKEW) > now) {
			log_invalid(&conn->ccmsg, ISCCC_R_CLOCKSKEW);
			goto cleanup_request;
		}
	} else {
		log_invalid(&conn->ccmsg, ISC_R_FAILURE);
		goto cleanup_request;
	}

	/*
	 * Expire messages that are too old.
	 */
	if (isccc_cc_lookupuint32(_ctrl, "_exp", &exp) == ISC_R_SUCCESS &&
	    now > exp) {
		log_invalid(&conn->ccmsg, ISCCC_R_EXPIRED);
		goto cleanup_request;
	}

	/*
	 * Duplicate suppression (required for UDP).
	 */
	isccc_cc_cleansymtab(listener->controls->symtab, now);
	result = isccc_cc_checkdup(listener->controls->symtab, request, now);
	if (result != ISC_R_SUCCESS) {
		if (result == ISC_R_EXISTS)
			result = ISCCC_R_DUPLICATE;
		log_invalid(&conn->ccmsg, result);
		goto cleanup_request;
	}

	if (conn->nonce != 0 &&
	    (isccc_cc_lookupuint32(_ctrl, "_nonce", &nonce) != ISC_R_SUCCESS ||
	     conn->nonce != nonce)) {
		log_invalid(&conn->ccmsg, ISCCC_R_BADAUTH);
		goto cleanup_request;
	}

	result = isc_buffer_allocate(listener->mctx, &text, 2 * 2048);
	if (result != ISC_R_SUCCESS)
		goto cleanup_request;

	/*
	 * Establish nonce.
	 */
	if (conn->nonce == 0) {
		while (conn->nonce == 0)
			isc_random_get(&conn->nonce);
		eresult = ISC_R_SUCCESS;
	} else
		eresult = ns_control_docommand(request, &text);

	result = isccc_cc_createresponse(request, now, now + 60, &response);
	if (result != ISC_R_SUCCESS)
		goto cleanup_request;

	data = isccc_alist_lookup(response, "_data");
	if (data != NULL) {
		if (isccc_cc_defineuint32(data, "result", eresult) == NULL)
			goto cleanup_response;
	}

	if (eresult != ISC_R_SUCCESS) {
		if (data != NULL) {
			const char *estr = isc_result_totext(eresult);
			if (isccc_cc_definestring(data, "err", estr) == NULL)
				goto cleanup_response;
		}
	}

	if (isc_buffer_usedlength(text) > 0) {
		if (data != NULL) {
			char *str = (char *)isc_buffer_base(text);
			if (isccc_cc_definestring(data, "text", str) == NULL)
				goto cleanup_response;
		}
	}

	_ctrl = isccc_alist_lookup(response, "_ctrl");
	if (_ctrl == NULL ||
	    isccc_cc_defineuint32(_ctrl, "_nonce", conn->nonce) == NULL)
		goto cleanup_response;

	if (conn->buffer == NULL) {
		result = isc_buffer_allocate(listener->mctx,
					     &conn->buffer, 2 * 2048);
		if (result != ISC_R_SUCCESS)
			goto cleanup_response;
	}

	isc_buffer_clear(conn->buffer);
	/* Skip the length field (4 bytes) */
	isc_buffer_add(conn->buffer, 4);

	result = isccc_cc_towire(response, &conn->buffer, algorithm, &secret);
	if (result != ISC_R_SUCCESS)
		goto cleanup_response;

	isc_buffer_init(&b, conn->buffer->base, 4);
	isc_buffer_putuint32(&b, conn->buffer->used - 4);

	r.base = conn->buffer->base;
	r.length = conn->buffer->used;

	result = isc_socket_send(conn->sock, &r, task, control_senddone, conn);
	if (result != ISC_R_SUCCESS)
		goto cleanup_response;
	conn->sending = ISC_TRUE;

	isc_mem_put(listener->mctx, secret.rstart, REGION_SIZE(secret));
	isccc_sexpr_free(&request);
	isccc_sexpr_free(&response);
	isc_buffer_free(&text);
	return;

 cleanup_response:
	isccc_sexpr_free(&response);

 cleanup_request:
	isccc_sexpr_free(&request);
	isc_mem_put(listener->mctx, secret.rstart, REGION_SIZE(secret));
	if (text != NULL)
		isc_buffer_free(&text);

 cleanup:
	isc_socket_detach(&conn->sock);
	isccc_ccmsg_invalidate(&conn->ccmsg);
	conn->ccmsg_valid = ISC_FALSE;
	maybe_free_connection(conn);
	maybe_free_listener(listener);
}
Esempio n. 14
0
static void
rndc_recvnonce(isc_task_t *task, isc_event_t *event) {
	isccc_sexpr_t *response = NULL;
	isccc_sexpr_t *_ctrl;
	isccc_region_t source;
	isc_result_t result;
	isc_uint32_t nonce;
	isccc_sexpr_t *request = NULL;
	isccc_time_t now;
	isc_region_t r;
	isccc_sexpr_t *data;
	isccc_region_t message;
	isc_uint32_t len;
	isc_buffer_t b;

	recvs--;

	if (ccmsg.result == ISC_R_EOF)
		fatal("connection to remote host closed\n"
		      "This may indicate that the remote server is using "
		      "an older version of \n"
		      "the command protocol, this host is not authorized "
		      "to connect,\nor the key is invalid.");

	if (ccmsg.result != ISC_R_SUCCESS)
		fatal("recv failed: %s", isc_result_totext(ccmsg.result));

	source.rstart = isc_buffer_base(&ccmsg.buffer);
	source.rend = isc_buffer_used(&ccmsg.buffer);

	DO("parse message", isccc_cc_fromwire(&source, &response, &secret));

	_ctrl = isccc_alist_lookup(response, "_ctrl");
	if (_ctrl == NULL)
		fatal("_ctrl section missing");
	nonce = 0;
	if (isccc_cc_lookupuint32(_ctrl, "_nonce", &nonce) != ISC_R_SUCCESS)
		nonce = 0;

	isc_stdtime_get(&now);

	DO("create message", isccc_cc_createmessage(1, NULL, NULL, ++serial,
						    now, now + 60, &request));
	data = isccc_alist_lookup(request, "_data");
	if (data == NULL)
		fatal("_data section missing");
	if (isccc_cc_definestring(data, "type", args) == NULL)
		fatal("out of memory");
	if (nonce != 0) {
		_ctrl = isccc_alist_lookup(request, "_ctrl");
		if (_ctrl == NULL)
			fatal("_ctrl section missing");
		if (isccc_cc_defineuint32(_ctrl, "_nonce", nonce) == NULL)
			fatal("out of memory");
	}
	message.rstart = databuf + 4;
	message.rend = databuf + sizeof(databuf);
	DO("render message", isccc_cc_towire(request, &message, &secret));
	len = sizeof(databuf) - REGION_SIZE(message);
	isc_buffer_init(&b, databuf, 4);
	isc_buffer_putuint32(&b, len - 4);
	r.length = len;
	r.base = databuf;

	isccc_ccmsg_cancelread(&ccmsg);
	DO("schedule recv", isccc_ccmsg_readmessage(&ccmsg, task,
						    rndc_recvdone, NULL));
	recvs++;
	DO("send message", isc_socket_send(sock, &r, task, rndc_senddone,
					   NULL));
	sends++;

	isc_event_free(&event);
	isccc_sexpr_free(&response);
	return;
}
Esempio n. 15
0
static void
rndc_recvnonce(isc_task_t *task, isc_event_t *event) {
	isccc_sexpr_t *response = NULL;
	isccc_sexpr_t *_ctrl;
	isccc_region_t source;
	isc_result_t result;
	isc_uint32_t nonce;
	isccc_sexpr_t *request = NULL;
	isccc_time_t now;
	isc_region_t r;
	isccc_sexpr_t *data;
	isc_buffer_t b;

	recvs--;

	if (ccmsg.result == ISC_R_EOF)
		fatal("connection to remote host closed\n"
		      "This may indicate that\n"
		      "* the remote server is using an older version of"
		      " the command protocol,\n"
		      "* this host is not authorized to connect,\n"
		      "* the clocks are not synchronized,\n"
		      "* the key signing algorithm is incorrect, or\n"
		      "* the key is invalid.");

	if (ccmsg.result != ISC_R_SUCCESS)
		fatal("recv failed: %s", isc_result_totext(ccmsg.result));

	source.rstart = isc_buffer_base(&ccmsg.buffer);
	source.rend = isc_buffer_used(&ccmsg.buffer);

	DO("parse message",
	   isccc_cc_fromwire(&source, &response, algorithm, &secret));

	_ctrl = isccc_alist_lookup(response, "_ctrl");
	if (!isccc_alist_alistp(_ctrl))
		fatal("bad or missing ctrl section in response");
	nonce = 0;
	if (isccc_cc_lookupuint32(_ctrl, "_nonce", &nonce) != ISC_R_SUCCESS)
		nonce = 0;

	isc_stdtime_get(&now);

	DO("create message", isccc_cc_createmessage(1, NULL, NULL, ++serial,
						    now, now + 60, &request));
	data = isccc_alist_lookup(request, "_data");
	if (data == NULL)
		fatal("_data section missing");
	if (isccc_cc_definestring(data, "type", args) == NULL)
		fatal("out of memory");
	if (nonce != 0) {
		_ctrl = isccc_alist_lookup(request, "_ctrl");
		if (_ctrl == NULL)
			fatal("_ctrl section missing");
		if (isccc_cc_defineuint32(_ctrl, "_nonce", nonce) == NULL)
			fatal("out of memory");
	}

	isc_buffer_clear(databuf);
	/* Skip the length field (4 bytes) */
	isc_buffer_add(databuf, 4);

	DO("render message",
	   isccc_cc_towire(request, &databuf, algorithm, &secret));

	isc_buffer_init(&b, databuf->base, 4);
	isc_buffer_putuint32(&b, databuf->used - 4);

	r.base = databuf->base;
	r.length = databuf->used;

	isccc_ccmsg_cancelread(&ccmsg);
	DO("schedule recv", isccc_ccmsg_readmessage(&ccmsg, task,
						    rndc_recvdone, NULL));
	recvs++;
	DO("send message", isc_socket_send(sock, &r, task, rndc_senddone,
					   NULL));
	sends++;

	isc_event_free(&event);
	isccc_sexpr_free(&response);
	return;
}