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