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