]> git.meshlink.io Git - catta/blob - avahi-core/server.c
* doxygen documentation updates
[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     register_hinfo(s);
1206     register_browse_domain(s);
1207     avahi_update_host_rrs(s->monitor, 0);
1208
1209     if (s->n_host_rr_pending == 0)
1210         server_set_state(s, AVAHI_SERVER_RUNNING);
1211 }
1212
1213 static void update_fqdn(AvahiServer *s) {
1214     char *n;
1215     
1216     assert(s);
1217     assert(s->host_name);
1218     assert(s->domain_name);
1219
1220     if (!(n = avahi_strdup_printf("%s.%s", s->host_name, s->domain_name)))
1221         return; /* OOM */
1222
1223     avahi_free(s->host_name_fqdn);
1224     s->host_name_fqdn = n;
1225 }
1226
1227 int avahi_server_set_host_name(AvahiServer *s, const char *host_name) {
1228     assert(s);
1229     assert(host_name);
1230
1231     if (host_name && !avahi_is_valid_host_name(host_name))
1232         return avahi_server_set_errno(s, AVAHI_ERR_INVALID_HOST_NAME);
1233
1234     withdraw_host_rrs(s);
1235
1236     avahi_free(s->host_name);
1237     s->host_name = host_name ? avahi_normalize_name(host_name) : avahi_get_host_name();
1238     s->host_name[strcspn(s->host_name, ".")] = 0;
1239     update_fqdn(s);
1240
1241     register_stuff(s);
1242     return AVAHI_OK;
1243 }
1244
1245 int avahi_server_set_domain_name(AvahiServer *s, const char *domain_name) {
1246     assert(s);
1247     assert(domain_name);
1248
1249     if (domain_name && !avahi_is_valid_domain_name(domain_name))
1250         return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
1251
1252     withdraw_host_rrs(s);
1253
1254     avahi_free(s->domain_name);
1255     s->domain_name = domain_name ? avahi_normalize_name(domain_name) : avahi_strdup("local");
1256     update_fqdn(s);
1257
1258     register_stuff(s);
1259     return AVAHI_OK;
1260 }
1261
1262 static int valid_server_config(const AvahiServerConfig *sc) {
1263
1264     if (sc->host_name && !avahi_is_valid_host_name(sc->host_name))
1265         return AVAHI_ERR_INVALID_HOST_NAME;
1266     
1267     if (sc->domain_name && !avahi_is_valid_domain_name(sc->domain_name))
1268         return AVAHI_ERR_INVALID_DOMAIN_NAME;
1269
1270     return AVAHI_OK;
1271 }
1272
1273 static int setup_sockets(AvahiServer *s) {
1274     assert(s);
1275     
1276     s->fd_ipv4 = s->config.use_ipv4 ? avahi_open_socket_ipv4() : -1;
1277     s->fd_ipv6 = s->config.use_ipv6 ? avahi_open_socket_ipv6() : -1;
1278     
1279     if (s->fd_ipv6 < 0 && s->fd_ipv4 < 0)
1280         return AVAHI_ERR_NO_NETWORK;
1281
1282     if (s->fd_ipv4 < 0 && s->config.use_ipv4)
1283         avahi_log_notice("Failed to create IPv4 socket, proceeding in IPv6 only mode");
1284     else if (s->fd_ipv6 < 0 && s->config.use_ipv6)
1285         avahi_log_notice("Failed to create IPv6 socket, proceeding in IPv4 only mode");
1286
1287     s->fd_legacy_unicast_ipv4 = s->fd_ipv4 >= 0 && s->config.enable_reflector ? avahi_open_legacy_unicast_socket_ipv4() : -1;
1288     s->fd_legacy_unicast_ipv6 = s->fd_ipv6 >= 0 && s->config.enable_reflector ? avahi_open_legacy_unicast_socket_ipv6() : -1;
1289
1290     s->watch_ipv4 = s->watch_ipv6 = s->watch_legacy_unicast_ipv4 = s->watch_legacy_unicast_ipv6 = NULL;
1291     
1292     if (s->fd_ipv4 >= 0)
1293         s->watch_ipv4 = s->poll_api->watch_new(s->poll_api, s->fd_ipv4, AVAHI_WATCH_IN, socket_event, s);
1294     if (s->fd_ipv6 >= 0)
1295         s->watch_ipv6 = s->poll_api->watch_new(s->poll_api, s->fd_ipv6, AVAHI_WATCH_IN, socket_event, s);
1296     if (s->fd_legacy_unicast_ipv4 >= 0)
1297         s->watch_legacy_unicast_ipv4 = s->poll_api->watch_new(s->poll_api, s->fd_legacy_unicast_ipv4, AVAHI_WATCH_IN, socket_event, s);
1298     if (s->fd_legacy_unicast_ipv6 >= 0)
1299         s->watch_legacy_unicast_ipv6 = s->poll_api->watch_new(s->poll_api, s->fd_legacy_unicast_ipv6, AVAHI_WATCH_IN, socket_event, s);
1300
1301     return 0;
1302 }
1303
1304 AvahiServer *avahi_server_new(const AvahiPoll *poll_api, const AvahiServerConfig *sc, AvahiServerCallback callback, void* userdata, int *error) {
1305     AvahiServer *s;
1306     int e;
1307     
1308     if ((e = valid_server_config(sc)) < 0) {
1309         if (error)
1310             *error = e;
1311         return NULL;
1312     }
1313     
1314     if (!(s = avahi_new(AvahiServer, 1))) {
1315         if (error)
1316             *error = AVAHI_ERR_NO_MEMORY;
1317
1318         return NULL;
1319     }
1320
1321     s->poll_api = poll_api;
1322
1323     if (sc)
1324         avahi_server_config_copy(&s->config, sc);
1325     else
1326         avahi_server_config_init(&s->config);
1327
1328     if ((e = setup_sockets(s)) < 0) {
1329         if (error)
1330             *error = e;
1331
1332         avahi_server_config_free(&s->config);
1333         avahi_free(s);
1334         
1335         return NULL;
1336     }
1337
1338     s->n_host_rr_pending = 0;
1339     s->need_entry_cleanup = 0;
1340     s->need_group_cleanup = 0;
1341     s->need_browser_cleanup = 0;
1342     
1343     s->time_event_queue = avahi_time_event_queue_new(poll_api);
1344     
1345     s->callback = callback;
1346     s->userdata = userdata;
1347     
1348     s->entries_by_key = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL);
1349     AVAHI_LLIST_HEAD_INIT(AvahiEntry, s->entries);
1350     AVAHI_LLIST_HEAD_INIT(AvahiGroup, s->groups);
1351
1352     s->record_browser_hashmap = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL);
1353     AVAHI_LLIST_HEAD_INIT(AvahiSRecordBrowser, s->record_browsers);
1354     AVAHI_LLIST_HEAD_INIT(AvahiSHostNameResolver, s->host_name_resolvers);
1355     AVAHI_LLIST_HEAD_INIT(AvahiSAddressResolver, s->address_resolvers);
1356     AVAHI_LLIST_HEAD_INIT(AvahiSDomainBrowser, s->domain_browsers);
1357     AVAHI_LLIST_HEAD_INIT(AvahiSServiceTypeBrowser, s->service_type_browsers);
1358     AVAHI_LLIST_HEAD_INIT(AvahiSServiceBrowser, s->service_browsers);
1359     AVAHI_LLIST_HEAD_INIT(AvahiSServiceResolver, s->service_resolvers);
1360     AVAHI_LLIST_HEAD_INIT(AvahiSDNSServerBrowser, s->dns_server_browsers);
1361
1362     s->legacy_unicast_reflect_slots = NULL;
1363     s->legacy_unicast_reflect_id = 0;
1364     
1365     /* Get host name */
1366     s->host_name = s->config.host_name ? avahi_normalize_name(s->config.host_name) : avahi_get_host_name();
1367     s->host_name[strcspn(s->host_name, ".")] = 0;
1368     s->domain_name = s->config.domain_name ? avahi_normalize_name(s->config.domain_name) : avahi_strdup("local");
1369     s->host_name_fqdn = NULL;
1370     update_fqdn(s);
1371
1372     s->record_list = avahi_record_list_new();
1373
1374     s->state = AVAHI_SERVER_INVALID;
1375
1376     s->monitor = avahi_interface_monitor_new(s);
1377     avahi_interface_monitor_sync(s->monitor);
1378
1379     register_localhost(s);
1380
1381     s->hinfo_entry_group = NULL;
1382     s->browse_domain_entry_group = NULL;
1383     register_stuff(s);
1384
1385     s->error = AVAHI_OK;
1386     
1387     return s;
1388 }
1389
1390 void avahi_server_free(AvahiServer* s) {
1391     assert(s);
1392
1393     while(s->entries)
1394         free_entry(s, s->entries);
1395
1396     avahi_interface_monitor_free(s->monitor);
1397
1398     while (s->groups)
1399         free_group(s, s->groups);
1400
1401     free_slots(s);
1402
1403     while (s->dns_server_browsers)
1404         avahi_s_dns_server_browser_free(s->dns_server_browsers);
1405     while (s->host_name_resolvers)
1406         avahi_s_host_name_resolver_free(s->host_name_resolvers);
1407     while (s->address_resolvers)
1408         avahi_s_address_resolver_free(s->address_resolvers);
1409     while (s->domain_browsers)
1410         avahi_s_domain_browser_free(s->domain_browsers);
1411     while (s->service_type_browsers)
1412         avahi_s_service_type_browser_free(s->service_type_browsers);
1413     while (s->service_browsers)
1414         avahi_s_service_browser_free(s->service_browsers);
1415     while (s->service_resolvers)
1416         avahi_s_service_resolver_free(s->service_resolvers);
1417     while (s->record_browsers)
1418         avahi_s_record_browser_destroy(s->record_browsers);
1419     
1420     avahi_hashmap_free(s->record_browser_hashmap);
1421     avahi_hashmap_free(s->entries_by_key);
1422
1423     avahi_time_event_queue_free(s->time_event_queue);
1424
1425     avahi_record_list_free(s->record_list);
1426
1427     if (s->watch_ipv4)
1428         s->poll_api->watch_free(s->watch_ipv4);
1429     if (s->watch_ipv6)
1430         s->poll_api->watch_free(s->watch_ipv6);
1431     if (s->watch_legacy_unicast_ipv4)
1432         s->poll_api->watch_free(s->watch_legacy_unicast_ipv4);
1433     if (s->watch_legacy_unicast_ipv6)
1434         s->poll_api->watch_free(s->watch_legacy_unicast_ipv6);
1435     
1436     if (s->fd_ipv4 >= 0)
1437         close(s->fd_ipv4);
1438     if (s->fd_ipv6 >= 0)
1439         close(s->fd_ipv6);
1440     if (s->fd_legacy_unicast_ipv4 >= 0)
1441         close(s->fd_legacy_unicast_ipv4);
1442     if (s->fd_legacy_unicast_ipv6 >= 0)
1443         close(s->fd_legacy_unicast_ipv6);
1444
1445     avahi_free(s->host_name);
1446     avahi_free(s->domain_name);
1447     avahi_free(s->host_name_fqdn);
1448
1449     avahi_server_config_free(&s->config);
1450
1451     avahi_free(s);
1452 }
1453
1454 static int check_record_conflict(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiRecord *r, AvahiEntryFlags flags) {
1455     AvahiEntry *e;
1456     
1457     assert(s);
1458     assert(r);
1459
1460     for (e = avahi_hashmap_lookup(s->entries_by_key, r->key); e; e = e->by_key_next) {
1461         if (e->dead)
1462             continue;
1463
1464         if (!(flags & AVAHI_ENTRY_UNIQUE) && !(e->flags & AVAHI_ENTRY_UNIQUE))
1465             continue;
1466         
1467         if ((flags & AVAHI_ENTRY_ALLOWMUTIPLE) && (e->flags & AVAHI_ENTRY_ALLOWMUTIPLE) )
1468             continue;
1469
1470         if (interface <= 0 ||
1471             e->interface <= 0 ||
1472             e->interface == interface ||
1473             protocol == AVAHI_PROTO_UNSPEC ||
1474             e->protocol == AVAHI_PROTO_UNSPEC ||
1475             e->protocol == protocol)
1476
1477             return -1;
1478
1479     }
1480
1481     return 0;
1482 }
1483
1484 int avahi_server_add(
1485     AvahiServer *s,
1486     AvahiSEntryGroup *g,
1487     AvahiIfIndex interface,
1488     AvahiProtocol protocol,
1489     AvahiEntryFlags flags,
1490     AvahiRecord *r) {
1491     
1492     AvahiEntry *e, *t;
1493     
1494     assert(s);
1495     assert(r);
1496
1497     if (r->ttl == 0)
1498         return avahi_server_set_errno(s, AVAHI_ERR_INVALID_TTL);
1499
1500     if (avahi_key_is_pattern(r->key))
1501         return avahi_server_set_errno(s, AVAHI_ERR_IS_PATTERN);
1502
1503     if (!avahi_record_is_valid(r))
1504         return avahi_server_set_errno(s, AVAHI_ERR_INVALID_RECORD);
1505
1506     if (check_record_conflict(s, interface, protocol, r, flags) < 0)
1507         return avahi_server_set_errno(s, AVAHI_ERR_LOCAL_COLLISION);
1508
1509     if (!(e = avahi_new(AvahiEntry, 1)))
1510         return avahi_server_set_errno(s, AVAHI_ERR_NO_NETWORK);
1511         
1512     e->server = s;
1513     e->record = avahi_record_ref(r);
1514     e->group = g;
1515     e->interface = interface;
1516     e->protocol = protocol;
1517     e->flags = flags;
1518     e->dead = 0;
1519
1520     AVAHI_LLIST_HEAD_INIT(AvahiAnnouncement, e->announcements);
1521
1522     AVAHI_LLIST_PREPEND(AvahiEntry, entries, s->entries, e);
1523
1524     /* Insert into hash table indexed by name */
1525     t = avahi_hashmap_lookup(s->entries_by_key, e->record->key);
1526     AVAHI_LLIST_PREPEND(AvahiEntry, by_key, t, e);
1527     avahi_hashmap_replace(s->entries_by_key, e->record->key, t);
1528
1529     /* Insert into group list */
1530     if (g)
1531         AVAHI_LLIST_PREPEND(AvahiEntry, by_group, g->entries, e); 
1532
1533     avahi_announce_entry(s, e);
1534
1535     return 0;
1536 }
1537
1538 const AvahiRecord *avahi_server_iterate(AvahiServer *s, AvahiSEntryGroup *g, void **state) {
1539     AvahiEntry **e = (AvahiEntry**) state;
1540     assert(s);
1541     assert(e);
1542
1543     if (!*e)
1544         *e = g ? g->entries : s->entries;
1545     
1546     while (*e && (*e)->dead)
1547         *e = g ? (*e)->by_group_next : (*e)->entries_next;
1548         
1549     if (!*e)
1550         return NULL;
1551
1552     return avahi_record_ref((*e)->record);
1553 }
1554
1555 int avahi_server_dump(AvahiServer *s, AvahiDumpCallback callback, void* userdata) {
1556     AvahiEntry *e;
1557     
1558     assert(s);
1559     assert(callback);
1560
1561     callback(";;; ZONE DUMP FOLLOWS ;;;", userdata);
1562
1563     for (e = s->entries; e; e = e->entries_next) {
1564         char *t;
1565         char ln[256];
1566
1567         if (e->dead)
1568             continue;
1569         
1570         if (!(t = avahi_record_to_string(e->record)))
1571             return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1572         
1573         snprintf(ln, sizeof(ln), "%s ; iface=%i proto=%i", t, e->interface, e->protocol);
1574         avahi_free(t);
1575
1576         callback(ln, userdata);
1577     }
1578
1579     avahi_dump_caches(s->monitor, callback, userdata);
1580     return AVAHI_OK;
1581 }
1582
1583 int avahi_server_add_ptr(
1584     AvahiServer *s,
1585     AvahiSEntryGroup *g,
1586     AvahiIfIndex interface,
1587     AvahiProtocol protocol,
1588     AvahiEntryFlags flags,
1589     uint32_t ttl,
1590     const char *name,
1591     const char *dest) {
1592
1593     AvahiRecord *r;
1594     int ret;
1595
1596     assert(s);
1597     assert(dest);
1598
1599     if (!(r = avahi_record_new_full(name ? name : s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR, ttl)))
1600         return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1601         
1602     r->data.ptr.name = avahi_normalize_name(dest);
1603     ret = avahi_server_add(s, g, interface, protocol, flags, r);
1604     avahi_record_unref(r);
1605     return ret;
1606 }
1607
1608 int avahi_server_add_address(
1609     AvahiServer *s,
1610     AvahiSEntryGroup *g,
1611     AvahiIfIndex interface,
1612     AvahiProtocol protocol,
1613     AvahiEntryFlags flags,
1614     const char *name,
1615     AvahiAddress *a) {
1616
1617     char *n = NULL;
1618     int ret = AVAHI_OK;
1619     assert(s);
1620     assert(a);
1621
1622     if (name) {
1623         if (!(n = avahi_normalize_name(name)))
1624             return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1625
1626         name = n;
1627     } else
1628         name = s->host_name_fqdn;
1629
1630     if (!avahi_is_valid_domain_name(name)) {
1631         ret = avahi_server_set_errno(s, AVAHI_ERR_INVALID_HOST_NAME);
1632         goto fail;
1633     }
1634     
1635     if (a->family == AVAHI_PROTO_INET) {
1636         char *reverse;
1637         AvahiRecord  *r;
1638
1639         if (!(r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A, AVAHI_DEFAULT_TTL_HOST_NAME))) {
1640             ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1641             goto fail;
1642         }
1643         
1644         r->data.a.address = a->data.ipv4;
1645         ret = avahi_server_add(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE | AVAHI_ENTRY_ALLOWMUTIPLE, r);
1646         avahi_record_unref(r);
1647
1648         if (ret < 0)
1649             goto fail;
1650         
1651         if (!(reverse = avahi_reverse_lookup_name_ipv4(&a->data.ipv4))) {
1652             ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1653             goto fail;
1654         }
1655
1656         ret = avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, AVAHI_DEFAULT_TTL_HOST_NAME, reverse, name);
1657         avahi_free(reverse);
1658
1659     } else {
1660         char *reverse;
1661         AvahiRecord *r;
1662
1663         assert(a->family == AVAHI_PROTO_INET6);
1664             
1665         if (!(r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA, AVAHI_DEFAULT_TTL_HOST_NAME))) {
1666             ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1667             goto fail;
1668         }
1669         
1670         r->data.aaaa.address = a->data.ipv6;
1671         ret = avahi_server_add(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE | AVAHI_ENTRY_ALLOWMUTIPLE, r);
1672         avahi_record_unref(r);
1673
1674         if (ret < 0)
1675             goto fail;
1676
1677         if (!(reverse = avahi_reverse_lookup_name_ipv6_arpa(&a->data.ipv6))) {
1678             ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1679             goto fail;
1680         }
1681             
1682         ret = avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, AVAHI_DEFAULT_TTL_HOST_NAME, reverse, name);
1683         avahi_free(reverse);
1684
1685         if (ret < 0)
1686             goto fail;
1687     
1688         if (!(reverse = avahi_reverse_lookup_name_ipv6_int(&a->data.ipv6))) {
1689             ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1690             goto fail;
1691         }
1692
1693         ret = avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, AVAHI_DEFAULT_TTL_HOST_NAME, reverse, name);
1694         avahi_free(reverse);
1695     }
1696
1697 fail:
1698     
1699     avahi_free(n);
1700
1701     return ret;
1702 }
1703
1704 static int server_add_txt_strlst_nocopy(
1705     AvahiServer *s,
1706     AvahiSEntryGroup *g,
1707     AvahiIfIndex interface,
1708     AvahiProtocol protocol,
1709     AvahiEntryFlags flags,
1710     uint32_t ttl,
1711     const char *name,
1712     AvahiStringList *strlst) {
1713
1714     AvahiRecord *r;
1715     int ret;
1716     
1717     assert(s);
1718     
1719     if (!(r = avahi_record_new_full(name ? name : s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_TXT, ttl)))
1720         return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1721     
1722     r->data.txt.string_list = strlst;
1723     ret = avahi_server_add(s, g, interface, protocol, flags, r);
1724     avahi_record_unref(r);
1725
1726     return ret;
1727 }
1728
1729 int avahi_server_add_txt_strlst(
1730     AvahiServer *s,
1731     AvahiSEntryGroup *g,
1732     AvahiIfIndex interface,
1733     AvahiProtocol protocol,
1734     AvahiEntryFlags flags,
1735     uint32_t ttl,
1736     const char *name,
1737     AvahiStringList *strlst) {
1738
1739     assert(s);
1740
1741     return server_add_txt_strlst_nocopy(s, g, interface, protocol, flags, ttl, name, avahi_string_list_copy(strlst));
1742 }
1743
1744 int avahi_server_add_txt_va(
1745     AvahiServer *s,
1746     AvahiSEntryGroup *g,
1747     AvahiIfIndex interface,
1748     AvahiProtocol protocol,
1749     AvahiEntryFlags flags,
1750     uint32_t ttl,
1751     const char *name,
1752     va_list va) {
1753
1754     assert(s);
1755
1756     return server_add_txt_strlst_nocopy(s, g, interface, protocol, flags, ttl, name, avahi_string_list_new_va(va));
1757 }
1758
1759 int avahi_server_add_txt(
1760     AvahiServer *s,
1761     AvahiSEntryGroup *g,
1762     AvahiIfIndex interface,
1763     AvahiProtocol protocol,
1764     AvahiEntryFlags flags,
1765     uint32_t ttl,
1766     const char *name,
1767     ...) {
1768
1769     va_list va;
1770     int ret;
1771     
1772     assert(s);
1773
1774     va_start(va, name);
1775     ret = avahi_server_add_txt_va(s, g, interface, protocol, flags, ttl, name, va);
1776     va_end(va);
1777
1778     return ret;
1779 }
1780
1781 static void escape_service_name(char *d, size_t size, const char *s) {
1782     assert(d);
1783     assert(size);
1784     assert(s);
1785
1786     while (*s && size >= 2) {
1787         if (*s == '.' || *s == '\\') {
1788             if (size < 3)
1789                 break;
1790
1791             *(d++) = '\\';
1792             size--;
1793         }
1794             
1795         *(d++) = *(s++);
1796         size--;
1797     }
1798
1799     assert(size > 0);
1800     *(d++) = 0;
1801 }
1802
1803 static int server_add_service_strlst_nocopy(
1804     AvahiServer *s,
1805     AvahiSEntryGroup *g,
1806     AvahiIfIndex interface,
1807     AvahiProtocol protocol,
1808     const char *name,
1809     const char *type,
1810     const char *domain,
1811     const char *host,
1812     uint16_t port,
1813     AvahiStringList *strlst) {
1814
1815     char ptr_name[256], svc_name[256], ename[64], enum_ptr[256];
1816     char *t = NULL, *d = NULL, *h = NULL;
1817     AvahiRecord *r = NULL;
1818     int ret = AVAHI_OK;
1819     
1820     assert(s);
1821     assert(type);
1822     assert(name);
1823
1824     if (!avahi_is_valid_service_name(name))
1825         return avahi_server_set_errno(s, AVAHI_ERR_INVALID_SERVICE_NAME);
1826
1827     if (!avahi_is_valid_service_type(type))
1828         return avahi_server_set_errno(s, AVAHI_ERR_INVALID_SERVICE_TYPE);
1829
1830     if (domain && !avahi_is_valid_domain_name(domain))
1831         return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
1832
1833     if (host && !avahi_is_valid_domain_name(host))
1834         return avahi_server_set_errno(s, AVAHI_ERR_INVALID_HOST_NAME);
1835
1836     escape_service_name(ename, sizeof(ename), name);
1837
1838     if (!domain)
1839         domain = s->domain_name;
1840
1841     if (!host)
1842         host = s->host_name_fqdn;
1843
1844     if (!(d = avahi_normalize_name(domain)) ||
1845         !(t = avahi_normalize_name(type)) ||
1846         !(h = avahi_normalize_name(host))) {
1847         ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1848         goto fail;
1849     }
1850
1851     snprintf(ptr_name, sizeof(ptr_name), "%s.%s", t, d);
1852     snprintf(svc_name, sizeof(svc_name), "%s.%s.%s", ename, t, d);
1853
1854     if ((ret = avahi_server_add_ptr(s, g, interface, protocol, AVAHI_ENTRY_NULL, AVAHI_DEFAULT_TTL, ptr_name, svc_name)) < 0)
1855         goto fail;
1856
1857     if (!(r = avahi_record_new_full(svc_name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV, AVAHI_DEFAULT_TTL_HOST_NAME))) {
1858         ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1859         goto fail;
1860     }
1861     
1862     r->data.srv.priority = 0;
1863     r->data.srv.weight = 0;
1864     r->data.srv.port = port;
1865     r->data.srv.name = h;
1866     h = NULL;
1867     ret = avahi_server_add(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE, r);
1868     avahi_record_unref(r);
1869
1870     if (ret < 0)
1871         goto fail;
1872
1873     ret = server_add_txt_strlst_nocopy(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE, AVAHI_DEFAULT_TTL, svc_name, strlst);
1874     strlst = NULL;
1875
1876     if (ret < 0)
1877         goto fail;
1878
1879     snprintf(enum_ptr, sizeof(enum_ptr), "_services._dns-sd._udp.%s", d);
1880     ret = avahi_server_add_ptr(s, g, interface, protocol, AVAHI_ENTRY_NULL, AVAHI_DEFAULT_TTL, enum_ptr, ptr_name);
1881
1882 fail:
1883     
1884     avahi_free(d);
1885     avahi_free(t);
1886     avahi_free(h);
1887
1888     avahi_string_list_free(strlst);
1889     
1890     return ret;
1891 }
1892
1893 int avahi_server_add_service_strlst(
1894     AvahiServer *s,
1895     AvahiSEntryGroup *g,
1896     AvahiIfIndex interface,
1897     AvahiProtocol protocol,
1898     const char *name,
1899     const char *type,
1900     const char *domain,
1901     const char *host,
1902     uint16_t port,
1903     AvahiStringList *strlst) {
1904
1905     assert(s);
1906     assert(type);
1907     assert(name);
1908
1909     return server_add_service_strlst_nocopy(s, g, interface, protocol, name, type, domain, host, port, avahi_string_list_copy(strlst));
1910 }
1911
1912 int avahi_server_add_service_va(
1913     AvahiServer *s,
1914     AvahiSEntryGroup *g,
1915     AvahiIfIndex interface,
1916     AvahiProtocol protocol,
1917     const char *name,
1918     const char *type,
1919     const char *domain,
1920     const char *host,
1921     uint16_t port,
1922     va_list va){
1923
1924     assert(s);
1925     assert(type);
1926     assert(name);
1927
1928     return server_add_service_strlst_nocopy(s, g, interface, protocol, name, type, domain, host, port, avahi_string_list_new_va(va));
1929 }
1930
1931 int avahi_server_add_service(
1932     AvahiServer *s,
1933     AvahiSEntryGroup *g,
1934     AvahiIfIndex interface,
1935     AvahiProtocol protocol,
1936     const char *name,
1937     const char *type,
1938     const char *domain,
1939     const char *host,
1940     uint16_t port,
1941     ... ){
1942
1943     va_list va;
1944     int ret;
1945     
1946     assert(s);
1947     assert(type);
1948     assert(name);
1949
1950     va_start(va, port);
1951     ret = avahi_server_add_service_va(s, g, interface, protocol, name, type, domain, host, port, va);
1952     va_end(va);
1953     return ret;
1954 }
1955
1956 static void hexstring(char *s, size_t sl, const void *p, size_t pl) {
1957     static const char hex[] = "0123456789abcdef";
1958     int b = 0;
1959     const uint8_t *k = p;
1960
1961     while (sl > 1 && pl > 0) {
1962         *(s++) = hex[(b ? *k : *k >> 4) & 0xF];
1963
1964         if (b) {
1965             k++;
1966             pl--;
1967         }
1968         
1969         b = !b;
1970
1971         sl--;
1972     }
1973
1974     if (sl > 0)
1975         *s = 0;
1976 }
1977
1978 int avahi_server_add_dns_server_address(
1979     AvahiServer *s,
1980     AvahiSEntryGroup *g,
1981     AvahiIfIndex interface,
1982     AvahiProtocol protocol,
1983     const char *domain,
1984     AvahiDNSServerType type,
1985     const AvahiAddress *address,
1986     uint16_t port /** should be 53 */) {
1987
1988     AvahiRecord *r;
1989     int ret;
1990     char n[64] = "ip-";
1991
1992     assert(s);
1993     assert(address);
1994     assert(type == AVAHI_DNS_SERVER_UPDATE || type == AVAHI_DNS_SERVER_RESOLVE);
1995     assert(address->family == AVAHI_PROTO_INET || address->family == AVAHI_PROTO_INET6);
1996
1997     if (port == 0)
1998         return avahi_server_set_errno(s, AVAHI_ERR_INVALID_PORT);
1999     
2000     if (domain && !avahi_is_valid_domain_name(domain))
2001         return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
2002
2003     if (address->family == AVAHI_PROTO_INET) {
2004         hexstring(n+3, sizeof(n)-3, &address->data, 4);
2005         r = avahi_record_new_full(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A, AVAHI_DEFAULT_TTL_HOST_NAME);
2006         r->data.a.address = address->data.ipv4;
2007     } else {
2008         hexstring(n+3, sizeof(n)-3, &address->data, 6);
2009         r = avahi_record_new_full(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA, AVAHI_DEFAULT_TTL_HOST_NAME);
2010         r->data.aaaa.address = address->data.ipv6;
2011     }
2012
2013     if (!r)
2014         return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
2015     
2016     ret = avahi_server_add(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE | AVAHI_ENTRY_ALLOWMUTIPLE, r);
2017     avahi_record_unref(r);
2018
2019     if (ret < 0)
2020         return ret;
2021     
2022     return avahi_server_add_dns_server_name(s, g, interface, protocol, domain, type, n, port);
2023 }
2024
2025 int avahi_server_add_dns_server_name(
2026     AvahiServer *s,
2027     AvahiSEntryGroup *g,
2028     AvahiIfIndex interface,
2029     AvahiProtocol protocol,
2030     const char *domain,
2031     AvahiDNSServerType type,
2032     const char *name,
2033     uint16_t port /** should be 53 */) {
2034
2035     int ret = -1;
2036     char t[256], *d = NULL, *n = NULL;
2037     AvahiRecord *r;
2038     
2039     assert(s);
2040     assert(name);
2041     assert(type == AVAHI_DNS_SERVER_UPDATE || type == AVAHI_DNS_SERVER_RESOLVE);
2042
2043     if (port == 0)
2044         return avahi_server_set_errno(s, AVAHI_ERR_INVALID_PORT);
2045
2046     if (!avahi_is_valid_domain_name(name))
2047         return avahi_server_set_errno(s, AVAHI_ERR_INVALID_HOST_NAME);
2048
2049     if (domain && !avahi_is_valid_domain_name(domain))
2050         return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
2051
2052     
2053     if (!domain)
2054         domain = s->domain_name;
2055
2056     if (!(n = avahi_normalize_name(name)) ||
2057         !(d = avahi_normalize_name(domain))) {
2058         avahi_free(n);
2059         avahi_free(d);
2060         return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
2061     }
2062
2063     snprintf(t, sizeof(t), "%s.%s", type == AVAHI_DNS_SERVER_RESOLVE ? "_domain._udp" : "_dns-update._udp", d);
2064     avahi_free(d);
2065     
2066     if (!(r = avahi_record_new_full(t, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV, AVAHI_DEFAULT_TTL_HOST_NAME))) {
2067         avahi_free(n);
2068         return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
2069     }
2070     
2071     r->data.srv.priority = 0;
2072     r->data.srv.weight = 0;
2073     r->data.srv.port = port;
2074     r->data.srv.name = n;
2075     ret = avahi_server_add(s, g, interface, protocol, AVAHI_ENTRY_NULL, r);
2076     avahi_record_unref(r);
2077
2078     return ret;
2079 }
2080
2081 static void post_query_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, void* userdata) {
2082     AvahiKey *k = userdata;
2083
2084     assert(m);
2085     assert(i);
2086     assert(k);
2087
2088     avahi_interface_post_query(i, k, 0);
2089 }
2090
2091 void avahi_server_post_query(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiKey *key) {
2092     assert(s);
2093     assert(key);
2094
2095     avahi_interface_monitor_walk(s->monitor, interface, protocol, post_query_callback, key);
2096 }
2097
2098 void avahi_s_entry_group_change_state(AvahiSEntryGroup *g, AvahiEntryGroupState state) {
2099     assert(g);
2100
2101     if (g->state == state)
2102         return;
2103
2104     assert(state <= AVAHI_ENTRY_GROUP_COLLISION);
2105
2106     g->state = state;
2107     
2108     if (g->callback)
2109         g->callback(g->server, g, state, g->userdata);
2110 }
2111
2112 AvahiSEntryGroup *avahi_s_entry_group_new(AvahiServer *s, AvahiSEntryGroupCallback callback, void* userdata) {
2113     AvahiSEntryGroup *g;
2114     
2115     assert(s);
2116
2117     if (!(g = avahi_new(AvahiSEntryGroup, 1))) {
2118         avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
2119         return NULL;
2120     }
2121     
2122     g->server = s;
2123     g->callback = callback;
2124     g->userdata = userdata;
2125     g->dead = 0;
2126     g->state = AVAHI_ENTRY_GROUP_UNCOMMITED;
2127     g->n_probing = 0;
2128     g->n_register_try = 0;
2129     g->register_time_event = NULL;
2130     g->register_time.tv_sec = 0;
2131     g->register_time.tv_usec = 0;
2132     AVAHI_LLIST_HEAD_INIT(AvahiEntry, g->entries);
2133
2134     AVAHI_LLIST_PREPEND(AvahiSEntryGroup, groups, s->groups, g);
2135     return g;
2136 }
2137
2138 void avahi_s_entry_group_free(AvahiSEntryGroup *g) {
2139     AvahiEntry *e;
2140     
2141     assert(g);
2142     assert(g->server);
2143
2144     for (e = g->entries; e; e = e->by_group_next) {
2145         if (!e->dead) {
2146             avahi_goodbye_entry(g->server, e, 1);
2147             e->dead = 1;
2148         }
2149     }
2150
2151     if (g->register_time_event) {
2152         avahi_time_event_free(g->register_time_event);
2153         g->register_time_event = NULL;
2154     }
2155
2156     g->dead = 1;
2157     
2158     g->server->need_group_cleanup = 1;
2159     g->server->need_entry_cleanup = 1;
2160 }
2161
2162 static void entry_group_commit_real(AvahiSEntryGroup *g) {
2163     assert(g);
2164
2165     gettimeofday(&g->register_time, NULL);
2166
2167     avahi_s_entry_group_change_state(g, AVAHI_ENTRY_GROUP_REGISTERING);
2168
2169     if (!g->dead) {
2170         avahi_announce_group(g->server, g);
2171         avahi_s_entry_group_check_probed(g, 0);
2172     }
2173 }
2174
2175 static void entry_group_register_time_event_callback(AvahiTimeEvent *e, void* userdata) {
2176     AvahiSEntryGroup *g = userdata;
2177     assert(g);
2178
2179 /*     avahi_log_debug("Holdoff passed, waking up and going on."); */
2180
2181     avahi_time_event_free(g->register_time_event);
2182     g->register_time_event = NULL;
2183     
2184     /* Holdoff time passed, so let's start probing */
2185     entry_group_commit_real(g);
2186 }
2187
2188 int avahi_s_entry_group_commit(AvahiSEntryGroup *g) {
2189     struct timeval now;
2190     
2191     assert(g);
2192     assert(!g->dead);
2193
2194     if (g->state != AVAHI_ENTRY_GROUP_UNCOMMITED && g->state != AVAHI_ENTRY_GROUP_COLLISION)
2195         return avahi_server_set_errno(g->server, AVAHI_ERR_BAD_STATE);
2196
2197     g->n_register_try++;
2198
2199     avahi_timeval_add(&g->register_time,
2200                       1000*(g->n_register_try >= AVAHI_RR_RATE_LIMIT_COUNT ?
2201                             AVAHI_RR_HOLDOFF_MSEC_RATE_LIMIT :
2202                             AVAHI_RR_HOLDOFF_MSEC));
2203
2204     gettimeofday(&now, NULL);
2205
2206     if (avahi_timeval_compare(&g->register_time, &now) <= 0) {
2207         /* Holdoff time passed, so let's start probing */
2208 /*         avahi_log_debug("Holdoff passed, directly going on.");  */
2209
2210         entry_group_commit_real(g);
2211     } else {
2212 /*          avahi_log_debug("Holdoff not passed, sleeping.");  */
2213
2214          /* Holdoff time has not yet passed, so let's wait */
2215         assert(!g->register_time_event);
2216         g->register_time_event = avahi_time_event_new(g->server->time_event_queue, &g->register_time, entry_group_register_time_event_callback, g);
2217         
2218         avahi_s_entry_group_change_state(g, AVAHI_ENTRY_GROUP_REGISTERING);
2219     }
2220
2221     return AVAHI_OK;
2222 }
2223
2224 void avahi_s_entry_group_reset(AvahiSEntryGroup *g) {
2225     AvahiEntry *e;
2226     assert(g);
2227     
2228     if (g->register_time_event) {
2229         avahi_time_event_free(g->register_time_event);
2230         g->register_time_event = NULL;
2231     }
2232     
2233     for (e = g->entries; e; e = e->by_group_next) {
2234         if (!e->dead) {
2235             avahi_goodbye_entry(g->server, e, 1);
2236             e->dead = 1;
2237         }
2238     }
2239
2240     if (g->register_time_event) {
2241         avahi_time_event_free(g->register_time_event);
2242         g->register_time_event = NULL;
2243     }
2244     
2245     g->server->need_entry_cleanup = 1;
2246     g->n_probing = 0;
2247
2248     avahi_s_entry_group_change_state(g, AVAHI_ENTRY_GROUP_UNCOMMITED);
2249 }
2250
2251 int avahi_entry_is_commited(AvahiEntry *e) {
2252     assert(e);
2253     assert(!e->dead);
2254
2255     return !e->group ||
2256         e->group->state == AVAHI_ENTRY_GROUP_REGISTERING ||
2257         e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED;
2258 }
2259
2260 AvahiEntryGroupState avahi_s_entry_group_get_state(AvahiSEntryGroup *g) {
2261     assert(g);
2262     assert(!g->dead);
2263
2264     return g->state;
2265 }
2266
2267 void avahi_s_entry_group_set_data(AvahiSEntryGroup *g, void* userdata) {
2268     assert(g);
2269
2270     g->userdata = userdata;
2271 }
2272
2273 void* avahi_s_entry_group_get_data(AvahiSEntryGroup *g) {
2274     assert(g);
2275
2276     return g->userdata;
2277 }
2278
2279 int avahi_s_entry_group_is_empty(AvahiSEntryGroup *g) {
2280     AvahiEntry *e;
2281     assert(g);
2282
2283     /* Look for an entry that is not dead */
2284     for (e = g->entries; e; e = e->by_group_next)
2285         if (!e->dead)
2286             return 0;
2287
2288     return 1;
2289 }
2290
2291 const char* avahi_server_get_domain_name(AvahiServer *s) {
2292     assert(s);
2293
2294     return s->domain_name;
2295 }
2296
2297 const char* avahi_server_get_host_name(AvahiServer *s) {
2298     assert(s);
2299
2300     return s->host_name;
2301 }
2302
2303 const char* avahi_server_get_host_name_fqdn(AvahiServer *s) {
2304     assert(s);
2305
2306     return s->host_name_fqdn;
2307 }
2308
2309 void* avahi_server_get_data(AvahiServer *s) {
2310     assert(s);
2311
2312     return s->userdata;
2313 }
2314
2315 void avahi_server_set_data(AvahiServer *s, void* userdata) {
2316     assert(s);
2317
2318     s->userdata = userdata;
2319 }
2320
2321 AvahiServerState avahi_server_get_state(AvahiServer *s) {
2322     assert(s);
2323
2324     return s->state;
2325 }
2326
2327 AvahiServerConfig* avahi_server_config_init(AvahiServerConfig *c) {
2328     assert(c);
2329
2330     memset(c, 0, sizeof(AvahiServerConfig));
2331     c->use_ipv6 = 1;
2332     c->use_ipv4 = 1;
2333     c->host_name = NULL;
2334     c->domain_name = NULL;
2335     c->check_response_ttl = 0;
2336     c->publish_hinfo = 1;
2337     c->publish_addresses = 1;
2338     c->publish_workstation = 1;
2339     c->publish_domain = 1;
2340     c->use_iff_running = 0;
2341     c->enable_reflector = 0;
2342     c->reflect_ipv = 0;
2343     
2344     return c;
2345 }
2346
2347 void avahi_server_config_free(AvahiServerConfig *c) {
2348     assert(c);
2349
2350     avahi_free(c->host_name);
2351     avahi_free(c->domain_name);
2352 }
2353
2354 AvahiServerConfig* avahi_server_config_copy(AvahiServerConfig *ret, const AvahiServerConfig *c) {
2355     char *d = NULL, *h = NULL;
2356     assert(ret);
2357     assert(c);
2358
2359     if (c->host_name)
2360         if (!(h = avahi_strdup(c->host_name)))
2361             return NULL;
2362
2363     if (c->domain_name)
2364         if (!(d = avahi_strdup(c->domain_name))) {
2365             avahi_free(h);
2366             return NULL;
2367         }
2368     
2369     *ret = *c;
2370     ret->host_name = h;
2371     ret->domain_name = d;
2372
2373     return ret;
2374 }
2375
2376 int avahi_server_errno(AvahiServer *s) {
2377     assert(s);
2378     
2379     return s->error;
2380 }
2381
2382 /* Just for internal use */
2383 int avahi_server_set_errno(AvahiServer *s, int error) {
2384     assert(s);
2385
2386     return s->error = error;
2387 }
2388