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