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