]> git.meshlink.io Git - catta/blob - avahi-core/server.c
* fix avahi when connected to the same network via two NICs
[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/socket.h>
27 #include <arpa/inet.h>
28 #include <string.h>
29 #include <sys/utsname.h>
30 #include <unistd.h>
31 #include <errno.h>
32
33 #include "server.h"
34 #include "util.h"
35 #include "iface.h"
36 #include "socket.h"
37 #include "browse.h"
38
39 #define AVAHI_HOST_RR_HOLDOFF_MSEC 2000
40
41 static void free_entry(AvahiServer*s, AvahiEntry *e) {
42     AvahiEntry *t;
43
44     g_assert(s);
45     g_assert(e);
46
47     avahi_goodbye_entry(s, e, TRUE);
48
49     /* Remove from linked list */
50     AVAHI_LLIST_REMOVE(AvahiEntry, entries, s->entries, e);
51
52     /* Remove from hash table indexed by name */
53     t = g_hash_table_lookup(s->entries_by_key, e->record->key);
54     AVAHI_LLIST_REMOVE(AvahiEntry, by_key, t, e);
55     if (t)
56         g_hash_table_replace(s->entries_by_key, t->record->key, t);
57     else
58         g_hash_table_remove(s->entries_by_key, e->record->key);
59
60     /* Remove from associated group */
61     if (e->group)
62         AVAHI_LLIST_REMOVE(AvahiEntry, by_group, e->group->entries, e);
63
64     avahi_record_unref(e->record);
65     g_free(e);
66 }
67
68 static void free_group(AvahiServer *s, AvahiEntryGroup *g) {
69     g_assert(s);
70     g_assert(g);
71
72     while (g->entries)
73         free_entry(s, g->entries);
74
75     AVAHI_LLIST_REMOVE(AvahiEntryGroup, groups, s->groups, g);
76     g_free(g);
77 }
78
79 static void cleanup_dead(AvahiServer *s) {
80     AvahiEntryGroup *g, *ng;
81     AvahiEntry *e, *ne;
82     g_assert(s);
83
84
85     if (s->need_group_cleanup) {
86         for (g = s->groups; g; g = ng) {
87             ng = g->groups_next;
88             
89             if (g->dead)
90                 free_group(s, g);
91         }
92
93         s->need_group_cleanup = FALSE;
94     }
95
96     if (s->need_entry_cleanup) {
97         for (e = s->entries; e; e = ne) {
98             ne = e->entries_next;
99             
100             if (e->dead)
101                 free_entry(s, e);
102         }
103
104         s->need_entry_cleanup = FALSE;
105     }
106
107     if (s->need_browser_cleanup)
108         avahi_browser_cleanup(s);
109 }
110
111 static void enum_aux_records(AvahiServer *s, AvahiInterface *i, const gchar *name, guint16 type, void (*callback)(AvahiServer *s, AvahiRecord *r, gboolean flush_cache, gpointer userdata), gpointer userdata) {
112     AvahiKey *k;
113     AvahiEntry *e;
114
115     g_assert(s);
116     g_assert(i);
117     g_assert(name);
118     g_assert(callback);
119
120     g_assert(type != AVAHI_DNS_TYPE_ANY);
121
122     k = avahi_key_new(name, AVAHI_DNS_CLASS_IN, type);
123
124     for (e = g_hash_table_lookup(s->entries_by_key, k); e; e = e->by_key_next)
125         if (!e->dead && avahi_entry_registered(s, e, i)) 
126             callback(s, e->record, e->flags & AVAHI_ENTRY_UNIQUE, userdata);
127
128     avahi_key_unref(k);
129 }
130
131 void avahi_server_enumerate_aux_records(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, void (*callback)(AvahiServer *s, AvahiRecord *r, gboolean flush_cache, gpointer userdata), gpointer userdata) {
132     g_assert(s);
133     g_assert(i);
134     g_assert(r);
135     g_assert(callback);
136     
137     if (r->key->class == AVAHI_DNS_CLASS_IN) {
138         if (r->key->type == AVAHI_DNS_TYPE_PTR) {
139             enum_aux_records(s, i, r->data.ptr.name, AVAHI_DNS_TYPE_SRV, callback, userdata);
140             enum_aux_records(s, i, r->data.ptr.name, AVAHI_DNS_TYPE_TXT, callback, userdata);
141         } else if (r->key->type == AVAHI_DNS_TYPE_SRV) {
142             enum_aux_records(s, i, r->data.srv.name, AVAHI_DNS_TYPE_A, callback, userdata);
143             enum_aux_records(s, i, r->data.srv.name, AVAHI_DNS_TYPE_AAAA, callback, userdata);
144         }
145     }
146 }
147
148 void avahi_server_prepare_response(AvahiServer *s, AvahiInterface *i, AvahiEntry *e, gboolean unicast_response, gboolean auxiliary) {
149     g_assert(s);
150     g_assert(i);
151     g_assert(e);
152
153     avahi_record_list_push(s->record_list, e->record, e->flags & AVAHI_ENTRY_UNIQUE, unicast_response, auxiliary);
154 }
155
156 void avahi_server_prepare_matching_responses(AvahiServer *s, AvahiInterface *i, AvahiKey *k, gboolean unicast_response) {
157     AvahiEntry *e;
158 /*     gchar *txt; */
159     
160     g_assert(s);
161     g_assert(i);
162     g_assert(k);
163
164 /*     g_message("Posting responses matching [%s]", txt = avahi_key_to_string(k)); */
165 /*     g_free(txt); */
166
167     if (avahi_key_is_pattern(k)) {
168
169         /* Handle ANY query */
170         
171         for (e = s->entries; e; e = e->entries_next)
172             if (!e->dead && avahi_key_pattern_match(k, e->record->key) && avahi_entry_registered(s, e, i))
173                 avahi_server_prepare_response(s, i, e, unicast_response, FALSE);
174
175     } else {
176
177         /* Handle all other queries */
178         
179         for (e = g_hash_table_lookup(s->entries_by_key, k); e; e = e->by_key_next)
180             if (!e->dead && avahi_entry_registered(s, e, i))
181                 avahi_server_prepare_response(s, i, e, unicast_response, FALSE);
182     }
183 }
184
185 static void withdraw_entry(AvahiServer *s, AvahiEntry *e) {
186     g_assert(s);
187     g_assert(e);
188     
189     if (e->group) {
190         AvahiEntry *k;
191         
192         for (k = e->group->entries; k; k = k->by_group_next) {
193             avahi_goodbye_entry(s, k, FALSE);
194             k->dead = TRUE;
195         }
196         
197         avahi_entry_group_change_state(e->group, AVAHI_ENTRY_GROUP_COLLISION);
198     } else {
199         avahi_goodbye_entry(s, e, FALSE);
200         e->dead = TRUE;
201     }
202
203     s->need_entry_cleanup = TRUE;
204 }
205
206 static void withdraw_rrset(AvahiServer *s, AvahiKey *key) {
207     AvahiEntry *e;
208     
209     g_assert(s);
210     g_assert(key);
211
212    for (e = g_hash_table_lookup(s->entries_by_key, key); e; e = e->by_key_next)
213         withdraw_entry(s, e);
214 }
215
216 static void incoming_probe(AvahiServer *s, AvahiRecord *record, AvahiInterface *i) {
217     AvahiEntry *e, *n;
218     gchar *t;
219     gboolean ours = FALSE, won = FALSE, lost = FALSE;
220     
221     g_assert(s);
222     g_assert(record);
223     g_assert(i);
224
225     for (e = g_hash_table_lookup(s->entries_by_key, record->key); e; e = n) {
226         gint cmp;
227         n = e->by_key_next;
228
229         if (e->dead)
230             continue;
231         
232         if ((cmp = avahi_record_lexicographical_compare(e->record, record)) == 0) {
233             ours = TRUE;
234             break;
235         } else {
236             
237             if (avahi_entry_probing(s, e, i)) {
238                 if (cmp > 0)
239                     won = TRUE;
240                 else /* cmp < 0 */
241                     lost = TRUE;
242             }
243         }
244     }
245
246     t = avahi_record_to_string(record);
247
248     if (!ours) {
249
250         if (won)
251             g_message("xxx Recieved conflicting probe [%s]. Local host won.", t);
252         else if (lost) {
253             g_message("yyy Recieved conflicting probe [%s]. Local host lost. Withdrawing.", t);
254             withdraw_rrset(s, record->key);
255         }
256     }
257
258     g_free(t);
259 }
260
261 static gboolean handle_conflict(AvahiServer *s, AvahiInterface *i, AvahiRecord *record, gboolean unique, const AvahiAddress *a) {
262     gboolean valid = TRUE, ours = FALSE, conflict = FALSE, withdraw_immediately = FALSE;
263     AvahiEntry *e, *n, *conflicting_entry = NULL;
264     
265     g_assert(s);
266     g_assert(i);
267     g_assert(record);
268
269
270 /*     g_message("CHECKING FOR CONFLICT: [%s]", t);   */
271
272     for (e = g_hash_table_lookup(s->entries_by_key, record->key); e; e = n) {
273         n = e->by_key_next;
274
275         if (e->dead || (!(e->flags & AVAHI_ENTRY_UNIQUE) && !unique))
276             continue;
277
278         /* Either our entry or the other is intended to be unique, so let's check */
279         
280         if (avahi_record_equal_no_ttl(e->record, record)) {
281             ours = TRUE; /* We have an identical record, so this is no conflict */
282             
283             /* Check wheter there is a TTL conflict */
284             if (record->ttl <= e->record->ttl/2 &&
285                 avahi_entry_registered(s, e, i)) {
286                 gchar *t;
287                 /* Refresh */
288                 t = avahi_record_to_string(record); 
289                 
290                 g_message("Recieved record with bad TTL [%s]. Refreshing.", t);
291                 avahi_server_prepare_matching_responses(s, i, e->record->key, FALSE);
292                 valid = FALSE;
293                 
294                 g_free(t);
295             }
296                 
297             /* There's no need to check the other entries of this RRset */
298             break;
299
300         } else {
301             
302             if (avahi_entry_registered(s, e, i)) {
303                 
304                 /* A conflict => we have to return to probe mode */
305                 conflict = TRUE;
306                 conflicting_entry = e;
307
308             } else if (avahi_entry_probing(s, e, i)) {
309
310                 /* We are currently registering a matching record, but
311                  * someone else already claimed it, so let's
312                  * withdraw */
313                 conflict = TRUE;
314                 withdraw_immediately = TRUE;
315             }
316         }
317     }
318
319 /*     g_message("ours=%i conflict=%i", ours, conflict); */
320
321     if (!ours && conflict) {
322         gchar *t;
323  
324         valid = FALSE;
325
326         t = avahi_record_to_string(record); 
327  
328         if (withdraw_immediately) {
329             g_message("Recieved conflicting record [%s] with local record to be. Withdrawing.", t);
330             withdraw_rrset(s, record->key);
331         } else {
332             g_assert(conflicting_entry);
333             g_message("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 probin
337              * state. Local shared records are reannounced. */
338         }
339
340         g_free(t);
341     }
342
343     return valid;
344 }
345
346 static void append_aux_callback(AvahiServer *s, AvahiRecord *r, gboolean flush_cache, gpointer userdata) {
347     gboolean *unicast_response = userdata;
348
349     g_assert(s);
350     g_assert(r);
351     g_assert(unicast_response);
352     
353     avahi_record_list_push(s->record_list, r, flush_cache, *unicast_response, TRUE);
354 }
355
356 static void append_aux_records_to_list(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, gboolean unicast_response) {
357     g_assert(s);
358     g_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, guint16 port, gboolean legacy_unicast) {
364
365     g_assert(s);
366     g_assert(i);
367     g_assert(!legacy_unicast || (a && port > 0 && p));
368
369     if (legacy_unicast) {
370         AvahiDnsPacket *reply;
371         AvahiRecord *r;
372
373         reply = avahi_dns_packet_new_reply(p, 512 /* unicast DNS maximum packet size is 512 */ , TRUE, TRUE);
374         
375         while ((r = avahi_record_list_next(s->record_list, NULL, NULL, NULL))) {
376
377             append_aux_records_to_list(s, i, r, FALSE);
378             
379             if (avahi_dns_packet_append_record(reply, r, FALSE, 10))
380                 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
381             else {
382                 gchar *t = avahi_record_to_string(r);
383                 g_warning("Record [%s] not fitting in legacy unicast packet, dropping.", t);
384                 g_free(t);
385             }
386
387             avahi_record_unref(r);
388         }
389
390         if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) != 0)
391             avahi_interface_send_packet_unicast(i, reply, a, port);
392
393         avahi_dns_packet_free(reply);
394
395     } else {
396         gboolean unicast_response, flush_cache, auxiliary;
397         AvahiDnsPacket *reply = NULL;
398         AvahiRecord *r;
399
400         /* In case the query packet was truncated never respond
401         immediately, because known answer suppression records might be
402         contained in later packets */
403         gboolean tc = p && !!(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_TC);
404         
405         while ((r = avahi_record_list_next(s->record_list, &flush_cache, &unicast_response, &auxiliary))) {
406                         
407             if (!avahi_interface_post_response(i, r, flush_cache, a, !tc && flush_cache && !auxiliary) && unicast_response) {
408
409                 append_aux_records_to_list(s, i, r, unicast_response);
410                 
411                 /* Due to some reasons the record has not been scheduled.
412                  * The client requested an unicast response in that
413                  * case. Therefore we prepare such a response */
414
415                 for (;;) {
416                 
417                     if (!reply) {
418                         g_assert(p);
419                         reply = avahi_dns_packet_new_reply(p, i->hardware->mtu, FALSE, FALSE);
420                     }
421                 
422                     if (avahi_dns_packet_append_record(reply, r, flush_cache, 0)) {
423
424                         /* Appending this record succeeded, so incremeant
425                          * the specific header field, and return to the caller */
426                         
427                         avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
428
429                         break;
430                     }
431
432                     if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) == 0) {
433                         guint size;
434
435                         /* The record is too large for one packet, so create a larger packet */
436
437                         avahi_dns_packet_free(reply);
438                         size = avahi_record_get_estimate_size(r) + AVAHI_DNS_PACKET_HEADER_SIZE;
439                         if (size > AVAHI_DNS_PACKET_MAX_SIZE)
440                             size = AVAHI_DNS_PACKET_MAX_SIZE;
441                         reply = avahi_dns_packet_new_reply(p, size, FALSE, TRUE);
442
443                         if (!avahi_dns_packet_append_record(reply, r, flush_cache, 0)) {
444                             avahi_dns_packet_free(reply);
445                             
446                             gchar *t = avahi_record_to_string(r);
447                             g_warning("Record [%s] too large, doesn't fit in any packet!", t);
448                             g_free(t);
449                             break;
450                         } else
451                             avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
452                     }
453
454                     /* Appending the record didn't succeeed, so let's send this packet, and create a new one */
455                     avahi_interface_send_packet_unicast(i, reply, a, port);
456                     avahi_dns_packet_free(reply);
457                     reply = NULL;
458                 }
459             }
460
461             avahi_record_unref(r);
462         }
463
464         if (reply) {
465             if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) != 0) 
466                 avahi_interface_send_packet_unicast(i, reply, a, port);
467             avahi_dns_packet_free(reply);
468         }
469     }
470
471     avahi_record_list_flush(s->record_list);
472 }
473
474
475 static void reflect_response(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, gboolean flush_cache) {
476     AvahiInterface *j;
477     
478     g_assert(s);
479     g_assert(i);
480     g_assert(r);
481
482     if (!s->config.enable_reflector)
483         return;
484
485     for (j = s->monitor->interfaces; j; j = j->interface_next)
486         if (j != i && (s->config.ipv_reflect || j->protocol == i->protocol))
487             avahi_interface_post_response(j, r, flush_cache, NULL, TRUE);
488 }
489
490 static gpointer reflect_cache_walk_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, gpointer userdata) {
491     AvahiServer *s = userdata;
492
493     g_assert(c);
494     g_assert(pattern);
495     g_assert(e);
496     g_assert(s);
497
498     avahi_record_list_push(s->record_list, e->record, e->cache_flush, FALSE, FALSE);
499     return NULL;
500 }
501
502 static void reflect_query(AvahiServer *s, AvahiInterface *i, AvahiKey *k) {
503     AvahiInterface *j;
504     
505     g_assert(s);
506     g_assert(i);
507     g_assert(k);
508
509     if (!s->config.enable_reflector)
510         return;
511
512     for (j = s->monitor->interfaces; j; j = j->interface_next)
513         if (j != i && (s->config.ipv_reflect || j->protocol == i->protocol)) {
514             /* Post the query to other networks */
515             avahi_interface_post_query(j, k, TRUE);
516
517             /* Reply from caches of other network. This is needed to
518              * "work around" known answer suppression. */
519
520             avahi_cache_walk(j->cache, k, reflect_cache_walk_callback, s);
521         }
522 }
523
524 static void reflect_probe(AvahiServer *s, AvahiInterface *i, AvahiRecord *r) {
525     AvahiInterface *j;
526     
527     g_assert(s);
528     g_assert(i);
529     g_assert(r);
530
531     if (!s->config.enable_reflector)
532         return;
533
534     for (j = s->monitor->interfaces; j; j = j->interface_next)
535         if (j != i && (s->config.ipv_reflect || j->protocol == i->protocol))
536             avahi_interface_post_probe(j, r, TRUE);
537 }
538
539 static void handle_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, guint16 port, gboolean legacy_unicast) {
540     guint n;
541     
542     g_assert(s);
543     g_assert(p);
544     g_assert(i);
545     g_assert(a);
546
547 /*     g_message("query"); */
548
549     g_assert(avahi_record_list_empty(s->record_list));
550     
551     /* Handle the questions */
552     for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT); n > 0; n --) {
553         AvahiKey *key;
554         gboolean unicast_response = FALSE;
555
556         if (!(key = avahi_dns_packet_consume_key(p, &unicast_response))) {
557             g_warning("Packet too short (1)");
558             goto fail;
559         }
560
561         if (!legacy_unicast)
562             reflect_query(s, i, key);
563         avahi_query_scheduler_incoming(i->query_scheduler, key);
564         avahi_server_prepare_matching_responses(s, i, key, unicast_response);
565         avahi_key_unref(key);
566     }
567
568     /* Known Answer Suppression */
569     for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT); n > 0; n --) {
570         AvahiRecord *record;
571         gboolean unique = FALSE;
572
573         if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
574             g_warning("Packet too short (2)");
575             goto fail;
576         }
577
578         if (handle_conflict(s, i, record, unique, a)) {
579             avahi_response_scheduler_suppress(i->response_scheduler, record, a);
580             avahi_record_list_drop(s->record_list, record);
581         }
582         
583         avahi_record_unref(record);
584     }
585
586     /* Probe record */
587     for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT); n > 0; n --) {
588         AvahiRecord *record;
589         gboolean unique = FALSE;
590
591         if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
592             g_warning("Packet too short (3)");
593             goto fail;
594         }
595
596         if (record->key->type != AVAHI_DNS_TYPE_ANY) {
597             reflect_probe(s, i, record);
598             incoming_probe(s, record, i);
599         }
600         
601         avahi_record_unref(record);
602     }
603
604     if (!avahi_record_list_empty(s->record_list))
605         avahi_server_generate_response(s, i, p, a, port, legacy_unicast);
606
607     return;
608     
609 fail:
610     avahi_record_list_flush(s->record_list);
611
612 }
613
614 static void handle_response_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a) {
615     guint n;
616     
617     g_assert(s);
618     g_assert(p);
619     g_assert(i);
620     g_assert(a);
621
622 /*     g_message("response"); */
623     
624     for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) +
625              avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT); n > 0; n--) {
626         AvahiRecord *record;
627         gboolean cache_flush = FALSE;
628 /*         gchar *txt; */
629         
630         if (!(record = avahi_dns_packet_consume_record(p, &cache_flush))) {
631             g_warning("Packet too short (4)");
632             break;
633         }
634
635         if (record->key->type != AVAHI_DNS_TYPE_ANY) {
636
637 /*             g_message("Handling response: %s", txt = avahi_record_to_string(record)); */
638 /*             g_free(txt); */
639             
640             if (handle_conflict(s, i, record, cache_flush, a)) {
641                 reflect_response(s, i, record, cache_flush);
642                 avahi_cache_update(i->cache, record, cache_flush, a);
643                 avahi_response_scheduler_incoming(i->response_scheduler, record, cache_flush);
644             }
645         }
646             
647         avahi_record_unref(record);
648     }
649 }
650
651 static AvahiLegacyUnicastReflectSlot* allocate_slot(AvahiServer *s) {
652     guint n, index = (guint) -1;
653     AvahiLegacyUnicastReflectSlot *slot;
654     
655     g_assert(s);
656
657     if (!s->legacy_unicast_reflect_slots)
658         s->legacy_unicast_reflect_slots = g_new0(AvahiLegacyUnicastReflectSlot*, AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS);
659
660     for (n = 0; n < AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS; n++, s->legacy_unicast_reflect_id++) {
661         index = s->legacy_unicast_reflect_id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
662         
663         if (!s->legacy_unicast_reflect_slots[index])
664             break;
665     }
666
667     if (index == (guint) -1 || s->legacy_unicast_reflect_slots[index])
668         return NULL;
669
670     slot = s->legacy_unicast_reflect_slots[index] = g_new(AvahiLegacyUnicastReflectSlot, 1);
671     slot->id = s->legacy_unicast_reflect_id++;
672     slot->server = s;
673     return slot;
674 }
675
676 static void deallocate_slot(AvahiServer *s, AvahiLegacyUnicastReflectSlot *slot) {
677     guint index;
678
679     g_assert(s);
680     g_assert(slot);
681
682     index = slot->id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
683
684     g_assert(s->legacy_unicast_reflect_slots[index] == slot);
685
686     avahi_time_event_queue_remove(s->time_event_queue, slot->time_event);
687     
688     g_free(slot);
689     s->legacy_unicast_reflect_slots[index] = NULL;
690 }
691
692 static void free_slots(AvahiServer *s) {
693     guint index;
694     g_assert(s);
695
696     if (!s->legacy_unicast_reflect_slots)
697         return;
698
699     for (index = 0; index < AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS; index ++)
700         if (s->legacy_unicast_reflect_slots[index])
701             deallocate_slot(s, s->legacy_unicast_reflect_slots[index]);
702
703     g_free(s->legacy_unicast_reflect_slots);
704     s->legacy_unicast_reflect_slots = NULL;
705 }
706
707 static AvahiLegacyUnicastReflectSlot* find_slot(AvahiServer *s, guint16 id) {
708     guint index;
709     
710     g_assert(s);
711
712     if (!s->legacy_unicast_reflect_slots)
713         return NULL;
714     
715     index = id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
716
717     if (!s->legacy_unicast_reflect_slots[index] || s->legacy_unicast_reflect_slots[index]->id != id)
718         return NULL;
719
720     return s->legacy_unicast_reflect_slots[index];
721 }
722
723 static void legacy_unicast_reflect_slot_timeout(AvahiTimeEvent *e, void *userdata) {
724     AvahiLegacyUnicastReflectSlot *slot = userdata;
725
726     g_assert(e);
727     g_assert(slot);
728     g_assert(slot->time_event == e);
729
730     deallocate_slot(slot->server, slot);
731 }
732
733 static void reflect_legacy_unicast_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, guint16 port) {
734     AvahiLegacyUnicastReflectSlot *slot;
735     AvahiInterface *j;
736
737     g_assert(s);
738     g_assert(p);
739     g_assert(i);
740     g_assert(a);
741     g_assert(port > 0);
742     g_assert(i->protocol == a->family);
743     
744     if (!s->config.enable_reflector)
745         return;
746
747 /*     g_message("legacy unicast reflectr"); */
748     
749     /* Reflecting legacy unicast queries is a little more complicated
750        than reflecting normal queries, since we must route the
751        responses back to the right client. Therefore we must store
752        some information for finding the right client contact data for
753        response packets. In contrast to normal queries legacy
754        unicast query and response packets are reflected untouched and
755        are not reassembled into larger packets */
756
757     if (!(slot = allocate_slot(s))) {
758         /* No slot available, we drop this legacy unicast query */
759         g_warning("No slot available for legacy unicast reflection, dropping query packet.");
760         return;
761     }
762
763     slot->original_id = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID);
764     slot->address = *a;
765     slot->port = port;
766     slot->interface = i->hardware->index;
767
768     avahi_elapse_time(&slot->elapse_time, 2000, 0);
769     slot->time_event = avahi_time_event_queue_add(s->time_event_queue, &slot->elapse_time, legacy_unicast_reflect_slot_timeout, slot);
770
771     /* Patch the packet with our new locally generatedt id */
772     avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
773     
774     for (j = s->monitor->interfaces; j; j = j->interface_next)
775         if (avahi_interface_relevant(j) &&
776             j != i &&
777             (s->config.ipv_reflect || j->protocol == i->protocol)) {
778
779             if (j->protocol == AF_INET && s->fd_legacy_unicast_ipv4 >= 0) {
780                 avahi_send_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, j->hardware->index, p, NULL, 0);
781                 } else if (j->protocol == AF_INET6 && s->fd_legacy_unicast_ipv6 >= 0)
782                 avahi_send_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, j->hardware->index, p, NULL, 0);
783         }
784
785     /* Reset the id */
786     avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
787 }
788
789 static gboolean originates_from_local_legacy_unicast_socket(AvahiServer *s, const struct sockaddr *sa) {
790     AvahiAddress a;
791     g_assert(s);
792     g_assert(sa);
793
794     if (!s->config.enable_reflector)
795         return FALSE;
796     
797     avahi_address_from_sockaddr(sa, &a);
798
799     if (!avahi_address_is_local(s->monitor, &a))
800         return FALSE;
801     
802     if (sa->sa_family == AF_INET && s->fd_legacy_unicast_ipv4 >= 0) {
803         struct sockaddr_in lsa;
804         socklen_t l = sizeof(lsa);
805         
806         if (getsockname(s->fd_legacy_unicast_ipv4, &lsa, &l) != 0)
807             g_warning("getsockname(): %s", strerror(errno));
808         else
809             return lsa.sin_port == ((struct sockaddr_in*) sa)->sin_port;
810
811     }
812
813     if (sa->sa_family == AF_INET6 && s->fd_legacy_unicast_ipv6 >= 0) {
814         struct sockaddr_in6 lsa;
815         socklen_t l = sizeof(lsa);
816
817         if (getsockname(s->fd_legacy_unicast_ipv6, &lsa, &l) != 0)
818             g_warning("getsockname(): %s", strerror(errno));
819         else
820             return lsa.sin6_port == ((struct sockaddr_in6*) sa)->sin6_port;
821     }
822
823     return FALSE;
824 }
825
826 static void dispatch_packet(AvahiServer *s, AvahiDnsPacket *p, const struct sockaddr *sa, gint iface, gint ttl) {
827     AvahiInterface *i;
828     AvahiAddress a;
829     guint16 port;
830     
831     g_assert(s);
832     g_assert(p);
833     g_assert(sa);
834     g_assert(iface > 0);
835
836     if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, sa->sa_family)) ||
837         !avahi_interface_relevant(i)) {
838         g_warning("Recieved packet from invalid interface.");
839         return;
840     }
841
842 /*     g_message("new packet recieved on interface '%s.%i'.", i->hardware->name, i->protocol); */
843
844     port = avahi_port_from_sockaddr(sa);
845     avahi_address_from_sockaddr(sa, &a);
846     
847     if (avahi_address_is_ipv4_in_ipv6(&a))
848         /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
849         return;
850
851     if (originates_from_local_legacy_unicast_socket(s, sa))
852         /* This originates from our local reflector, so let's ignore it */
853         return;
854
855     if (avahi_dns_packet_check_valid(p) < 0) {
856         g_warning("Recieved invalid packet.");
857         return;
858     }
859
860     if (avahi_dns_packet_is_query(p)) {
861         gboolean legacy_unicast = FALSE;
862
863         if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT) != 0) {
864             g_warning("Invalid query packet.");
865             return;
866         }
867
868         if (port != AVAHI_MDNS_PORT) {
869             /* Legacy Unicast */
870
871             if ((avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) != 0 ||
872                  avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0)) {
873                 g_warning("Invalid legacy unicast query packet.");
874                 return;
875             }
876         
877             legacy_unicast = TRUE;
878         }
879
880         if (legacy_unicast)
881             reflect_legacy_unicast_query_packet(s, p, i, &a, port);
882         
883         handle_query_packet(s, p, i, &a, port, legacy_unicast);
884         
885 /*         g_message("Handled query"); */
886     } else {
887
888         if (port != AVAHI_MDNS_PORT) {
889             g_warning("Recieved repsonse with invalid source port %u on interface '%s.%i'", port, i->hardware->name, i->protocol);
890             return;
891         }
892
893         if (ttl != 255) {
894             g_warning("Recieved response with invalid TTL %u on interface '%s.%i'.", ttl, i->hardware->name, i->protocol);
895             if (s->config.check_response_ttl)
896                 return;
897         }
898
899         if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT) != 0 ||
900             avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 ||
901             avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0) {
902             g_warning("Invalid response packet.");
903             return;
904         }
905
906         handle_response_packet(s, p, i, &a);
907 /*         g_message("Handled response"); */
908     }
909 }
910
911 static void dispatch_legacy_unicast_packet(AvahiServer *s, AvahiDnsPacket *p, const struct sockaddr *sa, gint iface, gint ttl) {
912     AvahiInterface *i, *j;
913     AvahiAddress a;
914     guint16 port;
915     AvahiLegacyUnicastReflectSlot *slot;
916     
917     g_assert(s);
918     g_assert(p);
919     g_assert(sa);
920     g_assert(iface > 0);
921
922     if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, sa->sa_family)) ||
923         !avahi_interface_relevant(i)) {
924         g_warning("Recieved packet from invalid interface.");
925         return;
926     }
927
928 /*     g_message("new legacy unicast packet recieved on interface '%s.%i'.", i->hardware->name, i->protocol); */
929
930     port = avahi_port_from_sockaddr(sa);
931     avahi_address_from_sockaddr(sa, &a);
932     
933     if (avahi_address_is_ipv4_in_ipv6(&a))
934         /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
935         return;
936
937     if (avahi_dns_packet_check_valid(p) < 0 || avahi_dns_packet_is_query(p)) {
938         g_warning("Recieved invalid packet.");
939         return;
940     }
941
942     if (!(slot = find_slot(s, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID)))) {
943         g_warning("Recieved legacy unicast response with unknown id");
944         return;
945     }
946
947     if (!(j = avahi_interface_monitor_get_interface(s->monitor, slot->interface, slot->address.family)) ||
948         !avahi_interface_relevant(j))
949         return;
950
951     /* Patch the original ID into this response */
952     avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
953
954     /* Forward the response to the correct client */
955     avahi_interface_send_packet_unicast(j, p, &slot->address, slot->port);
956
957     /* Undo changes to packet */
958     avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
959 }
960
961 static void work(AvahiServer *s) {
962     struct sockaddr_in6 sa6;
963     struct sockaddr_in sa;
964     AvahiDnsPacket *p;
965     gint iface = 0;
966     guint8 ttl;
967         
968     g_assert(s);
969
970     if (s->fd_ipv4 >= 0 && (s->pollfd_ipv4.revents & G_IO_IN)) {
971         if ((p = avahi_recv_dns_packet_ipv4(s->fd_ipv4, &sa, &iface, &ttl))) {
972             dispatch_packet(s, p, (struct sockaddr*) &sa, iface, ttl);
973             avahi_dns_packet_free(p);
974         }
975     }
976
977     if (s->fd_ipv6 >= 0 && (s->pollfd_ipv6.revents & G_IO_IN)) {
978         if ((p = avahi_recv_dns_packet_ipv6(s->fd_ipv6, &sa6, &iface, &ttl))) {
979             dispatch_packet(s, p, (struct sockaddr*) &sa6, iface, ttl);
980             avahi_dns_packet_free(p);
981         }
982     }
983
984     if (s->fd_legacy_unicast_ipv4 >= 0 && (s->pollfd_legacy_unicast_ipv4.revents & G_IO_IN)) {
985         if ((p = avahi_recv_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, &sa, &iface, &ttl))) {
986             dispatch_legacy_unicast_packet(s, p, (struct sockaddr*) &sa, iface, ttl);
987             avahi_dns_packet_free(p);
988         }
989     }
990
991     if (s->fd_legacy_unicast_ipv6 >= 0 && (s->pollfd_legacy_unicast_ipv6.revents & G_IO_IN)) {
992         if ((p = avahi_recv_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, &sa6, &iface, &ttl))) {
993             dispatch_legacy_unicast_packet(s, p, (struct sockaddr*) &sa6, iface, ttl);
994             avahi_dns_packet_free(p);
995         }
996     }
997 }
998
999 static gboolean prepare_func(GSource *source, gint *timeout) {
1000     g_assert(source);
1001     g_assert(timeout);
1002     
1003     *timeout = -1;
1004     return FALSE;
1005 }
1006
1007 static gboolean check_func(GSource *source) {
1008     AvahiServer* s;
1009     gushort revents = 0;
1010     
1011     g_assert(source);
1012
1013     s = *((AvahiServer**) (((guint8*) source) + sizeof(GSource)));
1014     g_assert(s);
1015
1016     if (s->fd_ipv4 >= 0)
1017         revents |= s->pollfd_ipv4.revents;
1018     if (s->fd_ipv6 >= 0)
1019         revents |= s->pollfd_ipv6.revents;
1020     if (s->fd_legacy_unicast_ipv4 >= 0)
1021         revents |= s->pollfd_legacy_unicast_ipv4.revents;
1022     if (s->fd_legacy_unicast_ipv6 >= 0)
1023         revents |= s->pollfd_legacy_unicast_ipv6.revents;
1024     
1025     return !!(revents & (G_IO_IN | G_IO_HUP | G_IO_ERR));
1026 }
1027
1028 static gboolean dispatch_func(GSource *source, GSourceFunc callback, gpointer user_data) {
1029     AvahiServer* s;
1030     g_assert(source);
1031
1032     s = *((AvahiServer**) (((guint8*) source) + sizeof(GSource)));
1033     g_assert(s);
1034
1035     work(s);
1036     cleanup_dead(s);
1037
1038     return TRUE;
1039 }
1040
1041 static void server_set_state(AvahiServer *s, AvahiServerState state) {
1042     g_assert(s);
1043
1044     if (s->state == state)
1045         return;
1046     
1047     s->state = state;
1048
1049     if (s->callback)
1050         s->callback(s, state, s->userdata);
1051 }
1052
1053 static void withdraw_host_rrs(AvahiServer *s) {
1054     g_assert(s);
1055
1056     if (s->hinfo_entry_group) {
1057         avahi_entry_group_free(s->hinfo_entry_group);
1058         s->hinfo_entry_group = NULL;
1059     }
1060
1061     if (s->browse_domain_entry_group) {
1062         avahi_entry_group_free(s->browse_domain_entry_group);
1063         s->browse_domain_entry_group = NULL;
1064     }
1065
1066     avahi_update_host_rrs(s->monitor, TRUE);
1067     s->n_host_rr_pending = 0;
1068 }
1069
1070 void avahi_server_decrease_host_rr_pending(AvahiServer *s) {
1071     g_assert(s);
1072     
1073     g_assert(s->n_host_rr_pending > 0);
1074
1075     if (--s->n_host_rr_pending == 0)
1076         server_set_state(s, AVAHI_SERVER_RUNNING);
1077 }
1078
1079 void avahi_server_increase_host_rr_pending(AvahiServer *s) {
1080     g_assert(s);
1081
1082     s->n_host_rr_pending ++;
1083 }
1084
1085 void avahi_host_rr_entry_group_callback(AvahiServer *s, AvahiEntryGroup *g, AvahiEntryGroupState state, void *userdata) {
1086     g_assert(s);
1087     g_assert(g);
1088
1089     if (state == AVAHI_ENTRY_GROUP_REGISTERING &&
1090         s->state == AVAHI_SERVER_REGISTERING)
1091         avahi_server_increase_host_rr_pending(s);
1092     else if (state == AVAHI_ENTRY_GROUP_COLLISION &&
1093         (s->state == AVAHI_SERVER_REGISTERING || s->state == AVAHI_SERVER_RUNNING)) {
1094         withdraw_host_rrs(s);
1095         server_set_state(s, AVAHI_SERVER_COLLISION);
1096     } else if (state == AVAHI_ENTRY_GROUP_ESTABLISHED &&
1097                s->state == AVAHI_SERVER_REGISTERING)
1098         avahi_server_decrease_host_rr_pending(s);
1099 }
1100
1101 static void register_hinfo(AvahiServer *s) {
1102     struct utsname utsname;
1103     AvahiRecord *r;
1104     
1105     g_assert(s);
1106     
1107     if (!s->config.register_hinfo || s->hinfo_entry_group)
1108         return;
1109     
1110     s->hinfo_entry_group = avahi_entry_group_new(s, avahi_host_rr_entry_group_callback, NULL);
1111     
1112     /* Fill in HINFO rr */
1113     r = avahi_record_new_full(s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_HINFO);
1114     uname(&utsname);
1115     r->data.hinfo.cpu = g_strdup(g_strup(utsname.machine));
1116     r->data.hinfo.os = g_strdup(g_strup(utsname.sysname));
1117     avahi_server_add(s, s->hinfo_entry_group, 0, AF_UNSPEC, AVAHI_ENTRY_UNIQUE, r);
1118     avahi_record_unref(r);
1119
1120     avahi_entry_group_commit(s->hinfo_entry_group);
1121 }
1122
1123 static void register_localhost(AvahiServer *s) {
1124     AvahiAddress a;
1125     g_assert(s);
1126     
1127     /* Add localhost entries */
1128     avahi_address_parse("127.0.0.1", AF_INET, &a);
1129     avahi_server_add_address(s, NULL, 0, AF_UNSPEC, AVAHI_ENTRY_NOPROBE|AVAHI_ENTRY_NOANNOUNCE, "localhost", &a);
1130
1131     avahi_address_parse("::1", AF_INET6, &a);
1132     avahi_server_add_address(s, NULL, 0, AF_UNSPEC, AVAHI_ENTRY_NOPROBE|AVAHI_ENTRY_NOANNOUNCE, "ip6-localhost", &a);
1133 }
1134
1135 static void register_browse_domain(AvahiServer *s) {
1136     g_assert(s);
1137
1138     if (!s->config.announce_domain || s->browse_domain_entry_group)
1139         return;
1140
1141     s->browse_domain_entry_group = avahi_entry_group_new(s, NULL, NULL);
1142     avahi_server_add_ptr(s, s->browse_domain_entry_group, 0, AF_UNSPEC, 0, "_browse._dns-sd._udp.local", s->domain_name);
1143     avahi_entry_group_commit(s->browse_domain_entry_group);
1144 }
1145
1146 static void register_stuff(AvahiServer *s) {
1147     g_assert(s);
1148
1149     server_set_state(s, AVAHI_SERVER_REGISTERING);
1150     register_hinfo(s);
1151     register_browse_domain(s);
1152     avahi_update_host_rrs(s->monitor, FALSE);
1153
1154     if (s->n_host_rr_pending == 0)
1155         server_set_state(s, AVAHI_SERVER_RUNNING);
1156 }
1157
1158 static void update_fqdn(AvahiServer *s) {
1159     g_assert(s);
1160     
1161     g_assert(s->host_name);
1162     g_assert(s->domain_name);
1163
1164     g_free(s->host_name_fqdn);
1165     s->host_name_fqdn = g_strdup_printf("%s.%s", s->host_name, s->domain_name);
1166 }
1167
1168 static void register_time_event_callback(AvahiTimeEvent *e, gpointer userdata) {
1169     AvahiServer *s = userdata;
1170     
1171     g_assert(e);
1172     g_assert(s);
1173
1174     g_assert(e == s->register_time_event);
1175     avahi_time_event_queue_remove(s->time_event_queue, s->register_time_event);
1176     s->register_time_event = NULL;
1177
1178     if (s->state == AVAHI_SERVER_SLEEPING)
1179         register_stuff(s);
1180 }
1181
1182 static void delayed_register_stuff(AvahiServer *s) {
1183     GTimeVal tv;
1184     
1185     g_assert(s);
1186
1187     avahi_elapse_time(&tv, AVAHI_HOST_RR_HOLDOFF_MSEC, 0);
1188
1189     if (s->register_time_event)
1190         avahi_time_event_queue_update(s->time_event_queue, s->register_time_event, &tv);
1191     else
1192         s->register_time_event = avahi_time_event_queue_add(s->time_event_queue, &tv, register_time_event_callback, s);
1193 }
1194
1195 gint avahi_server_set_host_name(AvahiServer *s, const gchar *host_name) {
1196     g_assert(s);
1197     g_assert(host_name);
1198
1199     server_set_state(s, AVAHI_SERVER_SLEEPING);
1200     withdraw_host_rrs(s);
1201
1202     g_free(s->host_name);
1203     s->host_name = host_name ? avahi_normalize_name(host_name) : avahi_get_host_name();
1204     s->host_name[strcspn(s->host_name, ".")] = 0;
1205     update_fqdn(s);
1206
1207     delayed_register_stuff(s);
1208     return 0;
1209 }
1210
1211 gint avahi_server_set_domain_name(AvahiServer *s, const gchar *domain_name) {
1212     g_assert(s);
1213     g_assert(domain_name);
1214
1215     server_set_state(s, AVAHI_SERVER_SLEEPING);
1216     withdraw_host_rrs(s);
1217
1218     g_free(s->domain_name);
1219     s->domain_name = domain_name ? avahi_normalize_name(domain_name) : g_strdup("local.");
1220     update_fqdn(s);
1221
1222     delayed_register_stuff(s);
1223     return 0;
1224 }
1225
1226
1227 static void prepare_pollfd(AvahiServer *s, GPollFD *pollfd, gint fd) {
1228     g_assert(s);
1229     g_assert(pollfd);
1230     g_assert(fd >= 0);
1231
1232     memset(pollfd, 0, sizeof(GPollFD));
1233     pollfd->fd = fd;
1234     pollfd->events = G_IO_IN|G_IO_ERR|G_IO_HUP;
1235     g_source_add_poll(s->source, pollfd);
1236 }
1237
1238 AvahiServer *avahi_server_new(GMainContext *c, const AvahiServerConfig *sc, AvahiServerCallback callback, gpointer userdata) {
1239     AvahiServer *s;
1240     
1241     static GSourceFuncs source_funcs = {
1242         prepare_func,
1243         check_func,
1244         dispatch_func,
1245         NULL,
1246         NULL,
1247         NULL
1248     };
1249
1250     s = g_new(AvahiServer, 1);
1251     s->n_host_rr_pending = 0;
1252     s->need_entry_cleanup = s->need_group_cleanup = s->need_browser_cleanup = FALSE;
1253
1254     if (sc)
1255         avahi_server_config_copy(&s->config, sc);
1256     else
1257         avahi_server_config_init(&s->config);
1258     
1259     s->fd_ipv4 = s->config.use_ipv4 ? avahi_open_socket_ipv4() : -1;
1260     s->fd_ipv6 = s->config.use_ipv6 ? avahi_open_socket_ipv6() : -1;
1261     
1262     if (s->fd_ipv6 < 0 && s->fd_ipv4 < 0) {
1263         g_critical("Selected neither IPv6 nor IPv4 support, aborting.\n");
1264         avahi_server_config_free(&s->config);
1265         g_free(s);
1266         return NULL;
1267     }
1268
1269     if (s->fd_ipv4 < 0 && s->config.use_ipv4)
1270         g_message("Failed to create IPv4 socket, proceeding in IPv6 only mode");
1271     else if (s->fd_ipv6 < 0 && s->config.use_ipv6)
1272         g_message("Failed to create IPv6 socket, proceeding in IPv4 only mode");
1273
1274     s->fd_legacy_unicast_ipv4 = s->fd_ipv4 >= 0 && s->config.enable_reflector ? avahi_open_legacy_unicast_socket_ipv4() : -1;
1275     s->fd_legacy_unicast_ipv6 = s->fd_ipv6 >= 0 && s->config.enable_reflector ? avahi_open_legacy_unicast_socket_ipv6() : -1;
1276     
1277     if (c)
1278         g_main_context_ref(s->context = c);
1279     else
1280         s->context = g_main_context_default();
1281
1282     /* Prepare IO source registration */
1283     s->source = g_source_new(&source_funcs, sizeof(GSource) + sizeof(AvahiServer*));
1284     *((AvahiServer**) (((guint8*) s->source) + sizeof(GSource))) = s;
1285
1286     if (s->fd_ipv4 >= 0)
1287         prepare_pollfd(s, &s->pollfd_ipv4, s->fd_ipv4);
1288     if (s->fd_ipv6 >= 0)
1289         prepare_pollfd(s, &s->pollfd_ipv6, s->fd_ipv6);
1290     if (s->fd_legacy_unicast_ipv4 >= 0)
1291         prepare_pollfd(s, &s->pollfd_legacy_unicast_ipv4, s->fd_legacy_unicast_ipv4);
1292     if (s->fd_legacy_unicast_ipv6 >= 0)
1293         prepare_pollfd(s, &s->pollfd_legacy_unicast_ipv6, s->fd_legacy_unicast_ipv6);
1294     
1295     g_source_attach(s->source, s->context);
1296     
1297     s->callback = callback;
1298     s->userdata = userdata;
1299     
1300     AVAHI_LLIST_HEAD_INIT(AvahiEntry, s->entries);
1301     s->entries_by_key = g_hash_table_new((GHashFunc) avahi_key_hash, (GEqualFunc) avahi_key_equal);
1302     AVAHI_LLIST_HEAD_INIT(AvahiGroup, s->groups);
1303
1304     AVAHI_LLIST_HEAD_INIT(AvahiRecordBrowser, s->record_browsers);
1305     s->record_browser_hashtable = g_hash_table_new((GHashFunc) avahi_key_hash, (GEqualFunc) avahi_key_equal);
1306     AVAHI_LLIST_HEAD_INIT(AvahiHostNameResolver, s->host_name_resolvers);
1307     AVAHI_LLIST_HEAD_INIT(AvahiAddressResolver, s->address_resolvers);
1308     AVAHI_LLIST_HEAD_INIT(AvahiDomainBrowser, s->domain_browsers);
1309     AVAHI_LLIST_HEAD_INIT(AvahiServiceTypeBrowser, s->service_type_browsers);
1310     AVAHI_LLIST_HEAD_INIT(AvahiServiceBrowser, s->service_browsers);
1311     AVAHI_LLIST_HEAD_INIT(AvahiServiceResolver, s->service_resolvers);
1312
1313     s->legacy_unicast_reflect_slots = NULL;
1314     s->legacy_unicast_reflect_id = 0;
1315     
1316     /* Get host name */
1317     s->host_name = s->config.host_name ? avahi_normalize_name(s->config.host_name) : avahi_get_host_name();
1318     s->host_name[strcspn(s->host_name, ".")] = 0;
1319     s->domain_name = s->config.domain_name ? avahi_normalize_name(s->config.domain_name) : g_strdup("local.");
1320     s->host_name_fqdn = NULL;
1321     update_fqdn(s);
1322
1323     s->record_list = avahi_record_list_new();
1324
1325     s->time_event_queue = avahi_time_event_queue_new(s->context, G_PRIORITY_DEFAULT+10); /* Slightly less priority than the FDs */
1326     s->register_time_event = NULL;
1327     
1328     s->state = AVAHI_SERVER_INVALID;
1329
1330     s->monitor = avahi_interface_monitor_new(s);
1331     avahi_interface_monitor_sync(s->monitor);
1332
1333     register_localhost(s);
1334
1335     s->hinfo_entry_group = NULL;
1336     s->browse_domain_entry_group = NULL;
1337     register_stuff(s);
1338     
1339     return s;
1340 }
1341
1342 void avahi_server_free(AvahiServer* s) {
1343     g_assert(s);
1344
1345     while(s->entries)
1346         free_entry(s, s->entries);
1347
1348     avahi_interface_monitor_free(s->monitor);
1349
1350     while (s->groups)
1351         free_group(s, s->groups);
1352
1353     free_slots(s);
1354     
1355     while (s->host_name_resolvers)
1356         avahi_host_name_resolver_free(s->host_name_resolvers);
1357     while (s->address_resolvers)
1358         avahi_address_resolver_free(s->address_resolvers);
1359     while (s->domain_browsers)
1360         avahi_domain_browser_free(s->domain_browsers);
1361     while (s->service_type_browsers)
1362         avahi_service_type_browser_free(s->service_type_browsers);
1363     while (s->service_browsers)
1364         avahi_service_browser_free(s->service_browsers);
1365     while (s->service_resolvers)
1366         avahi_service_resolver_free(s->service_resolvers);
1367     while (s->record_browsers)
1368         avahi_record_browser_destroy(s->record_browsers);
1369     g_hash_table_destroy(s->record_browser_hashtable);
1370
1371     g_hash_table_destroy(s->entries_by_key);
1372
1373     if (s->register_time_event)
1374         avahi_time_event_queue_remove(s->time_event_queue, s->register_time_event);
1375     avahi_time_event_queue_free(s->time_event_queue);
1376
1377     avahi_record_list_free(s->record_list);
1378     
1379     if (s->fd_ipv4 >= 0)
1380         close(s->fd_ipv4);
1381     if (s->fd_ipv6 >= 0)
1382         close(s->fd_ipv6);
1383     if (s->fd_legacy_unicast_ipv4 >= 0)
1384         close(s->fd_legacy_unicast_ipv4);
1385     if (s->fd_legacy_unicast_ipv6 >= 0)
1386         close(s->fd_legacy_unicast_ipv6);
1387
1388     g_free(s->host_name);
1389     g_free(s->domain_name);
1390     g_free(s->host_name_fqdn);
1391
1392     g_source_destroy(s->source);
1393     g_source_unref(s->source);
1394     g_main_context_unref(s->context);
1395
1396     avahi_server_config_free(&s->config);
1397
1398     g_free(s);
1399 }
1400
1401 gint avahi_server_add(
1402     AvahiServer *s,
1403     AvahiEntryGroup *g,
1404     gint interface,
1405     guchar protocol,
1406     AvahiEntryFlags flags,
1407     AvahiRecord *r) {
1408     
1409     AvahiEntry *e, *t;
1410     g_assert(s);
1411     g_assert(r);
1412
1413     g_assert(r->key->type != AVAHI_DNS_TYPE_ANY);
1414
1415     e = g_new(AvahiEntry, 1);
1416     e->server = s;
1417     e->record = avahi_record_ref(r);
1418     e->group = g;
1419     e->interface = interface;
1420     e->protocol = protocol;
1421     e->flags = flags;
1422     e->dead = FALSE;
1423
1424     AVAHI_LLIST_HEAD_INIT(AvahiAnnouncement, e->announcements);
1425
1426     AVAHI_LLIST_PREPEND(AvahiEntry, entries, s->entries, e);
1427
1428     /* Insert into hash table indexed by name */
1429     t = g_hash_table_lookup(s->entries_by_key, e->record->key);
1430     AVAHI_LLIST_PREPEND(AvahiEntry, by_key, t, e);
1431     g_hash_table_replace(s->entries_by_key, e->record->key, t);
1432
1433     /* Insert into group list */
1434     if (g)
1435         AVAHI_LLIST_PREPEND(AvahiEntry, by_group, g->entries, e); 
1436
1437     avahi_announce_entry(s, e);
1438
1439     return 0;
1440 }
1441
1442 const AvahiRecord *avahi_server_iterate(AvahiServer *s, AvahiEntryGroup *g, void **state) {
1443     AvahiEntry **e = (AvahiEntry**) state;
1444     g_assert(s);
1445     g_assert(e);
1446
1447     if (!*e)
1448         *e = g ? g->entries : s->entries;
1449     
1450     while (*e && (*e)->dead)
1451         *e = g ? (*e)->by_group_next : (*e)->entries_next;
1452         
1453     if (!*e)
1454         return NULL;
1455
1456     return avahi_record_ref((*e)->record);
1457 }
1458
1459 void avahi_server_dump(AvahiServer *s, FILE *f) {
1460     AvahiEntry *e;
1461     g_assert(s);
1462     g_assert(f);
1463
1464     fprintf(f, "\n;;; ZONE DUMP FOLLOWS ;;;\n");
1465
1466     for (e = s->entries; e; e = e->entries_next) {
1467         gchar *t;
1468
1469         if (e->dead)
1470             continue;
1471         
1472         t = avahi_record_to_string(e->record);
1473         fprintf(f, "%s ; iface=%i proto=%i\n", t, e->interface, e->protocol);
1474         g_free(t);
1475     }
1476
1477     avahi_dump_caches(s->monitor, f);
1478 }
1479
1480 gint avahi_server_add_ptr(
1481     AvahiServer *s,
1482     AvahiEntryGroup *g,
1483     gint interface,
1484     guchar protocol,
1485     AvahiEntryFlags flags,
1486     const gchar *name,
1487     const gchar *dest) {
1488
1489     AvahiRecord *r;
1490
1491     g_assert(dest);
1492
1493     r = avahi_record_new_full(name ? name : s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR);
1494     r->data.ptr.name = avahi_normalize_name(dest);
1495     avahi_server_add(s, g, interface, protocol, flags, r);
1496     avahi_record_unref(r);
1497     return 0;
1498 }
1499
1500 gint avahi_server_add_address(
1501     AvahiServer *s,
1502     AvahiEntryGroup *g,
1503     gint interface,
1504     guchar protocol,
1505     AvahiEntryFlags flags,
1506     const gchar *name,
1507     AvahiAddress *a) {
1508
1509     gchar *n = NULL;
1510     g_assert(s);
1511     g_assert(a);
1512
1513     name = name ? (n = avahi_normalize_name(name)) : s->host_name_fqdn;
1514     
1515     if (a->family == AF_INET) {
1516         gchar *reverse;
1517         AvahiRecord  *r;
1518
1519         r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A);
1520         r->data.a.address = a->data.ipv4;
1521         avahi_server_add(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, r);
1522         avahi_record_unref(r);
1523         
1524         reverse = avahi_reverse_lookup_name_ipv4(&a->data.ipv4);
1525         avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, reverse, name);
1526         g_free(reverse);
1527         
1528     } else {
1529         gchar *reverse;
1530         AvahiRecord *r;
1531             
1532         r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA);
1533         r->data.aaaa.address = a->data.ipv6;
1534         avahi_server_add(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, r);
1535         avahi_record_unref(r);
1536
1537         reverse = avahi_reverse_lookup_name_ipv6_arpa(&a->data.ipv6);
1538         avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, reverse, name);
1539         g_free(reverse);
1540     
1541         reverse = avahi_reverse_lookup_name_ipv6_int(&a->data.ipv6);
1542         avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, reverse, name);
1543         g_free(reverse);
1544     }
1545     
1546     g_free(n);
1547
1548     return 0;
1549 }
1550
1551 gint avahi_server_add_text_strlst(
1552     AvahiServer *s,
1553     AvahiEntryGroup *g,
1554     gint interface,
1555     guchar protocol,
1556     AvahiEntryFlags flags,
1557     const gchar *name,
1558     AvahiStringList *strlst) {
1559
1560     AvahiRecord *r;
1561     
1562     g_assert(s);
1563     
1564     r = avahi_record_new_full(name ? name : s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_TXT);
1565     r->data.txt.string_list = strlst;
1566     avahi_server_add(s, g, interface, protocol, flags, r);
1567     avahi_record_unref(r);
1568
1569     return 0;
1570 }
1571
1572 gint avahi_server_add_text_va(
1573     AvahiServer *s,
1574     AvahiEntryGroup *g,
1575     gint interface,
1576     guchar protocol,
1577     AvahiEntryFlags flags,
1578     const gchar *name,
1579     va_list va) {
1580     
1581     g_assert(s);
1582
1583     avahi_server_add_text_strlst(s, g, interface, protocol, flags, name, avahi_string_list_new_va(va));
1584     return 0;
1585 }
1586
1587 gint avahi_server_add_text(
1588     AvahiServer *s,
1589     AvahiEntryGroup *g,
1590     gint interface,
1591     guchar protocol,
1592     AvahiEntryFlags flags,
1593     const gchar *name,
1594     ...) {
1595
1596     va_list va;
1597     
1598     g_assert(s);
1599
1600     va_start(va, name);
1601     avahi_server_add_text_va(s, g, interface, protocol, flags, name, va);
1602     va_end(va);
1603
1604     return 0;
1605 }
1606
1607 static void escape_service_name(gchar *d, guint size, const gchar *s) {
1608     g_assert(d);
1609     g_assert(size);
1610     g_assert(s);
1611
1612     while (*s && size >= 2) {
1613         if (*s == '.' || *s == '\\') {
1614             if (size < 3)
1615                 break;
1616
1617             *(d++) = '\\';
1618             size--;
1619         }
1620             
1621         *(d++) = *(s++);
1622         size--;
1623     }
1624
1625     g_assert(size > 0);
1626     *(d++) = 0;
1627 }
1628
1629 gint avahi_server_add_service_strlst(
1630     AvahiServer *s,
1631     AvahiEntryGroup *g,
1632     gint interface,
1633     guchar protocol,
1634     const gchar *type,
1635     const gchar *name,
1636     const gchar *domain,
1637     const gchar *host,
1638     guint16 port,
1639     AvahiStringList *strlst) {
1640
1641     gchar ptr_name[256], svc_name[256], ename[64], enum_ptr[256];
1642     AvahiRecord *r;
1643     
1644     g_assert(s);
1645     g_assert(type);
1646     g_assert(name);
1647
1648     escape_service_name(ename, sizeof(ename), name);
1649
1650     if (domain) {
1651         while (domain[0] == '.')
1652             domain++;
1653     } else
1654         domain = s->domain_name;
1655
1656     if (!host)
1657         host = s->host_name_fqdn;
1658
1659     snprintf(ptr_name, sizeof(ptr_name), "%s.%s", type, domain);
1660     snprintf(svc_name, sizeof(svc_name), "%s.%s.%s", ename, type, domain);
1661     
1662     avahi_server_add_ptr(s, g, interface, protocol, AVAHI_ENTRY_NULL, ptr_name, svc_name);
1663
1664     r = avahi_record_new_full(svc_name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV);
1665     r->data.srv.priority = 0;
1666     r->data.srv.weight = 0;
1667     r->data.srv.port = port;
1668     r->data.srv.name = avahi_normalize_name(host);
1669     avahi_server_add(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE, r);
1670     avahi_record_unref(r);
1671
1672     avahi_server_add_text_strlst(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE, svc_name, strlst);
1673
1674     snprintf(enum_ptr, sizeof(enum_ptr), "_services._dns-sd._udp.%s", domain);
1675     avahi_server_add_ptr(s, g, interface, protocol, AVAHI_ENTRY_NULL, enum_ptr, ptr_name);
1676
1677     return 0;
1678 }
1679
1680 gint avahi_server_add_service_va(
1681     AvahiServer *s,
1682     AvahiEntryGroup *g,
1683     gint interface,
1684     guchar protocol,
1685     const gchar *type,
1686     const gchar *name,
1687     const gchar *domain,
1688     const gchar *host,
1689     guint16 port,
1690     va_list va){
1691
1692     g_assert(s);
1693     g_assert(type);
1694     g_assert(name);
1695
1696     avahi_server_add_service_strlst(s, g, interface, protocol, type, name, domain, host, port, avahi_string_list_new_va(va));
1697     return 0;
1698 }
1699
1700 gint avahi_server_add_service(
1701     AvahiServer *s,
1702     AvahiEntryGroup *g,
1703     gint interface,
1704     guchar protocol,
1705     const gchar *type,
1706     const gchar *name,
1707     const gchar *domain,
1708     const gchar *host,
1709     guint16 port,
1710     ... ){
1711
1712     va_list va;
1713     
1714     g_assert(s);
1715     g_assert(type);
1716     g_assert(name);
1717
1718     va_start(va, port);
1719     avahi_server_add_service_va(s, g, interface, protocol, type, name, domain, host, port, va);
1720     va_end(va);
1721     return 0;
1722 }
1723
1724 static void post_query_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, gpointer userdata) {
1725     AvahiKey *k = userdata;
1726
1727     g_assert(m);
1728     g_assert(i);
1729     g_assert(k);
1730
1731     avahi_interface_post_query(i, k, FALSE);
1732 }
1733
1734 void avahi_server_post_query(AvahiServer *s, gint interface, guchar protocol, AvahiKey *key) {
1735     g_assert(s);
1736     g_assert(key);
1737
1738     avahi_interface_monitor_walk(s->monitor, interface, protocol, post_query_callback, key);
1739 }
1740
1741 void avahi_entry_group_change_state(AvahiEntryGroup *g, AvahiEntryGroupState state) {
1742     g_assert(g);
1743
1744     if (g->state == state)
1745         return;
1746
1747     g->state = state;
1748     
1749     if (g->callback) {
1750         g->callback(g->server, g, state, g->userdata);
1751         return;
1752     }
1753 }
1754
1755 AvahiEntryGroup *avahi_entry_group_new(AvahiServer *s, AvahiEntryGroupCallback callback, gpointer userdata) {
1756     AvahiEntryGroup *g;
1757     
1758     g_assert(s);
1759
1760     g = g_new(AvahiEntryGroup, 1);
1761     g->server = s;
1762     g->callback = callback;
1763     g->userdata = userdata;
1764     g->dead = FALSE;
1765     g->state = AVAHI_ENTRY_GROUP_UNCOMMITED;
1766     g->n_probing = 0;
1767     AVAHI_LLIST_HEAD_INIT(AvahiEntry, g->entries);
1768
1769     AVAHI_LLIST_PREPEND(AvahiEntryGroup, groups, s->groups, g);
1770     return g;
1771 }
1772
1773 void avahi_entry_group_free(AvahiEntryGroup *g) {
1774     AvahiEntry *e;
1775     
1776     g_assert(g);
1777     g_assert(g->server);
1778
1779     for (e = g->entries; e; e = e->by_group_next) {
1780         avahi_goodbye_entry(g->server, e, TRUE);
1781         e->dead = TRUE;
1782     }
1783
1784     g->dead = TRUE;
1785     
1786     g->server->need_group_cleanup = TRUE;
1787     g->server->need_entry_cleanup = TRUE;
1788 }
1789
1790 gint avahi_entry_group_commit(AvahiEntryGroup *g) {
1791     g_assert(g);
1792     g_assert(!g->dead);
1793
1794     if (g->state != AVAHI_ENTRY_GROUP_UNCOMMITED)
1795         return -1;
1796
1797     avahi_entry_group_change_state(g, AVAHI_ENTRY_GROUP_REGISTERING);
1798     avahi_announce_group(g->server, g);
1799     avahi_entry_group_check_probed(g, FALSE);
1800
1801     return 0;
1802 }
1803
1804 gboolean avahi_entry_commited(AvahiEntry *e) {
1805     g_assert(e);
1806     g_assert(!e->dead);
1807
1808     return !e->group ||
1809         e->group->state == AVAHI_ENTRY_GROUP_REGISTERING ||
1810         e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED;
1811 }
1812
1813 AvahiEntryGroupState avahi_entry_group_get_state(AvahiEntryGroup *g) {
1814     g_assert(g);
1815     g_assert(!g->dead);
1816
1817     return g->state;
1818 }
1819
1820 void avahi_entry_group_set_data(AvahiEntryGroup *g, gpointer userdata) {
1821     g_assert(g);
1822
1823     g->userdata = userdata;
1824 }
1825
1826 gpointer avahi_entry_group_get_data(AvahiEntryGroup *g) {
1827     g_assert(g);
1828
1829     return g->userdata;
1830 }
1831
1832 const gchar* avahi_server_get_domain_name(AvahiServer *s) {
1833     g_assert(s);
1834
1835     return s->domain_name;
1836 }
1837
1838 const gchar* avahi_server_get_host_name(AvahiServer *s) {
1839     g_assert(s);
1840
1841     return s->host_name;
1842 }
1843
1844 const gchar* avahi_server_get_host_name_fqdn(AvahiServer *s) {
1845     g_assert(s);
1846
1847     return s->host_name_fqdn;
1848 }
1849
1850 gpointer avahi_server_get_data(AvahiServer *s) {
1851     g_assert(s);
1852
1853     return s->userdata;
1854 }
1855
1856 void avahi_server_set_data(AvahiServer *s, gpointer userdata) {
1857     g_assert(s);
1858
1859     s->userdata = userdata;
1860 }
1861
1862 AvahiServerState avahi_server_get_state(AvahiServer *s) {
1863     g_assert(s);
1864
1865     return s->state;
1866 }
1867
1868 AvahiServerConfig* avahi_server_config_init(AvahiServerConfig *c) {
1869     g_assert(c);
1870
1871     memset(c, 0, sizeof(AvahiServerConfig));
1872     c->register_hinfo = TRUE;
1873     c->register_addresses = TRUE;
1874     c->use_ipv6 = TRUE;
1875     c->use_ipv4 = TRUE;
1876     c->host_name = NULL;
1877     c->domain_name = NULL;
1878     c->check_response_ttl = TRUE;
1879     c->announce_domain = TRUE;
1880     c->use_iff_running = FALSE;
1881     c->enable_reflector = FALSE;
1882     c->ipv_reflect = FALSE;
1883     c->register_workstation = TRUE;
1884     
1885     return c;
1886 }
1887
1888 void avahi_server_config_free(AvahiServerConfig *c) {
1889     g_assert(c);
1890
1891     g_free(c->host_name);
1892     g_free(c->domain_name);
1893 }
1894
1895 AvahiServerConfig* avahi_server_config_copy(AvahiServerConfig *ret, const AvahiServerConfig *c) {
1896     g_assert(ret);
1897     g_assert(c);
1898
1899     *ret = *c;
1900
1901     ret->host_name = g_strdup(c->host_name);
1902     ret->domain_name = g_strdup(c->domain_name);
1903
1904     return ret;
1905 }