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