2 * Portions Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
3 * Portions Copyright (C) 2001-2003 Internet Software Consortium.
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.
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.
17 * Portions Copyright (C) 2001 Nominum, Inc.
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.
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.
32 /* $Id: cc.c,v 1.18 2007/08/28 07:20:43 tbox Exp $ */
42 #include <isc/assertions.h>
43 #include <isc/hmacmd5.h>
44 #include <isc/print.h>
45 #include <isc/stdlib.h>
47 #include <isccc/alist.h>
48 #include <isccc/base64.h>
50 #include <isccc/result.h>
51 #include <isccc/sexpr.h>
52 #include <isccc/symtab.h>
53 #include <isccc/symtype.h>
54 #include <isccc/util.h>
57 #define DUP_LIFETIME 900
59 typedef isccc_sexpr_t
*sexpr_ptr
;
61 static unsigned char auth_hmd5
[] = {
62 0x05, 0x5f, 0x61, 0x75, 0x74, 0x68, /*%< len + _auth */
63 ISCCC_CCMSGTYPE_TABLE
, /*%< message type */
64 0x00, 0x00, 0x00, 0x20, /*%< length == 32 */
65 0x04, 0x68, 0x6d, 0x64, 0x35, /*%< len + hmd5 */
66 ISCCC_CCMSGTYPE_BINARYDATA
, /*%< message type */
67 0x00, 0x00, 0x00, 0x16, /*%< length == 22 */
69 * The base64 encoding of one of our HMAC-MD5 signatures is
72 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
73 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
74 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
77 #define HMD5_OFFSET 21 /*%< 21 = 6 + 1 + 4 + 5 + 1 + 4 */
78 #define HMD5_LENGTH 22
81 table_towire(isccc_sexpr_t
*alist
, isccc_region_t
*target
);
84 list_towire(isccc_sexpr_t
*alist
, isccc_region_t
*target
);
87 value_towire(isccc_sexpr_t
*elt
, isccc_region_t
*target
)
94 if (isccc_sexpr_binaryp(elt
)) {
95 vr
= isccc_sexpr_tobinary(elt
);
96 len
= REGION_SIZE(*vr
);
97 if (REGION_SIZE(*target
) < 1 + 4 + len
)
98 return (ISC_R_NOSPACE
);
99 PUT8(ISCCC_CCMSGTYPE_BINARYDATA
, target
->rstart
);
100 PUT32(len
, target
->rstart
);
101 if (REGION_SIZE(*target
) < len
)
102 return (ISC_R_NOSPACE
);
103 PUT_MEM(vr
->rstart
, len
, target
->rstart
);
104 } else if (isccc_alist_alistp(elt
)) {
105 if (REGION_SIZE(*target
) < 1 + 4)
106 return (ISC_R_NOSPACE
);
107 PUT8(ISCCC_CCMSGTYPE_TABLE
, target
->rstart
);
109 * Emit a placeholder length.
111 lenp
= target
->rstart
;
112 PUT32(0, target
->rstart
);
116 result
= table_towire(elt
, target
);
117 if (result
!= ISC_R_SUCCESS
)
119 len
= (size_t)(target
->rstart
- lenp
);
121 * 'len' is 4 bytes too big, since it counts
122 * the placeholder length too. Adjust and
128 } else if (isccc_sexpr_listp(elt
)) {
129 if (REGION_SIZE(*target
) < 1 + 4)
130 return (ISC_R_NOSPACE
);
131 PUT8(ISCCC_CCMSGTYPE_LIST
, target
->rstart
);
133 * Emit a placeholder length and count.
135 lenp
= target
->rstart
;
136 PUT32(0, target
->rstart
);
140 result
= list_towire(elt
, target
);
141 if (result
!= ISC_R_SUCCESS
)
143 len
= (size_t)(target
->rstart
- lenp
);
145 * 'len' is 4 bytes too big, since it counts
146 * the placeholder length. Adjust and emit.
153 return (ISC_R_SUCCESS
);
157 table_towire(isccc_sexpr_t
*alist
, isccc_region_t
*target
)
159 isccc_sexpr_t
*kv
, *elt
, *k
, *v
;
164 for (elt
= isccc_alist_first(alist
);
166 elt
= ISCCC_SEXPR_CDR(elt
)) {
167 kv
= ISCCC_SEXPR_CAR(elt
);
168 k
= ISCCC_SEXPR_CAR(kv
);
169 ks
= isccc_sexpr_tostring(k
);
170 v
= ISCCC_SEXPR_CDR(kv
);
176 if (REGION_SIZE(*target
) < 1 + len
)
177 return (ISC_R_NOSPACE
);
178 PUT8(len
, target
->rstart
);
179 PUT_MEM(ks
, len
, target
->rstart
);
183 result
= value_towire(v
, target
);
184 if (result
!= ISC_R_SUCCESS
)
188 return (ISC_R_SUCCESS
);
192 list_towire(isccc_sexpr_t
*list
, isccc_region_t
*target
)
196 while (list
!= NULL
) {
197 result
= value_towire(ISCCC_SEXPR_CAR(list
), target
);
198 if (result
!= ISC_R_SUCCESS
)
200 list
= ISCCC_SEXPR_CDR(list
);
203 return (ISC_R_SUCCESS
);
207 sign(unsigned char *data
, unsigned int length
, unsigned char *hmd5
,
208 isccc_region_t
*secret
)
212 isccc_region_t source
, target
;
213 unsigned char digest
[ISC_MD5_DIGESTLENGTH
];
214 unsigned char digestb64
[ISC_MD5_DIGESTLENGTH
* 4];
216 isc_hmacmd5_init(&ctx
, secret
->rstart
, REGION_SIZE(*secret
));
217 isc_hmacmd5_update(&ctx
, data
, length
);
218 isc_hmacmd5_sign(&ctx
, digest
);
219 source
.rstart
= digest
;
220 source
.rend
= digest
+ ISC_MD5_DIGESTLENGTH
;
221 target
.rstart
= digestb64
;
222 target
.rend
= digestb64
+ ISC_MD5_DIGESTLENGTH
* 4;
223 result
= isccc_base64_encode(&source
, 64, "", &target
);
224 if (result
!= ISC_R_SUCCESS
)
226 PUT_MEM(digestb64
, HMD5_LENGTH
, hmd5
);
228 return (ISC_R_SUCCESS
);
232 isccc_cc_towire(isccc_sexpr_t
*alist
, isccc_region_t
*target
,
233 isccc_region_t
*secret
)
235 unsigned char *hmd5_rstart
, *signed_rstart
;
238 if (REGION_SIZE(*target
) < 4 + sizeof(auth_hmd5
))
239 return (ISC_R_NOSPACE
);
241 * Emit protocol version.
243 PUT32(1, target
->rstart
);
244 if (secret
!= NULL
) {
246 * Emit _auth section with zeroed HMAC-MD5 signature.
247 * We'll replace the zeros with the real signature once
248 * we know what it is.
250 hmd5_rstart
= target
->rstart
+ HMD5_OFFSET
;
251 PUT_MEM(auth_hmd5
, sizeof(auth_hmd5
), target
->rstart
);
254 signed_rstart
= target
->rstart
;
256 * Delete any existing _auth section so that we don't try
259 isccc_alist_delete(alist
, "_auth");
263 result
= table_towire(alist
, target
);
264 if (result
!= ISC_R_SUCCESS
)
267 return (sign(signed_rstart
, (target
->rstart
- signed_rstart
),
268 hmd5_rstart
, secret
));
269 return (ISC_R_SUCCESS
);
273 verify(isccc_sexpr_t
*alist
, unsigned char *data
, unsigned int length
,
274 isccc_region_t
*secret
)
277 isccc_region_t source
;
278 isccc_region_t target
;
280 isccc_sexpr_t
*_auth
, *hmd5
;
281 unsigned char digest
[ISC_MD5_DIGESTLENGTH
];
282 unsigned char digestb64
[ISC_MD5_DIGESTLENGTH
* 4];
287 _auth
= isccc_alist_lookup(alist
, "_auth");
289 return (ISC_R_FAILURE
);
290 hmd5
= isccc_alist_lookup(_auth
, "hmd5");
292 return (ISC_R_FAILURE
);
296 isc_hmacmd5_init(&ctx
, secret
->rstart
, REGION_SIZE(*secret
));
297 isc_hmacmd5_update(&ctx
, data
, length
);
298 isc_hmacmd5_sign(&ctx
, digest
);
299 source
.rstart
= digest
;
300 source
.rend
= digest
+ ISC_MD5_DIGESTLENGTH
;
301 target
.rstart
= digestb64
;
302 target
.rend
= digestb64
+ ISC_MD5_DIGESTLENGTH
* 4;
303 result
= isccc_base64_encode(&source
, 64, "", &target
);
304 if (result
!= ISC_R_SUCCESS
)
307 * Strip trailing == and NUL terminate target.
310 *target
.rstart
++ = '\0';
314 if (strcmp((char *)digestb64
, isccc_sexpr_tostring(hmd5
)) != 0)
315 return (ISCCC_R_BADAUTH
);
317 return (ISC_R_SUCCESS
);
321 table_fromwire(isccc_region_t
*source
, isccc_region_t
*secret
,
322 isccc_sexpr_t
**alistp
);
325 list_fromwire(isccc_region_t
*source
, isccc_sexpr_t
**listp
);
328 value_fromwire(isccc_region_t
*source
, isccc_sexpr_t
**valuep
)
330 unsigned int msgtype
;
332 isccc_sexpr_t
*value
;
333 isccc_region_t active
;
336 if (REGION_SIZE(*source
) < 1 + 4)
337 return (ISC_R_UNEXPECTEDEND
);
338 GET8(msgtype
, source
->rstart
);
339 GET32(len
, source
->rstart
);
340 if (REGION_SIZE(*source
) < len
)
341 return (ISC_R_UNEXPECTEDEND
);
342 active
.rstart
= source
->rstart
;
343 active
.rend
= active
.rstart
+ len
;
344 source
->rstart
= active
.rend
;
345 if (msgtype
== ISCCC_CCMSGTYPE_BINARYDATA
) {
346 value
= isccc_sexpr_frombinary(&active
);
349 result
= ISC_R_SUCCESS
;
351 result
= ISC_R_NOMEMORY
;
352 } else if (msgtype
== ISCCC_CCMSGTYPE_TABLE
)
353 result
= table_fromwire(&active
, NULL
, valuep
);
354 else if (msgtype
== ISCCC_CCMSGTYPE_LIST
)
355 result
= list_fromwire(&active
, valuep
);
357 result
= ISCCC_R_SYNTAX
;
363 table_fromwire(isccc_region_t
*source
, isccc_region_t
*secret
,
364 isccc_sexpr_t
**alistp
)
369 isccc_sexpr_t
*alist
, *value
;
370 isc_boolean_t first_tag
;
371 unsigned char *checksum_rstart
;
373 REQUIRE(alistp
!= NULL
&& *alistp
== NULL
);
375 checksum_rstart
= NULL
;
376 first_tag
= ISC_TRUE
;
377 alist
= isccc_alist_create();
379 return (ISC_R_NOMEMORY
);
381 while (!REGION_EMPTY(*source
)) {
382 GET8(len
, source
->rstart
);
383 if (REGION_SIZE(*source
) < len
) {
384 result
= ISC_R_UNEXPECTEDEND
;
387 GET_MEM(key
, len
, source
->rstart
);
388 key
[len
] = '\0'; /* Ensure NUL termination. */
390 result
= value_fromwire(source
, &value
);
391 if (result
!= ISC_R_SUCCESS
)
393 if (isccc_alist_define(alist
, key
, value
) == NULL
) {
394 result
= ISC_R_NOMEMORY
;
397 if (first_tag
&& secret
!= NULL
&& strcmp(key
, "_auth") == 0)
398 checksum_rstart
= source
->rstart
;
399 first_tag
= ISC_FALSE
;
404 if (secret
!= NULL
) {
405 if (checksum_rstart
!= NULL
)
406 return (verify(alist
, checksum_rstart
,
407 (source
->rend
- checksum_rstart
),
409 return (ISCCC_R_BADAUTH
);
412 return (ISC_R_SUCCESS
);
415 isccc_sexpr_free(&alist
);
421 list_fromwire(isccc_region_t
*source
, isccc_sexpr_t
**listp
)
423 isccc_sexpr_t
*list
, *value
;
427 while (!REGION_EMPTY(*source
)) {
429 result
= value_fromwire(source
, &value
);
430 if (result
!= ISC_R_SUCCESS
) {
431 isccc_sexpr_free(&list
);
434 if (isccc_sexpr_addtolist(&list
, value
) == NULL
) {
435 isccc_sexpr_free(&value
);
436 isccc_sexpr_free(&list
);
443 return (ISC_R_SUCCESS
);
447 isccc_cc_fromwire(isccc_region_t
*source
, isccc_sexpr_t
**alistp
,
448 isccc_region_t
*secret
)
451 isc_uint32_t version
;
453 size
= REGION_SIZE(*source
);
455 return (ISC_R_UNEXPECTEDEND
);
456 GET32(version
, source
->rstart
);
458 return (ISCCC_R_UNKNOWNVERSION
);
460 return (table_fromwire(source
, secret
, alistp
));
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
)
469 isccc_sexpr_t
*alist
, *_ctrl
, *_data
;
472 REQUIRE(alistp
!= NULL
&& *alistp
== NULL
);
475 return (ISCCC_R_UNKNOWNVERSION
);
477 alist
= isccc_alist_create();
479 return (ISC_R_NOMEMORY
);
481 result
= ISC_R_NOMEMORY
;
483 _ctrl
= isccc_alist_create();
486 if (isccc_alist_define(alist
, "_ctrl", _ctrl
) == NULL
) {
487 isccc_sexpr_free(&_ctrl
);
491 _data
= isccc_alist_create();
494 if (isccc_alist_define(alist
, "_data", _data
) == NULL
) {
495 isccc_sexpr_free(&_data
);
499 if (isccc_cc_defineuint32(_ctrl
, "_ser", serial
) == NULL
||
500 isccc_cc_defineuint32(_ctrl
, "_tim", now
) == NULL
||
502 isccc_cc_defineuint32(_ctrl
, "_exp", expires
) == NULL
))
505 isccc_cc_definestring(_ctrl
, "_frm", from
) == NULL
)
508 isccc_cc_definestring(_ctrl
, "_to", to
) == NULL
)
513 return (ISC_R_SUCCESS
);
516 isccc_sexpr_free(&alist
);
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
)
526 return (createmessage(version
, from
, to
, serial
, now
, expires
,
531 isccc_cc_createack(isccc_sexpr_t
*message
, isc_boolean_t ok
,
532 isccc_sexpr_t
**ackp
)
536 isccc_sexpr_t
*ack
, *_ctrl
;
540 REQUIRE(ackp
!= NULL
&& *ackp
== NULL
);
542 _ctrl
= isccc_alist_lookup(message
, "_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
);
548 * _frm and _to are optional.
551 (void)isccc_cc_lookupstring(_ctrl
, "_frm", &_frm
);
553 (void)isccc_cc_lookupstring(_ctrl
, "_to", &_to
);
558 result
= createmessage(1, _to
, _frm
, serial
, t
, 0, &ack
, ISC_FALSE
);
559 if (result
!= ISC_R_SUCCESS
)
562 _ctrl
= isccc_alist_lookup(ack
, "_ctrl");
564 return (ISC_R_FAILURE
);
565 if (isccc_cc_definestring(ack
, "_ack", (ok
) ? "1" : "0") == NULL
) {
566 result
= ISC_R_NOMEMORY
;
572 return (ISC_R_SUCCESS
);
575 isccc_sexpr_free(&ack
);
581 isccc_cc_isack(isccc_sexpr_t
*message
)
583 isccc_sexpr_t
*_ctrl
;
585 _ctrl
= isccc_alist_lookup(message
, "_ctrl");
588 if (isccc_cc_lookupstring(_ctrl
, "_ack", NULL
) == ISC_R_SUCCESS
)
594 isccc_cc_isreply(isccc_sexpr_t
*message
)
596 isccc_sexpr_t
*_ctrl
;
598 _ctrl
= isccc_alist_lookup(message
, "_ctrl");
601 if (isccc_cc_lookupstring(_ctrl
, "_rpl", NULL
) == ISC_R_SUCCESS
)
607 isccc_cc_createresponse(isccc_sexpr_t
*message
, isccc_time_t now
,
608 isccc_time_t expires
, isccc_sexpr_t
**alistp
)
610 char *_frm
, *_to
, *type
;
612 isccc_sexpr_t
*alist
, *_ctrl
, *_data
;
615 REQUIRE(alistp
!= NULL
&& *alistp
== NULL
);
617 _ctrl
= isccc_alist_lookup(message
, "_ctrl");
618 _data
= isccc_alist_lookup(message
, "_data");
621 isccc_cc_lookupuint32(_ctrl
, "_ser", &serial
) != ISC_R_SUCCESS
||
622 isccc_cc_lookupstring(_data
, "type", &type
) != ISC_R_SUCCESS
)
623 return (ISC_R_FAILURE
);
625 * _frm and _to are optional.
628 (void)isccc_cc_lookupstring(_ctrl
, "_frm", &_frm
);
630 (void)isccc_cc_lookupstring(_ctrl
, "_to", &_to
);
632 * Create the response.
635 result
= isccc_cc_createmessage(1, _to
, _frm
, serial
, now
, expires
,
637 if (result
!= ISC_R_SUCCESS
)
639 _ctrl
= isccc_alist_lookup(alist
, "_ctrl");
641 return (ISC_R_FAILURE
);
642 _data
= isccc_alist_lookup(alist
, "_data");
644 return (ISC_R_FAILURE
);
645 if (isccc_cc_definestring(_ctrl
, "_rpl", "1") == NULL
||
646 isccc_cc_definestring(_data
, "type", type
) == NULL
) {
647 isccc_sexpr_free(&alist
);
648 return (ISC_R_NOMEMORY
);
653 return (ISC_R_SUCCESS
);
657 isccc_cc_definestring(isccc_sexpr_t
*alist
, const char *key
, const char *str
)
663 DE_CONST(str
, r
.rstart
);
664 r
.rend
= r
.rstart
+ len
;
666 return (isccc_alist_definebinary(alist
, key
, &r
));
670 isccc_cc_defineuint32(isccc_sexpr_t
*alist
, const char *key
, isc_uint32_t i
)
676 snprintf(b
, sizeof(b
), "%u", i
);
678 r
.rstart
= (unsigned char *)b
;
679 r
.rend
= (unsigned char *)b
+ len
;
681 return (isccc_alist_definebinary(alist
, key
, &r
));
685 isccc_cc_lookupstring(isccc_sexpr_t
*alist
, const char *key
, char **strp
)
687 isccc_sexpr_t
*kv
, *v
;
689 kv
= isccc_alist_assq(alist
, key
);
691 v
= ISCCC_SEXPR_CDR(kv
);
692 if (isccc_sexpr_binaryp(v
)) {
694 *strp
= isccc_sexpr_tostring(v
);
695 return (ISC_R_SUCCESS
);
697 return (ISC_R_EXISTS
);
700 return (ISC_R_NOTFOUND
);
704 isccc_cc_lookupuint32(isccc_sexpr_t
*alist
, const char *key
,
707 isccc_sexpr_t
*kv
, *v
;
709 kv
= isccc_alist_assq(alist
, key
);
711 v
= ISCCC_SEXPR_CDR(kv
);
712 if (isccc_sexpr_binaryp(v
)) {
714 *uintp
= (isc_uint32_t
)
715 strtoul(isccc_sexpr_tostring(v
),
717 return (ISC_R_SUCCESS
);
719 return (ISC_R_EXISTS
);
722 return (ISC_R_NOTFOUND
);
726 symtab_undefine(char *key
, unsigned int type
, isccc_symvalue_t value
,
737 symtab_clean(char *key
, unsigned int type
, isccc_symvalue_t value
,
747 if (*now
< value
.as_uinteger
)
749 if ((*now
- value
.as_uinteger
) < DUP_LIFETIME
)
755 isccc_cc_createsymtab(isccc_symtab_t
**symtabp
)
757 return (isccc_symtab_create(11897, symtab_undefine
, NULL
, ISC_FALSE
,
762 isccc_cc_cleansymtab(isccc_symtab_t
*symtab
, isccc_time_t now
)
764 isccc_symtab_foreach(symtab
, symtab_clean
, &now
);
768 has_whitespace(const char *str
)
774 while ((c
= *str
++) != '\0') {
775 if (c
== ' ' || c
== '\t' || c
== '\n')
782 isccc_cc_checkdup(isccc_symtab_t
*symtab
, isccc_sexpr_t
*message
,
787 char *_ser
, *_tim
, *tmp
;
791 isccc_symvalue_t value
;
792 isccc_sexpr_t
*_ctrl
;
794 _ctrl
= isccc_alist_lookup(message
, "_ctrl");
796 isccc_cc_lookupstring(_ctrl
, "_ser", &_ser
) != ISC_R_SUCCESS
||
797 isccc_cc_lookupstring(_ctrl
, "_tim", &_tim
) != ISC_R_SUCCESS
)
798 return (ISC_R_FAILURE
);
800 * _frm and _to are optional.
802 if (isccc_cc_lookupstring(_ctrl
, "_frm", &tmp
) != ISC_R_SUCCESS
)
806 if (isccc_cc_lookupstring(_ctrl
, "_to", &tmp
) != ISC_R_SUCCESS
)
811 * Ensure there is no newline in any of the strings. This is so
812 * we can write them to a file later.
814 if (has_whitespace(_frm
) || has_whitespace(_to
) ||
815 has_whitespace(_ser
) || has_whitespace(_tim
))
816 return (ISC_R_FAILURE
);
817 len
= strlen(_frm
) + strlen(_to
) + strlen(_ser
) + strlen(_tim
) + 4;
820 return (ISC_R_NOMEMORY
);
821 snprintf(key
, len
, "%s;%s;%s;%s", _frm
, _to
, _ser
, _tim
);
822 value
.as_uinteger
= now
;
823 result
= isccc_symtab_define(symtab
, key
, ISCCC_SYMTYPE_CCDUP
, value
,
824 isccc_symexists_reject
);
825 if (result
!= ISC_R_SUCCESS
) {
830 return (ISC_R_SUCCESS
);