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