]> git.meshlink.io Git - catta/blob - avahi-core/server.c
fix memory corruption in avahi-test
[catta] / avahi-core / server.c
1 /* $Id$ */
2
3 /***
4   This file is part of avahi.
5  
6   avahi is free software; you can redistribute it and/or modify it
7   under the terms of the GNU Lesser General Public License as
8   published by the Free Software Foundation; either version 2.1 of the
9   License, or (at your option) any later version.
10  
11   avahi is distributed in the hope that it will be useful, but WITHOUT
12   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13   or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
14   Public License for more details.
15  
16   You should have received a copy of the GNU Lesser General Public
17   License along with avahi; if not, write to the Free Software
18   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
19   USA.
20 ***/
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <sys/socket.h>
27 #include <arpa/inet.h>
28 #include <string.h>
29 #include <sys/utsname.h>
30 #include <unistd.h>
31 #include <errno.h>
32 #include <stdio.h>
33 #include <assert.h>
34
35 #include <avahi-common/domain.h>
36 #include <avahi-common/timeval.h>
37 #include <avahi-common/malloc.h>
38 #include <avahi-common/error.h>
39
40 #include "server.h"
41 #include "iface.h"
42 #include "socket.h"
43 #include "browse.h"
44 #include "log.h"
45 #include "util.h"
46
47 #define AVAHI_RR_HOLDOFF_MSEC 1000
48 #define AVAHI_RR_HOLDOFF_MSEC_RATE_LIMIT 60000
49 #define AVAHI_RR_RATE_LIMIT_COUNT 15
50
51 static void free_entry(AvahiServer*s, AvahiEntry *e) {
52     AvahiEntry *t;
53
54     assert(s);
55     assert(e);
56
57     avahi_goodbye_entry(s, e, 1);
58
59     /* Remove from linked list */
60     AVAHI_LLIST_REMOVE(AvahiEntry, entries, s->entries, e);
61
62     /* Remove from hash table indexed by name */
63     t = avahi_hashmap_lookup(s->entries_by_key, e->record->key);
64     AVAHI_LLIST_REMOVE(AvahiEntry, by_key, t, e);
65     if (t)
66         avahi_hashmap_replace(s->entries_by_key, t->record->key, t);
67     else
68         avahi_hashmap_remove(s->entries_by_key, e->record->key);
69
70     /* Remove from associated group */
71     if (e->group)
72         AVAHI_LLIST_REMOVE(AvahiEntry, by_group, e->group->entries, e);
73
74     avahi_record_unref(e->record);
75     avahi_free(e);
76 }
77
78 static void free_group(AvahiServer *s, AvahiSEntryGroup *g) {
79     assert(s);
80     assert(g);
81
82     while (g->entries)
83         free_entry(s, g->entries);
84
85     if (g->register_time_event)
86         avahi_time_event_free(g->register_time_event);
87     
88     AVAHI_LLIST_REMOVE(AvahiSEntryGroup, groups, s->groups, g);
89     avahi_free(g);
90 }
91
92 static void cleanup_dead(AvahiServer *s) {
93     assert(s);
94
95     if (s->need_group_cleanup) {
96         AvahiSEntryGroup *g, *next;
97         
98         for (g = s->groups; g; g = next) {
99             next = g->groups_next;
100             
101             if (g->dead)
102                 free_group(s, g);
103         }
104
105         s->need_group_cleanup = 0;
106     }
107
108     if (s->need_entry_cleanup) {
109         AvahiEntry *e, *next;
110         
111         for (e = s->entries; e; e = next) {
112             next = e->entries_next;
113             
114             if (e->dead)
115                 free_entry(s, e);
116         }
117
118         s->need_entry_cleanup = 0;
119     }
120
121     if (s->need_browser_cleanup)
122         avahi_browser_cleanup(s);
123 }
124
125 static void enum_aux_records(AvahiServer *s, AvahiInterface *i, const char *name, uint16_t type, void (*callback)(AvahiServer *s, AvahiRecord *r, int flush_cache, void* userdata), void* userdata) {
126     AvahiKey *k;
127     AvahiEntry *e;
128
129     assert(s);
130     assert(i);
131     assert(name);
132     assert(callback);
133
134     assert(type != AVAHI_DNS_TYPE_ANY);
135
136     if (!(k = avahi_key_new(name, AVAHI_DNS_CLASS_IN, type)))
137         return; /** OOM */
138
139     for (e = avahi_hashmap_lookup(s->entries_by_key, k); e; e = e->by_key_next)
140         if (!e->dead && avahi_entry_is_registered(s, e, i)) 
141             callback(s, e->record, e->flags & AVAHI_ENTRY_UNIQUE, userdata);
142
143     avahi_key_unref(k);
144 }
145
146 void avahi_server_enumerate_aux_records(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, void (*callback)(AvahiServer *s, AvahiRecord *r, int flush_cache, void* userdata), void* userdata) {
147     assert(s);
148     assert(i);
149     assert(r);
150     assert(callback);
151     
152     if (r->key->clazz == AVAHI_DNS_CLASS_IN) {
153         if (r->key->type == AVAHI_DNS_TYPE_PTR) {
154             enum_aux_records(s, i, r->data.ptr.name, AVAHI_DNS_TYPE_SRV, callback, userdata);
155             enum_aux_records(s, i, r->data.ptr.name, AVAHI_DNS_TYPE_TXT, callback, userdata);
156         } else if (r->key->type == AVAHI_DNS_TYPE_SRV) {
157             enum_aux_records(s, i, r->data.srv.name, AVAHI_DNS_TYPE_A, callback, userdata);
158             enum_aux_records(s, i, r->data.srv.name, AVAHI_DNS_TYPE_AAAA, callback, userdata);
159         }
160     }
161 }
162
163 void avahi_server_prepare_response(AvahiServer *s, AvahiInterface *i, AvahiEntry *e, int unicast_response, int auxiliary) {
164     assert(s);
165     assert(i);
166     assert(e);
167
168     avahi_record_list_push(s->record_list, e->record, e->flags & AVAHI_ENTRY_UNIQUE, unicast_response, auxiliary);
169 }
170
171 void avahi_server_prepare_matching_responses(AvahiServer *s, AvahiInterface *i, AvahiKey *k, int unicast_response) {
172     AvahiEntry *e;
173 /*     char *txt; */
174     
175     assert(s);
176     assert(i);
177     assert(k);
178
179 /*     avahi_log_debug("Posting responses matching [%s]", txt = avahi_key_to_string(k)); */
180 /*     avahi_free(txt); */
181
182     if (avahi_key_is_pattern(k)) {
183
184         /* Handle ANY query */
185         
186         for (e = s->entries; e; e = e->entries_next)
187             if (!e->dead && avahi_key_pattern_match(k, e->record->key) && avahi_entry_is_registered(s, e, i))
188                 avahi_server_prepare_response(s, i, e, unicast_response, 0);
189
190     } else {
191
192         /* Handle all other queries */
193         
194         for (e = avahi_hashmap_lookup(s->entries_by_key, k); e; e = e->by_key_next)
195             if (!e->dead && avahi_entry_is_registered(s, e, i))
196                 avahi_server_prepare_response(s, i, e, unicast_response, 0);
197     }
198 }
199
200 static void withdraw_entry(AvahiServer *s, AvahiEntry *e) {
201     assert(s);
202     assert(e);
203     
204     if (e->group) {
205         AvahiEntry *k;
206         
207         for (k = e->group->entries; k; k = k->by_group_next) {
208             if (!k->dead) {
209                 avahi_goodbye_entry(s, k, 0);
210                 k->dead = 1;
211             }
212         }
213
214         e->group->n_probing = 0;
215
216         avahi_s_entry_group_change_state(e->group, AVAHI_ENTRY_GROUP_COLLISION);
217     } else {
218         avahi_goodbye_entry(s, e, 0);
219         e->dead = 1;
220     }
221
222     s->need_entry_cleanup = 1;
223 }
224
225 static void withdraw_rrset(AvahiServer *s, AvahiKey *key) {
226     AvahiEntry *e;
227     
228     assert(s);
229     assert(key);
230
231    for (e = avahi_hashmap_lookup(s->entries_by_key, key); e; e = e->by_key_next)
232        if (!e->dead)
233            withdraw_entry(s, e);
234 }
235
236 static void incoming_probe(AvahiServer *s, AvahiRecord *record, AvahiInterface *i) {
237     AvahiEntry *e, *n;
238     char *t;
239     int ours = 0, won = 0, lost = 0;
240     
241     assert(s);
242     assert(record);
243     assert(i);
244
245     t = avahi_record_to_string(record);
246
247 /*     avahi_log_debug("incoming_probe()");  */
248
249     for (e = avahi_hashmap_lookup(s->entries_by_key, record->key); e; e = n) {
250         int cmp;
251         n = e->by_key_next;
252
253         if (e->dead)
254             continue;
255         
256         if ((cmp = avahi_record_lexicographical_compare(e->record, record)) == 0) {
257             ours = 1;
258             break;
259         } else {
260             
261             if (avahi_entry_is_probing(s, e, i)) {
262                 if (cmp > 0)
263                     won = 1;
264                 else /* cmp < 0 */
265                     lost = 1;
266             }
267         }
268     }
269
270     if (!ours) {
271
272         if (won)
273             avahi_log_debug("Recieved conflicting probe [%s]. Local host won.", t);
274         else if (lost) {
275             avahi_log_debug("Recieved conflicting probe [%s]. Local host lost. Withdrawing.", t);
276             withdraw_rrset(s, record->key);
277         }/*  else */
278 /*             avahi_log_debug("Not conflicting probe"); */
279     }
280
281     avahi_free(t);
282 }
283
284 static int handle_conflict(AvahiServer *s, AvahiInterface *i, AvahiRecord *record, int unique, const AvahiAddress *a) {
285     int valid = 1, ours = 0, conflict = 0, withdraw_immediately = 0;
286     AvahiEntry *e, *n, *conflicting_entry = NULL;
287     
288     assert(s);
289     assert(i);
290     assert(record);
291
292
293 /*     avahi_log_debug("CHECKING FOR CONFLICT: [%s]", t);   */
294
295     for (e = avahi_hashmap_lookup(s->entries_by_key, record->key); e; e = n) {
296         n = e->by_key_next;
297
298         if (e->dead || (!(e->flags & AVAHI_ENTRY_UNIQUE) && !unique))
299             continue;
300
301         /* Either our entry or the other is intended to be unique, so let's check */
302         
303         if (avahi_record_equal_no_ttl(e->record, record)) {
304             ours = 1; /* We have an identical record, so this is no conflict */
305             
306             /* Check wheter there is a TTL conflict */
307             if (record->ttl <= e->record->ttl/2 &&
308                 avahi_entry_is_registered(s, e, i)) {
309                 char *t;
310                 /* Refresh */
311                 t = avahi_record_to_string(record); 
312                 
313                 avahi_log_debug("Recieved record with bad TTL [%s]. Refreshing.", t);
314                 avahi_server_prepare_matching_responses(s, i, e->record->key, 0);
315                 valid = 0;
316                 
317                 avahi_free(t);
318             }
319                 
320             /* There's no need to check the other entries of this RRset */
321             break;
322
323         } else {
324             
325             if (avahi_entry_is_registered(s, e, i)) {
326                 
327                 /* A conflict => we have to return to probe mode */
328                 conflict = 1;
329                 conflicting_entry = e;
330
331             } else if (avahi_entry_is_probing(s, e, i)) {
332
333                 /* We are currently registering a matching record, but
334                  * someone else already claimed it, so let's
335                  * withdraw */
336                 conflict = 1;
337                 withdraw_immediately = 1;
338             }
339         }
340     }
341
342 /*     avahi_log_debug("ours=%i conflict=%i", ours, conflict); */
343
344     if (!ours && conflict) {
345         char *t;
346  
347         valid = 0;
348
349         t = avahi_record_to_string(record); 
350  
351         if (withdraw_immediately) {
352             avahi_log_debug("Recieved conflicting record [%s] with local record to be. Withdrawing.", t);
353             withdraw_rrset(s, record->key);
354         } else {
355             assert(conflicting_entry);
356             avahi_log_debug("Recieved conflicting record [%s]. Resetting our record.", t);
357             avahi_entry_return_to_initial_state(s, conflicting_entry, i);
358
359             /* Local unique records are returned to probing
360              * state. Local shared records are reannounced. */
361         }
362
363         avahi_free(t);
364     }
365
366     return valid;
367 }
368
369 static void append_aux_callback(AvahiServer *s, AvahiRecord *r, int flush_cache, void* userdata) {
370     int *unicast_response = userdata;
371
372     assert(s);
373     assert(r);
374     assert(unicast_response);
375     
376     avahi_record_list_push(s->record_list, r, flush_cache, *unicast_response, 1);
377 }
378
379 static void append_aux_records_to_list(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, int unicast_response) {
380     assert(s);
381     assert(r);
382
383     avahi_server_enumerate_aux_records(s, i, r, append_aux_callback, &unicast_response);
384 }
385
386 void avahi_server_generate_response(AvahiServer *s, AvahiInterface *i, AvahiDnsPacket *p, const AvahiAddress *a, uint16_t port, int legacy_unicast, int immediately) {
387
388     assert(s);
389     assert(i);
390     assert(!legacy_unicast || (a && port > 0 && p));
391
392     if (legacy_unicast) {
393         AvahiDnsPacket *reply;
394         AvahiRecord *r;
395
396         if (!(reply = avahi_dns_packet_new_reply(p, 512 /* unicast DNS maximum packet size is 512 */ , 1, 1)))
397             return; /* OOM */
398         
399         while ((r = avahi_record_list_next(s->record_list, NULL, NULL, NULL))) {
400
401             append_aux_records_to_list(s, i, r, 0);
402             
403             if (avahi_dns_packet_append_record(reply, r, 0, 10))
404                 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
405             else {
406                 char *t = avahi_record_to_string(r);
407                 avahi_log_warn("Record [%s] not fitting in legacy unicast packet, dropping.", t);
408                 avahi_free(t);
409             }
410
411             avahi_record_unref(r);
412         }
413
414         if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) != 0)
415             avahi_interface_send_packet_unicast(i, reply, a, port);
416
417         avahi_dns_packet_free(reply);
418
419     } else {
420         int unicast_response, flush_cache, auxiliary;
421         AvahiDnsPacket *reply = NULL;
422         AvahiRecord *r;
423
424         /* In case the query packet was truncated never respond
425         immediately, because known answer suppression records might be
426         contained in later packets */
427         int tc = p && !!(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_TC);
428         
429         while ((r = avahi_record_list_next(s->record_list, &flush_cache, &unicast_response, &auxiliary))) {
430                         
431             if (!avahi_interface_post_response(i, r, flush_cache, a, immediately || (flush_cache && !tc && !auxiliary)) && unicast_response) {
432
433                 append_aux_records_to_list(s, i, r, unicast_response);
434                 
435                 /* Due to some reasons the record has not been scheduled.
436                  * The client requested an unicast response in that
437                  * case. Therefore we prepare such a response */
438
439                 for (;;) {
440                 
441                     if (!reply) {
442                         assert(p);
443
444                         if (!(reply = avahi_dns_packet_new_reply(p, i->hardware->mtu, 0, 0)))
445                             break; /* OOM */
446                     }
447                 
448                     if (avahi_dns_packet_append_record(reply, r, flush_cache, 0)) {
449
450                         /* Appending this record succeeded, so incremeant
451                          * the specific header field, and return to the caller */
452                         
453                         avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
454
455                         break;
456                     }
457
458                     if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) == 0) {
459                         size_t size;
460
461                         /* The record is too large for one packet, so create a larger packet */
462
463                         avahi_dns_packet_free(reply);
464                         size = avahi_record_get_estimate_size(r) + AVAHI_DNS_PACKET_HEADER_SIZE;
465                         if (size > AVAHI_DNS_PACKET_MAX_SIZE)
466                             size = AVAHI_DNS_PACKET_MAX_SIZE;
467
468                         if (!(reply = avahi_dns_packet_new_reply(p, size, 0, 1)))
469                             break; /* OOM */
470
471                         if (!avahi_dns_packet_append_record(reply, r, flush_cache, 0)) {
472                             char *t;
473                             avahi_dns_packet_free(reply);
474                             t = avahi_record_to_string(r);
475                             avahi_log_warn("Record [%s] too large, doesn't fit in any packet!", t);
476                             avahi_free(t);
477                             break;
478                         } else
479                             avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
480                     }
481
482                     /* Appending the record didn't succeeed, so let's send this packet, and create a new one */
483                     avahi_interface_send_packet_unicast(i, reply, a, port);
484                     avahi_dns_packet_free(reply);
485                     reply = NULL;
486                 }
487             }
488
489             avahi_record_unref(r);
490         }
491
492         if (reply) {
493             if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) != 0) 
494                 avahi_interface_send_packet_unicast(i, reply, a, port);
495             avahi_dns_packet_free(reply);
496         }
497     }
498
499     avahi_record_list_flush(s->record_list);
500 }
501
502
503 static void reflect_response(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, int flush_cache) {
504     AvahiInterface *j;
505     
506     assert(s);
507     assert(i);
508     assert(r);
509
510     if (!s->config.enable_reflector)
511         return;
512
513     for (j = s->monitor->interfaces; j; j = j->interface_next)
514         if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol))
515             avahi_interface_post_response(j, r, flush_cache, NULL, 1);
516 }
517
518 static void* reflect_cache_walk_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, void* userdata) {
519     AvahiServer *s = userdata;
520
521     assert(c);
522     assert(pattern);
523     assert(e);
524     assert(s);
525
526     avahi_record_list_push(s->record_list, e->record, e->cache_flush, 0, 0);
527     return NULL;
528 }
529
530 static void reflect_query(AvahiServer *s, AvahiInterface *i, AvahiKey *k) {
531     AvahiInterface *j;
532     
533     assert(s);
534     assert(i);
535     assert(k);
536
537     if (!s->config.enable_reflector)
538         return;
539
540     for (j = s->monitor->interfaces; j; j = j->interface_next)
541         if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol)) {
542             /* Post the query to other networks */
543             avahi_interface_post_query(j, k, 1);
544
545             /* Reply from caches of other network. This is needed to
546              * "work around" known answer suppression. */
547
548             avahi_cache_walk(j->cache, k, reflect_cache_walk_callback, s);
549         }
550 }
551
552 static void reflect_probe(AvahiServer *s, AvahiInterface *i, AvahiRecord *r) {
553     AvahiInterface *j;
554     
555     assert(s);
556     assert(i);
557     assert(r);
558
559     if (!s->config.enable_reflector)
560         return;
561
562     for (j = s->monitor->interfaces; j; j = j->interface_next)
563         if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol))
564             avahi_interface_post_probe(j, r, 1);
565 }
566
567 static void handle_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, uint16_t port, int legacy_unicast) {
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     s->legacy_unicast_reflect_slots[idx] = slot;
716     slot->id = s->legacy_unicast_reflect_id++;
717     slot->server = s;
718     
719     return slot;
720 }
721
722 static void deallocate_slot(AvahiServer *s, AvahiLegacyUnicastReflectSlot *slot) {
723     unsigned idx;
724
725     assert(s);
726     assert(slot);
727
728     idx = slot->id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
729
730     assert(s->legacy_unicast_reflect_slots[idx] == slot);
731
732     avahi_time_event_free(slot->time_event);
733     
734     avahi_free(slot);
735     s->legacy_unicast_reflect_slots[idx] = NULL;
736 }
737
738 static void free_slots(AvahiServer *s) {
739     unsigned idx;
740     assert(s);
741
742     if (!s->legacy_unicast_reflect_slots)
743         return;
744
745     for (idx = 0; idx < AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS; idx ++)
746         if (s->legacy_unicast_reflect_slots[idx])
747             deallocate_slot(s, s->legacy_unicast_reflect_slots[idx]);
748
749     avahi_free(s->legacy_unicast_reflect_slots);
750     s->legacy_unicast_reflect_slots = NULL;
751 }
752
753 static AvahiLegacyUnicastReflectSlot* find_slot(AvahiServer *s, uint16_t id) {
754     unsigned idx;
755     
756     assert(s);
757
758     if (!s->legacy_unicast_reflect_slots)
759         return NULL;
760     
761     idx = id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
762
763     if (!s->legacy_unicast_reflect_slots[idx] || s->legacy_unicast_reflect_slots[idx]->id != id)
764         return NULL;
765
766     return s->legacy_unicast_reflect_slots[idx];
767 }
768
769 static void legacy_unicast_reflect_slot_timeout(AvahiTimeEvent *e, void *userdata) {
770     AvahiLegacyUnicastReflectSlot *slot = userdata;
771
772     assert(e);
773     assert(slot);
774     assert(slot->time_event == e);
775
776     deallocate_slot(slot->server, slot);
777 }
778
779 static void reflect_legacy_unicast_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, uint16_t port) {
780     AvahiLegacyUnicastReflectSlot *slot;
781     AvahiInterface *j;
782
783     assert(s);
784     assert(p);
785     assert(i);
786     assert(a);
787     assert(port > 0);
788     assert(i->protocol == a->family);
789     
790     if (!s->config.enable_reflector)
791         return;
792
793 /*     avahi_log_debug("legacy unicast reflector"); */
794     
795     /* Reflecting legacy unicast queries is a little more complicated
796        than reflecting normal queries, since we must route the
797        responses back to the right client. Therefore we must store
798        some information for finding the right client contact data for
799        response packets. In contrast to normal queries legacy
800        unicast query and response packets are reflected untouched and
801        are not reassembled into larger packets */
802
803     if (!(slot = allocate_slot(s))) {
804         /* No slot available, we drop this legacy unicast query */
805         avahi_log_warn("No slot available for legacy unicast reflection, dropping query packet.");
806         return;
807     }
808
809     slot->original_id = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID);
810     slot->address = *a;
811     slot->port = port;
812     slot->interface = i->hardware->index;
813
814     avahi_elapse_time(&slot->elapse_time, 2000, 0);
815     slot->time_event = avahi_time_event_new(s->time_event_queue, &slot->elapse_time, legacy_unicast_reflect_slot_timeout, slot);
816
817     /* Patch the packet with our new locally generatedt id */
818     avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
819     
820     for (j = s->monitor->interfaces; j; j = j->interface_next)
821         if (avahi_interface_relevant(j) &&
822             j != i &&
823             (s->config.reflect_ipv || j->protocol == i->protocol)) {
824
825             if (j->protocol == AVAHI_PROTO_INET && s->fd_legacy_unicast_ipv4 >= 0) {
826                 avahi_send_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, j->hardware->index, p, NULL, 0);
827                 } else if (j->protocol == AVAHI_PROTO_INET6 && s->fd_legacy_unicast_ipv6 >= 0)
828                 avahi_send_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, j->hardware->index, p, NULL, 0);
829         }
830
831     /* Reset the id */
832     avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
833 }
834
835 static int originates_from_local_legacy_unicast_socket(AvahiServer *s, const struct sockaddr *sa) {
836     AvahiAddress a;
837     assert(s);
838     assert(sa);
839
840     if (!s->config.enable_reflector)
841         return 0;
842     
843     avahi_address_from_sockaddr(sa, &a);
844
845     if (!avahi_address_is_local(s->monitor, &a))
846         return 0;
847     
848     if (sa->sa_family == AF_INET && s->fd_legacy_unicast_ipv4 >= 0) {
849         struct sockaddr_in lsa;
850         socklen_t l = sizeof(lsa);
851         
852         if (getsockname(s->fd_legacy_unicast_ipv4, &lsa, &l) != 0)
853             avahi_log_warn("getsockname(): %s", strerror(errno));
854         else
855             return lsa.sin_port == ((const struct sockaddr_in*) sa)->sin_port;
856
857     }
858
859     if (sa->sa_family == AF_INET6 && s->fd_legacy_unicast_ipv6 >= 0) {
860         struct sockaddr_in6 lsa;
861         socklen_t l = sizeof(lsa);
862
863         if (getsockname(s->fd_legacy_unicast_ipv6, &lsa, &l) != 0)
864             avahi_log_warn("getsockname(): %s", strerror(errno));
865         else
866             return lsa.sin6_port == ((const struct sockaddr_in6*) sa)->sin6_port;
867     }
868
869     return 0;
870 }
871
872 static int is_mdns_mcast_address(const AvahiAddress *a) {
873     AvahiAddress b;
874     assert(a);
875
876     avahi_address_parse(a->family == AVAHI_PROTO_INET ? AVAHI_IPV4_MCAST_GROUP : AVAHI_IPV6_MCAST_GROUP, a->family, &b);
877     return avahi_address_cmp(a, &b) == 0;
878 }
879
880 static void dispatch_packet(AvahiServer *s, AvahiDnsPacket *p, const struct sockaddr *sa, AvahiAddress *dest, AvahiIfIndex iface, int ttl) {
881     AvahiInterface *i;
882     AvahiAddress a;
883     uint16_t port;
884     
885     assert(s);
886     assert(p);
887     assert(sa);
888     assert(dest);
889     assert(iface > 0);
890
891     if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, sa->sa_family)) ||
892         !avahi_interface_relevant(i)) {
893         avahi_log_warn("Recieved packet from invalid interface.");
894         return;
895     }
896
897 /*     avahi_log_debug("new packet recieved on interface '%s.%i'.", i->hardware->name, i->protocol); */
898
899     port = avahi_port_from_sockaddr(sa);
900     avahi_address_from_sockaddr(sa, &a);
901     
902     if (avahi_address_is_ipv4_in_ipv6(&a))
903         /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
904         return;
905
906     if (originates_from_local_legacy_unicast_socket(s, sa))
907         /* This originates from our local reflector, so let's ignore it */
908         return;
909
910     if (avahi_dns_packet_is_valid(p) < 0) {
911         avahi_log_warn("Recieved invalid packet.");
912         return;
913     }
914
915     if (avahi_dns_packet_is_query(p)) {
916         int legacy_unicast = 0;
917
918         if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT) != 0) {
919             avahi_log_warn("Invalid query packet.");
920             return;
921         }
922
923         if (port != AVAHI_MDNS_PORT) {
924             /* Legacy Unicast */
925
926             if ((avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) != 0 ||
927                  avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0)) {
928                 avahi_log_warn("Invalid legacy unicast query packet.");
929                 return;
930             }
931         
932             legacy_unicast = 1;
933         }
934
935         if (legacy_unicast)
936             reflect_legacy_unicast_query_packet(s, p, i, &a, port);
937         
938         handle_query_packet(s, p, i, &a, port, legacy_unicast);
939         
940 /*         avahi_log_debug("Handled query"); */
941     } else {
942         if (port != AVAHI_MDNS_PORT) {
943             avahi_log_warn("Recieved repsonse with invalid source port %u on interface '%s.%i'", port, i->hardware->name, i->protocol);
944             return;
945         }
946
947         if (ttl != 255 && s->config.check_response_ttl) {
948             avahi_log_warn("Recieved response with invalid TTL %u on interface '%s.%i'.", ttl, i->hardware->name, i->protocol);
949             return;
950         }
951
952         if (!is_mdns_mcast_address(dest) &&
953             !avahi_interface_address_on_link(i, &a)) {
954             avahi_log_warn("Recivied non-local response on interface '%s.%i'.", i->hardware->name, i->protocol);
955             return;
956         }
957         
958         if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT) != 0 ||
959             avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 ||
960             avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0) {
961             avahi_log_warn("Invalid response packet.");
962             return;
963         }
964
965         handle_response_packet(s, p, i, &a);
966 /*         avahi_log_debug("Handled response"); */
967     }
968 }
969
970 static void dispatch_legacy_unicast_packet(AvahiServer *s, AvahiDnsPacket *p, const struct sockaddr *sa, AvahiIfIndex iface, int ttl) {
971     AvahiInterface *i, *j;
972     AvahiAddress a;
973     uint16_t port;
974     AvahiLegacyUnicastReflectSlot *slot;
975     
976     assert(s);
977     assert(p);
978     assert(sa);
979     assert(iface > 0);
980
981     if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, sa->sa_family)) ||
982         !avahi_interface_relevant(i)) {
983         avahi_log_warn("Recieved packet from invalid interface.");
984         return;
985     }
986
987 /*     avahi_log_debug("new legacy unicast packet recieved on interface '%s.%i'.", i->hardware->name, i->protocol); */
988
989     port = avahi_port_from_sockaddr(sa);
990     avahi_address_from_sockaddr(sa, &a);
991     
992     if (avahi_address_is_ipv4_in_ipv6(&a))
993         /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
994         return;
995
996     if (avahi_dns_packet_is_valid(p) < 0 || avahi_dns_packet_is_query(p)) {
997         avahi_log_warn("Recieved invalid packet.");
998         return;
999     }
1000
1001     if (!(slot = find_slot(s, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID)))) {
1002         avahi_log_warn("Recieved legacy unicast response with unknown id");
1003         return;
1004     }
1005
1006     if (!(j = avahi_interface_monitor_get_interface(s->monitor, slot->interface, slot->address.family)) ||
1007         !avahi_interface_relevant(j))
1008         return;
1009
1010     /* Patch the original ID into this response */
1011     avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
1012
1013     /* Forward the response to the correct client */
1014     avahi_interface_send_packet_unicast(j, p, &slot->address, slot->port);
1015
1016     /* Undo changes to packet */
1017     avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
1018 }
1019
1020 static void socket_event(AvahiWatch *w, int fd, AvahiWatchEvent events, void *userdata) {
1021     AvahiServer *s = userdata;
1022     AvahiAddress dest;
1023     AvahiDnsPacket *p;
1024     AvahiIfIndex iface;
1025     uint8_t ttl;
1026     struct sockaddr_in sa;
1027     struct sockaddr_in6 sa6;
1028
1029     assert(w);
1030     assert(fd >= 0);
1031
1032     if (events & AVAHI_WATCH_IN) {
1033     
1034         if (fd == s->fd_ipv4) {
1035             dest.family = AVAHI_PROTO_INET;
1036             if ((p = avahi_recv_dns_packet_ipv4(s->fd_ipv4, &sa, &dest.data.ipv4, &iface, &ttl))) {
1037                 dispatch_packet(s, p, (struct sockaddr*) &sa, &dest, iface, ttl);
1038                 avahi_dns_packet_free(p);
1039             }
1040         } else if (fd == s->fd_ipv6) {
1041             dest.family = AVAHI_PROTO_INET6;
1042
1043             if ((p = avahi_recv_dns_packet_ipv6(s->fd_ipv6, &sa6, &dest.data.ipv6, &iface, &ttl))) {
1044                 dispatch_packet(s, p, (struct sockaddr*) &sa6, &dest, iface, ttl);
1045                 avahi_dns_packet_free(p);
1046             }
1047         } else if (fd == s->fd_legacy_unicast_ipv4) {
1048             dest.family = AVAHI_PROTO_INET;
1049             
1050             if ((p = avahi_recv_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, &sa, &dest.data.ipv4, &iface, &ttl))) {
1051                 dispatch_legacy_unicast_packet(s, p, (struct sockaddr*) &sa, iface, ttl);
1052                 avahi_dns_packet_free(p);
1053             }
1054         } else if (fd == s->fd_legacy_unicast_ipv6) {
1055             dest.family = AVAHI_PROTO_INET6;
1056             
1057             if ((p = avahi_recv_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, &sa6, &dest.data.ipv6, &iface, &ttl))) {
1058                 dispatch_legacy_unicast_packet(s, p, (struct sockaddr*) &sa6, iface, ttl);
1059                 avahi_dns_packet_free(p);
1060             }
1061         }
1062
1063         cleanup_dead(s);
1064     } else {
1065         assert(0);
1066     }
1067 }
1068
1069 static void server_set_state(AvahiServer *s, AvahiServerState state) {
1070     assert(s);
1071
1072     if (s->state == state)
1073         return;
1074     
1075     s->state = state;
1076
1077     if (s->callback)
1078         s->callback(s, state, s->userdata);
1079 }
1080
1081 static void withdraw_host_rrs(AvahiServer *s) {
1082     assert(s);
1083
1084     if (s->hinfo_entry_group)
1085         avahi_s_entry_group_reset(s->hinfo_entry_group);
1086
1087     if (s->browse_domain_entry_group)
1088         avahi_s_entry_group_reset(s->browse_domain_entry_group);
1089
1090     avahi_update_host_rrs(s->monitor, 1);
1091     s->n_host_rr_pending = 0;
1092 }
1093
1094 void avahi_server_decrease_host_rr_pending(AvahiServer *s) {
1095     assert(s);
1096     
1097     assert(s->n_host_rr_pending > 0);
1098
1099     if (--s->n_host_rr_pending == 0)
1100         server_set_state(s, AVAHI_SERVER_RUNNING);
1101 }
1102
1103 void avahi_server_increase_host_rr_pending(AvahiServer *s) {
1104     assert(s);
1105
1106     s->n_host_rr_pending ++;
1107 }
1108
1109 void avahi_host_rr_entry_group_callback(AvahiServer *s, AvahiSEntryGroup *g, AvahiEntryGroupState state, void *userdata) {
1110     assert(s);
1111     assert(g);
1112
1113     if (state == AVAHI_ENTRY_GROUP_REGISTERING &&
1114         s->state == AVAHI_SERVER_REGISTERING)
1115         avahi_server_increase_host_rr_pending(s);
1116     
1117     else if (state == AVAHI_ENTRY_GROUP_COLLISION &&
1118         (s->state == AVAHI_SERVER_REGISTERING || s->state == AVAHI_SERVER_RUNNING)) {
1119         withdraw_host_rrs(s);
1120         server_set_state(s, AVAHI_SERVER_COLLISION);
1121         
1122     } else if (state == AVAHI_ENTRY_GROUP_ESTABLISHED &&
1123                s->state == AVAHI_SERVER_REGISTERING)
1124         avahi_server_decrease_host_rr_pending(s);
1125 }
1126
1127 static void register_hinfo(AvahiServer *s) {
1128     struct utsname utsname;
1129     AvahiRecord *r;
1130     
1131     assert(s);
1132     
1133     if (!s->config.publish_hinfo)
1134         return;
1135
1136     if (s->hinfo_entry_group)
1137         assert(avahi_s_entry_group_is_empty(s->hinfo_entry_group));
1138     else
1139         s->hinfo_entry_group = avahi_s_entry_group_new(s, avahi_host_rr_entry_group_callback, NULL);
1140
1141     if (!s->hinfo_entry_group) {
1142         avahi_log_warn("Failed to create HINFO entry group: %s", avahi_strerror(s->error));
1143         return;
1144     }
1145     
1146     /* Fill in HINFO rr */
1147     if ((r = avahi_record_new_full(s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_HINFO, AVAHI_DEFAULT_TTL_HOST_NAME))) {
1148         uname(&utsname);
1149         r->data.hinfo.cpu = avahi_strdup(avahi_strup(utsname.machine));
1150         r->data.hinfo.os = avahi_strdup(avahi_strup(utsname.sysname));
1151
1152         if (avahi_server_add(s, s->hinfo_entry_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_ENTRY_UNIQUE, r) < 0) {
1153             avahi_log_warn("Failed to add HINFO RR: %s", avahi_strerror(s->error));
1154             return;
1155         }
1156
1157         avahi_record_unref(r);
1158     }
1159
1160     if (avahi_s_entry_group_commit(s->hinfo_entry_group) < 0)
1161         avahi_log_warn("Failed to commit HINFO entry group: %s", avahi_strerror(s->error));
1162
1163 }
1164
1165 static void register_localhost(AvahiServer *s) {
1166     AvahiAddress a;
1167     assert(s);
1168     
1169     /* Add localhost entries */
1170     avahi_address_parse("127.0.0.1", AVAHI_PROTO_INET, &a);
1171     avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_ENTRY_NOPROBE|AVAHI_ENTRY_NOANNOUNCE, "localhost", &a);
1172
1173     avahi_address_parse("::1", AVAHI_PROTO_INET6, &a);
1174     avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_ENTRY_NOPROBE|AVAHI_ENTRY_NOANNOUNCE, "ip6-localhost", &a);
1175 }
1176
1177 static void register_browse_domain(AvahiServer *s) {
1178     assert(s);
1179
1180     if (!s->config.publish_domain)
1181         return;
1182
1183     if (s->browse_domain_entry_group)
1184         assert(avahi_s_entry_group_is_empty(s->browse_domain_entry_group));
1185     else
1186         s->browse_domain_entry_group = avahi_s_entry_group_new(s, NULL, NULL);
1187
1188     if (!s->browse_domain_entry_group) {
1189         avahi_log_warn("Failed to create browse domain entry group: %s", avahi_strerror(s->error));
1190         return;
1191     }
1192     
1193     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) {
1194         avahi_log_warn("Failed to add browse domain RR: %s", avahi_strerror(s->error));
1195         return;
1196     }
1197
1198     if (avahi_s_entry_group_commit(s->browse_domain_entry_group) < 0)
1199         avahi_log_warn("Failed to commit browse domain entry group: %s", avahi_strerror(s->error));
1200 }
1201
1202 static void register_stuff(AvahiServer *s) {
1203     assert(s);
1204
1205     server_set_state(s, AVAHI_SERVER_REGISTERING);
1206     s->n_host_rr_pending ++;  /** Make sure that the state isn't changed tp AVAHI_SERVER_RUNNING too early */
1207
1208     register_hinfo(s);
1209     register_browse_domain(s);
1210     avahi_update_host_rrs(s->monitor, 0);
1211
1212     s->n_host_rr_pending --;
1213     
1214     if (s->n_host_rr_pending == 0)
1215         server_set_state(s, AVAHI_SERVER_RUNNING);
1216 }
1217
1218 static void update_fqdn(AvahiServer *s) {
1219     char *n;
1220     
1221     assert(s);
1222     assert(s->host_name);
1223     assert(s->domain_name);
1224
1225     if (!(n = avahi_strdup_printf("%s.%s", s->host_name, s->domain_name)))
1226         return; /* OOM */
1227
1228     avahi_free(s->host_name_fqdn);
1229     s->host_name_fqdn = n;
1230 }
1231
1232 int avahi_server_set_host_name(AvahiServer *s, const char *host_name) {
1233     assert(s);
1234     assert(host_name);
1235
1236     if (host_name && !avahi_is_valid_host_name(host_name))
1237         return avahi_server_set_errno(s, AVAHI_ERR_INVALID_HOST_NAME);
1238
1239     withdraw_host_rrs(s);
1240
1241     avahi_free(s->host_name);
1242     s->host_name = host_name ? avahi_normalize_name(host_name) : avahi_get_host_name();
1243     s->host_name[strcspn(s->host_name, ".")] = 0;
1244     update_fqdn(s);
1245
1246     register_stuff(s);
1247     return AVAHI_OK;
1248 }
1249
1250 int avahi_server_set_domain_name(AvahiServer *s, const char *domain_name) {
1251     assert(s);
1252     assert(domain_name);
1253
1254     if (domain_name && !avahi_is_valid_domain_name(domain_name))
1255         return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
1256
1257     withdraw_host_rrs(s);
1258
1259     avahi_free(s->domain_name);
1260     s->domain_name = domain_name ? avahi_normalize_name(domain_name) : avahi_strdup("local");
1261     update_fqdn(s);
1262
1263     register_stuff(s);
1264     return AVAHI_OK;
1265 }
1266
1267 static int valid_server_config(const AvahiServerConfig *sc) {
1268
1269     if (sc->host_name && !avahi_is_valid_host_name(sc->host_name))
1270         return AVAHI_ERR_INVALID_HOST_NAME;
1271     
1272     if (sc->domain_name && !avahi_is_valid_domain_name(sc->domain_name))
1273         return AVAHI_ERR_INVALID_DOMAIN_NAME;
1274
1275     return AVAHI_OK;
1276 }
1277
1278 static int setup_sockets(AvahiServer *s) {
1279     assert(s);
1280     
1281     s->fd_ipv4 = s->config.use_ipv4 ? avahi_open_socket_ipv4() : -1;
1282     s->fd_ipv6 = s->config.use_ipv6 ? avahi_open_socket_ipv6() : -1;
1283     
1284     if (s->fd_ipv6 < 0 && s->fd_ipv4 < 0)
1285         return AVAHI_ERR_NO_NETWORK;
1286
1287     if (s->fd_ipv4 < 0 && s->config.use_ipv4)
1288         avahi_log_notice("Failed to create IPv4 socket, proceeding in IPv6 only mode");
1289     else if (s->fd_ipv6 < 0 && s->config.use_ipv6)
1290         avahi_log_notice("Failed to create IPv6 socket, proceeding in IPv4 only mode");
1291
1292     s->fd_legacy_unicast_ipv4 = s->fd_ipv4 >= 0 && s->config.enable_reflector ? avahi_open_legacy_unicast_socket_ipv4() : -1;
1293     s->fd_legacy_unicast_ipv6 = s->fd_ipv6 >= 0 && s->config.enable_reflector ? avahi_open_legacy_unicast_socket_ipv6() : -1;
1294
1295     s->watch_ipv4 = s->watch_ipv6 = s->watch_legacy_unicast_ipv4 = s->watch_legacy_unicast_ipv6 = NULL;
1296     
1297     if (s->fd_ipv4 >= 0)
1298         s->watch_ipv4 = s->poll_api->watch_new(s->poll_api, s->fd_ipv4, AVAHI_WATCH_IN, socket_event, s);
1299     if (s->fd_ipv6 >= 0)
1300         s->watch_ipv6 = s->poll_api->watch_new(s->poll_api, s->fd_ipv6, AVAHI_WATCH_IN, socket_event, s);
1301     if (s->fd_legacy_unicast_ipv4 >= 0)
1302         s->watch_legacy_unicast_ipv4 = s->poll_api->watch_new(s->poll_api, s->fd_legacy_unicast_ipv4, AVAHI_WATCH_IN, socket_event, s);
1303     if (s->fd_legacy_unicast_ipv6 >= 0)
1304         s->watch_legacy_unicast_ipv6 = s->poll_api->watch_new(s->poll_api, s->fd_legacy_unicast_ipv6, AVAHI_WATCH_IN, socket_event, s);
1305
1306     return 0;
1307 }
1308
1309 AvahiServer *avahi_server_new(const AvahiPoll *poll_api, const AvahiServerConfig *sc, AvahiServerCallback callback, void* userdata, int *error) {
1310     AvahiServer *s;
1311     int e;
1312     
1313     if ((e = valid_server_config(sc)) < 0) {
1314         if (error)
1315             *error = e;
1316         return NULL;
1317     }
1318     
1319     if (!(s = avahi_new(AvahiServer, 1))) {
1320         if (error)
1321             *error = AVAHI_ERR_NO_MEMORY;
1322
1323         return NULL;
1324     }
1325
1326     s->poll_api = poll_api;
1327
1328     if (sc)
1329         avahi_server_config_copy(&s->config, sc);
1330     else
1331         avahi_server_config_init(&s->config);
1332
1333     if ((e = setup_sockets(s)) < 0) {
1334         if (error)
1335             *error = e;
1336
1337         avahi_server_config_free(&s->config);
1338         avahi_free(s);
1339         
1340         return NULL;
1341     }
1342
1343     s->n_host_rr_pending = 0;
1344     s->need_entry_cleanup = 0;
1345     s->need_group_cleanup = 0;
1346     s->need_browser_cleanup = 0;
1347     
1348     s->time_event_queue = avahi_time_event_queue_new(poll_api);
1349     
1350     s->callback = callback;
1351     s->userdata = userdata;
1352     
1353     s->entries_by_key = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL);
1354     AVAHI_LLIST_HEAD_INIT(AvahiEntry, s->entries);
1355     AVAHI_LLIST_HEAD_INIT(AvahiGroup, s->groups);
1356
1357     s->record_browser_hashmap = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL);
1358     AVAHI_LLIST_HEAD_INIT(AvahiSRecordBrowser, s->record_browsers);
1359     AVAHI_LLIST_HEAD_INIT(AvahiSHostNameResolver, s->host_name_resolvers);
1360     AVAHI_LLIST_HEAD_INIT(AvahiSAddressResolver, s->address_resolvers);
1361     AVAHI_LLIST_HEAD_INIT(AvahiSDomainBrowser, s->domain_browsers);
1362     AVAHI_LLIST_HEAD_INIT(AvahiSServiceTypeBrowser, s->service_type_browsers);
1363     AVAHI_LLIST_HEAD_INIT(AvahiSServiceBrowser, s->service_browsers);
1364     AVAHI_LLIST_HEAD_INIT(AvahiSServiceResolver, s->service_resolvers);
1365     AVAHI_LLIST_HEAD_INIT(AvahiSDNSServerBrowser, s->dns_server_browsers);
1366
1367     s->legacy_unicast_reflect_slots = NULL;
1368     s->legacy_unicast_reflect_id = 0;
1369     
1370     /* Get host name */
1371     s->host_name = s->config.host_name ? avahi_normalize_name(s->config.host_name) : avahi_get_host_name();
1372     s->host_name[strcspn(s->host_name, ".")] = 0;
1373     s->domain_name = s->config.domain_name ? avahi_normalize_name(s->config.domain_name) : avahi_strdup("local");
1374     s->host_name_fqdn = NULL;
1375     update_fqdn(s);
1376
1377     s->record_list = avahi_record_list_new();
1378
1379     s->state = AVAHI_SERVER_INVALID;
1380
1381     s->monitor = avahi_interface_monitor_new(s);
1382     avahi_interface_monitor_sync(s->monitor);
1383
1384     register_localhost(s);
1385
1386     s->hinfo_entry_group = NULL;
1387     s->browse_domain_entry_group = NULL;
1388     register_stuff(s);
1389
1390     s->error = AVAHI_OK;
1391     
1392     return s;
1393 }
1394
1395 void avahi_server_free(AvahiServer* s) {
1396     assert(s);
1397
1398     while(s->entries)
1399         free_entry(s, s->entries);
1400
1401     avahi_interface_monitor_free(s->monitor);
1402
1403     while (s->groups)
1404         free_group(s, s->groups);
1405
1406     free_slots(s);
1407
1408     while (s->dns_server_browsers)
1409         avahi_s_dns_server_browser_free(s->dns_server_browsers);
1410     while (s->host_name_resolvers)
1411         avahi_s_host_name_resolver_free(s->host_name_resolvers);
1412     while (s->address_resolvers)
1413         avahi_s_address_resolver_free(s->address_resolvers);
1414     while (s->domain_browsers)
1415         avahi_s_domain_browser_free(s->domain_browsers);
1416     while (s->service_type_browsers)
1417         avahi_s_service_type_browser_free(s->service_type_browsers);
1418     while (s->service_browsers)
1419         avahi_s_service_browser_free(s->service_browsers);
1420     while (s->service_resolvers)
1421         avahi_s_service_resolver_free(s->service_resolvers);
1422     while (s->record_browsers)
1423         avahi_s_record_browser_destroy(s->record_browsers);
1424     
1425     avahi_hashmap_free(s->record_browser_hashmap);
1426     avahi_hashmap_free(s->entries_by_key);
1427
1428     avahi_time_event_queue_free(s->time_event_queue);
1429
1430     avahi_record_list_free(s->record_list);
1431
1432     if (s->watch_ipv4)
1433         s->poll_api->watch_free(s->watch_ipv4);
1434     if (s->watch_ipv6)
1435         s->poll_api->watch_free(s->watch_ipv6);
1436     if (s->watch_legacy_unicast_ipv4)
1437         s->poll_api->watch_free(s->watch_legacy_unicast_ipv4);
1438     if (s->watch_legacy_unicast_ipv6)
1439         s->poll_api->watch_free(s->watch_legacy_unicast_ipv6);
1440     
1441     if (s->fd_ipv4 >= 0)
1442         close(s->fd_ipv4);
1443     if (s->fd_ipv6 >= 0)
1444         close(s->fd_ipv6);
1445     if (s->fd_legacy_unicast_ipv4 >= 0)
1446         close(s->fd_legacy_unicast_ipv4);
1447     if (s->fd_legacy_unicast_ipv6 >= 0)
1448         close(s->fd_legacy_unicast_ipv6);
1449
1450     avahi_free(s->host_name);
1451     avahi_free(s->domain_name);
1452     avahi_free(s->host_name_fqdn);
1453
1454     avahi_server_config_free(&s->config);
1455
1456     avahi_free(s);
1457 }
1458
1459 static int check_record_conflict(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiRecord *r, AvahiEntryFlags flags) {
1460     AvahiEntry *e;
1461     
1462     assert(s);
1463     assert(r);
1464
1465     for (e = avahi_hashmap_lookup(s->entries_by_key, r->key); e; e = e->by_key_next) {
1466         if (e->dead)
1467             continue;
1468
1469         if (!(flags & AVAHI_ENTRY_UNIQUE) && !(e->flags & AVAHI_ENTRY_UNIQUE))
1470             continue;
1471         
1472         if ((flags & AVAHI_ENTRY_ALLOWMUTIPLE) && (e->flags & AVAHI_ENTRY_ALLOWMUTIPLE) )
1473             continue;
1474
1475         if ((interface <= 0 ||
1476              e->interface <= 0 ||
1477              e->interface == interface) &&
1478             (protocol == AVAHI_PROTO_UNSPEC ||
1479              e->protocol == AVAHI_PROTO_UNSPEC ||
1480              e->protocol == protocol))
1481
1482             return -1;
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