4 * Portions Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
5 * Portions Copyright (C) 2001-2003 Internet Software Consortium.
7 * Permission to use, copy, modify, and/or distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
11 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NOMINUM DISCLAIMS ALL
12 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
13 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY
14 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 * Portions Copyright (C) 2001 Nominum, Inc.
21 * Permission to use, copy, modify, and/or distribute this software for any
22 * purpose with or without fee is hereby granted, provided that the above
23 * copyright notice and this permission notice appear in all copies.
25 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NOMINUM DISCLAIMS ALL
26 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
27 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY
28 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
29 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
30 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
31 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
34 /* Id: cc.c,v 1.18 2007/08/28 07:20:43 tbox Exp */
44 #include <isc/assertions.h>
45 #include <isc/hmacmd5.h>
46 #include <isc/print.h>
47 #include <isc/stdlib.h>
49 #include <isccc/alist.h>
50 #include <isccc/base64.h>
52 #include <isccc/result.h>
53 #include <isccc/sexpr.h>
54 #include <isccc/symtab.h>
55 #include <isccc/symtype.h>
56 #include <isccc/util.h>
59 #define DUP_LIFETIME 900
61 typedef isccc_sexpr_t
*sexpr_ptr
;
63 static unsigned char auth_hmd5
[] = {
64 0x05, 0x5f, 0x61, 0x75, 0x74, 0x68, /*%< len + _auth */
65 ISCCC_CCMSGTYPE_TABLE
, /*%< message type */
66 0x00, 0x00, 0x00, 0x20, /*%< length == 32 */
67 0x04, 0x68, 0x6d, 0x64, 0x35, /*%< len + hmd5 */
68 ISCCC_CCMSGTYPE_BINARYDATA
, /*%< message type */
69 0x00, 0x00, 0x00, 0x16, /*%< length == 22 */
71 * The base64 encoding of one of our HMAC-MD5 signatures is
74 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
75 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
76 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
79 #define HMD5_OFFSET 21 /*%< 21 = 6 + 1 + 4 + 5 + 1 + 4 */
80 #define HMD5_LENGTH 22
83 table_towire(isccc_sexpr_t
*alist
, isccc_region_t
*target
);
86 list_towire(isccc_sexpr_t
*alist
, isccc_region_t
*target
);
89 value_towire(isccc_sexpr_t
*elt
, isccc_region_t
*target
)
96 if (isccc_sexpr_binaryp(elt
)) {
97 vr
= isccc_sexpr_tobinary(elt
);
98 len
= REGION_SIZE(*vr
);
99 if (REGION_SIZE(*target
) < 1 + 4 + len
)
100 return (ISC_R_NOSPACE
);
101 PUT8(ISCCC_CCMSGTYPE_BINARYDATA
, target
->rstart
);
102 PUT32(len
, target
->rstart
);
103 if (REGION_SIZE(*target
) < len
)
104 return (ISC_R_NOSPACE
);
105 PUT_MEM(vr
->rstart
, len
, target
->rstart
);
106 } else if (isccc_alist_alistp(elt
)) {
107 if (REGION_SIZE(*target
) < 1 + 4)
108 return (ISC_R_NOSPACE
);
109 PUT8(ISCCC_CCMSGTYPE_TABLE
, target
->rstart
);
111 * Emit a placeholder length.
113 lenp
= target
->rstart
;
114 PUT32(0, target
->rstart
);
118 result
= table_towire(elt
, target
);
119 if (result
!= ISC_R_SUCCESS
)
121 len
= (size_t)(target
->rstart
- lenp
);
123 * 'len' is 4 bytes too big, since it counts
124 * the placeholder length too. Adjust and
130 } else if (isccc_sexpr_listp(elt
)) {
131 if (REGION_SIZE(*target
) < 1 + 4)
132 return (ISC_R_NOSPACE
);
133 PUT8(ISCCC_CCMSGTYPE_LIST
, target
->rstart
);
135 * Emit a placeholder length and count.
137 lenp
= target
->rstart
;
138 PUT32(0, target
->rstart
);
142 result
= list_towire(elt
, target
);
143 if (result
!= ISC_R_SUCCESS
)
145 len
= (size_t)(target
->rstart
- lenp
);
147 * 'len' is 4 bytes too big, since it counts
148 * the placeholder length. Adjust and emit.
155 return (ISC_R_SUCCESS
);
159 table_towire(isccc_sexpr_t
*alist
, isccc_region_t
*target
)
161 isccc_sexpr_t
*kv
, *elt
, *k
, *v
;
166 for (elt
= isccc_alist_first(alist
);
168 elt
= ISCCC_SEXPR_CDR(elt
)) {
169 kv
= ISCCC_SEXPR_CAR(elt
);
170 k
= ISCCC_SEXPR_CAR(kv
);
171 ks
= isccc_sexpr_tostring(k
);
172 v
= ISCCC_SEXPR_CDR(kv
);
178 if (REGION_SIZE(*target
) < 1 + len
)
179 return (ISC_R_NOSPACE
);
180 PUT8(len
, target
->rstart
);
181 PUT_MEM(ks
, len
, target
->rstart
);
185 result
= value_towire(v
, target
);
186 if (result
!= ISC_R_SUCCESS
)
190 return (ISC_R_SUCCESS
);
194 list_towire(isccc_sexpr_t
*list
, isccc_region_t
*target
)
198 while (list
!= NULL
) {
199 result
= value_towire(ISCCC_SEXPR_CAR(list
), target
);
200 if (result
!= ISC_R_SUCCESS
)
202 list
= ISCCC_SEXPR_CDR(list
);
205 return (ISC_R_SUCCESS
);
209 sign(unsigned char *data
, unsigned int length
, unsigned char *hmd5
,
210 isccc_region_t
*secret
)
214 isccc_region_t source
, target
;
215 unsigned char digest
[ISC_MD5_DIGESTLENGTH
];
216 unsigned char digestb64
[ISC_MD5_DIGESTLENGTH
* 4];
218 isc_hmacmd5_init(&ctx
, secret
->rstart
, REGION_SIZE(*secret
));
219 isc_hmacmd5_update(&ctx
, data
, length
);
220 isc_hmacmd5_sign(&ctx
, digest
);
221 source
.rstart
= digest
;
222 source
.rend
= digest
+ ISC_MD5_DIGESTLENGTH
;
223 target
.rstart
= digestb64
;
224 target
.rend
= digestb64
+ ISC_MD5_DIGESTLENGTH
* 4;
225 result
= isccc_base64_encode(&source
, 64, "", &target
);
226 if (result
!= ISC_R_SUCCESS
)
228 PUT_MEM(digestb64
, HMD5_LENGTH
, hmd5
);
230 return (ISC_R_SUCCESS
);
234 isccc_cc_towire(isccc_sexpr_t
*alist
, isccc_region_t
*target
,
235 isccc_region_t
*secret
)
237 unsigned char *hmd5_rstart
, *signed_rstart
;
240 if (REGION_SIZE(*target
) < 4 + sizeof(auth_hmd5
))
241 return (ISC_R_NOSPACE
);
243 * Emit protocol version.
245 PUT32(1, target
->rstart
);
246 if (secret
!= NULL
) {
248 * Emit _auth section with zeroed HMAC-MD5 signature.
249 * We'll replace the zeros with the real signature once
250 * we know what it is.
252 hmd5_rstart
= target
->rstart
+ HMD5_OFFSET
;
253 PUT_MEM(auth_hmd5
, sizeof(auth_hmd5
), target
->rstart
);
256 signed_rstart
= target
->rstart
;
258 * Delete any existing _auth section so that we don't try
261 isccc_alist_delete(alist
, "_auth");
265 result
= table_towire(alist
, target
);
266 if (result
!= ISC_R_SUCCESS
)
269 return (sign(signed_rstart
, (target
->rstart
- signed_rstart
),
270 hmd5_rstart
, secret
));
271 return (ISC_R_SUCCESS
);
275 verify(isccc_sexpr_t
*alist
, unsigned char *data
, unsigned int length
,
276 isccc_region_t
*secret
)
279 isccc_region_t source
;
280 isccc_region_t target
;
282 isccc_sexpr_t
*_auth
, *hmd5
;
283 unsigned char digest
[ISC_MD5_DIGESTLENGTH
];
284 unsigned char digestb64
[ISC_MD5_DIGESTLENGTH
* 4];
289 _auth
= isccc_alist_lookup(alist
, "_auth");
291 return (ISC_R_FAILURE
);
292 hmd5
= isccc_alist_lookup(_auth
, "hmd5");
294 return (ISC_R_FAILURE
);
298 isc_hmacmd5_init(&ctx
, secret
->rstart
, REGION_SIZE(*secret
));
299 isc_hmacmd5_update(&ctx
, data
, length
);
300 isc_hmacmd5_sign(&ctx
, digest
);
301 source
.rstart
= digest
;
302 source
.rend
= digest
+ ISC_MD5_DIGESTLENGTH
;
303 target
.rstart
= digestb64
;
304 target
.rend
= digestb64
+ ISC_MD5_DIGESTLENGTH
* 4;
305 result
= isccc_base64_encode(&source
, 64, "", &target
);
306 if (result
!= ISC_R_SUCCESS
)
309 * Strip trailing == and NUL terminate target.
312 *target
.rstart
++ = '\0';
316 if (strcmp((char *)digestb64
, isccc_sexpr_tostring(hmd5
)) != 0)
317 return (ISCCC_R_BADAUTH
);
319 return (ISC_R_SUCCESS
);
323 table_fromwire(isccc_region_t
*source
, isccc_region_t
*secret
,
324 isccc_sexpr_t
**alistp
);
327 list_fromwire(isccc_region_t
*source
, isccc_sexpr_t
**listp
);
330 value_fromwire(isccc_region_t
*source
, isccc_sexpr_t
**valuep
)
332 unsigned int msgtype
;
334 isccc_sexpr_t
*value
;
335 isccc_region_t active
;
338 if (REGION_SIZE(*source
) < 1 + 4)
339 return (ISC_R_UNEXPECTEDEND
);
340 GET8(msgtype
, source
->rstart
);
341 GET32(len
, source
->rstart
);
342 if (REGION_SIZE(*source
) < len
)
343 return (ISC_R_UNEXPECTEDEND
);
344 active
.rstart
= source
->rstart
;
345 active
.rend
= active
.rstart
+ len
;
346 source
->rstart
= active
.rend
;
347 if (msgtype
== ISCCC_CCMSGTYPE_BINARYDATA
) {
348 value
= isccc_sexpr_frombinary(&active
);
351 result
= ISC_R_SUCCESS
;
353 result
= ISC_R_NOMEMORY
;
354 } else if (msgtype
== ISCCC_CCMSGTYPE_TABLE
)
355 result
= table_fromwire(&active
, NULL
, valuep
);
356 else if (msgtype
== ISCCC_CCMSGTYPE_LIST
)
357 result
= list_fromwire(&active
, valuep
);
359 result
= ISCCC_R_SYNTAX
;
365 table_fromwire(isccc_region_t
*source
, isccc_region_t
*secret
,
366 isccc_sexpr_t
**alistp
)
371 isccc_sexpr_t
*alist
, *value
;
372 isc_boolean_t first_tag
;
373 unsigned char *checksum_rstart
;
375 REQUIRE(alistp
!= NULL
&& *alistp
== NULL
);
377 checksum_rstart
= NULL
;
378 first_tag
= ISC_TRUE
;
379 alist
= isccc_alist_create();
381 return (ISC_R_NOMEMORY
);
383 while (!REGION_EMPTY(*source
)) {
384 GET8(len
, source
->rstart
);
385 if (REGION_SIZE(*source
) < len
) {
386 result
= ISC_R_UNEXPECTEDEND
;
389 GET_MEM(key
, len
, source
->rstart
);
390 key
[len
] = '\0'; /* Ensure NUL termination. */
392 result
= value_fromwire(source
, &value
);
393 if (result
!= ISC_R_SUCCESS
)
395 if (isccc_alist_define(alist
, key
, value
) == NULL
) {
396 result
= ISC_R_NOMEMORY
;
399 if (first_tag
&& secret
!= NULL
&& strcmp(key
, "_auth") == 0)
400 checksum_rstart
= source
->rstart
;
401 first_tag
= ISC_FALSE
;
406 if (secret
!= NULL
) {
407 if (checksum_rstart
!= NULL
)
408 return (verify(alist
, checksum_rstart
,
409 (source
->rend
- checksum_rstart
),
411 return (ISCCC_R_BADAUTH
);
414 return (ISC_R_SUCCESS
);
417 isccc_sexpr_free(&alist
);
423 list_fromwire(isccc_region_t
*source
, isccc_sexpr_t
**listp
)
425 isccc_sexpr_t
*list
, *value
;
429 while (!REGION_EMPTY(*source
)) {
431 result
= value_fromwire(source
, &value
);
432 if (result
!= ISC_R_SUCCESS
) {
433 isccc_sexpr_free(&list
);
436 if (isccc_sexpr_addtolist(&list
, value
) == NULL
) {
437 isccc_sexpr_free(&value
);
438 isccc_sexpr_free(&list
);
445 return (ISC_R_SUCCESS
);
449 isccc_cc_fromwire(isccc_region_t
*source
, isccc_sexpr_t
**alistp
,
450 isccc_region_t
*secret
)
453 isc_uint32_t version
;
455 size
= REGION_SIZE(*source
);
457 return (ISC_R_UNEXPECTEDEND
);
458 GET32(version
, source
->rstart
);
460 return (ISCCC_R_UNKNOWNVERSION
);
462 return (table_fromwire(source
, secret
, alistp
));
466 createmessage(isc_uint32_t version
, const char *from
, const char *to
,
467 isc_uint32_t serial
, isccc_time_t now
,
468 isccc_time_t expires
, isccc_sexpr_t
**alistp
,
469 isc_boolean_t want_expires
)
471 isccc_sexpr_t
*alist
, *_ctrl
, *_data
;
474 REQUIRE(alistp
!= NULL
&& *alistp
== NULL
);
477 return (ISCCC_R_UNKNOWNVERSION
);
479 alist
= isccc_alist_create();
481 return (ISC_R_NOMEMORY
);
483 result
= ISC_R_NOMEMORY
;
485 _ctrl
= isccc_alist_create();
488 if (isccc_alist_define(alist
, "_ctrl", _ctrl
) == NULL
) {
489 isccc_sexpr_free(&_ctrl
);
493 _data
= isccc_alist_create();
496 if (isccc_alist_define(alist
, "_data", _data
) == NULL
) {
497 isccc_sexpr_free(&_data
);
501 if (isccc_cc_defineuint32(_ctrl
, "_ser", serial
) == NULL
||
502 isccc_cc_defineuint32(_ctrl
, "_tim", now
) == NULL
||
504 isccc_cc_defineuint32(_ctrl
, "_exp", expires
) == NULL
))
507 isccc_cc_definestring(_ctrl
, "_frm", from
) == NULL
)
510 isccc_cc_definestring(_ctrl
, "_to", to
) == NULL
)
515 return (ISC_R_SUCCESS
);
518 isccc_sexpr_free(&alist
);
524 isccc_cc_createmessage(isc_uint32_t version
, const char *from
, const char *to
,
525 isc_uint32_t serial
, isccc_time_t now
,
526 isccc_time_t expires
, isccc_sexpr_t
**alistp
)
528 return (createmessage(version
, from
, to
, serial
, now
, expires
,
533 isccc_cc_createack(isccc_sexpr_t
*message
, isc_boolean_t ok
,
534 isccc_sexpr_t
**ackp
)
538 isccc_sexpr_t
*ack
, *_ctrl
;
542 REQUIRE(ackp
!= NULL
&& *ackp
== NULL
);
544 _ctrl
= isccc_alist_lookup(message
, "_ctrl");
546 isccc_cc_lookupuint32(_ctrl
, "_ser", &serial
) != ISC_R_SUCCESS
||
547 isccc_cc_lookupuint32(_ctrl
, "_tim", &t
) != ISC_R_SUCCESS
)
548 return (ISC_R_FAILURE
);
550 * _frm and _to are optional.
553 (void)isccc_cc_lookupstring(_ctrl
, "_frm", &_frm
);
555 (void)isccc_cc_lookupstring(_ctrl
, "_to", &_to
);
560 result
= createmessage(1, _to
, _frm
, serial
, t
, 0, &ack
, ISC_FALSE
);
561 if (result
!= ISC_R_SUCCESS
)
564 _ctrl
= isccc_alist_lookup(ack
, "_ctrl");
566 return (ISC_R_FAILURE
);
567 if (isccc_cc_definestring(ack
, "_ack", (ok
) ? "1" : "0") == NULL
) {
568 result
= ISC_R_NOMEMORY
;
574 return (ISC_R_SUCCESS
);
577 isccc_sexpr_free(&ack
);
583 isccc_cc_isack(isccc_sexpr_t
*message
)
585 isccc_sexpr_t
*_ctrl
;
587 _ctrl
= isccc_alist_lookup(message
, "_ctrl");
590 if (isccc_cc_lookupstring(_ctrl
, "_ack", NULL
) == ISC_R_SUCCESS
)
596 isccc_cc_isreply(isccc_sexpr_t
*message
)
598 isccc_sexpr_t
*_ctrl
;
600 _ctrl
= isccc_alist_lookup(message
, "_ctrl");
603 if (isccc_cc_lookupstring(_ctrl
, "_rpl", NULL
) == ISC_R_SUCCESS
)
609 isccc_cc_createresponse(isccc_sexpr_t
*message
, isccc_time_t now
,
610 isccc_time_t expires
, isccc_sexpr_t
**alistp
)
612 char *_frm
, *_to
, *type
;
614 isccc_sexpr_t
*alist
, *_ctrl
, *_data
;
617 REQUIRE(alistp
!= NULL
&& *alistp
== NULL
);
619 _ctrl
= isccc_alist_lookup(message
, "_ctrl");
620 _data
= isccc_alist_lookup(message
, "_data");
623 isccc_cc_lookupuint32(_ctrl
, "_ser", &serial
) != ISC_R_SUCCESS
||
624 isccc_cc_lookupstring(_data
, "type", &type
) != ISC_R_SUCCESS
)
625 return (ISC_R_FAILURE
);
627 * _frm and _to are optional.
630 (void)isccc_cc_lookupstring(_ctrl
, "_frm", &_frm
);
632 (void)isccc_cc_lookupstring(_ctrl
, "_to", &_to
);
634 * Create the response.
637 result
= isccc_cc_createmessage(1, _to
, _frm
, serial
, now
, expires
,
639 if (result
!= ISC_R_SUCCESS
)
641 _ctrl
= isccc_alist_lookup(alist
, "_ctrl");
643 return (ISC_R_FAILURE
);
644 _data
= isccc_alist_lookup(alist
, "_data");
646 return (ISC_R_FAILURE
);
647 if (isccc_cc_definestring(_ctrl
, "_rpl", "1") == NULL
||
648 isccc_cc_definestring(_data
, "type", type
) == NULL
) {
649 isccc_sexpr_free(&alist
);
650 return (ISC_R_NOMEMORY
);
655 return (ISC_R_SUCCESS
);
659 isccc_cc_definestring(isccc_sexpr_t
*alist
, const char *key
, const char *str
)
665 DE_CONST(str
, r
.rstart
);
666 r
.rend
= r
.rstart
+ len
;
668 return (isccc_alist_definebinary(alist
, key
, &r
));
672 isccc_cc_defineuint32(isccc_sexpr_t
*alist
, const char *key
, isc_uint32_t i
)
678 snprintf(b
, sizeof(b
), "%u", i
);
680 r
.rstart
= (unsigned char *)b
;
681 r
.rend
= (unsigned char *)b
+ len
;
683 return (isccc_alist_definebinary(alist
, key
, &r
));
687 isccc_cc_lookupstring(isccc_sexpr_t
*alist
, const char *key
, char **strp
)
689 isccc_sexpr_t
*kv
, *v
;
691 kv
= isccc_alist_assq(alist
, key
);
693 v
= ISCCC_SEXPR_CDR(kv
);
694 if (isccc_sexpr_binaryp(v
)) {
696 *strp
= isccc_sexpr_tostring(v
);
697 return (ISC_R_SUCCESS
);
699 return (ISC_R_EXISTS
);
702 return (ISC_R_NOTFOUND
);
706 isccc_cc_lookupuint32(isccc_sexpr_t
*alist
, const char *key
,
709 isccc_sexpr_t
*kv
, *v
;
711 kv
= isccc_alist_assq(alist
, key
);
713 v
= ISCCC_SEXPR_CDR(kv
);
714 if (isccc_sexpr_binaryp(v
)) {
716 *uintp
= (isc_uint32_t
)
717 strtoul(isccc_sexpr_tostring(v
),
719 return (ISC_R_SUCCESS
);
721 return (ISC_R_EXISTS
);
724 return (ISC_R_NOTFOUND
);
728 symtab_undefine(char *key
, unsigned int type
, isccc_symvalue_t value
,
739 symtab_clean(char *key
, unsigned int type
, isccc_symvalue_t value
,
749 if (*now
< value
.as_uinteger
)
751 if ((*now
- value
.as_uinteger
) < DUP_LIFETIME
)
757 isccc_cc_createsymtab(isccc_symtab_t
**symtabp
)
759 return (isccc_symtab_create(11897, symtab_undefine
, NULL
, ISC_FALSE
,
764 isccc_cc_cleansymtab(isccc_symtab_t
*symtab
, isccc_time_t now
)
766 isccc_symtab_foreach(symtab
, symtab_clean
, &now
);
770 has_whitespace(const char *str
)
776 while ((c
= *str
++) != '\0') {
777 if (c
== ' ' || c
== '\t' || c
== '\n')
784 isccc_cc_checkdup(isccc_symtab_t
*symtab
, isccc_sexpr_t
*message
,
789 char *_ser
, *_tim
, *tmp
;
793 isccc_symvalue_t value
;
794 isccc_sexpr_t
*_ctrl
;
796 _ctrl
= isccc_alist_lookup(message
, "_ctrl");
798 isccc_cc_lookupstring(_ctrl
, "_ser", &_ser
) != ISC_R_SUCCESS
||
799 isccc_cc_lookupstring(_ctrl
, "_tim", &_tim
) != ISC_R_SUCCESS
)
800 return (ISC_R_FAILURE
);
802 * _frm and _to are optional.
804 if (isccc_cc_lookupstring(_ctrl
, "_frm", &tmp
) != ISC_R_SUCCESS
)
808 if (isccc_cc_lookupstring(_ctrl
, "_to", &tmp
) != ISC_R_SUCCESS
)
813 * Ensure there is no newline in any of the strings. This is so
814 * we can write them to a file later.
816 if (has_whitespace(_frm
) || has_whitespace(_to
) ||
817 has_whitespace(_ser
) || has_whitespace(_tim
))
818 return (ISC_R_FAILURE
);
819 len
= strlen(_frm
) + strlen(_to
) + strlen(_ser
) + strlen(_tim
) + 4;
822 return (ISC_R_NOMEMORY
);
823 snprintf(key
, len
, "%s;%s;%s;%s", _frm
, _to
, _ser
, _tim
);
824 value
.as_uinteger
= now
;
825 result
= isccc_symtab_define(symtab
, key
, ISCCC_SYMTYPE_CCDUP
, value
,
826 isccc_symexists_reject
);
827 if (result
!= ISC_R_SUCCESS
) {
832 return (ISC_R_SUCCESS
);