1/* 2 * Portions Copyright (C) 2004-2007, 2012 Internet Systems Consortium, Inc. ("ISC") 3 * Portions Copyright (C) 2001-2003 Internet Software Consortium. 4 * 5 * Permission to use, copy, modify, and/or distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NOMINUM DISCLAIMS ALL 10 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES 11 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY 12 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 * 17 * Portions Copyright (C) 2001 Nominum, Inc. 18 * 19 * Permission to use, copy, modify, and/or distribute this software for any 20 * purpose with or without fee is hereby granted, provided that the above 21 * copyright notice and this permission notice appear in all copies. 22 * 23 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NOMINUM DISCLAIMS ALL 24 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES 25 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY 26 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 27 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 28 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 29 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 30 */ 31 32/* $Id: cc.c,v 1.18 2007/08/28 07:20:43 tbox Exp $ */ 33 34/*! \file */ 35 36#include <config.h> 37 38#include <stdio.h> 39#include <string.h> 40#include <errno.h> 41 42#include <isc/assertions.h> 43#include <isc/hmacmd5.h> 44#include <isc/print.h> 45#include <isc/stdlib.h> 46 47#include <isccc/alist.h> 48#include <isccc/base64.h> 49#include <isccc/cc.h> 50#include <isccc/result.h> 51#include <isccc/sexpr.h> 52#include <isccc/symtab.h> 53#include <isccc/symtype.h> 54#include <isccc/util.h> 55 56#define MAX_TAGS 256 57#define DUP_LIFETIME 900 58 59typedef isccc_sexpr_t *sexpr_ptr; 60 61static unsigned char auth_hmd5[] = { 62 0x05, 0x5f, 0x61, 0x75, 0x74, 0x68, /*%< len + _auth */ 63 ISCCC_CCMSGTYPE_TABLE, /*%< message type */ 64 0x00, 0x00, 0x00, 0x20, /*%< length == 32 */ 65 0x04, 0x68, 0x6d, 0x64, 0x35, /*%< len + hmd5 */ 66 ISCCC_CCMSGTYPE_BINARYDATA, /*%< message type */ 67 0x00, 0x00, 0x00, 0x16, /*%< length == 22 */ 68 /* 69 * The base64 encoding of one of our HMAC-MD5 signatures is 70 * 22 bytes. 71 */ 72 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 73 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 74 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 75}; 76 77#define HMD5_OFFSET 21 /*%< 21 = 6 + 1 + 4 + 5 + 1 + 4 */ 78#define HMD5_LENGTH 22 79 80static isc_result_t 81table_towire(isccc_sexpr_t *alist, isccc_region_t *target); 82 83static isc_result_t 84list_towire(isccc_sexpr_t *alist, isccc_region_t *target); 85 86static isc_result_t 87value_towire(isccc_sexpr_t *elt, isccc_region_t *target) 88{ 89 size_t len; 90 unsigned char *lenp; 91 isccc_region_t *vr; 92 isc_result_t result; 93 94 if (isccc_sexpr_binaryp(elt)) { 95 vr = isccc_sexpr_tobinary(elt); 96 len = REGION_SIZE(*vr); 97 if (REGION_SIZE(*target) < 1 + 4 + len) 98 return (ISC_R_NOSPACE); 99 PUT8(ISCCC_CCMSGTYPE_BINARYDATA, target->rstart); 100 PUT32(len, target->rstart); 101 if (REGION_SIZE(*target) < len) 102 return (ISC_R_NOSPACE); 103 PUT_MEM(vr->rstart, len, target->rstart); 104 } else if (isccc_alist_alistp(elt)) { 105 if (REGION_SIZE(*target) < 1 + 4) 106 return (ISC_R_NOSPACE); 107 PUT8(ISCCC_CCMSGTYPE_TABLE, target->rstart); 108 /* 109 * Emit a placeholder length. 110 */ 111 lenp = target->rstart; 112 PUT32(0, target->rstart); 113 /* 114 * Emit the table. 115 */ 116 result = table_towire(elt, target); 117 if (result != ISC_R_SUCCESS) 118 return (result); 119 len = (size_t)(target->rstart - lenp); 120 /* 121 * 'len' is 4 bytes too big, since it counts 122 * the placeholder length too. Adjust and 123 * emit. 124 */ 125 INSIST(len >= 4U); 126 len -= 4; 127 PUT32(len, lenp); 128 } else if (isccc_sexpr_listp(elt)) { 129 if (REGION_SIZE(*target) < 1 + 4) 130 return (ISC_R_NOSPACE); 131 PUT8(ISCCC_CCMSGTYPE_LIST, target->rstart); 132 /* 133 * Emit a placeholder length and count. 134 */ 135 lenp = target->rstart; 136 PUT32(0, target->rstart); 137 /* 138 * Emit the list. 139 */ 140 result = list_towire(elt, target); 141 if (result != ISC_R_SUCCESS) 142 return (result); 143 len = (size_t)(target->rstart - lenp); 144 /* 145 * 'len' is 4 bytes too big, since it counts 146 * the placeholder length. Adjust and emit. 147 */ 148 INSIST(len >= 4U); 149 len -= 4; 150 PUT32(len, lenp); 151 } 152 153 return (ISC_R_SUCCESS); 154} 155 156static isc_result_t 157table_towire(isccc_sexpr_t *alist, isccc_region_t *target) 158{ 159 isccc_sexpr_t *kv, *elt, *k, *v; 160 char *ks; 161 isc_result_t result; 162 size_t len; 163 164 for (elt = isccc_alist_first(alist); 165 elt != NULL; 166 elt = ISCCC_SEXPR_CDR(elt)) { 167 kv = ISCCC_SEXPR_CAR(elt); 168 k = ISCCC_SEXPR_CAR(kv); 169 ks = isccc_sexpr_tostring(k); 170 v = ISCCC_SEXPR_CDR(kv); 171 len = strlen(ks); 172 INSIST(len <= 255U); 173 /* 174 * Emit the key name. 175 */ 176 if (REGION_SIZE(*target) < 1 + len) 177 return (ISC_R_NOSPACE); 178 PUT8(len, target->rstart); 179 PUT_MEM(ks, len, target->rstart); 180 /* 181 * Emit the value. 182 */ 183 result = value_towire(v, target); 184 if (result != ISC_R_SUCCESS) 185 return (result); 186 } 187 188 return (ISC_R_SUCCESS); 189} 190 191static isc_result_t 192list_towire(isccc_sexpr_t *list, isccc_region_t *target) 193{ 194 isc_result_t result; 195 196 while (list != NULL) { 197 result = value_towire(ISCCC_SEXPR_CAR(list), target); 198 if (result != ISC_R_SUCCESS) 199 return (result); 200 list = ISCCC_SEXPR_CDR(list); 201 } 202 203 return (ISC_R_SUCCESS); 204} 205 206static isc_result_t 207sign(unsigned char *data, unsigned int length, unsigned char *hmd5, 208 isccc_region_t *secret) 209{ 210 isc_hmacmd5_t ctx; 211 isc_result_t result; 212 isccc_region_t source, target; 213 unsigned char digest[ISC_MD5_DIGESTLENGTH]; 214 unsigned char digestb64[ISC_MD5_DIGESTLENGTH * 4]; 215 216 isc_hmacmd5_init(&ctx, secret->rstart, REGION_SIZE(*secret)); 217 isc_hmacmd5_update(&ctx, data, length); 218 isc_hmacmd5_sign(&ctx, digest); 219 source.rstart = digest; 220 source.rend = digest + ISC_MD5_DIGESTLENGTH; 221 target.rstart = digestb64; 222 target.rend = digestb64 + ISC_MD5_DIGESTLENGTH * 4; 223 result = isccc_base64_encode(&source, 64, "", &target); 224 if (result != ISC_R_SUCCESS) 225 return (result); 226 PUT_MEM(digestb64, HMD5_LENGTH, hmd5); 227 228 return (ISC_R_SUCCESS); 229} 230 231isc_result_t 232isccc_cc_towire(isccc_sexpr_t *alist, isccc_region_t *target, 233 isccc_region_t *secret) 234{ 235 unsigned char *hmd5_rstart, *signed_rstart; 236 isc_result_t result; 237 238 if (REGION_SIZE(*target) < 4 + sizeof(auth_hmd5)) 239 return (ISC_R_NOSPACE); 240 /* 241 * Emit protocol version. 242 */ 243 PUT32(1, target->rstart); 244 if (secret != NULL) { 245 /* 246 * Emit _auth section with zeroed HMAC-MD5 signature. 247 * We'll replace the zeros with the real signature once 248 * we know what it is. 249 */ 250 hmd5_rstart = target->rstart + HMD5_OFFSET; 251 PUT_MEM(auth_hmd5, sizeof(auth_hmd5), target->rstart); 252 } else 253 hmd5_rstart = NULL; 254 signed_rstart = target->rstart; 255 /* 256 * Delete any existing _auth section so that we don't try 257 * to encode it. 258 */ 259 isccc_alist_delete(alist, "_auth"); 260 /* 261 * Emit the message. 262 */ 263 result = table_towire(alist, target); 264 if (result != ISC_R_SUCCESS) 265 return (result); 266 if (secret != NULL) 267 return (sign(signed_rstart, (target->rstart - signed_rstart), 268 hmd5_rstart, secret)); 269 return (ISC_R_SUCCESS); 270} 271 272static isc_result_t 273verify(isccc_sexpr_t *alist, unsigned char *data, unsigned int length, 274 isccc_region_t *secret) 275{ 276 isc_hmacmd5_t ctx; 277 isccc_region_t source; 278 isccc_region_t target; 279 isc_result_t result; 280 isccc_sexpr_t *_auth, *hmd5; 281 unsigned char digest[ISC_MD5_DIGESTLENGTH]; 282 unsigned char digestb64[ISC_MD5_DIGESTLENGTH * 4]; 283 284 /* 285 * Extract digest. 286 */ 287 _auth = isccc_alist_lookup(alist, "_auth"); 288 if (_auth == NULL) 289 return (ISC_R_FAILURE); 290 hmd5 = isccc_alist_lookup(_auth, "hmd5"); 291 if (hmd5 == NULL) 292 return (ISC_R_FAILURE); 293 /* 294 * Compute digest. 295 */ 296 isc_hmacmd5_init(&ctx, secret->rstart, REGION_SIZE(*secret)); 297 isc_hmacmd5_update(&ctx, data, length); 298 isc_hmacmd5_sign(&ctx, digest); 299 source.rstart = digest; 300 source.rend = digest + ISC_MD5_DIGESTLENGTH; 301 target.rstart = digestb64; 302 target.rend = digestb64 + ISC_MD5_DIGESTLENGTH * 4; 303 result = isccc_base64_encode(&source, 64, "", &target); 304 if (result != ISC_R_SUCCESS) 305 return (result); 306 /* 307 * Strip trailing == and NUL terminate target. 308 */ 309 target.rstart -= 2; 310 *target.rstart++ = '\0'; 311 /* 312 * Verify. 313 */ 314 if (strcmp((char *)digestb64, isccc_sexpr_tostring(hmd5)) != 0) 315 return (ISCCC_R_BADAUTH); 316 317 return (ISC_R_SUCCESS); 318} 319 320static isc_result_t 321table_fromwire(isccc_region_t *source, isccc_region_t *secret, 322 isccc_sexpr_t **alistp); 323 324static isc_result_t 325list_fromwire(isccc_region_t *source, isccc_sexpr_t **listp); 326 327static isc_result_t 328value_fromwire(isccc_region_t *source, isccc_sexpr_t **valuep) 329{ 330 unsigned int msgtype; 331 isc_uint32_t len; 332 isccc_sexpr_t *value; 333 isccc_region_t active; 334 isc_result_t result; 335 336 if (REGION_SIZE(*source) < 1 + 4) 337 return (ISC_R_UNEXPECTEDEND); 338 GET8(msgtype, source->rstart); 339 GET32(len, source->rstart); 340 if (REGION_SIZE(*source) < len) 341 return (ISC_R_UNEXPECTEDEND); 342 active.rstart = source->rstart; 343 active.rend = active.rstart + len; 344 source->rstart = active.rend; 345 if (msgtype == ISCCC_CCMSGTYPE_BINARYDATA) { 346 value = isccc_sexpr_frombinary(&active); 347 if (value != NULL) { 348 *valuep = value; 349 result = ISC_R_SUCCESS; 350 } else 351 result = ISC_R_NOMEMORY; 352 } else if (msgtype == ISCCC_CCMSGTYPE_TABLE) 353 result = table_fromwire(&active, NULL, valuep); 354 else if (msgtype == ISCCC_CCMSGTYPE_LIST) 355 result = list_fromwire(&active, valuep); 356 else 357 result = ISCCC_R_SYNTAX; 358 359 return (result); 360} 361 362static isc_result_t 363table_fromwire(isccc_region_t *source, isccc_region_t *secret, 364 isccc_sexpr_t **alistp) 365{ 366 char key[256]; 367 isc_uint32_t len; 368 isc_result_t result; 369 isccc_sexpr_t *alist, *value; 370 isc_boolean_t first_tag; 371 unsigned char *checksum_rstart; 372 373 REQUIRE(alistp != NULL && *alistp == NULL); 374 375 checksum_rstart = NULL; 376 first_tag = ISC_TRUE; 377 alist = isccc_alist_create(); 378 if (alist == NULL) 379 return (ISC_R_NOMEMORY); 380 381 while (!REGION_EMPTY(*source)) { 382 GET8(len, source->rstart); 383 if (REGION_SIZE(*source) < len) { 384 result = ISC_R_UNEXPECTEDEND; 385 goto bad; 386 } 387 GET_MEM(key, len, source->rstart); 388 key[len] = '\0'; /* Ensure NUL termination. */ 389 value = NULL; 390 result = value_fromwire(source, &value); 391 if (result != ISC_R_SUCCESS) 392 goto bad; 393 if (isccc_alist_define(alist, key, value) == NULL) { 394 result = ISC_R_NOMEMORY; 395 goto bad; 396 } 397 if (first_tag && secret != NULL && strcmp(key, "_auth") == 0) 398 checksum_rstart = source->rstart; 399 first_tag = ISC_FALSE; 400 } 401 402 *alistp = alist; 403 404 if (secret != NULL) { 405 if (checksum_rstart != NULL) 406 result = verify(alist, checksum_rstart, 407 (source->rend - checksum_rstart), 408 secret); 409 else 410 result = ISCCC_R_BADAUTH; 411 } else 412 result = ISC_R_SUCCESS; 413 414 bad: 415 if (result != ISC_R_SUCCESS) 416 isccc_sexpr_free(&alist); 417 418 return (result); 419} 420 421static isc_result_t 422list_fromwire(isccc_region_t *source, isccc_sexpr_t **listp) 423{ 424 isccc_sexpr_t *list, *value; 425 isc_result_t result; 426 427 list = NULL; 428 while (!REGION_EMPTY(*source)) { 429 value = NULL; 430 result = value_fromwire(source, &value); 431 if (result != ISC_R_SUCCESS) { 432 isccc_sexpr_free(&list); 433 return (result); 434 } 435 if (isccc_sexpr_addtolist(&list, value) == NULL) { 436 isccc_sexpr_free(&value); 437 isccc_sexpr_free(&list); 438 return (result); 439 } 440 } 441 442 *listp = list; 443 444 return (ISC_R_SUCCESS); 445} 446 447isc_result_t 448isccc_cc_fromwire(isccc_region_t *source, isccc_sexpr_t **alistp, 449 isccc_region_t *secret) 450{ 451 unsigned int size; 452 isc_uint32_t version; 453 454 size = REGION_SIZE(*source); 455 if (size < 4) 456 return (ISC_R_UNEXPECTEDEND); 457 GET32(version, source->rstart); 458 if (version != 1) 459 return (ISCCC_R_UNKNOWNVERSION); 460 461 return (table_fromwire(source, secret, alistp)); 462} 463 464static isc_result_t 465createmessage(isc_uint32_t version, const char *from, const char *to, 466 isc_uint32_t serial, isccc_time_t now, 467 isccc_time_t expires, isccc_sexpr_t **alistp, 468 isc_boolean_t want_expires) 469{ 470 isccc_sexpr_t *alist, *_ctrl, *_data; 471 isc_result_t result; 472 473 REQUIRE(alistp != NULL && *alistp == NULL); 474 475 if (version != 1) 476 return (ISCCC_R_UNKNOWNVERSION); 477 478 alist = isccc_alist_create(); 479 if (alist == NULL) 480 return (ISC_R_NOMEMORY); 481 482 result = ISC_R_NOMEMORY; 483 484 _ctrl = isccc_alist_create(); 485 if (_ctrl == NULL) 486 goto bad; 487 if (isccc_alist_define(alist, "_ctrl", _ctrl) == NULL) { 488 isccc_sexpr_free(&_ctrl); 489 goto bad; 490 } 491 492 _data = isccc_alist_create(); 493 if (_data == NULL) 494 goto bad; 495 if (isccc_alist_define(alist, "_data", _data) == NULL) { 496 isccc_sexpr_free(&_data); 497 goto bad; 498 } 499 500 if (isccc_cc_defineuint32(_ctrl, "_ser", serial) == NULL || 501 isccc_cc_defineuint32(_ctrl, "_tim", now) == NULL || 502 (want_expires && 503 isccc_cc_defineuint32(_ctrl, "_exp", expires) == NULL)) 504 goto bad; 505 if (from != NULL && 506 isccc_cc_definestring(_ctrl, "_frm", from) == NULL) 507 goto bad; 508 if (to != NULL && 509 isccc_cc_definestring(_ctrl, "_to", to) == NULL) 510 goto bad; 511 512 *alistp = alist; 513 514 return (ISC_R_SUCCESS); 515 516 bad: 517 isccc_sexpr_free(&alist); 518 519 return (result); 520} 521 522isc_result_t 523isccc_cc_createmessage(isc_uint32_t version, const char *from, const char *to, 524 isc_uint32_t serial, isccc_time_t now, 525 isccc_time_t expires, isccc_sexpr_t **alistp) 526{ 527 return (createmessage(version, from, to, serial, now, expires, 528 alistp, ISC_TRUE)); 529} 530 531isc_result_t 532isccc_cc_createack(isccc_sexpr_t *message, isc_boolean_t ok, 533 isccc_sexpr_t **ackp) 534{ 535 char *_frm, *_to; 536 isc_uint32_t serial; 537 isccc_sexpr_t *ack, *_ctrl; 538 isc_result_t result; 539 isccc_time_t t; 540 541 REQUIRE(ackp != NULL && *ackp == NULL); 542 543 _ctrl = isccc_alist_lookup(message, "_ctrl"); 544 if (_ctrl == NULL || 545 isccc_cc_lookupuint32(_ctrl, "_ser", &serial) != ISC_R_SUCCESS || 546 isccc_cc_lookupuint32(_ctrl, "_tim", &t) != ISC_R_SUCCESS) 547 return (ISC_R_FAILURE); 548 /* 549 * _frm and _to are optional. 550 */ 551 _frm = NULL; 552 (void)isccc_cc_lookupstring(_ctrl, "_frm", &_frm); 553 _to = NULL; 554 (void)isccc_cc_lookupstring(_ctrl, "_to", &_to); 555 /* 556 * Create the ack. 557 */ 558 ack = NULL; 559 result = createmessage(1, _to, _frm, serial, t, 0, &ack, ISC_FALSE); 560 if (result != ISC_R_SUCCESS) 561 return (result); 562 563 _ctrl = isccc_alist_lookup(ack, "_ctrl"); 564 if (_ctrl == NULL) 565 return (ISC_R_FAILURE); 566 if (isccc_cc_definestring(ack, "_ack", (ok) ? "1" : "0") == NULL) { 567 result = ISC_R_NOMEMORY; 568 goto bad; 569 } 570 571 *ackp = ack; 572 573 return (ISC_R_SUCCESS); 574 575 bad: 576 isccc_sexpr_free(&ack); 577 578 return (result); 579} 580 581isc_boolean_t 582isccc_cc_isack(isccc_sexpr_t *message) 583{ 584 isccc_sexpr_t *_ctrl; 585 586 _ctrl = isccc_alist_lookup(message, "_ctrl"); 587 if (_ctrl == NULL) 588 return (ISC_FALSE); 589 if (isccc_cc_lookupstring(_ctrl, "_ack", NULL) == ISC_R_SUCCESS) 590 return (ISC_TRUE); 591 return (ISC_FALSE); 592} 593 594isc_boolean_t 595isccc_cc_isreply(isccc_sexpr_t *message) 596{ 597 isccc_sexpr_t *_ctrl; 598 599 _ctrl = isccc_alist_lookup(message, "_ctrl"); 600 if (_ctrl == NULL) 601 return (ISC_FALSE); 602 if (isccc_cc_lookupstring(_ctrl, "_rpl", NULL) == ISC_R_SUCCESS) 603 return (ISC_TRUE); 604 return (ISC_FALSE); 605} 606 607isc_result_t 608isccc_cc_createresponse(isccc_sexpr_t *message, isccc_time_t now, 609 isccc_time_t expires, isccc_sexpr_t **alistp) 610{ 611 char *_frm, *_to, *type; 612 isc_uint32_t serial; 613 isccc_sexpr_t *alist, *_ctrl, *_data; 614 isc_result_t result; 615 616 REQUIRE(alistp != NULL && *alistp == NULL); 617 618 _ctrl = isccc_alist_lookup(message, "_ctrl"); 619 _data = isccc_alist_lookup(message, "_data"); 620 if (_ctrl == NULL || 621 _data == NULL || 622 isccc_cc_lookupuint32(_ctrl, "_ser", &serial) != ISC_R_SUCCESS || 623 isccc_cc_lookupstring(_data, "type", &type) != ISC_R_SUCCESS) 624 return (ISC_R_FAILURE); 625 /* 626 * _frm and _to are optional. 627 */ 628 _frm = NULL; 629 (void)isccc_cc_lookupstring(_ctrl, "_frm", &_frm); 630 _to = NULL; 631 (void)isccc_cc_lookupstring(_ctrl, "_to", &_to); 632 /* 633 * Create the response. 634 */ 635 alist = NULL; 636 result = isccc_cc_createmessage(1, _to, _frm, serial, now, expires, 637 &alist); 638 if (result != ISC_R_SUCCESS) 639 return (result); 640 _ctrl = isccc_alist_lookup(alist, "_ctrl"); 641 if (_ctrl == NULL) 642 return (ISC_R_FAILURE); 643 _data = isccc_alist_lookup(alist, "_data"); 644 if (_data == NULL) 645 return (ISC_R_FAILURE); 646 if (isccc_cc_definestring(_ctrl, "_rpl", "1") == NULL || 647 isccc_cc_definestring(_data, "type", type) == NULL) { 648 isccc_sexpr_free(&alist); 649 return (ISC_R_NOMEMORY); 650 } 651 652 *alistp = alist; 653 654 return (ISC_R_SUCCESS); 655} 656 657isccc_sexpr_t * 658isccc_cc_definestring(isccc_sexpr_t *alist, const char *key, const char *str) 659{ 660 size_t len; 661 isccc_region_t r; 662 663 len = strlen(str); 664 DE_CONST(str, r.rstart); 665 r.rend = r.rstart + len; 666 667 return (isccc_alist_definebinary(alist, key, &r)); 668} 669 670isccc_sexpr_t * 671isccc_cc_defineuint32(isccc_sexpr_t *alist, const char *key, isc_uint32_t i) 672{ 673 char b[100]; 674 size_t len; 675 isccc_region_t r; 676 677 snprintf(b, sizeof(b), "%u", i); 678 len = strlen(b); 679 r.rstart = (unsigned char *)b; 680 r.rend = (unsigned char *)b + len; 681 682 return (isccc_alist_definebinary(alist, key, &r)); 683} 684 685isc_result_t 686isccc_cc_lookupstring(isccc_sexpr_t *alist, const char *key, char **strp) 687{ 688 isccc_sexpr_t *kv, *v; 689 690 kv = isccc_alist_assq(alist, key); 691 if (kv != NULL) { 692 v = ISCCC_SEXPR_CDR(kv); 693 if (isccc_sexpr_binaryp(v)) { 694 if (strp != NULL) 695 *strp = isccc_sexpr_tostring(v); 696 return (ISC_R_SUCCESS); 697 } else 698 return (ISC_R_EXISTS); 699 } 700 701 return (ISC_R_NOTFOUND); 702} 703 704isc_result_t 705isccc_cc_lookupuint32(isccc_sexpr_t *alist, const char *key, 706 isc_uint32_t *uintp) 707{ 708 isccc_sexpr_t *kv, *v; 709 710 kv = isccc_alist_assq(alist, key); 711 if (kv != NULL) { 712 v = ISCCC_SEXPR_CDR(kv); 713 if (isccc_sexpr_binaryp(v)) { 714 if (uintp != NULL) 715 *uintp = (isc_uint32_t) 716 strtoul(isccc_sexpr_tostring(v), 717 NULL, 10); 718 return (ISC_R_SUCCESS); 719 } else 720 return (ISC_R_EXISTS); 721 } 722 723 return (ISC_R_NOTFOUND); 724} 725 726static void 727symtab_undefine(char *key, unsigned int type, isccc_symvalue_t value, 728 void *arg) 729{ 730 UNUSED(type); 731 UNUSED(value); 732 UNUSED(arg); 733 734 free(key); 735} 736 737static isc_boolean_t 738symtab_clean(char *key, unsigned int type, isccc_symvalue_t value, 739 void *arg) 740{ 741 isccc_time_t *now; 742 743 UNUSED(key); 744 UNUSED(type); 745 746 now = arg; 747 748 if (*now < value.as_uinteger) 749 return (ISC_FALSE); 750 if ((*now - value.as_uinteger) < DUP_LIFETIME) 751 return (ISC_FALSE); 752 return (ISC_TRUE); 753} 754 755isc_result_t 756isccc_cc_createsymtab(isccc_symtab_t **symtabp) 757{ 758 return (isccc_symtab_create(11897, symtab_undefine, NULL, ISC_FALSE, 759 symtabp)); 760} 761 762void 763isccc_cc_cleansymtab(isccc_symtab_t *symtab, isccc_time_t now) 764{ 765 isccc_symtab_foreach(symtab, symtab_clean, &now); 766} 767 768static isc_boolean_t 769has_whitespace(const char *str) 770{ 771 char c; 772 773 if (str == NULL) 774 return (ISC_FALSE); 775 while ((c = *str++) != '\0') { 776 if (c == ' ' || c == '\t' || c == '\n') 777 return (ISC_TRUE); 778 } 779 return (ISC_FALSE); 780} 781 782isc_result_t 783isccc_cc_checkdup(isccc_symtab_t *symtab, isccc_sexpr_t *message, 784 isccc_time_t now) 785{ 786 const char *_frm; 787 const char *_to; 788 char *_ser, *_tim, *tmp; 789 isc_result_t result; 790 char *key; 791 size_t len; 792 isccc_symvalue_t value; 793 isccc_sexpr_t *_ctrl; 794 795 _ctrl = isccc_alist_lookup(message, "_ctrl"); 796 if (_ctrl == NULL || 797 isccc_cc_lookupstring(_ctrl, "_ser", &_ser) != ISC_R_SUCCESS || 798 isccc_cc_lookupstring(_ctrl, "_tim", &_tim) != ISC_R_SUCCESS) 799 return (ISC_R_FAILURE); 800 /* 801 * _frm and _to are optional. 802 */ 803 if (isccc_cc_lookupstring(_ctrl, "_frm", &tmp) != ISC_R_SUCCESS) 804 _frm = ""; 805 else 806 _frm = tmp; 807 if (isccc_cc_lookupstring(_ctrl, "_to", &tmp) != ISC_R_SUCCESS) 808 _to = ""; 809 else 810 _to = tmp; 811 /* 812 * Ensure there is no newline in any of the strings. This is so 813 * we can write them to a file later. 814 */ 815 if (has_whitespace(_frm) || has_whitespace(_to) || 816 has_whitespace(_ser) || has_whitespace(_tim)) 817 return (ISC_R_FAILURE); 818 len = strlen(_frm) + strlen(_to) + strlen(_ser) + strlen(_tim) + 4; 819 key = malloc(len); 820 if (key == NULL) 821 return (ISC_R_NOMEMORY); 822 snprintf(key, len, "%s;%s;%s;%s", _frm, _to, _ser, _tim); 823 value.as_uinteger = now; 824 result = isccc_symtab_define(symtab, key, ISCCC_SYMTYPE_CCDUP, value, 825 isccc_symexists_reject); 826 if (result != ISC_R_SUCCESS) { 827 free(key); 828 return (result); 829 } 830 831 return (ISC_R_SUCCESS); 832} 833