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