]> git.meshlink.io Git - catta/blob - avahi-core/server.c
CVE-2009-0758: Reflector creates packet storm on legacy unicast traffic
[catta] / avahi-core / server.c
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
54 static 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
85 void 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
105 void 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
113 void 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
154 static 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
184 static 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
196 static 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
241 static 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
346 static 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
356 static 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
363 void 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
499 static 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
514 static 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
526 static 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
548 static 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
563 static 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
646 fail:
647     avahi_record_list_flush(s->record_list);
648 }
649
650 static 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
689 static 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
718 static 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
734 static 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
749 static 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
765 static 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
775 static 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
829 static 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
864 static 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
872 static 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
884 static 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
981 static 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
1012 static void cleanup_dead(AvahiServer *s) {
1013     assert(s);
1014
1015     avahi_cleanup_dead_entries(s);
1016     avahi_browser_cleanup(s);
1017 }
1018
1019 static 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
1055 static 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
1078 static 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
1092 static 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
1105 void 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
1114 void 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
1132 static 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
1177 static 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
1189 static 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
1217 static void register_stuff(AvahiServer *s) {
1218     assert(s);
1219
1220     server_set_state(s, AVAHI_SERVER_REGISTERING);
1221     s->n_host_rr_pending ++; /** Make sure that the state isn't changed tp AVAHI_SERVER_RUNNING too early */
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
1233 static 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
1247 int 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
1275 int 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
1303 static 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
1324 static 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
1359 AvahiServer *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
1454 void 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
1533 const char* avahi_server_get_domain_name(AvahiServer *s) {
1534     assert(s);
1535
1536     return s->domain_name;
1537 }
1538
1539 const char* avahi_server_get_host_name(AvahiServer *s) {
1540     assert(s);
1541
1542     return s->host_name;
1543 }
1544
1545 const char* avahi_server_get_host_name_fqdn(AvahiServer *s) {
1546     assert(s);
1547
1548     return s->host_name_fqdn;
1549 }
1550
1551 void* avahi_server_get_data(AvahiServer *s) {
1552     assert(s);
1553
1554     return s->userdata;
1555 }
1556
1557 void avahi_server_set_data(AvahiServer *s, void* userdata) {
1558     assert(s);
1559
1560     s->userdata = userdata;
1561 }
1562
1563 AvahiServerState avahi_server_get_state(AvahiServer *s) {
1564     assert(s);
1565
1566     return s->state;
1567 }
1568
1569 AvahiServerConfig* 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
1600 void 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
1610 AvahiServerConfig* 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
1657 int avahi_server_errno(AvahiServer *s) {
1658     assert(s);
1659
1660     return s->error;
1661 }
1662
1663 /* Just for internal use */
1664 int avahi_server_set_errno(AvahiServer *s, int error) {
1665     assert(s);
1666
1667     return s->error = error;
1668 }
1669
1670 uint32_t avahi_server_get_local_service_cookie(AvahiServer *s) {
1671     assert(s);
1672
1673     return s->local_service_cookie;
1674 }
1675
1676 static 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
1693 int 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
1727 int 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
1749 int 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 */
1767 int 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
1777 const AvahiServerConfig* avahi_server_get_config(AvahiServer *s) {
1778     assert(s);
1779
1780     return &s->config;
1781 }
1782
1783 /** Set the browsing domains */
1784 int 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 }