]> git.meshlink.io Git - catta/blob - avahi-core/server.c
d03a2848e78f1dfa84e85b5d66df64aa3d94de59
[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     g_main_context_ref(c->context = c ? c : g_main_context_default());
1279
1280     /* Prepare IO source registration */
1281     s->source = g_source_new(&source_funcs, sizeof(GSource) + sizeof(AvahiServer*));
1282     *((AvahiServer**) (((guint8*) s->source) + sizeof(GSource))) = s;
1283
1284     if (s->fd_ipv4 >= 0)
1285         prepare_pollfd(s, &s->pollfd_ipv4, s->fd_ipv4);
1286     if (s->fd_ipv6 >= 0)
1287         prepare_pollfd(s, &s->pollfd_ipv6, s->fd_ipv6);
1288     if (s->fd_legacy_unicast_ipv4 >= 0)
1289         prepare_pollfd(s, &s->pollfd_legacy_unicast_ipv4, s->fd_legacy_unicast_ipv4);
1290     if (s->fd_legacy_unicast_ipv6 >= 0)
1291         prepare_pollfd(s, &s->pollfd_legacy_unicast_ipv6, s->fd_legacy_unicast_ipv6);
1292     
1293     g_source_attach(s->source, s->context);
1294     
1295     s->callback = callback;
1296     s->userdata = userdata;
1297     
1298     AVAHI_LLIST_HEAD_INIT(AvahiEntry, s->entries);
1299     s->entries_by_key = g_hash_table_new((GHashFunc) avahi_key_hash, (GEqualFunc) avahi_key_equal);
1300     AVAHI_LLIST_HEAD_INIT(AvahiGroup, s->groups);
1301
1302     AVAHI_LLIST_HEAD_INIT(AvahiRecordBrowser, s->record_browsers);
1303     s->record_browser_hashtable = g_hash_table_new((GHashFunc) avahi_key_hash, (GEqualFunc) avahi_key_equal);
1304     AVAHI_LLIST_HEAD_INIT(AvahiHostNameResolver, s->host_name_resolvers);
1305     AVAHI_LLIST_HEAD_INIT(AvahiAddressResolver, s->address_resolvers);
1306     AVAHI_LLIST_HEAD_INIT(AvahiDomainBrowser, s->domain_browsers);
1307     AVAHI_LLIST_HEAD_INIT(AvahiServiceTypeBrowser, s->service_type_browsers);
1308     AVAHI_LLIST_HEAD_INIT(AvahiServiceBrowser, s->service_browsers);
1309     AVAHI_LLIST_HEAD_INIT(AvahiServiceResolver, s->service_resolvers);
1310
1311     s->legacy_unicast_reflect_slots = NULL;
1312     s->legacy_unicast_reflect_id = 0;
1313     
1314     /* Get host name */
1315     s->host_name = s->config.host_name ? avahi_normalize_name(s->config.host_name) : avahi_get_host_name();
1316     s->host_name[strcspn(s->host_name, ".")] = 0;
1317     s->domain_name = s->config.domain_name ? avahi_normalize_name(s->config.domain_name) : g_strdup("local.");
1318     s->host_name_fqdn = NULL;
1319     update_fqdn(s);
1320
1321     s->record_list = avahi_record_list_new();
1322
1323     s->time_event_queue = avahi_time_event_queue_new(s->context, G_PRIORITY_DEFAULT+10); /* Slightly less priority than the FDs */
1324     s->register_time_event = NULL;
1325     
1326     s->state = AVAHI_SERVER_INVALID;
1327
1328     s->monitor = avahi_interface_monitor_new(s);
1329     avahi_interface_monitor_sync(s->monitor);
1330
1331     register_localhost(s);
1332
1333     s->hinfo_entry_group = NULL;
1334     s->browse_domain_entry_group = NULL;
1335     register_stuff(s);
1336     
1337     return s;
1338 }
1339
1340 void avahi_server_free(AvahiServer* s) {
1341     g_assert(s);
1342
1343     while(s->entries)
1344         free_entry(s, s->entries);
1345
1346     avahi_interface_monitor_free(s->monitor);
1347
1348     while (s->groups)
1349         free_group(s, s->groups);
1350
1351     free_slots(s);
1352     
1353     while (s->host_name_resolvers)
1354         avahi_host_name_resolver_free(s->host_name_resolvers);
1355     while (s->address_resolvers)
1356         avahi_address_resolver_free(s->address_resolvers);
1357     while (s->domain_browsers)
1358         avahi_domain_browser_free(s->domain_browsers);
1359     while (s->service_type_browsers)
1360         avahi_service_type_browser_free(s->service_type_browsers);
1361     while (s->service_browsers)
1362         avahi_service_browser_free(s->service_browsers);
1363     while (s->service_resolvers)
1364         avahi_service_resolver_free(s->service_resolvers);
1365     while (s->record_browsers)
1366         avahi_record_browser_destroy(s->record_browsers);
1367     g_hash_table_destroy(s->record_browser_hashtable);
1368
1369     g_hash_table_destroy(s->entries_by_key);
1370
1371     if (s->register_time_event)
1372         avahi_time_event_queue_remove(s->time_event_queue, s->register_time_event);
1373     avahi_time_event_queue_free(s->time_event_queue);
1374
1375     avahi_record_list_free(s->record_list);
1376     
1377     if (s->fd_ipv4 >= 0)
1378         close(s->fd_ipv4);
1379     if (s->fd_ipv6 >= 0)
1380         close(s->fd_ipv6);
1381     if (s->fd_legacy_unicast_ipv4 >= 0)
1382         close(s->fd_legacy_unicast_ipv4);
1383     if (s->fd_legacy_unicast_ipv6 >= 0)
1384         close(s->fd_legacy_unicast_ipv6);
1385
1386     g_free(s->host_name);
1387     g_free(s->domain_name);
1388     g_free(s->host_name_fqdn);
1389
1390     g_source_destroy(s->source);
1391     g_source_unref(s->source);
1392     g_main_context_unref(s->context);
1393
1394     avahi_server_config_free(&s->config);
1395
1396     g_free(s);
1397 }
1398
1399 gint avahi_server_add(
1400     AvahiServer *s,
1401     AvahiEntryGroup *g,
1402     gint interface,
1403     guchar protocol,
1404     AvahiEntryFlags flags,
1405     AvahiRecord *r) {
1406     
1407     AvahiEntry *e, *t;
1408     g_assert(s);
1409     g_assert(r);
1410
1411     g_assert(r->key->type != AVAHI_DNS_TYPE_ANY);
1412
1413     e = g_new(AvahiEntry, 1);
1414     e->server = s;
1415     e->record = avahi_record_ref(r);
1416     e->group = g;
1417     e->interface = interface;
1418     e->protocol = protocol;
1419     e->flags = flags;
1420     e->dead = FALSE;
1421
1422     AVAHI_LLIST_HEAD_INIT(AvahiAnnouncement, e->announcements);
1423
1424     AVAHI_LLIST_PREPEND(AvahiEntry, entries, s->entries, e);
1425
1426     /* Insert into hash table indexed by name */
1427     t = g_hash_table_lookup(s->entries_by_key, e->record->key);
1428     AVAHI_LLIST_PREPEND(AvahiEntry, by_key, t, e);
1429     g_hash_table_replace(s->entries_by_key, e->record->key, t);
1430
1431     /* Insert into group list */
1432     if (g)
1433         AVAHI_LLIST_PREPEND(AvahiEntry, by_group, g->entries, e); 
1434
1435     avahi_announce_entry(s, e);
1436
1437     return 0;
1438 }
1439
1440 const AvahiRecord *avahi_server_iterate(AvahiServer *s, AvahiEntryGroup *g, void **state) {
1441     AvahiEntry **e = (AvahiEntry**) state;
1442     g_assert(s);
1443     g_assert(e);
1444
1445     if (!*e)
1446         *e = g ? g->entries : s->entries;
1447     
1448     while (*e && (*e)->dead)
1449         *e = g ? (*e)->by_group_next : (*e)->entries_next;
1450         
1451     if (!*e)
1452         return NULL;
1453
1454     return avahi_record_ref((*e)->record);
1455 }
1456
1457 void avahi_server_dump(AvahiServer *s, FILE *f) {
1458     AvahiEntry *e;
1459     g_assert(s);
1460     g_assert(f);
1461
1462     fprintf(f, "\n;;; ZONE DUMP FOLLOWS ;;;\n");
1463
1464     for (e = s->entries; e; e = e->entries_next) {
1465         gchar *t;
1466
1467         if (e->dead)
1468             continue;
1469         
1470         t = avahi_record_to_string(e->record);
1471         fprintf(f, "%s ; iface=%i proto=%i\n", t, e->interface, e->protocol);
1472         g_free(t);
1473     }
1474
1475     avahi_dump_caches(s->monitor, f);
1476 }
1477
1478 gint avahi_server_add_ptr(
1479     AvahiServer *s,
1480     AvahiEntryGroup *g,
1481     gint interface,
1482     guchar protocol,
1483     AvahiEntryFlags flags,
1484     const gchar *name,
1485     const gchar *dest) {
1486
1487     AvahiRecord *r;
1488
1489     g_assert(dest);
1490
1491     r = avahi_record_new_full(name ? name : s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR);
1492     r->data.ptr.name = avahi_normalize_name(dest);
1493     avahi_server_add(s, g, interface, protocol, flags, r);
1494     avahi_record_unref(r);
1495     return 0;
1496 }
1497
1498 gint avahi_server_add_address(
1499     AvahiServer *s,
1500     AvahiEntryGroup *g,
1501     gint interface,
1502     guchar protocol,
1503     AvahiEntryFlags flags,
1504     const gchar *name,
1505     AvahiAddress *a) {
1506
1507     gchar *n = NULL;
1508     g_assert(s);
1509     g_assert(a);
1510
1511     name = name ? (n = avahi_normalize_name(name)) : s->host_name_fqdn;
1512     
1513     if (a->family == AF_INET) {
1514         gchar *reverse;
1515         AvahiRecord  *r;
1516
1517         r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A);
1518         r->data.a.address = a->data.ipv4;
1519         avahi_server_add(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, r);
1520         avahi_record_unref(r);
1521         
1522         reverse = avahi_reverse_lookup_name_ipv4(&a->data.ipv4);
1523         avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, reverse, name);
1524         g_free(reverse);
1525         
1526     } else {
1527         gchar *reverse;
1528         AvahiRecord *r;
1529             
1530         r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA);
1531         r->data.aaaa.address = a->data.ipv6;
1532         avahi_server_add(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, r);
1533         avahi_record_unref(r);
1534
1535         reverse = avahi_reverse_lookup_name_ipv6_arpa(&a->data.ipv6);
1536         avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, reverse, name);
1537         g_free(reverse);
1538     
1539         reverse = avahi_reverse_lookup_name_ipv6_int(&a->data.ipv6);
1540         avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, reverse, name);
1541         g_free(reverse);
1542     }
1543     
1544     g_free(n);
1545
1546     return 0;
1547 }
1548
1549 gint avahi_server_add_text_strlst(
1550     AvahiServer *s,
1551     AvahiEntryGroup *g,
1552     gint interface,
1553     guchar protocol,
1554     AvahiEntryFlags flags,
1555     const gchar *name,
1556     AvahiStringList *strlst) {
1557
1558     AvahiRecord *r;
1559     
1560     g_assert(s);
1561     
1562     r = avahi_record_new_full(name ? name : s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_TXT);
1563     r->data.txt.string_list = strlst;
1564     avahi_server_add(s, g, interface, protocol, flags, r);
1565     avahi_record_unref(r);
1566
1567     return 0;
1568 }
1569
1570 gint avahi_server_add_text_va(
1571     AvahiServer *s,
1572     AvahiEntryGroup *g,
1573     gint interface,
1574     guchar protocol,
1575     AvahiEntryFlags flags,
1576     const gchar *name,
1577     va_list va) {
1578     
1579     g_assert(s);
1580
1581     avahi_server_add_text_strlst(s, g, interface, protocol, flags, name, avahi_string_list_new_va(va));
1582     return 0;
1583 }
1584
1585 gint avahi_server_add_text(
1586     AvahiServer *s,
1587     AvahiEntryGroup *g,
1588     gint interface,
1589     guchar protocol,
1590     AvahiEntryFlags flags,
1591     const gchar *name,
1592     ...) {
1593
1594     va_list va;
1595     
1596     g_assert(s);
1597
1598     va_start(va, name);
1599     avahi_server_add_text_va(s, g, interface, protocol, flags, name, va);
1600     va_end(va);
1601
1602     return 0;
1603 }
1604
1605 static void escape_service_name(gchar *d, guint size, const gchar *s) {
1606     g_assert(d);
1607     g_assert(size);
1608     g_assert(s);
1609
1610     while (*s && size >= 2) {
1611         if (*s == '.' || *s == '\\') {
1612             if (size < 3)
1613                 break;
1614
1615             *(d++) = '\\';
1616             size--;
1617         }
1618             
1619         *(d++) = *(s++);
1620         size--;
1621     }
1622
1623     g_assert(size > 0);
1624     *(d++) = 0;
1625 }
1626
1627 gint avahi_server_add_service_strlst(
1628     AvahiServer *s,
1629     AvahiEntryGroup *g,
1630     gint interface,
1631     guchar protocol,
1632     const gchar *type,
1633     const gchar *name,
1634     const gchar *domain,
1635     const gchar *host,
1636     guint16 port,
1637     AvahiStringList *strlst) {
1638
1639     gchar ptr_name[256], svc_name[256], ename[64], enum_ptr[256];
1640     AvahiRecord *r;
1641     
1642     g_assert(s);
1643     g_assert(type);
1644     g_assert(name);
1645
1646     escape_service_name(ename, sizeof(ename), name);
1647
1648     if (domain) {
1649         while (domain[0] == '.')
1650             domain++;
1651     } else
1652         domain = s->domain_name;
1653
1654     if (!host)
1655         host = s->host_name_fqdn;
1656
1657     snprintf(ptr_name, sizeof(ptr_name), "%s.%s", type, domain);
1658     snprintf(svc_name, sizeof(svc_name), "%s.%s.%s", ename, type, domain);
1659     
1660     avahi_server_add_ptr(s, g, interface, protocol, AVAHI_ENTRY_NULL, ptr_name, svc_name);
1661
1662     r = avahi_record_new_full(svc_name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV);
1663     r->data.srv.priority = 0;
1664     r->data.srv.weight = 0;
1665     r->data.srv.port = port;
1666     r->data.srv.name = avahi_normalize_name(host);
1667     avahi_server_add(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE, r);
1668     avahi_record_unref(r);
1669
1670     avahi_server_add_text_strlst(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE, svc_name, strlst);
1671
1672     snprintf(enum_ptr, sizeof(enum_ptr), "_services._dns-sd._udp.%s", domain);
1673     avahi_server_add_ptr(s, g, interface, protocol, AVAHI_ENTRY_NULL, enum_ptr, ptr_name);
1674
1675     return 0;
1676 }
1677
1678 gint avahi_server_add_service_va(
1679     AvahiServer *s,
1680     AvahiEntryGroup *g,
1681     gint interface,
1682     guchar protocol,
1683     const gchar *type,
1684     const gchar *name,
1685     const gchar *domain,
1686     const gchar *host,
1687     guint16 port,
1688     va_list va){
1689
1690     g_assert(s);
1691     g_assert(type);
1692     g_assert(name);
1693
1694     avahi_server_add_service_strlst(s, g, interface, protocol, type, name, domain, host, port, avahi_string_list_new_va(va));
1695     return 0;
1696 }
1697
1698 gint avahi_server_add_service(
1699     AvahiServer *s,
1700     AvahiEntryGroup *g,
1701     gint interface,
1702     guchar protocol,
1703     const gchar *type,
1704     const gchar *name,
1705     const gchar *domain,
1706     const gchar *host,
1707     guint16 port,
1708     ... ){
1709
1710     va_list va;
1711     
1712     g_assert(s);
1713     g_assert(type);
1714     g_assert(name);
1715
1716     va_start(va, port);
1717     avahi_server_add_service_va(s, g, interface, protocol, type, name, domain, host, port, va);
1718     va_end(va);
1719     return 0;
1720 }
1721
1722 static void post_query_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, gpointer userdata) {
1723     AvahiKey *k = userdata;
1724
1725     g_assert(m);
1726     g_assert(i);
1727     g_assert(k);
1728
1729     avahi_interface_post_query(i, k, FALSE);
1730 }
1731
1732 void avahi_server_post_query(AvahiServer *s, gint interface, guchar protocol, AvahiKey *key) {
1733     g_assert(s);
1734     g_assert(key);
1735
1736     avahi_interface_monitor_walk(s->monitor, interface, protocol, post_query_callback, key);
1737 }
1738
1739 void avahi_entry_group_change_state(AvahiEntryGroup *g, AvahiEntryGroupState state) {
1740     g_assert(g);
1741
1742     if (g->state == state)
1743         return;
1744
1745     g->state = state;
1746     
1747     if (g->callback) {
1748         g->callback(g->server, g, state, g->userdata);
1749         return;
1750     }
1751 }
1752
1753 AvahiEntryGroup *avahi_entry_group_new(AvahiServer *s, AvahiEntryGroupCallback callback, gpointer userdata) {
1754     AvahiEntryGroup *g;
1755     
1756     g_assert(s);
1757
1758     g = g_new(AvahiEntryGroup, 1);
1759     g->server = s;
1760     g->callback = callback;
1761     g->userdata = userdata;
1762     g->dead = FALSE;
1763     g->state = AVAHI_ENTRY_GROUP_UNCOMMITED;
1764     g->n_probing = 0;
1765     AVAHI_LLIST_HEAD_INIT(AvahiEntry, g->entries);
1766
1767     AVAHI_LLIST_PREPEND(AvahiEntryGroup, groups, s->groups, g);
1768     return g;
1769 }
1770
1771 void avahi_entry_group_free(AvahiEntryGroup *g) {
1772     AvahiEntry *e;
1773     
1774     g_assert(g);
1775     g_assert(g->server);
1776
1777     for (e = g->entries; e; e = e->by_group_next) {
1778         avahi_goodbye_entry(g->server, e, TRUE);
1779         e->dead = TRUE;
1780     }
1781
1782     g->dead = TRUE;
1783     
1784     g->server->need_group_cleanup = TRUE;
1785     g->server->need_entry_cleanup = TRUE;
1786 }
1787
1788 gint avahi_entry_group_commit(AvahiEntryGroup *g) {
1789     g_assert(g);
1790     g_assert(!g->dead);
1791
1792     if (g->state != AVAHI_ENTRY_GROUP_UNCOMMITED)
1793         return -1;
1794
1795     avahi_entry_group_change_state(g, AVAHI_ENTRY_GROUP_REGISTERING);
1796     avahi_announce_group(g->server, g);
1797     avahi_entry_group_check_probed(g, FALSE);
1798
1799     return 0;
1800 }
1801
1802 gboolean avahi_entry_commited(AvahiEntry *e) {
1803     g_assert(e);
1804     g_assert(!e->dead);
1805
1806     return !e->group ||
1807         e->group->state == AVAHI_ENTRY_GROUP_REGISTERING ||
1808         e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED;
1809 }
1810
1811 AvahiEntryGroupState avahi_entry_group_get_state(AvahiEntryGroup *g) {
1812     g_assert(g);
1813     g_assert(!g->dead);
1814
1815     return g->state;
1816 }
1817
1818 void avahi_entry_group_set_data(AvahiEntryGroup *g, gpointer userdata) {
1819     g_assert(g);
1820
1821     g->userdata = userdata;
1822 }
1823
1824 gpointer avahi_entry_group_get_data(AvahiEntryGroup *g) {
1825     g_assert(g);
1826
1827     return g->userdata;
1828 }
1829
1830 const gchar* avahi_server_get_domain_name(AvahiServer *s) {
1831     g_assert(s);
1832
1833     return s->domain_name;
1834 }
1835
1836 const gchar* avahi_server_get_host_name(AvahiServer *s) {
1837     g_assert(s);
1838
1839     return s->host_name;
1840 }
1841
1842 const gchar* avahi_server_get_host_name_fqdn(AvahiServer *s) {
1843     g_assert(s);
1844
1845     return s->host_name_fqdn;
1846 }
1847
1848 gpointer avahi_server_get_data(AvahiServer *s) {
1849     g_assert(s);
1850
1851     return s->userdata;
1852 }
1853
1854 void avahi_server_set_data(AvahiServer *s, gpointer userdata) {
1855     g_assert(s);
1856
1857     s->userdata = userdata;
1858 }
1859
1860 AvahiServerState avahi_server_get_state(AvahiServer *s) {
1861     g_assert(s);
1862
1863     return s->state;
1864 }
1865
1866 AvahiServerConfig* avahi_server_config_init(AvahiServerConfig *c) {
1867     g_assert(c);
1868
1869     memset(c, 0, sizeof(AvahiServerConfig));
1870     c->use_ipv6 = TRUE;
1871     c->use_ipv4 = TRUE;
1872     c->host_name = NULL;
1873     c->domain_name = NULL;
1874     c->check_response_ttl = TRUE;
1875     c->publish_hinfo = TRUE;
1876     c->publish_addresses = TRUE;
1877     c->publish_workstation = TRUE;
1878     c->publish_domain = TRUE;
1879     c->use_iff_running = FALSE;
1880     c->enable_reflector = FALSE;
1881     c->reflect_ipv = FALSE;
1882     
1883     return c;
1884 }
1885
1886 void avahi_server_config_free(AvahiServerConfig *c) {
1887     g_assert(c);
1888
1889     g_free(c->host_name);
1890     g_free(c->domain_name);
1891 }
1892
1893 AvahiServerConfig* avahi_server_config_copy(AvahiServerConfig *ret, const AvahiServerConfig *c) {
1894     g_assert(ret);
1895     g_assert(c);
1896
1897     *ret = *c;
1898
1899     ret->host_name = g_strdup(c->host_name);
1900     ret->domain_name = g_strdup(c->domain_name);
1901
1902     return ret;
1903 }