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