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