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