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