Sync usage with man page.
[netbsd-mini2440.git] / external / bsd / bind / dist / lib / isccc / cc.c
blobeff830aa46ae8c36bb52b7aeafd51d06a79436aa
1 /* $NetBSD$ */
3 /*
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 */
36 /*! \file */
38 #include <config.h>
40 #include <stdio.h>
41 #include <string.h>
42 #include <errno.h>
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>
51 #include <isccc/cc.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>
58 #define MAX_TAGS 256
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
72 * 22 bytes.
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
82 static isc_result_t
83 table_towire(isccc_sexpr_t *alist, isccc_region_t *target);
85 static isc_result_t
86 list_towire(isccc_sexpr_t *alist, isccc_region_t *target);
88 static isc_result_t
89 value_towire(isccc_sexpr_t *elt, isccc_region_t *target)
91 size_t len;
92 unsigned char *lenp;
93 isccc_region_t *vr;
94 isc_result_t result;
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);
116 * Emit the table.
118 result = table_towire(elt, target);
119 if (result != ISC_R_SUCCESS)
120 return (result);
121 len = (size_t)(target->rstart - lenp);
123 * 'len' is 4 bytes too big, since it counts
124 * the placeholder length too. Adjust and
125 * emit.
127 INSIST(len >= 4U);
128 len -= 4;
129 PUT32(len, lenp);
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);
140 * Emit the list.
142 result = list_towire(elt, target);
143 if (result != ISC_R_SUCCESS)
144 return (result);
145 len = (size_t)(target->rstart - lenp);
147 * 'len' is 4 bytes too big, since it counts
148 * the placeholder length. Adjust and emit.
150 INSIST(len >= 4U);
151 len -= 4;
152 PUT32(len, lenp);
155 return (ISC_R_SUCCESS);
158 static isc_result_t
159 table_towire(isccc_sexpr_t *alist, isccc_region_t *target)
161 isccc_sexpr_t *kv, *elt, *k, *v;
162 char *ks;
163 isc_result_t result;
164 size_t len;
166 for (elt = isccc_alist_first(alist);
167 elt != NULL;
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);
173 len = strlen(ks);
174 INSIST(len <= 255U);
176 * Emit the key name.
178 if (REGION_SIZE(*target) < 1 + len)
179 return (ISC_R_NOSPACE);
180 PUT8(len, target->rstart);
181 PUT_MEM(ks, len, target->rstart);
183 * Emit the value.
185 result = value_towire(v, target);
186 if (result != ISC_R_SUCCESS)
187 return (result);
190 return (ISC_R_SUCCESS);
193 static isc_result_t
194 list_towire(isccc_sexpr_t *list, isccc_region_t *target)
196 isc_result_t result;
198 while (list != NULL) {
199 result = value_towire(ISCCC_SEXPR_CAR(list), target);
200 if (result != ISC_R_SUCCESS)
201 return (result);
202 list = ISCCC_SEXPR_CDR(list);
205 return (ISC_R_SUCCESS);
208 static isc_result_t
209 sign(unsigned char *data, unsigned int length, unsigned char *hmd5,
210 isccc_region_t *secret)
212 isc_hmacmd5_t ctx;
213 isc_result_t result;
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)
227 return (result);
228 PUT_MEM(digestb64, HMD5_LENGTH, hmd5);
230 return (ISC_R_SUCCESS);
233 isc_result_t
234 isccc_cc_towire(isccc_sexpr_t *alist, isccc_region_t *target,
235 isccc_region_t *secret)
237 unsigned char *hmd5_rstart, *signed_rstart;
238 isc_result_t result;
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);
254 } else
255 hmd5_rstart = NULL;
256 signed_rstart = target->rstart;
258 * Delete any existing _auth section so that we don't try
259 * to encode it.
261 isccc_alist_delete(alist, "_auth");
263 * Emit the message.
265 result = table_towire(alist, target);
266 if (result != ISC_R_SUCCESS)
267 return (result);
268 if (secret != NULL)
269 return (sign(signed_rstart, (target->rstart - signed_rstart),
270 hmd5_rstart, secret));
271 return (ISC_R_SUCCESS);
274 static isc_result_t
275 verify(isccc_sexpr_t *alist, unsigned char *data, unsigned int length,
276 isccc_region_t *secret)
278 isc_hmacmd5_t ctx;
279 isccc_region_t source;
280 isccc_region_t target;
281 isc_result_t result;
282 isccc_sexpr_t *_auth, *hmd5;
283 unsigned char digest[ISC_MD5_DIGESTLENGTH];
284 unsigned char digestb64[ISC_MD5_DIGESTLENGTH * 4];
287 * Extract digest.
289 _auth = isccc_alist_lookup(alist, "_auth");
290 if (_auth == NULL)
291 return (ISC_R_FAILURE);
292 hmd5 = isccc_alist_lookup(_auth, "hmd5");
293 if (hmd5 == NULL)
294 return (ISC_R_FAILURE);
296 * Compute digest.
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)
307 return (result);
309 * Strip trailing == and NUL terminate target.
311 target.rstart -= 2;
312 *target.rstart++ = '\0';
314 * Verify.
316 if (strcmp((char *)digestb64, isccc_sexpr_tostring(hmd5)) != 0)
317 return (ISCCC_R_BADAUTH);
319 return (ISC_R_SUCCESS);
322 static isc_result_t
323 table_fromwire(isccc_region_t *source, isccc_region_t *secret,
324 isccc_sexpr_t **alistp);
326 static isc_result_t
327 list_fromwire(isccc_region_t *source, isccc_sexpr_t **listp);
329 static isc_result_t
330 value_fromwire(isccc_region_t *source, isccc_sexpr_t **valuep)
332 unsigned int msgtype;
333 isc_uint32_t len;
334 isccc_sexpr_t *value;
335 isccc_region_t active;
336 isc_result_t result;
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);
349 if (value != NULL) {
350 *valuep = value;
351 result = ISC_R_SUCCESS;
352 } else
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);
358 else
359 result = ISCCC_R_SYNTAX;
361 return (result);
364 static isc_result_t
365 table_fromwire(isccc_region_t *source, isccc_region_t *secret,
366 isccc_sexpr_t **alistp)
368 char key[256];
369 isc_uint32_t len;
370 isc_result_t result;
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();
380 if (alist == NULL)
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;
387 goto bad;
389 GET_MEM(key, len, source->rstart);
390 key[len] = '\0'; /* Ensure NUL termination. */
391 value = NULL;
392 result = value_fromwire(source, &value);
393 if (result != ISC_R_SUCCESS)
394 goto bad;
395 if (isccc_alist_define(alist, key, value) == NULL) {
396 result = ISC_R_NOMEMORY;
397 goto bad;
399 if (first_tag && secret != NULL && strcmp(key, "_auth") == 0)
400 checksum_rstart = source->rstart;
401 first_tag = ISC_FALSE;
404 *alistp = alist;
406 if (secret != NULL) {
407 if (checksum_rstart != NULL)
408 return (verify(alist, checksum_rstart,
409 (source->rend - checksum_rstart),
410 secret));
411 return (ISCCC_R_BADAUTH);
414 return (ISC_R_SUCCESS);
416 bad:
417 isccc_sexpr_free(&alist);
419 return (result);
422 static isc_result_t
423 list_fromwire(isccc_region_t *source, isccc_sexpr_t **listp)
425 isccc_sexpr_t *list, *value;
426 isc_result_t result;
428 list = NULL;
429 while (!REGION_EMPTY(*source)) {
430 value = NULL;
431 result = value_fromwire(source, &value);
432 if (result != ISC_R_SUCCESS) {
433 isccc_sexpr_free(&list);
434 return (result);
436 if (isccc_sexpr_addtolist(&list, value) == NULL) {
437 isccc_sexpr_free(&value);
438 isccc_sexpr_free(&list);
439 return (result);
443 *listp = list;
445 return (ISC_R_SUCCESS);
448 isc_result_t
449 isccc_cc_fromwire(isccc_region_t *source, isccc_sexpr_t **alistp,
450 isccc_region_t *secret)
452 unsigned int size;
453 isc_uint32_t version;
455 size = REGION_SIZE(*source);
456 if (size < 4)
457 return (ISC_R_UNEXPECTEDEND);
458 GET32(version, source->rstart);
459 if (version != 1)
460 return (ISCCC_R_UNKNOWNVERSION);
462 return (table_fromwire(source, secret, alistp));
465 static isc_result_t
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;
472 isc_result_t result;
474 REQUIRE(alistp != NULL && *alistp == NULL);
476 if (version != 1)
477 return (ISCCC_R_UNKNOWNVERSION);
479 alist = isccc_alist_create();
480 if (alist == NULL)
481 return (ISC_R_NOMEMORY);
483 result = ISC_R_NOMEMORY;
485 _ctrl = isccc_alist_create();
486 if (_ctrl == NULL)
487 goto bad;
488 if (isccc_alist_define(alist, "_ctrl", _ctrl) == NULL) {
489 isccc_sexpr_free(&_ctrl);
490 goto bad;
493 _data = isccc_alist_create();
494 if (_data == NULL)
495 goto bad;
496 if (isccc_alist_define(alist, "_data", _data) == NULL) {
497 isccc_sexpr_free(&_data);
498 goto bad;
501 if (isccc_cc_defineuint32(_ctrl, "_ser", serial) == NULL ||
502 isccc_cc_defineuint32(_ctrl, "_tim", now) == NULL ||
503 (want_expires &&
504 isccc_cc_defineuint32(_ctrl, "_exp", expires) == NULL))
505 goto bad;
506 if (from != NULL &&
507 isccc_cc_definestring(_ctrl, "_frm", from) == NULL)
508 goto bad;
509 if (to != NULL &&
510 isccc_cc_definestring(_ctrl, "_to", to) == NULL)
511 goto bad;
513 *alistp = alist;
515 return (ISC_R_SUCCESS);
517 bad:
518 isccc_sexpr_free(&alist);
520 return (result);
523 isc_result_t
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,
529 alistp, ISC_TRUE));
532 isc_result_t
533 isccc_cc_createack(isccc_sexpr_t *message, isc_boolean_t ok,
534 isccc_sexpr_t **ackp)
536 char *_frm, *_to;
537 isc_uint32_t serial;
538 isccc_sexpr_t *ack, *_ctrl;
539 isc_result_t result;
540 isccc_time_t t;
542 REQUIRE(ackp != NULL && *ackp == NULL);
544 _ctrl = isccc_alist_lookup(message, "_ctrl");
545 if (_ctrl == NULL ||
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.
552 _frm = NULL;
553 (void)isccc_cc_lookupstring(_ctrl, "_frm", &_frm);
554 _to = NULL;
555 (void)isccc_cc_lookupstring(_ctrl, "_to", &_to);
557 * Create the ack.
559 ack = NULL;
560 result = createmessage(1, _to, _frm, serial, t, 0, &ack, ISC_FALSE);
561 if (result != ISC_R_SUCCESS)
562 return (result);
564 _ctrl = isccc_alist_lookup(ack, "_ctrl");
565 if (_ctrl == NULL)
566 return (ISC_R_FAILURE);
567 if (isccc_cc_definestring(ack, "_ack", (ok) ? "1" : "0") == NULL) {
568 result = ISC_R_NOMEMORY;
569 goto bad;
572 *ackp = ack;
574 return (ISC_R_SUCCESS);
576 bad:
577 isccc_sexpr_free(&ack);
579 return (result);
582 isc_boolean_t
583 isccc_cc_isack(isccc_sexpr_t *message)
585 isccc_sexpr_t *_ctrl;
587 _ctrl = isccc_alist_lookup(message, "_ctrl");
588 if (_ctrl == NULL)
589 return (ISC_FALSE);
590 if (isccc_cc_lookupstring(_ctrl, "_ack", NULL) == ISC_R_SUCCESS)
591 return (ISC_TRUE);
592 return (ISC_FALSE);
595 isc_boolean_t
596 isccc_cc_isreply(isccc_sexpr_t *message)
598 isccc_sexpr_t *_ctrl;
600 _ctrl = isccc_alist_lookup(message, "_ctrl");
601 if (_ctrl == NULL)
602 return (ISC_FALSE);
603 if (isccc_cc_lookupstring(_ctrl, "_rpl", NULL) == ISC_R_SUCCESS)
604 return (ISC_TRUE);
605 return (ISC_FALSE);
608 isc_result_t
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;
613 isc_uint32_t serial;
614 isccc_sexpr_t *alist, *_ctrl, *_data;
615 isc_result_t result;
617 REQUIRE(alistp != NULL && *alistp == NULL);
619 _ctrl = isccc_alist_lookup(message, "_ctrl");
620 _data = isccc_alist_lookup(message, "_data");
621 if (_ctrl == NULL ||
622 _data == NULL ||
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.
629 _frm = NULL;
630 (void)isccc_cc_lookupstring(_ctrl, "_frm", &_frm);
631 _to = NULL;
632 (void)isccc_cc_lookupstring(_ctrl, "_to", &_to);
634 * Create the response.
636 alist = NULL;
637 result = isccc_cc_createmessage(1, _to, _frm, serial, now, expires,
638 &alist);
639 if (result != ISC_R_SUCCESS)
640 return (result);
641 _ctrl = isccc_alist_lookup(alist, "_ctrl");
642 if (_ctrl == NULL)
643 return (ISC_R_FAILURE);
644 _data = isccc_alist_lookup(alist, "_data");
645 if (_data == NULL)
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);
653 *alistp = alist;
655 return (ISC_R_SUCCESS);
658 isccc_sexpr_t *
659 isccc_cc_definestring(isccc_sexpr_t *alist, const char *key, const char *str)
661 size_t len;
662 isccc_region_t r;
664 len = strlen(str);
665 DE_CONST(str, r.rstart);
666 r.rend = r.rstart + len;
668 return (isccc_alist_definebinary(alist, key, &r));
671 isccc_sexpr_t *
672 isccc_cc_defineuint32(isccc_sexpr_t *alist, const char *key, isc_uint32_t i)
674 char b[100];
675 size_t len;
676 isccc_region_t r;
678 snprintf(b, sizeof(b), "%u", i);
679 len = strlen(b);
680 r.rstart = (unsigned char *)b;
681 r.rend = (unsigned char *)b + len;
683 return (isccc_alist_definebinary(alist, key, &r));
686 isc_result_t
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);
692 if (kv != NULL) {
693 v = ISCCC_SEXPR_CDR(kv);
694 if (isccc_sexpr_binaryp(v)) {
695 if (strp != NULL)
696 *strp = isccc_sexpr_tostring(v);
697 return (ISC_R_SUCCESS);
698 } else
699 return (ISC_R_EXISTS);
702 return (ISC_R_NOTFOUND);
705 isc_result_t
706 isccc_cc_lookupuint32(isccc_sexpr_t *alist, const char *key,
707 isc_uint32_t *uintp)
709 isccc_sexpr_t *kv, *v;
711 kv = isccc_alist_assq(alist, key);
712 if (kv != NULL) {
713 v = ISCCC_SEXPR_CDR(kv);
714 if (isccc_sexpr_binaryp(v)) {
715 if (uintp != NULL)
716 *uintp = (isc_uint32_t)
717 strtoul(isccc_sexpr_tostring(v),
718 NULL, 10);
719 return (ISC_R_SUCCESS);
720 } else
721 return (ISC_R_EXISTS);
724 return (ISC_R_NOTFOUND);
727 static void
728 symtab_undefine(char *key, unsigned int type, isccc_symvalue_t value,
729 void *arg)
731 UNUSED(type);
732 UNUSED(value);
733 UNUSED(arg);
735 free(key);
738 static isc_boolean_t
739 symtab_clean(char *key, unsigned int type, isccc_symvalue_t value,
740 void *arg)
742 isccc_time_t *now;
744 UNUSED(key);
745 UNUSED(type);
747 now = arg;
749 if (*now < value.as_uinteger)
750 return (ISC_FALSE);
751 if ((*now - value.as_uinteger) < DUP_LIFETIME)
752 return (ISC_FALSE);
753 return (ISC_TRUE);
756 isc_result_t
757 isccc_cc_createsymtab(isccc_symtab_t **symtabp)
759 return (isccc_symtab_create(11897, symtab_undefine, NULL, ISC_FALSE,
760 symtabp));
763 void
764 isccc_cc_cleansymtab(isccc_symtab_t *symtab, isccc_time_t now)
766 isccc_symtab_foreach(symtab, symtab_clean, &now);
769 static isc_boolean_t
770 has_whitespace(const char *str)
772 char c;
774 if (str == NULL)
775 return (ISC_FALSE);
776 while ((c = *str++) != '\0') {
777 if (c == ' ' || c == '\t' || c == '\n')
778 return (ISC_TRUE);
780 return (ISC_FALSE);
783 isc_result_t
784 isccc_cc_checkdup(isccc_symtab_t *symtab, isccc_sexpr_t *message,
785 isccc_time_t now)
787 const char *_frm;
788 const char *_to;
789 char *_ser, *_tim, *tmp;
790 isc_result_t result;
791 char *key;
792 size_t len;
793 isccc_symvalue_t value;
794 isccc_sexpr_t *_ctrl;
796 _ctrl = isccc_alist_lookup(message, "_ctrl");
797 if (_ctrl == NULL ||
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)
805 _frm = "";
806 else
807 _frm = tmp;
808 if (isccc_cc_lookupstring(_ctrl, "_to", &tmp) != ISC_R_SUCCESS)
809 _to = "";
810 else
811 _to = tmp;
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;
820 key = malloc(len);
821 if (key == NULL)
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) {
828 free(key);
829 return (result);
832 return (ISC_R_SUCCESS);