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 <errno.h>
28#include <stdio.h>
29#include <stdlib.h>
30#include <sys/types.h>
31#include <sys/socket.h>
32#include <netinet/in.h>
33
34#include <avahi-common/error.h>
35#include <avahi-common/malloc.h>
36#include <avahi-common/domain.h>
37
38#include "iface.h"
39#include "dns.h"
40#include "socket.h"
41#include "announce.h"
42#include "util.h"
43#include "log.h"
44#include "multicast-lookup.h"
45#include "querier.h"
46
47void avahi_interface_address_update_rrs(AvahiInterfaceAddress *a, int remove_rrs) {
48    AvahiInterfaceMonitor *m;
49
50    assert(a);
51    m = a->monitor;
52
53    if (m->list_complete &&
54        avahi_interface_address_is_relevant(a) &&
55        avahi_interface_is_relevant(a->interface) &&
56        !remove_rrs &&
57        m->server->config.publish_addresses &&
58        (m->server->state == AVAHI_SERVER_RUNNING ||
59        m->server->state == AVAHI_SERVER_REGISTERING)) {
60
61        /* Fill the entry group */
62        if (!a->entry_group)
63            a->entry_group = avahi_s_entry_group_new(m->server, avahi_host_rr_entry_group_callback, NULL);
64
65        if (!a->entry_group) /* OOM */
66            return;
67
68        if (avahi_s_entry_group_is_empty(a->entry_group)) {
69            char t[AVAHI_ADDRESS_STR_MAX];
70            AvahiProtocol p;
71
72            p = (a->interface->protocol == AVAHI_PROTO_INET && m->server->config.publish_a_on_ipv6) ||
73                (a->interface->protocol == AVAHI_PROTO_INET6 && m->server->config.publish_aaaa_on_ipv4) ? AVAHI_PROTO_UNSPEC : a->interface->protocol;
74
75            avahi_address_snprint(t, sizeof(t), &a->address);
76            avahi_log_info("Registering new address record for %s on %s.%s.", t, a->interface->hardware->name, p == AVAHI_PROTO_UNSPEC ? "*" : avahi_proto_to_string(p));
77
78            if (avahi_server_add_address(m->server, a->entry_group, a->interface->hardware->index, p, 0, NULL, &a->address) < 0) {
79                avahi_log_warn(__FILE__": avahi_server_add_address() failed: %s", avahi_strerror(m->server->error));
80                avahi_s_entry_group_free(a->entry_group);
81                a->entry_group = NULL;
82                return;
83            }
84
85            avahi_s_entry_group_commit(a->entry_group);
86        }
87    } else {
88
89        /* Clear the entry group */
90
91        if (a->entry_group && !avahi_s_entry_group_is_empty(a->entry_group)) {
92            char t[AVAHI_ADDRESS_STR_MAX];
93            avahi_address_snprint(t, sizeof(t), &a->address);
94
95            if (avahi_s_entry_group_get_state(a->entry_group) == AVAHI_ENTRY_GROUP_REGISTERING &&
96		m->server->state == AVAHI_SERVER_REGISTERING)
97                avahi_server_decrease_host_rr_pending(m->server);
98
99            avahi_log_info("Withdrawing address record for %s on %s.", t, a->interface->hardware->name);
100
101            avahi_s_entry_group_reset(a->entry_group);
102        }
103    }
104}
105
106void avahi_interface_update_rrs(AvahiInterface *i, int remove_rrs) {
107    AvahiInterfaceAddress *a;
108
109    assert(i);
110
111    for (a = i->addresses; a; a = a->address_next)
112        avahi_interface_address_update_rrs(a, remove_rrs);
113}
114
115void avahi_hw_interface_update_rrs(AvahiHwInterface *hw, int remove_rrs) {
116    AvahiInterface *i;
117    AvahiInterfaceMonitor *m;
118
119    assert(hw);
120    m = hw->monitor;
121
122    for (i = hw->interfaces; i; i = i->by_hardware_next)
123        avahi_interface_update_rrs(i, remove_rrs);
124
125    if (m->list_complete &&
126        !remove_rrs &&
127        m->server->config.publish_workstation &&
128        (m->server->state == AVAHI_SERVER_RUNNING)) {
129
130        if (!hw->entry_group)
131            hw->entry_group = avahi_s_entry_group_new(m->server, avahi_host_rr_entry_group_callback, NULL);
132
133        if (!hw->entry_group)
134            return; /* OOM */
135
136        if (avahi_s_entry_group_is_empty(hw->entry_group)) {
137            char name[AVAHI_LABEL_MAX], mac[256];
138
139            avahi_format_mac_address(mac, sizeof(mac), hw->mac_address, hw->mac_address_size);
140            snprintf(name, sizeof(name), "%s [%s]", m->server->host_name, mac);
141
142            if (avahi_server_add_service(m->server, hw->entry_group, hw->index, AVAHI_PROTO_UNSPEC, 0, name, "_workstation._tcp", NULL, NULL, 9, NULL) < 0) {
143                avahi_log_warn(__FILE__": avahi_server_add_service() failed: %s", avahi_strerror(m->server->error));
144                avahi_s_entry_group_free(hw->entry_group);
145                hw->entry_group = NULL;
146            } else
147                avahi_s_entry_group_commit(hw->entry_group);
148        }
149
150    } else {
151
152        if (hw->entry_group && !avahi_s_entry_group_is_empty(hw->entry_group)) {
153
154            if (avahi_s_entry_group_get_state(hw->entry_group) == AVAHI_ENTRY_GROUP_REGISTERING)
155                avahi_server_decrease_host_rr_pending(m->server);
156
157            avahi_s_entry_group_reset(hw->entry_group);
158        }
159    }
160}
161
162void avahi_interface_monitor_update_rrs(AvahiInterfaceMonitor *m, int remove_rrs) {
163    AvahiHwInterface *hw;
164
165    assert(m);
166
167    for (hw = m->hw_interfaces; hw; hw = hw->hardware_next)
168        avahi_hw_interface_update_rrs(hw, remove_rrs);
169}
170
171static int interface_mdns_mcast_join(AvahiInterface *i, int join) {
172    char at[AVAHI_ADDRESS_STR_MAX];
173    int r;
174    assert(i);
175
176    if (!!join  == !!i->mcast_joined)
177        return 0;
178
179    if ((i->protocol == AVAHI_PROTO_INET6 && i->monitor->server->fd_ipv6 < 0) ||
180        (i->protocol == AVAHI_PROTO_INET && i->monitor->server->fd_ipv4 < 0))
181        return -1;
182
183    if (join) {
184        AvahiInterfaceAddress *a;
185
186        /* Look if there's an address with global scope */
187        for (a = i->addresses; a; a = a->address_next)
188            if (a->global_scope)
189                break;
190
191        /* No address with a global scope has been found, so let's use
192         * any. */
193        if (!a)
194            a = i->addresses;
195
196        /* Hmm, there is no address available. */
197        if (!a)
198            return -1;
199
200        i->local_mcast_address = a->address;
201    }
202
203    avahi_log_info("%s mDNS multicast group on interface %s.%s with address %s.",
204                   join ? "Joining" : "Leaving",
205                   i->hardware->name,
206                   avahi_proto_to_string(i->protocol),
207                   avahi_address_snprint(at, sizeof(at), &i->local_mcast_address));
208
209    if (i->protocol == AVAHI_PROTO_INET6)
210        r = avahi_mdns_mcast_join_ipv6(i->monitor->server->fd_ipv6, &i->local_mcast_address.data.ipv6, i->hardware->index, join);
211    else {
212        assert(i->protocol == AVAHI_PROTO_INET);
213
214        r = avahi_mdns_mcast_join_ipv4(i->monitor->server->fd_ipv4, &i->local_mcast_address.data.ipv4, i->hardware->index, join);
215    }
216
217    if (r < 0)
218        i->mcast_joined = 0;
219    else
220        i->mcast_joined = join;
221
222    return 0;
223}
224
225static int interface_mdns_mcast_rejoin(AvahiInterface *i) {
226    AvahiInterfaceAddress *a, *usable = NULL, *found = NULL;
227    assert(i);
228
229    if (!i->mcast_joined)
230        return 0;
231
232    /* Check whether old address we joined with is still available. If
233     * not, rejoin using an other address. */
234
235    for (a = i->addresses; a; a = a->address_next) {
236        if (a->global_scope && !usable)
237            usable = a;
238
239        if (avahi_address_cmp(&a->address, &i->local_mcast_address) == 0) {
240
241            if (a->global_scope)
242                /* No action necessary: the address still exists and
243                 * has global scope. */
244                return 0;
245
246            found = a;
247        }
248    }
249
250    if (found && !usable)
251        /* No action necessary: the address still exists and no better one has been found */
252        return 0;
253
254    interface_mdns_mcast_join(i, 0);
255    return interface_mdns_mcast_join(i, 1);
256}
257
258void avahi_interface_address_free(AvahiInterfaceAddress *a) {
259    assert(a);
260    assert(a->interface);
261
262    avahi_interface_address_update_rrs(a, 1);
263    AVAHI_LLIST_REMOVE(AvahiInterfaceAddress, address, a->interface->addresses, a);
264
265    if (a->entry_group)
266        avahi_s_entry_group_free(a->entry_group);
267
268    interface_mdns_mcast_rejoin(a->interface);
269
270    avahi_free(a);
271}
272
273void avahi_interface_free(AvahiInterface *i, int send_goodbye) {
274    assert(i);
275
276    /* Handle goodbyes and remove announcers */
277    avahi_goodbye_interface(i->monitor->server, i, send_goodbye, 1);
278    avahi_response_scheduler_force(i->response_scheduler);
279    assert(!i->announcers);
280
281    if (i->mcast_joined)
282        interface_mdns_mcast_join(i, 0);
283
284    /* Remove queriers */
285    avahi_querier_free_all(i);
286    avahi_hashmap_free(i->queriers_by_key);
287
288    /* Remove local RRs */
289    avahi_interface_update_rrs(i, 1);
290
291    while (i->addresses)
292        avahi_interface_address_free(i->addresses);
293
294    avahi_response_scheduler_free(i->response_scheduler);
295    avahi_query_scheduler_free(i->query_scheduler);
296    avahi_probe_scheduler_free(i->probe_scheduler);
297    avahi_cache_free(i->cache);
298
299    AVAHI_LLIST_REMOVE(AvahiInterface, interface, i->monitor->interfaces, i);
300    AVAHI_LLIST_REMOVE(AvahiInterface, by_hardware, i->hardware->interfaces, i);
301
302    avahi_free(i);
303}
304
305void avahi_hw_interface_free(AvahiHwInterface *hw, int send_goodbye) {
306    assert(hw);
307
308    avahi_hw_interface_update_rrs(hw, 1);
309
310    while (hw->interfaces)
311        avahi_interface_free(hw->interfaces, send_goodbye);
312
313    if (hw->entry_group)
314        avahi_s_entry_group_free(hw->entry_group);
315
316    AVAHI_LLIST_REMOVE(AvahiHwInterface, hardware, hw->monitor->hw_interfaces, hw);
317    avahi_hashmap_remove(hw->monitor->hashmap, &hw->index);
318
319    avahi_free(hw->name);
320    avahi_free(hw);
321}
322
323AvahiInterface* avahi_interface_new(AvahiInterfaceMonitor *m, AvahiHwInterface *hw, AvahiProtocol protocol) {
324    AvahiInterface *i;
325
326    assert(m);
327    assert(hw);
328    assert(AVAHI_PROTO_VALID(protocol));
329
330    if (!(i = avahi_new(AvahiInterface, 1)))
331        goto fail; /* OOM */
332
333    i->monitor = m;
334    i->hardware = hw;
335    i->protocol = protocol;
336    i->announcing = 0;
337    i->mcast_joined = 0;
338
339    AVAHI_LLIST_HEAD_INIT(AvahiInterfaceAddress, i->addresses);
340    AVAHI_LLIST_HEAD_INIT(AvahiAnnouncer, i->announcers);
341
342    AVAHI_LLIST_HEAD_INIT(AvahiQuerier, i->queriers);
343    i->queriers_by_key = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL);
344
345    i->cache = avahi_cache_new(m->server, i);
346    i->response_scheduler = avahi_response_scheduler_new(i);
347    i->query_scheduler = avahi_query_scheduler_new(i);
348    i->probe_scheduler = avahi_probe_scheduler_new(i);
349
350    if (!i->cache || !i->response_scheduler || !i->query_scheduler || !i->probe_scheduler)
351        goto fail; /* OOM */
352
353    AVAHI_LLIST_PREPEND(AvahiInterface, by_hardware, hw->interfaces, i);
354    AVAHI_LLIST_PREPEND(AvahiInterface, interface, m->interfaces, i);
355
356    return i;
357
358fail:
359
360    if (i) {
361        if (i->cache)
362            avahi_cache_free(i->cache);
363        if (i->response_scheduler)
364            avahi_response_scheduler_free(i->response_scheduler);
365        if (i->query_scheduler)
366            avahi_query_scheduler_free(i->query_scheduler);
367        if (i->probe_scheduler)
368            avahi_probe_scheduler_free(i->probe_scheduler);
369    }
370
371    return NULL;
372}
373
374AvahiHwInterface *avahi_hw_interface_new(AvahiInterfaceMonitor *m, AvahiIfIndex idx) {
375    AvahiHwInterface *hw;
376
377    assert(m);
378    assert(AVAHI_IF_VALID(idx));
379
380    if  (!(hw = avahi_new(AvahiHwInterface, 1)))
381        return NULL;
382
383    hw->monitor = m;
384    hw->name = NULL;
385    hw->flags_ok = 0;
386    hw->mtu = 1500;
387    hw->index = idx;
388    hw->mac_address_size = 0;
389    hw->entry_group = NULL;
390
391    AVAHI_LLIST_HEAD_INIT(AvahiInterface, hw->interfaces);
392    AVAHI_LLIST_PREPEND(AvahiHwInterface, hardware, m->hw_interfaces, hw);
393
394    avahi_hashmap_insert(m->hashmap, &hw->index, hw);
395
396    if (m->server->fd_ipv4 >= 0 || m->server->config.publish_a_on_ipv6)
397        avahi_interface_new(m, hw, AVAHI_PROTO_INET);
398    if (m->server->fd_ipv6 >= 0 || m->server->config.publish_aaaa_on_ipv4)
399        avahi_interface_new(m, hw, AVAHI_PROTO_INET6);
400
401    return hw;
402}
403
404AvahiInterfaceAddress *avahi_interface_address_new(AvahiInterfaceMonitor *m, AvahiInterface *i, const AvahiAddress *addr, unsigned prefix_len) {
405    AvahiInterfaceAddress *a;
406
407    assert(m);
408    assert(i);
409
410    if (!(a = avahi_new(AvahiInterfaceAddress, 1)))
411        return NULL;
412
413    a->interface = i;
414    a->monitor = m;
415    a->address = *addr;
416    a->prefix_len = prefix_len;
417    a->global_scope = 0;
418    a->entry_group = NULL;
419
420    AVAHI_LLIST_PREPEND(AvahiInterfaceAddress, address, i->addresses, a);
421
422    return a;
423}
424
425void avahi_interface_check_relevant(AvahiInterface *i) {
426    int b;
427    AvahiInterfaceMonitor *m;
428
429    assert(i);
430    m = i->monitor;
431
432    b = avahi_interface_is_relevant(i);
433
434    if (m->list_complete && b && !i->announcing) {
435        interface_mdns_mcast_join(i, 1);
436
437        if (i->mcast_joined) {
438            avahi_log_info("New relevant interface %s.%s for mDNS.", i->hardware->name, avahi_proto_to_string(i->protocol));
439
440            i->announcing = 1;
441            avahi_announce_interface(m->server, i);
442            avahi_multicast_lookup_engine_new_interface(m->server->multicast_lookup_engine, i);
443        }
444
445    } else if (!b && i->announcing) {
446        avahi_log_info("Interface %s.%s no longer relevant for mDNS.", i->hardware->name, avahi_proto_to_string(i->protocol));
447
448        interface_mdns_mcast_join(i, 0);
449
450        avahi_goodbye_interface(m->server, i, 0, 1);
451        avahi_querier_free_all(i);
452
453        avahi_response_scheduler_clear(i->response_scheduler);
454        avahi_query_scheduler_clear(i->query_scheduler);
455        avahi_probe_scheduler_clear(i->probe_scheduler);
456        avahi_cache_flush(i->cache);
457
458        i->announcing = 0;
459
460    } else
461        interface_mdns_mcast_rejoin(i);
462}
463
464void avahi_hw_interface_check_relevant(AvahiHwInterface *hw) {
465    AvahiInterface *i;
466
467    assert(hw);
468
469    for (i = hw->interfaces; i; i = i->by_hardware_next)
470        avahi_interface_check_relevant(i);
471}
472
473void avahi_interface_monitor_check_relevant(AvahiInterfaceMonitor *m) {
474    AvahiInterface *i;
475
476    assert(m);
477
478    for (i = m->interfaces; i; i = i->interface_next)
479        avahi_interface_check_relevant(i);
480}
481
482AvahiInterfaceMonitor *avahi_interface_monitor_new(AvahiServer *s) {
483    AvahiInterfaceMonitor *m = NULL;
484
485    if (!(m = avahi_new0(AvahiInterfaceMonitor, 1)))
486        return NULL; /* OOM */
487
488    m->server = s;
489    m->list_complete = 0;
490    m->hashmap = avahi_hashmap_new(avahi_int_hash, avahi_int_equal, NULL, NULL);
491
492    AVAHI_LLIST_HEAD_INIT(AvahiInterface, m->interfaces);
493    AVAHI_LLIST_HEAD_INIT(AvahiHwInterface, m->hw_interfaces);
494
495    if (avahi_interface_monitor_init_osdep(m) < 0)
496        goto fail;
497
498    return m;
499
500fail:
501    avahi_interface_monitor_free(m);
502    return NULL;
503}
504
505void avahi_interface_monitor_free(AvahiInterfaceMonitor *m) {
506    assert(m);
507
508    while (m->hw_interfaces)
509        avahi_hw_interface_free(m->hw_interfaces, 1);
510
511    assert(!m->interfaces);
512
513    avahi_interface_monitor_free_osdep(m);
514
515    if (m->hashmap)
516        avahi_hashmap_free(m->hashmap);
517
518    avahi_free(m);
519}
520
521
522AvahiInterface* avahi_interface_monitor_get_interface(AvahiInterfaceMonitor *m, AvahiIfIndex idx, AvahiProtocol protocol) {
523    AvahiHwInterface *hw;
524    AvahiInterface *i;
525
526    assert(m);
527    assert(idx >= 0);
528    assert(protocol != AVAHI_PROTO_UNSPEC);
529
530    if (!(hw = avahi_interface_monitor_get_hw_interface(m, idx)))
531        return NULL;
532
533    for (i = hw->interfaces; i; i = i->by_hardware_next)
534        if (i->protocol == protocol)
535            return i;
536
537    return NULL;
538}
539
540AvahiHwInterface* avahi_interface_monitor_get_hw_interface(AvahiInterfaceMonitor *m, AvahiIfIndex idx) {
541    assert(m);
542    assert(idx >= 0);
543
544    return avahi_hashmap_lookup(m->hashmap, &idx);
545}
546
547AvahiInterfaceAddress* avahi_interface_monitor_get_address(AvahiInterfaceMonitor *m, AvahiInterface *i, const AvahiAddress *raddr) {
548    AvahiInterfaceAddress *ia;
549
550    assert(m);
551    assert(i);
552    assert(raddr);
553
554    for (ia = i->addresses; ia; ia = ia->address_next)
555        if (avahi_address_cmp(&ia->address, raddr) == 0)
556            return ia;
557
558    return NULL;
559}
560
561void avahi_interface_send_packet_unicast(AvahiInterface *i, AvahiDnsPacket *p, const AvahiAddress *a, uint16_t port) {
562    assert(i);
563    assert(p);
564
565    if (!i->announcing)
566        return;
567
568    assert(!a || a->proto == i->protocol);
569
570    if (i->protocol == AVAHI_PROTO_INET && i->monitor->server->fd_ipv4 >= 0)
571        avahi_send_dns_packet_ipv4(i->monitor->server->fd_ipv4, i->hardware->index, p, i->mcast_joined ? &i->local_mcast_address.data.ipv4 : NULL, a ? &a->data.ipv4 : NULL, port);
572    else if (i->protocol == AVAHI_PROTO_INET6 && i->monitor->server->fd_ipv6 >= 0)
573        avahi_send_dns_packet_ipv6(i->monitor->server->fd_ipv6, i->hardware->index, p, i->mcast_joined ? &i->local_mcast_address.data.ipv6 : NULL, a ? &a->data.ipv6 : NULL, port);
574}
575
576void avahi_interface_send_packet(AvahiInterface *i, AvahiDnsPacket *p) {
577    assert(i);
578    assert(p);
579
580    avahi_interface_send_packet_unicast(i, p, NULL, 0);
581}
582
583int avahi_interface_post_query(AvahiInterface *i, AvahiKey *key, int immediately, unsigned *ret_id) {
584    assert(i);
585    assert(key);
586
587    if (!i->announcing)
588        return 0;
589
590    return avahi_query_scheduler_post(i->query_scheduler, key, immediately, ret_id);
591}
592
593int avahi_interface_withraw_query(AvahiInterface *i, unsigned id) {
594
595    return avahi_query_scheduler_withdraw_by_id(i->query_scheduler, id);
596}
597
598int avahi_interface_post_response(AvahiInterface *i, AvahiRecord *record, int flush_cache, const AvahiAddress *querier, int immediately) {
599    assert(i);
600    assert(record);
601
602    if (!i->announcing)
603        return 0;
604
605    return avahi_response_scheduler_post(i->response_scheduler, record, flush_cache, querier, immediately);
606}
607
608int avahi_interface_post_probe(AvahiInterface *i, AvahiRecord *record, int immediately) {
609    assert(i);
610    assert(record);
611
612    if (!i->announcing)
613        return 0;
614
615    return avahi_probe_scheduler_post(i->probe_scheduler, record, immediately);
616}
617
618int avahi_dump_caches(AvahiInterfaceMonitor *m, AvahiDumpCallback callback, void* userdata) {
619    AvahiInterface *i;
620    assert(m);
621
622    for (i = m->interfaces; i; i = i->interface_next) {
623        if (avahi_interface_is_relevant(i)) {
624            char ln[256];
625            snprintf(ln, sizeof(ln), ";;; INTERFACE %s.%s ;;;", i->hardware->name, avahi_proto_to_string(i->protocol));
626            callback(ln, userdata);
627            if (avahi_cache_dump(i->cache, callback, userdata) < 0)
628                return -1;
629        }
630    }
631
632    return 0;
633}
634
635static int avahi_interface_is_relevant_internal(AvahiInterface *i) {
636    AvahiInterfaceAddress *a;
637
638    assert(i);
639
640    if (!i->hardware->flags_ok)
641        return 0;
642
643    for (a = i->addresses; a; a = a->address_next)
644        if (avahi_interface_address_is_relevant(a))
645            return 1;
646
647    return 0;
648}
649
650int avahi_interface_is_relevant(AvahiInterface *i) {
651    AvahiStringList *l;
652    assert(i);
653
654    for (l = i->monitor->server->config.deny_interfaces; l; l = l->next)
655        if (strcasecmp((char*) l->text, i->hardware->name) == 0)
656            return 0;
657
658    if (i->monitor->server->config.allow_interfaces) {
659
660        for (l = i->monitor->server->config.allow_interfaces; l; l = l->next)
661            if (strcasecmp((char*) l->text, i->hardware->name) == 0)
662                goto good;
663
664        return 0;
665    }
666
667good:
668    return avahi_interface_is_relevant_internal(i);
669}
670
671int avahi_interface_address_is_relevant(AvahiInterfaceAddress *a) {
672    AvahiInterfaceAddress *b;
673    assert(a);
674
675    /* Publish public IP addresses */
676    if (a->global_scope)
677        return 1;
678    else {
679
680        /* Publish link local IP addresses if they are the only ones on the link */
681        for (b = a->interface->addresses; b; b = b->address_next) {
682            if (b == a)
683                continue;
684
685            if (b->global_scope)
686                return 0;
687        }
688
689        return 1;
690    }
691
692    return 0;
693}
694
695int avahi_interface_match(AvahiInterface *i, AvahiIfIndex idx, AvahiProtocol protocol) {
696    assert(i);
697
698    if (idx != AVAHI_IF_UNSPEC && idx != i->hardware->index)
699        return 0;
700
701    if (protocol != AVAHI_PROTO_UNSPEC && protocol != i->protocol)
702        return 0;
703
704    return 1;
705}
706
707void avahi_interface_monitor_walk(AvahiInterfaceMonitor *m, AvahiIfIndex interface, AvahiProtocol protocol, AvahiInterfaceMonitorWalkCallback callback, void* userdata) {
708    assert(m);
709    assert(callback);
710
711    if (interface != AVAHI_IF_UNSPEC) {
712        if (protocol != AVAHI_PROTO_UNSPEC) {
713            AvahiInterface *i;
714
715            if ((i = avahi_interface_monitor_get_interface(m, interface, protocol)))
716                callback(m, i, userdata);
717
718        } else {
719            AvahiHwInterface *hw;
720            AvahiInterface *i;
721
722            if ((hw = avahi_interface_monitor_get_hw_interface(m, interface)))
723                for (i = hw->interfaces; i; i = i->by_hardware_next)
724                    if (avahi_interface_match(i, interface, protocol))
725                        callback(m, i, userdata);
726        }
727
728    } else {
729        AvahiInterface *i;
730
731        for (i = m->interfaces; i; i = i->interface_next)
732            if (avahi_interface_match(i, interface, protocol))
733                callback(m, i, userdata);
734    }
735}
736
737
738int avahi_address_is_local(AvahiInterfaceMonitor *m, const AvahiAddress *a) {
739    AvahiInterface *i;
740    AvahiInterfaceAddress *ia;
741    assert(m);
742    assert(a);
743
744    for (i = m->interfaces; i; i = i->interface_next)
745        for (ia = i->addresses; ia; ia = ia->address_next)
746            if (avahi_address_cmp(a, &ia->address) == 0)
747                return 1;
748
749    return 0;
750}
751
752int avahi_interface_address_on_link(AvahiInterface *i, const AvahiAddress *a) {
753    AvahiInterfaceAddress *ia;
754
755    assert(i);
756    assert(a);
757
758    if (a->proto != i->protocol)
759        return 0;
760
761    for (ia = i->addresses; ia; ia = ia->address_next) {
762
763        if (a->proto == AVAHI_PROTO_INET) {
764            uint32_t m;
765
766            m = ~(((uint32_t) -1) >> ia->prefix_len);
767
768            if ((ntohl(a->data.ipv4.address) & m) == (ntohl(ia->address.data.ipv4.address) & m))
769                return 1;
770        } else {
771            unsigned j;
772            unsigned char pl;
773            assert(a->proto == AVAHI_PROTO_INET6);
774
775            pl = ia->prefix_len;
776
777            for (j = 0; j < 16; j++) {
778                uint8_t m;
779
780                if (pl == 0)
781                    return 1;
782
783                if (pl >= 8) {
784                    m = 0xFF;
785                    pl -= 8;
786                } else {
787                    m = ~(0xFF >> pl);
788                    pl = 0;
789                }
790
791                if ((a->data.ipv6.address[j] & m) != (ia->address.data.ipv6.address[j] & m))
792                    break;
793            }
794        }
795    }
796
797    return 0;
798}
799
800int avahi_interface_has_address(AvahiInterfaceMonitor *m, AvahiIfIndex iface, const AvahiAddress *a) {
801    AvahiInterface *i;
802    AvahiInterfaceAddress *j;
803
804    assert(m);
805    assert(iface != AVAHI_IF_UNSPEC);
806    assert(a);
807
808    if (!(i = avahi_interface_monitor_get_interface(m, iface, a->proto)))
809        return 0;
810
811    for (j = i->addresses; j; j = j->address_next)
812        if (avahi_address_cmp(a, &j->address) == 0)
813            return 1;
814
815    return 0;
816}
817
818AvahiIfIndex avahi_find_interface_for_address(AvahiInterfaceMonitor *m, const AvahiAddress *a) {
819    AvahiInterface *i;
820    assert(m);
821
822    /* Some stupid OS don't support passing the interface index when a
823     * packet is received. We have to work around that limitation by
824     * looking for an interface that has the incoming address
825     * attached. This is sometimes ambiguous, but we have to live with
826     * it. */
827
828    for (i = m->interfaces; i; i = i->interface_next) {
829        AvahiInterfaceAddress *ai;
830
831        if (i->protocol != a->proto)
832            continue;
833
834        for (ai = i->addresses; ai; ai = ai->address_next)
835            if (avahi_address_cmp(a, &ai->address) == 0)
836                return i->hardware->index;
837    }
838
839    return AVAHI_IF_UNSPEC;
840}
841