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