Home | History | Annotate | Download | only in avahi-core
      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