]> git.meshlink.io Git - catta/blob - avahi-core/server.c
Rename "AvahiAnnouncement" to "AvahiAnnouncer" to match AvahiQuerier
[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, 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_PUBLISH_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_PUBLISH_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, 1);
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, 1);
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_PUBLISH_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             avahi_cache_start_poof(i->cache, key, a);
619         }
620
621         if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 &&
622             !(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_TC))
623             /* Allow our own queries to be suppressed by incoming
624              * queries only when they do not include known answers */
625             avahi_query_scheduler_incoming(i->query_scheduler, key);
626         
627         avahi_server_prepare_matching_responses(s, i, key, unicast_response);
628         avahi_key_unref(key);
629     }
630
631     if (!legacy_unicast) {
632
633         /* Known Answer Suppression */
634         for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT); n > 0; n --) {
635             AvahiRecord *record;
636             int unique = 0;
637             
638             if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
639                 avahi_log_warn("Packet too short (2)");
640                 goto fail;
641             }
642             
643             if (handle_conflict(s, i, record, unique, a)) {
644                 avahi_response_scheduler_suppress(i->response_scheduler, record, a);
645                 avahi_record_list_drop(s->record_list, record);
646                 avahi_cache_stop_poof(i->cache, record, a);
647             }
648             
649             avahi_record_unref(record);
650         }
651         
652         /* Probe record */
653         for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT); n > 0; n --) {
654             AvahiRecord *record;
655             int unique = 0;
656             
657             if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
658                 avahi_log_warn("Packet too short (3)");
659                 goto fail;
660             }
661             
662             if (!avahi_key_is_pattern(record->key)) {
663                 if (!from_local_iface)
664                     reflect_probe(s, i, record);
665                 incoming_probe(s, record, i);
666             }
667             
668             avahi_record_unref(record);
669         }
670     }
671
672     if (!avahi_record_list_is_empty(s->record_list))
673         avahi_server_generate_response(s, i, p, a, port, legacy_unicast, is_probe);
674
675     return;
676     
677 fail:
678     avahi_record_list_flush(s->record_list);
679 }
680
681 static void handle_response_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, int from_local_iface) {
682     unsigned n;
683     
684     assert(s);
685     assert(p);
686     assert(i);
687     assert(a);
688
689 /*     avahi_log_debug("response"); */
690     
691     for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) +
692              avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT); n > 0; n--) {
693         AvahiRecord *record;
694         int cache_flush = 0;
695 /*         char *txt; */
696         
697         if (!(record = avahi_dns_packet_consume_record(p, &cache_flush))) {
698             avahi_log_warn("Packet too short (4)");
699             break;
700         }
701
702         if (!avahi_key_is_pattern(record->key)) {
703
704 /*             avahi_log_debug("Handling response: %s", txt = avahi_record_to_string(record)); */
705 /*             avahi_free(txt); */
706             
707             if (handle_conflict(s, i, record, cache_flush, a)) {
708                 if (!from_local_iface)
709                     reflect_response(s, i, record, cache_flush);
710                 avahi_cache_update(i->cache, record, cache_flush, a);
711                 avahi_response_scheduler_incoming(i->response_scheduler, record, cache_flush);
712             }
713         }
714             
715         avahi_record_unref(record);
716     }
717
718     /* If the incoming response contained a conflicting record, some
719        records have been scheduling for sending. We need to flush them
720        here. */
721     if (!avahi_record_list_is_empty(s->record_list))
722         avahi_server_generate_response(s, i, NULL, NULL, 0, 0, 1);
723 }
724
725 static AvahiLegacyUnicastReflectSlot* allocate_slot(AvahiServer *s) {
726     unsigned n, idx = (unsigned) -1;
727     AvahiLegacyUnicastReflectSlot *slot;
728     
729     assert(s);
730
731     if (!s->legacy_unicast_reflect_slots)
732         s->legacy_unicast_reflect_slots = avahi_new0(AvahiLegacyUnicastReflectSlot*, AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS);
733
734     for (n = 0; n < AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS; n++, s->legacy_unicast_reflect_id++) {
735         idx = s->legacy_unicast_reflect_id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
736         
737         if (!s->legacy_unicast_reflect_slots[idx])
738             break;
739     }
740
741     if (idx == (unsigned) -1 || s->legacy_unicast_reflect_slots[idx])
742         return NULL;
743
744     if (!(slot = avahi_new(AvahiLegacyUnicastReflectSlot, 1)))
745         return NULL; /* OOM */
746
747     s->legacy_unicast_reflect_slots[idx] = slot;
748     slot->id = s->legacy_unicast_reflect_id++;
749     slot->server = s;
750     
751     return slot;
752 }
753
754 static void deallocate_slot(AvahiServer *s, AvahiLegacyUnicastReflectSlot *slot) {
755     unsigned idx;
756
757     assert(s);
758     assert(slot);
759
760     idx = slot->id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
761
762     assert(s->legacy_unicast_reflect_slots[idx] == slot);
763
764     avahi_time_event_free(slot->time_event);
765     
766     avahi_free(slot);
767     s->legacy_unicast_reflect_slots[idx] = NULL;
768 }
769
770 static void free_slots(AvahiServer *s) {
771     unsigned idx;
772     assert(s);
773
774     if (!s->legacy_unicast_reflect_slots)
775         return;
776
777     for (idx = 0; idx < AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS; idx ++)
778         if (s->legacy_unicast_reflect_slots[idx])
779             deallocate_slot(s, s->legacy_unicast_reflect_slots[idx]);
780
781     avahi_free(s->legacy_unicast_reflect_slots);
782     s->legacy_unicast_reflect_slots = NULL;
783 }
784
785 static AvahiLegacyUnicastReflectSlot* find_slot(AvahiServer *s, uint16_t id) {
786     unsigned idx;
787     
788     assert(s);
789
790     if (!s->legacy_unicast_reflect_slots)
791         return NULL;
792     
793     idx = id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
794
795     if (!s->legacy_unicast_reflect_slots[idx] || s->legacy_unicast_reflect_slots[idx]->id != id)
796         return NULL;
797
798     return s->legacy_unicast_reflect_slots[idx];
799 }
800
801 static void legacy_unicast_reflect_slot_timeout(AvahiTimeEvent *e, void *userdata) {
802     AvahiLegacyUnicastReflectSlot *slot = userdata;
803
804     assert(e);
805     assert(slot);
806     assert(slot->time_event == e);
807
808     deallocate_slot(slot->server, slot);
809 }
810
811 static void reflect_legacy_unicast_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, uint16_t port) {
812     AvahiLegacyUnicastReflectSlot *slot;
813     AvahiInterface *j;
814
815     assert(s);
816     assert(p);
817     assert(i);
818     assert(a);
819     assert(port > 0);
820     assert(i->protocol == a->proto);
821     
822     if (!s->config.enable_reflector)
823         return;
824
825 /*     avahi_log_debug("legacy unicast reflector"); */
826     
827     /* Reflecting legacy unicast queries is a little more complicated
828        than reflecting normal queries, since we must route the
829        responses back to the right client. Therefore we must store
830        some information for finding the right client contact data for
831        response packets. In contrast to normal queries legacy
832        unicast query and response packets are reflected untouched and
833        are not reassembled into larger packets */
834
835     if (!(slot = allocate_slot(s))) {
836         /* No slot available, we drop this legacy unicast query */
837         avahi_log_warn("No slot available for legacy unicast reflection, dropping query packet.");
838         return;
839     }
840
841     slot->original_id = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID);
842     slot->address = *a;
843     slot->port = port;
844     slot->interface = i->hardware->index;
845
846     avahi_elapse_time(&slot->elapse_time, 2000, 0);
847     slot->time_event = avahi_time_event_new(s->time_event_queue, &slot->elapse_time, legacy_unicast_reflect_slot_timeout, slot);
848
849     /* Patch the packet with our new locally generatedt id */
850     avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
851     
852     for (j = s->monitor->interfaces; j; j = j->interface_next)
853         if (avahi_interface_is_relevant(j) &&
854             j != i &&
855             (s->config.reflect_ipv || j->protocol == i->protocol)) {
856
857             if (j->protocol == AVAHI_PROTO_INET && s->fd_legacy_unicast_ipv4 >= 0) {
858                 avahi_send_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, j->hardware->index, p, NULL, 0);
859                 } else if (j->protocol == AVAHI_PROTO_INET6 && s->fd_legacy_unicast_ipv6 >= 0)
860                 avahi_send_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, j->hardware->index, p, NULL, 0);
861         }
862
863     /* Reset the id */
864     avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
865 }
866
867 static int originates_from_local_legacy_unicast_socket(AvahiServer *s, const struct sockaddr *sa) {
868     AvahiAddress a;
869     assert(s);
870     assert(sa);
871
872     if (!s->config.enable_reflector)
873         return 0;
874     
875     avahi_address_from_sockaddr(sa, &a);
876
877     if (!avahi_address_is_local(s->monitor, &a))
878         return 0;
879     
880     if (sa->sa_family == AF_INET && s->fd_legacy_unicast_ipv4 >= 0) {
881         struct sockaddr_in lsa;
882         socklen_t l = sizeof(lsa);
883         
884         if (getsockname(s->fd_legacy_unicast_ipv4, (struct sockaddr*) &lsa, &l) != 0)
885             avahi_log_warn("getsockname(): %s", strerror(errno));
886         else
887             return lsa.sin_port == ((const struct sockaddr_in*) sa)->sin_port;
888
889     }
890
891     if (sa->sa_family == AF_INET6 && s->fd_legacy_unicast_ipv6 >= 0) {
892         struct sockaddr_in6 lsa;
893         socklen_t l = sizeof(lsa);
894
895         if (getsockname(s->fd_legacy_unicast_ipv6, (struct sockaddr*) &lsa, &l) != 0)
896             avahi_log_warn("getsockname(): %s", strerror(errno));
897         else
898             return lsa.sin6_port == ((const struct sockaddr_in6*) sa)->sin6_port;
899     }
900
901     return 0;
902 }
903
904 static int is_mdns_mcast_address(const AvahiAddress *a) {
905     AvahiAddress b;
906     assert(a);
907
908     avahi_address_parse(a->proto == AVAHI_PROTO_INET ? AVAHI_IPV4_MCAST_GROUP : AVAHI_IPV6_MCAST_GROUP, a->proto, &b);
909     return avahi_address_cmp(a, &b) == 0;
910 }
911
912 static int originates_from_local_iface(AvahiServer *s, AvahiIfIndex iface, const AvahiAddress *a, uint16_t port) {
913     assert(s);
914     assert(iface != AVAHI_IF_UNSPEC);
915     assert(a);
916
917     /* If it isn't the MDNS port it can't be generated by us */
918     if (port != AVAHI_MDNS_PORT)
919         return 0;
920
921     return avahi_interface_has_address(s->monitor, iface, a);
922 }
923
924 static void dispatch_packet(AvahiServer *s, AvahiDnsPacket *p, const struct sockaddr *sa, AvahiAddress *dest, AvahiIfIndex iface, int ttl) {
925     AvahiInterface *i;
926     AvahiAddress a;
927     uint16_t port;
928     int from_local_iface = 0;
929     
930     assert(s);
931     assert(p);
932     assert(sa);
933     assert(dest);
934     assert(iface > 0);
935
936     if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, avahi_af_to_proto(sa->sa_family))) ||
937         !avahi_interface_is_relevant(i)) {
938         avahi_log_warn("Recieved packet from invalid interface.");
939         return;
940     }
941
942 /*     avahi_log_debug("new packet recieved on interface '%s.%i'.", i->hardware->name, i->protocol); */
943
944     port = avahi_port_from_sockaddr(sa);
945     avahi_address_from_sockaddr(sa, &a);
946     
947     if (avahi_address_is_ipv4_in_ipv6(&a))
948         /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
949         return;
950
951     if (originates_from_local_legacy_unicast_socket(s, sa))
952         /* This originates from our local reflector, so let's ignore it */
953         return;
954
955     /* We don't want to reflect local traffic, so we check if this packet is generated locally. */
956     if (s->config.enable_reflector)
957         from_local_iface = originates_from_local_iface(s, iface, &a, port);
958
959     if (avahi_dns_packet_check_valid_multicast(p) < 0) {
960         avahi_log_warn("Recieved invalid packet.");
961         return;
962     }
963
964     if (avahi_dns_packet_is_query(p)) {
965         int legacy_unicast = 0;
966
967         if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT) != 0) {
968             avahi_log_warn("Invalid query packet.");
969             return;
970         }
971
972         if (port != AVAHI_MDNS_PORT) {
973             /* Legacy Unicast */
974
975             if ((avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) != 0 ||
976                  avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0)) {
977                 avahi_log_warn("Invalid legacy unicast query packet.");
978                 return;
979             }
980         
981             legacy_unicast = 1;
982         }
983
984         if (legacy_unicast)
985             reflect_legacy_unicast_query_packet(s, p, i, &a, port);
986         
987         handle_query_packet(s, p, i, &a, port, legacy_unicast, from_local_iface);
988         
989 /*         avahi_log_debug("Handled query"); */
990     } else {
991         if (port != AVAHI_MDNS_PORT) {
992             avahi_log_warn("Recieved repsonse with invalid source port %u on interface '%s.%i'", port, i->hardware->name, i->protocol);
993             return;
994         }
995
996         if (ttl != 255 && s->config.check_response_ttl) {
997             avahi_log_warn("Recieved response with invalid TTL %u on interface '%s.%i'.", ttl, i->hardware->name, i->protocol);
998             return;
999         }
1000
1001         if (!is_mdns_mcast_address(dest) &&
1002             !avahi_interface_address_on_link(i, &a)) {
1003             avahi_log_warn("Recivied non-local response on interface '%s.%i'.", i->hardware->name, i->protocol);
1004             return;
1005         }
1006         
1007         if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT) != 0 ||
1008             avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 ||
1009             avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0) {
1010             avahi_log_warn("Invalid response packet.");
1011             return;
1012         }
1013
1014         handle_response_packet(s, p, i, &a, from_local_iface);
1015 /*         avahi_log_debug("Handled response"); */
1016     }
1017 }
1018
1019 static void dispatch_legacy_unicast_packet(AvahiServer *s, AvahiDnsPacket *p, const struct sockaddr *sa, AvahiIfIndex iface) {
1020     AvahiInterface *i, *j;
1021     AvahiAddress a;
1022     AvahiLegacyUnicastReflectSlot *slot;
1023     
1024     assert(s);
1025     assert(p);
1026     assert(sa);
1027     assert(iface > 0);
1028
1029     if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, avahi_af_to_proto(sa->sa_family))) ||
1030         !avahi_interface_is_relevant(i)) {
1031         avahi_log_warn("Recieved packet from invalid interface.");
1032         return;
1033     }
1034
1035 /*     avahi_log_debug("new legacy unicast packet recieved on interface '%s.%i'.", i->hardware->name, i->protocol); */
1036
1037     avahi_address_from_sockaddr(sa, &a);
1038     
1039     if (avahi_address_is_ipv4_in_ipv6(&a))
1040         /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
1041         return;
1042
1043     if (avahi_dns_packet_check_valid(p) < 0 || avahi_dns_packet_is_query(p)) {
1044         avahi_log_warn("Recieved invalid packet.");
1045         return;
1046     }
1047
1048     if (!(slot = find_slot(s, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID)))) {
1049         avahi_log_warn("Recieved legacy unicast response with unknown id");
1050         return;
1051     }
1052
1053     if (!(j = avahi_interface_monitor_get_interface(s->monitor, slot->interface, slot->address.proto)) ||
1054         !avahi_interface_is_relevant(j))
1055         return;
1056
1057     /* Patch the original ID into this response */
1058     avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
1059
1060     /* Forward the response to the correct client */
1061     avahi_interface_send_packet_unicast(j, p, &slot->address, slot->port);
1062
1063     /* Undo changes to packet */
1064     avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
1065 }
1066
1067 static void socket_event(AvahiWatch *w, int fd, AvahiWatchEvent events, void *userdata) {
1068     AvahiServer *s = userdata;
1069     AvahiAddress dest;
1070     AvahiDnsPacket *p;
1071     AvahiIfIndex iface;
1072     uint8_t ttl;
1073     struct sockaddr_in sa;
1074     struct sockaddr_in6 sa6;
1075
1076     assert(w);
1077     assert(fd >= 0);
1078
1079     if (events & AVAHI_WATCH_IN) {
1080     
1081         if (fd == s->fd_ipv4) {
1082             dest.proto = AVAHI_PROTO_INET;
1083             if ((p = avahi_recv_dns_packet_ipv4(s->fd_ipv4, &sa, &dest.data.ipv4, &iface, &ttl))) {
1084                 dispatch_packet(s, p, (struct sockaddr*) &sa, &dest, iface, ttl);
1085                 avahi_dns_packet_free(p);
1086             }
1087         } else if (fd == s->fd_ipv6) {
1088             dest.proto = AVAHI_PROTO_INET6;
1089
1090             if ((p = avahi_recv_dns_packet_ipv6(s->fd_ipv6, &sa6, &dest.data.ipv6, &iface, &ttl))) {
1091                 dispatch_packet(s, p, (struct sockaddr*) &sa6, &dest, iface, ttl);
1092                 avahi_dns_packet_free(p);
1093             }
1094         } else if (fd == s->fd_legacy_unicast_ipv4) {
1095             dest.proto = AVAHI_PROTO_INET;
1096             
1097             if ((p = avahi_recv_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, &sa, &dest.data.ipv4, &iface, &ttl))) {
1098                 dispatch_legacy_unicast_packet(s, p, (struct sockaddr*) &sa, iface);
1099                 avahi_dns_packet_free(p);
1100             }
1101         } else if (fd == s->fd_legacy_unicast_ipv6) {
1102             dest.proto = AVAHI_PROTO_INET6;
1103             
1104             if ((p = avahi_recv_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, &sa6, &dest.data.ipv6, &iface, &ttl))) {
1105                 dispatch_legacy_unicast_packet(s, p, (struct sockaddr*) &sa6, iface);
1106                 avahi_dns_packet_free(p);
1107             }
1108         }
1109
1110         cleanup_dead(s);
1111     } else
1112         abort();
1113 }
1114
1115 static void server_set_state(AvahiServer *s, AvahiServerState state) {
1116     assert(s);
1117
1118     if (s->state == state)
1119         return;
1120     
1121     s->state = state;
1122
1123     if (s->callback)
1124         s->callback(s, state, s->userdata);
1125 }
1126
1127 static void withdraw_host_rrs(AvahiServer *s) {
1128     assert(s);
1129
1130     if (s->hinfo_entry_group)
1131         avahi_s_entry_group_reset(s->hinfo_entry_group);
1132
1133     if (s->browse_domain_entry_group)
1134         avahi_s_entry_group_reset(s->browse_domain_entry_group);
1135
1136     avahi_interface_monitor_update_rrs(s->monitor, 1);
1137     s->n_host_rr_pending = 0;
1138 }
1139
1140 void avahi_server_decrease_host_rr_pending(AvahiServer *s) {
1141     assert(s);
1142     
1143     assert(s->n_host_rr_pending > 0);
1144
1145     if (--s->n_host_rr_pending == 0)
1146         server_set_state(s, AVAHI_SERVER_RUNNING);
1147 }
1148
1149 void avahi_server_increase_host_rr_pending(AvahiServer *s) {
1150     assert(s);
1151
1152     s->n_host_rr_pending ++;
1153 }
1154
1155 void avahi_host_rr_entry_group_callback(AvahiServer *s, AvahiSEntryGroup *g, AvahiEntryGroupState state, void *userdata) {
1156     assert(s);
1157     assert(g);
1158
1159     if (state == AVAHI_ENTRY_GROUP_REGISTERING &&
1160         s->state == AVAHI_SERVER_REGISTERING)
1161         avahi_server_increase_host_rr_pending(s);
1162     
1163     else if (state == AVAHI_ENTRY_GROUP_COLLISION &&
1164         (s->state == AVAHI_SERVER_REGISTERING || s->state == AVAHI_SERVER_RUNNING)) {
1165         withdraw_host_rrs(s);
1166         server_set_state(s, AVAHI_SERVER_COLLISION);
1167         
1168     } else if (state == AVAHI_ENTRY_GROUP_ESTABLISHED &&
1169                s->state == AVAHI_SERVER_REGISTERING)
1170         avahi_server_decrease_host_rr_pending(s);
1171 }
1172
1173 static void register_hinfo(AvahiServer *s) {
1174     struct utsname utsname;
1175     AvahiRecord *r;
1176     
1177     assert(s);
1178     
1179     if (!s->config.publish_hinfo)
1180         return;
1181
1182     if (s->hinfo_entry_group)
1183         assert(avahi_s_entry_group_is_empty(s->hinfo_entry_group));
1184     else
1185         s->hinfo_entry_group = avahi_s_entry_group_new(s, avahi_host_rr_entry_group_callback, NULL);
1186
1187     if (!s->hinfo_entry_group) {
1188         avahi_log_warn("Failed to create HINFO entry group: %s", avahi_strerror(s->error));
1189         return;
1190     }
1191     
1192     /* Fill in HINFO rr */
1193     if ((r = avahi_record_new_full(s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_HINFO, AVAHI_DEFAULT_TTL_HOST_NAME))) {
1194         uname(&utsname);
1195         r->data.hinfo.cpu = avahi_strdup(avahi_strup(utsname.machine));
1196         r->data.hinfo.os = avahi_strdup(avahi_strup(utsname.sysname));
1197
1198         if (avahi_server_add(s, s->hinfo_entry_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_UNIQUE, r) < 0) {
1199             avahi_log_warn("Failed to add HINFO RR: %s", avahi_strerror(s->error));
1200             return;
1201         }
1202
1203         avahi_record_unref(r);
1204     }
1205
1206     if (avahi_s_entry_group_commit(s->hinfo_entry_group) < 0)
1207         avahi_log_warn("Failed to commit HINFO entry group: %s", avahi_strerror(s->error));
1208
1209 }
1210
1211 static void register_localhost(AvahiServer *s) {
1212     AvahiAddress a;
1213     assert(s);
1214     
1215     /* Add localhost entries */
1216     avahi_address_parse("127.0.0.1", AVAHI_PROTO_INET, &a);
1217     avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_NO_PROBE|AVAHI_PUBLISH_NO_ANNOUNCE, "localhost", &a);
1218
1219     avahi_address_parse("::1", AVAHI_PROTO_INET6, &a);
1220     avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_NO_PROBE|AVAHI_PUBLISH_NO_ANNOUNCE, "ip6-localhost", &a);
1221 }
1222
1223 static void register_browse_domain(AvahiServer *s) {
1224     assert(s);
1225
1226     if (!s->config.publish_domain)
1227         return;
1228
1229     if (s->browse_domain_entry_group)
1230         assert(avahi_s_entry_group_is_empty(s->browse_domain_entry_group));
1231     else
1232         s->browse_domain_entry_group = avahi_s_entry_group_new(s, NULL, NULL);
1233
1234     if (!s->browse_domain_entry_group) {
1235         avahi_log_warn("Failed to create browse domain entry group: %s", avahi_strerror(s->error));
1236         return;
1237     }
1238     
1239     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) {
1240         avahi_log_warn("Failed to add browse domain RR: %s", avahi_strerror(s->error));
1241         return;
1242     }
1243
1244     if (avahi_s_entry_group_commit(s->browse_domain_entry_group) < 0)
1245         avahi_log_warn("Failed to commit browse domain entry group: %s", avahi_strerror(s->error));
1246 }
1247
1248 static void register_stuff(AvahiServer *s) {
1249     assert(s);
1250
1251     server_set_state(s, AVAHI_SERVER_REGISTERING);
1252     s->n_host_rr_pending ++;  /** Make sure that the state isn't changed tp AVAHI_SERVER_RUNNING too early */
1253
1254     register_hinfo(s);
1255     register_browse_domain(s);
1256     avahi_interface_monitor_update_rrs(s->monitor, 0);
1257
1258     s->n_host_rr_pending --;
1259     
1260     if (s->n_host_rr_pending == 0)
1261         server_set_state(s, AVAHI_SERVER_RUNNING);
1262 }
1263
1264 static void update_fqdn(AvahiServer *s) {
1265     char *n;
1266     
1267     assert(s);
1268     assert(s->host_name);
1269     assert(s->domain_name);
1270
1271     if (!(n = avahi_strdup_printf("%s.%s", s->host_name, s->domain_name)))
1272         return; /* OOM */
1273
1274     avahi_free(s->host_name_fqdn);
1275     s->host_name_fqdn = n;
1276 }
1277
1278 int avahi_server_set_host_name(AvahiServer *s, const char *host_name) {
1279     assert(s);
1280     assert(host_name);
1281
1282     if (host_name && !avahi_is_valid_host_name(host_name))
1283         return avahi_server_set_errno(s, AVAHI_ERR_INVALID_HOST_NAME);
1284
1285     withdraw_host_rrs(s);
1286
1287     avahi_free(s->host_name);
1288     s->host_name = host_name ? avahi_normalize_name_strdup(host_name) : avahi_get_host_name_strdup();
1289     s->host_name[strcspn(s->host_name, ".")] = 0;
1290     update_fqdn(s);
1291
1292     register_stuff(s);
1293     return AVAHI_OK;
1294 }
1295
1296 int avahi_server_set_domain_name(AvahiServer *s, const char *domain_name) {
1297     assert(s);
1298     assert(domain_name);
1299
1300     if (domain_name && !avahi_is_valid_domain_name(domain_name))
1301         return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
1302
1303     withdraw_host_rrs(s);
1304
1305     avahi_free(s->domain_name);
1306     s->domain_name = domain_name ? avahi_normalize_name_strdup(domain_name) : avahi_strdup("local");
1307     update_fqdn(s);
1308
1309     register_stuff(s);
1310     return AVAHI_OK;
1311 }
1312
1313 static int valid_server_config(const AvahiServerConfig *sc) {
1314
1315     if (sc->host_name && !avahi_is_valid_host_name(sc->host_name))
1316         return AVAHI_ERR_INVALID_HOST_NAME;
1317     
1318     if (sc->domain_name && !avahi_is_valid_domain_name(sc->domain_name))
1319         return AVAHI_ERR_INVALID_DOMAIN_NAME;
1320
1321     return AVAHI_OK;
1322 }
1323
1324 static int setup_sockets(AvahiServer *s) {
1325     assert(s);
1326     
1327     s->fd_ipv4 = s->config.use_ipv4 ? avahi_open_socket_ipv4(s->config.disallow_other_stacks) : -1;
1328     s->fd_ipv6 = s->config.use_ipv6 ? avahi_open_socket_ipv6(s->config.disallow_other_stacks) : -1;
1329     
1330     if (s->fd_ipv6 < 0 && s->fd_ipv4 < 0)
1331         return AVAHI_ERR_NO_NETWORK;
1332
1333     if (s->fd_ipv4 < 0 && s->config.use_ipv4)
1334         avahi_log_notice("Failed to create IPv4 socket, proceeding in IPv6 only mode");
1335     else if (s->fd_ipv6 < 0 && s->config.use_ipv6)
1336         avahi_log_notice("Failed to create IPv6 socket, proceeding in IPv4 only mode");
1337
1338     s->fd_legacy_unicast_ipv4 = s->fd_ipv4 >= 0 && s->config.enable_reflector ? avahi_open_unicast_socket_ipv4() : -1;
1339     s->fd_legacy_unicast_ipv6 = s->fd_ipv6 >= 0 && s->config.enable_reflector ? avahi_open_unicast_socket_ipv6() : -1;
1340     
1341     s->watch_ipv4 =
1342         s->watch_ipv6 =
1343         s->watch_legacy_unicast_ipv4 =
1344         s->watch_legacy_unicast_ipv6 = NULL;
1345     
1346     if (s->fd_ipv4 >= 0)
1347         s->watch_ipv4 = s->poll_api->watch_new(s->poll_api, s->fd_ipv4, AVAHI_WATCH_IN, socket_event, s);
1348     if (s->fd_ipv6 >= 0)
1349         s->watch_ipv6 = s->poll_api->watch_new(s->poll_api, s->fd_ipv6, AVAHI_WATCH_IN, socket_event, s);
1350     
1351     if (s->fd_legacy_unicast_ipv4 >= 0)
1352         s->watch_legacy_unicast_ipv4 = s->poll_api->watch_new(s->poll_api, s->fd_legacy_unicast_ipv4, AVAHI_WATCH_IN, socket_event, s);
1353     if (s->fd_legacy_unicast_ipv6 >= 0)
1354         s->watch_legacy_unicast_ipv6 = s->poll_api->watch_new(s->poll_api, s->fd_legacy_unicast_ipv6, AVAHI_WATCH_IN, socket_event, s);
1355             
1356     return 0;
1357 }
1358
1359 AvahiServer *avahi_server_new(const AvahiPoll *poll_api, const AvahiServerConfig *sc, AvahiServerCallback callback, void* userdata, int *error) {
1360     AvahiServer *s;
1361     int e;
1362     
1363     if (sc && (e = valid_server_config(sc)) < 0) {
1364         if (error)
1365             *error = e;
1366         return NULL;
1367     }
1368     
1369     if (!(s = avahi_new(AvahiServer, 1))) {
1370         if (error)
1371             *error = AVAHI_ERR_NO_MEMORY;
1372
1373         return NULL;
1374     }
1375
1376     s->poll_api = poll_api;
1377
1378     if (sc)
1379         avahi_server_config_copy(&s->config, sc);
1380     else
1381         avahi_server_config_init(&s->config);
1382
1383     if ((e = setup_sockets(s)) < 0) {
1384         if (error)
1385             *error = e;
1386
1387         avahi_server_config_free(&s->config);
1388         avahi_free(s);
1389         
1390         return NULL;
1391     }
1392
1393     
1394     s->n_host_rr_pending = 0;
1395     s->need_entry_cleanup = 0;
1396     s->need_group_cleanup = 0;
1397     s->need_browser_cleanup = 0;
1398     
1399     s->time_event_queue = avahi_time_event_queue_new(poll_api);
1400     
1401     s->callback = callback;
1402     s->userdata = userdata;
1403     
1404     s->entries_by_key = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL);
1405     AVAHI_LLIST_HEAD_INIT(AvahiEntry, s->entries);
1406     AVAHI_LLIST_HEAD_INIT(AvahiGroup, s->groups);
1407
1408     s->record_browser_hashmap = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL);
1409     AVAHI_LLIST_HEAD_INIT(AvahiSRecordBrowser, s->record_browsers);
1410     AVAHI_LLIST_HEAD_INIT(AvahiSHostNameResolver, s->host_name_resolvers);
1411     AVAHI_LLIST_HEAD_INIT(AvahiSAddressResolver, s->address_resolvers);
1412     AVAHI_LLIST_HEAD_INIT(AvahiSDomainBrowser, s->domain_browsers);
1413     AVAHI_LLIST_HEAD_INIT(AvahiSServiceTypeBrowser, s->service_type_browsers);
1414     AVAHI_LLIST_HEAD_INIT(AvahiSServiceBrowser, s->service_browsers);
1415     AVAHI_LLIST_HEAD_INIT(AvahiSServiceResolver, s->service_resolvers);
1416     AVAHI_LLIST_HEAD_INIT(AvahiSDNSServerBrowser, s->dns_server_browsers);
1417
1418     s->legacy_unicast_reflect_slots = NULL;
1419     s->legacy_unicast_reflect_id = 0;
1420
1421     if (s->config.enable_wide_area) {
1422         s->wide_area_lookup_engine = avahi_wide_area_engine_new(s);
1423         avahi_wide_area_set_servers(s->wide_area_lookup_engine, s->config.wide_area_servers, s->config.n_wide_area_servers);
1424     } else
1425         s->wide_area_lookup_engine = NULL;
1426
1427     s->multicast_lookup_engine = avahi_multicast_lookup_engine_new(s);
1428
1429     do {
1430         s->local_service_cookie = (uint32_t) rand() * (uint32_t) rand();
1431     } while (s->local_service_cookie == AVAHI_SERVICE_COOKIE_INVALID);
1432     
1433     /* Get host name */
1434     s->host_name = s->config.host_name ? avahi_normalize_name_strdup(s->config.host_name) : avahi_get_host_name_strdup();
1435     s->host_name[strcspn(s->host_name, ".")] = 0;
1436     s->domain_name = s->config.domain_name ? avahi_normalize_name_strdup(s->config.domain_name) : avahi_strdup("local");
1437     s->host_name_fqdn = NULL;
1438     update_fqdn(s);
1439
1440     s->record_list = avahi_record_list_new();
1441
1442     s->state = AVAHI_SERVER_INVALID;
1443
1444     s->monitor = avahi_interface_monitor_new(s);
1445     avahi_interface_monitor_sync(s->monitor);
1446
1447     register_localhost(s);
1448
1449     s->hinfo_entry_group = NULL;
1450     s->browse_domain_entry_group = NULL;
1451     register_stuff(s);
1452
1453     s->error = AVAHI_OK;
1454     
1455     return s;
1456 }
1457
1458 void avahi_server_free(AvahiServer* s) {
1459     assert(s);
1460
1461     /* Remove all browsers */
1462
1463     while (s->dns_server_browsers)
1464         avahi_s_dns_server_browser_free(s->dns_server_browsers);
1465     while (s->host_name_resolvers)
1466         avahi_s_host_name_resolver_free(s->host_name_resolvers);
1467     while (s->address_resolvers)
1468         avahi_s_address_resolver_free(s->address_resolvers);
1469     while (s->domain_browsers)
1470         avahi_s_domain_browser_free(s->domain_browsers);
1471     while (s->service_type_browsers)
1472         avahi_s_service_type_browser_free(s->service_type_browsers);
1473     while (s->service_browsers)
1474         avahi_s_service_browser_free(s->service_browsers);
1475     while (s->service_resolvers)
1476         avahi_s_service_resolver_free(s->service_resolvers);
1477     while (s->record_browsers)
1478         avahi_s_record_browser_destroy(s->record_browsers);
1479     
1480     /* Remove all locally rgeistered stuff */
1481
1482     while(s->entries)
1483         free_entry(s, s->entries);
1484
1485     avahi_interface_monitor_free(s->monitor);
1486
1487     while (s->groups)
1488         free_group(s, s->groups);
1489
1490     free_slots(s);
1491
1492     avahi_hashmap_free(s->entries_by_key);
1493     avahi_record_list_free(s->record_list);
1494     avahi_hashmap_free(s->record_browser_hashmap);
1495
1496     if (s->wide_area_lookup_engine)
1497         avahi_wide_area_engine_free(s->wide_area_lookup_engine);
1498     avahi_multicast_lookup_engine_free(s->multicast_lookup_engine);
1499
1500     avahi_time_event_queue_free(s->time_event_queue);
1501     
1502     /* Free watches */
1503     
1504     if (s->watch_ipv4)
1505         s->poll_api->watch_free(s->watch_ipv4);
1506     if (s->watch_ipv6)
1507         s->poll_api->watch_free(s->watch_ipv6);
1508     
1509     if (s->watch_legacy_unicast_ipv4)
1510         s->poll_api->watch_free(s->watch_legacy_unicast_ipv4);
1511     if (s->watch_legacy_unicast_ipv6)
1512         s->poll_api->watch_free(s->watch_legacy_unicast_ipv6);
1513
1514     /* Free sockets */
1515     
1516     if (s->fd_ipv4 >= 0)
1517         close(s->fd_ipv4);
1518     if (s->fd_ipv6 >= 0)
1519         close(s->fd_ipv6);
1520     
1521     if (s->fd_legacy_unicast_ipv4 >= 0)
1522         close(s->fd_legacy_unicast_ipv4);
1523     if (s->fd_legacy_unicast_ipv6 >= 0)
1524         close(s->fd_legacy_unicast_ipv6);
1525     
1526     /* Free other stuff */
1527     
1528     avahi_free(s->host_name);
1529     avahi_free(s->domain_name);
1530     avahi_free(s->host_name_fqdn);
1531
1532     avahi_server_config_free(&s->config);
1533
1534     avahi_free(s);
1535 }
1536
1537 static int check_record_conflict(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiRecord *r, AvahiPublishFlags flags) {
1538     AvahiEntry *e;
1539     
1540     assert(s);
1541     assert(r);
1542
1543     for (e = avahi_hashmap_lookup(s->entries_by_key, r->key); e; e = e->by_key_next) {
1544         if (e->dead)
1545             continue;
1546
1547         if (!(flags & AVAHI_PUBLISH_UNIQUE) && !(e->flags & AVAHI_PUBLISH_UNIQUE))
1548             continue;
1549         
1550         if ((flags & AVAHI_PUBLISH_ALLOW_MULTIPLE) && (e->flags & AVAHI_PUBLISH_ALLOW_MULTIPLE) )
1551             continue;
1552
1553         if ((interface <= 0 ||
1554              e->interface <= 0 ||
1555              e->interface == interface) &&
1556             (protocol == AVAHI_PROTO_UNSPEC ||
1557              e->protocol == AVAHI_PROTO_UNSPEC ||
1558              e->protocol == protocol))
1559
1560             return -1;
1561     }
1562
1563     return 0;
1564 }
1565
1566 int avahi_server_add(
1567     AvahiServer *s,
1568     AvahiSEntryGroup *g,
1569     AvahiIfIndex interface,
1570     AvahiProtocol protocol,
1571     AvahiPublishFlags flags,
1572     AvahiRecord *r) {
1573     
1574     assert(s);
1575     assert(r);
1576
1577     if (!AVAHI_IF_VALID(interface))
1578         return avahi_server_set_errno(s, AVAHI_ERR_INVALID_INTERFACE);
1579
1580     if (!AVAHI_PROTO_VALID(protocol))
1581         return avahi_server_set_errno(s, AVAHI_ERR_INVALID_PROTOCOL);
1582     
1583     if (!AVAHI_FLAGS_VALID(
1584             flags,
1585             AVAHI_PUBLISH_NO_ANNOUNCE|
1586             AVAHI_PUBLISH_NO_PROBE|
1587             AVAHI_PUBLISH_UNIQUE|
1588             AVAHI_PUBLISH_ALLOW_MULTIPLE|
1589             AVAHI_PUBLISH_IS_PROXY|
1590             AVAHI_PUBLISH_UPDATE))
1591         return avahi_server_set_errno(s, AVAHI_ERR_INVALID_FLAGS);
1592     
1593     if (!avahi_is_valid_domain_name(r->key->name))
1594         return avahi_server_set_errno(s, AVAHI_ERR_INVALID_HOST_NAME);
1595     
1596     if (r->ttl == 0)
1597         return avahi_server_set_errno(s, AVAHI_ERR_INVALID_TTL);
1598
1599     if (avahi_key_is_pattern(r->key))
1600         return avahi_server_set_errno(s, AVAHI_ERR_IS_PATTERN);
1601
1602     if (!avahi_record_is_valid(r))
1603         return avahi_server_set_errno(s, AVAHI_ERR_INVALID_RECORD);
1604
1605     if (flags & AVAHI_PUBLISH_UPDATE) {
1606         AvahiEntry *e;
1607         AvahiRecord *old_record;
1608         int is_first = 1;
1609         
1610         /* Update and existing record */
1611
1612         /* Find the first matching entry */
1613         for (e = avahi_hashmap_lookup(s->entries_by_key, r->key); e; e = e->by_key_next) {
1614             if (!e->dead && e->group == g && e->interface == interface && e->protocol == protocol)
1615                 break;
1616
1617             is_first = 0;
1618         }
1619
1620         /* Hmm, nothing found? */
1621         if (!e)
1622             return avahi_server_set_errno(s, AVAHI_ERR_NOT_FOUND);
1623
1624         /* Update the entry */
1625         old_record = e->record;
1626         e->record = avahi_record_ref(r);
1627         e->flags = flags;
1628
1629         /* Announce our changes when needed */
1630         if (!avahi_record_equal_no_ttl(old_record, r) && (!g || g->state != AVAHI_ENTRY_GROUP_UNCOMMITED)) {
1631
1632             /* Remove the old entry from all caches, if needed */
1633             if (!(e->flags & AVAHI_PUBLISH_UNIQUE))
1634                 avahi_goodbye_entry(s, e, 1, 0);
1635
1636             /* Reannounce our updated entry */
1637             avahi_reannounce_entry(s, e);
1638         }
1639
1640         /* If we were the first entry in the list, we need to update the key */
1641         if (is_first)
1642             avahi_hashmap_replace(s->entries_by_key, e->record->key, e);
1643         
1644         avahi_record_unref(old_record);
1645
1646     } else {
1647         AvahiEntry *e, *t;
1648
1649         /* Add a new record */
1650     
1651         if (check_record_conflict(s, interface, protocol, r, flags) < 0)
1652             return avahi_server_set_errno(s, AVAHI_ERR_LOCAL_COLLISION);
1653
1654         if (!(e = avahi_new(AvahiEntry, 1)))
1655             return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1656         
1657         e->server = s;
1658         e->record = avahi_record_ref(r);
1659         e->group = g;
1660         e->interface = interface;
1661         e->protocol = protocol;
1662         e->flags = flags;
1663         e->dead = 0;
1664         
1665         AVAHI_LLIST_HEAD_INIT(AvahiAnnouncer, e->announcers);
1666         
1667         AVAHI_LLIST_PREPEND(AvahiEntry, entries, s->entries, e);
1668         
1669         /* Insert into hash table indexed by name */
1670         t = avahi_hashmap_lookup(s->entries_by_key, e->record->key);
1671         AVAHI_LLIST_PREPEND(AvahiEntry, by_key, t, e);
1672         avahi_hashmap_replace(s->entries_by_key, e->record->key, t);
1673         
1674         /* Insert into group list */
1675         if (g)
1676             AVAHI_LLIST_PREPEND(AvahiEntry, by_group, g->entries, e); 
1677         
1678         avahi_announce_entry(s, e);
1679     }
1680
1681     return AVAHI_OK;
1682 }
1683
1684 const AvahiRecord *avahi_server_iterate(AvahiServer *s, AvahiSEntryGroup *g, void **state) {
1685     AvahiEntry **e = (AvahiEntry**) state;
1686     assert(s);
1687     assert(e);
1688
1689     if (!*e)
1690         *e = g ? g->entries : s->entries;
1691     
1692     while (*e && (*e)->dead)
1693         *e = g ? (*e)->by_group_next : (*e)->entries_next;
1694         
1695     if (!*e)
1696         return NULL;
1697
1698     return avahi_record_ref((*e)->record);
1699 }
1700
1701 int avahi_server_dump(AvahiServer *s, AvahiDumpCallback callback, void* userdata) {
1702     AvahiEntry *e;
1703     
1704     assert(s);
1705     assert(callback);
1706
1707     callback(";;; ZONE DUMP FOLLOWS ;;;", userdata);
1708
1709     for (e = s->entries; e; e = e->entries_next) {
1710         char *t;
1711         char ln[256];
1712
1713         if (e->dead)
1714             continue;
1715         
1716         if (!(t = avahi_record_to_string(e->record)))
1717             return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1718         
1719         snprintf(ln, sizeof(ln), "%s ; iface=%i proto=%i", t, e->interface, e->protocol);
1720         avahi_free(t);
1721
1722         callback(ln, userdata);
1723     }
1724
1725     avahi_dump_caches(s->monitor, callback, userdata);
1726
1727     if (s->wide_area_lookup_engine)
1728         avahi_wide_area_cache_dump(s->wide_area_lookup_engine, callback, userdata);
1729     return AVAHI_OK;
1730 }
1731
1732 int avahi_server_add_ptr(
1733     AvahiServer *s,
1734     AvahiSEntryGroup *g,
1735     AvahiIfIndex interface,
1736     AvahiProtocol protocol,
1737     AvahiPublishFlags flags,
1738     uint32_t ttl,
1739     const char *name,
1740     const char *dest) {
1741
1742     AvahiRecord *r;
1743     int ret;
1744
1745     assert(s);
1746     assert(dest);
1747
1748     if ((name && !avahi_is_valid_domain_name(name)) || !avahi_is_valid_domain_name(dest))
1749         return avahi_server_set_errno(s, AVAHI_ERR_INVALID_HOST_NAME);
1750
1751     if (!name)
1752         name = s->host_name_fqdn;
1753     
1754     if (!(r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR, ttl)))
1755         return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1756         
1757     r->data.ptr.name = avahi_normalize_name_strdup(dest);
1758     ret = avahi_server_add(s, g, interface, protocol, flags, r);
1759     avahi_record_unref(r);
1760     return ret;
1761 }
1762
1763 int avahi_server_add_address(
1764     AvahiServer *s,
1765     AvahiSEntryGroup *g,
1766     AvahiIfIndex interface,
1767     AvahiProtocol protocol,
1768     AvahiPublishFlags flags,
1769     const char *name,
1770     AvahiAddress *a) {
1771
1772     char *n = NULL;
1773     int ret = AVAHI_OK;
1774     
1775     assert(s);
1776     assert(a);
1777
1778     if (!AVAHI_IF_VALID(interface))
1779         return avahi_server_set_errno(s, AVAHI_ERR_INVALID_INTERFACE);
1780
1781     if (!AVAHI_PROTO_VALID(protocol) || !AVAHI_PROTO_VALID(a->proto))
1782         return avahi_server_set_errno(s, AVAHI_ERR_INVALID_PROTOCOL);
1783     
1784     if (!AVAHI_FLAGS_VALID(flags, AVAHI_PUBLISH_NO_REVERSE|AVAHI_PUBLISH_NO_ANNOUNCE|AVAHI_PUBLISH_NO_PROBE|AVAHI_PUBLISH_UPDATE))
1785         return avahi_server_set_errno(s, AVAHI_ERR_INVALID_FLAGS);
1786     
1787     if (name && !avahi_is_valid_domain_name(name))
1788         return avahi_server_set_errno(s, AVAHI_ERR_INVALID_HOST_NAME);
1789
1790     if (!name)
1791         name = s->host_name_fqdn;
1792     else {
1793         if (!(n = avahi_normalize_name_strdup(name)))
1794             return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1795
1796         name = n;
1797     }
1798     
1799     if (a->proto == AVAHI_PROTO_INET) {
1800         AvahiRecord  *r;
1801
1802         if (!(r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A, AVAHI_DEFAULT_TTL_HOST_NAME))) {
1803             ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1804             goto fail;
1805         }
1806         
1807         r->data.a.address = a->data.ipv4;
1808         ret = avahi_server_add(s, g, interface, protocol, (flags & ~ AVAHI_PUBLISH_NO_REVERSE) | AVAHI_PUBLISH_UNIQUE | AVAHI_PUBLISH_ALLOW_MULTIPLE, r);
1809         avahi_record_unref(r);
1810
1811         if (ret < 0)
1812             goto fail;
1813
1814         if (!(flags & AVAHI_PUBLISH_NO_REVERSE)) {
1815             char *reverse;
1816             
1817             if (!(reverse = avahi_reverse_lookup_name_ipv4(&a->data.ipv4))) {
1818                 ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1819                 goto fail;
1820             }
1821             
1822             ret = avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_PUBLISH_UNIQUE, AVAHI_DEFAULT_TTL_HOST_NAME, reverse, name);
1823             avahi_free(reverse);
1824         }
1825         
1826     } else {
1827         AvahiRecord *r;
1828
1829         assert(a->proto == AVAHI_PROTO_INET6);
1830             
1831         if (!(r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA, AVAHI_DEFAULT_TTL_HOST_NAME))) {
1832             ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1833             goto fail;
1834         }
1835         
1836         r->data.aaaa.address = a->data.ipv6;
1837         ret = avahi_server_add(s, g, interface, protocol, (flags & ~ AVAHI_PUBLISH_NO_REVERSE) | AVAHI_PUBLISH_UNIQUE | AVAHI_PUBLISH_ALLOW_MULTIPLE, r);
1838         avahi_record_unref(r);
1839
1840         if (ret < 0)
1841             goto fail;
1842
1843         if (!(flags & AVAHI_PUBLISH_NO_REVERSE)) {
1844             char *reverse;
1845
1846             if (!(reverse = avahi_reverse_lookup_name_ipv6_arpa(&a->data.ipv6))) {
1847                 ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1848                 goto fail;
1849             }
1850             
1851             ret = avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_PUBLISH_UNIQUE, AVAHI_DEFAULT_TTL_HOST_NAME, reverse, name);
1852             avahi_free(reverse);
1853             
1854             if (ret < 0)
1855                 goto fail;
1856             
1857             if (!(reverse = avahi_reverse_lookup_name_ipv6_int(&a->data.ipv6))) {
1858                 ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1859                 goto fail;
1860             }
1861             
1862             ret = avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_PUBLISH_UNIQUE, AVAHI_DEFAULT_TTL_HOST_NAME, reverse, name);
1863             avahi_free(reverse);
1864         }
1865     }
1866
1867 fail:
1868     
1869     avahi_free(n);
1870
1871     return ret;
1872 }
1873
1874 static int server_add_txt_strlst_nocopy(
1875     AvahiServer *s,
1876     AvahiSEntryGroup *g,
1877     AvahiIfIndex interface,
1878     AvahiProtocol protocol,
1879     AvahiPublishFlags flags,
1880     uint32_t ttl,
1881     const char *name,
1882     AvahiStringList *strlst) {
1883
1884     AvahiRecord *r;
1885     int ret;
1886     
1887     assert(s);
1888
1889     if (!(r = avahi_record_new_full(name ? name : s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_TXT, ttl))) {
1890         avahi_string_list_free(strlst);
1891         return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1892     }
1893     
1894     r->data.txt.string_list = strlst;
1895     ret = avahi_server_add(s, g, interface, protocol, flags, r);
1896     avahi_record_unref(r);
1897
1898     return ret;
1899 }
1900
1901 int avahi_server_add_txt_strlst(
1902     AvahiServer *s,
1903     AvahiSEntryGroup *g,
1904     AvahiIfIndex interface,
1905     AvahiProtocol protocol,
1906     AvahiPublishFlags flags,
1907     uint32_t ttl,
1908     const char *name,
1909     AvahiStringList *strlst) {
1910
1911     assert(s);
1912
1913     return server_add_txt_strlst_nocopy(s, g, interface, protocol, flags, ttl, name, avahi_string_list_copy(strlst));
1914 }
1915
1916 int avahi_server_add_txt_va(
1917     AvahiServer *s,
1918     AvahiSEntryGroup *g,
1919     AvahiIfIndex interface,
1920     AvahiProtocol protocol,
1921     AvahiPublishFlags flags,
1922     uint32_t ttl,
1923     const char *name,
1924     va_list va) {
1925
1926     assert(s);
1927
1928     return server_add_txt_strlst_nocopy(s, g, interface, protocol, flags, ttl, name, avahi_string_list_new_va(va));
1929 }
1930
1931 int avahi_server_add_txt(
1932     AvahiServer *s,
1933     AvahiSEntryGroup *g,
1934     AvahiIfIndex interface,
1935     AvahiProtocol protocol,
1936     AvahiPublishFlags flags,
1937     uint32_t ttl,
1938     const char *name,
1939     ...) {
1940
1941     va_list va;
1942     int ret;
1943     
1944     assert(s);
1945
1946     va_start(va, name);
1947     ret = avahi_server_add_txt_va(s, g, interface, protocol, flags, ttl, name, va);
1948     va_end(va);
1949
1950     return ret;
1951 }
1952
1953 static AvahiStringList *add_magic_cookie(
1954     AvahiServer *s,
1955     AvahiStringList *strlst) {
1956
1957     assert(s);
1958
1959     if (!s->config.add_service_cookie)
1960         return strlst;
1961
1962     if (avahi_string_list_find(strlst, AVAHI_SERVICE_COOKIE))
1963         /* This string list already contains a magic cookie */
1964         return strlst;
1965
1966     return avahi_string_list_add_printf(strlst, AVAHI_SERVICE_COOKIE"=%u", s->local_service_cookie);
1967 }
1968
1969 static int server_add_service_strlst_nocopy(
1970     AvahiServer *s,
1971     AvahiSEntryGroup *g,
1972     AvahiIfIndex interface,
1973     AvahiProtocol protocol,
1974     AvahiPublishFlags flags,
1975     const char *name,
1976     const char *type,
1977     const char *domain,
1978     const char *host,
1979     uint16_t port,
1980     AvahiStringList *strlst) {
1981
1982     char ptr_name[AVAHI_DOMAIN_NAME_MAX], svc_name[AVAHI_DOMAIN_NAME_MAX], enum_ptr[AVAHI_DOMAIN_NAME_MAX], *h = NULL;
1983     AvahiRecord *r = NULL;
1984     int ret = AVAHI_OK;
1985     
1986     assert(s);
1987     assert(type);
1988     assert(name);
1989
1990     AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE);
1991     AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL);
1992     AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_FLAGS_VALID(flags, AVAHI_PUBLISH_NO_COOKIE|AVAHI_PUBLISH_IS_PROXY|AVAHI_PUBLISH_UPDATE), AVAHI_ERR_INVALID_FLAGS);
1993     AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_name(name), AVAHI_ERR_INVALID_SERVICE_NAME);
1994     AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_type_strict(type), AVAHI_ERR_INVALID_SERVICE_TYPE);
1995     AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME);
1996     AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, !host || avahi_is_valid_domain_name(host), AVAHI_ERR_INVALID_HOST_NAME);
1997
1998     if (!domain)
1999         domain = s->domain_name;
2000
2001     if (!host)
2002         host = s->host_name_fqdn;
2003
2004     if (!(h = avahi_normalize_name_strdup(host))) {
2005         ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
2006         goto fail;
2007     }
2008
2009     if ((ret = avahi_service_name_join(svc_name, sizeof(svc_name), name, type, domain)) < 0 ||
2010         (ret = avahi_service_name_join(ptr_name, sizeof(ptr_name), NULL, type, domain)) < 0 ||
2011         (ret = avahi_service_name_join(enum_ptr, sizeof(enum_ptr), NULL, "_services._dns-sd._udp", domain)) < 0) {
2012         avahi_server_set_errno(s, ret);
2013         goto fail;
2014     }
2015
2016     /* Add service enumeration PTR record */
2017     
2018     if ((ret = avahi_server_add_ptr(s, g, interface, protocol, flags & AVAHI_PUBLISH_IS_PROXY, AVAHI_DEFAULT_TTL, ptr_name, svc_name)) < 0)
2019         goto fail;
2020
2021     /* Add SRV record */
2022     
2023     if (!(r = avahi_record_new_full(svc_name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV, AVAHI_DEFAULT_TTL_HOST_NAME))) {
2024         ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
2025         goto fail;
2026     }
2027     
2028     r->data.srv.priority = 0;
2029     r->data.srv.weight = 0;
2030     r->data.srv.port = port;
2031     r->data.srv.name = h;
2032     h = NULL;
2033     ret = avahi_server_add(s, g, interface, protocol, (flags & AVAHI_PUBLISH_IS_PROXY) | AVAHI_PUBLISH_UNIQUE, r);
2034     avahi_record_unref(r);
2035
2036     if (ret < 0)
2037         goto fail;
2038
2039     /* Add TXT record */
2040
2041     if (!(flags & AVAHI_PUBLISH_NO_COOKIE))
2042         strlst = add_magic_cookie(s, strlst);
2043     
2044     ret = server_add_txt_strlst_nocopy(s, g, interface, protocol, (flags & AVAHI_PUBLISH_IS_PROXY) | AVAHI_PUBLISH_UNIQUE, AVAHI_DEFAULT_TTL, svc_name, strlst);
2045     strlst = NULL;
2046
2047     if (ret < 0)
2048         goto fail;
2049
2050     /* Add service type enumeration record */
2051     
2052     ret = avahi_server_add_ptr(s, g, interface, protocol, (flags & AVAHI_PUBLISH_IS_PROXY), AVAHI_DEFAULT_TTL, enum_ptr, ptr_name);
2053
2054 fail:
2055     
2056     avahi_string_list_free(strlst);
2057     avahi_free(h);
2058     
2059     return ret;
2060 }
2061
2062 int avahi_server_add_service_strlst(
2063     AvahiServer *s,
2064     AvahiSEntryGroup *g,
2065     AvahiIfIndex interface,
2066     AvahiProtocol protocol,
2067     AvahiPublishFlags flags,
2068     const char *name,
2069     const char *type,
2070     const char *domain,
2071     const char *host,
2072     uint16_t port,
2073     AvahiStringList *strlst) {
2074
2075     assert(s);
2076     assert(type);
2077     assert(name);
2078
2079     return server_add_service_strlst_nocopy(s, g, interface, protocol, flags, name, type, domain, host, port, avahi_string_list_copy(strlst));
2080 }
2081
2082 int avahi_server_add_service_va(
2083     AvahiServer *s,
2084     AvahiSEntryGroup *g,
2085     AvahiIfIndex interface,
2086     AvahiProtocol protocol,
2087     AvahiPublishFlags flags,
2088     const char *name,
2089     const char *type,
2090     const char *domain,
2091     const char *host,
2092     uint16_t port,
2093     va_list va) {
2094
2095     assert(s);
2096     assert(type);
2097     assert(name);
2098
2099     return server_add_service_strlst_nocopy(s, g, interface, protocol, flags, name, type, domain, host, port, avahi_string_list_new_va(va));
2100 }
2101
2102 int avahi_server_add_service(
2103     AvahiServer *s,
2104     AvahiSEntryGroup *g,
2105     AvahiIfIndex interface,
2106     AvahiProtocol protocol,
2107     AvahiPublishFlags flags,
2108     const char *name,
2109     const char *type,
2110     const char *domain,
2111     const char *host,
2112     uint16_t port,
2113     ... ){
2114
2115     va_list va;
2116     int ret;
2117     
2118     va_start(va, port);
2119     ret = avahi_server_add_service_va(s, g, interface, protocol, flags, name, type, domain, host, port, va);
2120     va_end(va);
2121     
2122     return ret;
2123 }
2124
2125 static int server_update_service_txt_strlst_nocopy(
2126     AvahiServer *s,
2127     AvahiSEntryGroup *g,
2128     AvahiIfIndex interface,
2129     AvahiProtocol protocol,
2130     AvahiPublishFlags flags,
2131     const char *name,     
2132     const char *type,     
2133     const char *domain,   
2134     AvahiStringList *strlst) {
2135
2136     char svc_name[AVAHI_DOMAIN_NAME_MAX];
2137     int ret = AVAHI_OK;
2138     
2139     assert(s);
2140     assert(type);
2141     assert(name);
2142
2143     AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE);
2144     AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL);
2145     AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_FLAGS_VALID(flags, AVAHI_PUBLISH_IS_PROXY|AVAHI_PUBLISH_NO_COOKIE), AVAHI_ERR_INVALID_FLAGS);
2146     AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_name(name), AVAHI_ERR_INVALID_SERVICE_NAME);
2147     AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_type_strict(type), AVAHI_ERR_INVALID_SERVICE_TYPE);
2148     AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME);
2149
2150     if (!domain)
2151         domain = s->domain_name;
2152
2153     if ((ret = avahi_service_name_join(svc_name, sizeof(svc_name), name, type, domain)) < 0) {
2154         avahi_server_set_errno(s, ret);
2155         goto fail;
2156     }
2157
2158     /* Add TXT record */
2159     if (!(flags & AVAHI_PUBLISH_NO_COOKIE))
2160         strlst = add_magic_cookie(s, strlst);
2161     
2162     ret = server_add_txt_strlst_nocopy(s, g, interface, protocol, (flags & AVAHI_PUBLISH_IS_PROXY) | AVAHI_PUBLISH_UNIQUE | AVAHI_PUBLISH_UPDATE, AVAHI_DEFAULT_TTL, svc_name, strlst);
2163     strlst = NULL;
2164
2165 fail:
2166     
2167     avahi_string_list_free(strlst);
2168     
2169     return ret;
2170 }
2171
2172 int avahi_server_update_service_txt_strlst(
2173     AvahiServer *s,
2174     AvahiSEntryGroup *g,
2175     AvahiIfIndex interface,
2176     AvahiProtocol protocol,
2177     AvahiPublishFlags flags,
2178     const char *name,     
2179     const char *type,     
2180     const char *domain,   
2181     AvahiStringList *strlst) {
2182
2183     return server_update_service_txt_strlst_nocopy(s, g, interface, protocol, flags, name, type, domain, avahi_string_list_copy(strlst));
2184 }
2185
2186 /** Update the TXT record for a service with the NULL terminated list of strings of the va_list. */
2187 int avahi_server_update_service_txt_va(
2188     AvahiServer *s,
2189     AvahiSEntryGroup *g,
2190     AvahiIfIndex interface,
2191     AvahiProtocol protocol,
2192     AvahiPublishFlags flags,
2193     const char *name,     
2194     const char *type,     
2195     const char *domain,   
2196     va_list va) {
2197
2198     return server_update_service_txt_strlst_nocopy(s, g, interface, protocol, flags, name, type, domain, avahi_string_list_new_va(va));
2199 }
2200
2201 /** Update the TXT record for a service with the NULL termonate list of strings */
2202 int avahi_server_update_service_txt(
2203     AvahiServer *s,
2204     AvahiSEntryGroup *g,
2205     AvahiIfIndex interface,
2206     AvahiProtocol protocol,
2207     AvahiPublishFlags flags,
2208     const char *name,     
2209     const char *type,     
2210     const char *domain,   
2211     ...) {
2212
2213     va_list va;
2214     int ret;
2215     
2216     va_start(va, domain);
2217     ret = avahi_server_update_service_txt_va(s, g, interface, protocol, flags, name, type, domain, va);
2218     va_end(va);
2219     
2220     return ret;
2221 }
2222
2223 int avahi_server_add_service_subtype(
2224     AvahiServer *s,
2225     AvahiSEntryGroup *g,
2226     AvahiIfIndex interface,
2227     AvahiProtocol protocol,
2228     AvahiPublishFlags flags,
2229     const char *name,        
2230     const char *type,        
2231     const char *domain,      
2232     const char *subtype) {
2233
2234     int ret = AVAHI_OK;
2235     char svc_name[AVAHI_DOMAIN_NAME_MAX], ptr_name[AVAHI_DOMAIN_NAME_MAX];
2236     
2237     assert(name);
2238     assert(type);
2239     assert(subtype);
2240
2241     AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE);
2242     AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL);
2243     AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_FLAGS_VALID(flags, 0), AVAHI_ERR_INVALID_FLAGS);
2244     AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_name(name), AVAHI_ERR_INVALID_SERVICE_NAME);
2245     AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_type_strict(type), AVAHI_ERR_INVALID_SERVICE_TYPE);
2246     AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME);
2247     AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_subtype(subtype), AVAHI_ERR_INVALID_SERVICE_SUBTYPE);
2248
2249     if (!domain)
2250         domain = s->domain_name;
2251
2252     if ((ret = avahi_service_name_join(svc_name, sizeof(svc_name), name, type, domain)) < 0 ||
2253         (ret = avahi_service_name_join(ptr_name, sizeof(ptr_name), NULL, subtype, domain)) < 0) {
2254         avahi_server_set_errno(s, ret);
2255         goto fail;
2256     }
2257
2258     if ((ret = avahi_server_add_ptr(s, g, interface, protocol, flags & AVAHI_PUBLISH_IS_PROXY, AVAHI_DEFAULT_TTL, ptr_name, svc_name)) < 0)
2259         goto fail;
2260
2261 fail:
2262     
2263     return ret;
2264 }
2265
2266 static void hexstring(char *s, size_t sl, const void *p, size_t pl) {
2267     static const char hex[] = "0123456789abcdef";
2268     int b = 0;
2269     const uint8_t *k = p;
2270
2271     while (sl > 1 && pl > 0) {
2272         *(s++) = hex[(b ? *k : *k >> 4) & 0xF];
2273
2274         if (b) {
2275             k++;
2276             pl--;
2277         }
2278         
2279         b = !b;
2280
2281         sl--;
2282     }
2283
2284     if (sl > 0)
2285         *s = 0;
2286 }
2287
2288 int avahi_server_add_dns_server_address(
2289     AvahiServer *s,
2290     AvahiSEntryGroup *g,
2291     AvahiIfIndex interface,
2292     AvahiProtocol protocol,
2293     AvahiPublishFlags flags,
2294     const char *domain,
2295     AvahiDNSServerType type,
2296     const AvahiAddress *address,
2297     uint16_t port /** should be 53 */) {
2298
2299     AvahiRecord *r;
2300     int ret;
2301     char n[64], h[64];
2302
2303     assert(s);
2304     assert(address);
2305
2306     if (!AVAHI_IF_VALID(interface))
2307         return avahi_server_set_errno(s, AVAHI_ERR_INVALID_INTERFACE);
2308
2309     if (!AVAHI_PROTO_VALID(protocol) || !AVAHI_PROTO_VALID(address->proto))
2310         return avahi_server_set_errno(s, AVAHI_ERR_INVALID_PROTOCOL);
2311     
2312     if (!AVAHI_FLAGS_VALID(flags, 0) || (type != AVAHI_DNS_SERVER_UPDATE && type != AVAHI_DNS_SERVER_RESOLVE))
2313         return avahi_server_set_errno(s, AVAHI_ERR_INVALID_FLAGS);
2314     
2315     if (port == 0)
2316         return avahi_server_set_errno(s, AVAHI_ERR_INVALID_PORT);
2317     
2318     if (domain && !avahi_is_valid_domain_name(domain))
2319         return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
2320
2321     if (!domain)
2322         domain = s->domain_name;
2323
2324     if (address->proto == AVAHI_PROTO_INET) {
2325         hexstring(h, sizeof(h), &address->data, sizeof(AvahiIPv4Address));
2326         snprintf(n, sizeof(n), "ip-%s.%s", h, domain);
2327         r = avahi_record_new_full(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A, AVAHI_DEFAULT_TTL_HOST_NAME);
2328         r->data.a.address = address->data.ipv4;
2329     } else {
2330         hexstring(h, sizeof(h), &address->data, sizeof(AvahiIPv6Address));
2331         snprintf(n, sizeof(n), "ip6-%s.%s", h, domain);
2332         r = avahi_record_new_full(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA, AVAHI_DEFAULT_TTL_HOST_NAME);
2333         r->data.aaaa.address = address->data.ipv6;
2334     }
2335
2336     if (!r)
2337         return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
2338     
2339     ret = avahi_server_add(s, g, interface, protocol, AVAHI_PUBLISH_UNIQUE | AVAHI_PUBLISH_ALLOW_MULTIPLE, r);
2340     avahi_record_unref(r);
2341
2342     if (ret < 0)
2343         return ret;
2344     
2345     return avahi_server_add_dns_server_name(s, g, interface, protocol, flags, domain, type, n, port);
2346 }
2347
2348 int avahi_server_add_dns_server_name(
2349     AvahiServer *s,
2350     AvahiSEntryGroup *g,
2351     AvahiIfIndex interface,
2352     AvahiProtocol protocol,
2353     AvahiPublishFlags flags,
2354     const char *domain,
2355     AvahiDNSServerType type,
2356     const char *name,
2357     uint16_t port /** should be 53 */) {
2358
2359     int ret = -1;
2360     char t[256], *d = NULL, *n = NULL;
2361     AvahiRecord *r;
2362     
2363     assert(s);
2364     assert(name);
2365
2366     if (!AVAHI_IF_VALID(interface))
2367         return avahi_server_set_errno(s, AVAHI_ERR_INVALID_INTERFACE);
2368
2369     if (!AVAHI_PROTO_VALID(protocol))
2370         return avahi_server_set_errno(s, AVAHI_ERR_INVALID_PROTOCOL);
2371
2372     if (!AVAHI_FLAGS_VALID(flags, 0) || (type != AVAHI_DNS_SERVER_UPDATE && type != AVAHI_DNS_SERVER_RESOLVE))
2373         return avahi_server_set_errno(s, AVAHI_ERR_INVALID_FLAGS);
2374     
2375     if (port == 0)
2376         return avahi_server_set_errno(s, AVAHI_ERR_INVALID_PORT);
2377
2378     if (!avahi_is_valid_domain_name(name))
2379         return avahi_server_set_errno(s, AVAHI_ERR_INVALID_HOST_NAME);
2380
2381     if (domain && !avahi_is_valid_domain_name(domain))
2382         return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
2383     
2384     if (!domain)
2385         domain = s->domain_name;
2386
2387     if (!(n = avahi_normalize_name_strdup(name)) ||
2388         !(d = avahi_normalize_name_strdup(domain))) {
2389         avahi_free(n);
2390         avahi_free(d);
2391         return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
2392     }
2393
2394     snprintf(t, sizeof(t), "%s.%s", type == AVAHI_DNS_SERVER_RESOLVE ? "_domain._udp" : "_dns-update._udp", d);
2395     avahi_free(d);
2396     
2397     if (!(r = avahi_record_new_full(t, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV, AVAHI_DEFAULT_TTL_HOST_NAME))) {
2398         avahi_free(n);
2399         return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
2400     }
2401     
2402     r->data.srv.priority = 0;
2403     r->data.srv.weight = 0;
2404     r->data.srv.port = port;
2405     r->data.srv.name = n;
2406     ret = avahi_server_add(s, g, interface, protocol, AVAHI_PUBLISH_NULL, r);
2407     avahi_record_unref(r);
2408
2409     return ret;
2410 }
2411
2412 static void post_query_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, void* userdata) {
2413     AvahiKey *k = userdata;
2414
2415     assert(m);
2416     assert(i);
2417     assert(k);
2418
2419     avahi_interface_post_query(i, k, 0);
2420 }
2421
2422 void avahi_server_post_query(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiKey *key) {
2423     assert(s);
2424     assert(key);
2425
2426     avahi_interface_monitor_walk(s->monitor, interface, protocol, post_query_callback, key);
2427 }
2428
2429 void avahi_s_entry_group_change_state(AvahiSEntryGroup *g, AvahiEntryGroupState state) {
2430     assert(g);
2431
2432     if (g->state == state)
2433         return;
2434
2435     assert(state <= AVAHI_ENTRY_GROUP_COLLISION);
2436
2437     if (g->state == AVAHI_ENTRY_GROUP_ESTABLISHED) {
2438
2439         /* If the entry group was established for a time longer then
2440          * 5s, reset the establishment trial counter */
2441         
2442         if (avahi_age(&g->established_at) > 5000000)
2443             g->n_register_try = 0;
2444     }
2445     
2446     if (state == AVAHI_ENTRY_GROUP_ESTABLISHED)
2447
2448         /* If the entry group is now established, remember the time
2449          * this happened */
2450         
2451         gettimeofday(&g->established_at, NULL);
2452     
2453     g->state = state;
2454     
2455     if (g->callback)
2456         g->callback(g->server, g, state, g->userdata);
2457 }
2458
2459 AvahiSEntryGroup *avahi_s_entry_group_new(AvahiServer *s, AvahiSEntryGroupCallback callback, void* userdata) {
2460     AvahiSEntryGroup *g;
2461     
2462     assert(s);
2463
2464     if (!(g = avahi_new(AvahiSEntryGroup, 1))) {
2465         avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
2466         return NULL;
2467     }
2468     
2469     g->server = s;
2470     g->callback = callback;
2471     g->userdata = userdata;
2472     g->dead = 0;
2473     g->state = AVAHI_ENTRY_GROUP_UNCOMMITED;
2474     g->n_probing = 0;
2475     g->n_register_try = 0;
2476     g->register_time_event = NULL;
2477     g->register_time.tv_sec = 0;
2478     g->register_time.tv_usec = 0;
2479     AVAHI_LLIST_HEAD_INIT(AvahiEntry, g->entries);
2480
2481     AVAHI_LLIST_PREPEND(AvahiSEntryGroup, groups, s->groups, g);
2482     return g;
2483 }
2484
2485 void avahi_s_entry_group_free(AvahiSEntryGroup *g) {
2486     AvahiEntry *e;
2487     
2488     assert(g);
2489     assert(g->server);
2490
2491     for (e = g->entries; e; e = e->by_group_next) {
2492         if (!e->dead) {
2493             avahi_goodbye_entry(g->server, e, 1, 1);
2494             e->dead = 1;
2495         }
2496     }
2497
2498     if (g->register_time_event) {
2499         avahi_time_event_free(g->register_time_event);
2500         g->register_time_event = NULL;
2501     }
2502
2503     g->dead = 1;
2504     
2505     g->server->need_group_cleanup = 1;
2506     g->server->need_entry_cleanup = 1;
2507 }
2508
2509 static void entry_group_commit_real(AvahiSEntryGroup *g) {
2510     assert(g);
2511
2512     gettimeofday(&g->register_time, NULL);
2513
2514     avahi_s_entry_group_change_state(g, AVAHI_ENTRY_GROUP_REGISTERING);
2515
2516     if (!g->dead) {
2517         avahi_announce_group(g->server, g);
2518         avahi_s_entry_group_check_probed(g, 0);
2519     }
2520 }
2521
2522 static void entry_group_register_time_event_callback(AvahiTimeEvent *e, void* userdata) {
2523     AvahiSEntryGroup *g = userdata;
2524     assert(g);
2525
2526 /*     avahi_log_debug("Holdoff passed, waking up and going on."); */
2527
2528     avahi_time_event_free(g->register_time_event);
2529     g->register_time_event = NULL;
2530     
2531     /* Holdoff time passed, so let's start probing */
2532     entry_group_commit_real(g);
2533 }
2534
2535 int avahi_s_entry_group_commit(AvahiSEntryGroup *g) {
2536     struct timeval now;
2537     
2538     assert(g);
2539     assert(!g->dead);
2540
2541     if (g->state != AVAHI_ENTRY_GROUP_UNCOMMITED && g->state != AVAHI_ENTRY_GROUP_COLLISION)
2542         return avahi_server_set_errno(g->server, AVAHI_ERR_BAD_STATE);
2543
2544     g->n_register_try++;
2545
2546     avahi_timeval_add(&g->register_time,
2547                       1000*(g->n_register_try >= AVAHI_RR_RATE_LIMIT_COUNT ?
2548                             AVAHI_RR_HOLDOFF_MSEC_RATE_LIMIT :
2549                             AVAHI_RR_HOLDOFF_MSEC));
2550
2551     gettimeofday(&now, NULL);
2552
2553     if (avahi_timeval_compare(&g->register_time, &now) <= 0) {
2554         /* Holdoff time passed, so let's start probing */
2555 /*         avahi_log_debug("Holdoff passed, directly going on.");  */
2556
2557         entry_group_commit_real(g);
2558     } else {
2559 /*          avahi_log_debug("Holdoff not passed, sleeping.");  */
2560
2561          /* Holdoff time has not yet passed, so let's wait */
2562         assert(!g->register_time_event);
2563         g->register_time_event = avahi_time_event_new(g->server->time_event_queue, &g->register_time, entry_group_register_time_event_callback, g);
2564         
2565         avahi_s_entry_group_change_state(g, AVAHI_ENTRY_GROUP_REGISTERING);
2566     }
2567
2568     return AVAHI_OK;
2569 }
2570
2571 void avahi_s_entry_group_reset(AvahiSEntryGroup *g) {
2572     AvahiEntry *e;
2573     assert(g);
2574     
2575     for (e = g->entries; e; e = e->by_group_next) {
2576         if (!e->dead) {
2577             avahi_goodbye_entry(g->server, e, 1, 1);
2578             e->dead = 1;
2579         }
2580     }
2581     g->server->need_entry_cleanup = 1;
2582
2583     if (g->register_time_event) {
2584         avahi_time_event_free(g->register_time_event);
2585         g->register_time_event = NULL;
2586     }
2587     
2588     g->n_probing = 0;
2589
2590     gettimeofday(&g->register_time, NULL);
2591
2592     avahi_s_entry_group_change_state(g, AVAHI_ENTRY_GROUP_UNCOMMITED);
2593 }
2594
2595 int avahi_entry_is_commited(AvahiEntry *e) {
2596     assert(e);
2597     assert(!e->dead);
2598
2599     return !e->group ||
2600         e->group->state == AVAHI_ENTRY_GROUP_REGISTERING ||
2601         e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED;
2602 }
2603
2604 AvahiEntryGroupState avahi_s_entry_group_get_state(AvahiSEntryGroup *g) {
2605     assert(g);
2606     assert(!g->dead);
2607
2608     return g->state;
2609 }
2610
2611 void avahi_s_entry_group_set_data(AvahiSEntryGroup *g, void* userdata) {
2612     assert(g);
2613
2614     g->userdata = userdata;
2615 }
2616
2617 void* avahi_s_entry_group_get_data(AvahiSEntryGroup *g) {
2618     assert(g);
2619
2620     return g->userdata;
2621 }
2622
2623 int avahi_s_entry_group_is_empty(AvahiSEntryGroup *g) {
2624     AvahiEntry *e;
2625     assert(g);
2626
2627     /* Look for an entry that is not dead */
2628     for (e = g->entries; e; e = e->by_group_next)
2629         if (!e->dead)
2630             return 0;
2631
2632     return 1;
2633 }
2634
2635 const char* avahi_server_get_domain_name(AvahiServer *s) {
2636     assert(s);
2637
2638     return s->domain_name;
2639 }
2640
2641 const char* avahi_server_get_host_name(AvahiServer *s) {
2642     assert(s);
2643
2644     return s->host_name;
2645 }
2646
2647 const char* avahi_server_get_host_name_fqdn(AvahiServer *s) {
2648     assert(s);
2649
2650     return s->host_name_fqdn;
2651 }
2652
2653 void* avahi_server_get_data(AvahiServer *s) {
2654     assert(s);
2655
2656     return s->userdata;
2657 }
2658
2659 void avahi_server_set_data(AvahiServer *s, void* userdata) {
2660     assert(s);
2661
2662     s->userdata = userdata;
2663 }
2664
2665 AvahiServerState avahi_server_get_state(AvahiServer *s) {
2666     assert(s);
2667
2668     return s->state;
2669 }
2670
2671 AvahiServerConfig* avahi_server_config_init(AvahiServerConfig *c) {
2672     assert(c);
2673
2674     memset(c, 0, sizeof(AvahiServerConfig));
2675     c->use_ipv6 = 1;
2676     c->use_ipv4 = 1;
2677     c->host_name = NULL;
2678     c->domain_name = NULL;
2679     c->check_response_ttl = 0;
2680     c->publish_hinfo = 1;
2681     c->publish_addresses = 1;
2682     c->publish_workstation = 1;
2683     c->publish_domain = 1;
2684     c->use_iff_running = 0;
2685     c->enable_reflector = 0;
2686     c->reflect_ipv = 0;
2687     c->add_service_cookie = 1;
2688     c->enable_wide_area = 0;
2689     c->n_wide_area_servers = 0;
2690     c->disallow_other_stacks = 0;
2691     
2692     return c;
2693 }
2694
2695 void avahi_server_config_free(AvahiServerConfig *c) {
2696     assert(c);
2697
2698     avahi_free(c->host_name);
2699     avahi_free(c->domain_name);
2700 }
2701
2702 AvahiServerConfig* avahi_server_config_copy(AvahiServerConfig *ret, const AvahiServerConfig *c) {
2703     char *d = NULL, *h = NULL;
2704     assert(ret);
2705     assert(c);
2706
2707     if (c->host_name)
2708         if (!(h = avahi_strdup(c->host_name)))
2709             return NULL;
2710
2711     if (c->domain_name)
2712         if (!(d = avahi_strdup(c->domain_name))) {
2713             avahi_free(h);
2714             return NULL;
2715         }
2716     
2717     *ret = *c;
2718     ret->host_name = h;
2719     ret->domain_name = d;
2720
2721     return ret;
2722 }
2723
2724 int avahi_server_errno(AvahiServer *s) {
2725     assert(s);
2726     
2727     return s->error;
2728 }
2729
2730 /* Just for internal use */
2731 int avahi_server_set_errno(AvahiServer *s, int error) {
2732     assert(s);
2733
2734     return s->error = error;
2735 }
2736
2737 uint32_t avahi_server_get_local_service_cookie(AvahiServer *s) {
2738     assert(s);
2739
2740     return s->local_service_cookie;
2741 }
2742
2743 int avahi_server_is_service_local(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, const char *name, const char *type, const char*domain) {
2744     AvahiKey *key = NULL;
2745     char n[256];
2746     int ret;
2747     AvahiEntry *e;
2748     
2749     assert(s);
2750     assert(name);
2751     assert(type);
2752     assert(domain);
2753
2754     if (!avahi_is_valid_service_name(name))
2755         return avahi_server_set_errno(s, AVAHI_ERR_INVALID_SERVICE_NAME);
2756
2757     if (!avahi_is_valid_service_type_strict(type))
2758         return avahi_server_set_errno(s, AVAHI_ERR_INVALID_SERVICE_TYPE);
2759
2760     if (domain && !avahi_is_valid_domain_name(domain))
2761         return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
2762
2763     if ((ret = avahi_service_name_join(n, sizeof(n), name, type, domain) < 0))
2764         return avahi_server_set_errno(s, ret);
2765         
2766     if (!(key = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV)))
2767         return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
2768
2769     ret = 0;
2770     
2771     for (e = avahi_hashmap_lookup(s->entries_by_key, key); e; e = e->by_key_next) {
2772
2773         if ((e->interface == interface || e->interface <= 0 || interface <= 0) &&
2774             (e->protocol == protocol || e->protocol == AVAHI_PROTO_UNSPEC || protocol == AVAHI_PROTO_UNSPEC) &&
2775             !(e->flags & AVAHI_PUBLISH_IS_PROXY)) {
2776             ret = 1;
2777             break;
2778         }
2779     }
2780     
2781     avahi_key_unref(key);
2782     
2783     return ret;
2784 }
2785
2786 /** Set the wide area DNS servers */
2787 int avahi_server_set_wide_area_servers(AvahiServer *s, const AvahiAddress *a, unsigned n) {
2788     assert(s);
2789
2790     if (!s->wide_area_lookup_engine)
2791         return avahi_server_set_errno(s, AVAHI_ERR_INVALID_CONFIG);
2792
2793     avahi_wide_area_set_servers(s->wide_area_lookup_engine, a, n);
2794     return AVAHI_OK;
2795 }