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