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