]> git.meshlink.io Git - catta/blob - avahi-core/server.c
* Update configure.ac to check for the availabilty of more functions
[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
621 static void handle_response_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a) {
622     guint n;
623     
624     g_assert(s);
625     g_assert(p);
626     g_assert(i);
627     g_assert(a);
628
629 /*     avahi_log_debug("response"); */
630     
631     for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) +
632              avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT); n > 0; n--) {
633         AvahiRecord *record;
634         gboolean cache_flush = FALSE;
635 /*         gchar *txt; */
636         
637         if (!(record = avahi_dns_packet_consume_record(p, &cache_flush))) {
638             avahi_log_warn("Packet too short (4)");
639             break;
640         }
641
642         if (record->key->type != AVAHI_DNS_TYPE_ANY) {
643
644 /*             avahi_log_debug("Handling response: %s", txt = avahi_record_to_string(record)); */
645 /*             g_free(txt); */
646             
647             if (handle_conflict(s, i, record, cache_flush, a)) {
648                 reflect_response(s, i, record, cache_flush);
649                 avahi_cache_update(i->cache, record, cache_flush, a);
650                 avahi_response_scheduler_incoming(i->response_scheduler, record, cache_flush);
651             }
652         }
653             
654         avahi_record_unref(record);
655     }
656 }
657
658 static AvahiLegacyUnicastReflectSlot* allocate_slot(AvahiServer *s) {
659     guint n, index = (guint) -1;
660     AvahiLegacyUnicastReflectSlot *slot;
661     
662     g_assert(s);
663
664     if (!s->legacy_unicast_reflect_slots)
665         s->legacy_unicast_reflect_slots = g_new0(AvahiLegacyUnicastReflectSlot*, AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS);
666
667     for (n = 0; n < AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS; n++, s->legacy_unicast_reflect_id++) {
668         index = s->legacy_unicast_reflect_id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
669         
670         if (!s->legacy_unicast_reflect_slots[index])
671             break;
672     }
673
674     if (index == (guint) -1 || s->legacy_unicast_reflect_slots[index])
675         return NULL;
676
677     slot = s->legacy_unicast_reflect_slots[index] = g_new(AvahiLegacyUnicastReflectSlot, 1);
678     slot->id = s->legacy_unicast_reflect_id++;
679     slot->server = s;
680     return slot;
681 }
682
683 static void deallocate_slot(AvahiServer *s, AvahiLegacyUnicastReflectSlot *slot) {
684     guint index;
685
686     g_assert(s);
687     g_assert(slot);
688
689     index = slot->id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
690
691     g_assert(s->legacy_unicast_reflect_slots[index] == slot);
692
693     avahi_time_event_queue_remove(s->time_event_queue, slot->time_event);
694     
695     g_free(slot);
696     s->legacy_unicast_reflect_slots[index] = NULL;
697 }
698
699 static void free_slots(AvahiServer *s) {
700     guint index;
701     g_assert(s);
702
703     if (!s->legacy_unicast_reflect_slots)
704         return;
705
706     for (index = 0; index < AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS; index ++)
707         if (s->legacy_unicast_reflect_slots[index])
708             deallocate_slot(s, s->legacy_unicast_reflect_slots[index]);
709
710     g_free(s->legacy_unicast_reflect_slots);
711     s->legacy_unicast_reflect_slots = NULL;
712 }
713
714 static AvahiLegacyUnicastReflectSlot* find_slot(AvahiServer *s, guint16 id) {
715     guint index;
716     
717     g_assert(s);
718
719     if (!s->legacy_unicast_reflect_slots)
720         return NULL;
721     
722     index = id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
723
724     if (!s->legacy_unicast_reflect_slots[index] || s->legacy_unicast_reflect_slots[index]->id != id)
725         return NULL;
726
727     return s->legacy_unicast_reflect_slots[index];
728 }
729
730 static void legacy_unicast_reflect_slot_timeout(AvahiTimeEvent *e, void *userdata) {
731     AvahiLegacyUnicastReflectSlot *slot = userdata;
732
733     g_assert(e);
734     g_assert(slot);
735     g_assert(slot->time_event == e);
736
737     deallocate_slot(slot->server, slot);
738 }
739
740 static void reflect_legacy_unicast_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, guint16 port) {
741     AvahiLegacyUnicastReflectSlot *slot;
742     AvahiInterface *j;
743
744     g_assert(s);
745     g_assert(p);
746     g_assert(i);
747     g_assert(a);
748     g_assert(port > 0);
749     g_assert(i->protocol == a->family);
750     
751     if (!s->config.enable_reflector)
752         return;
753
754 /*     avahi_log_debug("legacy unicast reflectr"); */
755     
756     /* Reflecting legacy unicast queries is a little more complicated
757        than reflecting normal queries, since we must route the
758        responses back to the right client. Therefore we must store
759        some information for finding the right client contact data for
760        response packets. In contrast to normal queries legacy
761        unicast query and response packets are reflected untouched and
762        are not reassembled into larger packets */
763
764     if (!(slot = allocate_slot(s))) {
765         /* No slot available, we drop this legacy unicast query */
766         avahi_log_warn("No slot available for legacy unicast reflection, dropping query packet.");
767         return;
768     }
769
770     slot->original_id = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID);
771     slot->address = *a;
772     slot->port = port;
773     slot->interface = i->hardware->index;
774
775     avahi_elapse_time(&slot->elapse_time, 2000, 0);
776     slot->time_event = avahi_time_event_queue_add(s->time_event_queue, &slot->elapse_time, legacy_unicast_reflect_slot_timeout, slot);
777
778     /* Patch the packet with our new locally generatedt id */
779     avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
780     
781     for (j = s->monitor->interfaces; j; j = j->interface_next)
782         if (avahi_interface_relevant(j) &&
783             j != i &&
784             (s->config.reflect_ipv || j->protocol == i->protocol)) {
785
786             if (j->protocol == AF_INET && s->fd_legacy_unicast_ipv4 >= 0) {
787                 avahi_send_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, j->hardware->index, p, NULL, 0);
788                 } else if (j->protocol == AF_INET6 && s->fd_legacy_unicast_ipv6 >= 0)
789                 avahi_send_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, j->hardware->index, p, NULL, 0);
790         }
791
792     /* Reset the id */
793     avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
794 }
795
796 static gboolean originates_from_local_legacy_unicast_socket(AvahiServer *s, const struct sockaddr *sa) {
797     AvahiAddress a;
798     g_assert(s);
799     g_assert(sa);
800
801     if (!s->config.enable_reflector)
802         return FALSE;
803     
804     avahi_address_from_sockaddr(sa, &a);
805
806     if (!avahi_address_is_local(s->monitor, &a))
807         return FALSE;
808     
809     if (sa->sa_family == AF_INET && s->fd_legacy_unicast_ipv4 >= 0) {
810         struct sockaddr_in lsa;
811         socklen_t l = sizeof(lsa);
812         
813         if (getsockname(s->fd_legacy_unicast_ipv4, &lsa, &l) != 0)
814             avahi_log_warn("getsockname(): %s", strerror(errno));
815         else
816             return lsa.sin_port == ((struct sockaddr_in*) sa)->sin_port;
817
818     }
819
820     if (sa->sa_family == AF_INET6 && s->fd_legacy_unicast_ipv6 >= 0) {
821         struct sockaddr_in6 lsa;
822         socklen_t l = sizeof(lsa);
823
824         if (getsockname(s->fd_legacy_unicast_ipv6, &lsa, &l) != 0)
825             avahi_log_warn("getsockname(): %s", strerror(errno));
826         else
827             return lsa.sin6_port == ((struct sockaddr_in6*) sa)->sin6_port;
828     }
829
830     return FALSE;
831 }
832
833 static void dispatch_packet(AvahiServer *s, AvahiDnsPacket *p, const struct sockaddr *sa, gint iface, gint ttl) {
834     AvahiInterface *i;
835     AvahiAddress a;
836     guint16 port;
837     
838     g_assert(s);
839     g_assert(p);
840     g_assert(sa);
841     g_assert(iface > 0);
842
843     if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, sa->sa_family)) ||
844         !avahi_interface_relevant(i)) {
845         avahi_log_warn("Recieved packet from invalid interface.");
846         return;
847     }
848
849 /*     avahi_log_debug("new packet recieved on interface '%s.%i'.", i->hardware->name, i->protocol); */
850
851     port = avahi_port_from_sockaddr(sa);
852     avahi_address_from_sockaddr(sa, &a);
853     
854     if (avahi_address_is_ipv4_in_ipv6(&a))
855         /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
856         return;
857
858     if (originates_from_local_legacy_unicast_socket(s, sa))
859         /* This originates from our local reflector, so let's ignore it */
860         return;
861
862     if (avahi_dns_packet_check_valid(p) < 0) {
863         avahi_log_warn("Recieved invalid packet.");
864         return;
865     }
866
867     if (avahi_dns_packet_is_query(p)) {
868         gboolean legacy_unicast = FALSE;
869
870         if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT) != 0) {
871             avahi_log_warn("Invalid query packet.");
872             return;
873         }
874
875         if (port != AVAHI_MDNS_PORT) {
876             /* Legacy Unicast */
877
878             if ((avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) != 0 ||
879                  avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0)) {
880                 avahi_log_warn("Invalid legacy unicast query packet.");
881                 return;
882             }
883         
884             legacy_unicast = TRUE;
885         }
886
887         if (legacy_unicast)
888             reflect_legacy_unicast_query_packet(s, p, i, &a, port);
889         
890         handle_query_packet(s, p, i, &a, port, legacy_unicast);
891         
892 /*         avahi_log_debug("Handled query"); */
893     } else {
894
895         if (port != AVAHI_MDNS_PORT) {
896             avahi_log_warn("Recieved repsonse with invalid source port %u on interface '%s.%i'", port, i->hardware->name, i->protocol);
897             return;
898         }
899
900         if (ttl != 255) {
901             avahi_log_warn("Recieved response with invalid TTL %u on interface '%s.%i'.", ttl, i->hardware->name, i->protocol);
902             if (s->config.check_response_ttl)
903                 return;
904         }
905
906         if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT) != 0 ||
907             avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 ||
908             avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0) {
909             avahi_log_warn("Invalid response packet.");
910             return;
911         }
912
913         handle_response_packet(s, p, i, &a);
914 /*         avahi_log_debug("Handled response"); */
915     }
916 }
917
918 static void dispatch_legacy_unicast_packet(AvahiServer *s, AvahiDnsPacket *p, const struct sockaddr *sa, gint iface, gint ttl) {
919     AvahiInterface *i, *j;
920     AvahiAddress a;
921     guint16 port;
922     AvahiLegacyUnicastReflectSlot *slot;
923     
924     g_assert(s);
925     g_assert(p);
926     g_assert(sa);
927     g_assert(iface > 0);
928
929     if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, sa->sa_family)) ||
930         !avahi_interface_relevant(i)) {
931         avahi_log_warn("Recieved packet from invalid interface.");
932         return;
933     }
934
935 /*     avahi_log_debug("new legacy unicast packet recieved on interface '%s.%i'.", i->hardware->name, i->protocol); */
936
937     port = avahi_port_from_sockaddr(sa);
938     avahi_address_from_sockaddr(sa, &a);
939     
940     if (avahi_address_is_ipv4_in_ipv6(&a))
941         /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
942         return;
943
944     if (avahi_dns_packet_check_valid(p) < 0 || avahi_dns_packet_is_query(p)) {
945         avahi_log_warn("Recieved invalid packet.");
946         return;
947     }
948
949     if (!(slot = find_slot(s, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID)))) {
950         avahi_log_warn("Recieved legacy unicast response with unknown id");
951         return;
952     }
953
954     if (!(j = avahi_interface_monitor_get_interface(s->monitor, slot->interface, slot->address.family)) ||
955         !avahi_interface_relevant(j))
956         return;
957
958     /* Patch the original ID into this response */
959     avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
960
961     /* Forward the response to the correct client */
962     avahi_interface_send_packet_unicast(j, p, &slot->address, slot->port);
963
964     /* Undo changes to packet */
965     avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
966 }
967
968 static void work(AvahiServer *s) {
969     struct sockaddr_in6 sa6;
970     struct sockaddr_in sa;
971     AvahiDnsPacket *p;
972     gint iface = 0;
973     guint8 ttl;
974         
975     g_assert(s);
976
977     if (s->fd_ipv4 >= 0 && (s->pollfd_ipv4.revents & G_IO_IN)) {
978         if ((p = avahi_recv_dns_packet_ipv4(s->fd_ipv4, &sa, &iface, &ttl))) {
979             dispatch_packet(s, p, (struct sockaddr*) &sa, iface, ttl);
980             avahi_dns_packet_free(p);
981         }
982     }
983
984     if (s->fd_ipv6 >= 0 && (s->pollfd_ipv6.revents & G_IO_IN)) {
985         if ((p = avahi_recv_dns_packet_ipv6(s->fd_ipv6, &sa6, &iface, &ttl))) {
986             dispatch_packet(s, p, (struct sockaddr*) &sa6, iface, ttl);
987             avahi_dns_packet_free(p);
988         }
989     }
990
991     if (s->fd_legacy_unicast_ipv4 >= 0 && (s->pollfd_legacy_unicast_ipv4.revents & G_IO_IN)) {
992         if ((p = avahi_recv_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, &sa, &iface, &ttl))) {
993             dispatch_legacy_unicast_packet(s, p, (struct sockaddr*) &sa, iface, ttl);
994             avahi_dns_packet_free(p);
995         }
996     }
997
998     if (s->fd_legacy_unicast_ipv6 >= 0 && (s->pollfd_legacy_unicast_ipv6.revents & G_IO_IN)) {
999         if ((p = avahi_recv_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, &sa6, &iface, &ttl))) {
1000             dispatch_legacy_unicast_packet(s, p, (struct sockaddr*) &sa6, iface, ttl);
1001             avahi_dns_packet_free(p);
1002         }
1003     }
1004 }
1005
1006 static gboolean prepare_func(GSource *source, gint *timeout) {
1007     g_assert(source);
1008     g_assert(timeout);
1009     
1010     *timeout = -1;
1011     return FALSE;
1012 }
1013
1014 static gboolean check_func(GSource *source) {
1015     AvahiServer* s;
1016     gushort revents = 0;
1017     
1018     g_assert(source);
1019
1020     s = *((AvahiServer**) (((guint8*) source) + sizeof(GSource)));
1021     g_assert(s);
1022
1023     if (s->fd_ipv4 >= 0)
1024         revents |= s->pollfd_ipv4.revents;
1025     if (s->fd_ipv6 >= 0)
1026         revents |= s->pollfd_ipv6.revents;
1027     if (s->fd_legacy_unicast_ipv4 >= 0)
1028         revents |= s->pollfd_legacy_unicast_ipv4.revents;
1029     if (s->fd_legacy_unicast_ipv6 >= 0)
1030         revents |= s->pollfd_legacy_unicast_ipv6.revents;
1031     
1032     return !!(revents & (G_IO_IN | G_IO_HUP | G_IO_ERR));
1033 }
1034
1035 static gboolean dispatch_func(GSource *source, GSourceFunc callback, gpointer user_data) {
1036     AvahiServer* s;
1037     g_assert(source);
1038
1039     s = *((AvahiServer**) (((guint8*) source) + sizeof(GSource)));
1040     g_assert(s);
1041
1042     work(s);
1043     cleanup_dead(s);
1044
1045     return TRUE;
1046 }
1047
1048 static void server_set_state(AvahiServer *s, AvahiServerState state) {
1049     g_assert(s);
1050
1051     if (s->state == state)
1052         return;
1053     
1054     s->state = state;
1055
1056     if (s->callback)
1057         s->callback(s, state, s->userdata);
1058 }
1059
1060 static void withdraw_host_rrs(AvahiServer *s) {
1061     g_assert(s);
1062
1063     if (s->hinfo_entry_group) {
1064         avahi_entry_group_free(s->hinfo_entry_group);
1065         s->hinfo_entry_group = NULL;
1066     }
1067
1068     if (s->browse_domain_entry_group) {
1069         avahi_entry_group_free(s->browse_domain_entry_group);
1070         s->browse_domain_entry_group = NULL;
1071     }
1072
1073     avahi_update_host_rrs(s->monitor, TRUE);
1074     s->n_host_rr_pending = 0;
1075 }
1076
1077 void avahi_server_decrease_host_rr_pending(AvahiServer *s) {
1078     g_assert(s);
1079     
1080     g_assert(s->n_host_rr_pending > 0);
1081
1082     if (--s->n_host_rr_pending == 0)
1083         server_set_state(s, AVAHI_SERVER_RUNNING);
1084 }
1085
1086 void avahi_server_increase_host_rr_pending(AvahiServer *s) {
1087     g_assert(s);
1088
1089     s->n_host_rr_pending ++;
1090 }
1091
1092 void avahi_host_rr_entry_group_callback(AvahiServer *s, AvahiEntryGroup *g, AvahiEntryGroupState state, void *userdata) {
1093     g_assert(s);
1094     g_assert(g);
1095
1096     if (state == AVAHI_ENTRY_GROUP_REGISTERING &&
1097         s->state == AVAHI_SERVER_REGISTERING)
1098         avahi_server_increase_host_rr_pending(s);
1099     else if (state == AVAHI_ENTRY_GROUP_COLLISION &&
1100         (s->state == AVAHI_SERVER_REGISTERING || s->state == AVAHI_SERVER_RUNNING)) {
1101         withdraw_host_rrs(s);
1102         server_set_state(s, AVAHI_SERVER_COLLISION);
1103     } else if (state == AVAHI_ENTRY_GROUP_ESTABLISHED &&
1104                s->state == AVAHI_SERVER_REGISTERING)
1105         avahi_server_decrease_host_rr_pending(s);
1106 }
1107
1108 static void register_hinfo(AvahiServer *s) {
1109     struct utsname utsname;
1110     AvahiRecord *r;
1111     
1112     g_assert(s);
1113     
1114     if (!s->config.publish_hinfo || s->hinfo_entry_group)
1115         return;
1116     
1117     s->hinfo_entry_group = avahi_entry_group_new(s, avahi_host_rr_entry_group_callback, NULL);
1118     
1119     /* Fill in HINFO rr */
1120     r = avahi_record_new_full(s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_HINFO);
1121     uname(&utsname);
1122     r->data.hinfo.cpu = g_strdup(g_strup(utsname.machine));
1123     r->data.hinfo.os = g_strdup(g_strup(utsname.sysname));
1124     avahi_server_add(s, s->hinfo_entry_group, 0, AF_UNSPEC, AVAHI_ENTRY_UNIQUE, r);
1125     avahi_record_unref(r);
1126
1127     avahi_entry_group_commit(s->hinfo_entry_group);
1128 }
1129
1130 static void register_localhost(AvahiServer *s) {
1131     AvahiAddress a;
1132     g_assert(s);
1133     
1134     /* Add localhost entries */
1135     avahi_address_parse("127.0.0.1", AF_INET, &a);
1136     avahi_server_add_address(s, NULL, 0, AF_UNSPEC, AVAHI_ENTRY_NOPROBE|AVAHI_ENTRY_NOANNOUNCE, "localhost", &a);
1137
1138     avahi_address_parse("::1", AF_INET6, &a);
1139     avahi_server_add_address(s, NULL, 0, AF_UNSPEC, AVAHI_ENTRY_NOPROBE|AVAHI_ENTRY_NOANNOUNCE, "ip6-localhost", &a);
1140 }
1141
1142 static void register_browse_domain(AvahiServer *s) {
1143     g_assert(s);
1144
1145     if (!s->config.publish_domain || s->browse_domain_entry_group)
1146         return;
1147
1148     s->browse_domain_entry_group = avahi_entry_group_new(s, NULL, NULL);
1149     avahi_server_add_ptr(s, s->browse_domain_entry_group, 0, AF_UNSPEC, 0, "_browse._dns-sd._udp.local", s->domain_name);
1150     avahi_entry_group_commit(s->browse_domain_entry_group);
1151 }
1152
1153 static void register_stuff(AvahiServer *s) {
1154     g_assert(s);
1155
1156     server_set_state(s, AVAHI_SERVER_REGISTERING);
1157     register_hinfo(s);
1158     register_browse_domain(s);
1159     avahi_update_host_rrs(s->monitor, FALSE);
1160
1161     if (s->n_host_rr_pending == 0)
1162         server_set_state(s, AVAHI_SERVER_RUNNING);
1163 }
1164
1165 static void update_fqdn(AvahiServer *s) {
1166     g_assert(s);
1167     
1168     g_assert(s->host_name);
1169     g_assert(s->domain_name);
1170
1171     g_free(s->host_name_fqdn);
1172     s->host_name_fqdn = g_strdup_printf("%s.%s", s->host_name, s->domain_name);
1173 }
1174
1175 static void register_time_event_callback(AvahiTimeEvent *e, gpointer userdata) {
1176     AvahiServer *s = userdata;
1177     
1178     g_assert(e);
1179     g_assert(s);
1180
1181     g_assert(e == s->register_time_event);
1182     avahi_time_event_queue_remove(s->time_event_queue, s->register_time_event);
1183     s->register_time_event = NULL;
1184
1185     if (s->state == AVAHI_SERVER_SLEEPING)
1186         register_stuff(s);
1187 }
1188
1189 static void delayed_register_stuff(AvahiServer *s) {
1190     GTimeVal tv;
1191     
1192     g_assert(s);
1193
1194     avahi_elapse_time(&tv, AVAHI_HOST_RR_HOLDOFF_MSEC, 0);
1195
1196     if (s->register_time_event)
1197         avahi_time_event_queue_update(s->time_event_queue, s->register_time_event, &tv);
1198     else
1199         s->register_time_event = avahi_time_event_queue_add(s->time_event_queue, &tv, register_time_event_callback, s);
1200 }
1201
1202 gint avahi_server_set_host_name(AvahiServer *s, const gchar *host_name) {
1203     g_assert(s);
1204     g_assert(host_name);
1205
1206     server_set_state(s, AVAHI_SERVER_SLEEPING);
1207     withdraw_host_rrs(s);
1208
1209     g_free(s->host_name);
1210     s->host_name = host_name ? avahi_normalize_name(host_name) : avahi_get_host_name();
1211     s->host_name[strcspn(s->host_name, ".")] = 0;
1212     update_fqdn(s);
1213
1214     delayed_register_stuff(s);
1215     return 0;
1216 }
1217
1218 gint avahi_server_set_domain_name(AvahiServer *s, const gchar *domain_name) {
1219     g_assert(s);
1220     g_assert(domain_name);
1221
1222     server_set_state(s, AVAHI_SERVER_SLEEPING);
1223     withdraw_host_rrs(s);
1224
1225     g_free(s->domain_name);
1226     s->domain_name = domain_name ? avahi_normalize_name(domain_name) : g_strdup("local.");
1227     update_fqdn(s);
1228
1229     delayed_register_stuff(s);
1230     return 0;
1231 }
1232
1233
1234 static void prepare_pollfd(AvahiServer *s, GPollFD *pollfd, gint fd) {
1235     g_assert(s);
1236     g_assert(pollfd);
1237     g_assert(fd >= 0);
1238
1239     memset(pollfd, 0, sizeof(GPollFD));
1240     pollfd->fd = fd;
1241     pollfd->events = G_IO_IN|G_IO_ERR|G_IO_HUP;
1242     g_source_add_poll(s->source, pollfd);
1243 }
1244
1245 AvahiServer *avahi_server_new(GMainContext *c, const AvahiServerConfig *sc, AvahiServerCallback callback, gpointer userdata) {
1246     AvahiServer *s;
1247     
1248     static GSourceFuncs source_funcs = {
1249         prepare_func,
1250         check_func,
1251         dispatch_func,
1252         NULL,
1253         NULL,
1254         NULL
1255     };
1256
1257     s = g_new(AvahiServer, 1);
1258     s->n_host_rr_pending = 0;
1259     s->need_entry_cleanup = s->need_group_cleanup = s->need_browser_cleanup = FALSE;
1260
1261     if (sc)
1262         avahi_server_config_copy(&s->config, sc);
1263     else
1264         avahi_server_config_init(&s->config);
1265     
1266     s->fd_ipv4 = s->config.use_ipv4 ? avahi_open_socket_ipv4() : -1;
1267     s->fd_ipv6 = s->config.use_ipv6 ? avahi_open_socket_ipv6() : -1;
1268     
1269     if (s->fd_ipv6 < 0 && s->fd_ipv4 < 0) {
1270         g_critical("Selected neither IPv6 nor IPv4 support, aborting.\n");
1271         avahi_server_config_free(&s->config);
1272         g_free(s);
1273         return NULL;
1274     }
1275
1276     if (s->fd_ipv4 < 0 && s->config.use_ipv4)
1277         avahi_log_notice("Failed to create IPv4 socket, proceeding in IPv6 only mode");
1278     else if (s->fd_ipv6 < 0 && s->config.use_ipv6)
1279         avahi_log_notice("Failed to create IPv6 socket, proceeding in IPv4 only mode");
1280
1281     s->fd_legacy_unicast_ipv4 = s->fd_ipv4 >= 0 && s->config.enable_reflector ? avahi_open_legacy_unicast_socket_ipv4() : -1;
1282     s->fd_legacy_unicast_ipv6 = s->fd_ipv6 >= 0 && s->config.enable_reflector ? avahi_open_legacy_unicast_socket_ipv6() : -1;
1283
1284     g_main_context_ref(s->context = (c ? c : g_main_context_default()));
1285
1286     /* Prepare IO source registration */
1287     s->source = g_source_new(&source_funcs, sizeof(GSource) + sizeof(AvahiServer*));
1288     *((AvahiServer**) (((guint8*) s->source) + sizeof(GSource))) = s;
1289
1290     if (s->fd_ipv4 >= 0)
1291         prepare_pollfd(s, &s->pollfd_ipv4, s->fd_ipv4);
1292     if (s->fd_ipv6 >= 0)
1293         prepare_pollfd(s, &s->pollfd_ipv6, s->fd_ipv6);
1294     if (s->fd_legacy_unicast_ipv4 >= 0)
1295         prepare_pollfd(s, &s->pollfd_legacy_unicast_ipv4, s->fd_legacy_unicast_ipv4);
1296     if (s->fd_legacy_unicast_ipv6 >= 0)
1297         prepare_pollfd(s, &s->pollfd_legacy_unicast_ipv6, s->fd_legacy_unicast_ipv6);
1298     
1299     g_source_attach(s->source, s->context);
1300     
1301     s->callback = callback;
1302     s->userdata = userdata;
1303     
1304     AVAHI_LLIST_HEAD_INIT(AvahiEntry, s->entries);
1305     s->entries_by_key = g_hash_table_new((GHashFunc) avahi_key_hash, (GEqualFunc) avahi_key_equal);
1306     AVAHI_LLIST_HEAD_INIT(AvahiGroup, s->groups);
1307
1308     AVAHI_LLIST_HEAD_INIT(AvahiRecordBrowser, s->record_browsers);
1309     s->record_browser_hashtable = g_hash_table_new((GHashFunc) avahi_key_hash, (GEqualFunc) avahi_key_equal);
1310     AVAHI_LLIST_HEAD_INIT(AvahiHostNameResolver, s->host_name_resolvers);
1311     AVAHI_LLIST_HEAD_INIT(AvahiAddressResolver, s->address_resolvers);
1312     AVAHI_LLIST_HEAD_INIT(AvahiDomainBrowser, s->domain_browsers);
1313     AVAHI_LLIST_HEAD_INIT(AvahiServiceTypeBrowser, s->service_type_browsers);
1314     AVAHI_LLIST_HEAD_INIT(AvahiServiceBrowser, s->service_browsers);
1315     AVAHI_LLIST_HEAD_INIT(AvahiServiceResolver, s->service_resolvers);
1316
1317     s->legacy_unicast_reflect_slots = NULL;
1318     s->legacy_unicast_reflect_id = 0;
1319     
1320     /* Get host name */
1321     s->host_name = s->config.host_name ? avahi_normalize_name(s->config.host_name) : avahi_get_host_name();
1322     s->host_name[strcspn(s->host_name, ".")] = 0;
1323     s->domain_name = s->config.domain_name ? avahi_normalize_name(s->config.domain_name) : g_strdup("local.");
1324     s->host_name_fqdn = NULL;
1325     update_fqdn(s);
1326
1327     s->record_list = avahi_record_list_new();
1328
1329     s->time_event_queue = avahi_time_event_queue_new(s->context, G_PRIORITY_DEFAULT+10); /* Slightly less priority than the FDs */
1330     s->register_time_event = NULL;
1331     
1332     s->state = AVAHI_SERVER_INVALID;
1333
1334     s->monitor = avahi_interface_monitor_new(s);
1335     avahi_interface_monitor_sync(s->monitor);
1336
1337     register_localhost(s);
1338
1339     s->hinfo_entry_group = NULL;
1340     s->browse_domain_entry_group = NULL;
1341     register_stuff(s);
1342     
1343     return s;
1344 }
1345
1346 void avahi_server_free(AvahiServer* s) {
1347     g_assert(s);
1348
1349     while(s->entries)
1350         free_entry(s, s->entries);
1351
1352     avahi_interface_monitor_free(s->monitor);
1353
1354     while (s->groups)
1355         free_group(s, s->groups);
1356
1357     free_slots(s);
1358     
1359     while (s->host_name_resolvers)
1360         avahi_host_name_resolver_free(s->host_name_resolvers);
1361     while (s->address_resolvers)
1362         avahi_address_resolver_free(s->address_resolvers);
1363     while (s->domain_browsers)
1364         avahi_domain_browser_free(s->domain_browsers);
1365     while (s->service_type_browsers)
1366         avahi_service_type_browser_free(s->service_type_browsers);
1367     while (s->service_browsers)
1368         avahi_service_browser_free(s->service_browsers);
1369     while (s->service_resolvers)
1370         avahi_service_resolver_free(s->service_resolvers);
1371     while (s->record_browsers)
1372         avahi_record_browser_destroy(s->record_browsers);
1373     g_hash_table_destroy(s->record_browser_hashtable);
1374
1375     g_hash_table_destroy(s->entries_by_key);
1376
1377     if (s->register_time_event)
1378         avahi_time_event_queue_remove(s->time_event_queue, s->register_time_event);
1379     avahi_time_event_queue_free(s->time_event_queue);
1380
1381     avahi_record_list_free(s->record_list);
1382     
1383     if (s->fd_ipv4 >= 0)
1384         close(s->fd_ipv4);
1385     if (s->fd_ipv6 >= 0)
1386         close(s->fd_ipv6);
1387     if (s->fd_legacy_unicast_ipv4 >= 0)
1388         close(s->fd_legacy_unicast_ipv4);
1389     if (s->fd_legacy_unicast_ipv6 >= 0)
1390         close(s->fd_legacy_unicast_ipv6);
1391
1392     g_free(s->host_name);
1393     g_free(s->domain_name);
1394     g_free(s->host_name_fqdn);
1395
1396     g_source_destroy(s->source);
1397     g_source_unref(s->source);
1398     g_main_context_unref(s->context);
1399
1400     avahi_server_config_free(&s->config);
1401
1402     g_free(s);
1403 }
1404
1405 static gint check_record_conflict(AvahiServer *s, gint interface, guchar protocol, AvahiRecord *r, AvahiEntryFlags flags) {
1406     AvahiEntry *e;
1407     
1408     g_assert(s);
1409     g_assert(r);
1410
1411     for (e = g_hash_table_lookup(s->entries_by_key, r->key); e; e = e->by_key_next) {
1412         if (e->dead)
1413             continue;
1414
1415         if (!(flags & AVAHI_ENTRY_UNIQUE) && !(e->flags & AVAHI_ENTRY_UNIQUE))
1416             continue;
1417         
1418         if ((flags & AVAHI_ENTRY_ALLOWMUTIPLE) && (e->flags & AVAHI_ENTRY_ALLOWMUTIPLE) )
1419             continue;
1420
1421         if (interface <= 0 ||
1422             e->interface <= 0 ||
1423             e->interface == interface ||
1424             protocol == AF_UNSPEC ||
1425             e->protocol == AF_UNSPEC ||
1426             e->protocol == protocol)
1427
1428             return -1;
1429
1430     }
1431
1432     return 0;
1433 }
1434
1435 gint avahi_server_add(
1436     AvahiServer *s,
1437     AvahiEntryGroup *g,
1438     gint interface,
1439     guchar protocol,
1440     AvahiEntryFlags flags,
1441     AvahiRecord *r) {
1442     
1443     AvahiEntry *e, *t;
1444     g_assert(s);
1445     g_assert(r);
1446
1447     g_assert(r->key->type != AVAHI_DNS_TYPE_ANY);
1448
1449     if (check_record_conflict(s, interface, protocol, r, flags) < 0)
1450         return -1;
1451
1452     e = g_new(AvahiEntry, 1);
1453     e->server = s;
1454     e->record = avahi_record_ref(r);
1455     e->group = g;
1456     e->interface = interface;
1457     e->protocol = protocol;
1458     e->flags = flags;
1459     e->dead = FALSE;
1460
1461     AVAHI_LLIST_HEAD_INIT(AvahiAnnouncement, e->announcements);
1462
1463     AVAHI_LLIST_PREPEND(AvahiEntry, entries, s->entries, e);
1464
1465     /* Insert into hash table indexed by name */
1466     t = g_hash_table_lookup(s->entries_by_key, e->record->key);
1467     AVAHI_LLIST_PREPEND(AvahiEntry, by_key, t, e);
1468     g_hash_table_replace(s->entries_by_key, e->record->key, t);
1469
1470     /* Insert into group list */
1471     if (g)
1472         AVAHI_LLIST_PREPEND(AvahiEntry, by_group, g->entries, e); 
1473
1474     avahi_announce_entry(s, e);
1475
1476     return 0;
1477 }
1478
1479 const AvahiRecord *avahi_server_iterate(AvahiServer *s, AvahiEntryGroup *g, void **state) {
1480     AvahiEntry **e = (AvahiEntry**) state;
1481     g_assert(s);
1482     g_assert(e);
1483
1484     if (!*e)
1485         *e = g ? g->entries : s->entries;
1486     
1487     while (*e && (*e)->dead)
1488         *e = g ? (*e)->by_group_next : (*e)->entries_next;
1489         
1490     if (!*e)
1491         return NULL;
1492
1493     return avahi_record_ref((*e)->record);
1494 }
1495
1496 void avahi_server_dump(AvahiServer *s, FILE *f) {
1497     AvahiEntry *e;
1498     g_assert(s);
1499     g_assert(f);
1500
1501     fprintf(f, "\n;;; ZONE DUMP FOLLOWS ;;;\n");
1502
1503     for (e = s->entries; e; e = e->entries_next) {
1504         gchar *t;
1505
1506         if (e->dead)
1507             continue;
1508         
1509         t = avahi_record_to_string(e->record);
1510         fprintf(f, "%s ; iface=%i proto=%i\n", t, e->interface, e->protocol);
1511         g_free(t);
1512     }
1513
1514     avahi_dump_caches(s->monitor, f);
1515 }
1516
1517 gint avahi_server_add_ptr(
1518     AvahiServer *s,
1519     AvahiEntryGroup *g,
1520     gint interface,
1521     guchar protocol,
1522     AvahiEntryFlags flags,
1523     const gchar *name,
1524     const gchar *dest) {
1525
1526     AvahiRecord *r;
1527     gint ret;
1528
1529     g_assert(dest);
1530
1531     r = avahi_record_new_full(name ? name : s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR);
1532     r->data.ptr.name = avahi_normalize_name(dest);
1533     ret = avahi_server_add(s, g, interface, protocol, flags, r);
1534     avahi_record_unref(r);
1535     return ret;
1536 }
1537
1538 gint avahi_server_add_address(
1539     AvahiServer *s,
1540     AvahiEntryGroup *g,
1541     gint interface,
1542     guchar protocol,
1543     AvahiEntryFlags flags,
1544     const gchar *name,
1545     AvahiAddress *a) {
1546
1547     gchar *n = NULL;
1548     gint ret = 0;
1549     g_assert(s);
1550     g_assert(a);
1551
1552     name = name ? (n = avahi_normalize_name(name)) : s->host_name_fqdn;
1553     
1554     if (a->family == AF_INET) {
1555         gchar *reverse;
1556         AvahiRecord  *r;
1557
1558         r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A);
1559         r->data.a.address = a->data.ipv4;
1560         ret = avahi_server_add(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE | AVAHI_ENTRY_ALLOWMUTIPLE, r);
1561         avahi_record_unref(r);
1562         
1563         reverse = avahi_reverse_lookup_name_ipv4(&a->data.ipv4);
1564         ret |= avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, reverse, name);
1565         g_free(reverse);
1566         
1567     } else {
1568         gchar *reverse;
1569         AvahiRecord *r;
1570             
1571         r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA);
1572         r->data.aaaa.address = a->data.ipv6;
1573         ret = avahi_server_add(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE | AVAHI_ENTRY_ALLOWMUTIPLE, r);
1574         avahi_record_unref(r);
1575
1576         reverse = avahi_reverse_lookup_name_ipv6_arpa(&a->data.ipv6);
1577         ret |= avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, reverse, name);
1578         g_free(reverse);
1579     
1580         reverse = avahi_reverse_lookup_name_ipv6_int(&a->data.ipv6);
1581         ret |= avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, reverse, name);
1582         g_free(reverse);
1583     }
1584     
1585     g_free(n);
1586
1587     return ret;
1588 }
1589
1590 gint avahi_server_add_text_strlst(
1591     AvahiServer *s,
1592     AvahiEntryGroup *g,
1593     gint interface,
1594     guchar protocol,
1595     AvahiEntryFlags flags,
1596     const gchar *name,
1597     AvahiStringList *strlst) {
1598
1599     AvahiRecord *r;
1600     gint ret;
1601     
1602     g_assert(s);
1603     
1604     r = avahi_record_new_full(name ? name : s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_TXT);
1605     r->data.txt.string_list = strlst;
1606     ret = avahi_server_add(s, g, interface, protocol, flags, r);
1607     avahi_record_unref(r);
1608
1609     return ret;
1610 }
1611
1612 gint avahi_server_add_text_va(
1613     AvahiServer *s,
1614     AvahiEntryGroup *g,
1615     gint interface,
1616     guchar protocol,
1617     AvahiEntryFlags flags,
1618     const gchar *name,
1619     va_list va) {
1620     
1621     g_assert(s);
1622
1623     return avahi_server_add_text_strlst(s, g, interface, protocol, flags, name, avahi_string_list_new_va(va));
1624 }
1625
1626 gint avahi_server_add_text(
1627     AvahiServer *s,
1628     AvahiEntryGroup *g,
1629     gint interface,
1630     guchar protocol,
1631     AvahiEntryFlags flags,
1632     const gchar *name,
1633     ...) {
1634
1635     va_list va;
1636     gint ret;
1637     
1638     g_assert(s);
1639
1640     va_start(va, name);
1641     ret = avahi_server_add_text_va(s, g, interface, protocol, flags, name, va);
1642     va_end(va);
1643
1644     return ret;
1645 }
1646
1647 static void escape_service_name(gchar *d, guint size, const gchar *s) {
1648     g_assert(d);
1649     g_assert(size);
1650     g_assert(s);
1651
1652     while (*s && size >= 2) {
1653         if (*s == '.' || *s == '\\') {
1654             if (size < 3)
1655                 break;
1656
1657             *(d++) = '\\';
1658             size--;
1659         }
1660             
1661         *(d++) = *(s++);
1662         size--;
1663     }
1664
1665     g_assert(size > 0);
1666     *(d++) = 0;
1667 }
1668
1669 gint avahi_server_add_service_strlst(
1670     AvahiServer *s,
1671     AvahiEntryGroup *g,
1672     gint interface,
1673     guchar protocol,
1674     const gchar *type,
1675     const gchar *name,
1676     const gchar *domain,
1677     const gchar *host,
1678     guint16 port,
1679     AvahiStringList *strlst) {
1680
1681     gchar ptr_name[256], svc_name[256], ename[64], enum_ptr[256];
1682     AvahiRecord *r;
1683     gint ret = 0;
1684     
1685     g_assert(s);
1686     g_assert(type);
1687     g_assert(name);
1688
1689     escape_service_name(ename, sizeof(ename), name);
1690
1691     if (domain) {
1692         while (domain[0] == '.')
1693             domain++;
1694     } else
1695         domain = s->domain_name;
1696
1697     if (!host)
1698         host = s->host_name_fqdn;
1699
1700     snprintf(ptr_name, sizeof(ptr_name), "%s.%s", type, domain);
1701     snprintf(svc_name, sizeof(svc_name), "%s.%s.%s", ename, type, domain);
1702     
1703     ret = avahi_server_add_ptr(s, g, interface, protocol, AVAHI_ENTRY_NULL, ptr_name, svc_name);
1704
1705     r = avahi_record_new_full(svc_name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV);
1706     r->data.srv.priority = 0;
1707     r->data.srv.weight = 0;
1708     r->data.srv.port = port;
1709     r->data.srv.name = avahi_normalize_name(host);
1710     ret |= avahi_server_add(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE, r);
1711     avahi_record_unref(r);
1712
1713     ret |= avahi_server_add_text_strlst(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE, svc_name, strlst);
1714
1715     snprintf(enum_ptr, sizeof(enum_ptr), "_services._dns-sd._udp.%s", domain);
1716     ret |=avahi_server_add_ptr(s, g, interface, protocol, AVAHI_ENTRY_NULL, enum_ptr, ptr_name);
1717
1718     return ret;
1719 }
1720
1721 gint avahi_server_add_service_va(
1722     AvahiServer *s,
1723     AvahiEntryGroup *g,
1724     gint interface,
1725     guchar protocol,
1726     const gchar *type,
1727     const gchar *name,
1728     const gchar *domain,
1729     const gchar *host,
1730     guint16 port,
1731     va_list va){
1732
1733     g_assert(s);
1734     g_assert(type);
1735     g_assert(name);
1736
1737     return avahi_server_add_service_strlst(s, g, interface, protocol, type, name, domain, host, port, avahi_string_list_new_va(va));
1738 }
1739
1740 gint avahi_server_add_service(
1741     AvahiServer *s,
1742     AvahiEntryGroup *g,
1743     gint interface,
1744     guchar protocol,
1745     const gchar *type,
1746     const gchar *name,
1747     const gchar *domain,
1748     const gchar *host,
1749     guint16 port,
1750     ... ){
1751
1752     va_list va;
1753     gint ret;
1754     
1755     g_assert(s);
1756     g_assert(type);
1757     g_assert(name);
1758
1759     va_start(va, port);
1760     ret = avahi_server_add_service_va(s, g, interface, protocol, type, name, domain, host, port, va);
1761     va_end(va);
1762     return ret;
1763 }
1764
1765 static void post_query_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, gpointer userdata) {
1766     AvahiKey *k = userdata;
1767
1768     g_assert(m);
1769     g_assert(i);
1770     g_assert(k);
1771
1772     avahi_interface_post_query(i, k, FALSE);
1773 }
1774
1775 void avahi_server_post_query(AvahiServer *s, gint interface, guchar protocol, AvahiKey *key) {
1776     g_assert(s);
1777     g_assert(key);
1778
1779     avahi_interface_monitor_walk(s->monitor, interface, protocol, post_query_callback, key);
1780 }
1781
1782 void avahi_entry_group_change_state(AvahiEntryGroup *g, AvahiEntryGroupState state) {
1783     g_assert(g);
1784
1785     if (g->state == state)
1786         return;
1787
1788     g->state = state;
1789     
1790     if (g->callback) {
1791         g->callback(g->server, g, state, g->userdata);
1792         return;
1793     }
1794 }
1795
1796 AvahiEntryGroup *avahi_entry_group_new(AvahiServer *s, AvahiEntryGroupCallback callback, gpointer userdata) {
1797     AvahiEntryGroup *g;
1798     
1799     g_assert(s);
1800
1801     g = g_new(AvahiEntryGroup, 1);
1802     g->server = s;
1803     g->callback = callback;
1804     g->userdata = userdata;
1805     g->dead = FALSE;
1806     g->state = AVAHI_ENTRY_GROUP_UNCOMMITED;
1807     g->n_probing = 0;
1808     AVAHI_LLIST_HEAD_INIT(AvahiEntry, g->entries);
1809
1810     AVAHI_LLIST_PREPEND(AvahiEntryGroup, groups, s->groups, g);
1811     return g;
1812 }
1813
1814 void avahi_entry_group_free(AvahiEntryGroup *g) {
1815     AvahiEntry *e;
1816     
1817     g_assert(g);
1818     g_assert(g->server);
1819
1820     for (e = g->entries; e; e = e->by_group_next) {
1821         avahi_goodbye_entry(g->server, e, TRUE);
1822         e->dead = TRUE;
1823     }
1824
1825     g->dead = TRUE;
1826     
1827     g->server->need_group_cleanup = TRUE;
1828     g->server->need_entry_cleanup = TRUE;
1829 }
1830
1831 gint avahi_entry_group_commit(AvahiEntryGroup *g) {
1832     g_assert(g);
1833     g_assert(!g->dead);
1834
1835     if (g->state != AVAHI_ENTRY_GROUP_UNCOMMITED)
1836         return -1;
1837
1838     avahi_entry_group_change_state(g, AVAHI_ENTRY_GROUP_REGISTERING);
1839     avahi_announce_group(g->server, g);
1840     avahi_entry_group_check_probed(g, FALSE);
1841
1842     return 0;
1843 }
1844
1845 gboolean avahi_entry_commited(AvahiEntry *e) {
1846     g_assert(e);
1847     g_assert(!e->dead);
1848
1849     return !e->group ||
1850         e->group->state == AVAHI_ENTRY_GROUP_REGISTERING ||
1851         e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED;
1852 }
1853
1854 AvahiEntryGroupState avahi_entry_group_get_state(AvahiEntryGroup *g) {
1855     g_assert(g);
1856     g_assert(!g->dead);
1857
1858     return g->state;
1859 }
1860
1861 void avahi_entry_group_set_data(AvahiEntryGroup *g, gpointer userdata) {
1862     g_assert(g);
1863
1864     g->userdata = userdata;
1865 }
1866
1867 gpointer avahi_entry_group_get_data(AvahiEntryGroup *g) {
1868     g_assert(g);
1869
1870     return g->userdata;
1871 }
1872
1873 const gchar* avahi_server_get_domain_name(AvahiServer *s) {
1874     g_assert(s);
1875
1876     return s->domain_name;
1877 }
1878
1879 const gchar* avahi_server_get_host_name(AvahiServer *s) {
1880     g_assert(s);
1881
1882     return s->host_name;
1883 }
1884
1885 const gchar* avahi_server_get_host_name_fqdn(AvahiServer *s) {
1886     g_assert(s);
1887
1888     return s->host_name_fqdn;
1889 }
1890
1891 gpointer avahi_server_get_data(AvahiServer *s) {
1892     g_assert(s);
1893
1894     return s->userdata;
1895 }
1896
1897 void avahi_server_set_data(AvahiServer *s, gpointer userdata) {
1898     g_assert(s);
1899
1900     s->userdata = userdata;
1901 }
1902
1903 AvahiServerState avahi_server_get_state(AvahiServer *s) {
1904     g_assert(s);
1905
1906     return s->state;
1907 }
1908
1909 AvahiServerConfig* avahi_server_config_init(AvahiServerConfig *c) {
1910     g_assert(c);
1911
1912     memset(c, 0, sizeof(AvahiServerConfig));
1913     c->use_ipv6 = TRUE;
1914     c->use_ipv4 = TRUE;
1915     c->host_name = NULL;
1916     c->domain_name = NULL;
1917     c->check_response_ttl = TRUE;
1918     c->publish_hinfo = TRUE;
1919     c->publish_addresses = TRUE;
1920     c->publish_workstation = TRUE;
1921     c->publish_domain = TRUE;
1922     c->use_iff_running = FALSE;
1923     c->enable_reflector = FALSE;
1924     c->reflect_ipv = FALSE;
1925     
1926     return c;
1927 }
1928
1929 void avahi_server_config_free(AvahiServerConfig *c) {
1930     g_assert(c);
1931
1932     g_free(c->host_name);
1933     g_free(c->domain_name);
1934 }
1935
1936 AvahiServerConfig* avahi_server_config_copy(AvahiServerConfig *ret, const AvahiServerConfig *c) {
1937     g_assert(ret);
1938     g_assert(c);
1939
1940     *ret = *c;
1941
1942     ret->host_name = g_strdup(c->host_name);
1943     ret->domain_name = g_strdup(c->domain_name);
1944
1945     return ret;
1946 }