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