]> git.meshlink.io Git - catta/blob - avahi-core/server.c
Don't enable PIC code for all compilation.
[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 lsa.sin_port == 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 lsa.sin6_port == 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 (avahi_address_is_ipv4_in_ipv6(src_address))
902         /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
903         return;
904
905     if (originates_from_local_legacy_unicast_socket(s, src_address, port))
906         /* This originates from our local reflector, so let's ignore it */
907         return;
908
909     /* We don't want to reflect local traffic, so we check if this packet is generated locally. */
910     if (s->config.enable_reflector)
911         from_local_iface = originates_from_local_iface(s, iface, src_address, port);
912
913     if (avahi_dns_packet_check_valid_multicast(p) < 0) {
914         avahi_log_warn("Received invalid packet.");
915         return;
916     }
917
918     if (avahi_dns_packet_is_query(p)) {
919         int legacy_unicast = 0;
920
921         if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT) != 0) {
922             avahi_log_warn("Invalid query packet.");
923             return;
924         }
925
926         if (port != AVAHI_MDNS_PORT) {
927             /* Legacy Unicast */
928
929             if ((avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) != 0 ||
930                  avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0)) {
931                 avahi_log_warn("Invalid legacy unicast query packet.");
932                 return;
933             }
934         
935             legacy_unicast = 1;
936         }
937
938         if (legacy_unicast)
939             reflect_legacy_unicast_query_packet(s, p, i, src_address, port);
940         
941         handle_query_packet(s, p, i, src_address, port, legacy_unicast, from_local_iface);
942         
943     } else {
944         if (port != AVAHI_MDNS_PORT) {
945             avahi_log_warn("Received response with invalid source port %u on interface '%s.%i'", port, i->hardware->name, i->protocol);
946             return;
947         }
948
949         if (ttl != 255 && s->config.check_response_ttl) {
950             avahi_log_warn("Received response with invalid TTL %u on interface '%s.%i'.", ttl, i->hardware->name, i->protocol);
951             return;
952         }
953
954         if (!is_mdns_mcast_address(dst_address) &&
955             !avahi_interface_address_on_link(i, src_address)) {
956             avahi_log_warn("Received non-local response on interface '%s.%i'.", i->hardware->name, i->protocol);
957             return;
958         }
959         
960         if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT) != 0 ||
961             avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 ||
962             avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0) {
963             avahi_log_warn("Invalid response packet.");
964             return;
965         }
966
967         handle_response_packet(s, p, i, src_address, from_local_iface);
968     }
969 }
970
971 static void dispatch_legacy_unicast_packet(AvahiServer *s, AvahiDnsPacket *p) {
972     AvahiInterface *j;
973     AvahiLegacyUnicastReflectSlot *slot;
974     
975     assert(s);
976     assert(p);
977
978     if (avahi_dns_packet_check_valid(p) < 0 || avahi_dns_packet_is_query(p)) {
979         avahi_log_warn("Received invalid packet.");
980         return;
981     }
982
983     if (!(slot = find_slot(s, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID)))) {
984         avahi_log_warn("Received legacy unicast response with unknown id");
985         return;
986     }
987
988     if (!(j = avahi_interface_monitor_get_interface(s->monitor, slot->interface, slot->address.proto)) ||
989         !j->announcing)
990         return;
991
992     /* Patch the original ID into this response */
993     avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
994
995     /* Forward the response to the correct client */
996     avahi_interface_send_packet_unicast(j, p, &slot->address, slot->port);
997
998     /* Undo changes to packet */
999     avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
1000 }
1001
1002 static void cleanup_dead(AvahiServer *s) {
1003     assert(s);
1004     
1005     avahi_cleanup_dead_entries(s);
1006     avahi_browser_cleanup(s);
1007 }
1008
1009 static void mcast_socket_event(AvahiWatch *w, int fd, AvahiWatchEvent events, void *userdata) {
1010     AvahiServer *s = userdata;
1011     AvahiAddress dest, src;
1012     AvahiDnsPacket *p = NULL;
1013     AvahiIfIndex iface;
1014     uint16_t port;
1015     uint8_t ttl;
1016
1017     assert(w);
1018     assert(fd >= 0);
1019     assert(events & AVAHI_WATCH_IN);
1020     
1021     if (fd == s->fd_ipv4) {
1022         dest.proto = src.proto = AVAHI_PROTO_INET;
1023         p = avahi_recv_dns_packet_ipv4(s->fd_ipv4, &src.data.ipv4, &port, &dest.data.ipv4, &iface, &ttl);
1024     } else {
1025         assert(fd == s->fd_ipv6);
1026         dest.proto = src.proto = AVAHI_PROTO_INET6;
1027         p = avahi_recv_dns_packet_ipv6(s->fd_ipv6, &src.data.ipv6, &port, &dest.data.ipv6, &iface, &ttl);
1028     }
1029         
1030     if (p) {
1031         if (iface == AVAHI_IF_UNSPEC) 
1032             iface = avahi_find_interface_for_address(s->monitor, &dest);
1033         
1034         if (iface != AVAHI_IF_UNSPEC)
1035             dispatch_packet(s, p, &src, port, &dest, iface, ttl);
1036         else
1037             avahi_log_error("Incoming packet recieved on address that isn't local.");
1038
1039         avahi_dns_packet_free(p);
1040
1041         cleanup_dead(s);
1042     }
1043 }
1044
1045 static void legacy_unicast_socket_event(AvahiWatch *w, int fd, AvahiWatchEvent events, void *userdata) {
1046     AvahiServer *s = userdata;
1047     AvahiDnsPacket *p = NULL;
1048
1049     assert(w);
1050     assert(fd >= 0);
1051     assert(events & AVAHI_WATCH_IN);
1052     
1053     if (fd == s->fd_legacy_unicast_ipv4)
1054         p = avahi_recv_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, NULL, NULL, NULL, NULL, NULL);
1055     else {
1056         assert(fd == s->fd_legacy_unicast_ipv6);
1057         p = avahi_recv_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, NULL, NULL, NULL, NULL, NULL);
1058     }
1059
1060     if (p) {
1061         dispatch_legacy_unicast_packet(s, p);
1062         avahi_dns_packet_free(p);
1063
1064         cleanup_dead(s);
1065     }
1066 }
1067
1068 static void server_set_state(AvahiServer *s, AvahiServerState state) {
1069     assert(s);
1070
1071     if (s->state == state)
1072         return;
1073     
1074     s->state = state;
1075
1076     avahi_interface_monitor_update_rrs(s->monitor, 0);
1077     
1078     if (s->callback)
1079         s->callback(s, state, s->userdata);
1080 }
1081
1082 static void withdraw_host_rrs(AvahiServer *s) {
1083     assert(s);
1084
1085     if (s->hinfo_entry_group)
1086         avahi_s_entry_group_reset(s->hinfo_entry_group);
1087
1088     if (s->browse_domain_entry_group)
1089         avahi_s_entry_group_reset(s->browse_domain_entry_group);
1090
1091     avahi_interface_monitor_update_rrs(s->monitor, 1);
1092     s->n_host_rr_pending = 0;
1093 }
1094
1095 void avahi_server_decrease_host_rr_pending(AvahiServer *s) {
1096     assert(s);
1097     
1098     assert(s->n_host_rr_pending > 0);
1099
1100     if (--s->n_host_rr_pending == 0)
1101         server_set_state(s, AVAHI_SERVER_RUNNING);
1102 }
1103
1104 void avahi_host_rr_entry_group_callback(AvahiServer *s, AvahiSEntryGroup *g, AvahiEntryGroupState state, AVAHI_GCC_UNUSED void *userdata) {
1105     assert(s);
1106     assert(g);
1107
1108     if (state == AVAHI_ENTRY_GROUP_REGISTERING &&
1109         s->state == AVAHI_SERVER_REGISTERING)
1110         s->n_host_rr_pending ++;
1111     
1112     else if (state == AVAHI_ENTRY_GROUP_COLLISION &&
1113         (s->state == AVAHI_SERVER_REGISTERING || s->state == AVAHI_SERVER_RUNNING)) {
1114         withdraw_host_rrs(s);
1115         server_set_state(s, AVAHI_SERVER_COLLISION);
1116         
1117     } else if (state == AVAHI_ENTRY_GROUP_ESTABLISHED &&
1118                s->state == AVAHI_SERVER_REGISTERING)
1119         avahi_server_decrease_host_rr_pending(s);
1120 }
1121
1122 static void register_hinfo(AvahiServer *s) {
1123     struct utsname utsname;
1124     AvahiRecord *r;
1125     
1126     assert(s);
1127     
1128     if (!s->config.publish_hinfo)
1129         return;
1130
1131     if (s->hinfo_entry_group)
1132         assert(avahi_s_entry_group_is_empty(s->hinfo_entry_group));
1133     else
1134         s->hinfo_entry_group = avahi_s_entry_group_new(s, avahi_host_rr_entry_group_callback, NULL);
1135
1136     if (!s->hinfo_entry_group) {
1137         avahi_log_warn("Failed to create HINFO entry group: %s", avahi_strerror(s->error));
1138         return;
1139     }
1140     
1141     /* Fill in HINFO rr */
1142     if ((r = avahi_record_new_full(s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_HINFO, AVAHI_DEFAULT_TTL_HOST_NAME))) {
1143
1144         if (uname(&utsname) < 0)
1145             avahi_log_warn("uname() failed: %s\n", avahi_strerror(errno));
1146         else {
1147             
1148             r->data.hinfo.cpu = avahi_strdup(avahi_strup(utsname.machine));
1149             r->data.hinfo.os = avahi_strdup(avahi_strup(utsname.sysname));
1150             
1151             avahi_log_info("Registering HINFO record with values '%s'/'%s'.", r->data.hinfo.cpu, r->data.hinfo.os);
1152             
1153             if (avahi_server_add(s, s->hinfo_entry_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_UNIQUE, r) < 0) {
1154                 avahi_log_warn("Failed to add HINFO RR: %s", avahi_strerror(s->error));
1155                 return;
1156             }
1157         }
1158
1159         avahi_record_unref(r);
1160     }
1161
1162     if (avahi_s_entry_group_commit(s->hinfo_entry_group) < 0)
1163         avahi_log_warn("Failed to commit HINFO entry group: %s", avahi_strerror(s->error));
1164
1165 }
1166
1167 static void register_localhost(AvahiServer *s) {
1168     AvahiAddress a;
1169     assert(s);
1170     
1171     /* Add localhost entries */
1172     avahi_address_parse("127.0.0.1", AVAHI_PROTO_INET, &a);
1173     avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_NO_PROBE|AVAHI_PUBLISH_NO_ANNOUNCE, "localhost", &a);
1174
1175     avahi_address_parse("::1", AVAHI_PROTO_INET6, &a);
1176     avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_NO_PROBE|AVAHI_PUBLISH_NO_ANNOUNCE, "ip6-localhost", &a);
1177 }
1178
1179 static void register_browse_domain(AvahiServer *s) {
1180     assert(s);
1181
1182     if (!s->config.publish_domain)
1183         return;
1184
1185     if (avahi_domain_equal(s->domain_name, "local"))
1186         return;
1187
1188     if (s->browse_domain_entry_group)
1189         assert(avahi_s_entry_group_is_empty(s->browse_domain_entry_group));
1190     else
1191         s->browse_domain_entry_group = avahi_s_entry_group_new(s, NULL, NULL);
1192
1193     if (!s->browse_domain_entry_group) {
1194         avahi_log_warn("Failed to create browse domain entry group: %s", avahi_strerror(s->error));
1195         return;
1196     }
1197     
1198     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) {
1199         avahi_log_warn("Failed to add browse domain RR: %s", avahi_strerror(s->error));
1200         return;
1201     }
1202
1203     if (avahi_s_entry_group_commit(s->browse_domain_entry_group) < 0)
1204         avahi_log_warn("Failed to commit browse domain entry group: %s", avahi_strerror(s->error));
1205 }
1206
1207 static void register_stuff(AvahiServer *s) {
1208     assert(s);
1209
1210     server_set_state(s, AVAHI_SERVER_REGISTERING);
1211     s->n_host_rr_pending ++; /** Make sure that the state isn't changed tp AVAHI_SERVER_RUNNING too early */
1212
1213     register_hinfo(s);
1214     register_browse_domain(s);
1215     avahi_interface_monitor_update_rrs(s->monitor, 0);
1216
1217     s->n_host_rr_pending --;
1218     
1219     if (s->n_host_rr_pending == 0)
1220         server_set_state(s, AVAHI_SERVER_RUNNING);
1221 }
1222
1223 static void update_fqdn(AvahiServer *s) {
1224     char *n;
1225     
1226     assert(s);
1227     assert(s->host_name);
1228     assert(s->domain_name);
1229
1230     if (!(n = avahi_strdup_printf("%s.%s", s->host_name, s->domain_name)))
1231         return; /* OOM */
1232
1233     avahi_free(s->host_name_fqdn);
1234     s->host_name_fqdn = n;
1235 }
1236
1237 int avahi_server_set_host_name(AvahiServer *s, const char *host_name) {
1238     char *hn = NULL;
1239     assert(s);
1240     
1241     AVAHI_CHECK_VALIDITY(s, !host_name || avahi_is_valid_host_name(host_name), AVAHI_ERR_INVALID_HOST_NAME);
1242
1243     if (!host_name) {
1244         hn = avahi_get_host_name_strdup();
1245         hn[strcspn(hn, ".")] = 0;
1246         host_name = hn;
1247     }
1248
1249     if (avahi_domain_equal(s->host_name, host_name) && s->state != AVAHI_SERVER_COLLISION) {
1250         avahi_free(hn);
1251         return avahi_server_set_errno(s, AVAHI_ERR_NO_CHANGE);
1252     }
1253     
1254     withdraw_host_rrs(s);
1255
1256     avahi_free(s->host_name);
1257     s->host_name = hn ? hn : avahi_strdup(host_name);
1258     
1259     update_fqdn(s);
1260
1261     register_stuff(s);
1262     return AVAHI_OK;
1263 }
1264
1265 int avahi_server_set_domain_name(AvahiServer *s, const char *domain_name) {
1266     char *dn = NULL;
1267     assert(s);
1268
1269     AVAHI_CHECK_VALIDITY(s, !domain_name || avahi_is_valid_domain_name(domain_name), AVAHI_ERR_INVALID_DOMAIN_NAME);
1270
1271     if (!domain_name) {
1272         dn = avahi_strdup("local");
1273         domain_name = dn;
1274     }
1275     
1276     if (avahi_domain_equal(s->domain_name, domain_name)) {
1277         avahi_free(dn);
1278         return avahi_server_set_errno(s, AVAHI_ERR_NO_CHANGE);
1279     }
1280
1281     withdraw_host_rrs(s);
1282
1283     avahi_free(s->domain_name);
1284     s->domain_name = avahi_normalize_name_strdup(domain_name);
1285     update_fqdn(s);
1286
1287     register_stuff(s);
1288
1289     avahi_free(dn);
1290     return AVAHI_OK;
1291 }
1292
1293 static int valid_server_config(const AvahiServerConfig *sc) {
1294     AvahiStringList *l;
1295
1296     assert(sc);
1297
1298     if (sc->n_wide_area_servers > AVAHI_WIDE_AREA_SERVERS_MAX)
1299         return AVAHI_ERR_INVALID_CONFIG;
1300     
1301     if (sc->host_name && !avahi_is_valid_host_name(sc->host_name))
1302         return AVAHI_ERR_INVALID_HOST_NAME;
1303     
1304     if (sc->domain_name && !avahi_is_valid_domain_name(sc->domain_name))
1305         return AVAHI_ERR_INVALID_DOMAIN_NAME;
1306
1307     for (l = sc->browse_domains; l; l = l->next)
1308         if (!avahi_is_valid_domain_name((char*) l->text))
1309             return AVAHI_ERR_INVALID_DOMAIN_NAME;
1310
1311     return AVAHI_OK;
1312 }
1313
1314 static int setup_sockets(AvahiServer *s) {
1315     assert(s);
1316     
1317     s->fd_ipv4 = s->config.use_ipv4 ? avahi_open_socket_ipv4(s->config.disallow_other_stacks) : -1;
1318     s->fd_ipv6 = s->config.use_ipv6 ? avahi_open_socket_ipv6(s->config.disallow_other_stacks) : -1;
1319     
1320     if (s->fd_ipv6 < 0 && s->fd_ipv4 < 0)
1321         return AVAHI_ERR_NO_NETWORK;
1322
1323     if (s->fd_ipv4 < 0 && s->config.use_ipv4)
1324         avahi_log_notice("Failed to create IPv4 socket, proceeding in IPv6 only mode");
1325     else if (s->fd_ipv6 < 0 && s->config.use_ipv6)
1326         avahi_log_notice("Failed to create IPv6 socket, proceeding in IPv4 only mode");
1327
1328     s->fd_legacy_unicast_ipv4 = s->fd_ipv4 >= 0 && s->config.enable_reflector ? avahi_open_unicast_socket_ipv4() : -1;
1329     s->fd_legacy_unicast_ipv6 = s->fd_ipv6 >= 0 && s->config.enable_reflector ? avahi_open_unicast_socket_ipv6() : -1;
1330     
1331     s->watch_ipv4 =
1332         s->watch_ipv6 =
1333         s->watch_legacy_unicast_ipv4 =
1334         s->watch_legacy_unicast_ipv6 = NULL;
1335     
1336     if (s->fd_ipv4 >= 0)
1337         s->watch_ipv4 = s->poll_api->watch_new(s->poll_api, s->fd_ipv4, AVAHI_WATCH_IN, mcast_socket_event, s);
1338     if (s->fd_ipv6 >= 0)
1339         s->watch_ipv6 = s->poll_api->watch_new(s->poll_api, s->fd_ipv6, AVAHI_WATCH_IN, mcast_socket_event, s);
1340     
1341     if (s->fd_legacy_unicast_ipv4 >= 0)
1342         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);
1343     if (s->fd_legacy_unicast_ipv6 >= 0)
1344         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);
1345             
1346     return 0;
1347 }
1348
1349 AvahiServer *avahi_server_new(const AvahiPoll *poll_api, const AvahiServerConfig *sc, AvahiServerCallback callback, void* userdata, int *error) {
1350     AvahiServer *s;
1351     int e;
1352     
1353     if (sc && (e = valid_server_config(sc)) < 0) {
1354         if (error)
1355             *error = e;
1356         return NULL;
1357     }
1358     
1359     if (!(s = avahi_new(AvahiServer, 1))) {
1360         if (error)
1361             *error = AVAHI_ERR_NO_MEMORY;
1362
1363         return NULL;
1364     }
1365
1366     s->poll_api = poll_api;
1367
1368     if (sc)
1369         avahi_server_config_copy(&s->config, sc);
1370     else
1371         avahi_server_config_init(&s->config);
1372
1373     if ((e = setup_sockets(s)) < 0) {
1374         if (error)
1375             *error = e;
1376
1377         avahi_server_config_free(&s->config);
1378         avahi_free(s);
1379         
1380         return NULL;
1381     }
1382     
1383     s->n_host_rr_pending = 0;
1384     s->need_entry_cleanup = 0;
1385     s->need_group_cleanup = 0;
1386     s->need_browser_cleanup = 0;
1387     s->hinfo_entry_group = NULL;
1388     s->browse_domain_entry_group = NULL;
1389     s->error = AVAHI_OK;
1390     s->state = AVAHI_SERVER_INVALID;
1391
1392     s->callback = callback;
1393     s->userdata = userdata;
1394
1395     s->time_event_queue = avahi_time_event_queue_new(poll_api);
1396
1397     s->entries_by_key = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL);
1398     AVAHI_LLIST_HEAD_INIT(AvahiEntry, s->entries);
1399     AVAHI_LLIST_HEAD_INIT(AvahiGroup, s->groups);
1400
1401     s->record_browser_hashmap = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL);
1402     AVAHI_LLIST_HEAD_INIT(AvahiSRecordBrowser, s->record_browsers);
1403     AVAHI_LLIST_HEAD_INIT(AvahiSHostNameResolver, s->host_name_resolvers);
1404     AVAHI_LLIST_HEAD_INIT(AvahiSAddressResolver, s->address_resolvers);
1405     AVAHI_LLIST_HEAD_INIT(AvahiSDomainBrowser, s->domain_browsers);
1406     AVAHI_LLIST_HEAD_INIT(AvahiSServiceTypeBrowser, s->service_type_browsers);
1407     AVAHI_LLIST_HEAD_INIT(AvahiSServiceBrowser, s->service_browsers);
1408     AVAHI_LLIST_HEAD_INIT(AvahiSServiceResolver, s->service_resolvers);
1409     AVAHI_LLIST_HEAD_INIT(AvahiSDNSServerBrowser, s->dns_server_browsers);
1410
1411     s->legacy_unicast_reflect_slots = NULL;
1412     s->legacy_unicast_reflect_id = 0;
1413
1414     s->record_list = avahi_record_list_new();
1415     
1416     /* Get host name */
1417     s->host_name = s->config.host_name ? avahi_normalize_name_strdup(s->config.host_name) : avahi_get_host_name_strdup();
1418     s->host_name[strcspn(s->host_name, ".")] = 0;
1419     s->domain_name = s->config.domain_name ? avahi_normalize_name_strdup(s->config.domain_name) : avahi_strdup("local");
1420     s->host_name_fqdn = NULL;
1421     update_fqdn(s);
1422
1423     do {
1424         s->local_service_cookie = (uint32_t) rand() * (uint32_t) rand();
1425     } while (s->local_service_cookie == AVAHI_SERVICE_COOKIE_INVALID);
1426
1427     if (s->config.enable_wide_area) {
1428         s->wide_area_lookup_engine = avahi_wide_area_engine_new(s);
1429         avahi_wide_area_set_servers(s->wide_area_lookup_engine, s->config.wide_area_servers, s->config.n_wide_area_servers);
1430     } else
1431         s->wide_area_lookup_engine = NULL;
1432
1433     s->multicast_lookup_engine = avahi_multicast_lookup_engine_new(s);
1434     
1435     s->monitor = avahi_interface_monitor_new(s);
1436     avahi_interface_monitor_sync(s->monitor);
1437
1438     register_localhost(s);
1439     register_stuff(s);
1440     
1441     return s;
1442 }
1443
1444 void avahi_server_free(AvahiServer* s) {
1445     assert(s);
1446
1447     /* Remove all browsers */
1448
1449     while (s->dns_server_browsers)
1450         avahi_s_dns_server_browser_free(s->dns_server_browsers);
1451     while (s->host_name_resolvers)
1452         avahi_s_host_name_resolver_free(s->host_name_resolvers);
1453     while (s->address_resolvers)
1454         avahi_s_address_resolver_free(s->address_resolvers);
1455     while (s->domain_browsers)
1456         avahi_s_domain_browser_free(s->domain_browsers);
1457     while (s->service_type_browsers)
1458         avahi_s_service_type_browser_free(s->service_type_browsers);
1459     while (s->service_browsers)
1460         avahi_s_service_browser_free(s->service_browsers);
1461     while (s->service_resolvers)
1462         avahi_s_service_resolver_free(s->service_resolvers);
1463     while (s->record_browsers)
1464         avahi_s_record_browser_destroy(s->record_browsers);
1465     
1466     /* Remove all locally rgeistered stuff */
1467
1468     while(s->entries)
1469         avahi_entry_free(s, s->entries);
1470
1471     avahi_interface_monitor_free(s->monitor);
1472
1473     while (s->groups)
1474         avahi_entry_group_free(s, s->groups);
1475
1476     free_slots(s);
1477
1478     avahi_hashmap_free(s->entries_by_key);
1479     avahi_record_list_free(s->record_list);
1480     avahi_hashmap_free(s->record_browser_hashmap);
1481
1482     if (s->wide_area_lookup_engine)
1483         avahi_wide_area_engine_free(s->wide_area_lookup_engine);
1484     avahi_multicast_lookup_engine_free(s->multicast_lookup_engine);
1485
1486     avahi_time_event_queue_free(s->time_event_queue);
1487     
1488     /* Free watches */
1489     
1490     if (s->watch_ipv4)
1491         s->poll_api->watch_free(s->watch_ipv4);
1492     if (s->watch_ipv6)
1493         s->poll_api->watch_free(s->watch_ipv6);
1494     
1495     if (s->watch_legacy_unicast_ipv4)
1496         s->poll_api->watch_free(s->watch_legacy_unicast_ipv4);
1497     if (s->watch_legacy_unicast_ipv6)
1498         s->poll_api->watch_free(s->watch_legacy_unicast_ipv6);
1499
1500     /* Free sockets */
1501     
1502     if (s->fd_ipv4 >= 0)
1503         close(s->fd_ipv4);
1504     if (s->fd_ipv6 >= 0)
1505         close(s->fd_ipv6);
1506     
1507     if (s->fd_legacy_unicast_ipv4 >= 0)
1508         close(s->fd_legacy_unicast_ipv4);
1509     if (s->fd_legacy_unicast_ipv6 >= 0)
1510         close(s->fd_legacy_unicast_ipv6);
1511     
1512     /* Free other stuff */
1513     
1514     avahi_free(s->host_name);
1515     avahi_free(s->domain_name);
1516     avahi_free(s->host_name_fqdn);
1517
1518     avahi_server_config_free(&s->config);
1519
1520     avahi_free(s);
1521 }
1522
1523 const char* avahi_server_get_domain_name(AvahiServer *s) {
1524     assert(s);
1525
1526     return s->domain_name;
1527 }
1528
1529 const char* avahi_server_get_host_name(AvahiServer *s) {
1530     assert(s);
1531
1532     return s->host_name;
1533 }
1534
1535 const char* avahi_server_get_host_name_fqdn(AvahiServer *s) {
1536     assert(s);
1537
1538     return s->host_name_fqdn;
1539 }
1540
1541 void* avahi_server_get_data(AvahiServer *s) {
1542     assert(s);
1543
1544     return s->userdata;
1545 }
1546
1547 void avahi_server_set_data(AvahiServer *s, void* userdata) {
1548     assert(s);
1549
1550     s->userdata = userdata;
1551 }
1552
1553 AvahiServerState avahi_server_get_state(AvahiServer *s) {
1554     assert(s);
1555
1556     return s->state;
1557 }
1558
1559 AvahiServerConfig* avahi_server_config_init(AvahiServerConfig *c) {
1560     assert(c);
1561
1562     memset(c, 0, sizeof(AvahiServerConfig));
1563     c->use_ipv6 = 1;
1564     c->use_ipv4 = 1;
1565     c->host_name = NULL;
1566     c->domain_name = NULL;
1567     c->check_response_ttl = 0;
1568     c->publish_hinfo = 1;
1569     c->publish_addresses = 1;
1570     c->publish_workstation = 1;
1571     c->publish_domain = 1;
1572     c->use_iff_running = 0;
1573     c->enable_reflector = 0;
1574     c->reflect_ipv = 0;
1575     c->add_service_cookie = 0;
1576     c->enable_wide_area = 0;
1577     c->n_wide_area_servers = 0;
1578     c->disallow_other_stacks = 0;
1579     c->browse_domains = NULL;
1580     c->disable_publishing = 0;
1581     c->allow_point_to_point = 0;
1582     c->publish_aaaa_on_ipv4 = 1;
1583     c->publish_a_on_ipv6 = 0;
1584     
1585     return c;
1586 }
1587
1588 void avahi_server_config_free(AvahiServerConfig *c) {
1589     assert(c);
1590
1591     avahi_free(c->host_name);
1592     avahi_free(c->domain_name);
1593     avahi_string_list_free(c->browse_domains);
1594 }
1595
1596 AvahiServerConfig* avahi_server_config_copy(AvahiServerConfig *ret, const AvahiServerConfig *c) {
1597     char *d = NULL, *h = NULL;
1598     AvahiStringList *l = NULL;
1599     assert(ret);
1600     assert(c);
1601
1602     if (c->host_name)
1603         if (!(h = avahi_strdup(c->host_name)))
1604             return NULL;
1605
1606     if (c->domain_name)
1607         if (!(d = avahi_strdup(c->domain_name))) {
1608             avahi_free(h);
1609             return NULL;
1610         }
1611
1612     if (!(l = avahi_string_list_copy(c->browse_domains)) && c->browse_domains) {
1613         avahi_free(h);
1614         avahi_free(d);
1615         return NULL;
1616     }
1617     
1618     *ret = *c;
1619     ret->host_name = h;
1620     ret->domain_name = d;
1621     ret->browse_domains = l;
1622
1623     return ret;
1624 }
1625
1626 int avahi_server_errno(AvahiServer *s) {
1627     assert(s);
1628     
1629     return s->error;
1630 }
1631
1632 /* Just for internal use */
1633 int avahi_server_set_errno(AvahiServer *s, int error) {
1634     assert(s);
1635
1636     return s->error = error;
1637 }
1638
1639 uint32_t avahi_server_get_local_service_cookie(AvahiServer *s) {
1640     assert(s);
1641
1642     return s->local_service_cookie;
1643 }
1644
1645 static AvahiEntry *find_entry(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiKey *key) {
1646     AvahiEntry *e;
1647     
1648     assert(s);
1649     assert(key);
1650
1651     for (e = avahi_hashmap_lookup(s->entries_by_key, key); e; e = e->by_key_next)
1652
1653         if ((e->interface == interface || e->interface <= 0 || interface <= 0) &&
1654             (e->protocol == protocol || e->protocol == AVAHI_PROTO_UNSPEC || protocol == AVAHI_PROTO_UNSPEC) &&
1655             (!e->group || e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED || e->group->state == AVAHI_ENTRY_GROUP_REGISTERING))
1656
1657             return e;
1658
1659     return NULL;
1660 }
1661
1662 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) {
1663     AvahiKey *key = NULL;
1664     AvahiEntry *e;
1665     int ret;
1666     char n[AVAHI_DOMAIN_NAME_MAX];
1667     
1668     assert(s);
1669     assert(name);
1670     assert(type);
1671     assert(ret_group);
1672
1673     AVAHI_CHECK_VALIDITY(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE);
1674     AVAHI_CHECK_VALIDITY(s, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL);
1675     AVAHI_CHECK_VALIDITY(s, avahi_is_valid_service_name(name), AVAHI_ERR_INVALID_SERVICE_NAME);
1676     AVAHI_CHECK_VALIDITY(s, avahi_is_valid_service_type_strict(type), AVAHI_ERR_INVALID_SERVICE_TYPE);
1677     AVAHI_CHECK_VALIDITY(s, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME);
1678
1679     if ((ret = avahi_service_name_join(n, sizeof(n), name, type, domain) < 0))
1680         return avahi_server_set_errno(s, ret);
1681         
1682     if (!(key = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV)))
1683         return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1684
1685     e = find_entry(s, interface, protocol, key);
1686     avahi_key_unref(key);
1687
1688     if (e) {
1689         *ret_group = e->group;
1690         return AVAHI_OK;
1691     }
1692
1693     return avahi_server_set_errno(s, AVAHI_ERR_NOT_FOUND);
1694 }
1695
1696 int avahi_server_is_service_local(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, const char *name) {
1697     AvahiKey *key = NULL;
1698     AvahiEntry *e;
1699
1700     assert(s);
1701     assert(name);
1702
1703     if (!s->host_name_fqdn)
1704         return 0;
1705     
1706     if (!(key = avahi_key_new(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV)))
1707         return 0;
1708
1709     e = find_entry(s, interface, protocol, key);
1710     avahi_key_unref(key);
1711
1712     if (!e)
1713         return 0;
1714     
1715     return avahi_domain_equal(s->host_name_fqdn, e->record->data.srv.name);
1716 }
1717
1718 int avahi_server_is_record_local(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiRecord *record) {
1719     AvahiEntry *e;
1720
1721     assert(s);
1722     assert(record);
1723     
1724     for (e = avahi_hashmap_lookup(s->entries_by_key, record->key); e; e = e->by_key_next)
1725
1726         if ((e->interface == interface || e->interface <= 0 || interface <= 0) &&
1727             (e->protocol == protocol || e->protocol == AVAHI_PROTO_UNSPEC || protocol == AVAHI_PROTO_UNSPEC) &&
1728             (!e->group || e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED || e->group->state == AVAHI_ENTRY_GROUP_REGISTERING) &&
1729             avahi_record_equal_no_ttl(record, e->record))
1730             return 1;
1731
1732     return 0;
1733 }
1734
1735 /** Set the wide area DNS servers */
1736 int avahi_server_set_wide_area_servers(AvahiServer *s, const AvahiAddress *a, unsigned n) {
1737     assert(s);
1738
1739     if (!s->wide_area_lookup_engine)
1740         return avahi_server_set_errno(s, AVAHI_ERR_INVALID_CONFIG);
1741
1742     avahi_wide_area_set_servers(s->wide_area_lookup_engine, a, n);
1743     return AVAHI_OK;
1744 }
1745
1746 const AvahiServerConfig* avahi_server_get_config(AvahiServer *s) {
1747     assert(s);
1748
1749     return &s->config;
1750 }
1751
1752 /** Set the browsing domains */
1753 int avahi_server_set_browse_domains(AvahiServer *s, AvahiStringList *domains) {
1754     AvahiStringList *l;
1755     
1756     assert(s);
1757
1758     for (l = s->config.browse_domains; l; l = l->next)
1759         if (!avahi_is_valid_domain_name((char*) l->text))
1760             return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
1761     
1762     avahi_string_list_free(s->config.browse_domains);
1763     s->config.browse_domains = avahi_string_list_copy(domains);
1764
1765     return AVAHI_OK;
1766 }