Import bind 9.5.2 vendor sources.
[dragonfly.git] / contrib / bind-9.5.2 / lib / isccc / cc.c
1 /*
2  * Portions Copyright (C) 2004-2007  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         *alistp = alist;
403
404         if (secret != NULL) {
405                 if (checksum_rstart != NULL)
406                         return (verify(alist, checksum_rstart,
407                                        (source->rend - checksum_rstart),
408                                        secret));
409                 return (ISCCC_R_BADAUTH);
410         }
411
412         return (ISC_R_SUCCESS);
413
414  bad:
415         isccc_sexpr_free(&alist);
416
417         return (result);
418 }
419
420 static isc_result_t
421 list_fromwire(isccc_region_t *source, isccc_sexpr_t **listp)
422 {
423         isccc_sexpr_t *list, *value;
424         isc_result_t result;
425
426         list = NULL;
427         while (!REGION_EMPTY(*source)) {
428                 value = NULL;
429                 result = value_fromwire(source, &value);
430                 if (result != ISC_R_SUCCESS) {
431                         isccc_sexpr_free(&list);
432                         return (result);
433                 }
434                 if (isccc_sexpr_addtolist(&list, value) == NULL) {
435                         isccc_sexpr_free(&value);
436                         isccc_sexpr_free(&list);
437                         return (result);
438                 }
439         }
440
441         *listp = list;
442         
443         return (ISC_R_SUCCESS);
444 }
445
446 isc_result_t
447 isccc_cc_fromwire(isccc_region_t *source, isccc_sexpr_t **alistp,
448                 isccc_region_t *secret)
449 {
450         unsigned int size;
451         isc_uint32_t version;
452
453         size = REGION_SIZE(*source);
454         if (size < 4)
455                 return (ISC_R_UNEXPECTEDEND);
456         GET32(version, source->rstart);
457         if (version != 1)
458                 return (ISCCC_R_UNKNOWNVERSION);        
459         
460         return (table_fromwire(source, secret, alistp));
461 }
462
463 static isc_result_t
464 createmessage(isc_uint32_t version, const char *from, const char *to,
465               isc_uint32_t serial, isccc_time_t now,
466               isccc_time_t expires, isccc_sexpr_t **alistp,
467               isc_boolean_t want_expires)
468 {
469         isccc_sexpr_t *alist, *_ctrl, *_data;
470         isc_result_t result;
471
472         REQUIRE(alistp != NULL && *alistp == NULL);
473
474         if (version != 1)
475                 return (ISCCC_R_UNKNOWNVERSION);
476
477         alist = isccc_alist_create();
478         if (alist == NULL)
479                 return (ISC_R_NOMEMORY);
480
481         result = ISC_R_NOMEMORY;
482
483         _ctrl = isccc_alist_create();
484         if (_ctrl == NULL)
485                 goto bad;
486         if (isccc_alist_define(alist, "_ctrl", _ctrl) == NULL) {
487                 isccc_sexpr_free(&_ctrl);
488                 goto bad;
489         }
490
491         _data = isccc_alist_create();
492         if (_data == NULL)
493                 goto bad;
494         if (isccc_alist_define(alist, "_data", _data) == NULL) {
495                 isccc_sexpr_free(&_data);
496                 goto bad;
497         }
498
499         if (isccc_cc_defineuint32(_ctrl, "_ser", serial) == NULL ||
500             isccc_cc_defineuint32(_ctrl, "_tim", now) == NULL ||
501             (want_expires &&
502              isccc_cc_defineuint32(_ctrl, "_exp", expires) == NULL))
503                 goto bad;
504         if (from != NULL &&
505             isccc_cc_definestring(_ctrl, "_frm", from) == NULL)
506                 goto bad;
507         if (to != NULL &&
508             isccc_cc_definestring(_ctrl, "_to", to) == NULL)
509                 goto bad;
510                 
511         *alistp = alist;
512
513         return (ISC_R_SUCCESS);
514
515  bad:
516         isccc_sexpr_free(&alist);
517
518         return (result);
519 }
520
521 isc_result_t
522 isccc_cc_createmessage(isc_uint32_t version, const char *from, const char *to,
523                      isc_uint32_t serial, isccc_time_t now,
524                      isccc_time_t expires, isccc_sexpr_t **alistp)
525 {
526         return (createmessage(version, from, to, serial, now, expires,
527                               alistp, ISC_TRUE));
528 }
529
530 isc_result_t
531 isccc_cc_createack(isccc_sexpr_t *message, isc_boolean_t ok,
532                  isccc_sexpr_t **ackp)
533 {
534         char *_frm, *_to;
535         isc_uint32_t serial;
536         isccc_sexpr_t *ack, *_ctrl;
537         isc_result_t result;
538         isccc_time_t t;
539
540         REQUIRE(ackp != NULL && *ackp == NULL);
541
542         _ctrl = isccc_alist_lookup(message, "_ctrl");
543         if (_ctrl == NULL ||
544             isccc_cc_lookupuint32(_ctrl, "_ser", &serial) != ISC_R_SUCCESS ||
545             isccc_cc_lookupuint32(_ctrl, "_tim", &t) != ISC_R_SUCCESS)
546                 return (ISC_R_FAILURE);
547         /*
548          * _frm and _to are optional.
549          */
550         _frm = NULL;
551         (void)isccc_cc_lookupstring(_ctrl, "_frm", &_frm);
552         _to = NULL;
553         (void)isccc_cc_lookupstring(_ctrl, "_to", &_to);
554         /*
555          * Create the ack.
556          */
557         ack = NULL;
558         result = createmessage(1, _to, _frm, serial, t, 0, &ack, ISC_FALSE);
559         if (result != ISC_R_SUCCESS)
560                 return (result);
561
562         _ctrl = isccc_alist_lookup(ack, "_ctrl");
563         if (_ctrl == NULL)
564                 return (ISC_R_FAILURE);
565         if (isccc_cc_definestring(ack, "_ack", (ok) ? "1" : "0") == NULL) {
566                 result = ISC_R_NOMEMORY;
567                 goto bad;
568         }
569
570         *ackp = ack;
571
572         return (ISC_R_SUCCESS);
573
574  bad:
575         isccc_sexpr_free(&ack);
576
577         return (result);
578 }
579
580 isc_boolean_t
581 isccc_cc_isack(isccc_sexpr_t *message)
582 {
583         isccc_sexpr_t *_ctrl;
584
585         _ctrl = isccc_alist_lookup(message, "_ctrl");
586         if (_ctrl == NULL)
587                 return (ISC_FALSE);
588         if (isccc_cc_lookupstring(_ctrl, "_ack", NULL) == ISC_R_SUCCESS)
589                 return (ISC_TRUE);
590         return (ISC_FALSE);
591 }
592
593 isc_boolean_t
594 isccc_cc_isreply(isccc_sexpr_t *message)
595 {
596         isccc_sexpr_t *_ctrl;
597
598         _ctrl = isccc_alist_lookup(message, "_ctrl");
599         if (_ctrl == NULL)
600                 return (ISC_FALSE);
601         if (isccc_cc_lookupstring(_ctrl, "_rpl", NULL) == ISC_R_SUCCESS)
602                 return (ISC_TRUE);
603         return (ISC_FALSE);
604 }
605
606 isc_result_t
607 isccc_cc_createresponse(isccc_sexpr_t *message, isccc_time_t now,
608                       isccc_time_t expires, isccc_sexpr_t **alistp)
609 {
610         char *_frm, *_to, *type;
611         isc_uint32_t serial;
612         isccc_sexpr_t *alist, *_ctrl, *_data;
613         isc_result_t result;
614
615         REQUIRE(alistp != NULL && *alistp == NULL);
616
617         _ctrl = isccc_alist_lookup(message, "_ctrl");
618         _data = isccc_alist_lookup(message, "_data");
619         if (_ctrl == NULL ||
620             _data == NULL ||
621             isccc_cc_lookupuint32(_ctrl, "_ser", &serial) != ISC_R_SUCCESS ||
622             isccc_cc_lookupstring(_data, "type", &type) != ISC_R_SUCCESS)
623                 return (ISC_R_FAILURE);
624         /*
625          * _frm and _to are optional.
626          */
627         _frm = NULL;
628         (void)isccc_cc_lookupstring(_ctrl, "_frm", &_frm);
629         _to = NULL;
630         (void)isccc_cc_lookupstring(_ctrl, "_to", &_to);
631         /*
632          * Create the response.
633          */
634         alist = NULL;
635         result = isccc_cc_createmessage(1, _to, _frm, serial, now, expires,
636                                          &alist);
637         if (result != ISC_R_SUCCESS)
638                 return (result);
639         _ctrl = isccc_alist_lookup(alist, "_ctrl");
640         if (_ctrl == NULL)
641                 return (ISC_R_FAILURE);
642         _data = isccc_alist_lookup(alist, "_data");
643         if (_data == NULL)
644                 return (ISC_R_FAILURE);
645         if (isccc_cc_definestring(_ctrl, "_rpl", "1") == NULL ||
646             isccc_cc_definestring(_data, "type", type) == NULL) {
647                 isccc_sexpr_free(&alist);
648                 return (ISC_R_NOMEMORY);
649         }
650
651         *alistp = alist;
652
653         return (ISC_R_SUCCESS);
654 }
655
656 isccc_sexpr_t *
657 isccc_cc_definestring(isccc_sexpr_t *alist, const char *key, const char *str)
658 {
659         size_t len;
660         isccc_region_t r;
661
662         len = strlen(str);
663         DE_CONST(str, r.rstart);
664         r.rend = r.rstart + len;
665
666         return (isccc_alist_definebinary(alist, key, &r));
667 }
668
669 isccc_sexpr_t *
670 isccc_cc_defineuint32(isccc_sexpr_t *alist, const char *key, isc_uint32_t i)
671 {
672         char b[100];
673         size_t len;
674         isccc_region_t r;
675
676         snprintf(b, sizeof(b), "%u", i);
677         len = strlen(b);
678         r.rstart = (unsigned char *)b;
679         r.rend = (unsigned char *)b + len;
680
681         return (isccc_alist_definebinary(alist, key, &r));
682 }
683
684 isc_result_t
685 isccc_cc_lookupstring(isccc_sexpr_t *alist, const char *key, char **strp)
686 {
687         isccc_sexpr_t *kv, *v;
688
689         kv = isccc_alist_assq(alist, key);
690         if (kv != NULL) {
691                 v = ISCCC_SEXPR_CDR(kv);
692                 if (isccc_sexpr_binaryp(v)) {
693                         if (strp != NULL)
694                                 *strp = isccc_sexpr_tostring(v);
695                         return (ISC_R_SUCCESS);
696                 } else
697                         return (ISC_R_EXISTS);
698         }
699
700         return (ISC_R_NOTFOUND);
701 }
702
703 isc_result_t
704 isccc_cc_lookupuint32(isccc_sexpr_t *alist, const char *key,
705                        isc_uint32_t *uintp)
706 {
707         isccc_sexpr_t *kv, *v;
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 (uintp != NULL)
714                                 *uintp = (isc_uint32_t)
715                                         strtoul(isccc_sexpr_tostring(v),
716                                                 NULL, 10);
717                         return (ISC_R_SUCCESS);
718                 } else
719                         return (ISC_R_EXISTS);
720         }
721
722         return (ISC_R_NOTFOUND);
723 }
724
725 static void
726 symtab_undefine(char *key, unsigned int type, isccc_symvalue_t value,
727                 void *arg)
728 {
729         UNUSED(type);
730         UNUSED(value);
731         UNUSED(arg);
732
733         free(key);
734 }
735
736 static isc_boolean_t
737 symtab_clean(char *key, unsigned int type, isccc_symvalue_t value,
738              void *arg)
739 {
740         isccc_time_t *now;
741
742         UNUSED(key);
743         UNUSED(type);
744
745         now = arg;
746
747         if (*now < value.as_uinteger)
748                 return (ISC_FALSE);
749         if ((*now - value.as_uinteger) < DUP_LIFETIME)
750                 return (ISC_FALSE);
751         return (ISC_TRUE);
752 }
753
754 isc_result_t
755 isccc_cc_createsymtab(isccc_symtab_t **symtabp)
756 {
757         return (isccc_symtab_create(11897, symtab_undefine, NULL, ISC_FALSE,
758                                   symtabp));
759 }
760
761 void
762 isccc_cc_cleansymtab(isccc_symtab_t *symtab, isccc_time_t now)
763 {
764         isccc_symtab_foreach(symtab, symtab_clean, &now);
765 }
766
767 static isc_boolean_t
768 has_whitespace(const char *str)
769 {
770         char c;
771
772         if (str == NULL)
773                 return (ISC_FALSE);
774         while ((c = *str++) != '\0') {
775                 if (c == ' ' || c == '\t' || c == '\n')
776                         return (ISC_TRUE);
777         }
778         return (ISC_FALSE);
779 }
780
781 isc_result_t
782 isccc_cc_checkdup(isccc_symtab_t *symtab, isccc_sexpr_t *message,
783                 isccc_time_t now)
784 {
785         const char *_frm;
786         const char *_to;
787         char *_ser, *_tim, *tmp;
788         isc_result_t result;
789         char *key;
790         size_t len;
791         isccc_symvalue_t value;
792         isccc_sexpr_t *_ctrl;
793
794         _ctrl = isccc_alist_lookup(message, "_ctrl");
795         if (_ctrl == NULL ||
796             isccc_cc_lookupstring(_ctrl, "_ser", &_ser) != ISC_R_SUCCESS ||
797             isccc_cc_lookupstring(_ctrl, "_tim", &_tim) != ISC_R_SUCCESS)
798                 return (ISC_R_FAILURE);
799         /*
800          * _frm and _to are optional.
801          */
802         if (isccc_cc_lookupstring(_ctrl, "_frm", &tmp) != ISC_R_SUCCESS)
803                 _frm = "";
804         else
805                 _frm = tmp;
806         if (isccc_cc_lookupstring(_ctrl, "_to", &tmp) != ISC_R_SUCCESS)
807                 _to = "";
808         else
809                 _to = tmp;
810         /*
811          * Ensure there is no newline in any of the strings.  This is so
812          * we can write them to a file later.
813          */
814         if (has_whitespace(_frm) || has_whitespace(_to) ||
815             has_whitespace(_ser) || has_whitespace(_tim))
816                 return (ISC_R_FAILURE);
817         len = strlen(_frm) + strlen(_to) + strlen(_ser) + strlen(_tim) + 4;
818         key = malloc(len);
819         if (key == NULL)
820                 return (ISC_R_NOMEMORY);
821         snprintf(key, len, "%s;%s;%s;%s", _frm, _to, _ser, _tim);
822         value.as_uinteger = now;
823         result = isccc_symtab_define(symtab, key, ISCCC_SYMTYPE_CCDUP, value,
824                                    isccc_symexists_reject);
825         if (result != ISC_R_SUCCESS) {
826                 free(key);
827                 return (result);
828         }
829
830         return (ISC_R_SUCCESS);
831 }