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