]> git.meshlink.io Git - catta/blob - avahi-core/server.c
add new API function avahi_server_set_browse_domains() to avahi-core
[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("Recieved conflicting probe [%s]. Local host won.", t);
232         else if (lost) {
233             avahi_log_debug("Recieved 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("Recieved 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("Recieved 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("Recieved 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("Recieved 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 an UTF8 problem?)");
583             goto fail;
584         }
585
586         if (!legacy_unicast && !from_local_iface) {
587             reflect_query(s, i, key);
588             avahi_cache_start_poof(i->cache, key, a);
589         }
590
591         if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 &&
592             !(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_TC))
593             /* Allow our own queries to be suppressed by incoming
594              * queries only when they do not include known answers */
595             avahi_query_scheduler_incoming(i->query_scheduler, key);
596         
597         avahi_server_prepare_matching_responses(s, i, key, unicast_response);
598         avahi_key_unref(key);
599     }
600
601     if (!legacy_unicast) {
602
603         /* Known Answer Suppression */
604         for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT); n > 0; n --) {
605             AvahiRecord *record;
606             int unique = 0;
607             
608             if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
609                 avahi_log_warn(__FILE__": Packet too short or invalid while reading known answer record. (Maybe an UTF8 problem?)");
610                 goto fail;
611             }
612             
613             avahi_response_scheduler_suppress(i->response_scheduler, record, a);
614             avahi_record_list_drop(s->record_list, record);
615             avahi_cache_stop_poof(i->cache, record, a);
616             
617             avahi_record_unref(record);
618         }
619         
620         /* Probe record */
621         for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT); n > 0; n --) {
622             AvahiRecord *record;
623             int unique = 0;
624             
625             if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
626                 avahi_log_warn(__FILE__": Packet too short or invalid while reading probe record. (Maybe an UTF8 problem?)");
627                 goto fail;
628             }
629             
630             if (!avahi_key_is_pattern(record->key)) {
631                 if (!from_local_iface)
632                     reflect_probe(s, i, record);
633                 incoming_probe(s, record, i);
634             }
635             
636             avahi_record_unref(record);
637         }
638     }
639
640     if (!avahi_record_list_is_empty(s->record_list))
641         avahi_server_generate_response(s, i, p, a, port, legacy_unicast, is_probe);
642
643     return;
644     
645 fail:
646     avahi_record_list_flush(s->record_list);
647 }
648
649 static void handle_response_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, int from_local_iface) {
650     unsigned n;
651     
652     assert(s);
653     assert(p);
654     assert(i);
655     assert(a);
656
657     for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) +
658              avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT); n > 0; n--) {
659         AvahiRecord *record;
660         int cache_flush = 0;
661 /*         char *txt; */
662         
663         if (!(record = avahi_dns_packet_consume_record(p, &cache_flush))) {
664             avahi_log_warn(__FILE__": Packet too short or invalid while reading response record. (Maybe an UTF8 problem?)");
665             break;
666         }
667
668         if (!avahi_key_is_pattern(record->key)) {
669
670             if (handle_conflict(s, i, record, cache_flush)) {
671                 if (!from_local_iface)
672                     reflect_response(s, i, record, cache_flush);
673                 avahi_cache_update(i->cache, record, cache_flush, a);
674                 avahi_response_scheduler_incoming(i->response_scheduler, record, cache_flush);
675             }
676         }
677             
678         avahi_record_unref(record);
679     }
680
681     /* If the incoming response contained a conflicting record, some
682        records have been scheduling for sending. We need to flush them
683        here. */
684     if (!avahi_record_list_is_empty(s->record_list))
685         avahi_server_generate_response(s, i, NULL, NULL, 0, 0, 1);
686 }
687
688 static AvahiLegacyUnicastReflectSlot* allocate_slot(AvahiServer *s) {
689     unsigned n, idx = (unsigned) -1;
690     AvahiLegacyUnicastReflectSlot *slot;
691     
692     assert(s);
693
694     if (!s->legacy_unicast_reflect_slots)
695         s->legacy_unicast_reflect_slots = avahi_new0(AvahiLegacyUnicastReflectSlot*, AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX);
696
697     for (n = 0; n < AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX; n++, s->legacy_unicast_reflect_id++) {
698         idx = s->legacy_unicast_reflect_id % AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX;
699         
700         if (!s->legacy_unicast_reflect_slots[idx])
701             break;
702     }
703
704     if (idx == (unsigned) -1 || s->legacy_unicast_reflect_slots[idx])
705         return NULL;
706
707     if (!(slot = avahi_new(AvahiLegacyUnicastReflectSlot, 1)))
708         return NULL; /* OOM */
709
710     s->legacy_unicast_reflect_slots[idx] = slot;
711     slot->id = s->legacy_unicast_reflect_id++;
712     slot->server = s;
713     
714     return slot;
715 }
716
717 static void deallocate_slot(AvahiServer *s, AvahiLegacyUnicastReflectSlot *slot) {
718     unsigned idx;
719
720     assert(s);
721     assert(slot);
722
723     idx = slot->id % AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX;
724
725     assert(s->legacy_unicast_reflect_slots[idx] == slot);
726
727     avahi_time_event_free(slot->time_event);
728     
729     avahi_free(slot);
730     s->legacy_unicast_reflect_slots[idx] = NULL;
731 }
732
733 static void free_slots(AvahiServer *s) {
734     unsigned idx;
735     assert(s);
736
737     if (!s->legacy_unicast_reflect_slots)
738         return;
739
740     for (idx = 0; idx < AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX; idx ++)
741         if (s->legacy_unicast_reflect_slots[idx])
742             deallocate_slot(s, s->legacy_unicast_reflect_slots[idx]);
743
744     avahi_free(s->legacy_unicast_reflect_slots);
745     s->legacy_unicast_reflect_slots = NULL;
746 }
747
748 static AvahiLegacyUnicastReflectSlot* find_slot(AvahiServer *s, uint16_t id) {
749     unsigned idx;
750     
751     assert(s);
752
753     if (!s->legacy_unicast_reflect_slots)
754         return NULL;
755     
756     idx = id % AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX;
757
758     if (!s->legacy_unicast_reflect_slots[idx] || s->legacy_unicast_reflect_slots[idx]->id != id)
759         return NULL;
760
761     return s->legacy_unicast_reflect_slots[idx];
762 }
763
764 static void legacy_unicast_reflect_slot_timeout(AvahiTimeEvent *e, void *userdata) {
765     AvahiLegacyUnicastReflectSlot *slot = userdata;
766
767     assert(e);
768     assert(slot);
769     assert(slot->time_event == e);
770
771     deallocate_slot(slot->server, slot);
772 }
773
774 static void reflect_legacy_unicast_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, uint16_t port) {
775     AvahiLegacyUnicastReflectSlot *slot;
776     AvahiInterface *j;
777
778     assert(s);
779     assert(p);
780     assert(i);
781     assert(a);
782     assert(port > 0);
783     assert(i->protocol == a->proto);
784     
785     if (!s->config.enable_reflector)
786         return;
787
788     /* Reflecting legacy unicast queries is a little more complicated
789        than reflecting normal queries, since we must route the
790        responses back to the right client. Therefore we must store
791        some information for finding the right client contact data for
792        response packets. In contrast to normal queries legacy
793        unicast query and response packets are reflected untouched and
794        are not reassembled into larger packets */
795
796     if (!(slot = allocate_slot(s))) {
797         /* No slot available, we drop this legacy unicast query */
798         avahi_log_warn("No slot available for legacy unicast reflection, dropping query packet.");
799         return;
800     }
801
802     slot->original_id = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID);
803     slot->address = *a;
804     slot->port = port;
805     slot->interface = i->hardware->index;
806
807     avahi_elapse_time(&slot->elapse_time, 2000, 0);
808     slot->time_event = avahi_time_event_new(s->time_event_queue, &slot->elapse_time, legacy_unicast_reflect_slot_timeout, slot);
809
810     /* Patch the packet with our new locally generatet id */
811     avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
812     
813     for (j = s->monitor->interfaces; j; j = j->interface_next)
814         if (j->announcing &&
815             j != i &&
816             (s->config.reflect_ipv || j->protocol == i->protocol)) {
817
818             if (j->protocol == AVAHI_PROTO_INET && s->fd_legacy_unicast_ipv4 >= 0) {
819                 avahi_send_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, j->hardware->index, p, NULL, NULL, 0);
820             } else if (j->protocol == AVAHI_PROTO_INET6 && s->fd_legacy_unicast_ipv6 >= 0)
821                 avahi_send_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, j->hardware->index, p, NULL, NULL, 0);
822         }
823
824     /* Reset the id */
825     avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
826 }
827
828 static int originates_from_local_legacy_unicast_socket(AvahiServer *s, const AvahiAddress *address, uint16_t port) {
829     assert(s);
830     assert(address);
831     assert(port > 0);
832
833     if (!s->config.enable_reflector)
834         return 0;
835     
836     if (!avahi_address_is_local(s->monitor, address))
837         return 0;
838     
839     if (address->proto == AVAHI_PROTO_INET && s->fd_legacy_unicast_ipv4 >= 0) {
840         struct sockaddr_in lsa;
841         socklen_t l = sizeof(lsa);
842         
843         if (getsockname(s->fd_legacy_unicast_ipv4, (struct sockaddr*) &lsa, &l) != 0)
844             avahi_log_warn("getsockname(): %s", strerror(errno));
845         else
846             return lsa.sin_port == port;
847
848     }
849
850     if (address->proto == AVAHI_PROTO_INET6 && s->fd_legacy_unicast_ipv6 >= 0) {
851         struct sockaddr_in6 lsa;
852         socklen_t l = sizeof(lsa);
853
854         if (getsockname(s->fd_legacy_unicast_ipv6, (struct sockaddr*) &lsa, &l) != 0)
855             avahi_log_warn("getsockname(): %s", strerror(errno));
856         else
857             return lsa.sin6_port == port;
858     }
859
860     return 0;
861 }
862
863 static int is_mdns_mcast_address(const AvahiAddress *a) {
864     AvahiAddress b;
865     assert(a);
866
867     avahi_address_parse(a->proto == AVAHI_PROTO_INET ? AVAHI_IPV4_MCAST_GROUP : AVAHI_IPV6_MCAST_GROUP, a->proto, &b);
868     return avahi_address_cmp(a, &b) == 0;
869 }
870
871 static int originates_from_local_iface(AvahiServer *s, AvahiIfIndex iface, const AvahiAddress *a, uint16_t port) {
872     assert(s);
873     assert(iface != AVAHI_IF_UNSPEC);
874     assert(a);
875
876     /* If it isn't the MDNS port it can't be generated by us */
877     if (port != AVAHI_MDNS_PORT)
878         return 0;
879
880     return avahi_interface_has_address(s->monitor, iface, a);
881 }
882
883 static void dispatch_packet(AvahiServer *s, AvahiDnsPacket *p, const AvahiAddress *src_address, uint16_t port, const AvahiAddress *dst_address, AvahiIfIndex iface, int ttl) {
884     AvahiInterface *i;
885     int from_local_iface = 0;
886     
887     assert(s);
888     assert(p);
889     assert(src_address);
890     assert(dst_address);
891     assert(iface > 0);
892     assert(src_address->proto == dst_address->proto);
893
894     if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, src_address->proto)) ||
895         !i->announcing) {
896         avahi_log_warn("Recieved packet from invalid interface.");
897         return;
898     }
899
900     if (avahi_address_is_ipv4_in_ipv6(src_address))
901         /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
902         return;
903
904     if (originates_from_local_legacy_unicast_socket(s, src_address, port))
905         /* This originates from our local reflector, so let's ignore it */
906         return;
907
908     /* We don't want to reflect local traffic, so we check if this packet is generated locally. */
909     if (s->config.enable_reflector)
910         from_local_iface = originates_from_local_iface(s, iface, src_address, port);
911
912     if (avahi_dns_packet_check_valid_multicast(p) < 0) {
913         avahi_log_warn("Recieved invalid packet.");
914         return;
915     }
916
917     if (avahi_dns_packet_is_query(p)) {
918         int legacy_unicast = 0;
919
920         if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT) != 0) {
921             avahi_log_warn("Invalid query packet.");
922             return;
923         }
924
925         if (port != AVAHI_MDNS_PORT) {
926             /* Legacy Unicast */
927
928             if ((avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) != 0 ||
929                  avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0)) {
930                 avahi_log_warn("Invalid legacy unicast query packet.");
931                 return;
932             }
933         
934             legacy_unicast = 1;
935         }
936
937         if (legacy_unicast)
938             reflect_legacy_unicast_query_packet(s, p, i, src_address, port);
939         
940         handle_query_packet(s, p, i, src_address, port, legacy_unicast, from_local_iface);
941         
942     } else {
943         if (port != AVAHI_MDNS_PORT) {
944             avahi_log_warn("Recieved repsonse with invalid source port %u on interface '%s.%i'", port, i->hardware->name, i->protocol);
945             return;
946         }
947
948         if (ttl != 255 && s->config.check_response_ttl) {
949             avahi_log_warn("Recieved response with invalid TTL %u on interface '%s.%i'.", ttl, i->hardware->name, i->protocol);
950             return;
951         }
952
953         if (!is_mdns_mcast_address(dst_address) &&
954             !avahi_interface_address_on_link(i, src_address)) {
955             avahi_log_warn("Received non-local response on interface '%s.%i'.", i->hardware->name, i->protocol);
956             return;
957         }
958         
959         if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT) != 0 ||
960             avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 ||
961             avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0) {
962             avahi_log_warn("Invalid response packet.");
963             return;
964         }
965
966         handle_response_packet(s, p, i, src_address, from_local_iface);
967     }
968 }
969
970 static void dispatch_legacy_unicast_packet(AvahiServer *s, AvahiDnsPacket *p) {
971     AvahiInterface *j;
972     AvahiLegacyUnicastReflectSlot *slot;
973     
974     assert(s);
975     assert(p);
976
977     if (avahi_dns_packet_check_valid(p) < 0 || avahi_dns_packet_is_query(p)) {
978         avahi_log_warn("Recieved invalid packet.");
979         return;
980     }
981
982     if (!(slot = find_slot(s, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID)))) {
983         avahi_log_warn("Recieved legacy unicast response with unknown id");
984         return;
985     }
986
987     if (!(j = avahi_interface_monitor_get_interface(s->monitor, slot->interface, slot->address.proto)) ||
988         !j->announcing)
989         return;
990
991     /* Patch the original ID into this response */
992     avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
993
994     /* Forward the response to the correct client */
995     avahi_interface_send_packet_unicast(j, p, &slot->address, slot->port);
996
997     /* Undo changes to packet */
998     avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
999 }
1000
1001 static void cleanup_dead(AvahiServer *s) {
1002     assert(s);
1003     
1004     avahi_cleanup_dead_entries(s);
1005     avahi_browser_cleanup(s);
1006 }
1007
1008 static void mcast_socket_event(AvahiWatch *w, int fd, AvahiWatchEvent events, void *userdata) {
1009     AvahiServer *s = userdata;
1010     AvahiAddress dest, src;
1011     AvahiDnsPacket *p = NULL;
1012     AvahiIfIndex iface;
1013     uint16_t port;
1014     uint8_t ttl;
1015
1016     assert(w);
1017     assert(fd >= 0);
1018     assert(events & AVAHI_WATCH_IN);
1019     
1020     if (fd == s->fd_ipv4) {
1021         dest.proto = src.proto = AVAHI_PROTO_INET;
1022         p = avahi_recv_dns_packet_ipv4(s->fd_ipv4, &src.data.ipv4, &port, &dest.data.ipv4, &iface, &ttl);
1023     } else {
1024         assert(fd == s->fd_ipv6);
1025         dest.proto = src.proto = AVAHI_PROTO_INET6;
1026         p = avahi_recv_dns_packet_ipv6(s->fd_ipv6, &src.data.ipv6, &port, &dest.data.ipv6, &iface, &ttl);
1027     }
1028         
1029     if (p) {
1030         if (iface == AVAHI_IF_UNSPEC) 
1031             iface = avahi_find_interface_for_address(s->monitor, &dest);
1032         
1033         if (iface != AVAHI_IF_UNSPEC)
1034             dispatch_packet(s, p, &src, port, &dest, iface, ttl);
1035         else
1036             avahi_log_error("Incoming packet recieved on address that isn't local.");
1037
1038         avahi_dns_packet_free(p);
1039
1040         cleanup_dead(s);
1041     }
1042 }
1043
1044 static void legacy_unicast_socket_event(AvahiWatch *w, int fd, AvahiWatchEvent events, void *userdata) {
1045     AvahiServer *s = userdata;
1046     AvahiDnsPacket *p = NULL;
1047
1048     assert(w);
1049     assert(fd >= 0);
1050     assert(events & AVAHI_WATCH_IN);
1051     
1052     if (fd == s->fd_legacy_unicast_ipv4)
1053         p = avahi_recv_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, NULL, NULL, NULL, NULL, NULL);
1054     else {
1055         assert(fd == s->fd_legacy_unicast_ipv6);
1056         p = avahi_recv_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, NULL, NULL, NULL, NULL, NULL);
1057     }
1058
1059     if (p) {
1060         dispatch_legacy_unicast_packet(s, p);
1061         avahi_dns_packet_free(p);
1062
1063         cleanup_dead(s);
1064     }
1065 }
1066
1067 static void server_set_state(AvahiServer *s, AvahiServerState state) {
1068     assert(s);
1069
1070     if (s->state == state)
1071         return;
1072     
1073     s->state = state;
1074
1075     avahi_interface_monitor_update_rrs(s->monitor, 0);
1076     
1077     if (s->callback)
1078         s->callback(s, state, s->userdata);
1079 }
1080
1081 static void withdraw_host_rrs(AvahiServer *s) {
1082     assert(s);
1083
1084     if (s->hinfo_entry_group)
1085         avahi_s_entry_group_reset(s->hinfo_entry_group);
1086
1087     if (s->browse_domain_entry_group)
1088         avahi_s_entry_group_reset(s->browse_domain_entry_group);
1089
1090     avahi_interface_monitor_update_rrs(s->monitor, 1);
1091     s->n_host_rr_pending = 0;
1092 }
1093
1094 void avahi_server_decrease_host_rr_pending(AvahiServer *s) {
1095     assert(s);
1096     
1097     assert(s->n_host_rr_pending > 0);
1098
1099     if (--s->n_host_rr_pending == 0)
1100         server_set_state(s, AVAHI_SERVER_RUNNING);
1101 }
1102
1103 void avahi_host_rr_entry_group_callback(AvahiServer *s, AvahiSEntryGroup *g, AvahiEntryGroupState state, AVAHI_GCC_UNUSED void *userdata) {
1104     assert(s);
1105     assert(g);
1106
1107     if (state == AVAHI_ENTRY_GROUP_REGISTERING &&
1108         s->state == AVAHI_SERVER_REGISTERING)
1109         s->n_host_rr_pending ++;
1110     
1111     else if (state == AVAHI_ENTRY_GROUP_COLLISION &&
1112         (s->state == AVAHI_SERVER_REGISTERING || s->state == AVAHI_SERVER_RUNNING)) {
1113         withdraw_host_rrs(s);
1114         server_set_state(s, AVAHI_SERVER_COLLISION);
1115         
1116     } else if (state == AVAHI_ENTRY_GROUP_ESTABLISHED &&
1117                s->state == AVAHI_SERVER_REGISTERING)
1118         avahi_server_decrease_host_rr_pending(s);
1119 }
1120
1121 static void register_hinfo(AvahiServer *s) {
1122     struct utsname utsname;
1123     AvahiRecord *r;
1124     
1125     assert(s);
1126     
1127     if (!s->config.publish_hinfo)
1128         return;
1129
1130     if (s->hinfo_entry_group)
1131         assert(avahi_s_entry_group_is_empty(s->hinfo_entry_group));
1132     else
1133         s->hinfo_entry_group = avahi_s_entry_group_new(s, avahi_host_rr_entry_group_callback, NULL);
1134
1135     if (!s->hinfo_entry_group) {
1136         avahi_log_warn("Failed to create HINFO entry group: %s", avahi_strerror(s->error));
1137         return;
1138     }
1139     
1140     /* Fill in HINFO rr */
1141     if ((r = avahi_record_new_full(s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_HINFO, AVAHI_DEFAULT_TTL_HOST_NAME))) {
1142
1143         if (uname(&utsname) < 0)
1144             avahi_log_warn("uname() failed: %s\n", avahi_strerror(errno));
1145         else {
1146             
1147             r->data.hinfo.cpu = avahi_strdup(avahi_strup(utsname.machine));
1148             r->data.hinfo.os = avahi_strdup(avahi_strup(utsname.sysname));
1149             
1150             avahi_log_info("Registering HINFO record with values '%s'/'%s'.", r->data.hinfo.cpu, r->data.hinfo.os);
1151             
1152             if (avahi_server_add(s, s->hinfo_entry_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_UNIQUE, r) < 0) {
1153                 avahi_log_warn("Failed to add HINFO RR: %s", avahi_strerror(s->error));
1154                 return;
1155             }
1156         }
1157
1158         avahi_record_unref(r);
1159     }
1160
1161     if (avahi_s_entry_group_commit(s->hinfo_entry_group) < 0)
1162         avahi_log_warn("Failed to commit HINFO entry group: %s", avahi_strerror(s->error));
1163
1164 }
1165
1166 static void register_localhost(AvahiServer *s) {
1167     AvahiAddress a;
1168     assert(s);
1169     
1170     /* Add localhost entries */
1171     avahi_address_parse("127.0.0.1", AVAHI_PROTO_INET, &a);
1172     avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_NO_PROBE|AVAHI_PUBLISH_NO_ANNOUNCE, "localhost", &a);
1173
1174     avahi_address_parse("::1", AVAHI_PROTO_INET6, &a);
1175     avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_NO_PROBE|AVAHI_PUBLISH_NO_ANNOUNCE, "ip6-localhost", &a);
1176 }
1177
1178 static void register_browse_domain(AvahiServer *s) {
1179     assert(s);
1180
1181     if (!s->config.publish_domain)
1182         return;
1183
1184     if (avahi_domain_equal(s->domain_name, "local"))
1185         return;
1186
1187     if (s->browse_domain_entry_group)
1188         assert(avahi_s_entry_group_is_empty(s->browse_domain_entry_group));
1189     else
1190         s->browse_domain_entry_group = avahi_s_entry_group_new(s, NULL, NULL);
1191
1192     if (!s->browse_domain_entry_group) {
1193         avahi_log_warn("Failed to create browse domain entry group: %s", avahi_strerror(s->error));
1194         return;
1195     }
1196     
1197     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) {
1198         avahi_log_warn("Failed to add browse domain RR: %s", avahi_strerror(s->error));
1199         return;
1200     }
1201
1202     if (avahi_s_entry_group_commit(s->browse_domain_entry_group) < 0)
1203         avahi_log_warn("Failed to commit browse domain entry group: %s", avahi_strerror(s->error));
1204 }
1205
1206 static void register_stuff(AvahiServer *s) {
1207     assert(s);
1208
1209     server_set_state(s, AVAHI_SERVER_REGISTERING);
1210     s->n_host_rr_pending ++; /** Make sure that the state isn't changed tp AVAHI_SERVER_RUNNING too early */
1211
1212     register_hinfo(s);
1213     register_browse_domain(s);
1214     avahi_interface_monitor_update_rrs(s->monitor, 0);
1215
1216     s->n_host_rr_pending --;
1217     
1218     if (s->n_host_rr_pending == 0)
1219         server_set_state(s, AVAHI_SERVER_RUNNING);
1220 }
1221
1222 static void update_fqdn(AvahiServer *s) {
1223     char *n;
1224     
1225     assert(s);
1226     assert(s->host_name);
1227     assert(s->domain_name);
1228
1229     if (!(n = avahi_strdup_printf("%s.%s", s->host_name, s->domain_name)))
1230         return; /* OOM */
1231
1232     avahi_free(s->host_name_fqdn);
1233     s->host_name_fqdn = n;
1234 }
1235
1236 int avahi_server_set_host_name(AvahiServer *s, const char *host_name) {
1237     char *hn = NULL;
1238     assert(s);
1239     
1240     AVAHI_CHECK_VALIDITY(s, !host_name || avahi_is_valid_host_name(host_name), AVAHI_ERR_INVALID_HOST_NAME);
1241
1242     if (!host_name) {
1243         hn = avahi_get_host_name_strdup();
1244         hn[strcspn(hn, ".")] = 0;
1245         host_name = hn;
1246     }
1247
1248     if (avahi_domain_equal(s->host_name, host_name) && s->state != AVAHI_SERVER_COLLISION) {
1249         avahi_free(hn);
1250         return avahi_server_set_errno(s, AVAHI_ERR_NO_CHANGE);
1251     }
1252     
1253     withdraw_host_rrs(s);
1254
1255     avahi_free(s->host_name);
1256     s->host_name = hn ? hn : avahi_strdup(host_name);
1257     
1258     update_fqdn(s);
1259
1260     register_stuff(s);
1261     return AVAHI_OK;
1262 }
1263
1264 int avahi_server_set_domain_name(AvahiServer *s, const char *domain_name) {
1265     char *dn = NULL;
1266     assert(s);
1267
1268     AVAHI_CHECK_VALIDITY(s, !domain_name || avahi_is_valid_domain_name(domain_name), AVAHI_ERR_INVALID_DOMAIN_NAME);
1269
1270     if (!domain_name) {
1271         dn = avahi_strdup("local");
1272         domain_name = dn;
1273     }
1274     
1275     if (avahi_domain_equal(s->domain_name, domain_name)) {
1276         avahi_free(dn);
1277         return avahi_server_set_errno(s, AVAHI_ERR_NO_CHANGE);
1278     }
1279
1280     withdraw_host_rrs(s);
1281
1282     avahi_free(s->domain_name);
1283     s->domain_name = avahi_normalize_name_strdup(domain_name);
1284     update_fqdn(s);
1285
1286     register_stuff(s);
1287
1288     avahi_free(dn);
1289     return AVAHI_OK;
1290 }
1291
1292 static int valid_server_config(const AvahiServerConfig *sc) {
1293     AvahiStringList *l;
1294
1295     assert(sc);
1296
1297     if (sc->n_wide_area_servers > AVAHI_WIDE_AREA_SERVERS_MAX)
1298         return AVAHI_ERR_INVALID_CONFIG;
1299     
1300     if (sc->host_name && !avahi_is_valid_host_name(sc->host_name))
1301         return AVAHI_ERR_INVALID_HOST_NAME;
1302     
1303     if (sc->domain_name && !avahi_is_valid_domain_name(sc->domain_name))
1304         return AVAHI_ERR_INVALID_DOMAIN_NAME;
1305
1306     for (l = sc->browse_domains; l; l = l->next)
1307         if (!avahi_is_valid_domain_name((char*) l->text))
1308             return AVAHI_ERR_INVALID_DOMAIN_NAME;
1309
1310     return AVAHI_OK;
1311 }
1312
1313 static int setup_sockets(AvahiServer *s) {
1314     assert(s);
1315     
1316     s->fd_ipv4 = s->config.use_ipv4 ? avahi_open_socket_ipv4(s->config.disallow_other_stacks) : -1;
1317     s->fd_ipv6 = s->config.use_ipv6 ? avahi_open_socket_ipv6(s->config.disallow_other_stacks) : -1;
1318     
1319     if (s->fd_ipv6 < 0 && s->fd_ipv4 < 0)
1320         return AVAHI_ERR_NO_NETWORK;
1321
1322     if (s->fd_ipv4 < 0 && s->config.use_ipv4)
1323         avahi_log_notice("Failed to create IPv4 socket, proceeding in IPv6 only mode");
1324     else if (s->fd_ipv6 < 0 && s->config.use_ipv6)
1325         avahi_log_notice("Failed to create IPv6 socket, proceeding in IPv4 only mode");
1326
1327     s->fd_legacy_unicast_ipv4 = s->fd_ipv4 >= 0 && s->config.enable_reflector ? avahi_open_unicast_socket_ipv4() : -1;
1328     s->fd_legacy_unicast_ipv6 = s->fd_ipv6 >= 0 && s->config.enable_reflector ? avahi_open_unicast_socket_ipv6() : -1;
1329     
1330     s->watch_ipv4 =
1331         s->watch_ipv6 =
1332         s->watch_legacy_unicast_ipv4 =
1333         s->watch_legacy_unicast_ipv6 = NULL;
1334     
1335     if (s->fd_ipv4 >= 0)
1336         s->watch_ipv4 = s->poll_api->watch_new(s->poll_api, s->fd_ipv4, AVAHI_WATCH_IN, mcast_socket_event, s);
1337     if (s->fd_ipv6 >= 0)
1338         s->watch_ipv6 = s->poll_api->watch_new(s->poll_api, s->fd_ipv6, AVAHI_WATCH_IN, mcast_socket_event, s);
1339     
1340     if (s->fd_legacy_unicast_ipv4 >= 0)
1341         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);
1342     if (s->fd_legacy_unicast_ipv6 >= 0)
1343         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);
1344             
1345     return 0;
1346 }
1347
1348 AvahiServer *avahi_server_new(const AvahiPoll *poll_api, const AvahiServerConfig *sc, AvahiServerCallback callback, void* userdata, int *error) {
1349     AvahiServer *s;
1350     int e;
1351     
1352     if (sc && (e = valid_server_config(sc)) < 0) {
1353         if (error)
1354             *error = e;
1355         return NULL;
1356     }
1357     
1358     if (!(s = avahi_new(AvahiServer, 1))) {
1359         if (error)
1360             *error = AVAHI_ERR_NO_MEMORY;
1361
1362         return NULL;
1363     }
1364
1365     s->poll_api = poll_api;
1366
1367     if (sc)
1368         avahi_server_config_copy(&s->config, sc);
1369     else
1370         avahi_server_config_init(&s->config);
1371
1372     if ((e = setup_sockets(s)) < 0) {
1373         if (error)
1374             *error = e;
1375
1376         avahi_server_config_free(&s->config);
1377         avahi_free(s);
1378         
1379         return NULL;
1380     }
1381     
1382     s->n_host_rr_pending = 0;
1383     s->need_entry_cleanup = 0;
1384     s->need_group_cleanup = 0;
1385     s->need_browser_cleanup = 0;
1386     s->hinfo_entry_group = NULL;
1387     s->browse_domain_entry_group = NULL;
1388     s->error = AVAHI_OK;
1389     s->state = AVAHI_SERVER_INVALID;
1390
1391     s->callback = callback;
1392     s->userdata = userdata;
1393
1394     s->time_event_queue = avahi_time_event_queue_new(poll_api);
1395
1396     s->entries_by_key = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL);
1397     AVAHI_LLIST_HEAD_INIT(AvahiEntry, s->entries);
1398     AVAHI_LLIST_HEAD_INIT(AvahiGroup, s->groups);
1399
1400     s->record_browser_hashmap = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL);
1401     AVAHI_LLIST_HEAD_INIT(AvahiSRecordBrowser, s->record_browsers);
1402     AVAHI_LLIST_HEAD_INIT(AvahiSHostNameResolver, s->host_name_resolvers);
1403     AVAHI_LLIST_HEAD_INIT(AvahiSAddressResolver, s->address_resolvers);
1404     AVAHI_LLIST_HEAD_INIT(AvahiSDomainBrowser, s->domain_browsers);
1405     AVAHI_LLIST_HEAD_INIT(AvahiSServiceTypeBrowser, s->service_type_browsers);
1406     AVAHI_LLIST_HEAD_INIT(AvahiSServiceBrowser, s->service_browsers);
1407     AVAHI_LLIST_HEAD_INIT(AvahiSServiceResolver, s->service_resolvers);
1408     AVAHI_LLIST_HEAD_INIT(AvahiSDNSServerBrowser, s->dns_server_browsers);
1409
1410     s->legacy_unicast_reflect_slots = NULL;
1411     s->legacy_unicast_reflect_id = 0;
1412
1413     s->record_list = avahi_record_list_new();
1414     
1415     /* Get host name */
1416     s->host_name = s->config.host_name ? avahi_normalize_name_strdup(s->config.host_name) : avahi_get_host_name_strdup();
1417     s->host_name[strcspn(s->host_name, ".")] = 0;
1418     s->domain_name = s->config.domain_name ? avahi_normalize_name_strdup(s->config.domain_name) : avahi_strdup("local");
1419     s->host_name_fqdn = NULL;
1420     update_fqdn(s);
1421
1422     do {
1423         s->local_service_cookie = (uint32_t) rand() * (uint32_t) rand();
1424     } while (s->local_service_cookie == AVAHI_SERVICE_COOKIE_INVALID);
1425
1426     if (s->config.enable_wide_area) {
1427         s->wide_area_lookup_engine = avahi_wide_area_engine_new(s);
1428         avahi_wide_area_set_servers(s->wide_area_lookup_engine, s->config.wide_area_servers, s->config.n_wide_area_servers);
1429     } else
1430         s->wide_area_lookup_engine = NULL;
1431
1432     s->multicast_lookup_engine = avahi_multicast_lookup_engine_new(s);
1433     
1434     s->monitor = avahi_interface_monitor_new(s);
1435     avahi_interface_monitor_sync(s->monitor);
1436
1437     register_localhost(s);
1438     register_stuff(s);
1439     
1440     return s;
1441 }
1442
1443 void avahi_server_free(AvahiServer* s) {
1444     assert(s);
1445
1446     /* Remove all browsers */
1447
1448     while (s->dns_server_browsers)
1449         avahi_s_dns_server_browser_free(s->dns_server_browsers);
1450     while (s->host_name_resolvers)
1451         avahi_s_host_name_resolver_free(s->host_name_resolvers);
1452     while (s->address_resolvers)
1453         avahi_s_address_resolver_free(s->address_resolvers);
1454     while (s->domain_browsers)
1455         avahi_s_domain_browser_free(s->domain_browsers);
1456     while (s->service_type_browsers)
1457         avahi_s_service_type_browser_free(s->service_type_browsers);
1458     while (s->service_browsers)
1459         avahi_s_service_browser_free(s->service_browsers);
1460     while (s->service_resolvers)
1461         avahi_s_service_resolver_free(s->service_resolvers);
1462     while (s->record_browsers)
1463         avahi_s_record_browser_destroy(s->record_browsers);
1464     
1465     /* Remove all locally rgeistered stuff */
1466
1467     while(s->entries)
1468         avahi_entry_free(s, s->entries);
1469
1470     avahi_interface_monitor_free(s->monitor);
1471
1472     while (s->groups)
1473         avahi_entry_group_free(s, s->groups);
1474
1475     free_slots(s);
1476
1477     avahi_hashmap_free(s->entries_by_key);
1478     avahi_record_list_free(s->record_list);
1479     avahi_hashmap_free(s->record_browser_hashmap);
1480
1481     if (s->wide_area_lookup_engine)
1482         avahi_wide_area_engine_free(s->wide_area_lookup_engine);
1483     avahi_multicast_lookup_engine_free(s->multicast_lookup_engine);
1484
1485     avahi_time_event_queue_free(s->time_event_queue);
1486     
1487     /* Free watches */
1488     
1489     if (s->watch_ipv4)
1490         s->poll_api->watch_free(s->watch_ipv4);
1491     if (s->watch_ipv6)
1492         s->poll_api->watch_free(s->watch_ipv6);
1493     
1494     if (s->watch_legacy_unicast_ipv4)
1495         s->poll_api->watch_free(s->watch_legacy_unicast_ipv4);
1496     if (s->watch_legacy_unicast_ipv6)
1497         s->poll_api->watch_free(s->watch_legacy_unicast_ipv6);
1498
1499     /* Free sockets */
1500     
1501     if (s->fd_ipv4 >= 0)
1502         close(s->fd_ipv4);
1503     if (s->fd_ipv6 >= 0)
1504         close(s->fd_ipv6);
1505     
1506     if (s->fd_legacy_unicast_ipv4 >= 0)
1507         close(s->fd_legacy_unicast_ipv4);
1508     if (s->fd_legacy_unicast_ipv6 >= 0)
1509         close(s->fd_legacy_unicast_ipv6);
1510     
1511     /* Free other stuff */
1512     
1513     avahi_free(s->host_name);
1514     avahi_free(s->domain_name);
1515     avahi_free(s->host_name_fqdn);
1516
1517     avahi_server_config_free(&s->config);
1518
1519     avahi_free(s);
1520 }
1521
1522 const char* avahi_server_get_domain_name(AvahiServer *s) {
1523     assert(s);
1524
1525     return s->domain_name;
1526 }
1527
1528 const char* avahi_server_get_host_name(AvahiServer *s) {
1529     assert(s);
1530
1531     return s->host_name;
1532 }
1533
1534 const char* avahi_server_get_host_name_fqdn(AvahiServer *s) {
1535     assert(s);
1536
1537     return s->host_name_fqdn;
1538 }
1539
1540 void* avahi_server_get_data(AvahiServer *s) {
1541     assert(s);
1542
1543     return s->userdata;
1544 }
1545
1546 void avahi_server_set_data(AvahiServer *s, void* userdata) {
1547     assert(s);
1548
1549     s->userdata = userdata;
1550 }
1551
1552 AvahiServerState avahi_server_get_state(AvahiServer *s) {
1553     assert(s);
1554
1555     return s->state;
1556 }
1557
1558 AvahiServerConfig* avahi_server_config_init(AvahiServerConfig *c) {
1559     assert(c);
1560
1561     memset(c, 0, sizeof(AvahiServerConfig));
1562     c->use_ipv6 = 1;
1563     c->use_ipv4 = 1;
1564     c->host_name = NULL;
1565     c->domain_name = NULL;
1566     c->check_response_ttl = 0;
1567     c->publish_hinfo = 1;
1568     c->publish_addresses = 1;
1569     c->publish_workstation = 1;
1570     c->publish_domain = 1;
1571     c->use_iff_running = 0;
1572     c->enable_reflector = 0;
1573     c->reflect_ipv = 0;
1574     c->add_service_cookie = 0;
1575     c->enable_wide_area = 0;
1576     c->n_wide_area_servers = 0;
1577     c->disallow_other_stacks = 0;
1578     c->browse_domains = NULL;
1579     c->disable_publishing = 0;
1580     c->allow_point_to_point = 0;
1581     c->publish_aaaa_on_ipv4 = 1;
1582     c->publish_a_on_ipv6 = 0;
1583     
1584     return c;
1585 }
1586
1587 void avahi_server_config_free(AvahiServerConfig *c) {
1588     assert(c);
1589
1590     avahi_free(c->host_name);
1591     avahi_free(c->domain_name);
1592     avahi_string_list_free(c->browse_domains);
1593 }
1594
1595 AvahiServerConfig* avahi_server_config_copy(AvahiServerConfig *ret, const AvahiServerConfig *c) {
1596     char *d = NULL, *h = NULL;
1597     AvahiStringList *l = NULL;
1598     assert(ret);
1599     assert(c);
1600
1601     if (c->host_name)
1602         if (!(h = avahi_strdup(c->host_name)))
1603             return NULL;
1604
1605     if (c->domain_name)
1606         if (!(d = avahi_strdup(c->domain_name))) {
1607             avahi_free(h);
1608             return NULL;
1609         }
1610
1611     if (!(l = avahi_string_list_copy(c->browse_domains)) && c->browse_domains) {
1612         avahi_free(h);
1613         avahi_free(d);
1614         return NULL;
1615     }
1616     
1617     *ret = *c;
1618     ret->host_name = h;
1619     ret->domain_name = d;
1620     ret->browse_domains = l;
1621
1622     return ret;
1623 }
1624
1625 int avahi_server_errno(AvahiServer *s) {
1626     assert(s);
1627     
1628     return s->error;
1629 }
1630
1631 /* Just for internal use */
1632 int avahi_server_set_errno(AvahiServer *s, int error) {
1633     assert(s);
1634
1635     return s->error = error;
1636 }
1637
1638 uint32_t avahi_server_get_local_service_cookie(AvahiServer *s) {
1639     assert(s);
1640
1641     return s->local_service_cookie;
1642 }
1643
1644 static AvahiEntry *find_entry(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiKey *key) {
1645     AvahiEntry *e;
1646     
1647     assert(s);
1648     assert(key);
1649
1650     for (e = avahi_hashmap_lookup(s->entries_by_key, key); e; e = e->by_key_next)
1651
1652         if ((e->interface == interface || e->interface <= 0 || interface <= 0) &&
1653             (e->protocol == protocol || e->protocol == AVAHI_PROTO_UNSPEC || protocol == AVAHI_PROTO_UNSPEC) &&
1654             (!e->group || e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED || e->group->state == AVAHI_ENTRY_GROUP_REGISTERING))
1655
1656             return e;
1657
1658     return NULL;
1659 }
1660
1661 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) {
1662     AvahiKey *key = NULL;
1663     AvahiEntry *e;
1664     int ret;
1665     char n[AVAHI_DOMAIN_NAME_MAX];
1666     
1667     assert(s);
1668     assert(name);
1669     assert(type);
1670     assert(ret_group);
1671
1672     AVAHI_CHECK_VALIDITY(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE);
1673     AVAHI_CHECK_VALIDITY(s, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL);
1674     AVAHI_CHECK_VALIDITY(s, avahi_is_valid_service_name(name), AVAHI_ERR_INVALID_SERVICE_NAME);
1675     AVAHI_CHECK_VALIDITY(s, avahi_is_valid_service_type_strict(type), AVAHI_ERR_INVALID_SERVICE_TYPE);
1676     AVAHI_CHECK_VALIDITY(s, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME);
1677
1678     if ((ret = avahi_service_name_join(n, sizeof(n), name, type, domain) < 0))
1679         return avahi_server_set_errno(s, ret);
1680         
1681     if (!(key = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV)))
1682         return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1683
1684     e = find_entry(s, interface, protocol, key);
1685     avahi_key_unref(key);
1686
1687     if (e) {
1688         *ret_group = e->group;
1689         return AVAHI_OK;
1690     }
1691
1692     return avahi_server_set_errno(s, AVAHI_ERR_NOT_FOUND);
1693 }
1694
1695 int avahi_server_is_service_local(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, const char *name) {
1696     AvahiKey *key = NULL;
1697     AvahiEntry *e;
1698
1699     assert(s);
1700     assert(name);
1701
1702     if (!s->host_name_fqdn)
1703         return 0;
1704     
1705     if (!(key = avahi_key_new(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV)))
1706         return 0;
1707
1708     e = find_entry(s, interface, protocol, key);
1709     avahi_key_unref(key);
1710
1711     if (!e)
1712         return 0;
1713     
1714     return avahi_domain_equal(s->host_name_fqdn, e->record->data.srv.name);
1715 }
1716
1717 int avahi_server_is_record_local(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiRecord *record) {
1718     AvahiEntry *e;
1719
1720     assert(s);
1721     assert(record);
1722     
1723     for (e = avahi_hashmap_lookup(s->entries_by_key, record->key); e; e = e->by_key_next)
1724
1725         if ((e->interface == interface || e->interface <= 0 || interface <= 0) &&
1726             (e->protocol == protocol || e->protocol == AVAHI_PROTO_UNSPEC || protocol == AVAHI_PROTO_UNSPEC) &&
1727             (!e->group || e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED || e->group->state == AVAHI_ENTRY_GROUP_REGISTERING) &&
1728             avahi_record_equal_no_ttl(record, e->record))
1729             return 1;
1730
1731     return 0;
1732 }
1733
1734 /** Set the wide area DNS servers */
1735 int avahi_server_set_wide_area_servers(AvahiServer *s, const AvahiAddress *a, unsigned n) {
1736     assert(s);
1737
1738     if (!s->wide_area_lookup_engine)
1739         return avahi_server_set_errno(s, AVAHI_ERR_INVALID_CONFIG);
1740
1741     avahi_wide_area_set_servers(s->wide_area_lookup_engine, a, n);
1742     return AVAHI_OK;
1743 }
1744
1745 const AvahiServerConfig* avahi_server_get_config(AvahiServer *s) {
1746     assert(s);
1747
1748     return &s->config;
1749 }
1750
1751 /** Set the browsing domains */
1752 int avahi_server_set_browse_domains(AvahiServer *s, AvahiStringList *domains) {
1753     AvahiStringList *l;
1754     
1755     assert(s);
1756
1757     for (l = s->config.browse_domains; l; l = l->next)
1758         if (!avahi_is_valid_domain_name((char*) l->text))
1759             return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
1760     
1761     avahi_string_list_free(s->config.browse_domains);
1762     s->config.browse_domains = avahi_string_list_copy(domains);
1763
1764     return AVAHI_OK;
1765 }