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