Home | History | Annotate | Line # | Download | only in isccc
      1  1.9  christos /*	$NetBSD: cc.c,v 1.9 2025/01/26 16:25:44 christos Exp $	*/
      2  1.1  christos 
      3  1.1  christos /*
      4  1.6  christos  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
      5  1.6  christos  *
      6  1.6  christos  * SPDX-License-Identifier: MPL-2.0 AND ISC
      7  1.1  christos  *
      8  1.1  christos  * This Source Code Form is subject to the terms of the Mozilla Public
      9  1.1  christos  * License, v. 2.0. If a copy of the MPL was not distributed with this
     10  1.5  christos  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
     11  1.1  christos  *
     12  1.1  christos  * See the COPYRIGHT file distributed with this work for additional
     13  1.1  christos  * information regarding copyright ownership.
     14  1.6  christos  */
     15  1.6  christos 
     16  1.6  christos /*
     17  1.6  christos  * Copyright (C) 2001 Nominum, Inc.
     18  1.1  christos  *
     19  1.1  christos  * Permission to use, copy, modify, and/or distribute this software for any
     20  1.1  christos  * purpose with or without fee is hereby granted, provided that the above
     21  1.1  christos  * copyright notice and this permission notice appear in all copies.
     22  1.1  christos  *
     23  1.1  christos  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NOMINUM DISCLAIMS ALL
     24  1.1  christos  * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
     25  1.1  christos  * OF MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY
     26  1.1  christos  * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     27  1.1  christos  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     28  1.1  christos  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     29  1.1  christos  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     30  1.1  christos  */
     31  1.1  christos 
     32  1.1  christos /*! \file */
     33  1.1  christos 
     34  1.4  christos #include <errno.h>
     35  1.4  christos #include <inttypes.h>
     36  1.3  christos #include <stdbool.h>
     37  1.1  christos #include <stdio.h>
     38  1.4  christos #include <stdlib.h>
     39  1.1  christos #include <string.h>
     40  1.1  christos 
     41  1.1  christos #include <isc/assertions.h>
     42  1.3  christos #include <isc/hmac.h>
     43  1.8  christos #include <isc/result.h>
     44  1.1  christos #include <isc/safe.h>
     45  1.1  christos 
     46  1.1  christos #include <isccc/alist.h>
     47  1.1  christos #include <isccc/base64.h>
     48  1.1  christos #include <isccc/cc.h>
     49  1.1  christos #include <isccc/sexpr.h>
     50  1.1  christos #include <isccc/symtab.h>
     51  1.1  christos #include <isccc/symtype.h>
     52  1.1  christos #include <isccc/util.h>
     53  1.1  christos 
     54  1.4  christos #define MAX_TAGS     256
     55  1.4  christos #define DUP_LIFETIME 900
     56  1.8  christos #ifndef ISCCC_MAXDEPTH
     57  1.8  christos #define ISCCC_MAXDEPTH \
     58  1.8  christos 	10 /* Big enough for rndc which just sends a string each way. */
     59  1.8  christos #endif
     60  1.1  christos 
     61  1.1  christos typedef isccc_sexpr_t *sexpr_ptr;
     62  1.1  christos 
     63  1.1  christos static unsigned char auth_hmd5[] = {
     64  1.4  christos 	0x05, 0x5f, 0x61, 0x75, 0x74, 0x68, /*%< len + _auth */
     65  1.4  christos 	ISCCC_CCMSGTYPE_TABLE,		    /*%< message type */
     66  1.4  christos 	0x00, 0x00, 0x00, 0x20,		    /*%< length == 32 */
     67  1.4  christos 	0x04, 0x68, 0x6d, 0x64, 0x35,	    /*%< len + hmd5 */
     68  1.4  christos 	ISCCC_CCMSGTYPE_BINARYDATA,	    /*%< message type */
     69  1.4  christos 	0x00, 0x00, 0x00, 0x16,		    /*%< length == 22 */
     70  1.1  christos 	/*
     71  1.1  christos 	 * The base64 encoding of one of our HMAC-MD5 signatures is
     72  1.1  christos 	 * 22 bytes.
     73  1.1  christos 	 */
     74  1.4  christos 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     75  1.4  christos 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
     76  1.1  christos };
     77  1.1  christos 
     78  1.4  christos #define HMD5_OFFSET 21 /*%< 21 = 6 + 1 + 4 + 5 + 1 + 4 */
     79  1.4  christos #define HMD5_LENGTH 22
     80  1.1  christos 
     81  1.1  christos static unsigned char auth_hsha[] = {
     82  1.4  christos 	0x05, 0x5f, 0x61, 0x75, 0x74, 0x68, /*%< len + _auth */
     83  1.4  christos 	ISCCC_CCMSGTYPE_TABLE,		    /*%< message type */
     84  1.4  christos 	0x00, 0x00, 0x00, 0x63,		    /*%< length == 99 */
     85  1.4  christos 	0x04, 0x68, 0x73, 0x68, 0x61,	    /*%< len + hsha */
     86  1.4  christos 	ISCCC_CCMSGTYPE_BINARYDATA,	    /*%< message type */
     87  1.4  christos 	0x00, 0x00, 0x00, 0x59,		    /*%< length == 89 */
     88  1.4  christos 	0x00,				    /*%< algorithm */
     89  1.1  christos 	/*
     90  1.1  christos 	 * The base64 encoding of one of our HMAC-SHA* signatures is
     91  1.1  christos 	 * 88 bytes.
     92  1.1  christos 	 */
     93  1.4  christos 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     94  1.4  christos 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     95  1.4  christos 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     96  1.4  christos 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     97  1.4  christos 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     98  1.4  christos 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     99  1.4  christos 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    100  1.4  christos 	0x00, 0x00, 0x00, 0x00
    101  1.1  christos };
    102  1.1  christos 
    103  1.4  christos #define HSHA_OFFSET 22 /*%< 21 = 6 + 1 + 4 + 5 + 1 + 4 + 1 */
    104  1.4  christos #define HSHA_LENGTH 88
    105  1.1  christos 
    106  1.1  christos static isc_result_t
    107  1.1  christos table_towire(isccc_sexpr_t *alist, isc_buffer_t **buffer);
    108  1.1  christos 
    109  1.1  christos static isc_result_t
    110  1.1  christos list_towire(isccc_sexpr_t *alist, isc_buffer_t **buffer);
    111  1.1  christos 
    112  1.1  christos static isc_result_t
    113  1.1  christos value_towire(isccc_sexpr_t *elt, isc_buffer_t **buffer) {
    114  1.1  christos 	unsigned int len;
    115  1.1  christos 	isccc_region_t *vr;
    116  1.1  christos 	isc_result_t result;
    117  1.1  christos 
    118  1.1  christos 	if (isccc_sexpr_binaryp(elt)) {
    119  1.1  christos 		vr = isccc_sexpr_tobinary(elt);
    120  1.1  christos 		len = REGION_SIZE(*vr);
    121  1.9  christos 		result = isc_buffer_reserve(*buffer, 1 + 4);
    122  1.4  christos 		if (result != ISC_R_SUCCESS) {
    123  1.9  christos 			return ISC_R_NOSPACE;
    124  1.4  christos 		}
    125  1.1  christos 		isc_buffer_putuint8(*buffer, ISCCC_CCMSGTYPE_BINARYDATA);
    126  1.1  christos 		isc_buffer_putuint32(*buffer, len);
    127  1.1  christos 
    128  1.9  christos 		result = isc_buffer_reserve(*buffer, len);
    129  1.4  christos 		if (result != ISC_R_SUCCESS) {
    130  1.9  christos 			return ISC_R_NOSPACE;
    131  1.4  christos 		}
    132  1.1  christos 		isc_buffer_putmem(*buffer, vr->rstart, len);
    133  1.1  christos 	} else if (isccc_alist_alistp(elt)) {
    134  1.1  christos 		unsigned int used;
    135  1.1  christos 		isc_buffer_t b;
    136  1.1  christos 
    137  1.9  christos 		result = isc_buffer_reserve(*buffer, 1 + 4);
    138  1.4  christos 		if (result != ISC_R_SUCCESS) {
    139  1.9  christos 			return ISC_R_NOSPACE;
    140  1.4  christos 		}
    141  1.1  christos 		isc_buffer_putuint8(*buffer, ISCCC_CCMSGTYPE_TABLE);
    142  1.1  christos 		/*
    143  1.1  christos 		 * Emit a placeholder length.
    144  1.1  christos 		 */
    145  1.1  christos 		used = (*buffer)->used;
    146  1.1  christos 		isc_buffer_putuint32(*buffer, 0);
    147  1.1  christos 
    148  1.1  christos 		/*
    149  1.1  christos 		 * Emit the table.
    150  1.1  christos 		 */
    151  1.1  christos 		result = table_towire(elt, buffer);
    152  1.4  christos 		if (result != ISC_R_SUCCESS) {
    153  1.9  christos 			return result;
    154  1.4  christos 		}
    155  1.1  christos 
    156  1.1  christos 		len = (*buffer)->used - used;
    157  1.1  christos 		/*
    158  1.1  christos 		 * 'len' is 4 bytes too big, since it counts
    159  1.1  christos 		 * the placeholder length too.	Adjust and
    160  1.1  christos 		 * emit.
    161  1.1  christos 		 */
    162  1.1  christos 		INSIST(len >= 4U);
    163  1.1  christos 		len -= 4;
    164  1.1  christos 
    165  1.4  christos 		isc_buffer_init(&b, (unsigned char *)(*buffer)->base + used, 4);
    166  1.1  christos 		isc_buffer_putuint32(&b, len);
    167  1.1  christos 	} else if (isccc_sexpr_listp(elt)) {
    168  1.1  christos 		unsigned int used;
    169  1.1  christos 		isc_buffer_t b;
    170  1.1  christos 
    171  1.9  christos 		result = isc_buffer_reserve(*buffer, 1 + 4);
    172  1.4  christos 		if (result != ISC_R_SUCCESS) {
    173  1.9  christos 			return ISC_R_NOSPACE;
    174  1.4  christos 		}
    175  1.1  christos 		isc_buffer_putuint8(*buffer, ISCCC_CCMSGTYPE_LIST);
    176  1.1  christos 		/*
    177  1.1  christos 		 * Emit a placeholder length.
    178  1.1  christos 		 */
    179  1.1  christos 		used = (*buffer)->used;
    180  1.1  christos 		isc_buffer_putuint32(*buffer, 0);
    181  1.1  christos 
    182  1.1  christos 		/*
    183  1.1  christos 		 * Emit the list.
    184  1.1  christos 		 */
    185  1.1  christos 		result = list_towire(elt, buffer);
    186  1.4  christos 		if (result != ISC_R_SUCCESS) {
    187  1.9  christos 			return result;
    188  1.4  christos 		}
    189  1.1  christos 
    190  1.1  christos 		len = (*buffer)->used - used;
    191  1.1  christos 		/*
    192  1.1  christos 		 * 'len' is 4 bytes too big, since it counts
    193  1.1  christos 		 * the placeholder length too.	Adjust and
    194  1.1  christos 		 * emit.
    195  1.1  christos 		 */
    196  1.1  christos 		INSIST(len >= 4U);
    197  1.1  christos 		len -= 4;
    198  1.1  christos 
    199  1.4  christos 		isc_buffer_init(&b, (unsigned char *)(*buffer)->base + used, 4);
    200  1.1  christos 		isc_buffer_putuint32(&b, len);
    201  1.1  christos 	}
    202  1.1  christos 
    203  1.9  christos 	return ISC_R_SUCCESS;
    204  1.1  christos }
    205  1.1  christos 
    206  1.1  christos static isc_result_t
    207  1.1  christos table_towire(isccc_sexpr_t *alist, isc_buffer_t **buffer) {
    208  1.1  christos 	isccc_sexpr_t *kv, *elt, *k, *v;
    209  1.1  christos 	char *ks;
    210  1.1  christos 	isc_result_t result;
    211  1.1  christos 	unsigned int len;
    212  1.1  christos 
    213  1.4  christos 	for (elt = isccc_alist_first(alist); elt != NULL;
    214  1.7  christos 	     elt = ISCCC_SEXPR_CDR(elt))
    215  1.7  christos 	{
    216  1.1  christos 		kv = ISCCC_SEXPR_CAR(elt);
    217  1.1  christos 		k = ISCCC_SEXPR_CAR(kv);
    218  1.1  christos 		ks = isccc_sexpr_tostring(k);
    219  1.1  christos 		v = ISCCC_SEXPR_CDR(kv);
    220  1.1  christos 		len = (unsigned int)strlen(ks);
    221  1.1  christos 		INSIST(len <= 255U);
    222  1.1  christos 		/*
    223  1.1  christos 		 * Emit the key name.
    224  1.1  christos 		 */
    225  1.9  christos 		result = isc_buffer_reserve(*buffer, 1 + len);
    226  1.4  christos 		if (result != ISC_R_SUCCESS) {
    227  1.9  christos 			return ISC_R_NOSPACE;
    228  1.4  christos 		}
    229  1.3  christos 		isc_buffer_putuint8(*buffer, (uint8_t)len);
    230  1.4  christos 		isc_buffer_putmem(*buffer, (const unsigned char *)ks, len);
    231  1.1  christos 		/*
    232  1.1  christos 		 * Emit the value.
    233  1.1  christos 		 */
    234  1.1  christos 		result = value_towire(v, buffer);
    235  1.4  christos 		if (result != ISC_R_SUCCESS) {
    236  1.9  christos 			return result;
    237  1.4  christos 		}
    238  1.1  christos 	}
    239  1.1  christos 
    240  1.9  christos 	return ISC_R_SUCCESS;
    241  1.1  christos }
    242  1.1  christos 
    243  1.1  christos static isc_result_t
    244  1.1  christos list_towire(isccc_sexpr_t *list, isc_buffer_t **buffer) {
    245  1.1  christos 	isc_result_t result;
    246  1.1  christos 
    247  1.1  christos 	while (list != NULL) {
    248  1.1  christos 		result = value_towire(ISCCC_SEXPR_CAR(list), buffer);
    249  1.4  christos 		if (result != ISC_R_SUCCESS) {
    250  1.9  christos 			return result;
    251  1.4  christos 		}
    252  1.1  christos 		list = ISCCC_SEXPR_CDR(list);
    253  1.1  christos 	}
    254  1.1  christos 
    255  1.9  christos 	return ISC_R_SUCCESS;
    256  1.1  christos }
    257  1.1  christos 
    258  1.1  christos static isc_result_t
    259  1.8  christos sign(unsigned char *data, unsigned int length, unsigned char *out,
    260  1.4  christos      uint32_t algorithm, isccc_region_t *secret) {
    261  1.4  christos 	const isc_md_type_t *md_type;
    262  1.1  christos 	isc_result_t result;
    263  1.1  christos 	isccc_region_t source, target;
    264  1.3  christos 	unsigned char digest[ISC_MAX_MD_SIZE];
    265  1.8  christos 	unsigned int digestlen = sizeof(digest);
    266  1.1  christos 	unsigned char digestb64[HSHA_LENGTH + 4];
    267  1.1  christos 
    268  1.1  christos 	source.rstart = digest;
    269  1.1  christos 
    270  1.1  christos 	switch (algorithm) {
    271  1.1  christos 	case ISCCC_ALG_HMACMD5:
    272  1.3  christos 		md_type = ISC_MD_MD5;
    273  1.1  christos 		break;
    274  1.1  christos 	case ISCCC_ALG_HMACSHA1:
    275  1.3  christos 		md_type = ISC_MD_SHA1;
    276  1.1  christos 		break;
    277  1.1  christos 	case ISCCC_ALG_HMACSHA224:
    278  1.3  christos 		md_type = ISC_MD_SHA224;
    279  1.1  christos 		break;
    280  1.1  christos 	case ISCCC_ALG_HMACSHA256:
    281  1.3  christos 		md_type = ISC_MD_SHA256;
    282  1.1  christos 		break;
    283  1.1  christos 	case ISCCC_ALG_HMACSHA384:
    284  1.3  christos 		md_type = ISC_MD_SHA384;
    285  1.1  christos 		break;
    286  1.1  christos 	case ISCCC_ALG_HMACSHA512:
    287  1.3  christos 		md_type = ISC_MD_SHA512;
    288  1.1  christos 		break;
    289  1.3  christos 	default:
    290  1.9  christos 		return ISC_R_NOTIMPLEMENTED;
    291  1.3  christos 	}
    292  1.1  christos 
    293  1.4  christos 	result = isc_hmac(md_type, secret->rstart, REGION_SIZE(*secret), data,
    294  1.4  christos 			  length, digest, &digestlen);
    295  1.3  christos 	if (result != ISC_R_SUCCESS) {
    296  1.9  christos 		return result;
    297  1.1  christos 	}
    298  1.3  christos 	source.rend = digest + digestlen;
    299  1.1  christos 
    300  1.1  christos 	memset(digestb64, 0, sizeof(digestb64));
    301  1.1  christos 	target.rstart = digestb64;
    302  1.1  christos 	target.rend = digestb64 + sizeof(digestb64);
    303  1.1  christos 	result = isccc_base64_encode(&source, 64, "", &target);
    304  1.4  christos 	if (result != ISC_R_SUCCESS) {
    305  1.9  christos 		return result;
    306  1.4  christos 	}
    307  1.4  christos 	if (algorithm == ISCCC_ALG_HMACMD5) {
    308  1.8  christos 		PUT_MEM(digestb64, HMD5_LENGTH, out);
    309  1.4  christos 	} else {
    310  1.8  christos 		PUT_MEM(digestb64, HSHA_LENGTH, out);
    311  1.4  christos 	}
    312  1.9  christos 	return ISC_R_SUCCESS;
    313  1.1  christos }
    314  1.1  christos 
    315  1.1  christos isc_result_t
    316  1.4  christos isccc_cc_towire(isccc_sexpr_t *alist, isc_buffer_t **buffer, uint32_t algorithm,
    317  1.4  christos 		isccc_region_t *secret) {
    318  1.1  christos 	unsigned int hmac_base, signed_base;
    319  1.1  christos 	isc_result_t result;
    320  1.1  christos 
    321  1.9  christos 	result = isc_buffer_reserve(*buffer,
    322  1.4  christos 				    4 + ((algorithm == ISCCC_ALG_HMACMD5)
    323  1.4  christos 						 ? sizeof(auth_hmd5)
    324  1.4  christos 						 : sizeof(auth_hsha)));
    325  1.4  christos 	if (result != ISC_R_SUCCESS) {
    326  1.9  christos 		return ISC_R_NOSPACE;
    327  1.4  christos 	}
    328  1.1  christos 
    329  1.1  christos 	/*
    330  1.1  christos 	 * Emit protocol version.
    331  1.1  christos 	 */
    332  1.1  christos 	isc_buffer_putuint32(*buffer, 1);
    333  1.1  christos 
    334  1.1  christos 	if (secret != NULL) {
    335  1.1  christos 		/*
    336  1.1  christos 		 * Emit _auth section with zeroed HMAC signature.
    337  1.1  christos 		 * We'll replace the zeros with the real signature once
    338  1.1  christos 		 * we know what it is.
    339  1.1  christos 		 */
    340  1.1  christos 		if (algorithm == ISCCC_ALG_HMACMD5) {
    341  1.1  christos 			hmac_base = (*buffer)->used + HMD5_OFFSET;
    342  1.4  christos 			isc_buffer_putmem(*buffer, auth_hmd5,
    343  1.4  christos 					  sizeof(auth_hmd5));
    344  1.4  christos 		} else {
    345  1.1  christos 			unsigned char *hmac_alg;
    346  1.1  christos 
    347  1.1  christos 			hmac_base = (*buffer)->used + HSHA_OFFSET;
    348  1.4  christos 			hmac_alg = (unsigned char *)isc_buffer_used(*buffer) +
    349  1.4  christos 				   HSHA_OFFSET - 1;
    350  1.4  christos 			isc_buffer_putmem(*buffer, auth_hsha,
    351  1.4  christos 					  sizeof(auth_hsha));
    352  1.1  christos 			*hmac_alg = algorithm;
    353  1.1  christos 		}
    354  1.4  christos 	} else {
    355  1.1  christos 		hmac_base = 0;
    356  1.4  christos 	}
    357  1.1  christos 	signed_base = (*buffer)->used;
    358  1.1  christos 	/*
    359  1.1  christos 	 * Delete any existing _auth section so that we don't try
    360  1.1  christos 	 * to encode it.
    361  1.1  christos 	 */
    362  1.1  christos 	isccc_alist_delete(alist, "_auth");
    363  1.1  christos 	/*
    364  1.1  christos 	 * Emit the message.
    365  1.1  christos 	 */
    366  1.1  christos 	result = table_towire(alist, buffer);
    367  1.4  christos 	if (result != ISC_R_SUCCESS) {
    368  1.9  christos 		return result;
    369  1.4  christos 	}
    370  1.4  christos 	if (secret != NULL) {
    371  1.9  christos 		return sign((unsigned char *)(*buffer)->base + signed_base,
    372  1.9  christos 			    (*buffer)->used - signed_base,
    373  1.9  christos 			    (unsigned char *)(*buffer)->base + hmac_base,
    374  1.9  christos 			    algorithm, secret);
    375  1.4  christos 	}
    376  1.9  christos 	return ISC_R_SUCCESS;
    377  1.1  christos }
    378  1.1  christos 
    379  1.1  christos static isc_result_t
    380  1.1  christos verify(isccc_sexpr_t *alist, unsigned char *data, unsigned int length,
    381  1.4  christos        uint32_t algorithm, isccc_region_t *secret) {
    382  1.4  christos 	const isc_md_type_t *md_type;
    383  1.1  christos 	isccc_region_t source;
    384  1.1  christos 	isccc_region_t target;
    385  1.1  christos 	isc_result_t result;
    386  1.8  christos 	isccc_sexpr_t *_auth, *hmacvalue;
    387  1.3  christos 	unsigned char digest[ISC_MAX_MD_SIZE];
    388  1.8  christos 	unsigned int digestlen = sizeof(digest);
    389  1.1  christos 	unsigned char digestb64[HSHA_LENGTH * 4];
    390  1.1  christos 
    391  1.1  christos 	/*
    392  1.1  christos 	 * Extract digest.
    393  1.1  christos 	 */
    394  1.1  christos 	_auth = isccc_alist_lookup(alist, "_auth");
    395  1.4  christos 	if (!isccc_alist_alistp(_auth)) {
    396  1.9  christos 		return ISC_R_FAILURE;
    397  1.4  christos 	}
    398  1.4  christos 	if (algorithm == ISCCC_ALG_HMACMD5) {
    399  1.8  christos 		hmacvalue = isccc_alist_lookup(_auth, "hmd5");
    400  1.4  christos 	} else {
    401  1.8  christos 		hmacvalue = isccc_alist_lookup(_auth, "hsha");
    402  1.4  christos 	}
    403  1.8  christos 	if (!isccc_sexpr_binaryp(hmacvalue)) {
    404  1.9  christos 		return ISC_R_FAILURE;
    405  1.4  christos 	}
    406  1.1  christos 	/*
    407  1.1  christos 	 * Compute digest.
    408  1.1  christos 	 */
    409  1.1  christos 	source.rstart = digest;
    410  1.3  christos 
    411  1.1  christos 	switch (algorithm) {
    412  1.1  christos 	case ISCCC_ALG_HMACMD5:
    413  1.3  christos 		md_type = ISC_MD_MD5;
    414  1.1  christos 		break;
    415  1.1  christos 	case ISCCC_ALG_HMACSHA1:
    416  1.3  christos 		md_type = ISC_MD_SHA1;
    417  1.1  christos 		break;
    418  1.1  christos 	case ISCCC_ALG_HMACSHA224:
    419  1.3  christos 		md_type = ISC_MD_SHA224;
    420  1.1  christos 		break;
    421  1.1  christos 	case ISCCC_ALG_HMACSHA256:
    422  1.3  christos 		md_type = ISC_MD_SHA256;
    423  1.1  christos 		break;
    424  1.1  christos 	case ISCCC_ALG_HMACSHA384:
    425  1.3  christos 		md_type = ISC_MD_SHA384;
    426  1.1  christos 		break;
    427  1.1  christos 	case ISCCC_ALG_HMACSHA512:
    428  1.3  christos 		md_type = ISC_MD_SHA512;
    429  1.1  christos 		break;
    430  1.3  christos 	default:
    431  1.9  christos 		return ISC_R_NOTIMPLEMENTED;
    432  1.3  christos 	}
    433  1.1  christos 
    434  1.4  christos 	result = isc_hmac(md_type, secret->rstart, REGION_SIZE(*secret), data,
    435  1.4  christos 			  length, digest, &digestlen);
    436  1.3  christos 	if (result != ISC_R_SUCCESS) {
    437  1.9  christos 		return result;
    438  1.1  christos 	}
    439  1.3  christos 	source.rend = digest + digestlen;
    440  1.3  christos 
    441  1.1  christos 	target.rstart = digestb64;
    442  1.1  christos 	target.rend = digestb64 + sizeof(digestb64);
    443  1.1  christos 	memset(digestb64, 0, sizeof(digestb64));
    444  1.1  christos 	result = isccc_base64_encode(&source, 64, "", &target);
    445  1.4  christos 	if (result != ISC_R_SUCCESS) {
    446  1.9  christos 		return result;
    447  1.4  christos 	}
    448  1.1  christos 
    449  1.1  christos 	/*
    450  1.1  christos 	 * Verify.
    451  1.1  christos 	 */
    452  1.1  christos 	if (algorithm == ISCCC_ALG_HMACMD5) {
    453  1.1  christos 		isccc_region_t *region;
    454  1.1  christos 		unsigned char *value;
    455  1.1  christos 
    456  1.8  christos 		region = isccc_sexpr_tobinary(hmacvalue);
    457  1.4  christos 		if ((region->rend - region->rstart) != HMD5_LENGTH) {
    458  1.9  christos 			return ISCCC_R_BADAUTH;
    459  1.4  christos 		}
    460  1.1  christos 		value = region->rstart;
    461  1.4  christos 		if (!isc_safe_memequal(value, digestb64, HMD5_LENGTH)) {
    462  1.9  christos 			return ISCCC_R_BADAUTH;
    463  1.4  christos 		}
    464  1.3  christos 	} else {
    465  1.1  christos 		isccc_region_t *region;
    466  1.1  christos 		unsigned char *value;
    467  1.3  christos 		uint32_t valalg;
    468  1.1  christos 
    469  1.8  christos 		region = isccc_sexpr_tobinary(hmacvalue);
    470  1.1  christos 
    471  1.1  christos 		/*
    472  1.1  christos 		 * Note: with non-MD5 algorithms, there's an extra octet
    473  1.1  christos 		 * to identify which algorithm is in use.
    474  1.1  christos 		 */
    475  1.4  christos 		if ((region->rend - region->rstart) != HSHA_LENGTH + 1) {
    476  1.9  christos 			return ISCCC_R_BADAUTH;
    477  1.4  christos 		}
    478  1.1  christos 		value = region->rstart;
    479  1.1  christos 		GET8(valalg, value);
    480  1.1  christos 		if ((valalg != algorithm) ||
    481  1.7  christos 		    !isc_safe_memequal(value, digestb64, HSHA_LENGTH))
    482  1.7  christos 		{
    483  1.9  christos 			return ISCCC_R_BADAUTH;
    484  1.4  christos 		}
    485  1.1  christos 	}
    486  1.1  christos 
    487  1.9  christos 	return ISC_R_SUCCESS;
    488  1.1  christos }
    489  1.1  christos 
    490  1.1  christos static isc_result_t
    491  1.1  christos table_fromwire(isccc_region_t *source, isccc_region_t *secret,
    492  1.8  christos 	       uint32_t algorithm, unsigned int depth, isccc_sexpr_t **alistp);
    493  1.1  christos 
    494  1.1  christos static isc_result_t
    495  1.8  christos list_fromwire(isccc_region_t *source, unsigned int depth,
    496  1.8  christos 	      isccc_sexpr_t **listp);
    497  1.1  christos 
    498  1.1  christos static isc_result_t
    499  1.8  christos value_fromwire(isccc_region_t *source, unsigned int depth,
    500  1.8  christos 	       isccc_sexpr_t **valuep) {
    501  1.1  christos 	unsigned int msgtype;
    502  1.3  christos 	uint32_t len;
    503  1.1  christos 	isccc_sexpr_t *value;
    504  1.1  christos 	isccc_region_t active;
    505  1.1  christos 	isc_result_t result;
    506  1.1  christos 
    507  1.8  christos 	if (depth > ISCCC_MAXDEPTH) {
    508  1.9  christos 		return ISCCC_R_MAXDEPTH;
    509  1.8  christos 	}
    510  1.8  christos 
    511  1.4  christos 	if (REGION_SIZE(*source) < 1 + 4) {
    512  1.9  christos 		return ISC_R_UNEXPECTEDEND;
    513  1.4  christos 	}
    514  1.1  christos 	GET8(msgtype, source->rstart);
    515  1.1  christos 	GET32(len, source->rstart);
    516  1.4  christos 	if (REGION_SIZE(*source) < len) {
    517  1.9  christos 		return ISC_R_UNEXPECTEDEND;
    518  1.4  christos 	}
    519  1.1  christos 	active.rstart = source->rstart;
    520  1.1  christos 	active.rend = active.rstart + len;
    521  1.1  christos 	source->rstart = active.rend;
    522  1.1  christos 	if (msgtype == ISCCC_CCMSGTYPE_BINARYDATA) {
    523  1.1  christos 		value = isccc_sexpr_frombinary(&active);
    524  1.1  christos 		if (value != NULL) {
    525  1.1  christos 			*valuep = value;
    526  1.1  christos 			result = ISC_R_SUCCESS;
    527  1.4  christos 		} else {
    528  1.1  christos 			result = ISC_R_NOMEMORY;
    529  1.4  christos 		}
    530  1.4  christos 	} else if (msgtype == ISCCC_CCMSGTYPE_TABLE) {
    531  1.8  christos 		result = table_fromwire(&active, NULL, 0, depth + 1, valuep);
    532  1.4  christos 	} else if (msgtype == ISCCC_CCMSGTYPE_LIST) {
    533  1.8  christos 		result = list_fromwire(&active, depth + 1, valuep);
    534  1.4  christos 	} else {
    535  1.1  christos 		result = ISCCC_R_SYNTAX;
    536  1.4  christos 	}
    537  1.1  christos 
    538  1.9  christos 	return result;
    539  1.1  christos }
    540  1.1  christos 
    541  1.1  christos static isc_result_t
    542  1.1  christos table_fromwire(isccc_region_t *source, isccc_region_t *secret,
    543  1.8  christos 	       uint32_t algorithm, unsigned int depth, isccc_sexpr_t **alistp) {
    544  1.1  christos 	char key[256];
    545  1.3  christos 	uint32_t len;
    546  1.1  christos 	isc_result_t result;
    547  1.1  christos 	isccc_sexpr_t *alist, *value;
    548  1.3  christos 	bool first_tag;
    549  1.1  christos 	unsigned char *checksum_rstart;
    550  1.1  christos 
    551  1.1  christos 	REQUIRE(alistp != NULL && *alistp == NULL);
    552  1.1  christos 
    553  1.8  christos 	if (depth > ISCCC_MAXDEPTH) {
    554  1.9  christos 		return ISCCC_R_MAXDEPTH;
    555  1.8  christos 	}
    556  1.8  christos 
    557  1.1  christos 	checksum_rstart = NULL;
    558  1.3  christos 	first_tag = true;
    559  1.1  christos 	alist = isccc_alist_create();
    560  1.4  christos 	if (alist == NULL) {
    561  1.9  christos 		return ISC_R_NOMEMORY;
    562  1.4  christos 	}
    563  1.1  christos 
    564  1.1  christos 	while (!REGION_EMPTY(*source)) {
    565  1.1  christos 		GET8(len, source->rstart);
    566  1.1  christos 		if (REGION_SIZE(*source) < len) {
    567  1.1  christos 			result = ISC_R_UNEXPECTEDEND;
    568  1.1  christos 			goto bad;
    569  1.1  christos 		}
    570  1.1  christos 		GET_MEM(key, len, source->rstart);
    571  1.4  christos 		key[len] = '\0'; /* Ensure NUL termination. */
    572  1.1  christos 		value = NULL;
    573  1.8  christos 		result = value_fromwire(source, depth + 1, &value);
    574  1.4  christos 		if (result != ISC_R_SUCCESS) {
    575  1.1  christos 			goto bad;
    576  1.4  christos 		}
    577  1.1  christos 		if (isccc_alist_define(alist, key, value) == NULL) {
    578  1.1  christos 			result = ISC_R_NOMEMORY;
    579  1.1  christos 			goto bad;
    580  1.1  christos 		}
    581  1.4  christos 		if (first_tag && secret != NULL && strcmp(key, "_auth") == 0) {
    582  1.1  christos 			checksum_rstart = source->rstart;
    583  1.4  christos 		}
    584  1.3  christos 		first_tag = false;
    585  1.1  christos 	}
    586  1.1  christos 
    587  1.1  christos 	if (secret != NULL) {
    588  1.4  christos 		if (checksum_rstart != NULL) {
    589  1.4  christos 			result = verify(
    590  1.4  christos 				alist, checksum_rstart,
    591  1.4  christos 				(unsigned int)(source->rend - checksum_rstart),
    592  1.4  christos 				algorithm, secret);
    593  1.4  christos 		} else {
    594  1.1  christos 			result = ISCCC_R_BADAUTH;
    595  1.4  christos 		}
    596  1.4  christos 	} else {
    597  1.1  christos 		result = ISC_R_SUCCESS;
    598  1.4  christos 	}
    599  1.1  christos 
    600  1.4  christos bad:
    601  1.4  christos 	if (result == ISC_R_SUCCESS) {
    602  1.1  christos 		*alistp = alist;
    603  1.4  christos 	} else {
    604  1.1  christos 		isccc_sexpr_free(&alist);
    605  1.4  christos 	}
    606  1.1  christos 
    607  1.9  christos 	return result;
    608  1.1  christos }
    609  1.1  christos 
    610  1.1  christos static isc_result_t
    611  1.8  christos list_fromwire(isccc_region_t *source, unsigned int depth,
    612  1.8  christos 	      isccc_sexpr_t **listp) {
    613  1.1  christos 	isccc_sexpr_t *list, *value;
    614  1.1  christos 	isc_result_t result;
    615  1.1  christos 
    616  1.8  christos 	if (depth > ISCCC_MAXDEPTH) {
    617  1.9  christos 		return ISCCC_R_MAXDEPTH;
    618  1.8  christos 	}
    619  1.8  christos 
    620  1.1  christos 	list = NULL;
    621  1.1  christos 	while (!REGION_EMPTY(*source)) {
    622  1.1  christos 		value = NULL;
    623  1.8  christos 		result = value_fromwire(source, depth + 1, &value);
    624  1.1  christos 		if (result != ISC_R_SUCCESS) {
    625  1.1  christos 			isccc_sexpr_free(&list);
    626  1.9  christos 			return result;
    627  1.1  christos 		}
    628  1.1  christos 		if (isccc_sexpr_addtolist(&list, value) == NULL) {
    629  1.1  christos 			isccc_sexpr_free(&value);
    630  1.1  christos 			isccc_sexpr_free(&list);
    631  1.9  christos 			return ISC_R_NOMEMORY;
    632  1.1  christos 		}
    633  1.1  christos 	}
    634  1.1  christos 
    635  1.1  christos 	*listp = list;
    636  1.1  christos 
    637  1.9  christos 	return ISC_R_SUCCESS;
    638  1.1  christos }
    639  1.1  christos 
    640  1.1  christos isc_result_t
    641  1.1  christos isccc_cc_fromwire(isccc_region_t *source, isccc_sexpr_t **alistp,
    642  1.4  christos 		  uint32_t algorithm, isccc_region_t *secret) {
    643  1.1  christos 	unsigned int size;
    644  1.3  christos 	uint32_t version;
    645  1.1  christos 
    646  1.1  christos 	size = REGION_SIZE(*source);
    647  1.4  christos 	if (size < 4) {
    648  1.9  christos 		return ISC_R_UNEXPECTEDEND;
    649  1.4  christos 	}
    650  1.1  christos 	GET32(version, source->rstart);
    651  1.4  christos 	if (version != 1) {
    652  1.9  christos 		return ISCCC_R_UNKNOWNVERSION;
    653  1.4  christos 	}
    654  1.1  christos 
    655  1.9  christos 	return table_fromwire(source, secret, algorithm, 0, alistp);
    656  1.1  christos }
    657  1.1  christos 
    658  1.1  christos static isc_result_t
    659  1.3  christos createmessage(uint32_t version, const char *from, const char *to,
    660  1.4  christos 	      uint32_t serial, isccc_time_t now, isccc_time_t expires,
    661  1.4  christos 	      isccc_sexpr_t **alistp, bool want_expires) {
    662  1.1  christos 	isccc_sexpr_t *alist, *_ctrl, *_data;
    663  1.1  christos 	isc_result_t result;
    664  1.1  christos 
    665  1.1  christos 	REQUIRE(alistp != NULL && *alistp == NULL);
    666  1.1  christos 
    667  1.4  christos 	if (version != 1) {
    668  1.9  christos 		return ISCCC_R_UNKNOWNVERSION;
    669  1.4  christos 	}
    670  1.1  christos 
    671  1.1  christos 	alist = isccc_alist_create();
    672  1.4  christos 	if (alist == NULL) {
    673  1.9  christos 		return ISC_R_NOMEMORY;
    674  1.4  christos 	}
    675  1.1  christos 
    676  1.1  christos 	result = ISC_R_NOMEMORY;
    677  1.1  christos 
    678  1.1  christos 	_ctrl = isccc_alist_create();
    679  1.4  christos 	if (_ctrl == NULL) {
    680  1.1  christos 		goto bad;
    681  1.4  christos 	}
    682  1.1  christos 	if (isccc_alist_define(alist, "_ctrl", _ctrl) == NULL) {
    683  1.1  christos 		isccc_sexpr_free(&_ctrl);
    684  1.1  christos 		goto bad;
    685  1.1  christos 	}
    686  1.1  christos 
    687  1.1  christos 	_data = isccc_alist_create();
    688  1.4  christos 	if (_data == NULL) {
    689  1.1  christos 		goto bad;
    690  1.4  christos 	}
    691  1.1  christos 	if (isccc_alist_define(alist, "_data", _data) == NULL) {
    692  1.1  christos 		isccc_sexpr_free(&_data);
    693  1.1  christos 		goto bad;
    694  1.1  christos 	}
    695  1.1  christos 
    696  1.1  christos 	if (isccc_cc_defineuint32(_ctrl, "_ser", serial) == NULL ||
    697  1.1  christos 	    isccc_cc_defineuint32(_ctrl, "_tim", now) == NULL ||
    698  1.1  christos 	    (want_expires &&
    699  1.1  christos 	     isccc_cc_defineuint32(_ctrl, "_exp", expires) == NULL))
    700  1.4  christos 	{
    701  1.1  christos 		goto bad;
    702  1.4  christos 	}
    703  1.4  christos 	if (from != NULL && isccc_cc_definestring(_ctrl, "_frm", from) == NULL)
    704  1.4  christos 	{
    705  1.1  christos 		goto bad;
    706  1.4  christos 	}
    707  1.4  christos 	if (to != NULL && isccc_cc_definestring(_ctrl, "_to", to) == NULL) {
    708  1.1  christos 		goto bad;
    709  1.4  christos 	}
    710  1.1  christos 
    711  1.1  christos 	*alistp = alist;
    712  1.1  christos 
    713  1.9  christos 	return ISC_R_SUCCESS;
    714  1.1  christos 
    715  1.4  christos bad:
    716  1.1  christos 	isccc_sexpr_free(&alist);
    717  1.1  christos 
    718  1.9  christos 	return result;
    719  1.1  christos }
    720  1.1  christos 
    721  1.1  christos isc_result_t
    722  1.3  christos isccc_cc_createmessage(uint32_t version, const char *from, const char *to,
    723  1.4  christos 		       uint32_t serial, isccc_time_t now, isccc_time_t expires,
    724  1.4  christos 		       isccc_sexpr_t **alistp) {
    725  1.9  christos 	return createmessage(version, from, to, serial, now, expires, alistp,
    726  1.9  christos 			     true);
    727  1.1  christos }
    728  1.1  christos 
    729  1.1  christos isc_result_t
    730  1.4  christos isccc_cc_createack(isccc_sexpr_t *message, bool ok, isccc_sexpr_t **ackp) {
    731  1.1  christos 	char *_frm, *_to;
    732  1.3  christos 	uint32_t serial;
    733  1.1  christos 	isccc_sexpr_t *ack, *_ctrl;
    734  1.1  christos 	isc_result_t result;
    735  1.1  christos 	isccc_time_t t;
    736  1.1  christos 
    737  1.1  christos 	REQUIRE(ackp != NULL && *ackp == NULL);
    738  1.1  christos 
    739  1.1  christos 	_ctrl = isccc_alist_lookup(message, "_ctrl");
    740  1.1  christos 	if (!isccc_alist_alistp(_ctrl) ||
    741  1.1  christos 	    isccc_cc_lookupuint32(_ctrl, "_ser", &serial) != ISC_R_SUCCESS ||
    742  1.1  christos 	    isccc_cc_lookupuint32(_ctrl, "_tim", &t) != ISC_R_SUCCESS)
    743  1.4  christos 	{
    744  1.9  christos 		return ISC_R_FAILURE;
    745  1.4  christos 	}
    746  1.1  christos 	/*
    747  1.1  christos 	 * _frm and _to are optional.
    748  1.1  christos 	 */
    749  1.1  christos 	_frm = NULL;
    750  1.1  christos 	(void)isccc_cc_lookupstring(_ctrl, "_frm", &_frm);
    751  1.1  christos 	_to = NULL;
    752  1.1  christos 	(void)isccc_cc_lookupstring(_ctrl, "_to", &_to);
    753  1.1  christos 	/*
    754  1.1  christos 	 * Create the ack.
    755  1.1  christos 	 */
    756  1.1  christos 	ack = NULL;
    757  1.3  christos 	result = createmessage(1, _to, _frm, serial, t, 0, &ack, false);
    758  1.4  christos 	if (result != ISC_R_SUCCESS) {
    759  1.9  christos 		return result;
    760  1.4  christos 	}
    761  1.1  christos 
    762  1.1  christos 	_ctrl = isccc_alist_lookup(ack, "_ctrl");
    763  1.1  christos 	if (_ctrl == NULL) {
    764  1.1  christos 		result = ISC_R_FAILURE;
    765  1.1  christos 		goto bad;
    766  1.1  christos 	}
    767  1.1  christos 	if (isccc_cc_definestring(ack, "_ack", (ok) ? "1" : "0") == NULL) {
    768  1.1  christos 		result = ISC_R_NOMEMORY;
    769  1.1  christos 		goto bad;
    770  1.1  christos 	}
    771  1.1  christos 
    772  1.1  christos 	*ackp = ack;
    773  1.1  christos 
    774  1.9  christos 	return ISC_R_SUCCESS;
    775  1.1  christos 
    776  1.4  christos bad:
    777  1.1  christos 	isccc_sexpr_free(&ack);
    778  1.1  christos 
    779  1.9  christos 	return result;
    780  1.1  christos }
    781  1.1  christos 
    782  1.3  christos bool
    783  1.1  christos isccc_cc_isack(isccc_sexpr_t *message) {
    784  1.1  christos 	isccc_sexpr_t *_ctrl;
    785  1.1  christos 
    786  1.1  christos 	_ctrl = isccc_alist_lookup(message, "_ctrl");
    787  1.4  christos 	if (!isccc_alist_alistp(_ctrl)) {
    788  1.9  christos 		return false;
    789  1.4  christos 	}
    790  1.4  christos 	if (isccc_cc_lookupstring(_ctrl, "_ack", NULL) == ISC_R_SUCCESS) {
    791  1.9  christos 		return true;
    792  1.4  christos 	}
    793  1.9  christos 	return false;
    794  1.1  christos }
    795  1.1  christos 
    796  1.3  christos bool
    797  1.1  christos isccc_cc_isreply(isccc_sexpr_t *message) {
    798  1.1  christos 	isccc_sexpr_t *_ctrl;
    799  1.1  christos 
    800  1.1  christos 	_ctrl = isccc_alist_lookup(message, "_ctrl");
    801  1.4  christos 	if (!isccc_alist_alistp(_ctrl)) {
    802  1.9  christos 		return false;
    803  1.4  christos 	}
    804  1.4  christos 	if (isccc_cc_lookupstring(_ctrl, "_rpl", NULL) == ISC_R_SUCCESS) {
    805  1.9  christos 		return true;
    806  1.4  christos 	}
    807  1.9  christos 	return false;
    808  1.1  christos }
    809  1.1  christos 
    810  1.1  christos isc_result_t
    811  1.1  christos isccc_cc_createresponse(isccc_sexpr_t *message, isccc_time_t now,
    812  1.4  christos 			isccc_time_t expires, isccc_sexpr_t **alistp) {
    813  1.1  christos 	char *_frm, *_to, *type = NULL;
    814  1.3  christos 	uint32_t serial;
    815  1.1  christos 	isccc_sexpr_t *alist, *_ctrl, *_data;
    816  1.1  christos 	isc_result_t result;
    817  1.1  christos 
    818  1.1  christos 	REQUIRE(alistp != NULL && *alistp == NULL);
    819  1.1  christos 
    820  1.1  christos 	_ctrl = isccc_alist_lookup(message, "_ctrl");
    821  1.1  christos 	_data = isccc_alist_lookup(message, "_data");
    822  1.1  christos 	if (!isccc_alist_alistp(_ctrl) || !isccc_alist_alistp(_data) ||
    823  1.1  christos 	    isccc_cc_lookupuint32(_ctrl, "_ser", &serial) != ISC_R_SUCCESS ||
    824  1.1  christos 	    isccc_cc_lookupstring(_data, "type", &type) != ISC_R_SUCCESS)
    825  1.4  christos 	{
    826  1.9  christos 		return ISC_R_FAILURE;
    827  1.4  christos 	}
    828  1.1  christos 	/*
    829  1.1  christos 	 * _frm and _to are optional.
    830  1.1  christos 	 */
    831  1.1  christos 	_frm = NULL;
    832  1.1  christos 	(void)isccc_cc_lookupstring(_ctrl, "_frm", &_frm);
    833  1.1  christos 	_to = NULL;
    834  1.1  christos 	(void)isccc_cc_lookupstring(_ctrl, "_to", &_to);
    835  1.1  christos 	/*
    836  1.1  christos 	 * Create the response.
    837  1.1  christos 	 */
    838  1.1  christos 	alist = NULL;
    839  1.1  christos 	result = isccc_cc_createmessage(1, _to, _frm, serial, now, expires,
    840  1.4  christos 					&alist);
    841  1.4  christos 	if (result != ISC_R_SUCCESS) {
    842  1.9  christos 		return result;
    843  1.4  christos 	}
    844  1.1  christos 
    845  1.1  christos 	_ctrl = isccc_alist_lookup(alist, "_ctrl");
    846  1.1  christos 	if (_ctrl == NULL) {
    847  1.1  christos 		result = ISC_R_FAILURE;
    848  1.1  christos 		goto bad;
    849  1.1  christos 	}
    850  1.1  christos 
    851  1.1  christos 	_data = isccc_alist_lookup(alist, "_data");
    852  1.1  christos 	if (_data == NULL) {
    853  1.1  christos 		result = ISC_R_FAILURE;
    854  1.1  christos 		goto bad;
    855  1.1  christos 	}
    856  1.1  christos 
    857  1.1  christos 	if (isccc_cc_definestring(_ctrl, "_rpl", "1") == NULL ||
    858  1.1  christos 	    isccc_cc_definestring(_data, "type", type) == NULL)
    859  1.1  christos 	{
    860  1.1  christos 		result = ISC_R_NOMEMORY;
    861  1.1  christos 		goto bad;
    862  1.1  christos 	}
    863  1.1  christos 
    864  1.1  christos 	*alistp = alist;
    865  1.1  christos 
    866  1.9  christos 	return ISC_R_SUCCESS;
    867  1.1  christos 
    868  1.4  christos bad:
    869  1.1  christos 	isccc_sexpr_free(&alist);
    870  1.9  christos 	return result;
    871  1.1  christos }
    872  1.1  christos 
    873  1.1  christos isccc_sexpr_t *
    874  1.1  christos isccc_cc_definestring(isccc_sexpr_t *alist, const char *key, const char *str) {
    875  1.1  christos 	size_t len;
    876  1.1  christos 	isccc_region_t r;
    877  1.1  christos 
    878  1.1  christos 	len = strlen(str);
    879  1.9  christos 	r.rstart = UNCONST(str);
    880  1.1  christos 	r.rend = r.rstart + len;
    881  1.1  christos 
    882  1.9  christos 	return isccc_alist_definebinary(alist, key, &r);
    883  1.1  christos }
    884  1.1  christos 
    885  1.1  christos isccc_sexpr_t *
    886  1.3  christos isccc_cc_defineuint32(isccc_sexpr_t *alist, const char *key, uint32_t i) {
    887  1.1  christos 	char b[100];
    888  1.1  christos 	size_t len;
    889  1.1  christos 	isccc_region_t r;
    890  1.1  christos 
    891  1.1  christos 	snprintf(b, sizeof(b), "%u", i);
    892  1.1  christos 	len = strlen(b);
    893  1.1  christos 	r.rstart = (unsigned char *)b;
    894  1.1  christos 	r.rend = (unsigned char *)b + len;
    895  1.1  christos 
    896  1.9  christos 	return isccc_alist_definebinary(alist, key, &r);
    897  1.1  christos }
    898  1.1  christos 
    899  1.1  christos isc_result_t
    900  1.1  christos isccc_cc_lookupstring(isccc_sexpr_t *alist, const char *key, char **strp) {
    901  1.1  christos 	isccc_sexpr_t *kv, *v;
    902  1.1  christos 
    903  1.1  christos 	REQUIRE(strp == NULL || *strp == NULL);
    904  1.1  christos 
    905  1.1  christos 	kv = isccc_alist_assq(alist, key);
    906  1.1  christos 	if (kv != NULL) {
    907  1.1  christos 		v = ISCCC_SEXPR_CDR(kv);
    908  1.1  christos 		if (isccc_sexpr_binaryp(v)) {
    909  1.4  christos 			if (strp != NULL) {
    910  1.1  christos 				*strp = isccc_sexpr_tostring(v);
    911  1.4  christos 			}
    912  1.9  christos 			return ISC_R_SUCCESS;
    913  1.4  christos 		} else {
    914  1.9  christos 			return ISC_R_EXISTS;
    915  1.4  christos 		}
    916  1.1  christos 	}
    917  1.1  christos 
    918  1.9  christos 	return ISC_R_NOTFOUND;
    919  1.1  christos }
    920  1.1  christos 
    921  1.1  christos isc_result_t
    922  1.4  christos isccc_cc_lookupuint32(isccc_sexpr_t *alist, const char *key, uint32_t *uintp) {
    923  1.1  christos 	isccc_sexpr_t *kv, *v;
    924  1.1  christos 
    925  1.1  christos 	kv = isccc_alist_assq(alist, key);
    926  1.1  christos 	if (kv != NULL) {
    927  1.1  christos 		v = ISCCC_SEXPR_CDR(kv);
    928  1.1  christos 		if (isccc_sexpr_binaryp(v)) {
    929  1.4  christos 			if (uintp != NULL) {
    930  1.4  christos 				*uintp = (uint32_t)strtoul(
    931  1.4  christos 					isccc_sexpr_tostring(v), NULL, 10);
    932  1.4  christos 			}
    933  1.9  christos 			return ISC_R_SUCCESS;
    934  1.4  christos 		} else {
    935  1.9  christos 			return ISC_R_EXISTS;
    936  1.4  christos 		}
    937  1.1  christos 	}
    938  1.1  christos 
    939  1.9  christos 	return ISC_R_NOTFOUND;
    940  1.1  christos }
    941  1.1  christos 
    942  1.1  christos static void
    943  1.1  christos symtab_undefine(char *key, unsigned int type, isccc_symvalue_t value,
    944  1.4  christos 		void *arg) {
    945  1.1  christos 	UNUSED(type);
    946  1.1  christos 	UNUSED(value);
    947  1.1  christos 	UNUSED(arg);
    948  1.1  christos 
    949  1.1  christos 	free(key);
    950  1.1  christos }
    951  1.1  christos 
    952  1.3  christos static bool
    953  1.1  christos symtab_clean(char *key, unsigned int type, isccc_symvalue_t value, void *arg) {
    954  1.1  christos 	isccc_time_t *now;
    955  1.1  christos 
    956  1.1  christos 	UNUSED(key);
    957  1.1  christos 	UNUSED(type);
    958  1.1  christos 
    959  1.1  christos 	now = arg;
    960  1.1  christos 
    961  1.4  christos 	if (*now < value.as_uinteger) {
    962  1.9  christos 		return false;
    963  1.4  christos 	}
    964  1.4  christos 	if ((*now - value.as_uinteger) < DUP_LIFETIME) {
    965  1.9  christos 		return false;
    966  1.4  christos 	}
    967  1.9  christos 	return true;
    968  1.1  christos }
    969  1.1  christos 
    970  1.1  christos isc_result_t
    971  1.1  christos isccc_cc_createsymtab(isccc_symtab_t **symtabp) {
    972  1.9  christos 	return isccc_symtab_create(11897, symtab_undefine, NULL, false,
    973  1.9  christos 				   symtabp);
    974  1.1  christos }
    975  1.1  christos 
    976  1.1  christos void
    977  1.1  christos isccc_cc_cleansymtab(isccc_symtab_t *symtab, isccc_time_t now) {
    978  1.1  christos 	isccc_symtab_foreach(symtab, symtab_clean, &now);
    979  1.1  christos }
    980  1.1  christos 
    981  1.3  christos static bool
    982  1.1  christos has_whitespace(const char *str) {
    983  1.1  christos 	char c;
    984  1.1  christos 
    985  1.4  christos 	if (str == NULL) {
    986  1.9  christos 		return false;
    987  1.4  christos 	}
    988  1.1  christos 	while ((c = *str++) != '\0') {
    989  1.4  christos 		if (c == ' ' || c == '\t' || c == '\n') {
    990  1.9  christos 			return true;
    991  1.4  christos 		}
    992  1.1  christos 	}
    993  1.9  christos 	return false;
    994  1.1  christos }
    995  1.1  christos 
    996  1.1  christos isc_result_t
    997  1.1  christos isccc_cc_checkdup(isccc_symtab_t *symtab, isccc_sexpr_t *message,
    998  1.4  christos 		  isccc_time_t now) {
    999  1.1  christos 	const char *_frm;
   1000  1.1  christos 	const char *_to;
   1001  1.1  christos 	char *_ser = NULL, *_tim = NULL, *tmp;
   1002  1.1  christos 	isc_result_t result;
   1003  1.1  christos 	char *key;
   1004  1.1  christos 	size_t len;
   1005  1.1  christos 	isccc_symvalue_t value;
   1006  1.1  christos 	isccc_sexpr_t *_ctrl;
   1007  1.1  christos 
   1008  1.1  christos 	_ctrl = isccc_alist_lookup(message, "_ctrl");
   1009  1.1  christos 	if (!isccc_alist_alistp(_ctrl) ||
   1010  1.1  christos 	    isccc_cc_lookupstring(_ctrl, "_ser", &_ser) != ISC_R_SUCCESS ||
   1011  1.1  christos 	    isccc_cc_lookupstring(_ctrl, "_tim", &_tim) != ISC_R_SUCCESS)
   1012  1.4  christos 	{
   1013  1.9  christos 		return ISC_R_FAILURE;
   1014  1.4  christos 	}
   1015  1.1  christos 
   1016  1.1  christos 	INSIST(_ser != NULL);
   1017  1.1  christos 	INSIST(_tim != NULL);
   1018  1.1  christos 
   1019  1.1  christos 	/*
   1020  1.1  christos 	 * _frm and _to are optional.
   1021  1.1  christos 	 */
   1022  1.1  christos 	tmp = NULL;
   1023  1.4  christos 	if (isccc_cc_lookupstring(_ctrl, "_frm", &tmp) != ISC_R_SUCCESS) {
   1024  1.1  christos 		_frm = "";
   1025  1.4  christos 	} else {
   1026  1.1  christos 		_frm = tmp;
   1027  1.9  christos 		INSIST(_frm != NULL);
   1028  1.4  christos 	}
   1029  1.1  christos 	tmp = NULL;
   1030  1.4  christos 	if (isccc_cc_lookupstring(_ctrl, "_to", &tmp) != ISC_R_SUCCESS) {
   1031  1.1  christos 		_to = "";
   1032  1.4  christos 	} else {
   1033  1.1  christos 		_to = tmp;
   1034  1.9  christos 		INSIST(_to != NULL);
   1035  1.4  christos 	}
   1036  1.1  christos 	/*
   1037  1.1  christos 	 * Ensure there is no newline in any of the strings.  This is so
   1038  1.1  christos 	 * we can write them to a file later.
   1039  1.1  christos 	 */
   1040  1.1  christos 	if (has_whitespace(_frm) || has_whitespace(_to) ||
   1041  1.1  christos 	    has_whitespace(_ser) || has_whitespace(_tim))
   1042  1.4  christos 	{
   1043  1.9  christos 		return ISC_R_FAILURE;
   1044  1.4  christos 	}
   1045  1.1  christos 	len = strlen(_frm) + strlen(_to) + strlen(_ser) + strlen(_tim) + 4;
   1046  1.1  christos 	key = malloc(len);
   1047  1.4  christos 	if (key == NULL) {
   1048  1.9  christos 		return ISC_R_NOMEMORY;
   1049  1.4  christos 	}
   1050  1.1  christos 	snprintf(key, len, "%s;%s;%s;%s", _frm, _to, _ser, _tim);
   1051  1.1  christos 	value.as_uinteger = now;
   1052  1.1  christos 	result = isccc_symtab_define(symtab, key, ISCCC_SYMTYPE_CCDUP, value,
   1053  1.4  christos 				     isccc_symexists_reject);
   1054  1.1  christos 	if (result != ISC_R_SUCCESS) {
   1055  1.1  christos 		free(key);
   1056  1.9  christos 		return result;
   1057  1.1  christos 	}
   1058  1.1  christos 
   1059  1.9  christos 	return ISC_R_SUCCESS;
   1060  1.1  christos }
   1061