]> git.meshlink.io Git - catta/blob - avahi-core/server.c
small fix
[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     
1135     else if (state == AVAHI_ENTRY_GROUP_COLLISION &&
1136         (s->state == AVAHI_SERVER_REGISTERING || s->state == AVAHI_SERVER_RUNNING)) {
1137         withdraw_host_rrs(s);
1138         server_set_state(s, AVAHI_SERVER_COLLISION);
1139         
1140     } else if (state == AVAHI_ENTRY_GROUP_ESTABLISHED &&
1141                s->state == AVAHI_SERVER_REGISTERING)
1142         avahi_server_decrease_host_rr_pending(s);
1143 }
1144
1145 static void register_hinfo(AvahiServer *s) {
1146     struct utsname utsname;
1147     AvahiRecord *r;
1148     
1149     g_assert(s);
1150     
1151     if (!s->config.publish_hinfo)
1152         return;
1153
1154     if (s->hinfo_entry_group)
1155         g_assert(avahi_entry_group_is_empty(s->hinfo_entry_group));
1156     else
1157         s->hinfo_entry_group = avahi_entry_group_new(s, avahi_host_rr_entry_group_callback, NULL);
1158     
1159     /* Fill in HINFO rr */
1160     r = avahi_record_new_full(s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_HINFO, AVAHI_DEFAULT_TTL_HOST_NAME);
1161     uname(&utsname);
1162     r->data.hinfo.cpu = g_strdup(g_strup(utsname.machine));
1163     r->data.hinfo.os = g_strdup(g_strup(utsname.sysname));
1164     avahi_server_add(s, s->hinfo_entry_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_ENTRY_UNIQUE, r);
1165     avahi_record_unref(r);
1166
1167     avahi_entry_group_commit(s->hinfo_entry_group);
1168 }
1169
1170 static void register_localhost(AvahiServer *s) {
1171     AvahiAddress a;
1172     g_assert(s);
1173     
1174     /* Add localhost entries */
1175     avahi_address_parse("127.0.0.1", AVAHI_PROTO_INET, &a);
1176     avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_ENTRY_NOPROBE|AVAHI_ENTRY_NOANNOUNCE, "localhost", &a);
1177
1178     avahi_address_parse("::1", AVAHI_PROTO_INET6, &a);
1179     avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_ENTRY_NOPROBE|AVAHI_ENTRY_NOANNOUNCE, "ip6-localhost", &a);
1180 }
1181
1182 static void register_browse_domain(AvahiServer *s) {
1183     g_assert(s);
1184
1185     if (!s->config.publish_domain)
1186         return;
1187
1188     if (s->browse_domain_entry_group)
1189         g_assert(avahi_entry_group_is_empty(s->browse_domain_entry_group));
1190     else
1191         s->browse_domain_entry_group = avahi_entry_group_new(s, NULL, NULL);
1192     
1193     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);
1194     avahi_entry_group_commit(s->browse_domain_entry_group);
1195 }
1196
1197 static void register_stuff(AvahiServer *s) {
1198     g_assert(s);
1199
1200     server_set_state(s, AVAHI_SERVER_REGISTERING);
1201     register_hinfo(s);
1202     register_browse_domain(s);
1203     avahi_update_host_rrs(s->monitor, FALSE);
1204
1205     if (s->n_host_rr_pending == 0)
1206         server_set_state(s, AVAHI_SERVER_RUNNING);
1207 }
1208
1209 static void update_fqdn(AvahiServer *s) {
1210     g_assert(s);
1211     
1212     g_assert(s->host_name);
1213     g_assert(s->domain_name);
1214
1215     g_free(s->host_name_fqdn);
1216     s->host_name_fqdn = g_strdup_printf("%s.%s", s->host_name, s->domain_name);
1217 }
1218
1219 gint avahi_server_set_host_name(AvahiServer *s, const gchar *host_name) {
1220     g_assert(s);
1221     g_assert(host_name);
1222
1223     withdraw_host_rrs(s);
1224
1225     g_free(s->host_name);
1226     s->host_name = host_name ? avahi_normalize_name(host_name) : avahi_get_host_name();
1227     s->host_name[strcspn(s->host_name, ".")] = 0;
1228     update_fqdn(s);
1229
1230     register_stuff(s);
1231     return AVAHI_OK;
1232 }
1233
1234 gint avahi_server_set_domain_name(AvahiServer *s, const gchar *domain_name) {
1235     g_assert(s);
1236     g_assert(domain_name);
1237
1238     withdraw_host_rrs(s);
1239
1240     g_free(s->domain_name);
1241     s->domain_name = domain_name ? avahi_normalize_name(domain_name) : g_strdup("local");
1242     update_fqdn(s);
1243
1244     register_stuff(s);
1245     return AVAHI_OK;
1246 }
1247
1248 static void prepare_pollfd(AvahiServer *s, GPollFD *pollfd, gint fd) {
1249     g_assert(s);
1250     g_assert(pollfd);
1251     g_assert(fd >= 0);
1252
1253     memset(pollfd, 0, sizeof(GPollFD));
1254     pollfd->fd = fd;
1255     pollfd->events = G_IO_IN|G_IO_ERR|G_IO_HUP;
1256     g_source_add_poll(s->source, pollfd);
1257 }
1258
1259 AvahiServer *avahi_server_new(GMainContext *c, const AvahiServerConfig *sc, AvahiServerCallback callback, gpointer userdata) {
1260     AvahiServer *s;
1261     
1262     static GSourceFuncs source_funcs = {
1263         prepare_func,
1264         check_func,
1265         dispatch_func,
1266         NULL,
1267         NULL,
1268         NULL
1269     };
1270
1271     s = g_new(AvahiServer, 1);
1272     s->n_host_rr_pending = 0;
1273     s->need_entry_cleanup = s->need_group_cleanup = s->need_browser_cleanup = FALSE;
1274
1275     if (sc)
1276         avahi_server_config_copy(&s->config, sc);
1277     else
1278         avahi_server_config_init(&s->config);
1279     
1280     s->fd_ipv4 = s->config.use_ipv4 ? avahi_open_socket_ipv4() : -1;
1281     s->fd_ipv6 = s->config.use_ipv6 ? avahi_open_socket_ipv6() : -1;
1282     
1283     if (s->fd_ipv6 < 0 && s->fd_ipv4 < 0) {
1284         g_critical("Selected neither IPv6 nor IPv4 support, aborting.\n");
1285         avahi_server_config_free(&s->config);
1286         g_free(s);
1287         return NULL;
1288     }
1289
1290     if (s->fd_ipv4 < 0 && s->config.use_ipv4)
1291         avahi_log_notice("Failed to create IPv4 socket, proceeding in IPv6 only mode");
1292     else if (s->fd_ipv6 < 0 && s->config.use_ipv6)
1293         avahi_log_notice("Failed to create IPv6 socket, proceeding in IPv4 only mode");
1294
1295     s->fd_legacy_unicast_ipv4 = s->fd_ipv4 >= 0 && s->config.enable_reflector ? avahi_open_legacy_unicast_socket_ipv4() : -1;
1296     s->fd_legacy_unicast_ipv6 = s->fd_ipv6 >= 0 && s->config.enable_reflector ? avahi_open_legacy_unicast_socket_ipv6() : -1;
1297
1298     g_main_context_ref(s->context = (c ? c : g_main_context_default()));
1299
1300     /* Prepare IO source registration */
1301     s->source = g_source_new(&source_funcs, sizeof(GSource) + sizeof(AvahiServer*));
1302     *((AvahiServer**) (((guint8*) s->source) + sizeof(GSource))) = s;
1303
1304     if (s->fd_ipv4 >= 0)
1305         prepare_pollfd(s, &s->pollfd_ipv4, s->fd_ipv4);
1306     if (s->fd_ipv6 >= 0)
1307         prepare_pollfd(s, &s->pollfd_ipv6, s->fd_ipv6);
1308     if (s->fd_legacy_unicast_ipv4 >= 0)
1309         prepare_pollfd(s, &s->pollfd_legacy_unicast_ipv4, s->fd_legacy_unicast_ipv4);
1310     if (s->fd_legacy_unicast_ipv6 >= 0)
1311         prepare_pollfd(s, &s->pollfd_legacy_unicast_ipv6, s->fd_legacy_unicast_ipv6);
1312     
1313     g_source_attach(s->source, s->context);
1314     
1315     s->callback = callback;
1316     s->userdata = userdata;
1317     
1318     AVAHI_LLIST_HEAD_INIT(AvahiEntry, s->entries);
1319     s->entries_by_key = g_hash_table_new((GHashFunc) avahi_key_hash, (GEqualFunc) avahi_key_equal);
1320     AVAHI_LLIST_HEAD_INIT(AvahiGroup, s->groups);
1321
1322     AVAHI_LLIST_HEAD_INIT(AvahiRecordBrowser, s->record_browsers);
1323     s->record_browser_hashtable = g_hash_table_new((GHashFunc) avahi_key_hash, (GEqualFunc) avahi_key_equal);
1324     AVAHI_LLIST_HEAD_INIT(AvahiHostNameResolver, s->host_name_resolvers);
1325     AVAHI_LLIST_HEAD_INIT(AvahiAddressResolver, s->address_resolvers);
1326     AVAHI_LLIST_HEAD_INIT(AvahiDomainBrowser, s->domain_browsers);
1327     AVAHI_LLIST_HEAD_INIT(AvahiServiceTypeBrowser, s->service_type_browsers);
1328     AVAHI_LLIST_HEAD_INIT(AvahiServiceBrowser, s->service_browsers);
1329     AVAHI_LLIST_HEAD_INIT(AvahiServiceResolver, s->service_resolvers);
1330     AVAHI_LLIST_HEAD_INIT(AvahiDNSServerBrowser, s->dns_server_browsers);
1331
1332     s->legacy_unicast_reflect_slots = NULL;
1333     s->legacy_unicast_reflect_id = 0;
1334     
1335     /* Get host name */
1336     s->host_name = s->config.host_name ? avahi_normalize_name(s->config.host_name) : avahi_get_host_name();
1337     s->host_name[strcspn(s->host_name, ".")] = 0;
1338     s->domain_name = s->config.domain_name ? avahi_normalize_name(s->config.domain_name) : g_strdup("local");
1339     s->host_name_fqdn = NULL;
1340     update_fqdn(s);
1341
1342     s->record_list = avahi_record_list_new();
1343
1344     s->time_event_queue = avahi_time_event_queue_new(s->context, G_PRIORITY_DEFAULT+10); /* Slightly less priority than the FDs */
1345     
1346     s->state = AVAHI_SERVER_INVALID;
1347
1348     s->monitor = avahi_interface_monitor_new(s);
1349     avahi_interface_monitor_sync(s->monitor);
1350
1351     register_localhost(s);
1352
1353     s->hinfo_entry_group = NULL;
1354     s->browse_domain_entry_group = NULL;
1355     register_stuff(s);
1356     
1357     return s;
1358 }
1359
1360 void avahi_server_free(AvahiServer* s) {
1361     g_assert(s);
1362
1363     while(s->entries)
1364         free_entry(s, s->entries);
1365
1366     avahi_interface_monitor_free(s->monitor);
1367
1368     while (s->groups)
1369         free_group(s, s->groups);
1370
1371     free_slots(s);
1372
1373     while (s->dns_server_browsers)
1374         avahi_dns_server_browser_free(s->dns_server_browsers);
1375     while (s->host_name_resolvers)
1376         avahi_host_name_resolver_free(s->host_name_resolvers);
1377     while (s->address_resolvers)
1378         avahi_address_resolver_free(s->address_resolvers);
1379     while (s->domain_browsers)
1380         avahi_domain_browser_free(s->domain_browsers);
1381     while (s->service_type_browsers)
1382         avahi_service_type_browser_free(s->service_type_browsers);
1383     while (s->service_browsers)
1384         avahi_service_browser_free(s->service_browsers);
1385     while (s->service_resolvers)
1386         avahi_service_resolver_free(s->service_resolvers);
1387     while (s->record_browsers)
1388         avahi_record_browser_destroy(s->record_browsers);
1389     g_hash_table_destroy(s->record_browser_hashtable);
1390
1391     g_hash_table_destroy(s->entries_by_key);
1392
1393     avahi_time_event_queue_free(s->time_event_queue);
1394
1395     avahi_record_list_free(s->record_list);
1396     
1397     if (s->fd_ipv4 >= 0)
1398         close(s->fd_ipv4);
1399     if (s->fd_ipv6 >= 0)
1400         close(s->fd_ipv6);
1401     if (s->fd_legacy_unicast_ipv4 >= 0)
1402         close(s->fd_legacy_unicast_ipv4);
1403     if (s->fd_legacy_unicast_ipv6 >= 0)
1404         close(s->fd_legacy_unicast_ipv6);
1405
1406     g_free(s->host_name);
1407     g_free(s->domain_name);
1408     g_free(s->host_name_fqdn);
1409
1410     g_source_destroy(s->source);
1411     g_source_unref(s->source);
1412     g_main_context_unref(s->context);
1413
1414     avahi_server_config_free(&s->config);
1415
1416     g_free(s);
1417 }
1418
1419 static gint check_record_conflict(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiRecord *r, AvahiEntryFlags flags) {
1420     AvahiEntry *e;
1421     
1422     g_assert(s);
1423     g_assert(r);
1424
1425     for (e = g_hash_table_lookup(s->entries_by_key, r->key); e; e = e->by_key_next) {
1426         if (e->dead)
1427             continue;
1428
1429         if (!(flags & AVAHI_ENTRY_UNIQUE) && !(e->flags & AVAHI_ENTRY_UNIQUE))
1430             continue;
1431         
1432         if ((flags & AVAHI_ENTRY_ALLOWMUTIPLE) && (e->flags & AVAHI_ENTRY_ALLOWMUTIPLE) )
1433             continue;
1434
1435         if (interface <= 0 ||
1436             e->interface <= 0 ||
1437             e->interface == interface ||
1438             protocol == AVAHI_PROTO_UNSPEC ||
1439             e->protocol == AVAHI_PROTO_UNSPEC ||
1440             e->protocol == protocol)
1441
1442             return -1;
1443
1444     }
1445
1446     return 0;
1447 }
1448
1449 gint avahi_server_add(
1450     AvahiServer *s,
1451     AvahiEntryGroup *g,
1452     AvahiIfIndex interface,
1453     AvahiProtocol protocol,
1454     AvahiEntryFlags flags,
1455     AvahiRecord *r) {
1456     
1457     AvahiEntry *e, *t;
1458     
1459     g_assert(s);
1460     g_assert(r);
1461
1462     if (r->ttl == 0)
1463         return -1;
1464
1465     if (avahi_key_is_pattern(r->key))
1466         return -1;
1467
1468     if (check_record_conflict(s, interface, protocol, r, flags) < 0)
1469         return -1;
1470
1471     e = g_new(AvahiEntry, 1);
1472     e->server = s;
1473     e->record = avahi_record_ref(r);
1474     e->group = g;
1475     e->interface = interface;
1476     e->protocol = protocol;
1477     e->flags = flags;
1478     e->dead = FALSE;
1479
1480     AVAHI_LLIST_HEAD_INIT(AvahiAnnouncement, e->announcements);
1481
1482     AVAHI_LLIST_PREPEND(AvahiEntry, entries, s->entries, e);
1483
1484     /* Insert into hash table indexed by name */
1485     t = g_hash_table_lookup(s->entries_by_key, e->record->key);
1486     AVAHI_LLIST_PREPEND(AvahiEntry, by_key, t, e);
1487     g_hash_table_replace(s->entries_by_key, e->record->key, t);
1488
1489     /* Insert into group list */
1490     if (g)
1491         AVAHI_LLIST_PREPEND(AvahiEntry, by_group, g->entries, e); 
1492
1493     avahi_announce_entry(s, e);
1494
1495     return 0;
1496 }
1497
1498 const AvahiRecord *avahi_server_iterate(AvahiServer *s, AvahiEntryGroup *g, void **state) {
1499     AvahiEntry **e = (AvahiEntry**) state;
1500     g_assert(s);
1501     g_assert(e);
1502
1503     if (!*e)
1504         *e = g ? g->entries : s->entries;
1505     
1506     while (*e && (*e)->dead)
1507         *e = g ? (*e)->by_group_next : (*e)->entries_next;
1508         
1509     if (!*e)
1510         return NULL;
1511
1512     return avahi_record_ref((*e)->record);
1513 }
1514
1515 void avahi_server_dump(AvahiServer *s, AvahiDumpCallback callback, gpointer userdata) {
1516     AvahiEntry *e;
1517     
1518     g_assert(s);
1519     g_assert(callback);
1520
1521     callback(";;; ZONE DUMP FOLLOWS ;;;", userdata);
1522
1523     for (e = s->entries; e; e = e->entries_next) {
1524         gchar *t;
1525         gchar ln[256];
1526
1527         if (e->dead)
1528             continue;
1529         
1530         t = avahi_record_to_string(e->record);
1531         g_snprintf(ln, sizeof(ln), "%s ; iface=%i proto=%i", t, e->interface, e->protocol);
1532         g_free(t);
1533
1534         callback(ln, userdata);
1535     }
1536
1537     avahi_dump_caches(s->monitor, callback, userdata);
1538 }
1539
1540 gint avahi_server_add_ptr(
1541     AvahiServer *s,
1542     AvahiEntryGroup *g,
1543     AvahiIfIndex interface,
1544     AvahiProtocol protocol,
1545     AvahiEntryFlags flags,
1546     guint32 ttl,
1547     const gchar *name,
1548     const gchar *dest) {
1549
1550     AvahiRecord *r;
1551     gint ret;
1552
1553     g_assert(dest);
1554
1555     r = avahi_record_new_full(name ? name : s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR, ttl);
1556     r->data.ptr.name = avahi_normalize_name(dest);
1557     ret = avahi_server_add(s, g, interface, protocol, flags, r);
1558     avahi_record_unref(r);
1559     return ret;
1560 }
1561
1562 gint avahi_server_add_address(
1563     AvahiServer *s,
1564     AvahiEntryGroup *g,
1565     AvahiIfIndex interface,
1566     AvahiProtocol protocol,
1567     AvahiEntryFlags flags,
1568     const gchar *name,
1569     AvahiAddress *a) {
1570
1571     gchar *n = NULL;
1572     gint ret = 0;
1573     g_assert(s);
1574     g_assert(a);
1575
1576     name = name ? (n = avahi_normalize_name(name)) : s->host_name_fqdn;
1577     
1578     if (a->family == AVAHI_PROTO_INET) {
1579         gchar *reverse;
1580         AvahiRecord  *r;
1581
1582         r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A, AVAHI_DEFAULT_TTL_HOST_NAME);
1583         r->data.a.address = a->data.ipv4;
1584         ret = avahi_server_add(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE | AVAHI_ENTRY_ALLOWMUTIPLE, r);
1585         avahi_record_unref(r);
1586         
1587         reverse = avahi_reverse_lookup_name_ipv4(&a->data.ipv4);
1588         ret |= avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, AVAHI_DEFAULT_TTL_HOST_NAME, reverse, name);
1589         g_free(reverse);
1590         
1591     } else {
1592         gchar *reverse;
1593         AvahiRecord *r;
1594             
1595         r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA, AVAHI_DEFAULT_TTL_HOST_NAME);
1596         r->data.aaaa.address = a->data.ipv6;
1597         ret = avahi_server_add(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE | AVAHI_ENTRY_ALLOWMUTIPLE, r);
1598         avahi_record_unref(r);
1599
1600         reverse = avahi_reverse_lookup_name_ipv6_arpa(&a->data.ipv6);
1601         ret |= avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, AVAHI_DEFAULT_TTL_HOST_NAME, reverse, name);
1602         g_free(reverse);
1603     
1604         reverse = avahi_reverse_lookup_name_ipv6_int(&a->data.ipv6);
1605         ret |= avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, AVAHI_DEFAULT_TTL_HOST_NAME, reverse, name);
1606         g_free(reverse);
1607     }
1608     
1609     g_free(n);
1610
1611     return ret;
1612 }
1613
1614 static gint server_add_txt_strlst_nocopy(
1615     AvahiServer *s,
1616     AvahiEntryGroup *g,
1617     AvahiIfIndex interface,
1618     AvahiProtocol protocol,
1619     AvahiEntryFlags flags,
1620     guint32 ttl,
1621     const gchar *name,
1622     AvahiStringList *strlst) {
1623
1624     AvahiRecord *r;
1625     gint ret;
1626     
1627     g_assert(s);
1628     
1629     r = avahi_record_new_full(name ? name : s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_TXT, ttl);
1630     r->data.txt.string_list = strlst;
1631     ret = avahi_server_add(s, g, interface, protocol, flags, r);
1632     avahi_record_unref(r);
1633
1634     return ret;
1635 }
1636
1637 gint avahi_server_add_txt_strlst(
1638     AvahiServer *s,
1639     AvahiEntryGroup *g,
1640     AvahiIfIndex interface,
1641     AvahiProtocol protocol,
1642     AvahiEntryFlags flags,
1643     guint32 ttl,
1644     const gchar *name,
1645     AvahiStringList *strlst) {
1646
1647     return server_add_txt_strlst_nocopy(s, g, interface, protocol, flags, ttl, name, avahi_string_list_copy(strlst));
1648 }
1649
1650 gint avahi_server_add_txt_va(
1651     AvahiServer *s,
1652     AvahiEntryGroup *g,
1653     AvahiIfIndex interface,
1654     AvahiProtocol protocol,
1655     AvahiEntryFlags flags,
1656     guint32 ttl,
1657     const gchar *name,
1658     va_list va) {
1659
1660     g_assert(s);
1661
1662     return server_add_txt_strlst_nocopy(s, g, interface, protocol, flags, ttl, name, avahi_string_list_new_va(va));
1663 }
1664
1665 gint avahi_server_add_txt(
1666     AvahiServer *s,
1667     AvahiEntryGroup *g,
1668     AvahiIfIndex interface,
1669     AvahiProtocol protocol,
1670     AvahiEntryFlags flags,
1671     guint32 ttl,
1672     const gchar *name,
1673     ...) {
1674
1675     va_list va;
1676     gint ret;
1677     
1678     g_assert(s);
1679
1680     va_start(va, name);
1681     ret = avahi_server_add_txt_va(s, g, interface, protocol, flags, ttl, name, va);
1682     va_end(va);
1683
1684     return ret;
1685 }
1686
1687 static void escape_service_name(gchar *d, guint size, const gchar *s) {
1688     g_assert(d);
1689     g_assert(size);
1690     g_assert(s);
1691
1692     while (*s && size >= 2) {
1693         if (*s == '.' || *s == '\\') {
1694             if (size < 3)
1695                 break;
1696
1697             *(d++) = '\\';
1698             size--;
1699         }
1700             
1701         *(d++) = *(s++);
1702         size--;
1703     }
1704
1705     g_assert(size > 0);
1706     *(d++) = 0;
1707 }
1708
1709 static gint server_add_service_strlst_nocopy(
1710     AvahiServer *s,
1711     AvahiEntryGroup *g,
1712     AvahiIfIndex interface,
1713     AvahiProtocol protocol,
1714     const gchar *name,
1715     const gchar *type,
1716     const gchar *domain,
1717     const gchar *host,
1718     guint16 port,
1719     AvahiStringList *strlst) {
1720
1721     gchar ptr_name[256], svc_name[256], ename[64], enum_ptr[256];
1722     gchar *t, *d;
1723     AvahiRecord *r;
1724     gint ret = 0;
1725     
1726     g_assert(s);
1727     g_assert(type);
1728     g_assert(name);
1729
1730     escape_service_name(ename, sizeof(ename), name);
1731
1732     if (domain) {
1733         while (domain[0] == '.')
1734             domain++;
1735     } else
1736         domain = s->domain_name;
1737
1738     if (!host)
1739         host = s->host_name_fqdn;
1740
1741     d = avahi_normalize_name(domain);
1742     t = avahi_normalize_name(type);
1743     
1744     g_snprintf(ptr_name, sizeof(ptr_name), "%s.%s", t, d);
1745     g_snprintf(svc_name, sizeof(svc_name), "%s.%s.%s", ename, t, d);
1746
1747     ret = avahi_server_add_ptr(s, g, interface, protocol, AVAHI_ENTRY_NULL, AVAHI_DEFAULT_TTL, ptr_name, svc_name);
1748
1749     r = avahi_record_new_full(svc_name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV, AVAHI_DEFAULT_TTL_HOST_NAME);
1750     r->data.srv.priority = 0;
1751     r->data.srv.weight = 0;
1752     r->data.srv.port = port;
1753     r->data.srv.name = avahi_normalize_name(host);
1754     ret |= avahi_server_add(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE, r);
1755     avahi_record_unref(r);
1756
1757     ret |= server_add_txt_strlst_nocopy(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE, AVAHI_DEFAULT_TTL, svc_name, strlst);
1758
1759     g_snprintf(enum_ptr, sizeof(enum_ptr), "_services._dns-sd._udp.%s", d);
1760     ret |=avahi_server_add_ptr(s, g, interface, protocol, AVAHI_ENTRY_NULL, AVAHI_DEFAULT_TTL, enum_ptr, ptr_name);
1761
1762     g_free(d);
1763     g_free(t);
1764     
1765     return ret;
1766 }
1767
1768 gint avahi_server_add_service_strlst(
1769     AvahiServer *s,
1770     AvahiEntryGroup *g,
1771     AvahiIfIndex interface,
1772     AvahiProtocol protocol,
1773     const gchar *name,
1774     const gchar *type,
1775     const gchar *domain,
1776     const gchar *host,
1777     guint16 port,
1778     AvahiStringList *strlst) {
1779
1780     return server_add_service_strlst_nocopy(s, g, interface, protocol, name, type, domain, host, port, avahi_string_list_copy(strlst));
1781 }
1782
1783 gint avahi_server_add_service_va(
1784     AvahiServer *s,
1785     AvahiEntryGroup *g,
1786     AvahiIfIndex interface,
1787     AvahiProtocol protocol,
1788     const gchar *name,
1789     const gchar *type,
1790     const gchar *domain,
1791     const gchar *host,
1792     guint16 port,
1793     va_list va){
1794
1795     g_assert(s);
1796     g_assert(type);
1797     g_assert(name);
1798
1799     return server_add_service_strlst_nocopy(s, g, interface, protocol, name, type, domain, host, port, avahi_string_list_new_va(va));
1800 }
1801
1802 gint avahi_server_add_service(
1803     AvahiServer *s,
1804     AvahiEntryGroup *g,
1805     AvahiIfIndex interface,
1806     AvahiProtocol protocol,
1807     const gchar *name,
1808     const gchar *type,
1809     const gchar *domain,
1810     const gchar *host,
1811     guint16 port,
1812     ... ){
1813
1814     va_list va;
1815     gint ret;
1816     
1817     g_assert(s);
1818     g_assert(type);
1819     g_assert(name);
1820
1821     va_start(va, port);
1822     ret = avahi_server_add_service_va(s, g, interface, protocol, name, type, domain, host, port, va);
1823     va_end(va);
1824     return ret;
1825 }
1826
1827 static void hexstring(gchar *s, size_t sl, const void *p, size_t pl) {
1828     static const gchar hex[] = "0123456789abcdef";
1829     gboolean b = FALSE;
1830     const guint8 *k = p;
1831
1832     while (sl > 1 && pl > 0) {
1833         *(s++) = hex[(b ? *k : *k >> 4) & 0xF];
1834
1835         if (b) {
1836             k++;
1837             pl--;
1838         }
1839         
1840         b = !b;
1841
1842         sl--;
1843     }
1844
1845     if (sl > 0)
1846         *s = 0;
1847 }
1848
1849 gint avahi_server_add_dns_server_address(
1850     AvahiServer *s,
1851     AvahiEntryGroup *g,
1852     AvahiIfIndex interface,
1853     AvahiProtocol protocol,
1854     const gchar *domain,
1855     AvahiDNSServerType type,
1856     const AvahiAddress *address,
1857     guint16 port /** should be 53 */) {
1858
1859     AvahiRecord *r;
1860     gint ret;
1861     gchar n[64] = "ip-";
1862
1863     g_assert(s);
1864     g_assert(address);
1865     g_assert(type == AVAHI_DNS_SERVER_UPDATE || type == AVAHI_DNS_SERVER_RESOLVE);
1866     g_assert(address->family == AVAHI_PROTO_INET || address->family == AVAHI_PROTO_INET6);
1867
1868     if (address->family == AVAHI_PROTO_INET) {
1869         hexstring(n+3, sizeof(n)-3, &address->data, 4);
1870         r = avahi_record_new_full(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A, AVAHI_DEFAULT_TTL_HOST_NAME);
1871         r->data.a.address = address->data.ipv4;
1872     } else {
1873         hexstring(n+3, sizeof(n)-3, &address->data, 6);
1874         r = avahi_record_new_full(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA, AVAHI_DEFAULT_TTL_HOST_NAME);
1875         r->data.aaaa.address = address->data.ipv6;
1876     }
1877     
1878     ret = avahi_server_add(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE | AVAHI_ENTRY_ALLOWMUTIPLE, r);
1879     avahi_record_unref(r);
1880     
1881     ret |= avahi_server_add_dns_server_name(s, g, interface, protocol, domain, type, n, port);
1882
1883     return ret;
1884 }
1885
1886 gint avahi_server_add_dns_server_name(
1887     AvahiServer *s,
1888     AvahiEntryGroup *g,
1889     AvahiIfIndex interface,
1890     AvahiProtocol protocol,
1891     const gchar *domain,
1892     AvahiDNSServerType type,
1893     const gchar *name,
1894     guint16 port /** should be 53 */) {
1895
1896     gint ret = -1;
1897     gchar t[256], *d;
1898     AvahiRecord *r;
1899     
1900     g_assert(s);
1901     g_assert(name);
1902     g_assert(type == AVAHI_DNS_SERVER_UPDATE || type == AVAHI_DNS_SERVER_RESOLVE);
1903
1904     if (domain) {
1905         while (domain[0] == '.')
1906             domain++;
1907     } else
1908         domain = s->domain_name;
1909
1910     d = avahi_normalize_name(domain);
1911     g_snprintf(t, sizeof(t), "%s.%s", type == AVAHI_DNS_SERVER_RESOLVE ? "_domain._udp" : "_dns-update._udp", d);
1912     g_free(d);
1913     
1914     r = avahi_record_new_full(t, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV, AVAHI_DEFAULT_TTL_HOST_NAME);
1915     r->data.srv.priority = 0;
1916     r->data.srv.weight = 0;
1917     r->data.srv.port = port;
1918     r->data.srv.name = avahi_normalize_name(name);
1919     ret = avahi_server_add(s, g, interface, protocol, AVAHI_ENTRY_NULL, r);
1920     avahi_record_unref(r);
1921
1922     return ret;
1923 }
1924
1925 static void post_query_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, gpointer userdata) {
1926     AvahiKey *k = userdata;
1927
1928     g_assert(m);
1929     g_assert(i);
1930     g_assert(k);
1931
1932     avahi_interface_post_query(i, k, FALSE);
1933 }
1934
1935 void avahi_server_post_query(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiKey *key) {
1936     g_assert(s);
1937     g_assert(key);
1938
1939     avahi_interface_monitor_walk(s->monitor, interface, protocol, post_query_callback, key);
1940 }
1941
1942 void avahi_entry_group_change_state(AvahiEntryGroup *g, AvahiEntryGroupState state) {
1943     g_assert(g);
1944
1945     if (g->state == state)
1946         return;
1947
1948     g->state = state;
1949     
1950     if (g->callback) {
1951         g->callback(g->server, g, state, g->userdata);
1952         return;
1953     }
1954 }
1955
1956 AvahiEntryGroup *avahi_entry_group_new(AvahiServer *s, AvahiEntryGroupCallback callback, gpointer userdata) {
1957     AvahiEntryGroup *g;
1958     
1959     g_assert(s);
1960
1961     g = g_new(AvahiEntryGroup, 1);
1962     g->server = s;
1963     g->callback = callback;
1964     g->userdata = userdata;
1965     g->dead = FALSE;
1966     g->state = AVAHI_ENTRY_GROUP_UNCOMMITED;
1967     g->n_probing = 0;
1968     g->n_register_try = 0;
1969     g->register_time_event = NULL;
1970     g->register_time.tv_sec = 0;
1971     g->register_time.tv_usec = 0;
1972     AVAHI_LLIST_HEAD_INIT(AvahiEntry, g->entries);
1973
1974     AVAHI_LLIST_PREPEND(AvahiEntryGroup, groups, s->groups, g);
1975     return g;
1976 }
1977
1978 void avahi_entry_group_free(AvahiEntryGroup *g) {
1979     AvahiEntry *e;
1980     
1981     g_assert(g);
1982     g_assert(g->server);
1983
1984     for (e = g->entries; e; e = e->by_group_next) {
1985         if (!e->dead) {
1986             avahi_goodbye_entry(g->server, e, TRUE);
1987             e->dead = TRUE;
1988         }
1989     }
1990
1991     if (g->register_time_event) {
1992         avahi_time_event_queue_remove(g->server->time_event_queue, g->register_time_event);
1993         g->register_time_event = NULL;
1994     }
1995
1996     g->dead = TRUE;
1997     
1998     g->server->need_group_cleanup = TRUE;
1999     g->server->need_entry_cleanup = TRUE;
2000 }
2001
2002 static void entry_group_commit_real(AvahiEntryGroup *g) {
2003     g_assert(g);
2004
2005     avahi_entry_group_change_state(g, AVAHI_ENTRY_GROUP_REGISTERING);
2006     avahi_announce_group(g->server, g);
2007     avahi_entry_group_check_probed(g, FALSE);
2008
2009     g_get_current_time(&g->register_time);
2010 }
2011
2012 static void entry_group_register_time_event_callback(AvahiTimeEvent *e, gpointer userdata) {
2013     AvahiEntryGroup *g = userdata;
2014     g_assert(g);
2015
2016 /*     avahi_log_debug("Holdoff passed, waking up and going on."); */
2017
2018     avahi_time_event_queue_remove(g->server->time_event_queue, g->register_time_event);
2019     g->register_time_event = NULL;
2020     
2021     /* Holdoff time passed, so let's start probing */
2022     entry_group_commit_real(g);
2023 }
2024
2025 gint avahi_entry_group_commit(AvahiEntryGroup *g) {
2026     GTimeVal now;
2027     
2028     g_assert(g);
2029     g_assert(!g->dead);
2030
2031     if (g->state != AVAHI_ENTRY_GROUP_UNCOMMITED && g->state != AVAHI_ENTRY_GROUP_COLLISION)
2032         return AVAHI_ERR_BAD_STATE;
2033
2034     g->n_register_try++;
2035
2036     avahi_timeval_add(&g->register_time,
2037                       1000*(g->n_register_try >= AVAHI_RR_RATE_LIMIT_COUNT ?
2038                             AVAHI_RR_HOLDOFF_MSEC_RATE_LIMIT :
2039                             AVAHI_RR_HOLDOFF_MSEC));
2040
2041     g_get_current_time(&now);
2042
2043     if (avahi_timeval_compare(&g->register_time, &now) <= 0) {
2044         /* Holdoff time passed, so let's start probing */
2045 /*         avahi_log_debug("Holdoff passed, directly going on."); */
2046
2047         entry_group_commit_real(g);
2048     } else {
2049 /*         avahi_log_debug("Holdoff not passed, sleeping."); */
2050
2051         /* Holdoff time has not yet passed, so let's wait */
2052         avahi_entry_group_change_state(g, AVAHI_ENTRY_GROUP_REGISTERING);
2053         
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
2058     return AVAHI_OK;
2059 }
2060
2061 void avahi_entry_group_reset(AvahiEntryGroup *g) {
2062     AvahiEntry *e;
2063     g_assert(g);
2064     
2065     if (g->register_time_event) {
2066         avahi_time_event_queue_remove(g->server->time_event_queue, g->register_time_event);
2067         g->register_time_event = NULL;
2068     }
2069     
2070     for (e = g->entries; e; e = e->by_group_next) {
2071         if (!e->dead) {
2072             avahi_goodbye_entry(g->server, e, TRUE);
2073             e->dead = TRUE;
2074         }
2075     }
2076
2077     if (g->register_time_event) {
2078         avahi_time_event_queue_remove(g->server->time_event_queue, g->register_time_event);
2079         g->register_time_event = NULL;
2080     }
2081     
2082     g->server->need_entry_cleanup = TRUE;
2083     g->n_probing = 0;
2084
2085     avahi_entry_group_change_state(g, AVAHI_ENTRY_GROUP_UNCOMMITED);
2086 }
2087
2088 gboolean avahi_entry_commited(AvahiEntry *e) {
2089     g_assert(e);
2090     g_assert(!e->dead);
2091
2092     return !e->group ||
2093         e->group->state == AVAHI_ENTRY_GROUP_REGISTERING ||
2094         e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED;
2095 }
2096
2097 AvahiEntryGroupState avahi_entry_group_get_state(AvahiEntryGroup *g) {
2098     g_assert(g);
2099     g_assert(!g->dead);
2100
2101     return g->state;
2102 }
2103
2104 void avahi_entry_group_set_data(AvahiEntryGroup *g, gpointer userdata) {
2105     g_assert(g);
2106
2107     g->userdata = userdata;
2108 }
2109
2110 gpointer avahi_entry_group_get_data(AvahiEntryGroup *g) {
2111     g_assert(g);
2112
2113     return g->userdata;
2114 }
2115
2116 gboolean avahi_entry_group_is_empty(AvahiEntryGroup *g) {
2117     AvahiEntry *e;
2118     g_assert(g);
2119
2120     /* Look for an entry that is not dead */
2121     for (e = g->entries; e; e = e->by_group_next)
2122         if (!e->dead)
2123             return FALSE;
2124
2125     return TRUE;
2126 }
2127
2128 const gchar* avahi_server_get_domain_name(AvahiServer *s) {
2129     g_assert(s);
2130
2131     return s->domain_name;
2132 }
2133
2134 const gchar* avahi_server_get_host_name(AvahiServer *s) {
2135     g_assert(s);
2136
2137     return s->host_name;
2138 }
2139
2140 const gchar* avahi_server_get_host_name_fqdn(AvahiServer *s) {
2141     g_assert(s);
2142
2143     return s->host_name_fqdn;
2144 }
2145
2146 gpointer avahi_server_get_data(AvahiServer *s) {
2147     g_assert(s);
2148
2149     return s->userdata;
2150 }
2151
2152 void avahi_server_set_data(AvahiServer *s, gpointer userdata) {
2153     g_assert(s);
2154
2155     s->userdata = userdata;
2156 }
2157
2158 AvahiServerState avahi_server_get_state(AvahiServer *s) {
2159     g_assert(s);
2160
2161     return s->state;
2162 }
2163
2164 AvahiServerConfig* avahi_server_config_init(AvahiServerConfig *c) {
2165     g_assert(c);
2166
2167     memset(c, 0, sizeof(AvahiServerConfig));
2168     c->use_ipv6 = TRUE;
2169     c->use_ipv4 = TRUE;
2170     c->host_name = NULL;
2171     c->domain_name = NULL;
2172     c->check_response_ttl = FALSE;
2173     c->publish_hinfo = TRUE;
2174     c->publish_addresses = TRUE;
2175     c->publish_workstation = TRUE;
2176     c->publish_domain = TRUE;
2177     c->use_iff_running = FALSE;
2178     c->enable_reflector = FALSE;
2179     c->reflect_ipv = FALSE;
2180     
2181     return c;
2182 }
2183
2184 void avahi_server_config_free(AvahiServerConfig *c) {
2185     g_assert(c);
2186
2187     g_free(c->host_name);
2188     g_free(c->domain_name);
2189 }
2190
2191 AvahiServerConfig* avahi_server_config_copy(AvahiServerConfig *ret, const AvahiServerConfig *c) {
2192     g_assert(ret);
2193     g_assert(c);
2194
2195     *ret = *c;
2196
2197     ret->host_name = g_strdup(c->host_name);
2198     ret->domain_name = g_strdup(c->domain_name);
2199
2200     return ret;
2201 }