]> git.meshlink.io Git - catta/blob - avahi-core/server.c
356a917cec8a9296a8117f990c257cc563f5419f
[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, "_browse._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
1322     s->legacy_unicast_reflect_slots = NULL;
1323     s->legacy_unicast_reflect_id = 0;
1324     
1325     /* Get host name */
1326     s->host_name = s->config.host_name ? avahi_normalize_name(s->config.host_name) : avahi_get_host_name();
1327     s->host_name[strcspn(s->host_name, ".")] = 0;
1328     s->domain_name = s->config.domain_name ? avahi_normalize_name(s->config.domain_name) : g_strdup("local.");
1329     s->host_name_fqdn = NULL;
1330     update_fqdn(s);
1331
1332     s->record_list = avahi_record_list_new();
1333
1334     s->time_event_queue = avahi_time_event_queue_new(s->context, G_PRIORITY_DEFAULT+10); /* Slightly less priority than the FDs */
1335     s->register_time_event = NULL;
1336     
1337     s->state = AVAHI_SERVER_INVALID;
1338
1339     s->monitor = avahi_interface_monitor_new(s);
1340     avahi_interface_monitor_sync(s->monitor);
1341
1342     register_localhost(s);
1343
1344     s->hinfo_entry_group = NULL;
1345     s->browse_domain_entry_group = NULL;
1346     register_stuff(s);
1347     
1348     return s;
1349 }
1350
1351 void avahi_server_free(AvahiServer* s) {
1352     g_assert(s);
1353
1354     while(s->entries)
1355         free_entry(s, s->entries);
1356
1357     avahi_interface_monitor_free(s->monitor);
1358
1359     while (s->groups)
1360         free_group(s, s->groups);
1361
1362     free_slots(s);
1363     
1364     while (s->host_name_resolvers)
1365         avahi_host_name_resolver_free(s->host_name_resolvers);
1366     while (s->address_resolvers)
1367         avahi_address_resolver_free(s->address_resolvers);
1368     while (s->domain_browsers)
1369         avahi_domain_browser_free(s->domain_browsers);
1370     while (s->service_type_browsers)
1371         avahi_service_type_browser_free(s->service_type_browsers);
1372     while (s->service_browsers)
1373         avahi_service_browser_free(s->service_browsers);
1374     while (s->service_resolvers)
1375         avahi_service_resolver_free(s->service_resolvers);
1376     while (s->record_browsers)
1377         avahi_record_browser_destroy(s->record_browsers);
1378     g_hash_table_destroy(s->record_browser_hashtable);
1379
1380     g_hash_table_destroy(s->entries_by_key);
1381
1382     if (s->register_time_event)
1383         avahi_time_event_queue_remove(s->time_event_queue, s->register_time_event);
1384     avahi_time_event_queue_free(s->time_event_queue);
1385
1386     avahi_record_list_free(s->record_list);
1387     
1388     if (s->fd_ipv4 >= 0)
1389         close(s->fd_ipv4);
1390     if (s->fd_ipv6 >= 0)
1391         close(s->fd_ipv6);
1392     if (s->fd_legacy_unicast_ipv4 >= 0)
1393         close(s->fd_legacy_unicast_ipv4);
1394     if (s->fd_legacy_unicast_ipv6 >= 0)
1395         close(s->fd_legacy_unicast_ipv6);
1396
1397     g_free(s->host_name);
1398     g_free(s->domain_name);
1399     g_free(s->host_name_fqdn);
1400
1401     g_source_destroy(s->source);
1402     g_source_unref(s->source);
1403     g_main_context_unref(s->context);
1404
1405     avahi_server_config_free(&s->config);
1406
1407     g_free(s);
1408 }
1409
1410 static gint check_record_conflict(AvahiServer *s, gint interface, guchar protocol, AvahiRecord *r, AvahiEntryFlags flags) {
1411     AvahiEntry *e;
1412     
1413     g_assert(s);
1414     g_assert(r);
1415
1416     for (e = g_hash_table_lookup(s->entries_by_key, r->key); e; e = e->by_key_next) {
1417         if (e->dead)
1418             continue;
1419
1420         if (!(flags & AVAHI_ENTRY_UNIQUE) && !(e->flags & AVAHI_ENTRY_UNIQUE))
1421             continue;
1422         
1423         if ((flags & AVAHI_ENTRY_ALLOWMUTIPLE) && (e->flags & AVAHI_ENTRY_ALLOWMUTIPLE) )
1424             continue;
1425
1426         if (interface <= 0 ||
1427             e->interface <= 0 ||
1428             e->interface == interface ||
1429             protocol == AF_UNSPEC ||
1430             e->protocol == AF_UNSPEC ||
1431             e->protocol == protocol)
1432
1433             return -1;
1434
1435     }
1436
1437     return 0;
1438 }
1439
1440 gint avahi_server_add(
1441     AvahiServer *s,
1442     AvahiEntryGroup *g,
1443     gint interface,
1444     guchar protocol,
1445     AvahiEntryFlags flags,
1446     AvahiRecord *r) {
1447     
1448     AvahiEntry *e, *t;
1449     g_assert(s);
1450     g_assert(r);
1451
1452     g_assert(r->key->type != AVAHI_DNS_TYPE_ANY);
1453
1454     if (check_record_conflict(s, interface, protocol, r, flags) < 0)
1455         return -1;
1456
1457     e = g_new(AvahiEntry, 1);
1458     e->server = s;
1459     e->record = avahi_record_ref(r);
1460     e->group = g;
1461     e->interface = interface;
1462     e->protocol = protocol;
1463     e->flags = flags;
1464     e->dead = FALSE;
1465
1466     AVAHI_LLIST_HEAD_INIT(AvahiAnnouncement, e->announcements);
1467
1468     AVAHI_LLIST_PREPEND(AvahiEntry, entries, s->entries, e);
1469
1470     /* Insert into hash table indexed by name */
1471     t = g_hash_table_lookup(s->entries_by_key, e->record->key);
1472     AVAHI_LLIST_PREPEND(AvahiEntry, by_key, t, e);
1473     g_hash_table_replace(s->entries_by_key, e->record->key, t);
1474
1475     /* Insert into group list */
1476     if (g)
1477         AVAHI_LLIST_PREPEND(AvahiEntry, by_group, g->entries, e); 
1478
1479     avahi_announce_entry(s, e);
1480
1481     return 0;
1482 }
1483
1484 const AvahiRecord *avahi_server_iterate(AvahiServer *s, AvahiEntryGroup *g, void **state) {
1485     AvahiEntry **e = (AvahiEntry**) state;
1486     g_assert(s);
1487     g_assert(e);
1488
1489     if (!*e)
1490         *e = g ? g->entries : s->entries;
1491     
1492     while (*e && (*e)->dead)
1493         *e = g ? (*e)->by_group_next : (*e)->entries_next;
1494         
1495     if (!*e)
1496         return NULL;
1497
1498     return avahi_record_ref((*e)->record);
1499 }
1500
1501 void avahi_server_dump(AvahiServer *s, FILE *f) {
1502     AvahiEntry *e;
1503     g_assert(s);
1504     g_assert(f);
1505
1506     fprintf(f, "\n;;; ZONE DUMP FOLLOWS ;;;\n");
1507
1508     for (e = s->entries; e; e = e->entries_next) {
1509         gchar *t;
1510
1511         if (e->dead)
1512             continue;
1513         
1514         t = avahi_record_to_string(e->record);
1515         fprintf(f, "%s ; iface=%i proto=%i\n", t, e->interface, e->protocol);
1516         g_free(t);
1517     }
1518
1519     avahi_dump_caches(s->monitor, f);
1520 }
1521
1522 gint avahi_server_add_ptr(
1523     AvahiServer *s,
1524     AvahiEntryGroup *g,
1525     gint interface,
1526     guchar protocol,
1527     AvahiEntryFlags flags,
1528     const gchar *name,
1529     const gchar *dest) {
1530
1531     AvahiRecord *r;
1532     gint ret;
1533
1534     g_assert(dest);
1535
1536     r = avahi_record_new_full(name ? name : s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR);
1537     r->data.ptr.name = avahi_normalize_name(dest);
1538     ret = avahi_server_add(s, g, interface, protocol, flags, r);
1539     avahi_record_unref(r);
1540     return ret;
1541 }
1542
1543 gint avahi_server_add_address(
1544     AvahiServer *s,
1545     AvahiEntryGroup *g,
1546     gint interface,
1547     guchar protocol,
1548     AvahiEntryFlags flags,
1549     const gchar *name,
1550     AvahiAddress *a) {
1551
1552     gchar *n = NULL;
1553     gint ret = 0;
1554     g_assert(s);
1555     g_assert(a);
1556
1557     name = name ? (n = avahi_normalize_name(name)) : s->host_name_fqdn;
1558     
1559     if (a->family == AF_INET) {
1560         gchar *reverse;
1561         AvahiRecord  *r;
1562
1563         r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A);
1564         r->data.a.address = a->data.ipv4;
1565         ret = avahi_server_add(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE | AVAHI_ENTRY_ALLOWMUTIPLE, r);
1566         avahi_record_unref(r);
1567         
1568         reverse = avahi_reverse_lookup_name_ipv4(&a->data.ipv4);
1569         ret |= avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, reverse, name);
1570         g_free(reverse);
1571         
1572     } else {
1573         gchar *reverse;
1574         AvahiRecord *r;
1575             
1576         r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA);
1577         r->data.aaaa.address = a->data.ipv6;
1578         ret = avahi_server_add(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE | AVAHI_ENTRY_ALLOWMUTIPLE, r);
1579         avahi_record_unref(r);
1580
1581         reverse = avahi_reverse_lookup_name_ipv6_arpa(&a->data.ipv6);
1582         ret |= avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, reverse, name);
1583         g_free(reverse);
1584     
1585         reverse = avahi_reverse_lookup_name_ipv6_int(&a->data.ipv6);
1586         ret |= avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, reverse, name);
1587         g_free(reverse);
1588     }
1589     
1590     g_free(n);
1591
1592     return ret;
1593 }
1594
1595 gint avahi_server_add_text_strlst(
1596     AvahiServer *s,
1597     AvahiEntryGroup *g,
1598     gint interface,
1599     guchar protocol,
1600     AvahiEntryFlags flags,
1601     const gchar *name,
1602     AvahiStringList *strlst) {
1603
1604     AvahiRecord *r;
1605     gint ret;
1606     
1607     g_assert(s);
1608     
1609     r = avahi_record_new_full(name ? name : s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_TXT);
1610     r->data.txt.string_list = strlst;
1611     ret = avahi_server_add(s, g, interface, protocol, flags, r);
1612     avahi_record_unref(r);
1613
1614     return ret;
1615 }
1616
1617 gint avahi_server_add_text_va(
1618     AvahiServer *s,
1619     AvahiEntryGroup *g,
1620     gint interface,
1621     guchar protocol,
1622     AvahiEntryFlags flags,
1623     const gchar *name,
1624     va_list va) {
1625     
1626     g_assert(s);
1627
1628     return avahi_server_add_text_strlst(s, g, interface, protocol, flags, name, avahi_string_list_new_va(va));
1629 }
1630
1631 gint avahi_server_add_text(
1632     AvahiServer *s,
1633     AvahiEntryGroup *g,
1634     gint interface,
1635     guchar protocol,
1636     AvahiEntryFlags flags,
1637     const gchar *name,
1638     ...) {
1639
1640     va_list va;
1641     gint ret;
1642     
1643     g_assert(s);
1644
1645     va_start(va, name);
1646     ret = avahi_server_add_text_va(s, g, interface, protocol, flags, name, va);
1647     va_end(va);
1648
1649     return ret;
1650 }
1651
1652 static void escape_service_name(gchar *d, guint size, const gchar *s) {
1653     g_assert(d);
1654     g_assert(size);
1655     g_assert(s);
1656
1657     while (*s && size >= 2) {
1658         if (*s == '.' || *s == '\\') {
1659             if (size < 3)
1660                 break;
1661
1662             *(d++) = '\\';
1663             size--;
1664         }
1665             
1666         *(d++) = *(s++);
1667         size--;
1668     }
1669
1670     g_assert(size > 0);
1671     *(d++) = 0;
1672 }
1673
1674 gint avahi_server_add_service_strlst(
1675     AvahiServer *s,
1676     AvahiEntryGroup *g,
1677     gint interface,
1678     guchar protocol,
1679     const gchar *type,
1680     const gchar *name,
1681     const gchar *domain,
1682     const gchar *host,
1683     guint16 port,
1684     AvahiStringList *strlst) {
1685
1686     gchar ptr_name[256], svc_name[256], ename[64], enum_ptr[256];
1687     AvahiRecord *r;
1688     gint ret = 0;
1689     
1690     g_assert(s);
1691     g_assert(type);
1692     g_assert(name);
1693
1694     escape_service_name(ename, sizeof(ename), name);
1695
1696     if (domain) {
1697         while (domain[0] == '.')
1698             domain++;
1699     } else
1700         domain = s->domain_name;
1701
1702     if (!host)
1703         host = s->host_name_fqdn;
1704
1705     snprintf(ptr_name, sizeof(ptr_name), "%s.%s", type, domain);
1706     snprintf(svc_name, sizeof(svc_name), "%s.%s.%s", ename, type, domain);
1707     
1708     ret = avahi_server_add_ptr(s, g, interface, protocol, AVAHI_ENTRY_NULL, ptr_name, svc_name);
1709
1710     r = avahi_record_new_full(svc_name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV);
1711     r->data.srv.priority = 0;
1712     r->data.srv.weight = 0;
1713     r->data.srv.port = port;
1714     r->data.srv.name = avahi_normalize_name(host);
1715     ret |= avahi_server_add(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE, r);
1716     avahi_record_unref(r);
1717
1718     ret |= avahi_server_add_text_strlst(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE, svc_name, strlst);
1719
1720     snprintf(enum_ptr, sizeof(enum_ptr), "_services._dns-sd._udp.%s", domain);
1721     ret |=avahi_server_add_ptr(s, g, interface, protocol, AVAHI_ENTRY_NULL, enum_ptr, ptr_name);
1722
1723     return ret;
1724 }
1725
1726 gint avahi_server_add_service_va(
1727     AvahiServer *s,
1728     AvahiEntryGroup *g,
1729     gint interface,
1730     guchar protocol,
1731     const gchar *type,
1732     const gchar *name,
1733     const gchar *domain,
1734     const gchar *host,
1735     guint16 port,
1736     va_list va){
1737
1738     g_assert(s);
1739     g_assert(type);
1740     g_assert(name);
1741
1742     return avahi_server_add_service_strlst(s, g, interface, protocol, type, name, domain, host, port, avahi_string_list_new_va(va));
1743 }
1744
1745 gint avahi_server_add_service(
1746     AvahiServer *s,
1747     AvahiEntryGroup *g,
1748     gint interface,
1749     guchar protocol,
1750     const gchar *type,
1751     const gchar *name,
1752     const gchar *domain,
1753     const gchar *host,
1754     guint16 port,
1755     ... ){
1756
1757     va_list va;
1758     gint ret;
1759     
1760     g_assert(s);
1761     g_assert(type);
1762     g_assert(name);
1763
1764     va_start(va, port);
1765     ret = avahi_server_add_service_va(s, g, interface, protocol, type, name, domain, host, port, va);
1766     va_end(va);
1767     return ret;
1768 }
1769
1770 static void post_query_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, gpointer userdata) {
1771     AvahiKey *k = userdata;
1772
1773     g_assert(m);
1774     g_assert(i);
1775     g_assert(k);
1776
1777     avahi_interface_post_query(i, k, FALSE);
1778 }
1779
1780 void avahi_server_post_query(AvahiServer *s, gint interface, guchar protocol, AvahiKey *key) {
1781     g_assert(s);
1782     g_assert(key);
1783
1784     avahi_interface_monitor_walk(s->monitor, interface, protocol, post_query_callback, key);
1785 }
1786
1787 void avahi_entry_group_change_state(AvahiEntryGroup *g, AvahiEntryGroupState state) {
1788     g_assert(g);
1789
1790     if (g->state == state)
1791         return;
1792
1793     g->state = state;
1794     
1795     if (g->callback) {
1796         g->callback(g->server, g, state, g->userdata);
1797         return;
1798     }
1799 }
1800
1801 AvahiEntryGroup *avahi_entry_group_new(AvahiServer *s, AvahiEntryGroupCallback callback, gpointer userdata) {
1802     AvahiEntryGroup *g;
1803     
1804     g_assert(s);
1805
1806     g = g_new(AvahiEntryGroup, 1);
1807     g->server = s;
1808     g->callback = callback;
1809     g->userdata = userdata;
1810     g->dead = FALSE;
1811     g->state = AVAHI_ENTRY_GROUP_UNCOMMITED;
1812     g->n_probing = 0;
1813     AVAHI_LLIST_HEAD_INIT(AvahiEntry, g->entries);
1814
1815     AVAHI_LLIST_PREPEND(AvahiEntryGroup, groups, s->groups, g);
1816     return g;
1817 }
1818
1819 void avahi_entry_group_free(AvahiEntryGroup *g) {
1820     AvahiEntry *e;
1821     
1822     g_assert(g);
1823     g_assert(g->server);
1824
1825     for (e = g->entries; e; e = e->by_group_next) {
1826         avahi_goodbye_entry(g->server, e, TRUE);
1827         e->dead = TRUE;
1828     }
1829
1830     g->dead = TRUE;
1831     
1832     g->server->need_group_cleanup = TRUE;
1833     g->server->need_entry_cleanup = TRUE;
1834 }
1835
1836 gint avahi_entry_group_commit(AvahiEntryGroup *g) {
1837     g_assert(g);
1838     g_assert(!g->dead);
1839
1840     if (g->state != AVAHI_ENTRY_GROUP_UNCOMMITED)
1841         return -1;
1842
1843     avahi_entry_group_change_state(g, AVAHI_ENTRY_GROUP_REGISTERING);
1844     avahi_announce_group(g->server, g);
1845     avahi_entry_group_check_probed(g, FALSE);
1846
1847     return 0;
1848 }
1849
1850 gboolean avahi_entry_commited(AvahiEntry *e) {
1851     g_assert(e);
1852     g_assert(!e->dead);
1853
1854     return !e->group ||
1855         e->group->state == AVAHI_ENTRY_GROUP_REGISTERING ||
1856         e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED;
1857 }
1858
1859 AvahiEntryGroupState avahi_entry_group_get_state(AvahiEntryGroup *g) {
1860     g_assert(g);
1861     g_assert(!g->dead);
1862
1863     return g->state;
1864 }
1865
1866 void avahi_entry_group_set_data(AvahiEntryGroup *g, gpointer userdata) {
1867     g_assert(g);
1868
1869     g->userdata = userdata;
1870 }
1871
1872 gpointer avahi_entry_group_get_data(AvahiEntryGroup *g) {
1873     g_assert(g);
1874
1875     return g->userdata;
1876 }
1877
1878 const gchar* avahi_server_get_domain_name(AvahiServer *s) {
1879     g_assert(s);
1880
1881     return s->domain_name;
1882 }
1883
1884 const gchar* avahi_server_get_host_name(AvahiServer *s) {
1885     g_assert(s);
1886
1887     return s->host_name;
1888 }
1889
1890 const gchar* avahi_server_get_host_name_fqdn(AvahiServer *s) {
1891     g_assert(s);
1892
1893     return s->host_name_fqdn;
1894 }
1895
1896 gpointer avahi_server_get_data(AvahiServer *s) {
1897     g_assert(s);
1898
1899     return s->userdata;
1900 }
1901
1902 void avahi_server_set_data(AvahiServer *s, gpointer userdata) {
1903     g_assert(s);
1904
1905     s->userdata = userdata;
1906 }
1907
1908 AvahiServerState avahi_server_get_state(AvahiServer *s) {
1909     g_assert(s);
1910
1911     return s->state;
1912 }
1913
1914 AvahiServerConfig* avahi_server_config_init(AvahiServerConfig *c) {
1915     g_assert(c);
1916
1917     memset(c, 0, sizeof(AvahiServerConfig));
1918     c->use_ipv6 = TRUE;
1919     c->use_ipv4 = TRUE;
1920     c->host_name = NULL;
1921     c->domain_name = NULL;
1922     c->check_response_ttl = TRUE;
1923     c->publish_hinfo = TRUE;
1924     c->publish_addresses = TRUE;
1925     c->publish_workstation = TRUE;
1926     c->publish_domain = TRUE;
1927     c->use_iff_running = FALSE;
1928     c->enable_reflector = FALSE;
1929     c->reflect_ipv = FALSE;
1930     
1931     return c;
1932 }
1933
1934 void avahi_server_config_free(AvahiServerConfig *c) {
1935     g_assert(c);
1936
1937     g_free(c->host_name);
1938     g_free(c->domain_name);
1939 }
1940
1941 AvahiServerConfig* avahi_server_config_copy(AvahiServerConfig *ret, const AvahiServerConfig *c) {
1942     g_assert(ret);
1943     g_assert(c);
1944
1945     *ret = *c;
1946
1947     ret->host_name = g_strdup(c->host_name);
1948     ret->domain_name = g_strdup(c->domain_name);
1949
1950     return ret;
1951 }