cc.c revision 296611
1/*
2 * Portions Copyright (C) 2004-2007, 2012, 2013  Internet Systems Consortium, Inc. ("ISC")
3 * Portions Copyright (C) 2001-2003  Internet Software Consortium.
4 *
5 * Permission to use, copy, modify, and/or distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NOMINUM DISCLAIMS ALL
10 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
11 * OF MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY
12 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 *
17 * Portions Copyright (C) 2001  Nominum, Inc.
18 *
19 * Permission to use, copy, modify, and/or distribute this software for any
20 * purpose with or without fee is hereby granted, provided that the above
21 * copyright notice and this permission notice appear in all copies.
22 *
23 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NOMINUM DISCLAIMS ALL
24 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
25 * OF MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY
26 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
27 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
28 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
29 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
30 */
31
32/* $Id: cc.c,v 1.18 2007/08/28 07:20:43 tbox Exp $ */
33
34/*! \file */
35
36#include <config.h>
37
38#include <stdio.h>
39#include <string.h>
40#include <errno.h>
41
42#include <isc/assertions.h>
43#include <isc/hmacmd5.h>
44#include <isc/print.h>
45#include <isc/safe.h>
46#include <isc/stdlib.h>
47
48#include <isccc/alist.h>
49#include <isccc/base64.h>
50#include <isccc/cc.h>
51#include <isccc/result.h>
52#include <isccc/sexpr.h>
53#include <isccc/symtab.h>
54#include <isccc/symtype.h>
55#include <isccc/util.h>
56
57#define MAX_TAGS		256
58#define DUP_LIFETIME		900
59
60typedef isccc_sexpr_t *sexpr_ptr;
61
62static unsigned char auth_hmd5[] = {
63	0x05, 0x5f, 0x61, 0x75, 0x74, 0x68,		/*%< len + _auth */
64	ISCCC_CCMSGTYPE_TABLE,				/*%< message type */
65	0x00, 0x00, 0x00, 0x20,				/*%< length == 32 */
66	0x04, 0x68, 0x6d, 0x64, 0x35,			/*%< len + hmd5 */
67	ISCCC_CCMSGTYPE_BINARYDATA,			/*%< message type */
68	0x00, 0x00, 0x00, 0x16,				/*%< length == 22 */
69	/*
70	 * The base64 encoding of one of our HMAC-MD5 signatures is
71	 * 22 bytes.
72	 */
73	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
74	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
75	0x00, 0x00, 0x00, 0x00, 0x00, 0x00
76};
77
78#define HMD5_OFFSET	21		/*%< 21 = 6 + 1 + 4 + 5 + 1 + 4 */
79#define HMD5_LENGTH	22
80
81static isc_result_t
82table_towire(isccc_sexpr_t *alist, isccc_region_t *target);
83
84static isc_result_t
85list_towire(isccc_sexpr_t *alist, isccc_region_t *target);
86
87static isc_result_t
88value_towire(isccc_sexpr_t *elt, isccc_region_t *target)
89{
90	unsigned int len;
91	unsigned char *lenp;
92	isccc_region_t *vr;
93	isc_result_t result;
94
95	if (isccc_sexpr_binaryp(elt)) {
96		vr = isccc_sexpr_tobinary(elt);
97		len = REGION_SIZE(*vr);
98		if (REGION_SIZE(*target) < 1 + 4 + len)
99			return (ISC_R_NOSPACE);
100		PUT8(ISCCC_CCMSGTYPE_BINARYDATA, target->rstart);
101		PUT32(len, target->rstart);
102		if (REGION_SIZE(*target) < len)
103			return (ISC_R_NOSPACE);
104		PUT_MEM(vr->rstart, len, target->rstart);
105	} else if (isccc_alist_alistp(elt)) {
106		if (REGION_SIZE(*target) < 1 + 4)
107			return (ISC_R_NOSPACE);
108		PUT8(ISCCC_CCMSGTYPE_TABLE, target->rstart);
109		/*
110		 * Emit a placeholder length.
111		 */
112		lenp = target->rstart;
113		PUT32(0, target->rstart);
114		/*
115		 * Emit the table.
116		 */
117		result = table_towire(elt, target);
118		if (result != ISC_R_SUCCESS)
119			return (result);
120		len = (unsigned int)(target->rstart - lenp);
121		/*
122		 * 'len' is 4 bytes too big, since it counts
123		 * the placeholder length too.  Adjust and
124		 * emit.
125		 */
126		INSIST(len >= 4U);
127		len -= 4;
128		PUT32(len, lenp);
129	} else if (isccc_sexpr_listp(elt)) {
130		if (REGION_SIZE(*target) < 1 + 4)
131			return (ISC_R_NOSPACE);
132		PUT8(ISCCC_CCMSGTYPE_LIST, target->rstart);
133		/*
134		 * Emit a placeholder length and count.
135		 */
136		lenp = target->rstart;
137		PUT32(0, target->rstart);
138		/*
139		 * Emit the list.
140		 */
141		result = list_towire(elt, target);
142		if (result != ISC_R_SUCCESS)
143			return (result);
144		len = (unsigned int)(target->rstart - lenp);
145		/*
146		 * 'len' is 4 bytes too big, since it counts
147		 * the placeholder length.  Adjust and emit.
148		 */
149		INSIST(len >= 4U);
150		len -= 4;
151		PUT32(len, lenp);
152	}
153
154	return (ISC_R_SUCCESS);
155}
156
157static isc_result_t
158table_towire(isccc_sexpr_t *alist, isccc_region_t *target)
159{
160	isccc_sexpr_t *kv, *elt, *k, *v;
161	char *ks;
162	isc_result_t result;
163	size_t len;
164
165	for (elt = isccc_alist_first(alist);
166	     elt != NULL;
167	     elt = ISCCC_SEXPR_CDR(elt)) {
168		kv = ISCCC_SEXPR_CAR(elt);
169		k = ISCCC_SEXPR_CAR(kv);
170		ks = isccc_sexpr_tostring(k);
171		v = ISCCC_SEXPR_CDR(kv);
172		len = strlen(ks);
173		INSIST(len <= 255U);
174		/*
175		 * Emit the key name.
176		 */
177		if (REGION_SIZE(*target) < 1 + len)
178			return (ISC_R_NOSPACE);
179		PUT8(len, target->rstart);
180		PUT_MEM(ks, len, target->rstart);
181		/*
182		 * Emit the value.
183		 */
184		result = value_towire(v, target);
185		if (result != ISC_R_SUCCESS)
186			return (result);
187	}
188
189	return (ISC_R_SUCCESS);
190}
191
192static isc_result_t
193list_towire(isccc_sexpr_t *list, isccc_region_t *target)
194{
195	isc_result_t result;
196
197	while (list != NULL) {
198		result = value_towire(ISCCC_SEXPR_CAR(list), target);
199		if (result != ISC_R_SUCCESS)
200			return (result);
201		list = ISCCC_SEXPR_CDR(list);
202	}
203
204	return (ISC_R_SUCCESS);
205}
206
207static isc_result_t
208sign(unsigned char *data, unsigned int length, unsigned char *hmd5,
209     isccc_region_t *secret)
210{
211	isc_hmacmd5_t ctx;
212	isc_result_t result;
213	isccc_region_t source, target;
214	unsigned char digest[ISC_MD5_DIGESTLENGTH];
215	unsigned char digestb64[ISC_MD5_DIGESTLENGTH * 4];
216
217	isc_hmacmd5_init(&ctx, secret->rstart, REGION_SIZE(*secret));
218	isc_hmacmd5_update(&ctx, data, length);
219	isc_hmacmd5_sign(&ctx, digest);
220	source.rstart = digest;
221	source.rend = digest + ISC_MD5_DIGESTLENGTH;
222	target.rstart = digestb64;
223	target.rend = digestb64 + ISC_MD5_DIGESTLENGTH * 4;
224	result = isccc_base64_encode(&source, 64, "", &target);
225	if (result != ISC_R_SUCCESS)
226		return (result);
227	PUT_MEM(digestb64, HMD5_LENGTH, hmd5);
228
229	return (ISC_R_SUCCESS);
230}
231
232isc_result_t
233isccc_cc_towire(isccc_sexpr_t *alist, isccc_region_t *target,
234	      isccc_region_t *secret)
235{
236	unsigned char *hmd5_rstart, *signed_rstart;
237	isc_result_t result;
238
239	if (REGION_SIZE(*target) < 4 + sizeof(auth_hmd5))
240		return (ISC_R_NOSPACE);
241	/*
242	 * Emit protocol version.
243	 */
244	PUT32(1, target->rstart);
245	if (secret != NULL) {
246		/*
247		 * Emit _auth section with zeroed HMAC-MD5 signature.
248		 * We'll replace the zeros with the real signature once
249		 * we know what it is.
250		 */
251		hmd5_rstart = target->rstart + HMD5_OFFSET;
252		PUT_MEM(auth_hmd5, sizeof(auth_hmd5), target->rstart);
253	} else
254		hmd5_rstart = NULL;
255	signed_rstart = target->rstart;
256	/*
257	 * Delete any existing _auth section so that we don't try
258	 * to encode it.
259	 */
260	isccc_alist_delete(alist, "_auth");
261	/*
262	 * Emit the message.
263	 */
264	result = table_towire(alist, target);
265	if (result != ISC_R_SUCCESS)
266		return (result);
267	if (secret != NULL)
268		return (sign(signed_rstart,
269			     (unsigned int)(target->rstart - signed_rstart),
270			     hmd5_rstart, secret));
271	return (ISC_R_SUCCESS);
272}
273
274static isc_result_t
275verify(isccc_sexpr_t *alist, unsigned char *data, unsigned int length,
276       isccc_region_t *secret)
277{
278	isc_hmacmd5_t ctx;
279	isccc_region_t source;
280	isccc_region_t target;
281	isc_result_t result;
282	isccc_sexpr_t *_auth, *hmd5;
283	unsigned char digest[ISC_MD5_DIGESTLENGTH];
284	unsigned char digestb64[ISC_MD5_DIGESTLENGTH * 4];
285
286	/*
287	 * Extract digest.
288	 */
289	_auth = isccc_alist_lookup(alist, "_auth");
290	if (!isccc_alist_alistp(_auth))
291		return (ISC_R_FAILURE);
292	hmd5 = isccc_alist_lookup(_auth, "hmd5");
293	if (!isccc_sexpr_binaryp(hmd5))
294		return (ISC_R_FAILURE);
295	/*
296	 * Compute digest.
297	 */
298	isc_hmacmd5_init(&ctx, secret->rstart, REGION_SIZE(*secret));
299	isc_hmacmd5_update(&ctx, data, length);
300	isc_hmacmd5_sign(&ctx, digest);
301	source.rstart = digest;
302	source.rend = digest + ISC_MD5_DIGESTLENGTH;
303	target.rstart = digestb64;
304	target.rend = digestb64 + ISC_MD5_DIGESTLENGTH * 4;
305	result = isccc_base64_encode(&source, 64, "", &target);
306	if (result != ISC_R_SUCCESS)
307		return (result);
308	/*
309	 * Strip trailing == and NUL terminate target.
310	 */
311	target.rstart -= 2;
312	*target.rstart++ = '\0';
313	/*
314	 * Verify.
315	 */
316	if (!isc_safe_memcmp((unsigned char *) isccc_sexpr_tostring(hmd5),
317			     digestb64, HMD5_LENGTH))
318		return (ISCCC_R_BADAUTH);
319
320	return (ISC_R_SUCCESS);
321}
322
323static isc_result_t
324table_fromwire(isccc_region_t *source, isccc_region_t *secret,
325	       isccc_sexpr_t **alistp);
326
327static isc_result_t
328list_fromwire(isccc_region_t *source, isccc_sexpr_t **listp);
329
330static isc_result_t
331value_fromwire(isccc_region_t *source, isccc_sexpr_t **valuep)
332{
333	unsigned int msgtype;
334	isc_uint32_t len;
335	isccc_sexpr_t *value;
336	isccc_region_t active;
337	isc_result_t result;
338
339	if (REGION_SIZE(*source) < 1 + 4)
340		return (ISC_R_UNEXPECTEDEND);
341	GET8(msgtype, source->rstart);
342	GET32(len, source->rstart);
343	if (REGION_SIZE(*source) < len)
344		return (ISC_R_UNEXPECTEDEND);
345	active.rstart = source->rstart;
346	active.rend = active.rstart + len;
347	source->rstart = active.rend;
348	if (msgtype == ISCCC_CCMSGTYPE_BINARYDATA) {
349		value = isccc_sexpr_frombinary(&active);
350		if (value != NULL) {
351			*valuep = value;
352			result = ISC_R_SUCCESS;
353		} else
354			result = ISC_R_NOMEMORY;
355	} else if (msgtype == ISCCC_CCMSGTYPE_TABLE)
356		result = table_fromwire(&active, NULL, valuep);
357	else if (msgtype == ISCCC_CCMSGTYPE_LIST)
358		result = list_fromwire(&active, valuep);
359	else
360		result = ISCCC_R_SYNTAX;
361
362	return (result);
363}
364
365static isc_result_t
366table_fromwire(isccc_region_t *source, isccc_region_t *secret,
367	       isccc_sexpr_t **alistp)
368{
369	char key[256];
370	isc_uint32_t len;
371	isc_result_t result;
372	isccc_sexpr_t *alist, *value;
373	isc_boolean_t first_tag;
374	unsigned char *checksum_rstart;
375
376	REQUIRE(alistp != NULL && *alistp == NULL);
377
378	checksum_rstart = NULL;
379	first_tag = ISC_TRUE;
380	alist = isccc_alist_create();
381	if (alist == NULL)
382		return (ISC_R_NOMEMORY);
383
384	while (!REGION_EMPTY(*source)) {
385		GET8(len, source->rstart);
386		if (REGION_SIZE(*source) < len) {
387			result = ISC_R_UNEXPECTEDEND;
388			goto bad;
389		}
390		GET_MEM(key, len, source->rstart);
391		key[len] = '\0';	/* Ensure NUL termination. */
392		value = NULL;
393		result = value_fromwire(source, &value);
394		if (result != ISC_R_SUCCESS)
395			goto bad;
396		if (isccc_alist_define(alist, key, value) == NULL) {
397			result = ISC_R_NOMEMORY;
398			goto bad;
399		}
400		if (first_tag && secret != NULL && strcmp(key, "_auth") == 0)
401			checksum_rstart = source->rstart;
402		first_tag = ISC_FALSE;
403	}
404
405	if (secret != NULL) {
406		if (checksum_rstart != NULL)
407			result = verify(alist, checksum_rstart,
408					(unsigned int)
409					(source->rend - checksum_rstart),
410					secret);
411		else
412			result = ISCCC_R_BADAUTH;
413	} else
414		result = ISC_R_SUCCESS;
415
416 bad:
417	if (result == ISC_R_SUCCESS)
418		*alistp = alist;
419	else
420		isccc_sexpr_free(&alist);
421
422	return (result);
423}
424
425static isc_result_t
426list_fromwire(isccc_region_t *source, isccc_sexpr_t **listp)
427{
428	isccc_sexpr_t *list, *value;
429	isc_result_t result;
430
431	list = NULL;
432	while (!REGION_EMPTY(*source)) {
433		value = NULL;
434		result = value_fromwire(source, &value);
435		if (result != ISC_R_SUCCESS) {
436			isccc_sexpr_free(&list);
437			return (result);
438		}
439		if (isccc_sexpr_addtolist(&list, value) == NULL) {
440			isccc_sexpr_free(&value);
441			isccc_sexpr_free(&list);
442			return (result);
443		}
444	}
445
446	*listp = list;
447
448	return (ISC_R_SUCCESS);
449}
450
451isc_result_t
452isccc_cc_fromwire(isccc_region_t *source, isccc_sexpr_t **alistp,
453		isccc_region_t *secret)
454{
455	unsigned int size;
456	isc_uint32_t version;
457
458	size = REGION_SIZE(*source);
459	if (size < 4)
460		return (ISC_R_UNEXPECTEDEND);
461	GET32(version, source->rstart);
462	if (version != 1)
463		return (ISCCC_R_UNKNOWNVERSION);
464
465	return (table_fromwire(source, secret, alistp));
466}
467
468static isc_result_t
469createmessage(isc_uint32_t version, const char *from, const char *to,
470	      isc_uint32_t serial, isccc_time_t now,
471	      isccc_time_t expires, isccc_sexpr_t **alistp,
472	      isc_boolean_t want_expires)
473{
474	isccc_sexpr_t *alist, *_ctrl, *_data;
475	isc_result_t result;
476
477	REQUIRE(alistp != NULL && *alistp == NULL);
478
479	if (version != 1)
480		return (ISCCC_R_UNKNOWNVERSION);
481
482	alist = isccc_alist_create();
483	if (alist == NULL)
484		return (ISC_R_NOMEMORY);
485
486	result = ISC_R_NOMEMORY;
487
488	_ctrl = isccc_alist_create();
489	if (_ctrl == NULL)
490		goto bad;
491	if (isccc_alist_define(alist, "_ctrl", _ctrl) == NULL) {
492		isccc_sexpr_free(&_ctrl);
493		goto bad;
494	}
495
496	_data = isccc_alist_create();
497	if (_data == NULL)
498		goto bad;
499	if (isccc_alist_define(alist, "_data", _data) == NULL) {
500		isccc_sexpr_free(&_data);
501		goto bad;
502	}
503
504	if (isccc_cc_defineuint32(_ctrl, "_ser", serial) == NULL ||
505	    isccc_cc_defineuint32(_ctrl, "_tim", now) == NULL ||
506	    (want_expires &&
507	     isccc_cc_defineuint32(_ctrl, "_exp", expires) == NULL))
508		goto bad;
509	if (from != NULL &&
510	    isccc_cc_definestring(_ctrl, "_frm", from) == NULL)
511		goto bad;
512	if (to != NULL &&
513	    isccc_cc_definestring(_ctrl, "_to", to) == NULL)
514		goto bad;
515
516	*alistp = alist;
517
518	return (ISC_R_SUCCESS);
519
520 bad:
521	isccc_sexpr_free(&alist);
522
523	return (result);
524}
525
526isc_result_t
527isccc_cc_createmessage(isc_uint32_t version, const char *from, const char *to,
528		     isc_uint32_t serial, isccc_time_t now,
529		     isccc_time_t expires, isccc_sexpr_t **alistp)
530{
531	return (createmessage(version, from, to, serial, now, expires,
532			      alistp, ISC_TRUE));
533}
534
535isc_result_t
536isccc_cc_createack(isccc_sexpr_t *message, isc_boolean_t ok,
537		 isccc_sexpr_t **ackp)
538{
539	char *_frm, *_to;
540	isc_uint32_t serial;
541	isccc_sexpr_t *ack, *_ctrl;
542	isc_result_t result;
543	isccc_time_t t;
544
545	REQUIRE(ackp != NULL && *ackp == NULL);
546
547	_ctrl = isccc_alist_lookup(message, "_ctrl");
548	if (!isccc_alist_alistp(_ctrl) ||
549	    isccc_cc_lookupuint32(_ctrl, "_ser", &serial) != ISC_R_SUCCESS ||
550	    isccc_cc_lookupuint32(_ctrl, "_tim", &t) != ISC_R_SUCCESS)
551		return (ISC_R_FAILURE);
552	/*
553	 * _frm and _to are optional.
554	 */
555	_frm = NULL;
556	(void)isccc_cc_lookupstring(_ctrl, "_frm", &_frm);
557	_to = NULL;
558	(void)isccc_cc_lookupstring(_ctrl, "_to", &_to);
559	/*
560	 * Create the ack.
561	 */
562	ack = NULL;
563	result = createmessage(1, _to, _frm, serial, t, 0, &ack, ISC_FALSE);
564	if (result != ISC_R_SUCCESS)
565		return (result);
566
567	_ctrl = isccc_alist_lookup(ack, "_ctrl");
568	if (_ctrl == NULL) {
569		result = ISC_R_FAILURE;
570		goto bad;
571	}
572	if (isccc_cc_definestring(ack, "_ack", (ok) ? "1" : "0") == NULL) {
573		result = ISC_R_NOMEMORY;
574		goto bad;
575	}
576
577	*ackp = ack;
578
579	return (ISC_R_SUCCESS);
580
581 bad:
582	isccc_sexpr_free(&ack);
583
584	return (result);
585}
586
587isc_boolean_t
588isccc_cc_isack(isccc_sexpr_t *message)
589{
590	isccc_sexpr_t *_ctrl;
591
592	_ctrl = isccc_alist_lookup(message, "_ctrl");
593	if (!isccc_alist_alistp(_ctrl))
594		return (ISC_FALSE);
595	if (isccc_cc_lookupstring(_ctrl, "_ack", NULL) == ISC_R_SUCCESS)
596		return (ISC_TRUE);
597	return (ISC_FALSE);
598}
599
600isc_boolean_t
601isccc_cc_isreply(isccc_sexpr_t *message)
602{
603	isccc_sexpr_t *_ctrl;
604
605	_ctrl = isccc_alist_lookup(message, "_ctrl");
606	if (!isccc_alist_alistp(_ctrl))
607		return (ISC_FALSE);
608	if (isccc_cc_lookupstring(_ctrl, "_rpl", NULL) == ISC_R_SUCCESS)
609		return (ISC_TRUE);
610	return (ISC_FALSE);
611}
612
613isc_result_t
614isccc_cc_createresponse(isccc_sexpr_t *message, isccc_time_t now,
615		      isccc_time_t expires, isccc_sexpr_t **alistp)
616{
617	char *_frm, *_to, *type = NULL;
618	isc_uint32_t serial;
619	isccc_sexpr_t *alist, *_ctrl, *_data;
620	isc_result_t result;
621
622	REQUIRE(alistp != NULL && *alistp == NULL);
623
624	_ctrl = isccc_alist_lookup(message, "_ctrl");
625	_data = isccc_alist_lookup(message, "_data");
626	if (!isccc_alist_alistp(_ctrl) || !isccc_alist_alistp(_data) ||
627	    isccc_cc_lookupuint32(_ctrl, "_ser", &serial) != ISC_R_SUCCESS ||
628	    isccc_cc_lookupstring(_data, "type", &type) != ISC_R_SUCCESS)
629		return (ISC_R_FAILURE);
630	/*
631	 * _frm and _to are optional.
632	 */
633	_frm = NULL;
634	(void)isccc_cc_lookupstring(_ctrl, "_frm", &_frm);
635	_to = NULL;
636	(void)isccc_cc_lookupstring(_ctrl, "_to", &_to);
637	/*
638	 * Create the response.
639	 */
640	alist = NULL;
641	result = isccc_cc_createmessage(1, _to, _frm, serial, now, expires,
642					 &alist);
643	if (result != ISC_R_SUCCESS)
644		return (result);
645
646	_ctrl = isccc_alist_lookup(alist, "_ctrl");
647	if (_ctrl == NULL) {
648		result = ISC_R_FAILURE;
649		goto bad;
650	}
651
652	_data = isccc_alist_lookup(alist, "_data");
653	if (_data == NULL) {
654		result = ISC_R_FAILURE;
655		goto bad;
656	}
657
658	if (isccc_cc_definestring(_ctrl, "_rpl", "1") == NULL ||
659	    isccc_cc_definestring(_data, "type", type) == NULL)
660	{
661		result = ISC_R_NOMEMORY;
662		goto bad;
663	}
664
665	*alistp = alist;
666
667	return (ISC_R_SUCCESS);
668
669 bad:
670	isccc_sexpr_free(&alist);
671	return (result);
672}
673
674isccc_sexpr_t *
675isccc_cc_definestring(isccc_sexpr_t *alist, const char *key, const char *str)
676{
677	size_t len;
678	isccc_region_t r;
679
680	len = strlen(str);
681	DE_CONST(str, r.rstart);
682	r.rend = r.rstart + len;
683
684	return (isccc_alist_definebinary(alist, key, &r));
685}
686
687isccc_sexpr_t *
688isccc_cc_defineuint32(isccc_sexpr_t *alist, const char *key, isc_uint32_t i)
689{
690	char b[100];
691	size_t len;
692	isccc_region_t r;
693
694	snprintf(b, sizeof(b), "%u", i);
695	len = strlen(b);
696	r.rstart = (unsigned char *)b;
697	r.rend = (unsigned char *)b + len;
698
699	return (isccc_alist_definebinary(alist, key, &r));
700}
701
702isc_result_t
703isccc_cc_lookupstring(isccc_sexpr_t *alist, const char *key, char **strp)
704{
705	isccc_sexpr_t *kv, *v;
706
707	REQUIRE(strp == NULL || *strp == NULL);
708
709	kv = isccc_alist_assq(alist, key);
710	if (kv != NULL) {
711		v = ISCCC_SEXPR_CDR(kv);
712		if (isccc_sexpr_binaryp(v)) {
713			if (strp != NULL)
714				*strp = isccc_sexpr_tostring(v);
715			return (ISC_R_SUCCESS);
716		} else
717			return (ISC_R_EXISTS);
718	}
719
720	return (ISC_R_NOTFOUND);
721}
722
723isc_result_t
724isccc_cc_lookupuint32(isccc_sexpr_t *alist, const char *key,
725		       isc_uint32_t *uintp)
726{
727	isccc_sexpr_t *kv, *v;
728
729	kv = isccc_alist_assq(alist, key);
730	if (kv != NULL) {
731		v = ISCCC_SEXPR_CDR(kv);
732		if (isccc_sexpr_binaryp(v)) {
733			if (uintp != NULL)
734				*uintp = (isc_uint32_t)
735					strtoul(isccc_sexpr_tostring(v),
736						NULL, 10);
737			return (ISC_R_SUCCESS);
738		} else
739			return (ISC_R_EXISTS);
740	}
741
742	return (ISC_R_NOTFOUND);
743}
744
745static void
746symtab_undefine(char *key, unsigned int type, isccc_symvalue_t value,
747		void *arg)
748{
749	UNUSED(type);
750	UNUSED(value);
751	UNUSED(arg);
752
753	free(key);
754}
755
756static isc_boolean_t
757symtab_clean(char *key, unsigned int type, isccc_symvalue_t value,
758	     void *arg)
759{
760	isccc_time_t *now;
761
762	UNUSED(key);
763	UNUSED(type);
764
765	now = arg;
766
767	if (*now < value.as_uinteger)
768		return (ISC_FALSE);
769	if ((*now - value.as_uinteger) < DUP_LIFETIME)
770		return (ISC_FALSE);
771	return (ISC_TRUE);
772}
773
774isc_result_t
775isccc_cc_createsymtab(isccc_symtab_t **symtabp)
776{
777	return (isccc_symtab_create(11897, symtab_undefine, NULL, ISC_FALSE,
778				  symtabp));
779}
780
781void
782isccc_cc_cleansymtab(isccc_symtab_t *symtab, isccc_time_t now)
783{
784	isccc_symtab_foreach(symtab, symtab_clean, &now);
785}
786
787static isc_boolean_t
788has_whitespace(const char *str)
789{
790	char c;
791
792	if (str == NULL)
793		return (ISC_FALSE);
794	while ((c = *str++) != '\0') {
795		if (c == ' ' || c == '\t' || c == '\n')
796			return (ISC_TRUE);
797	}
798	return (ISC_FALSE);
799}
800
801isc_result_t
802isccc_cc_checkdup(isccc_symtab_t *symtab, isccc_sexpr_t *message,
803		isccc_time_t now)
804{
805	const char *_frm;
806	const char *_to;
807	char *_ser = NULL, *_tim = NULL, *tmp;
808	isc_result_t result;
809	char *key;
810	size_t len;
811	isccc_symvalue_t value;
812	isccc_sexpr_t *_ctrl;
813
814	_ctrl = isccc_alist_lookup(message, "_ctrl");
815	if (!isccc_alist_alistp(_ctrl) ||
816	    isccc_cc_lookupstring(_ctrl, "_ser", &_ser) != ISC_R_SUCCESS ||
817	    isccc_cc_lookupstring(_ctrl, "_tim", &_tim) != ISC_R_SUCCESS)
818		return (ISC_R_FAILURE);
819
820	INSIST(_ser != NULL);
821	INSIST(_tim != NULL);
822
823	/*
824	 * _frm and _to are optional.
825	 */
826	tmp = NULL;
827	if (isccc_cc_lookupstring(_ctrl, "_frm", &tmp) != ISC_R_SUCCESS)
828		_frm = "";
829	else
830		_frm = tmp;
831	tmp = NULL;
832	if (isccc_cc_lookupstring(_ctrl, "_to", &tmp) != ISC_R_SUCCESS)
833		_to = "";
834	else
835		_to = tmp;
836	/*
837	 * Ensure there is no newline in any of the strings.  This is so
838	 * we can write them to a file later.
839	 */
840	if (has_whitespace(_frm) || has_whitespace(_to) ||
841	    has_whitespace(_ser) || has_whitespace(_tim))
842		return (ISC_R_FAILURE);
843	len = strlen(_frm) + strlen(_to) + strlen(_ser) + strlen(_tim) + 4;
844	key = malloc(len);
845	if (key == NULL)
846		return (ISC_R_NOMEMORY);
847	snprintf(key, len, "%s;%s;%s;%s", _frm, _to, _ser, _tim);
848	value.as_uinteger = now;
849	result = isccc_symtab_define(symtab, key, ISCCC_SYMTYPE_CCDUP, value,
850				   isccc_symexists_reject);
851	if (result != ISC_R_SUCCESS) {
852		free(key);
853		return (result);
854	}
855
856	return (ISC_R_SUCCESS);
857}
858