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