]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - contrib/bind9/lib/isccc/cc.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / contrib / bind9 / lib / isccc / cc.c
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
59 typedef isccc_sexpr_t *sexpr_ptr;
60
61 static 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
80 static isc_result_t
81 table_towire(isccc_sexpr_t *alist, isccc_region_t *target);
82
83 static isc_result_t
84 list_towire(isccc_sexpr_t *alist, isccc_region_t *target);
85
86 static isc_result_t
87 value_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
156 static isc_result_t
157 table_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
191 static isc_result_t
192 list_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
206 static isc_result_t
207 sign(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
231 isc_result_t
232 isccc_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
272 static isc_result_t
273 verify(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
320 static isc_result_t
321 table_fromwire(isccc_region_t *source, isccc_region_t *secret,
322                isccc_sexpr_t **alistp);
323
324 static isc_result_t
325 list_fromwire(isccc_region_t *source, isccc_sexpr_t **listp);
326
327 static isc_result_t
328 value_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
362 static isc_result_t
363 table_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         if (secret != NULL) {
403                 if (checksum_rstart != NULL)
404                         result = verify(alist, checksum_rstart,
405                                         (source->rend - checksum_rstart),
406                                         secret);
407                 else
408                         result = ISCCC_R_BADAUTH;
409         } else
410                 result = ISC_R_SUCCESS;
411
412  bad:
413         if (result == ISC_R_SUCCESS)
414                 *alistp = alist;
415         else
416                 isccc_sexpr_free(&alist);
417
418         return (result);
419 }
420
421 static isc_result_t
422 list_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
447 isc_result_t
448 isccc_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
464 static isc_result_t
465 createmessage(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
522 isc_result_t
523 isccc_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
531 isc_result_t
532 isccc_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
581 isc_boolean_t
582 isccc_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
594 isc_boolean_t
595 isccc_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
607 isc_result_t
608 isccc_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
657 isccc_sexpr_t *
658 isccc_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
670 isccc_sexpr_t *
671 isccc_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
685 isc_result_t
686 isccc_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
704 isc_result_t
705 isccc_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
726 static void
727 symtab_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
737 static isc_boolean_t
738 symtab_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
755 isc_result_t
756 isccc_cc_createsymtab(isccc_symtab_t **symtabp)
757 {
758         return (isccc_symtab_create(11897, symtab_undefine, NULL, ISC_FALSE,
759                                   symtabp));
760 }
761
762 void
763 isccc_cc_cleansymtab(isccc_symtab_t *symtab, isccc_time_t now)
764 {
765         isccc_symtab_foreach(symtab, symtab_clean, &now);
766 }
767
768 static isc_boolean_t
769 has_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
782 isc_result_t
783 isccc_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 }