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