]> CyberLeo.Net >> Repos - FreeBSD/stable/9.git/blob - contrib/bind9/lib/isccc/cc.c
MFV r306384:
[FreeBSD/stable/9.git] / contrib / bind9 / lib / isccc / cc.c
1 /*
2  * Portions Copyright (C) 2004-2007, 2012, 2013, 2015, 2016  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 /*! \file */
33
34 #include <config.h>
35
36 #include <stdio.h>
37 #include <string.h>
38 #include <errno.h>
39
40 #include <isc/assertions.h>
41 #include <isc/hmacmd5.h>
42 #include <isc/print.h>
43 #include <isc/safe.h>
44 #include <isc/stdlib.h>
45
46 #include <isccc/alist.h>
47 #include <isccc/base64.h>
48 #include <isccc/cc.h>
49 #include <isccc/result.h>
50 #include <isccc/sexpr.h>
51 #include <isccc/symtab.h>
52 #include <isccc/symtype.h>
53 #include <isccc/util.h>
54
55 #define MAX_TAGS                256
56 #define DUP_LIFETIME            900
57
58 typedef isccc_sexpr_t *sexpr_ptr;
59
60 static unsigned char auth_hmd5[] = {
61         0x05, 0x5f, 0x61, 0x75, 0x74, 0x68,             /*%< len + _auth */
62         ISCCC_CCMSGTYPE_TABLE,                          /*%< message type */
63         0x00, 0x00, 0x00, 0x20,                         /*%< length == 32 */
64         0x04, 0x68, 0x6d, 0x64, 0x35,                   /*%< len + hmd5 */
65         ISCCC_CCMSGTYPE_BINARYDATA,                     /*%< message type */
66         0x00, 0x00, 0x00, 0x16,                         /*%< length == 22 */
67         /*
68          * The base64 encoding of one of our HMAC-MD5 signatures is
69          * 22 bytes.
70          */
71         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
72         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
73         0x00, 0x00, 0x00, 0x00, 0x00, 0x00
74 };
75
76 #define HMD5_OFFSET     21              /*%< 21 = 6 + 1 + 4 + 5 + 1 + 4 */
77 #define HMD5_LENGTH     22
78
79 static isc_result_t
80 table_towire(isccc_sexpr_t *alist, isccc_region_t *target);
81
82 static isc_result_t
83 list_towire(isccc_sexpr_t *alist, isccc_region_t *target);
84
85 static isc_result_t
86 value_towire(isccc_sexpr_t *elt, isccc_region_t *target) {
87         unsigned int len;
88         unsigned char *lenp;
89         isccc_region_t *vr;
90         isc_result_t result;
91
92         if (isccc_sexpr_binaryp(elt)) {
93                 vr = isccc_sexpr_tobinary(elt);
94                 len = REGION_SIZE(*vr);
95                 if (REGION_SIZE(*target) < 1 + 4 + len)
96                         return (ISC_R_NOSPACE);
97                 PUT8(ISCCC_CCMSGTYPE_BINARYDATA, target->rstart);
98                 PUT32(len, target->rstart);
99                 if (REGION_SIZE(*target) < len)
100                         return (ISC_R_NOSPACE);
101                 PUT_MEM(vr->rstart, len, target->rstart);
102         } else if (isccc_alist_alistp(elt)) {
103                 if (REGION_SIZE(*target) < 1 + 4)
104                         return (ISC_R_NOSPACE);
105                 PUT8(ISCCC_CCMSGTYPE_TABLE, target->rstart);
106                 /*
107                  * Emit a placeholder length.
108                  */
109                 lenp = target->rstart;
110                 PUT32(0, target->rstart);
111                 /*
112                  * Emit the table.
113                  */
114                 result = table_towire(elt, target);
115                 if (result != ISC_R_SUCCESS)
116                         return (result);
117                 len = (unsigned int)(target->rstart - lenp);
118                 /*
119                  * 'len' is 4 bytes too big, since it counts
120                  * the placeholder length too.  Adjust and
121                  * emit.
122                  */
123                 INSIST(len >= 4U);
124                 len -= 4;
125                 PUT32(len, lenp);
126         } else if (isccc_sexpr_listp(elt)) {
127                 if (REGION_SIZE(*target) < 1 + 4)
128                         return (ISC_R_NOSPACE);
129                 PUT8(ISCCC_CCMSGTYPE_LIST, target->rstart);
130                 /*
131                  * Emit a placeholder length and count.
132                  */
133                 lenp = target->rstart;
134                 PUT32(0, target->rstart);
135                 /*
136                  * Emit the list.
137                  */
138                 result = list_towire(elt, target);
139                 if (result != ISC_R_SUCCESS)
140                         return (result);
141                 len = (unsigned int)(target->rstart - lenp);
142                 /*
143                  * 'len' is 4 bytes too big, since it counts
144                  * the placeholder length.  Adjust and emit.
145                  */
146                 INSIST(len >= 4U);
147                 len -= 4;
148                 PUT32(len, lenp);
149         }
150
151         return (ISC_R_SUCCESS);
152 }
153
154 static isc_result_t
155 table_towire(isccc_sexpr_t *alist, isccc_region_t *target)
156 {
157         isccc_sexpr_t *kv, *elt, *k, *v;
158         char *ks;
159         isc_result_t result;
160         unsigned int len;
161
162         for (elt = isccc_alist_first(alist);
163              elt != NULL;
164              elt = ISCCC_SEXPR_CDR(elt)) {
165                 kv = ISCCC_SEXPR_CAR(elt);
166                 k = ISCCC_SEXPR_CAR(kv);
167                 ks = isccc_sexpr_tostring(k);
168                 v = ISCCC_SEXPR_CDR(kv);
169                 len = (unsigned int)strlen(ks);
170                 INSIST(len <= 255U);
171                 /*
172                  * Emit the key name.
173                  */
174                 if (REGION_SIZE(*target) < 1 + len)
175                         return (ISC_R_NOSPACE);
176                 PUT8(len, target->rstart);
177                 PUT_MEM(ks, len, target->rstart);
178                 /*
179                  * Emit the value.
180                  */
181                 result = value_towire(v, target);
182                 if (result != ISC_R_SUCCESS)
183                         return (result);
184         }
185
186         return (ISC_R_SUCCESS);
187 }
188
189 static isc_result_t
190 list_towire(isccc_sexpr_t *list, isccc_region_t *target)
191 {
192         isc_result_t result;
193
194         while (list != NULL) {
195                 result = value_towire(ISCCC_SEXPR_CAR(list), target);
196                 if (result != ISC_R_SUCCESS)
197                         return (result);
198                 list = ISCCC_SEXPR_CDR(list);
199         }
200
201         return (ISC_R_SUCCESS);
202 }
203
204 static isc_result_t
205 sign(unsigned char *data, unsigned int length, unsigned char *hmd5,
206      isccc_region_t *secret)
207 {
208         isc_hmacmd5_t ctx;
209         isc_result_t result;
210         isccc_region_t source, target;
211         unsigned char digest[ISC_MD5_DIGESTLENGTH];
212         unsigned char digestb64[ISC_MD5_DIGESTLENGTH * 4];
213
214         isc_hmacmd5_init(&ctx, secret->rstart, REGION_SIZE(*secret));
215         isc_hmacmd5_update(&ctx, data, length);
216         isc_hmacmd5_sign(&ctx, digest);
217         source.rstart = digest;
218         source.rend = digest + ISC_MD5_DIGESTLENGTH;
219         target.rstart = digestb64;
220         target.rend = digestb64 + ISC_MD5_DIGESTLENGTH * 4;
221         result = isccc_base64_encode(&source, 64, "", &target);
222         if (result != ISC_R_SUCCESS)
223                 return (result);
224         PUT_MEM(digestb64, HMD5_LENGTH, hmd5);
225
226         return (ISC_R_SUCCESS);
227 }
228
229 isc_result_t
230 isccc_cc_towire(isccc_sexpr_t *alist, isccc_region_t *target,
231               isccc_region_t *secret)
232 {
233         unsigned char *hmd5_rstart, *signed_rstart;
234         isc_result_t result;
235
236         if (REGION_SIZE(*target) < 4 + sizeof(auth_hmd5))
237                 return (ISC_R_NOSPACE);
238         /*
239          * Emit protocol version.
240          */
241         PUT32(1, target->rstart);
242         if (secret != NULL) {
243                 /*
244                  * Emit _auth section with zeroed HMAC-MD5 signature.
245                  * We'll replace the zeros with the real signature once
246                  * we know what it is.
247                  */
248                 hmd5_rstart = target->rstart + HMD5_OFFSET;
249                 PUT_MEM(auth_hmd5, sizeof(auth_hmd5), target->rstart);
250         } else
251                 hmd5_rstart = NULL;
252         signed_rstart = target->rstart;
253         /*
254          * Delete any existing _auth section so that we don't try
255          * to encode it.
256          */
257         isccc_alist_delete(alist, "_auth");
258         /*
259          * Emit the message.
260          */
261         result = table_towire(alist, target);
262         if (result != ISC_R_SUCCESS)
263                 return (result);
264         if (secret != NULL)
265                 return (sign(signed_rstart,
266                              (unsigned int)(target->rstart - signed_rstart),
267                              hmd5_rstart, secret));
268         return (ISC_R_SUCCESS);
269 }
270
271 static isc_result_t
272 verify(isccc_sexpr_t *alist, unsigned char *data, unsigned int length,
273        isccc_region_t *secret)
274 {
275         isc_hmacmd5_t ctx;
276         isccc_region_t source;
277         isccc_region_t target;
278         isc_result_t result;
279         isccc_sexpr_t *_auth, *hmd5;
280         unsigned char digest[ISC_MD5_DIGESTLENGTH];
281         unsigned char digestb64[ISC_MD5_DIGESTLENGTH * 4];
282
283         /*
284          * Extract digest.
285          */
286         _auth = isccc_alist_lookup(alist, "_auth");
287         if (!isccc_alist_alistp(_auth))
288                 return (ISC_R_FAILURE);
289         hmd5 = isccc_alist_lookup(_auth, "hmd5");
290         if (!isccc_sexpr_binaryp(hmd5))
291                 return (ISC_R_FAILURE);
292         /*
293          * Compute digest.
294          */
295         isc_hmacmd5_init(&ctx, secret->rstart, REGION_SIZE(*secret));
296         isc_hmacmd5_update(&ctx, data, length);
297         isc_hmacmd5_sign(&ctx, digest);
298         source.rstart = digest;
299         source.rend = digest + ISC_MD5_DIGESTLENGTH;
300         target.rstart = digestb64;
301         target.rend = digestb64 + ISC_MD5_DIGESTLENGTH * 4;
302         result = isccc_base64_encode(&source, 64, "", &target);
303         if (result != ISC_R_SUCCESS)
304                 return (result);
305         /*
306          * Strip trailing == and NUL terminate target.
307          */
308         target.rstart -= 2;
309         *target.rstart++ = '\0';
310         /*
311          * Verify.
312          */
313         if (!isc_safe_memequal((unsigned char *) isccc_sexpr_tostring(hmd5),
314                                digestb64, HMD5_LENGTH))
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         unsigned int msgtype;
330         isc_uint32_t len;
331         isccc_sexpr_t *value;
332         isccc_region_t active;
333         isc_result_t result;
334
335         if (REGION_SIZE(*source) < 1 + 4)
336                 return (ISC_R_UNEXPECTEDEND);
337         GET8(msgtype, source->rstart);
338         GET32(len, source->rstart);
339         if (REGION_SIZE(*source) < len)
340                 return (ISC_R_UNEXPECTEDEND);
341         active.rstart = source->rstart;
342         active.rend = active.rstart + len;
343         source->rstart = active.rend;
344         if (msgtype == ISCCC_CCMSGTYPE_BINARYDATA) {
345                 value = isccc_sexpr_frombinary(&active);
346                 if (value != NULL) {
347                         *valuep = value;
348                         result = ISC_R_SUCCESS;
349                 } else
350                         result = ISC_R_NOMEMORY;
351         } else if (msgtype == ISCCC_CCMSGTYPE_TABLE)
352                 result = table_fromwire(&active, NULL, valuep);
353         else if (msgtype == ISCCC_CCMSGTYPE_LIST)
354                 result = list_fromwire(&active, valuep);
355         else
356                 result = ISCCC_R_SYNTAX;
357
358         return (result);
359 }
360
361 static isc_result_t
362 table_fromwire(isccc_region_t *source, isccc_region_t *secret,
363                isccc_sexpr_t **alistp)
364 {
365         char key[256];
366         isc_uint32_t len;
367         isc_result_t result;
368         isccc_sexpr_t *alist, *value;
369         isc_boolean_t first_tag;
370         unsigned char *checksum_rstart;
371
372         REQUIRE(alistp != NULL && *alistp == NULL);
373
374         checksum_rstart = NULL;
375         first_tag = ISC_TRUE;
376         alist = isccc_alist_create();
377         if (alist == NULL)
378                 return (ISC_R_NOMEMORY);
379
380         while (!REGION_EMPTY(*source)) {
381                 GET8(len, source->rstart);
382                 if (REGION_SIZE(*source) < len) {
383                         result = ISC_R_UNEXPECTEDEND;
384                         goto bad;
385                 }
386                 GET_MEM(key, len, source->rstart);
387                 key[len] = '\0';        /* Ensure NUL termination. */
388                 value = NULL;
389                 result = value_fromwire(source, &value);
390                 if (result != ISC_R_SUCCESS)
391                         goto bad;
392                 if (isccc_alist_define(alist, key, value) == NULL) {
393                         result = ISC_R_NOMEMORY;
394                         goto bad;
395                 }
396                 if (first_tag && secret != NULL && strcmp(key, "_auth") == 0)
397                         checksum_rstart = source->rstart;
398                 first_tag = ISC_FALSE;
399         }
400
401         if (secret != NULL) {
402                 if (checksum_rstart != NULL)
403                         result = verify(alist, checksum_rstart,
404                                         (unsigned int)
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         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 (ISC_R_NOMEMORY);
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 (!isccc_alist_alistp(_ctrl) ||
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                 result = ISC_R_FAILURE;
565                 goto bad;
566         }
567         if (isccc_cc_definestring(ack, "_ack", (ok) ? "1" : "0") == NULL) {
568                 result = ISC_R_NOMEMORY;
569                 goto bad;
570         }
571
572         *ackp = ack;
573
574         return (ISC_R_SUCCESS);
575
576  bad:
577         isccc_sexpr_free(&ack);
578
579         return (result);
580 }
581
582 isc_boolean_t
583 isccc_cc_isack(isccc_sexpr_t *message) {
584         isccc_sexpr_t *_ctrl;
585
586         _ctrl = isccc_alist_lookup(message, "_ctrl");
587         if (!isccc_alist_alistp(_ctrl))
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         isccc_sexpr_t *_ctrl;
597
598         _ctrl = isccc_alist_lookup(message, "_ctrl");
599         if (!isccc_alist_alistp(_ctrl))
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 = NULL;
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 (!isccc_alist_alistp(_ctrl) || !isccc_alist_alistp(_data) ||
620             isccc_cc_lookupuint32(_ctrl, "_ser", &serial) != ISC_R_SUCCESS ||
621             isccc_cc_lookupstring(_data, "type", &type) != ISC_R_SUCCESS)
622                 return (ISC_R_FAILURE);
623         /*
624          * _frm and _to are optional.
625          */
626         _frm = NULL;
627         (void)isccc_cc_lookupstring(_ctrl, "_frm", &_frm);
628         _to = NULL;
629         (void)isccc_cc_lookupstring(_ctrl, "_to", &_to);
630         /*
631          * Create the response.
632          */
633         alist = NULL;
634         result = isccc_cc_createmessage(1, _to, _frm, serial, now, expires,
635                                          &alist);
636         if (result != ISC_R_SUCCESS)
637                 return (result);
638
639         _ctrl = isccc_alist_lookup(alist, "_ctrl");
640         if (_ctrl == NULL) {
641                 result = ISC_R_FAILURE;
642                 goto bad;
643         }
644
645         _data = isccc_alist_lookup(alist, "_data");
646         if (_data == NULL) {
647                 result = ISC_R_FAILURE;
648                 goto bad;
649         }
650
651         if (isccc_cc_definestring(_ctrl, "_rpl", "1") == NULL ||
652             isccc_cc_definestring(_data, "type", type) == NULL)
653         {
654                 result = ISC_R_NOMEMORY;
655                 goto bad;
656         }
657
658         *alistp = alist;
659
660         return (ISC_R_SUCCESS);
661
662  bad:
663         isccc_sexpr_free(&alist);
664         return (result);
665 }
666
667 isccc_sexpr_t *
668 isccc_cc_definestring(isccc_sexpr_t *alist, const char *key, const char *str) {
669         size_t len;
670         isccc_region_t r;
671
672         len = strlen(str);
673         DE_CONST(str, r.rstart);
674         r.rend = r.rstart + len;
675
676         return (isccc_alist_definebinary(alist, key, &r));
677 }
678
679 isccc_sexpr_t *
680 isccc_cc_defineuint32(isccc_sexpr_t *alist, const char *key, isc_uint32_t i) {
681         char b[100];
682         size_t len;
683         isccc_region_t r;
684
685         snprintf(b, sizeof(b), "%u", i);
686         len = strlen(b);
687         r.rstart = (unsigned char *)b;
688         r.rend = (unsigned char *)b + len;
689
690         return (isccc_alist_definebinary(alist, key, &r));
691 }
692
693 isc_result_t
694 isccc_cc_lookupstring(isccc_sexpr_t *alist, const char *key, char **strp) {
695         isccc_sexpr_t *kv, *v;
696
697         REQUIRE(strp == NULL || *strp == NULL);
698
699         kv = isccc_alist_assq(alist, key);
700         if (kv != NULL) {
701                 v = ISCCC_SEXPR_CDR(kv);
702                 if (isccc_sexpr_binaryp(v)) {
703                         if (strp != NULL)
704                                 *strp = isccc_sexpr_tostring(v);
705                         return (ISC_R_SUCCESS);
706                 } else
707                         return (ISC_R_EXISTS);
708         }
709
710         return (ISC_R_NOTFOUND);
711 }
712
713 isc_result_t
714 isccc_cc_lookupuint32(isccc_sexpr_t *alist, const char *key,
715                        isc_uint32_t *uintp)
716 {
717         isccc_sexpr_t *kv, *v;
718
719         kv = isccc_alist_assq(alist, key);
720         if (kv != NULL) {
721                 v = ISCCC_SEXPR_CDR(kv);
722                 if (isccc_sexpr_binaryp(v)) {
723                         if (uintp != NULL)
724                                 *uintp = (isc_uint32_t)
725                                         strtoul(isccc_sexpr_tostring(v),
726                                                 NULL, 10);
727                         return (ISC_R_SUCCESS);
728                 } else
729                         return (ISC_R_EXISTS);
730         }
731
732         return (ISC_R_NOTFOUND);
733 }
734
735 static void
736 symtab_undefine(char *key, unsigned int type, isccc_symvalue_t value,
737                 void *arg)
738 {
739         UNUSED(type);
740         UNUSED(value);
741         UNUSED(arg);
742
743         free(key);
744 }
745
746 static isc_boolean_t
747 symtab_clean(char *key, unsigned int type, isccc_symvalue_t value, void *arg) {
748         isccc_time_t *now;
749
750         UNUSED(key);
751         UNUSED(type);
752
753         now = arg;
754
755         if (*now < value.as_uinteger)
756                 return (ISC_FALSE);
757         if ((*now - value.as_uinteger) < DUP_LIFETIME)
758                 return (ISC_FALSE);
759         return (ISC_TRUE);
760 }
761
762 isc_result_t
763 isccc_cc_createsymtab(isccc_symtab_t **symtabp) {
764         return (isccc_symtab_create(11897, symtab_undefine, NULL, ISC_FALSE,
765                                   symtabp));
766 }
767
768 void
769 isccc_cc_cleansymtab(isccc_symtab_t *symtab, isccc_time_t now) {
770         isccc_symtab_foreach(symtab, symtab_clean, &now);
771 }
772
773 static isc_boolean_t
774 has_whitespace(const char *str) {
775         char c;
776
777         if (str == NULL)
778                 return (ISC_FALSE);
779         while ((c = *str++) != '\0') {
780                 if (c == ' ' || c == '\t' || c == '\n')
781                         return (ISC_TRUE);
782         }
783         return (ISC_FALSE);
784 }
785
786 isc_result_t
787 isccc_cc_checkdup(isccc_symtab_t *symtab, isccc_sexpr_t *message,
788                 isccc_time_t now)
789 {
790         const char *_frm;
791         const char *_to;
792         char *_ser = NULL, *_tim = NULL, *tmp;
793         isc_result_t result;
794         char *key;
795         size_t len;
796         isccc_symvalue_t value;
797         isccc_sexpr_t *_ctrl;
798
799         _ctrl = isccc_alist_lookup(message, "_ctrl");
800         if (!isccc_alist_alistp(_ctrl) ||
801             isccc_cc_lookupstring(_ctrl, "_ser", &_ser) != ISC_R_SUCCESS ||
802             isccc_cc_lookupstring(_ctrl, "_tim", &_tim) != ISC_R_SUCCESS)
803                 return (ISC_R_FAILURE);
804
805         INSIST(_ser != NULL);
806         INSIST(_tim != NULL);
807
808         /*
809          * _frm and _to are optional.
810          */
811         tmp = NULL;
812         if (isccc_cc_lookupstring(_ctrl, "_frm", &tmp) != ISC_R_SUCCESS)
813                 _frm = "";
814         else
815                 _frm = tmp;
816         tmp = NULL;
817         if (isccc_cc_lookupstring(_ctrl, "_to", &tmp) != ISC_R_SUCCESS)
818                 _to = "";
819         else
820                 _to = tmp;
821         /*
822          * Ensure there is no newline in any of the strings.  This is so
823          * we can write them to a file later.
824          */
825         if (has_whitespace(_frm) || has_whitespace(_to) ||
826             has_whitespace(_ser) || has_whitespace(_tim))
827                 return (ISC_R_FAILURE);
828         len = strlen(_frm) + strlen(_to) + strlen(_ser) + strlen(_tim) + 4;
829         key = malloc(len);
830         if (key == NULL)
831                 return (ISC_R_NOMEMORY);
832         snprintf(key, len, "%s;%s;%s;%s", _frm, _to, _ser, _tim);
833         value.as_uinteger = now;
834         result = isccc_symtab_define(symtab, key, ISCCC_SYMTYPE_CCDUP, value,
835                                    isccc_symexists_reject);
836         if (result != ISC_R_SUCCESS) {
837                 free(key);
838                 return (result);
839         }
840
841         return (ISC_R_SUCCESS);
842 }