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