• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/router/avahi-0.6.31/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 <sys/types.h>
25#include <sys/socket.h>
26#include <netinet/in.h>
27#include <arpa/inet.h>
28#include <string.h>
29#include <sys/utsname.h>
30#include <unistd.h>
31#include <errno.h>
32#include <stdio.h>
33#include <assert.h>
34#include <stdlib.h>
35
36#include <avahi-common/domain.h>
37#include <avahi-common/timeval.h>
38#include <avahi-common/malloc.h>
39#include <avahi-common/error.h>
40
41#include "internal.h"
42#include "iface.h"
43#include "socket.h"
44#include "browse.h"
45#include "log.h"
46#include "util.h"
47#include "dns-srv-rr.h"
48#include "addr-util.h"
49#include "domain-util.h"
50#include "rr-util.h"
51
52#define AVAHI_DEFAULT_CACHE_ENTRIES_MAX 4096
53
54static void enum_aux_records(AvahiServer *s, AvahiInterface *i, const char *name, uint16_t type, void (*callback)(AvahiServer *s, AvahiRecord *r, int flush_cache, void* userdata), void* userdata) {
55    assert(s);
56    assert(i);
57    assert(name);
58    assert(callback);
59
60    if (type == AVAHI_DNS_TYPE_ANY) {
61        AvahiEntry *e;
62
63        for (e = s->mdns.entries; e; e = e->entries_next)
64            if (!e->dead &&
65                e->type == AVAHI_ENTRY_MDNS &&
66                avahi_entry_is_registered(s, e, i) &&
67                e->record->key->clazz == AVAHI_DNS_CLASS_IN &&
68                avahi_domain_equal(name, e->record->key->name))
69                callback(s, e->record, e->flags & AVAHI_PUBLISH_UNIQUE, userdata);
70
71    } else {
72        AvahiEntry *e;
73        AvahiKey *k;
74
75        if (!(k = avahi_key_new(name, AVAHI_DNS_CLASS_IN, type)))
76            return; /** OOM */
77
78        for (e = avahi_hashmap_lookup(s->mdns.entries_by_key, k); e; e = e->by_key_next)
79            if (!e->dead && e->type == AVAHI_ENTRY_MDNS && avahi_entry_is_registered(s, e, i))
80                callback(s, e->record, e->flags & AVAHI_PUBLISH_UNIQUE, userdata);
81
82        avahi_key_unref(k);
83    }
84}
85
86void avahi_server_enumerate_aux_records(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, void (*callback)(AvahiServer *s, AvahiRecord *r, int flush_cache, void* userdata), void* userdata) {
87    assert(s);
88    assert(i);
89    assert(r);
90    assert(callback);
91
92    /* Call the specified callback far all records referenced by the one specified in *r */
93
94    if (r->key->clazz == AVAHI_DNS_CLASS_IN) {
95        if (r->key->type == AVAHI_DNS_TYPE_PTR) {
96            enum_aux_records(s, i, r->data.ptr.name, AVAHI_DNS_TYPE_SRV, callback, userdata);
97            enum_aux_records(s, i, r->data.ptr.name, AVAHI_DNS_TYPE_TXT, callback, userdata);
98        } else if (r->key->type == AVAHI_DNS_TYPE_SRV) {
99            enum_aux_records(s, i, r->data.srv.name, AVAHI_DNS_TYPE_A, callback, userdata);
100            enum_aux_records(s, i, r->data.srv.name, AVAHI_DNS_TYPE_AAAA, callback, userdata);
101        } else if (r->key->type == AVAHI_DNS_TYPE_CNAME)
102            enum_aux_records(s, i, r->data.cname.name, AVAHI_DNS_TYPE_ANY, callback, userdata);
103    }
104}
105
106void avahi_server_prepare_response(AvahiServer *s, AvahiInterface *i, AvahiEntry *e, int unicast_response, int auxiliary) {
107    assert(s);
108    assert(i);
109    assert(e);
110    assert(e->type == AVAHI_ENTRY_MDNS);
111
112    avahi_record_list_push(s->mdns.record_list, e->record, e->flags & AVAHI_PUBLISH_UNIQUE, unicast_response, auxiliary);
113}
114
115void avahi_server_prepare_matching_responses(AvahiServer *s, AvahiInterface *i, AvahiKey *k, int unicast_response) {
116    assert(s);
117    assert(i);
118    assert(k);
119
120    /* Push all records that match the specified key to the record list */
121
122    if (avahi_key_is_pattern(k)) {
123        AvahiEntry *e;
124
125        /* Handle ANY query */
126
127        for (e = s->mdns.entries; e; e = e->entries_next)
128            if (!e->dead && avahi_key_pattern_match(k, e->record->key) && avahi_entry_is_registered(s, e, i))
129                avahi_server_prepare_response(s, i, e, unicast_response, 0);
130
131    } else {
132        AvahiEntry *e;
133
134        /* Handle all other queries */
135
136        for (e = avahi_hashmap_lookup(s->mdns.entries_by_key, k); e; e = e->by_key_next)
137            if (!e->dead && avahi_entry_is_registered(s, e, i))
138                avahi_server_prepare_response(s, i, e, unicast_response, 0);
139    }
140
141    /* Look for CNAME records */
142
143    if ((k->clazz == AVAHI_DNS_CLASS_IN || k->clazz == AVAHI_DNS_CLASS_ANY)
144        && k->type != AVAHI_DNS_TYPE_CNAME && k->type != AVAHI_DNS_TYPE_ANY) {
145
146        AvahiKey *cname_key;
147
148        if (!(cname_key = avahi_key_new(k->name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_CNAME)))
149            return;
150
151        avahi_server_prepare_matching_responses(s, i, cname_key, unicast_response);
152        avahi_key_unref(cname_key);
153    }
154}
155
156static void withdraw_entry(AvahiServer *s, AvahiEntry *e) {
157    assert(s);
158    assert(e && e->type == AVAHI_ENTRY_MDNS);
159
160    /* Withdraw the specified entry, and if is part of an entry group,
161     * put that into COLLISION state */
162
163    if (e->dead)
164        return;
165
166    if (e->group) {
167        AvahiEntry *k;
168
169        assert(e->group->type == AVAHI_GROUP_MDNS);
170        for (k = e->group->entries; k; k = k->by_group_next) {
171            assert(k->type == AVAHI_ENTRY_MDNS);
172            if (!k->dead) {
173                avahi_goodbye_entry(s, k, 0, 1);
174                k->dead = 1;
175            }
176        }
177
178        e->group->proto.mdns.n_probing = 0;
179
180        avahi_s_entry_group_change_state(e->group, AVAHI_ENTRY_GROUP_COLLISION);
181    } else {
182        avahi_goodbye_entry(s, e, 0, 1);
183        e->dead = 1;
184    }
185
186    s->mdns.need_entry_cleanup = 1;
187}
188
189static void withdraw_rrset(AvahiServer *s, AvahiKey *key) {
190    AvahiEntry *e;
191
192    assert(s);
193    assert(key);
194
195    /* Withdraw an entry RRSset */
196
197    for (e = avahi_hashmap_lookup(s->mdns.entries_by_key, key); e; e = e->by_key_next)
198        withdraw_entry(s, e);
199}
200
201static void incoming_probe(AvahiServer *s, AvahiRecord *record, AvahiInterface *i) {
202    AvahiEntry *e, *n;
203    int ours = 0, won = 0, lost = 0;
204
205    assert(s);
206    assert(record);
207    assert(i);
208
209    /* Handle incoming probes and check if they conflict our own probes */
210
211    for (e = avahi_hashmap_lookup(s->mdns.entries_by_key, record->key); e; e = n) {
212        int cmp;
213        n = e->by_key_next;
214
215        if (e->dead)
216            continue;
217
218        if ((cmp = avahi_record_lexicographical_compare(e->record, record)) == 0) {
219            ours = 1;
220            break;
221        } else {
222
223            if (avahi_entry_is_probing(s, e, i)) {
224                if (cmp > 0)
225                    won = 1;
226                else /* cmp < 0 */
227                    lost = 1;
228            }
229        }
230    }
231
232    if (!ours) {
233        char *t = avahi_record_to_string(record);
234
235        if (won)
236            avahi_log_debug("Received conflicting probe [%s]. Local host won.", t);
237        else if (lost) {
238            avahi_log_debug("Received conflicting probe [%s]. Local host lost. Withdrawing.", t);
239            withdraw_rrset(s, record->key);
240        }
241
242        avahi_free(t);
243    }
244}
245
246static int handle_conflict(AvahiServer *s, AvahiInterface *i, AvahiRecord *record, int unique) {
247    int valid = 1, ours = 0, conflict = 0, withdraw_immediately = 0;
248    AvahiEntry *e, *n, *conflicting_entry = NULL;
249
250    assert(s);
251    assert(i);
252    assert(record);
253
254    /* Check whether an incoming record conflicts with one of our own */
255
256    for (e = avahi_hashmap_lookup(s->mdns.entries_by_key, record->key); e; e = n) {
257        assert(e->type == AVAHI_ENTRY_MDNS);
258        n = e->by_key_next;
259
260        if (e->dead)
261            continue;
262
263        /* Check if the incoming is a goodbye record */
264        if (avahi_record_is_goodbye(record)) {
265
266            if (avahi_record_equal_no_ttl(e->record, record)) {
267                char *t;
268
269                /* Refresh */
270                t = avahi_record_to_string(record);
271                avahi_log_debug("Received goodbye record for one of our records [%s]. Refreshing.", t);
272                avahi_server_prepare_matching_responses(s, i, e->record->key, 0);
273
274                valid = 0;
275                avahi_free(t);
276                break;
277            }
278
279            /* If the goodybe packet doesn't match one of our own RRs, we simply ignore it. */
280            continue;
281        }
282
283        if (!(e->flags & AVAHI_PUBLISH_UNIQUE) && !unique)
284            continue;
285
286        /* Either our entry or the other is intended to be unique, so let's check */
287
288        if (avahi_record_equal_no_ttl(e->record, record)) {
289            ours = 1; /* We have an identical record, so this is no conflict */
290
291            /* Check wheter there is a TTL conflict */
292            if (record->ttl <= e->record->ttl/2 &&
293                avahi_entry_is_registered(s, e, i)) {
294                char *t;
295                /* Refresh */
296                t = avahi_record_to_string(record);
297
298                avahi_log_debug("Received record with bad TTL [%s]. Refreshing.", t);
299                avahi_server_prepare_matching_responses(s, i, e->record->key, 0);
300                valid = 0;
301
302                avahi_free(t);
303            }
304
305            /* There's no need to check the other entries of this RRset */
306            break;
307
308        } else {
309
310            if (avahi_entry_is_registered(s, e, i)) {
311
312                /* A conflict => we have to return to probe mode */
313                conflict = 1;
314                conflicting_entry = e;
315
316            } else if (avahi_entry_is_probing(s, e, i)) {
317
318                /* We are currently registering a matching record, but
319                 * someone else already claimed it, so let's
320                 * withdraw */
321                conflict = 1;
322                withdraw_immediately = 1;
323            }
324        }
325    }
326
327    if (!ours && conflict) {
328        char *t;
329
330        valid = 0;
331
332        t = avahi_record_to_string(record);
333
334        if (withdraw_immediately) {
335            avahi_log_debug("Received conflicting record [%s] with local record to be. Withdrawing.", t);
336            withdraw_rrset(s, record->key);
337        } else {
338            assert(conflicting_entry);
339            avahi_log_debug("Received conflicting record [%s]. Resetting our record.", t);
340            avahi_entry_return_to_initial_state(s, conflicting_entry, i);
341
342            /* Local unique records are returned to probing
343             * state. Local shared records are reannounced. */
344        }
345
346        avahi_free(t);
347    }
348
349    return valid;
350}
351
352static void append_aux_callback(AvahiServer *s, AvahiRecord *r, int flush_cache, void* userdata) {
353    int *unicast_response = userdata;
354
355    assert(s);
356    assert(r);
357    assert(unicast_response);
358
359    avahi_record_list_push(s->mdns.record_list, r, flush_cache, *unicast_response, 1);
360}
361
362static void append_aux_records_to_list(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, int unicast_response) {
363    assert(s);
364    assert(r);
365
366    avahi_server_enumerate_aux_records(s, i, r, append_aux_callback, &unicast_response);
367}
368
369void avahi_server_generate_response(AvahiServer *s, AvahiInterface *i, AvahiDnsPacket *p, const AvahiAddress *a, uint16_t port, int legacy_unicast, int immediately) {
370
371    assert(s);
372    assert(i);
373    assert(!legacy_unicast || (a && port > 0 && p));
374
375    if (legacy_unicast) {
376        AvahiDnsPacket *reply;
377        AvahiRecord *r;
378
379        if (!(reply = avahi_dns_packet_new_reply(p, 512 + AVAHI_DNS_PACKET_EXTRA_SIZE /* unicast DNS maximum packet size is 512 */ , 1, 1)))
380            return; /* OOM */
381
382        while ((r = avahi_record_list_next(s->mdns.record_list, NULL, NULL, NULL))) {
383
384            append_aux_records_to_list(s, i, r, 0);
385
386            if (avahi_dns_packet_append_record(reply, r, 0, 10))
387                avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
388            else {
389                char *t = avahi_record_to_string(r);
390                avahi_log_warn("Record [%s] not fitting in legacy unicast packet, dropping.", t);
391                avahi_free(t);
392            }
393
394            avahi_record_unref(r);
395        }
396
397        if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) != 0)
398            avahi_interface_send_packet_unicast(i, reply, a, port, /*for fd*/ AVAHI_MDNS);
399
400        avahi_dns_packet_free(reply);
401
402    } else {
403        int unicast_response, flush_cache, auxiliary;
404        AvahiDnsPacket *reply = NULL;
405        AvahiRecord *r;
406
407        /* In case the query packet was truncated never respond
408        immediately, because known answer suppression records might be
409        contained in later packets */
410        int tc = p && !!(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_TC);
411
412        while ((r = avahi_record_list_next(s->mdns.record_list, &flush_cache, &unicast_response, &auxiliary))) {
413
414            int im = immediately;
415
416            /* Only send the response immediately if it contains a
417             * unique entry AND it is not in reply to a truncated
418             * packet AND it is not an auxiliary record AND all other
419             * responses for this record are unique too. */
420
421            if (flush_cache && !tc && !auxiliary && avahi_record_list_all_flush_cache(s->mdns.record_list))
422                im = 1;
423
424            if (!avahi_interface_post_response(i, r, flush_cache, a, im) && unicast_response) {
425
426                /* Due to some reasons the record has not been scheduled.
427                 * The client requested an unicast response in that
428                 * case. Therefore we prepare such a response */
429
430                append_aux_records_to_list(s, i, r, unicast_response);
431
432                for (;;) {
433
434                    if (!reply) {
435                        assert(p);
436
437                        if (!(reply = avahi_dns_packet_new_reply(p, i->hardware->mtu, 0, 0)))
438                            break; /* OOM */
439                    }
440
441                    if (avahi_dns_packet_append_record(reply, r, flush_cache, 0)) {
442
443                        /* Appending this record succeeded, so incremeant
444                         * the specific header field, and return to the caller */
445
446                        avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
447                        break;
448                    }
449
450                    if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) == 0) {
451                        size_t size;
452
453                        /* The record is too large for one packet, so create a larger packet */
454
455                        avahi_dns_packet_free(reply);
456                        size = avahi_record_get_estimate_size(r) + AVAHI_DNS_PACKET_HEADER_SIZE;
457
458                        if (!(reply = avahi_dns_packet_new_reply(p, size + AVAHI_DNS_PACKET_EXTRA_SIZE, 0, 1)))
459                            break; /* OOM */
460
461                        if (avahi_dns_packet_append_record(reply, r, flush_cache, 0)) {
462
463                            /* Appending this record succeeded, so incremeant
464                             * the specific header field, and return to the caller */
465
466                            avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
467                            break;
468
469                        }  else {
470
471                            /* We completely fucked up, there's
472                             * nothing we can do. The RR just doesn't
473                             * fit in. Let's ignore it. */
474
475                            char *t;
476                            avahi_dns_packet_free(reply);
477                            reply = NULL;
478                            t = avahi_record_to_string(r);
479                            avahi_log_warn("Record [%s] too large, doesn't fit in any packet!", t);
480                            avahi_free(t);
481                            break;
482                        }
483                    }
484
485                    /* Appending the record didn't succeeed, so let's send this packet, and create a new one */
486                    avahi_interface_send_packet_unicast(i, reply, a, port, AVAHI_MDNS);
487                    avahi_dns_packet_free(reply);
488                    reply = NULL;
489                }
490            }
491
492            avahi_record_unref(r);
493        }
494
495        if (reply) {
496            if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) != 0)
497                avahi_interface_send_packet_unicast(i, reply, a, port, AVAHI_MDNS);
498            avahi_dns_packet_free(reply);
499        }
500    }
501
502    avahi_record_list_flush(s->mdns.record_list);
503}
504
505static void reflect_response(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, int flush_cache) {
506    AvahiInterface *j;
507
508    assert(s);
509    assert(i);
510    assert(r);
511
512    if (!s->config.enable_reflector)
513        return;
514
515    for (j = s->monitor->interfaces; j; j = j->interface_next)
516        if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol))
517            avahi_interface_post_response(j, r, flush_cache, NULL, 1);
518}
519
520static void* reflect_cache_walk_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, void* userdata) {
521    AvahiServer *s = userdata;
522    AvahiRecord* r;
523
524    assert(c);
525    assert(pattern);
526    assert(e);
527    assert(s);
528
529    /* Don't reflect cache entry with ipv6 link-local addresses. */
530    r = e->record;
531    if ((r->key->type == AVAHI_DNS_TYPE_AAAA) &&
532            (r->data.aaaa.address.address[0] == 0xFE) &&
533            (r->data.aaaa.address.address[1] == 0x80))
534      return NULL;
535
536    avahi_record_list_push(s->mdns.record_list, e->record, e->cache_flush, 0, 0);
537    return NULL;
538}
539
540static void reflect_query(AvahiServer *s, AvahiInterface *i, AvahiKey *k) {
541    AvahiInterface *j;
542
543    assert(s);
544    assert(i);
545    assert(k);
546
547    if (!s->config.enable_reflector)
548        return;
549
550    for (j = s->monitor->interfaces; j; j = j->interface_next)
551        if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol)) {
552            /* Post the query to other networks */
553            avahi_interface_post_query(j, k, 1, NULL);
554
555            /* Reply from caches of other network. This is needed to
556             * "work around" known answer suppression. */
557
558            avahi_cache_walk(j->mdns.cache, k, reflect_cache_walk_callback, s);
559        }
560}
561
562static void reflect_probe(AvahiServer *s, AvahiInterface *i, AvahiRecord *r) {
563    AvahiInterface *j;
564
565    assert(s);
566    assert(i);
567    assert(r);
568
569    if (!s->config.enable_reflector)
570        return;
571
572    for (j = s->monitor->interfaces; j; j = j->interface_next)
573        if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol))
574            avahi_interface_post_probe(j, r, 1);
575}
576
577static void handle_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, uint16_t port, int legacy_unicast, int from_local_iface) {
578    size_t n;
579    int is_probe;
580
581    assert(s);
582    assert(p);
583    assert(i);
584    assert(a);
585
586    assert(avahi_record_list_is_empty(s->mdns.record_list));
587
588    is_probe = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) > 0;
589
590    /* Handle the questions */
591    for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT); n > 0; n --) {
592        AvahiKey *key;
593        int unicast_response = 0;
594
595        if (!(key = avahi_dns_packet_consume_key(p, &unicast_response))) {
596            avahi_log_warn(__FILE__": Packet too short or invalid while reading question key. (Maybe a UTF-8 problem?)");
597            goto fail;
598        }
599
600        if (!legacy_unicast && !from_local_iface) {
601            reflect_query(s, i, key);
602            if (!unicast_response)
603              avahi_cache_start_poof(i->mdns.cache, key, a);
604        }
605
606        if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 &&
607            !(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_TC))
608            /* Allow our own queries to be suppressed by incoming
609             * queries only when they do not include known answers */
610            avahi_query_scheduler_incoming(i->mdns.query_scheduler, key);
611
612        avahi_server_prepare_matching_responses(s, i, key, unicast_response);
613        avahi_key_unref(key);
614    }
615
616    if (!legacy_unicast) {
617
618        /* Known Answer Suppression */
619        for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT); n > 0; n --) {
620            AvahiRecord *record;
621            int unique = 0;
622
623            if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
624                avahi_log_warn(__FILE__": Packet too short or invalid while reading known answer record. (Maybe a UTF-8 problem?)");
625                goto fail;
626            }
627
628            avahi_response_scheduler_suppress(i->mdns.response_scheduler, record, a);
629            avahi_record_list_drop(s->mdns.record_list, record);
630            avahi_cache_stop_poof(i->mdns.cache, record, a);
631
632            avahi_record_unref(record);
633        }
634
635        /* Probe record */
636        for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT); n > 0; n --) {
637            AvahiRecord *record;
638            int unique = 0;
639
640            if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
641                avahi_log_warn(__FILE__": Packet too short or invalid while reading probe record. (Maybe a UTF-8 problem?)");
642                goto fail;
643            }
644
645            if (!avahi_key_is_pattern(record->key)) {
646                if (!from_local_iface)
647                    reflect_probe(s, i, record);
648                incoming_probe(s, record, i);
649            }
650
651            avahi_record_unref(record);
652        }
653    }
654
655    if (!avahi_record_list_is_empty(s->mdns.record_list))
656        avahi_server_generate_response(s, i, p, a, port, legacy_unicast, is_probe);
657
658    return;
659
660fail:
661    avahi_record_list_flush(s->mdns.record_list);
662}
663
664static void handle_response_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, int from_local_iface) {
665    unsigned n;
666
667    assert(s);
668    assert(p);
669    assert(i);
670    assert(a);
671
672    for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) +
673             avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT); n > 0; n--) {
674        AvahiRecord *record;
675        int cache_flush = 0;
676
677        if (!(record = avahi_dns_packet_consume_record(p, &cache_flush))) {
678            avahi_log_warn(__FILE__": Packet too short or invalid while reading response record. (Maybe a UTF-8 problem?)");
679            break;
680        }
681
682        if (!avahi_key_is_pattern(record->key)) {
683
684            if (handle_conflict(s, i, record, cache_flush)) {
685                if (!from_local_iface && !avahi_record_is_link_local_address(record))
686                    reflect_response(s, i, record, cache_flush);
687                avahi_cache_update(i->mdns.cache, record, cache_flush, a);
688                avahi_response_scheduler_incoming(i->mdns.response_scheduler, record, cache_flush);
689            }
690        }
691
692        avahi_record_unref(record);
693    }
694
695    /* If the incoming response contained a conflicting record, some
696       records have been scheduled for sending. We need to flush them
697       here. */
698    if (!avahi_record_list_is_empty(s->mdns.record_list))
699        avahi_server_generate_response(s, i, NULL, NULL, 0, 0, 1);
700}
701
702static AvahiLegacyUnicastReflectSlot* allocate_slot(AvahiServer *s) {
703    unsigned n, idx = (unsigned) -1;
704    AvahiLegacyUnicastReflectSlot *slot;
705
706    assert(s);
707
708    if (!s->legacy_unicast_reflect_slots)
709        s->legacy_unicast_reflect_slots = avahi_new0(AvahiLegacyUnicastReflectSlot*, AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX);
710
711    for (n = 0; n < AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX; n++, s->legacy_unicast_reflect_id++) {
712        idx = s->legacy_unicast_reflect_id % AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX;
713
714        if (!s->legacy_unicast_reflect_slots[idx])
715            break;
716    }
717
718    if (idx == (unsigned) -1 || s->legacy_unicast_reflect_slots[idx])
719        return NULL;
720
721    if (!(slot = avahi_new(AvahiLegacyUnicastReflectSlot, 1)))
722        return NULL; /* OOM */
723
724    s->legacy_unicast_reflect_slots[idx] = slot;
725    slot->id = s->legacy_unicast_reflect_id++;
726    slot->server = s;
727
728    return slot;
729}
730
731static void deallocate_slot(AvahiServer *s, AvahiLegacyUnicastReflectSlot *slot) {
732    unsigned idx;
733
734    assert(s);
735    assert(slot);
736
737    idx = slot->id % AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX;
738
739    assert(s->legacy_unicast_reflect_slots[idx] == slot);
740
741    avahi_time_event_free(slot->time_event);
742
743    avahi_free(slot);
744    s->legacy_unicast_reflect_slots[idx] = NULL;
745}
746
747static void free_slots(AvahiServer *s) {
748    unsigned idx;
749    assert(s);
750
751    if (!s->legacy_unicast_reflect_slots)
752        return;
753
754    for (idx = 0; idx < AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX; idx ++)
755        if (s->legacy_unicast_reflect_slots[idx])
756            deallocate_slot(s, s->legacy_unicast_reflect_slots[idx]);
757
758    avahi_free(s->legacy_unicast_reflect_slots);
759    s->legacy_unicast_reflect_slots = NULL;
760}
761
762static AvahiLegacyUnicastReflectSlot* find_slot(AvahiServer *s, uint16_t id) {
763    unsigned idx;
764
765    assert(s);
766
767    if (!s->legacy_unicast_reflect_slots)
768        return NULL;
769
770    idx = id % AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX;
771
772    if (!s->legacy_unicast_reflect_slots[idx] || s->legacy_unicast_reflect_slots[idx]->id != id)
773        return NULL;
774
775    return s->legacy_unicast_reflect_slots[idx];
776}
777
778static void legacy_unicast_reflect_slot_timeout(AvahiTimeEvent *e, void *userdata) {
779    AvahiLegacyUnicastReflectSlot *slot = userdata;
780
781    assert(e);
782    assert(slot);
783    assert(slot->time_event == e);
784
785    deallocate_slot(slot->server, slot);
786}
787
788static void reflect_legacy_unicast_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, uint16_t port) {
789    AvahiLegacyUnicastReflectSlot *slot;
790    AvahiInterface *j;
791
792    assert(s);
793    assert(p);
794    assert(i);
795    assert(a);
796    assert(port > 0);
797    assert(i->protocol == a->proto);
798
799    if (!s->config.enable_reflector)
800        return;
801
802    /* Reflecting legacy unicast queries is a little more complicated
803       than reflecting normal queries, since we must route the
804       responses back to the right client. Therefore we must store
805       some information for finding the right client contact data for
806       response packets. In contrast to normal queries legacy
807       unicast query and response packets are reflected untouched and
808       are not reassembled into larger packets */
809
810    if (!(slot = allocate_slot(s))) {
811        /* No slot available, we drop this legacy unicast query */
812        avahi_log_warn("No slot available for legacy unicast reflection, dropping query packet.");
813        return;
814    }
815
816    slot->original_id = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID);
817    slot->address = *a;
818    slot->port = port;
819    slot->interface = i->hardware->index;
820
821    avahi_elapse_time(&slot->elapse_time, 2000, 0);
822    slot->time_event = avahi_time_event_new(s->time_event_queue, &slot->elapse_time, legacy_unicast_reflect_slot_timeout, slot);
823
824    /* Patch the packet with our new locally generatet id */
825    avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
826
827    for (j = s->monitor->interfaces; j; j = j->interface_next)
828        if (j->mdns.announcing &&
829            j != i &&
830            (s->config.reflect_ipv || j->protocol == i->protocol)) {
831
832            if (j->protocol == AVAHI_PROTO_INET && s->mdns.fd_legacy_unicast_ipv4 >= 0) {
833                avahi_send_dns_packet_ipv4(s->mdns.fd_legacy_unicast_ipv4, j->hardware->index, p, NULL, NULL, 0, AVAHI_MDNS);
834            } else if (j->protocol == AVAHI_PROTO_INET6 && s->mdns.fd_legacy_unicast_ipv6 >= 0)
835                avahi_send_dns_packet_ipv6(s->mdns.fd_legacy_unicast_ipv6, j->hardware->index, p, NULL, NULL, 0, AVAHI_MDNS);
836        }
837
838    /* Reset the id */
839    avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
840}
841
842static int originates_from_local_legacy_unicast_socket(AvahiServer *s, const AvahiAddress *address, uint16_t port) {
843    assert(s);
844    assert(address);
845    assert(port > 0);
846
847    if (!s->config.enable_reflector)
848        return 0;
849
850    if (!avahi_address_is_local(s->monitor, address))
851        return 0;
852
853    if (address->proto == AVAHI_PROTO_INET && s->mdns.fd_legacy_unicast_ipv4 >= 0) {
854        struct sockaddr_in lsa;
855        socklen_t l = sizeof(lsa);
856
857        if (getsockname(s->mdns.fd_legacy_unicast_ipv4, (struct sockaddr*) &lsa, &l) != 0)
858            avahi_log_warn("getsockname(): %s", strerror(errno));
859        else
860            return avahi_port_from_sockaddr((struct sockaddr*) &lsa) == port;
861
862    }
863
864    if (address->proto == AVAHI_PROTO_INET6 && s->mdns.fd_legacy_unicast_ipv6 >= 0) {
865        struct sockaddr_in6 lsa;
866        socklen_t l = sizeof(lsa);
867
868        if (getsockname(s->mdns.fd_legacy_unicast_ipv6, (struct sockaddr*) &lsa, &l) != 0)
869            avahi_log_warn("getsockname(): %s", strerror(errno));
870        else
871            return avahi_port_from_sockaddr((struct sockaddr*) &lsa) == port;
872    }
873
874    return 0;
875}
876
877static int is_mdns_mcast_address(const AvahiAddress *a) {
878    AvahiAddress b;
879    assert(a);
880
881    avahi_address_parse(a->proto == AVAHI_PROTO_INET ? AVAHI_IPV4_MCAST_GROUP : AVAHI_IPV6_MCAST_GROUP, a->proto, &b);
882    return avahi_address_cmp(a, &b) == 0;
883}
884
885static int originates_from_local_iface(AvahiServer *s, AvahiIfIndex iface, const AvahiAddress *a, uint16_t port) {
886    assert(s);
887    assert(iface != AVAHI_IF_UNSPEC);
888    assert(a);
889
890    /* If it isn't the MDNS port it can't be generated by us */
891    if (port != AVAHI_MDNS_PORT)
892        return 0;
893
894    return avahi_interface_has_address(s->monitor, iface, a);
895}
896
897static void dispatch_packet(AvahiServer *s, AvahiDnsPacket *p, const AvahiAddress *src_address, uint16_t port, const AvahiAddress *dst_address, AvahiIfIndex iface, int ttl) {
898    AvahiInterface *i;
899    int from_local_iface = 0;
900
901    assert(s);
902    assert(p);
903    assert(src_address);
904    assert(dst_address);
905    assert(iface > 0);
906    assert(src_address->proto == dst_address->proto);
907
908    if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, src_address->proto)) ||
909        !i->mdns.announcing) {
910        avahi_log_warn("Received packet from invalid interface.");
911        return;
912    }
913
914    if (port <= 0) {
915        /* This fixes RHBZ #475394 */
916        avahi_log_warn("Received packet from invalid source port %u.", (unsigned) port);
917        return;
918    }
919
920    if (avahi_address_is_ipv4_in_ipv6(src_address))
921        /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
922        return;
923
924    if (originates_from_local_legacy_unicast_socket(s, src_address, port))
925        /* This originates from our local reflector, so let's ignore it */
926        return;
927
928    /* We don't want to reflect local traffic, so we check if this packet is generated locally. */
929    if (s->config.enable_reflector)
930        from_local_iface = originates_from_local_iface(s, iface, src_address, port);
931
932    if (avahi_dns_packet_check_valid_multicast(p) < 0) {
933        avahi_log_warn("Received invalid packet.");
934        return;
935    }
936
937    if (avahi_dns_packet_is_query(p)) {
938        int legacy_unicast = 0;
939
940        /* For queries EDNS0 might allow ARCOUNT != 0. We ignore the
941         * AR section completely here, so far. Until the day we add
942         * EDNS0 support. */
943
944        if (port != AVAHI_MDNS_PORT) {
945            /* Legacy Unicast */
946
947            if ((avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) != 0 ||
948                 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0)) {
949                avahi_log_warn("Invalid legacy unicast query packet.");
950                return;
951            }
952
953            legacy_unicast = 1;
954        }
955
956        if (legacy_unicast)
957            reflect_legacy_unicast_query_packet(s, p, i, src_address, port);
958
959        handle_query_packet(s, p, i, src_address, port, legacy_unicast, from_local_iface);
960
961    } else {
962        char t[AVAHI_ADDRESS_STR_MAX];
963
964        if (port != AVAHI_MDNS_PORT) {
965            avahi_log_warn("Received response from host %s with invalid source port %u on interface '%s.%i'", avahi_address_snprint(t, sizeof(t), src_address), port, i->hardware->name, i->protocol);
966            return;
967        }
968
969        if (ttl != 255 && s->config.check_response_ttl) {
970            avahi_log_warn("Received response from host %s with invalid TTL %u on interface '%s.%i'.", avahi_address_snprint(t, sizeof(t), src_address), ttl, i->hardware->name, i->protocol);
971            return;
972        }
973
974        if (!is_mdns_mcast_address(dst_address) &&
975            !avahi_interface_address_on_link(i, src_address)) {
976
977            avahi_log_warn("Received non-local response from host %s on interface '%s.%i'.", avahi_address_snprint(t, sizeof(t), src_address), i->hardware->name, i->protocol);
978            return;
979        }
980
981        if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT) != 0 ||
982            avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 ||
983            avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0) {
984
985            avahi_log_warn("Invalid response packet from host %s.", avahi_address_snprint(t, sizeof(t), src_address));
986            return;
987        }
988
989        handle_response_packet(s, p, i, src_address, from_local_iface);
990    }
991}
992
993static void dispatch_legacy_unicast_packet(AvahiServer *s, AvahiDnsPacket *p) {
994    AvahiInterface *j;
995    AvahiLegacyUnicastReflectSlot *slot;
996
997    assert(s);
998    assert(p);
999
1000    if (avahi_dns_packet_check_valid(p) < 0 || avahi_dns_packet_is_query(p)) {
1001        avahi_log_warn("Received invalid packet.");
1002        return;
1003    }
1004
1005    if (!(slot = find_slot(s, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID)))) {
1006        avahi_log_warn("Received legacy unicast response with unknown id");
1007        return;
1008    }
1009
1010    if (!(j = avahi_interface_monitor_get_interface(s->monitor, slot->interface, slot->address.proto)) ||
1011        !j->mdns.announcing)
1012        return;
1013
1014    /* Patch the original ID into this response */
1015    avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
1016
1017    /* Forward the response to the correct client */
1018    avahi_interface_send_packet_unicast(j, p, &slot->address, slot->port, AVAHI_MDNS);
1019
1020    /* Undo changes to packet */
1021    avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
1022}
1023
1024static void mcast_socket_event(AvahiWatch *w, int fd, AvahiWatchEvent events, void *userdata) {
1025    AvahiServer *s = userdata;
1026    AvahiAddress dest, src;
1027    AvahiDnsPacket *p = NULL;
1028    AvahiIfIndex iface;
1029    uint16_t port;
1030    uint8_t ttl;
1031
1032    assert(w);
1033    assert(fd >= 0);
1034    assert(events & AVAHI_WATCH_IN);
1035
1036    if (fd == s->mdns.fd_ipv4) {
1037        dest.proto = src.proto = AVAHI_PROTO_INET;
1038        p = avahi_recv_dns_packet_ipv4(s->mdns.fd_ipv4, &src.data.ipv4, &port, &dest.data.ipv4, &iface, &ttl);
1039    } else {
1040        assert(fd == s->mdns.fd_ipv6);
1041        dest.proto = src.proto = AVAHI_PROTO_INET6;
1042        p = avahi_recv_dns_packet_ipv6(s->mdns.fd_ipv6, &src.data.ipv6, &port, &dest.data.ipv6, &iface, &ttl);
1043    }
1044
1045    if (p) {
1046        if (iface == AVAHI_IF_UNSPEC)
1047            iface = avahi_find_interface_for_address(s->monitor, &dest);
1048
1049        if (iface != AVAHI_IF_UNSPEC)
1050            dispatch_packet(s, p, &src, port, &dest, iface, ttl);
1051        else
1052            avahi_log_error("Incoming packet received on address that isn't local.");
1053
1054        avahi_dns_packet_free(p);
1055
1056        avahi_cleanup_dead_entries(s);
1057    }
1058}
1059
1060static void legacy_unicast_socket_event(AvahiWatch *w, int fd, AvahiWatchEvent events, void *userdata) {
1061    AvahiServer *s = userdata;
1062    AvahiDnsPacket *p = NULL;
1063
1064    assert(w);
1065    assert(fd >= 0);
1066    assert(events & AVAHI_WATCH_IN);
1067
1068    if (fd == s->mdns.fd_legacy_unicast_ipv4)
1069        p = avahi_recv_dns_packet_ipv4(s->mdns.fd_legacy_unicast_ipv4, NULL, NULL, NULL, NULL, NULL);
1070    else {
1071        assert(fd == s->mdns.fd_legacy_unicast_ipv6);
1072        p = avahi_recv_dns_packet_ipv6(s->mdns.fd_legacy_unicast_ipv6, NULL, NULL, NULL, NULL, NULL);
1073    }
1074
1075    if (p) {
1076        dispatch_legacy_unicast_packet(s, p);
1077        avahi_dns_packet_free(p);
1078
1079        avahi_cleanup_dead_entries(s);
1080    }
1081}
1082
1083static void server_set_state(AvahiServer *s, AvahiServerState state) {
1084    assert(s);
1085
1086    if (s->state == state)
1087        return;
1088
1089    s->state = state;
1090
1091    avahi_interface_monitor_update_rrs(s->monitor, 0);
1092
1093    if (s->callback)
1094        s->callback(s, state, s->userdata);
1095}
1096
1097static void withdraw_host_rrs(AvahiServer *s) {
1098    assert(s);
1099
1100    if (s->mdns.hinfo_entry_group)
1101        avahi_s_entry_group_reset(s->mdns.hinfo_entry_group);
1102
1103    if (s->mdns.browse_domain_entry_group)
1104        avahi_s_entry_group_reset(s->mdns.browse_domain_entry_group);
1105
1106    avahi_interface_monitor_update_rrs(s->monitor, 1);
1107    s->n_host_rr_pending = 0;
1108}
1109
1110void avahi_server_decrease_host_rr_pending(AvahiServer *s) {
1111    assert(s);
1112
1113    assert(s->n_host_rr_pending > 0);
1114
1115    if (--s->n_host_rr_pending == 0) {
1116        server_set_state(s, AVAHI_SERVER_RUNNING);
1117        avahi_log_info("\nAll host RR's have been announced/verified : SERVER RUNNING");
1118    }
1119}
1120
1121void avahi_host_rr_entry_group_callback(AvahiServer *s, AvahiSEntryGroup *g, AvahiEntryGroupState state, AVAHI_GCC_UNUSED void *userdata) {
1122    assert(s);
1123    assert(g);
1124
1125    if ((state == AVAHI_ENTRY_GROUP_REGISTERING || state == AVAHI_ENTRY_GROUP_LLMNR_VERIFYING) &&
1126        s->state == AVAHI_SERVER_REGISTERING)
1127        s->n_host_rr_pending ++;
1128
1129    else if ((state == AVAHI_ENTRY_GROUP_COLLISION || state == AVAHI_ENTRY_GROUP_LLMNR_COLLISION) &&
1130        (s->state == AVAHI_SERVER_REGISTERING || s->state == AVAHI_SERVER_RUNNING)) {
1131        withdraw_host_rrs(s);
1132        server_set_state(s, AVAHI_SERVER_COLLISION);
1133
1134    } else if ((state == AVAHI_ENTRY_GROUP_ESTABLISHED || state == AVAHI_ENTRY_GROUP_LLMNR_ESTABLISHED) &&
1135               s->state == AVAHI_SERVER_REGISTERING)
1136        avahi_server_decrease_host_rr_pending(s);
1137}
1138
1139static void register_hinfo(AvahiServer *s) {
1140    struct utsname utsname;
1141    AvahiRecord *r;
1142
1143    assert(s);
1144
1145    if (!s->config.publish_hinfo)
1146        return;
1147
1148    if (s->mdns.hinfo_entry_group)
1149        assert(avahi_s_entry_group_is_empty(s->mdns.hinfo_entry_group));
1150    else
1151        s->mdns.hinfo_entry_group = avahi_s_entry_group_new(s, avahi_host_rr_entry_group_callback, NULL);
1152
1153    if (!s->mdns.hinfo_entry_group) {
1154        avahi_log_warn("Failed to create HINFO entry group: %s", avahi_strerror(s->error));
1155        return;
1156    }
1157
1158    /* Fill in HINFO rr */
1159    if ((r = avahi_record_new_full(s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_HINFO, AVAHI_DEFAULT_TTL_HOST_NAME))) {
1160
1161        if (uname(&utsname) < 0)
1162            avahi_log_warn("uname() failed: %s\n", avahi_strerror(errno));
1163        else {
1164
1165            r->data.hinfo.cpu = avahi_strdup(avahi_strup(utsname.machine));
1166            r->data.hinfo.os = avahi_strdup(avahi_strup(utsname.sysname));
1167
1168            avahi_log_info("Registering HINFO record with values '%s'/'%s'.", r->data.hinfo.cpu, r->data.hinfo.os);
1169
1170            if (avahi_server_add(s, s->mdns.hinfo_entry_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_UNIQUE, r) < 0) {
1171                avahi_log_warn("Failed to add HINFO RR: %s", avahi_strerror(s->error));
1172                return;
1173            }
1174        }
1175
1176        avahi_record_unref(r);
1177    }
1178
1179    if (avahi_s_entry_group_commit(s->mdns.hinfo_entry_group) < 0)
1180        avahi_log_warn("Failed to commit HINFO entry group: %s", avahi_strerror(s->error));
1181
1182}
1183
1184static void register_localhost(AvahiServer *s) {
1185    AvahiAddress a;
1186    assert(s);
1187
1188    /* Add localhost entries */
1189    avahi_address_parse("127.0.0.1", AVAHI_PROTO_INET, &a);
1190    avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_NO_PROBE|AVAHI_PUBLISH_NO_ANNOUNCE|AVAHI_PUBLISH_USE_MULTICAST, "localhost", &a);
1191    avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_NO_VERIFY|AVAHI_PUBLISH_USE_LLMNR, "localhost", &a);
1192
1193    avahi_address_parse("::1", AVAHI_PROTO_INET6, &a);
1194    avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_NO_PROBE|AVAHI_PUBLISH_NO_ANNOUNCE|AVAHI_PUBLISH_USE_MULTICAST, "ip6-localhost", &a);
1195    avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_NO_VERIFY|AVAHI_PUBLISH_USE_LLMNR, "ip6-localhost", &a);
1196}
1197
1198static void register_browse_domain(AvahiServer *s) {
1199    assert(s);
1200
1201    if (!s->config.publish_domain)
1202        return;
1203
1204    if (avahi_domain_equal(s->domain_name, "local"))
1205        return;
1206
1207    if (s->mdns.browse_domain_entry_group)
1208        assert(avahi_s_entry_group_is_empty(s->mdns.browse_domain_entry_group));
1209    else
1210        s->mdns.browse_domain_entry_group = avahi_s_entry_group_new(s, NULL, NULL);
1211
1212    if (!s->mdns.browse_domain_entry_group) {
1213        avahi_log_warn("Failed to create browse domain entry group: %s", avahi_strerror(s->error));
1214        return;
1215    }
1216
1217    if (avahi_server_add_ptr(s, s->mdns.browse_domain_entry_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, AVAHI_DEFAULT_TTL, "b._dns-sd._udp.local", s->domain_name) < 0) {
1218        avahi_log_warn("Failed to add browse domain RR: %s", avahi_strerror(s->error));
1219        return;
1220    }
1221
1222    if (avahi_s_entry_group_commit(s->mdns.browse_domain_entry_group) < 0)
1223        avahi_log_warn("Failed to commit browse domain entry group: %s", avahi_strerror(s->error));
1224}
1225
1226static void register_stuff(AvahiServer *s) {
1227    assert(s);
1228
1229    server_set_state(s, AVAHI_SERVER_REGISTERING);
1230    s->n_host_rr_pending ++; /** Make sure that the state isn't changed tp AVAHI_SERVER_RUNNING too early */
1231
1232    register_hinfo(s);
1233    register_browse_domain(s);
1234    avahi_interface_monitor_update_rrs(s->monitor, 0);
1235
1236    assert(s->n_host_rr_pending > 0);
1237    s->n_host_rr_pending --;
1238
1239    if (s->n_host_rr_pending == 0)
1240        server_set_state(s, AVAHI_SERVER_RUNNING);
1241}
1242
1243static void update_fqdn(AvahiServer *s) {
1244    char *n;
1245
1246    assert(s);
1247    assert(s->host_name);
1248    assert(s->domain_name);
1249
1250    if (!(n = avahi_strdup_printf("%s.%s", s->host_name, s->domain_name)))
1251        return; /* OOM */
1252
1253    avahi_free(s->host_name_fqdn);
1254    s->host_name_fqdn = n;
1255}
1256
1257int avahi_server_set_host_name(AvahiServer *s, const char *host_name) {
1258    char *hn = NULL;
1259    assert(s);
1260
1261    AVAHI_CHECK_VALIDITY(s, !host_name || avahi_is_valid_host_name(host_name), AVAHI_ERR_INVALID_HOST_NAME);
1262
1263    if (!host_name)
1264        hn = avahi_get_host_name_strdup();
1265    else
1266        hn = avahi_normalize_name_strdup(host_name);
1267
1268    hn[strcspn(hn, ".")] = 0;
1269
1270    if (avahi_domain_equal(s->host_name, hn) && s->state != AVAHI_SERVER_COLLISION) {
1271        avahi_free(hn);
1272        return avahi_server_set_errno(s, AVAHI_ERR_NO_CHANGE);
1273    }
1274
1275    withdraw_host_rrs(s);
1276
1277    avahi_free(s->host_name);
1278    s->host_name = hn;
1279
1280    update_fqdn(s);
1281
1282    register_stuff(s);
1283    return AVAHI_OK;
1284}
1285
1286int avahi_server_set_domain_name(AvahiServer *s, const char *domain_name) {
1287    char *dn = NULL;
1288    assert(s);
1289
1290    AVAHI_CHECK_VALIDITY(s, !domain_name || avahi_is_valid_domain_name(domain_name), AVAHI_ERR_INVALID_DOMAIN_NAME);
1291
1292    if (!domain_name)
1293        dn = avahi_strdup("local");
1294    else
1295        dn = avahi_normalize_name_strdup(domain_name);
1296
1297    if (avahi_domain_equal(s->domain_name, domain_name)) {
1298        avahi_free(dn);
1299        return avahi_server_set_errno(s, AVAHI_ERR_NO_CHANGE);
1300    }
1301
1302    withdraw_host_rrs(s);
1303
1304    avahi_free(s->domain_name);
1305    s->domain_name = dn;
1306    update_fqdn(s);
1307
1308    register_stuff(s);
1309
1310    avahi_free(dn);
1311    return AVAHI_OK;
1312}
1313
1314static int valid_server_config(const AvahiServerConfig *sc) {
1315    AvahiStringList *l;
1316
1317    assert(sc);
1318
1319    if (sc->n_wide_area_servers > AVAHI_WIDE_AREA_SERVERS_MAX)
1320        return AVAHI_ERR_INVALID_CONFIG;
1321
1322    if (sc->host_name && !avahi_is_valid_host_name(sc->host_name))
1323        return AVAHI_ERR_INVALID_HOST_NAME;
1324
1325    if (sc->domain_name && !avahi_is_valid_domain_name(sc->domain_name))
1326        return AVAHI_ERR_INVALID_DOMAIN_NAME;
1327
1328    for (l = sc->browse_domains; l; l = l->next)
1329        if (!avahi_is_valid_domain_name((char*) l->text))
1330            return AVAHI_ERR_INVALID_DOMAIN_NAME;
1331
1332    return AVAHI_OK;
1333}
1334
1335static int setup_sockets(AvahiServer *s) {
1336    assert(s);
1337
1338    s->mdns.fd_ipv4 = s->config.use_ipv4 ? avahi_open_socket_ipv4(s->config.disallow_other_stacks, AVAHI_MDNS) : -1;
1339    s->mdns.fd_ipv6 = s->config.use_ipv6 ? avahi_open_socket_ipv6(s->config.disallow_other_stacks, AVAHI_MDNS) : -1;
1340
1341    if (s->mdns.fd_ipv6 < 0 && s->mdns.fd_ipv4 < 0)
1342        return AVAHI_ERR_NO_NETWORK;
1343
1344    if (s->mdns.fd_ipv4 < 0 && s->config.use_ipv4)
1345        avahi_log_notice("Failed to create IPv4 mDNS socket, proceeding in IPv6 only mode");
1346    else if (s->mdns.fd_ipv6 < 0 && s->config.use_ipv6)
1347        avahi_log_notice("Failed to create IPv6 mDNS socket, proceeding in IPv4 only mode");
1348
1349    s->mdns.fd_legacy_unicast_ipv4 = s->mdns.fd_ipv4 >= 0 && s->config.enable_reflector ? avahi_open_unicast_socket_ipv4() : -1;
1350    s->mdns.fd_legacy_unicast_ipv6 = s->mdns.fd_ipv6 >= 0 && s->config.enable_reflector ? avahi_open_unicast_socket_ipv6() : -1;
1351
1352    s->mdns.watch_ipv4 =
1353    s->mdns.watch_ipv6 =
1354    s->mdns.watch_legacy_unicast_ipv4 =
1355    s->mdns.watch_legacy_unicast_ipv6 = NULL;
1356
1357    if (s->mdns.fd_ipv4 >= 0)
1358        s->mdns.watch_ipv4 = s->poll_api->watch_new(s->poll_api, s->mdns.fd_ipv4, AVAHI_WATCH_IN, mcast_socket_event, s);
1359    if (s->mdns.fd_ipv6 >= 0)
1360        s->mdns.watch_ipv6 = s->poll_api->watch_new(s->poll_api, s->mdns.fd_ipv6, AVAHI_WATCH_IN, mcast_socket_event, s);
1361
1362    if (s->mdns.fd_legacy_unicast_ipv4 >= 0)
1363        s->mdns.watch_legacy_unicast_ipv4 = s->poll_api->watch_new(s->poll_api, s->mdns.fd_legacy_unicast_ipv4, AVAHI_WATCH_IN, legacy_unicast_socket_event, s);
1364    if (s->mdns.fd_legacy_unicast_ipv6 >= 0)
1365        s->mdns.watch_legacy_unicast_ipv6 = s->poll_api->watch_new(s->poll_api, s->mdns.fd_legacy_unicast_ipv6, AVAHI_WATCH_IN, legacy_unicast_socket_event, s);
1366
1367    return 0;
1368}
1369
1370AvahiServer *avahi_server_new(const AvahiPoll *poll_api, const AvahiServerConfig *sc, AvahiServerCallback callback, void* userdata, int *error) {
1371    AvahiServer *s;
1372    int e;
1373
1374    if (sc && (e = valid_server_config(sc)) < 0) {
1375        if (error)
1376            *error = e;
1377        return NULL;
1378    }
1379
1380    if (!(s = avahi_new(AvahiServer, 1))) {
1381        if (error)
1382            *error = AVAHI_ERR_NO_MEMORY;
1383
1384        return NULL;
1385    }
1386
1387    s->poll_api = poll_api;
1388
1389    if (sc)
1390        avahi_server_config_copy(&s->config, sc);
1391    else
1392        avahi_server_config_init(&s->config);
1393
1394    if ((e = setup_sockets(s)) < 0) {
1395        if (error)
1396            *error = e;
1397
1398        avahi_server_config_free(&s->config);
1399        avahi_free(s);
1400
1401        return NULL;
1402    }
1403
1404    if ((e = setup_llmnr_sockets(s)) < 0) {
1405        if (error)
1406            *error = e;
1407
1408        avahi_server_config_free(&s->config);
1409        avahi_free(s);
1410
1411        return NULL;
1412    }
1413
1414    s->n_host_rr_pending = 0;
1415    s->mdns.need_entry_cleanup = 0;
1416    s->mdns.need_group_cleanup = 0;
1417    s->llmnr.need_entry_cleanup = 0;
1418    s->llmnr.need_group_cleanup = 0;
1419    s->need_browser_cleanup = 0;
1420    s->cleanup_time_event = NULL;
1421    s->mdns.hinfo_entry_group = NULL;
1422    s->mdns.browse_domain_entry_group = NULL;
1423    s->error = AVAHI_OK;
1424    s->state = AVAHI_SERVER_INVALID;
1425
1426    s->callback = callback;
1427    s->userdata = userdata;
1428
1429    s->time_event_queue = avahi_time_event_queue_new(poll_api);
1430
1431    s->mdns.entries_by_key = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL);
1432    s->llmnr.entries_by_key = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL);
1433
1434    AVAHI_LLIST_HEAD_INIT(AvahiEntry, s->mdns.entries);
1435    AVAHI_LLIST_HEAD_INIT(AvahiEntry, s->llmnr.entries);
1436
1437    AVAHI_LLIST_HEAD_INIT(AvahiGroup, s->mdns.groups);
1438    AVAHI_LLIST_HEAD_INIT(AvahiGroup, s->llmnr.groups);
1439
1440    s->record_browser_hashmap = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL);
1441    AVAHI_LLIST_HEAD_INIT(AvahiSRecordBrowser, s->record_browsers);
1442    AVAHI_LLIST_HEAD_INIT(AvahiSHostNameResolver, s->host_name_resolvers);
1443    AVAHI_LLIST_HEAD_INIT(AvahiSAddressResolver, s->address_resolvers);
1444    AVAHI_LLIST_HEAD_INIT(AvahiSDomainBrowser, s->domain_browsers);
1445    AVAHI_LLIST_HEAD_INIT(AvahiSServiceTypeBrowser, s->service_type_browsers);
1446    AVAHI_LLIST_HEAD_INIT(AvahiSServiceBrowser, s->service_browsers);
1447    AVAHI_LLIST_HEAD_INIT(AvahiSServiceResolver, s->service_resolvers);
1448    AVAHI_LLIST_HEAD_INIT(AvahiSDNSServerBrowser, s->dns_server_browsers);
1449
1450    s->legacy_unicast_reflect_slots = NULL;
1451    s->legacy_unicast_reflect_id = 0;
1452
1453    s->mdns.record_list = avahi_record_list_new();
1454    s->llmnr.record_list = avahi_record_list_new();
1455
1456    /* Get host name */
1457    s->host_name = s->config.host_name ? avahi_normalize_name_strdup(s->config.host_name) : avahi_get_host_name_strdup();
1458    s->host_name[strcspn(s->host_name, ".")] = 0;
1459    s->domain_name = s->config.domain_name ? avahi_normalize_name_strdup(s->config.domain_name) : avahi_strdup("local");
1460    s->host_name_fqdn = NULL;
1461    update_fqdn(s);
1462
1463    do {
1464        s->local_service_cookie = (uint32_t) rand() * (uint32_t) rand();
1465    } while (s->local_service_cookie == AVAHI_SERVICE_COOKIE_INVALID);
1466
1467    if (s->config.enable_wide_area) {
1468        s->wide_area.wide_area_lookup_engine = avahi_wide_area_engine_new(s);
1469        avahi_wide_area_set_servers(s->wide_area.wide_area_lookup_engine, s->config.wide_area_servers, s->config.n_wide_area_servers);
1470    } else
1471        s->wide_area.wide_area_lookup_engine = NULL;
1472
1473    s->mdns.multicast_lookup_engine = avahi_multicast_lookup_engine_new(s);
1474    s->llmnr.llmnr_lookup_engine = avahi_llmnr_lookup_engine_new(s);
1475
1476    s->monitor = avahi_interface_monitor_new(s);
1477    avahi_interface_monitor_sync(s->monitor);
1478
1479    register_localhost(s);
1480    register_stuff(s);
1481
1482    return s;
1483}
1484
1485void avahi_server_free(AvahiServer* s) {
1486    assert(s);
1487
1488    /* Remove all browsers */
1489
1490    while (s->dns_server_browsers)
1491        avahi_s_dns_server_browser_free(s->dns_server_browsers);
1492    while (s->host_name_resolvers)
1493        avahi_s_host_name_resolver_free(s->host_name_resolvers);
1494    while (s->address_resolvers)
1495        avahi_s_address_resolver_free(s->address_resolvers);
1496    while (s->domain_browsers)
1497        avahi_s_domain_browser_free(s->domain_browsers);
1498    while (s->service_type_browsers)
1499        avahi_s_service_type_browser_free(s->service_type_browsers);
1500    while (s->service_browsers)
1501        avahi_s_service_browser_free(s->service_browsers);
1502    while (s->service_resolvers)
1503        avahi_s_service_resolver_free(s->service_resolvers);
1504    while (s->record_browsers)
1505        avahi_s_record_browser_destroy(s->record_browsers);
1506
1507    /* Remove all locally rgeistered stuff */
1508
1509    while (s->mdns.entries)
1510        avahi_entry_free(s, s->mdns.entries);
1511
1512    while (s->llmnr.entries)
1513        avahi_entry_free(s, s->llmnr.entries);
1514
1515    avahi_interface_monitor_free(s->monitor);
1516    free_slots(s);
1517
1518    avahi_hashmap_free(s->mdns.entries_by_key);
1519    avahi_hashmap_free(s->llmnr.entries_by_key);
1520
1521    avahi_record_list_free(s->mdns.record_list);
1522    avahi_record_list_free(s->llmnr.record_list);
1523
1524    avahi_hashmap_free(s->record_browser_hashmap);
1525
1526    if (s->wide_area.wide_area_lookup_engine)
1527        avahi_wide_area_engine_free(s->wide_area.wide_area_lookup_engine);
1528
1529    avahi_multicast_lookup_engine_free(s->mdns.multicast_lookup_engine);
1530    avahi_llmnr_lookup_engine_free(s->llmnr.llmnr_lookup_engine);
1531
1532    if (s->cleanup_time_event)
1533        avahi_time_event_free(s->cleanup_time_event);
1534
1535    avahi_time_event_queue_free(s->time_event_queue);
1536
1537    /* Free watches */
1538
1539    if (s->mdns.watch_ipv4)
1540        s->poll_api->watch_free(s->mdns.watch_ipv4);
1541    if (s->mdns.watch_ipv6)
1542        s->poll_api->watch_free(s->mdns.watch_ipv6);
1543
1544    if (s->mdns.watch_legacy_unicast_ipv4)
1545        s->poll_api->watch_free(s->mdns.watch_legacy_unicast_ipv4);
1546    if (s->mdns.watch_legacy_unicast_ipv6)
1547        s->poll_api->watch_free(s->mdns.watch_legacy_unicast_ipv6);
1548
1549    if (s->llmnr.watch_ipv4)
1550        s->poll_api->watch_free(s->llmnr.watch_ipv4);
1551    if (s->llmnr.watch_ipv6)
1552        s->poll_api->watch_free(s->llmnr.watch_ipv6);
1553
1554    /* Free sockets */
1555
1556    if (s->mdns.fd_ipv4 >= 0)
1557        close(s->mdns.fd_ipv4);
1558    if (s->mdns.fd_ipv6 >= 0)
1559        close(s->mdns.fd_ipv6);
1560
1561    if (s->mdns.fd_legacy_unicast_ipv4 >= 0)
1562        close(s->mdns.fd_legacy_unicast_ipv4);
1563    if (s->mdns.fd_legacy_unicast_ipv6 >= 0)
1564        close(s->mdns.fd_legacy_unicast_ipv6);
1565
1566    if (s->llmnr.fd_ipv4 >= 0)
1567        close(s->llmnr.fd_ipv4);
1568    if (s->llmnr.fd_ipv6 >= 0)
1569        close(s->mdns.fd_ipv6);
1570
1571    /* Free other stuff */
1572
1573    avahi_free(s->host_name);
1574    avahi_free(s->domain_name);
1575    avahi_free(s->host_name_fqdn);
1576
1577    avahi_server_config_free(&s->config);
1578
1579    avahi_free(s);
1580}
1581
1582const char* avahi_server_get_domain_name(AvahiServer *s) {
1583    assert(s);
1584
1585    return s->domain_name;
1586}
1587
1588const char* avahi_server_get_host_name(AvahiServer *s) {
1589    assert(s);
1590
1591    return s->host_name;
1592}
1593
1594const char* avahi_server_get_host_name_fqdn(AvahiServer *s) {
1595    assert(s);
1596
1597    return s->host_name_fqdn;
1598}
1599
1600void* avahi_server_get_data(AvahiServer *s) {
1601    assert(s);
1602
1603    return s->userdata;
1604}
1605
1606void avahi_server_set_data(AvahiServer *s, void* userdata) {
1607    assert(s);
1608
1609    s->userdata = userdata;
1610}
1611
1612AvahiServerState avahi_server_get_state(AvahiServer *s) {
1613    assert(s);
1614
1615    return s->state;
1616}
1617
1618AvahiServerConfig* avahi_server_config_init(AvahiServerConfig *c) {
1619    assert(c);
1620
1621    memset(c, 0, sizeof(AvahiServerConfig));
1622    c->use_ipv6 = 1;
1623    c->use_ipv4 = 1;
1624    c->allow_interfaces = NULL;
1625    c->deny_interfaces = NULL;
1626    c->host_name = NULL;
1627    c->domain_name = NULL;
1628    c->check_response_ttl = 0;
1629    c->publish_hinfo = 1;
1630    c->publish_addresses = 1;
1631    c->publish_workstation = 1;
1632    c->publish_domain = 1;
1633    c->use_iff_running = 0;
1634    c->enable_reflector = 0;
1635    c->reflect_ipv = 0;
1636    c->add_service_cookie = 0;
1637    c->enable_wide_area = 0;
1638    c->n_wide_area_servers = 0;
1639    c->disallow_other_stacks = 0;
1640    c->browse_domains = NULL;
1641    c->disable_publishing = 0;
1642    c->allow_point_to_point = 0;
1643    c->publish_aaaa_on_ipv4 = 1;
1644    c->publish_a_on_ipv6 = 0;
1645    c->n_cache_entries_max = AVAHI_DEFAULT_CACHE_ENTRIES_MAX;
1646    c->ratelimit_interval = 0;
1647    c->ratelimit_burst = 0;
1648
1649    return c;
1650}
1651
1652void avahi_server_config_free(AvahiServerConfig *c) {
1653    assert(c);
1654
1655    avahi_free(c->host_name);
1656    avahi_free(c->domain_name);
1657    avahi_string_list_free(c->browse_domains);
1658    avahi_string_list_free(c->allow_interfaces);
1659    avahi_string_list_free(c->deny_interfaces);
1660}
1661
1662AvahiServerConfig* avahi_server_config_copy(AvahiServerConfig *ret, const AvahiServerConfig *c) {
1663    char *d = NULL, *h = NULL;
1664    AvahiStringList *browse = NULL, *allow = NULL, *deny = NULL;
1665    assert(ret);
1666    assert(c);
1667
1668    if (c->host_name)
1669        if (!(h = avahi_strdup(c->host_name)))
1670            return NULL;
1671
1672    if (c->domain_name)
1673        if (!(d = avahi_strdup(c->domain_name))) {
1674            avahi_free(h);
1675            return NULL;
1676        }
1677
1678    if (!(browse = avahi_string_list_copy(c->browse_domains)) && c->browse_domains) {
1679        avahi_free(h);
1680        avahi_free(d);
1681        return NULL;
1682    }
1683
1684    if (!(allow = avahi_string_list_copy(c->allow_interfaces)) && c->allow_interfaces) {
1685        avahi_string_list_free(browse);
1686        avahi_free(h);
1687        avahi_free(d);
1688        return NULL;
1689    }
1690
1691    if (!(deny = avahi_string_list_copy(c->deny_interfaces)) && c->deny_interfaces) {
1692        avahi_string_list_free(allow);
1693        avahi_string_list_free(browse);
1694        avahi_free(h);
1695        avahi_free(d);
1696        return NULL;
1697    }
1698
1699    *ret = *c;
1700    ret->host_name = h;
1701    ret->domain_name = d;
1702    ret->browse_domains = browse;
1703    ret->allow_interfaces = allow;
1704    ret->deny_interfaces = deny;
1705
1706    return ret;
1707}
1708
1709int avahi_server_errno(AvahiServer *s) {
1710    assert(s);
1711
1712    return s->error;
1713}
1714
1715/* Just for internal use */
1716int avahi_server_set_errno(AvahiServer *s, int error) {
1717    assert(s);
1718
1719    return s->error = error;
1720}
1721
1722uint32_t avahi_server_get_local_service_cookie(AvahiServer *s) {
1723    assert(s);
1724
1725    return s->local_service_cookie;
1726}
1727
1728static AvahiEntry *find_entry(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiKey *key) {
1729    AvahiEntry *e;
1730
1731    assert(s);
1732    assert(key);
1733
1734    for (e = avahi_hashmap_lookup(s->mdns.entries_by_key, key); e; e = e->by_key_next) {
1735
1736        assert(e->type == AVAHI_ENTRY_MDNS);
1737        if ((e->interface == interface || e->interface <= 0 || interface <= 0) &&
1738            (e->protocol == protocol || e->protocol == AVAHI_PROTO_UNSPEC || protocol == AVAHI_PROTO_UNSPEC) &&
1739            (!e->group || e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED || e->group->state == AVAHI_ENTRY_GROUP_REGISTERING))
1740
1741            return e;
1742    }
1743
1744    return NULL;
1745}
1746
1747int avahi_server_get_group_of_service(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, const char *name, const char *type, const char *domain, AvahiSEntryGroup** ret_group) {
1748    AvahiKey *key = NULL;
1749    AvahiEntry *e;
1750    int ret;
1751    char n[AVAHI_DOMAIN_NAME_MAX];
1752
1753    assert(s);
1754    assert(name);
1755    assert(type);
1756    assert(ret_group);
1757
1758    AVAHI_CHECK_VALIDITY(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE);
1759    AVAHI_CHECK_VALIDITY(s, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL);
1760    AVAHI_CHECK_VALIDITY(s, avahi_is_valid_service_name(name), AVAHI_ERR_INVALID_SERVICE_NAME);
1761    AVAHI_CHECK_VALIDITY(s, avahi_is_valid_service_type_strict(type), AVAHI_ERR_INVALID_SERVICE_TYPE);
1762    AVAHI_CHECK_VALIDITY(s, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME);
1763
1764    if ((ret = avahi_service_name_join(n, sizeof(n), name, type, domain) < 0))
1765        return avahi_server_set_errno(s, ret);
1766
1767    if (!(key = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV)))
1768        return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1769
1770    e = find_entry(s, interface, protocol, key);
1771    avahi_key_unref(key);
1772
1773    if (e) {
1774        *ret_group = e->group;
1775        return AVAHI_OK;
1776    }
1777
1778    return avahi_server_set_errno(s, AVAHI_ERR_NOT_FOUND);
1779}
1780
1781int avahi_server_is_service_local(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, const char *name) {
1782    AvahiKey *key = NULL;
1783    AvahiEntry *e;
1784
1785    assert(s);
1786    assert(name);
1787
1788    if (!s->host_name_fqdn)
1789        return 0;
1790
1791    if (!(key = avahi_key_new(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV)))
1792        return 0;
1793
1794    e = find_entry(s, interface, protocol, key);
1795    avahi_key_unref(key);
1796
1797    if (!e)
1798        return 0;
1799
1800    return avahi_domain_equal(s->host_name_fqdn, e->record->data.srv.name);
1801}
1802
1803int avahi_server_is_record_local(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiRecord *record) {
1804    AvahiEntry *e;
1805
1806    assert(s);
1807    assert(record);
1808
1809    for (e = avahi_hashmap_lookup(s->mdns.entries_by_key, record->key); e; e = e->by_key_next) {
1810
1811        assert(e->type == AVAHI_ENTRY_MDNS);
1812        if ((e->interface == interface || e->interface <= 0 || interface <= 0) &&
1813            (e->protocol == protocol || e->protocol == AVAHI_PROTO_UNSPEC || protocol == AVAHI_PROTO_UNSPEC) &&
1814            (!e->group || e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED || e->group->state == AVAHI_ENTRY_GROUP_REGISTERING) &&
1815            avahi_record_equal_no_ttl(record, e->record))
1816            return 1;
1817    }
1818
1819    return 0;
1820}
1821
1822/** Set the wide area DNS servers */
1823int avahi_server_set_wide_area_servers(AvahiServer *s, const AvahiAddress *a, unsigned n) {
1824    assert(s);
1825
1826    if (!s->wide_area.wide_area_lookup_engine)
1827        return avahi_server_set_errno(s, AVAHI_ERR_INVALID_CONFIG);
1828
1829    avahi_wide_area_set_servers(s->wide_area.wide_area_lookup_engine, a, n);
1830    return AVAHI_OK;
1831}
1832
1833const AvahiServerConfig* avahi_server_get_config(AvahiServer *s) {
1834    assert(s);
1835
1836    return &s->config;
1837}
1838
1839/** Set the browsing domains */
1840int avahi_server_set_browse_domains(AvahiServer *s, AvahiStringList *domains) {
1841    AvahiStringList *l;
1842
1843    assert(s);
1844
1845    for (l = s->config.browse_domains; l; l = l->next)
1846        if (!avahi_is_valid_domain_name((char*) l->text))
1847            return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
1848
1849    avahi_string_list_free(s->config.browse_domains);
1850    s->config.browse_domains = avahi_string_list_copy(domains);
1851
1852    return AVAHI_OK;
1853}
1854