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