1 /*** 2 This file is part of avahi. 3 4 avahi is free software; you can redistribute it and/or modify it 5 under the terms of the GNU Lesser General Public License as 6 published by the Free Software Foundation; either version 2.1 of the 7 License, or (at your option) any later version. 8 9 avahi is distributed in the hope that it will be useful, but WITHOUT 10 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 11 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General 12 Public License for more details. 13 14 You should have received a copy of the GNU Lesser General Public 15 License along with avahi; if not, write to the Free Software 16 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 17 USA. 18 ***/ 19 20 #ifdef HAVE_CONFIG_H 21 #include <config.h> 22 #endif 23 24 #include <string.h> 25 #include <stdio.h> 26 #include <sys/types.h> 27 #include <sys/socket.h> 28 #include <arpa/inet.h> 29 #include <assert.h> 30 31 #include <avahi-common/domain.h> 32 #include "avahi-common/avahi-malloc.h" 33 #include <avahi-common/defs.h> 34 35 #include "rr.h" 36 #include "log.h" 37 #include "util.h" 38 #include "hashmap.h" 39 #include "domain-util.h" 40 #include "rr-util.h" 41 #include "addr-util.h" 42 43 AvahiKey *avahi_key_new(const char *name, uint16_t class, uint16_t type) { 44 AvahiKey *k; 45 assert(name); 46 47 if (!(k = avahi_new(AvahiKey, 1))) { 48 avahi_log_error("avahi_new() failed."); 49 return NULL; 50 } 51 52 if (!(k->name = avahi_normalize_name_strdup(name))) { 53 avahi_log_error("avahi_normalize_name() failed."); 54 avahi_free(k); 55 return NULL; 56 } 57 58 k->ref = 1; 59 k->clazz = class; 60 k->type = type; 61 62 return k; 63 } 64 65 AvahiKey *avahi_key_new_cname(AvahiKey *key) { 66 assert(key); 67 68 if (key->clazz != AVAHI_DNS_CLASS_IN) 69 return NULL; 70 71 if (key->type == AVAHI_DNS_TYPE_CNAME) 72 return NULL; 73 74 return avahi_key_new(key->name, key->clazz, AVAHI_DNS_TYPE_CNAME); 75 } 76 77 AvahiKey *avahi_key_ref(AvahiKey *k) { 78 assert(k); 79 assert(k->ref >= 1); 80 81 k->ref++; 82 83 return k; 84 } 85 86 void avahi_key_unref(AvahiKey *k) { 87 assert(k); 88 assert(k->ref >= 1); 89 90 if ((--k->ref) <= 0) { 91 avahi_free(k->name); 92 avahi_free(k); 93 } 94 } 95 96 AvahiRecord *avahi_record_new(AvahiKey *k, uint32_t ttl) { 97 AvahiRecord *r; 98 99 assert(k); 100 101 if (!(r = avahi_new(AvahiRecord, 1))) { 102 avahi_log_error("avahi_new() failed."); 103 return NULL; 104 } 105 106 r->ref = 1; 107 r->key = avahi_key_ref(k); 108 109 memset(&r->data, 0, sizeof(r->data)); 110 111 r->ttl = ttl != (uint32_t) -1 ? ttl : AVAHI_DEFAULT_TTL; 112 113 return r; 114 } 115 116 AvahiRecord *avahi_record_new_full(const char *name, uint16_t class, uint16_t type, uint32_t ttl) { 117 AvahiRecord *r; 118 AvahiKey *k; 119 120 assert(name); 121 122 if (!(k = avahi_key_new(name, class, type))) { 123 avahi_log_error("avahi_key_new() failed."); 124 return NULL; 125 } 126 127 r = avahi_record_new(k, ttl); 128 avahi_key_unref(k); 129 130 if (!r) { 131 avahi_log_error("avahi_record_new() failed."); 132 return NULL; 133 } 134 135 return r; 136 } 137 138 AvahiRecord *avahi_record_ref(AvahiRecord *r) { 139 assert(r); 140 assert(r->ref >= 1); 141 142 r->ref++; 143 return r; 144 } 145 146 void avahi_record_unref(AvahiRecord *r) { 147 assert(r); 148 assert(r->ref >= 1); 149 150 if ((--r->ref) <= 0) { 151 switch (r->key->type) { 152 153 case AVAHI_DNS_TYPE_SRV: 154 avahi_free(r->data.srv.name); 155 break; 156 157 case AVAHI_DNS_TYPE_PTR: 158 case AVAHI_DNS_TYPE_CNAME: 159 case AVAHI_DNS_TYPE_NS: 160 avahi_free(r->data.ptr.name); 161 break; 162 163 case AVAHI_DNS_TYPE_HINFO: 164 avahi_free(r->data.hinfo.cpu); 165 avahi_free(r->data.hinfo.os); 166 break; 167 168 case AVAHI_DNS_TYPE_TXT: 169 avahi_string_list_free(r->data.txt.string_list); 170 break; 171 172 case AVAHI_DNS_TYPE_A: 173 case AVAHI_DNS_TYPE_AAAA: 174 break; 175 176 default: 177 avahi_free(r->data.generic.data); 178 } 179 180 avahi_key_unref(r->key); 181 avahi_free(r); 182 } 183 } 184 185 const char *avahi_dns_class_to_string(uint16_t class) { 186 if (class & AVAHI_DNS_CACHE_FLUSH) 187 return "FLUSH"; 188 189 switch (class) { 190 case AVAHI_DNS_CLASS_IN: 191 return "IN"; 192 case AVAHI_DNS_CLASS_ANY: 193 return "ANY"; 194 default: 195 return NULL; 196 } 197 } 198 199 const char *avahi_dns_type_to_string(uint16_t type) { 200 switch (type) { 201 case AVAHI_DNS_TYPE_CNAME: 202 return "CNAME"; 203 case AVAHI_DNS_TYPE_A: 204 return "A"; 205 case AVAHI_DNS_TYPE_AAAA: 206 return "AAAA"; 207 case AVAHI_DNS_TYPE_PTR: 208 return "PTR"; 209 case AVAHI_DNS_TYPE_HINFO: 210 return "HINFO"; 211 case AVAHI_DNS_TYPE_TXT: 212 return "TXT"; 213 case AVAHI_DNS_TYPE_SRV: 214 return "SRV"; 215 case AVAHI_DNS_TYPE_ANY: 216 return "ANY"; 217 case AVAHI_DNS_TYPE_SOA: 218 return "SOA"; 219 case AVAHI_DNS_TYPE_NS: 220 return "NS"; 221 default: 222 return NULL; 223 } 224 } 225 226 char *avahi_key_to_string(const AvahiKey *k) { 227 char class[16], type[16]; 228 const char *c, *t; 229 230 assert(k); 231 assert(k->ref >= 1); 232 233 /* According to RFC3597 */ 234 235 if (!(c = avahi_dns_class_to_string(k->clazz))) { 236 snprintf(class, sizeof(class), "CLASS%u", k->clazz); 237 c = class; 238 } 239 240 if (!(t = avahi_dns_type_to_string(k->type))) { 241 snprintf(type, sizeof(type), "TYPE%u", k->type); 242 t = type; 243 } 244 245 return avahi_strdup_printf("%s\t%s\t%s", k->name, c, t); 246 } 247 248 char *avahi_record_to_string(const AvahiRecord *r) { 249 char *p, *s; 250 char buf[1024], *t = NULL, *d = NULL; 251 252 assert(r); 253 assert(r->ref >= 1); 254 255 switch (r->key->type) { 256 case AVAHI_DNS_TYPE_A: 257 inet_ntop(AF_INET, &r->data.a.address.address, t = buf, sizeof(buf)); 258 break; 259 260 case AVAHI_DNS_TYPE_AAAA: 261 inet_ntop(AF_INET6, &r->data.aaaa.address.address, t = buf, sizeof(buf)); 262 break; 263 264 case AVAHI_DNS_TYPE_PTR: 265 case AVAHI_DNS_TYPE_CNAME: 266 case AVAHI_DNS_TYPE_NS: 267 268 t = r->data.ptr.name; 269 break; 270 271 case AVAHI_DNS_TYPE_TXT: 272 t = d = avahi_string_list_to_string(r->data.txt.string_list); 273 break; 274 275 case AVAHI_DNS_TYPE_HINFO: 276 277 snprintf(t = buf, sizeof(buf), "\"%s\" \"%s\"", r->data.hinfo.cpu, r->data.hinfo.os); 278 break; 279 280 case AVAHI_DNS_TYPE_SRV: 281 282 snprintf(t = buf, sizeof(buf), "%u %u %u %s", 283 r->data.srv.priority, 284 r->data.srv.weight, 285 r->data.srv.port, 286 r->data.srv.name); 287 288 break; 289 290 default: { 291 292 uint8_t *c; 293 uint16_t n; 294 int i; 295 char *e; 296 297 /* According to RFC3597 */ 298 299 snprintf(t = buf, sizeof(buf), "\\# %u", r->data.generic.size); 300 301 e = strchr(t, 0); 302 303 for (c = r->data.generic.data, n = r->data.generic.size, i = 0; 304 n > 0 && i < 20; 305 c ++, n --, i++) { 306 307 sprintf(e, " %02X", *c); 308 e = strchr(e, 0); 309 } 310 311 break; 312 } 313 } 314 315 p = avahi_key_to_string(r->key); 316 s = avahi_strdup_printf("%s %s ; ttl=%u", p, t, r->ttl); 317 avahi_free(p); 318 avahi_free(d); 319 320 return s; 321 } 322 323 int avahi_key_equal(const AvahiKey *a, const AvahiKey *b) { 324 assert(a); 325 assert(b); 326 327 if (a == b) 328 return 1; 329 330 return avahi_domain_equal(a->name, b->name) && 331 a->type == b->type && 332 a->clazz == b->clazz; 333 } 334 335 int avahi_key_pattern_match(const AvahiKey *pattern, const AvahiKey *k) { 336 assert(pattern); 337 assert(k); 338 339 assert(!avahi_key_is_pattern(k)); 340 341 if (pattern == k) 342 return 1; 343 344 return avahi_domain_equal(pattern->name, k->name) && 345 (pattern->type == k->type || pattern->type == AVAHI_DNS_TYPE_ANY) && 346 (pattern->clazz == k->clazz || pattern->clazz == AVAHI_DNS_CLASS_ANY); 347 } 348 349 int avahi_key_is_pattern(const AvahiKey *k) { 350 assert(k); 351 352 return 353 k->type == AVAHI_DNS_TYPE_ANY || 354 k->clazz == AVAHI_DNS_CLASS_ANY; 355 } 356 357 unsigned avahi_key_hash(const AvahiKey *k) { 358 assert(k); 359 360 return 361 avahi_domain_hash(k->name) + 362 k->type + 363 k->clazz; 364 } 365 366 static int rdata_equal(const AvahiRecord *a, const AvahiRecord *b) { 367 assert(a); 368 assert(b); 369 assert(a->key->type == b->key->type); 370 371 switch (a->key->type) { 372 case AVAHI_DNS_TYPE_SRV: 373 return 374 a->data.srv.priority == b->data.srv.priority && 375 a->data.srv.weight == b->data.srv.weight && 376 a->data.srv.port == b->data.srv.port && 377 avahi_domain_equal(a->data.srv.name, b->data.srv.name); 378 379 case AVAHI_DNS_TYPE_PTR: 380 case AVAHI_DNS_TYPE_CNAME: 381 case AVAHI_DNS_TYPE_NS: 382 return avahi_domain_equal(a->data.ptr.name, b->data.ptr.name); 383 384 case AVAHI_DNS_TYPE_HINFO: 385 return 386 !strcmp(a->data.hinfo.cpu, b->data.hinfo.cpu) && 387 !strcmp(a->data.hinfo.os, b->data.hinfo.os); 388 389 case AVAHI_DNS_TYPE_TXT: 390 return avahi_string_list_equal(a->data.txt.string_list, b->data.txt.string_list); 391 392 case AVAHI_DNS_TYPE_A: 393 return memcmp(&a->data.a.address, &b->data.a.address, sizeof(AvahiIPv4Address)) == 0; 394 395 case AVAHI_DNS_TYPE_AAAA: 396 return memcmp(&a->data.aaaa.address, &b->data.aaaa.address, sizeof(AvahiIPv6Address)) == 0; 397 398 default: 399 return a->data.generic.size == b->data.generic.size && 400 (a->data.generic.size == 0 || memcmp(a->data.generic.data, b->data.generic.data, a->data.generic.size) == 0); 401 } 402 403 } 404 405 int avahi_record_equal_no_ttl(const AvahiRecord *a, const AvahiRecord *b) { 406 assert(a); 407 assert(b); 408 409 if (a == b) 410 return 1; 411 412 return 413 avahi_key_equal(a->key, b->key) && 414 rdata_equal(a, b); 415 } 416 417 418 AvahiRecord *avahi_record_copy(AvahiRecord *r) { 419 AvahiRecord *copy; 420 421 if (!(copy = avahi_new(AvahiRecord, 1))) { 422 avahi_log_error("avahi_new() failed."); 423 return NULL; 424 } 425 426 copy->ref = 1; 427 copy->key = avahi_key_ref(r->key); 428 copy->ttl = r->ttl; 429 430 switch (r->key->type) { 431 case AVAHI_DNS_TYPE_PTR: 432 case AVAHI_DNS_TYPE_CNAME: 433 case AVAHI_DNS_TYPE_NS: 434 if (!(copy->data.ptr.name = avahi_strdup(r->data.ptr.name))) 435 goto fail; 436 break; 437 438 case AVAHI_DNS_TYPE_SRV: 439 copy->data.srv.priority = r->data.srv.priority; 440 copy->data.srv.weight = r->data.srv.weight; 441 copy->data.srv.port = r->data.srv.port; 442 if (!(copy->data.srv.name = avahi_strdup(r->data.srv.name))) 443 goto fail; 444 break; 445 446 case AVAHI_DNS_TYPE_HINFO: 447 if (!(copy->data.hinfo.os = avahi_strdup(r->data.hinfo.os))) 448 goto fail; 449 450 if (!(copy->data.hinfo.cpu = avahi_strdup(r->data.hinfo.cpu))) { 451 avahi_free(r->data.hinfo.os); 452 goto fail; 453 } 454 break; 455 456 case AVAHI_DNS_TYPE_TXT: 457 copy->data.txt.string_list = avahi_string_list_copy(r->data.txt.string_list); 458 break; 459 460 case AVAHI_DNS_TYPE_A: 461 copy->data.a.address = r->data.a.address; 462 break; 463 464 case AVAHI_DNS_TYPE_AAAA: 465 copy->data.aaaa.address = r->data.aaaa.address; 466 break; 467 468 default: 469 if (!(copy->data.generic.data = avahi_memdup(r->data.generic.data, r->data.generic.size))) 470 goto fail; 471 copy->data.generic.size = r->data.generic.size; 472 break; 473 474 } 475 476 return copy; 477 478 fail: 479 avahi_log_error("Failed to allocate memory"); 480 481 avahi_key_unref(copy->key); 482 avahi_free(copy); 483 484 return NULL; 485 } 486 487 488 size_t avahi_key_get_estimate_size(AvahiKey *k) { 489 assert(k); 490 491 return strlen(k->name)+1+4; 492 } 493 494 size_t avahi_record_get_estimate_size(AvahiRecord *r) { 495 size_t n; 496 assert(r); 497 498 n = avahi_key_get_estimate_size(r->key) + 4 + 2; 499 500 switch (r->key->type) { 501 case AVAHI_DNS_TYPE_PTR: 502 case AVAHI_DNS_TYPE_CNAME: 503 case AVAHI_DNS_TYPE_NS: 504 n += strlen(r->data.ptr.name) + 1; 505 break; 506 507 case AVAHI_DNS_TYPE_SRV: 508 n += 6 + strlen(r->data.srv.name) + 1; 509 break; 510 511 case AVAHI_DNS_TYPE_HINFO: 512 n += strlen(r->data.hinfo.os) + 1 + strlen(r->data.hinfo.cpu) + 1; 513 break; 514 515 case AVAHI_DNS_TYPE_TXT: 516 n += avahi_string_list_serialize(r->data.txt.string_list, NULL, 0); 517 break; 518 519 case AVAHI_DNS_TYPE_A: 520 n += sizeof(AvahiIPv4Address); 521 break; 522 523 case AVAHI_DNS_TYPE_AAAA: 524 n += sizeof(AvahiIPv6Address); 525 break; 526 527 default: 528 n += r->data.generic.size; 529 } 530 531 return n; 532 } 533 534 static int lexicographical_memcmp(const void* a, size_t al, const void* b, size_t bl) { 535 size_t c; 536 int ret; 537 538 assert(a); 539 assert(b); 540 541 c = al < bl ? al : bl; 542 if ((ret = memcmp(a, b, c))) 543 return ret; 544 545 if (al == bl) 546 return 0; 547 else 548 return al == c ? 1 : -1; 549 } 550 551 static int uint16_cmp(uint16_t a, uint16_t b) { 552 return a == b ? 0 : (a < b ? -1 : 1); 553 } 554 555 int avahi_record_lexicographical_compare(AvahiRecord *a, AvahiRecord *b) { 556 int r; 557 /* char *t1, *t2; */ 558 559 assert(a); 560 assert(b); 561 562 /* t1 = avahi_record_to_string(a); */ 563 /* t2 = avahi_record_to_string(b); */ 564 /* g_message("lexicocmp: %s %s", t1, t2); */ 565 /* avahi_free(t1); */ 566 /* avahi_free(t2); */ 567 568 if (a == b) 569 return 0; 570 571 if ((r = uint16_cmp(a->key->clazz, b->key->clazz)) || 572 (r = uint16_cmp(a->key->type, b->key->type))) 573 return r; 574 575 switch (a->key->type) { 576 577 case AVAHI_DNS_TYPE_PTR: 578 case AVAHI_DNS_TYPE_CNAME: 579 case AVAHI_DNS_TYPE_NS: 580 return avahi_binary_domain_cmp(a->data.ptr.name, b->data.ptr.name); 581 582 case AVAHI_DNS_TYPE_SRV: { 583 if ((r = uint16_cmp(a->data.srv.priority, b->data.srv.priority)) == 0 && 584 (r = uint16_cmp(a->data.srv.weight, b->data.srv.weight)) == 0 && 585 (r = uint16_cmp(a->data.srv.port, b->data.srv.port)) == 0) 586 r = avahi_binary_domain_cmp(a->data.srv.name, b->data.srv.name); 587 588 return r; 589 } 590 591 case AVAHI_DNS_TYPE_HINFO: { 592 593 if ((r = strcmp(a->data.hinfo.cpu, b->data.hinfo.cpu)) || 594 (r = strcmp(a->data.hinfo.os, b->data.hinfo.os))) 595 return r; 596 597 return 0; 598 599 } 600 601 case AVAHI_DNS_TYPE_TXT: { 602 603 uint8_t *ma = NULL, *mb = NULL; 604 size_t asize, bsize; 605 606 asize = avahi_string_list_serialize(a->data.txt.string_list, NULL, 0); 607 bsize = avahi_string_list_serialize(b->data.txt.string_list, NULL, 0); 608 609 if (asize > 0 && !(ma = avahi_new(uint8_t, asize))) 610 goto fail; 611 612 if (bsize > 0 && !(mb = avahi_new(uint8_t, bsize))) { 613 avahi_free(ma); 614 goto fail; 615 } 616 617 avahi_string_list_serialize(a->data.txt.string_list, ma, asize); 618 avahi_string_list_serialize(b->data.txt.string_list, mb, bsize); 619 620 if (asize && bsize) 621 r = lexicographical_memcmp(ma, asize, mb, bsize); 622 else if (asize && !bsize) 623 r = 1; 624 else if (!asize && bsize) 625 r = -1; 626 else 627 r = 0; 628 629 avahi_free(ma); 630 avahi_free(mb); 631 632 return r; 633 } 634 635 case AVAHI_DNS_TYPE_A: 636 return memcmp(&a->data.a.address, &b->data.a.address, sizeof(AvahiIPv4Address)); 637 638 case AVAHI_DNS_TYPE_AAAA: 639 return memcmp(&a->data.aaaa.address, &b->data.aaaa.address, sizeof(AvahiIPv6Address)); 640 641 default: 642 return lexicographical_memcmp(a->data.generic.data, a->data.generic.size, 643 b->data.generic.data, b->data.generic.size); 644 } 645 646 647 fail: 648 avahi_log_error(__FILE__": Out of memory"); 649 return -1; /* or whatever ... */ 650 } 651 652 int avahi_record_is_goodbye(AvahiRecord *r) { 653 assert(r); 654 655 return r->ttl == 0; 656 } 657 658 int avahi_key_is_valid(AvahiKey *k) { 659 assert(k); 660 661 if (!avahi_is_valid_domain_name(k->name)) 662 return 0; 663 664 return 1; 665 } 666 667 int avahi_record_is_valid(AvahiRecord *r) { 668 assert(r); 669 670 if (!avahi_key_is_valid(r->key)) 671 return 0; 672 673 switch (r->key->type) { 674 675 case AVAHI_DNS_TYPE_PTR: 676 case AVAHI_DNS_TYPE_CNAME: 677 case AVAHI_DNS_TYPE_NS: 678 return avahi_is_valid_domain_name(r->data.ptr.name); 679 680 case AVAHI_DNS_TYPE_SRV: 681 return avahi_is_valid_domain_name(r->data.srv.name); 682 683 case AVAHI_DNS_TYPE_HINFO: 684 return 685 strlen(r->data.hinfo.os) <= 255 && 686 strlen(r->data.hinfo.cpu) <= 255; 687 688 case AVAHI_DNS_TYPE_TXT: { 689 690 AvahiStringList *strlst; 691 692 for (strlst = r->data.txt.string_list; strlst; strlst = strlst->next) 693 if (strlst->size > 255 || strlst->size <= 0) 694 return 0; 695 696 return 1; 697 } 698 } 699 700 return 1; 701 } 702 703 static AvahiAddress *get_address(const AvahiRecord *r, AvahiAddress *a) { 704 assert(r); 705 706 switch (r->key->type) { 707 case AVAHI_DNS_TYPE_A: 708 a->proto = AVAHI_PROTO_INET; 709 a->data.ipv4 = r->data.a.address; 710 break; 711 712 case AVAHI_DNS_TYPE_AAAA: 713 a->proto = AVAHI_PROTO_INET6; 714 a->data.ipv6 = r->data.aaaa.address; 715 break; 716 717 default: 718 return NULL; 719 } 720 721 return a; 722 } 723 724 int avahi_record_is_link_local_address(const AvahiRecord *r) { 725 AvahiAddress a; 726 727 assert(r); 728 729 if (!get_address(r, &a)) 730 return 0; 731 732 return avahi_address_is_link_local(&a); 733 } 734