diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 07:24:22 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 07:24:22 +0000 |
commit | 45d6379135504814ab723b57f0eb8be23393a51d (patch) | |
tree | d4f2ec4acca824a8446387a758b0ce4238a4dffa /lib/isccc/cc.c | |
parent | Initial commit. (diff) | |
download | bind9-45d6379135504814ab723b57f0eb8be23393a51d.tar.xz bind9-45d6379135504814ab723b57f0eb8be23393a51d.zip |
Adding upstream version 1:9.16.44.upstream/1%9.16.44
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'lib/isccc/cc.c')
-rw-r--r-- | lib/isccc/cc.c | 1059 |
1 files changed, 1059 insertions, 0 deletions
diff --git a/lib/isccc/cc.c b/lib/isccc/cc.c new file mode 100644 index 0000000..5a716bd --- /dev/null +++ b/lib/isccc/cc.c @@ -0,0 +1,1059 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 AND ISC + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/* + * Copyright (C) 2001 Nominum, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NOMINUM DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/*! \file */ + +#include <errno.h> +#include <inttypes.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <isc/assertions.h> +#include <isc/hmac.h> +#include <isc/print.h> +#include <isc/safe.h> + +#include <pk11/site.h> + +#include <isccc/alist.h> +#include <isccc/base64.h> +#include <isccc/cc.h> +#include <isccc/result.h> +#include <isccc/sexpr.h> +#include <isccc/symtab.h> +#include <isccc/symtype.h> +#include <isccc/util.h> + +#define MAX_TAGS 256 +#define DUP_LIFETIME 900 +#ifndef ISCCC_MAXDEPTH +#define ISCCC_MAXDEPTH \ + 10 /* Big enough for rndc which just sends a string each way. */ +#endif + +typedef isccc_sexpr_t *sexpr_ptr; + +static unsigned char auth_hmd5[] = { + 0x05, 0x5f, 0x61, 0x75, 0x74, 0x68, /*%< len + _auth */ + ISCCC_CCMSGTYPE_TABLE, /*%< message type */ + 0x00, 0x00, 0x00, 0x20, /*%< length == 32 */ + 0x04, 0x68, 0x6d, 0x64, 0x35, /*%< len + hmd5 */ + ISCCC_CCMSGTYPE_BINARYDATA, /*%< message type */ + 0x00, 0x00, 0x00, 0x16, /*%< length == 22 */ + /* + * The base64 encoding of one of our HMAC-MD5 signatures is + * 22 bytes. + */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +#define HMD5_OFFSET 21 /*%< 21 = 6 + 1 + 4 + 5 + 1 + 4 */ +#define HMD5_LENGTH 22 + +static unsigned char auth_hsha[] = { + 0x05, 0x5f, 0x61, 0x75, 0x74, 0x68, /*%< len + _auth */ + ISCCC_CCMSGTYPE_TABLE, /*%< message type */ + 0x00, 0x00, 0x00, 0x63, /*%< length == 99 */ + 0x04, 0x68, 0x73, 0x68, 0x61, /*%< len + hsha */ + ISCCC_CCMSGTYPE_BINARYDATA, /*%< message type */ + 0x00, 0x00, 0x00, 0x59, /*%< length == 89 */ + 0x00, /*%< algorithm */ + /* + * The base64 encoding of one of our HMAC-SHA* signatures is + * 88 bytes. + */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 +}; + +#define HSHA_OFFSET 22 /*%< 21 = 6 + 1 + 4 + 5 + 1 + 4 + 1 */ +#define HSHA_LENGTH 88 + +static isc_result_t +table_towire(isccc_sexpr_t *alist, isc_buffer_t **buffer); + +static isc_result_t +list_towire(isccc_sexpr_t *alist, isc_buffer_t **buffer); + +static isc_result_t +value_towire(isccc_sexpr_t *elt, isc_buffer_t **buffer) { + unsigned int len; + isccc_region_t *vr; + isc_result_t result; + + if (isccc_sexpr_binaryp(elt)) { + vr = isccc_sexpr_tobinary(elt); + len = REGION_SIZE(*vr); + result = isc_buffer_reserve(buffer, 1 + 4); + if (result != ISC_R_SUCCESS) { + return (ISC_R_NOSPACE); + } + isc_buffer_putuint8(*buffer, ISCCC_CCMSGTYPE_BINARYDATA); + isc_buffer_putuint32(*buffer, len); + + result = isc_buffer_reserve(buffer, len); + if (result != ISC_R_SUCCESS) { + return (ISC_R_NOSPACE); + } + isc_buffer_putmem(*buffer, vr->rstart, len); + } else if (isccc_alist_alistp(elt)) { + unsigned int used; + isc_buffer_t b; + + result = isc_buffer_reserve(buffer, 1 + 4); + if (result != ISC_R_SUCCESS) { + return (ISC_R_NOSPACE); + } + isc_buffer_putuint8(*buffer, ISCCC_CCMSGTYPE_TABLE); + /* + * Emit a placeholder length. + */ + used = (*buffer)->used; + isc_buffer_putuint32(*buffer, 0); + + /* + * Emit the table. + */ + result = table_towire(elt, buffer); + if (result != ISC_R_SUCCESS) { + return (result); + } + + len = (*buffer)->used - used; + /* + * 'len' is 4 bytes too big, since it counts + * the placeholder length too. Adjust and + * emit. + */ + INSIST(len >= 4U); + len -= 4; + + isc_buffer_init(&b, (unsigned char *)(*buffer)->base + used, 4); + isc_buffer_putuint32(&b, len); + } else if (isccc_sexpr_listp(elt)) { + unsigned int used; + isc_buffer_t b; + + result = isc_buffer_reserve(buffer, 1 + 4); + if (result != ISC_R_SUCCESS) { + return (ISC_R_NOSPACE); + } + isc_buffer_putuint8(*buffer, ISCCC_CCMSGTYPE_LIST); + /* + * Emit a placeholder length. + */ + used = (*buffer)->used; + isc_buffer_putuint32(*buffer, 0); + + /* + * Emit the list. + */ + result = list_towire(elt, buffer); + if (result != ISC_R_SUCCESS) { + return (result); + } + + len = (*buffer)->used - used; + /* + * 'len' is 4 bytes too big, since it counts + * the placeholder length too. Adjust and + * emit. + */ + INSIST(len >= 4U); + len -= 4; + + isc_buffer_init(&b, (unsigned char *)(*buffer)->base + used, 4); + isc_buffer_putuint32(&b, len); + } + + return (ISC_R_SUCCESS); +} + +static isc_result_t +table_towire(isccc_sexpr_t *alist, isc_buffer_t **buffer) { + isccc_sexpr_t *kv, *elt, *k, *v; + char *ks; + isc_result_t result; + unsigned int len; + + for (elt = isccc_alist_first(alist); elt != NULL; + elt = ISCCC_SEXPR_CDR(elt)) + { + kv = ISCCC_SEXPR_CAR(elt); + k = ISCCC_SEXPR_CAR(kv); + ks = isccc_sexpr_tostring(k); + v = ISCCC_SEXPR_CDR(kv); + len = (unsigned int)strlen(ks); + INSIST(len <= 255U); + /* + * Emit the key name. + */ + result = isc_buffer_reserve(buffer, 1 + len); + if (result != ISC_R_SUCCESS) { + return (ISC_R_NOSPACE); + } + isc_buffer_putuint8(*buffer, (uint8_t)len); + isc_buffer_putmem(*buffer, (const unsigned char *)ks, len); + /* + * Emit the value. + */ + result = value_towire(v, buffer); + if (result != ISC_R_SUCCESS) { + return (result); + } + } + + return (ISC_R_SUCCESS); +} + +static isc_result_t +list_towire(isccc_sexpr_t *list, isc_buffer_t **buffer) { + isc_result_t result; + + while (list != NULL) { + result = value_towire(ISCCC_SEXPR_CAR(list), buffer); + if (result != ISC_R_SUCCESS) { + return (result); + } + list = ISCCC_SEXPR_CDR(list); + } + + return (ISC_R_SUCCESS); +} + +static isc_result_t +sign(unsigned char *data, unsigned int length, unsigned char *hmac, + uint32_t algorithm, isccc_region_t *secret) { + const isc_md_type_t *md_type; + isc_result_t result; + isccc_region_t source, target; + unsigned char digest[ISC_MAX_MD_SIZE]; + unsigned int digestlen; + unsigned char digestb64[HSHA_LENGTH + 4]; + + source.rstart = digest; + + switch (algorithm) { + case ISCCC_ALG_HMACMD5: + md_type = ISC_MD_MD5; + break; + case ISCCC_ALG_HMACSHA1: + md_type = ISC_MD_SHA1; + break; + case ISCCC_ALG_HMACSHA224: + md_type = ISC_MD_SHA224; + break; + case ISCCC_ALG_HMACSHA256: + md_type = ISC_MD_SHA256; + break; + case ISCCC_ALG_HMACSHA384: + md_type = ISC_MD_SHA384; + break; + case ISCCC_ALG_HMACSHA512: + md_type = ISC_MD_SHA512; + break; + default: + return (ISC_R_NOTIMPLEMENTED); + } + + result = isc_hmac(md_type, secret->rstart, REGION_SIZE(*secret), data, + length, digest, &digestlen); + if (result != ISC_R_SUCCESS) { + return (result); + } + source.rend = digest + digestlen; + + memset(digestb64, 0, sizeof(digestb64)); + target.rstart = digestb64; + target.rend = digestb64 + sizeof(digestb64); + result = isccc_base64_encode(&source, 64, "", &target); + if (result != ISC_R_SUCCESS) { + return (result); + } + if (algorithm == ISCCC_ALG_HMACMD5) { + PUT_MEM(digestb64, HMD5_LENGTH, hmac); + } else { + PUT_MEM(digestb64, HSHA_LENGTH, hmac); + } + return (ISC_R_SUCCESS); +} + +isc_result_t +isccc_cc_towire(isccc_sexpr_t *alist, isc_buffer_t **buffer, uint32_t algorithm, + isccc_region_t *secret) { + unsigned int hmac_base, signed_base; + isc_result_t result; + + result = isc_buffer_reserve(buffer, + 4 + ((algorithm == ISCCC_ALG_HMACMD5) + ? sizeof(auth_hmd5) + : sizeof(auth_hsha))); + if (result != ISC_R_SUCCESS) { + return (ISC_R_NOSPACE); + } + + /* + * Emit protocol version. + */ + isc_buffer_putuint32(*buffer, 1); + + if (secret != NULL) { + /* + * Emit _auth section with zeroed HMAC signature. + * We'll replace the zeros with the real signature once + * we know what it is. + */ + if (algorithm == ISCCC_ALG_HMACMD5) { + hmac_base = (*buffer)->used + HMD5_OFFSET; + isc_buffer_putmem(*buffer, auth_hmd5, + sizeof(auth_hmd5)); + } else { + unsigned char *hmac_alg; + + hmac_base = (*buffer)->used + HSHA_OFFSET; + hmac_alg = (unsigned char *)isc_buffer_used(*buffer) + + HSHA_OFFSET - 1; + isc_buffer_putmem(*buffer, auth_hsha, + sizeof(auth_hsha)); + *hmac_alg = algorithm; + } + } else { + hmac_base = 0; + } + signed_base = (*buffer)->used; + /* + * Delete any existing _auth section so that we don't try + * to encode it. + */ + isccc_alist_delete(alist, "_auth"); + /* + * Emit the message. + */ + result = table_towire(alist, buffer); + if (result != ISC_R_SUCCESS) { + return (result); + } + if (secret != NULL) { + return (sign((unsigned char *)(*buffer)->base + signed_base, + (*buffer)->used - signed_base, + (unsigned char *)(*buffer)->base + hmac_base, + algorithm, secret)); + } + return (ISC_R_SUCCESS); +} + +static isc_result_t +verify(isccc_sexpr_t *alist, unsigned char *data, unsigned int length, + uint32_t algorithm, isccc_region_t *secret) { + const isc_md_type_t *md_type; + isccc_region_t source; + isccc_region_t target; + isc_result_t result; + isccc_sexpr_t *_auth, *hmac; + unsigned char digest[ISC_MAX_MD_SIZE]; + unsigned int digestlen; + unsigned char digestb64[HSHA_LENGTH * 4]; + + /* + * Extract digest. + */ + _auth = isccc_alist_lookup(alist, "_auth"); + if (!isccc_alist_alistp(_auth)) { + return (ISC_R_FAILURE); + } + if (algorithm == ISCCC_ALG_HMACMD5) { + hmac = isccc_alist_lookup(_auth, "hmd5"); + } else { + hmac = isccc_alist_lookup(_auth, "hsha"); + } + if (!isccc_sexpr_binaryp(hmac)) { + return (ISC_R_FAILURE); + } + /* + * Compute digest. + */ + source.rstart = digest; + + switch (algorithm) { + case ISCCC_ALG_HMACMD5: + md_type = ISC_MD_MD5; + break; + case ISCCC_ALG_HMACSHA1: + md_type = ISC_MD_SHA1; + break; + case ISCCC_ALG_HMACSHA224: + md_type = ISC_MD_SHA224; + break; + case ISCCC_ALG_HMACSHA256: + md_type = ISC_MD_SHA256; + break; + case ISCCC_ALG_HMACSHA384: + md_type = ISC_MD_SHA384; + break; + case ISCCC_ALG_HMACSHA512: + md_type = ISC_MD_SHA512; + break; + default: + return (ISC_R_NOTIMPLEMENTED); + } + + result = isc_hmac(md_type, secret->rstart, REGION_SIZE(*secret), data, + length, digest, &digestlen); + if (result != ISC_R_SUCCESS) { + return (result); + } + source.rend = digest + digestlen; + + target.rstart = digestb64; + target.rend = digestb64 + sizeof(digestb64); + memset(digestb64, 0, sizeof(digestb64)); + result = isccc_base64_encode(&source, 64, "", &target); + if (result != ISC_R_SUCCESS) { + return (result); + } + + /* + * Verify. + */ + if (algorithm == ISCCC_ALG_HMACMD5) { + isccc_region_t *region; + unsigned char *value; + + region = isccc_sexpr_tobinary(hmac); + if ((region->rend - region->rstart) != HMD5_LENGTH) { + return (ISCCC_R_BADAUTH); + } + value = region->rstart; + if (!isc_safe_memequal(value, digestb64, HMD5_LENGTH)) { + return (ISCCC_R_BADAUTH); + } + } else { + isccc_region_t *region; + unsigned char *value; + uint32_t valalg; + + region = isccc_sexpr_tobinary(hmac); + + /* + * Note: with non-MD5 algorithms, there's an extra octet + * to identify which algorithm is in use. + */ + if ((region->rend - region->rstart) != HSHA_LENGTH + 1) { + return (ISCCC_R_BADAUTH); + } + value = region->rstart; + GET8(valalg, value); + if ((valalg != algorithm) || + !isc_safe_memequal(value, digestb64, HSHA_LENGTH)) + { + return (ISCCC_R_BADAUTH); + } + } + + return (ISC_R_SUCCESS); +} + +static isc_result_t +table_fromwire(isccc_region_t *source, isccc_region_t *secret, + uint32_t algorithm, unsigned int depth, isccc_sexpr_t **alistp); + +static isc_result_t +list_fromwire(isccc_region_t *source, unsigned int depth, + isccc_sexpr_t **listp); + +static isc_result_t +value_fromwire(isccc_region_t *source, unsigned int depth, + isccc_sexpr_t **valuep) { + unsigned int msgtype; + uint32_t len; + isccc_sexpr_t *value; + isccc_region_t active; + isc_result_t result; + + if (depth > ISCCC_MAXDEPTH) { + return (ISCCC_R_MAXDEPTH); + } + + if (REGION_SIZE(*source) < 1 + 4) { + return (ISC_R_UNEXPECTEDEND); + } + GET8(msgtype, source->rstart); + GET32(len, source->rstart); + if (REGION_SIZE(*source) < len) { + return (ISC_R_UNEXPECTEDEND); + } + active.rstart = source->rstart; + active.rend = active.rstart + len; + source->rstart = active.rend; + if (msgtype == ISCCC_CCMSGTYPE_BINARYDATA) { + value = isccc_sexpr_frombinary(&active); + if (value != NULL) { + *valuep = value; + result = ISC_R_SUCCESS; + } else { + result = ISC_R_NOMEMORY; + } + } else if (msgtype == ISCCC_CCMSGTYPE_TABLE) { + result = table_fromwire(&active, NULL, 0, depth + 1, valuep); + } else if (msgtype == ISCCC_CCMSGTYPE_LIST) { + result = list_fromwire(&active, depth + 1, valuep); + } else { + result = ISCCC_R_SYNTAX; + } + + return (result); +} + +static isc_result_t +table_fromwire(isccc_region_t *source, isccc_region_t *secret, + uint32_t algorithm, unsigned int depth, isccc_sexpr_t **alistp) { + char key[256]; + uint32_t len; + isc_result_t result; + isccc_sexpr_t *alist, *value; + bool first_tag; + unsigned char *checksum_rstart; + + REQUIRE(alistp != NULL && *alistp == NULL); + + if (depth > ISCCC_MAXDEPTH) { + return (ISCCC_R_MAXDEPTH); + } + + checksum_rstart = NULL; + first_tag = true; + alist = isccc_alist_create(); + if (alist == NULL) { + return (ISC_R_NOMEMORY); + } + + while (!REGION_EMPTY(*source)) { + GET8(len, source->rstart); + if (REGION_SIZE(*source) < len) { + result = ISC_R_UNEXPECTEDEND; + goto bad; + } + GET_MEM(key, len, source->rstart); + key[len] = '\0'; /* Ensure NUL termination. */ + value = NULL; + result = value_fromwire(source, depth + 1, &value); + if (result != ISC_R_SUCCESS) { + goto bad; + } + if (isccc_alist_define(alist, key, value) == NULL) { + result = ISC_R_NOMEMORY; + goto bad; + } + if (first_tag && secret != NULL && strcmp(key, "_auth") == 0) { + checksum_rstart = source->rstart; + } + first_tag = false; + } + + if (secret != NULL) { + if (checksum_rstart != NULL) { + result = verify( + alist, checksum_rstart, + (unsigned int)(source->rend - checksum_rstart), + algorithm, secret); + } else { + result = ISCCC_R_BADAUTH; + } + } else { + result = ISC_R_SUCCESS; + } + +bad: + if (result == ISC_R_SUCCESS) { + *alistp = alist; + } else { + isccc_sexpr_free(&alist); + } + + return (result); +} + +static isc_result_t +list_fromwire(isccc_region_t *source, unsigned int depth, + isccc_sexpr_t **listp) { + isccc_sexpr_t *list, *value; + isc_result_t result; + + if (depth > ISCCC_MAXDEPTH) { + return (ISCCC_R_MAXDEPTH); + } + + list = NULL; + while (!REGION_EMPTY(*source)) { + value = NULL; + result = value_fromwire(source, depth + 1, &value); + if (result != ISC_R_SUCCESS) { + isccc_sexpr_free(&list); + return (result); + } + if (isccc_sexpr_addtolist(&list, value) == NULL) { + isccc_sexpr_free(&value); + isccc_sexpr_free(&list); + return (ISC_R_NOMEMORY); + } + } + + *listp = list; + + return (ISC_R_SUCCESS); +} + +isc_result_t +isccc_cc_fromwire(isccc_region_t *source, isccc_sexpr_t **alistp, + uint32_t algorithm, isccc_region_t *secret) { + unsigned int size; + uint32_t version; + + size = REGION_SIZE(*source); + if (size < 4) { + return (ISC_R_UNEXPECTEDEND); + } + GET32(version, source->rstart); + if (version != 1) { + return (ISCCC_R_UNKNOWNVERSION); + } + + return (table_fromwire(source, secret, algorithm, 0, alistp)); +} + +static isc_result_t +createmessage(uint32_t version, const char *from, const char *to, + uint32_t serial, isccc_time_t now, isccc_time_t expires, + isccc_sexpr_t **alistp, bool want_expires) { + isccc_sexpr_t *alist, *_ctrl, *_data; + isc_result_t result; + + REQUIRE(alistp != NULL && *alistp == NULL); + + if (version != 1) { + return (ISCCC_R_UNKNOWNVERSION); + } + + alist = isccc_alist_create(); + if (alist == NULL) { + return (ISC_R_NOMEMORY); + } + + result = ISC_R_NOMEMORY; + + _ctrl = isccc_alist_create(); + if (_ctrl == NULL) { + goto bad; + } + if (isccc_alist_define(alist, "_ctrl", _ctrl) == NULL) { + isccc_sexpr_free(&_ctrl); + goto bad; + } + + _data = isccc_alist_create(); + if (_data == NULL) { + goto bad; + } + if (isccc_alist_define(alist, "_data", _data) == NULL) { + isccc_sexpr_free(&_data); + goto bad; + } + + if (isccc_cc_defineuint32(_ctrl, "_ser", serial) == NULL || + isccc_cc_defineuint32(_ctrl, "_tim", now) == NULL || + (want_expires && + isccc_cc_defineuint32(_ctrl, "_exp", expires) == NULL)) + { + goto bad; + } + if (from != NULL && isccc_cc_definestring(_ctrl, "_frm", from) == NULL) + { + goto bad; + } + if (to != NULL && isccc_cc_definestring(_ctrl, "_to", to) == NULL) { + goto bad; + } + + *alistp = alist; + + return (ISC_R_SUCCESS); + +bad: + isccc_sexpr_free(&alist); + + return (result); +} + +isc_result_t +isccc_cc_createmessage(uint32_t version, const char *from, const char *to, + uint32_t serial, isccc_time_t now, isccc_time_t expires, + isccc_sexpr_t **alistp) { + return (createmessage(version, from, to, serial, now, expires, alistp, + true)); +} + +isc_result_t +isccc_cc_createack(isccc_sexpr_t *message, bool ok, isccc_sexpr_t **ackp) { + char *_frm, *_to; + uint32_t serial; + isccc_sexpr_t *ack, *_ctrl; + isc_result_t result; + isccc_time_t t; + + REQUIRE(ackp != NULL && *ackp == NULL); + + _ctrl = isccc_alist_lookup(message, "_ctrl"); + if (!isccc_alist_alistp(_ctrl) || + isccc_cc_lookupuint32(_ctrl, "_ser", &serial) != ISC_R_SUCCESS || + isccc_cc_lookupuint32(_ctrl, "_tim", &t) != ISC_R_SUCCESS) + { + return (ISC_R_FAILURE); + } + /* + * _frm and _to are optional. + */ + _frm = NULL; + (void)isccc_cc_lookupstring(_ctrl, "_frm", &_frm); + _to = NULL; + (void)isccc_cc_lookupstring(_ctrl, "_to", &_to); + /* + * Create the ack. + */ + ack = NULL; + result = createmessage(1, _to, _frm, serial, t, 0, &ack, false); + if (result != ISC_R_SUCCESS) { + return (result); + } + + _ctrl = isccc_alist_lookup(ack, "_ctrl"); + if (_ctrl == NULL) { + result = ISC_R_FAILURE; + goto bad; + } + if (isccc_cc_definestring(ack, "_ack", (ok) ? "1" : "0") == NULL) { + result = ISC_R_NOMEMORY; + goto bad; + } + + *ackp = ack; + + return (ISC_R_SUCCESS); + +bad: + isccc_sexpr_free(&ack); + + return (result); +} + +bool +isccc_cc_isack(isccc_sexpr_t *message) { + isccc_sexpr_t *_ctrl; + + _ctrl = isccc_alist_lookup(message, "_ctrl"); + if (!isccc_alist_alistp(_ctrl)) { + return (false); + } + if (isccc_cc_lookupstring(_ctrl, "_ack", NULL) == ISC_R_SUCCESS) { + return (true); + } + return (false); +} + +bool +isccc_cc_isreply(isccc_sexpr_t *message) { + isccc_sexpr_t *_ctrl; + + _ctrl = isccc_alist_lookup(message, "_ctrl"); + if (!isccc_alist_alistp(_ctrl)) { + return (false); + } + if (isccc_cc_lookupstring(_ctrl, "_rpl", NULL) == ISC_R_SUCCESS) { + return (true); + } + return (false); +} + +isc_result_t +isccc_cc_createresponse(isccc_sexpr_t *message, isccc_time_t now, + isccc_time_t expires, isccc_sexpr_t **alistp) { + char *_frm, *_to, *type = NULL; + uint32_t serial; + isccc_sexpr_t *alist, *_ctrl, *_data; + isc_result_t result; + + REQUIRE(alistp != NULL && *alistp == NULL); + + _ctrl = isccc_alist_lookup(message, "_ctrl"); + _data = isccc_alist_lookup(message, "_data"); + if (!isccc_alist_alistp(_ctrl) || !isccc_alist_alistp(_data) || + isccc_cc_lookupuint32(_ctrl, "_ser", &serial) != ISC_R_SUCCESS || + isccc_cc_lookupstring(_data, "type", &type) != ISC_R_SUCCESS) + { + return (ISC_R_FAILURE); + } + /* + * _frm and _to are optional. + */ + _frm = NULL; + (void)isccc_cc_lookupstring(_ctrl, "_frm", &_frm); + _to = NULL; + (void)isccc_cc_lookupstring(_ctrl, "_to", &_to); + /* + * Create the response. + */ + alist = NULL; + result = isccc_cc_createmessage(1, _to, _frm, serial, now, expires, + &alist); + if (result != ISC_R_SUCCESS) { + return (result); + } + + _ctrl = isccc_alist_lookup(alist, "_ctrl"); + if (_ctrl == NULL) { + result = ISC_R_FAILURE; + goto bad; + } + + _data = isccc_alist_lookup(alist, "_data"); + if (_data == NULL) { + result = ISC_R_FAILURE; + goto bad; + } + + if (isccc_cc_definestring(_ctrl, "_rpl", "1") == NULL || + isccc_cc_definestring(_data, "type", type) == NULL) + { + result = ISC_R_NOMEMORY; + goto bad; + } + + *alistp = alist; + + return (ISC_R_SUCCESS); + +bad: + isccc_sexpr_free(&alist); + return (result); +} + +isccc_sexpr_t * +isccc_cc_definestring(isccc_sexpr_t *alist, const char *key, const char *str) { + size_t len; + isccc_region_t r; + + len = strlen(str); + DE_CONST(str, r.rstart); + r.rend = r.rstart + len; + + return (isccc_alist_definebinary(alist, key, &r)); +} + +isccc_sexpr_t * +isccc_cc_defineuint32(isccc_sexpr_t *alist, const char *key, uint32_t i) { + char b[100]; + size_t len; + isccc_region_t r; + + snprintf(b, sizeof(b), "%u", i); + len = strlen(b); + r.rstart = (unsigned char *)b; + r.rend = (unsigned char *)b + len; + + return (isccc_alist_definebinary(alist, key, &r)); +} + +isc_result_t +isccc_cc_lookupstring(isccc_sexpr_t *alist, const char *key, char **strp) { + isccc_sexpr_t *kv, *v; + + REQUIRE(strp == NULL || *strp == NULL); + + kv = isccc_alist_assq(alist, key); + if (kv != NULL) { + v = ISCCC_SEXPR_CDR(kv); + if (isccc_sexpr_binaryp(v)) { + if (strp != NULL) { + *strp = isccc_sexpr_tostring(v); + } + return (ISC_R_SUCCESS); + } else { + return (ISC_R_EXISTS); + } + } + + return (ISC_R_NOTFOUND); +} + +isc_result_t +isccc_cc_lookupuint32(isccc_sexpr_t *alist, const char *key, uint32_t *uintp) { + isccc_sexpr_t *kv, *v; + + kv = isccc_alist_assq(alist, key); + if (kv != NULL) { + v = ISCCC_SEXPR_CDR(kv); + if (isccc_sexpr_binaryp(v)) { + if (uintp != NULL) { + *uintp = (uint32_t)strtoul( + isccc_sexpr_tostring(v), NULL, 10); + } + return (ISC_R_SUCCESS); + } else { + return (ISC_R_EXISTS); + } + } + + return (ISC_R_NOTFOUND); +} + +static void +symtab_undefine(char *key, unsigned int type, isccc_symvalue_t value, + void *arg) { + UNUSED(type); + UNUSED(value); + UNUSED(arg); + + free(key); +} + +static bool +symtab_clean(char *key, unsigned int type, isccc_symvalue_t value, void *arg) { + isccc_time_t *now; + + UNUSED(key); + UNUSED(type); + + now = arg; + + if (*now < value.as_uinteger) { + return (false); + } + if ((*now - value.as_uinteger) < DUP_LIFETIME) { + return (false); + } + return (true); +} + +isc_result_t +isccc_cc_createsymtab(isccc_symtab_t **symtabp) { + return (isccc_symtab_create(11897, symtab_undefine, NULL, false, + symtabp)); +} + +void +isccc_cc_cleansymtab(isccc_symtab_t *symtab, isccc_time_t now) { + isccc_symtab_foreach(symtab, symtab_clean, &now); +} + +static bool +has_whitespace(const char *str) { + char c; + + if (str == NULL) { + return (false); + } + while ((c = *str++) != '\0') { + if (c == ' ' || c == '\t' || c == '\n') { + return (true); + } + } + return (false); +} + +isc_result_t +isccc_cc_checkdup(isccc_symtab_t *symtab, isccc_sexpr_t *message, + isccc_time_t now) { + const char *_frm; + const char *_to; + char *_ser = NULL, *_tim = NULL, *tmp; + isc_result_t result; + char *key; + size_t len; + isccc_symvalue_t value; + isccc_sexpr_t *_ctrl; + + _ctrl = isccc_alist_lookup(message, "_ctrl"); + if (!isccc_alist_alistp(_ctrl) || + isccc_cc_lookupstring(_ctrl, "_ser", &_ser) != ISC_R_SUCCESS || + isccc_cc_lookupstring(_ctrl, "_tim", &_tim) != ISC_R_SUCCESS) + { + return (ISC_R_FAILURE); + } + + INSIST(_ser != NULL); + INSIST(_tim != NULL); + + /* + * _frm and _to are optional. + */ + tmp = NULL; + if (isccc_cc_lookupstring(_ctrl, "_frm", &tmp) != ISC_R_SUCCESS) { + _frm = ""; + } else { + _frm = tmp; + } + tmp = NULL; + if (isccc_cc_lookupstring(_ctrl, "_to", &tmp) != ISC_R_SUCCESS) { + _to = ""; + } else { + _to = tmp; + } + /* + * Ensure there is no newline in any of the strings. This is so + * we can write them to a file later. + */ + if (has_whitespace(_frm) || has_whitespace(_to) || + has_whitespace(_ser) || has_whitespace(_tim)) + { + return (ISC_R_FAILURE); + } + len = strlen(_frm) + strlen(_to) + strlen(_ser) + strlen(_tim) + 4; + key = malloc(len); + if (key == NULL) { + return (ISC_R_NOMEMORY); + } + snprintf(key, len, "%s;%s;%s;%s", _frm, _to, _ser, _tim); + value.as_uinteger = now; + result = isccc_symtab_define(symtab, key, ISCCC_SYMTYPE_CCDUP, value, + isccc_symexists_reject); + if (result != ISC_R_SUCCESS) { + free(key); + return (result); + } + + return (ISC_R_SUCCESS); +} |