]> git.meshlink.io Git - catta/blob - avahi-core/entry.c
* many trivial fix to make avahi compile on OpenBSD
[catta] / avahi-core / entry.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 <string.h>
27 #include <unistd.h>
28 #include <errno.h>
29 #include <stdio.h>
30 #include <assert.h>
31 #include <stdlib.h>
32
33 #include <arpa/inet.h>
34
35 #include <sys/utsname.h>
36 #include <sys/types.h>
37 #include <sys/socket.h>
38
39 #include <avahi-common/domain.h>
40 #include <avahi-common/timeval.h>
41 #include <avahi-common/malloc.h>
42 #include <avahi-common/error.h>
43
44 #include "internal.h"
45 #include "iface.h"
46 #include "socket.h"
47 #include "browse.h"
48 #include "log.h"
49 #include "util.h"
50 #include "dns-srv-rr.h"
51 #include "rr-util.h"
52
53 void avahi_entry_free(AvahiServer*s, AvahiEntry *e) {
54     AvahiEntry *t;
55
56     assert(s);
57     assert(e);
58
59     avahi_goodbye_entry(s, e, 1, 1);
60
61     /* Remove from linked list */
62     AVAHI_LLIST_REMOVE(AvahiEntry, entries, s->entries, e);
63
64     /* Remove from hash table indexed by name */
65     t = avahi_hashmap_lookup(s->entries_by_key, e->record->key);
66     AVAHI_LLIST_REMOVE(AvahiEntry, by_key, t, e);
67     if (t)
68         avahi_hashmap_replace(s->entries_by_key, t->record->key, t);
69     else
70         avahi_hashmap_remove(s->entries_by_key, e->record->key);
71
72     /* Remove from associated group */
73     if (e->group)
74         AVAHI_LLIST_REMOVE(AvahiEntry, by_group, e->group->entries, e);
75
76     avahi_record_unref(e->record);
77     avahi_free(e);
78 }
79
80 void avahi_entry_group_free(AvahiServer *s, AvahiSEntryGroup *g) {
81     assert(s);
82     assert(g);
83
84     while (g->entries)
85         avahi_entry_free(s, g->entries);
86
87     if (g->register_time_event)
88         avahi_time_event_free(g->register_time_event);
89     
90     AVAHI_LLIST_REMOVE(AvahiSEntryGroup, groups, s->groups, g);
91     avahi_free(g);
92 }
93
94 void avahi_cleanup_dead_entries(AvahiServer *s) {
95     assert(s);
96
97     if (s->need_group_cleanup) {
98         AvahiSEntryGroup *g, *next;
99         
100         for (g = s->groups; g; g = next) {
101             next = g->groups_next;
102             
103             if (g->dead)
104                 avahi_entry_group_free(s, g);
105         }
106
107         s->need_group_cleanup = 0;
108     }
109
110     if (s->need_entry_cleanup) {
111         AvahiEntry *e, *next;
112         
113         for (e = s->entries; e; e = next) {
114             next = e->entries_next;
115             
116             if (e->dead)
117                 avahi_entry_free(s, e);
118         }
119
120         s->need_entry_cleanup = 0;
121     }
122
123     if (s->need_browser_cleanup)
124         avahi_browser_cleanup(s);
125 }
126
127 static int check_record_conflict(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiRecord *r, AvahiPublishFlags flags) {
128     AvahiEntry *e;
129     
130     assert(s);
131     assert(r);
132
133     for (e = avahi_hashmap_lookup(s->entries_by_key, r->key); e; e = e->by_key_next) {
134         if (e->dead)
135             continue;
136
137         if (!(flags & AVAHI_PUBLISH_UNIQUE) && !(e->flags & AVAHI_PUBLISH_UNIQUE))
138             continue;
139         
140         if ((flags & AVAHI_PUBLISH_ALLOW_MULTIPLE) && (e->flags & AVAHI_PUBLISH_ALLOW_MULTIPLE) )
141             continue;
142
143         if ((interface <= 0 ||
144              e->interface <= 0 ||
145              e->interface == interface) &&
146             (protocol == AVAHI_PROTO_UNSPEC ||
147              e->protocol == AVAHI_PROTO_UNSPEC ||
148              e->protocol == protocol))
149
150             return -1;
151     }
152
153     return 0;
154 }
155
156 static AvahiEntry * server_add_internal(
157     AvahiServer *s,
158     AvahiSEntryGroup *g,
159     AvahiIfIndex interface,
160     AvahiProtocol protocol,
161     AvahiPublishFlags flags,
162     AvahiRecord *r) {
163
164     AvahiEntry *e;
165     
166     assert(s);
167     assert(r);
168
169     AVAHI_CHECK_VALIDITY_RETURN_NULL(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE);
170     AVAHI_CHECK_VALIDITY_RETURN_NULL(s, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL);
171     AVAHI_CHECK_VALIDITY_RETURN_NULL(s, AVAHI_FLAGS_VALID(
172                                          flags,
173                                          AVAHI_PUBLISH_NO_ANNOUNCE|
174                                          AVAHI_PUBLISH_NO_PROBE|
175                                          AVAHI_PUBLISH_UNIQUE|
176                                          AVAHI_PUBLISH_ALLOW_MULTIPLE|
177                                          AVAHI_PUBLISH_UPDATE), AVAHI_ERR_INVALID_FLAGS);
178     AVAHI_CHECK_VALIDITY_RETURN_NULL(s, avahi_is_valid_domain_name(r->key->name), AVAHI_ERR_INVALID_HOST_NAME);
179     AVAHI_CHECK_VALIDITY_RETURN_NULL(s, r->ttl != 0, AVAHI_ERR_INVALID_TTL);
180     AVAHI_CHECK_VALIDITY_RETURN_NULL(s, !avahi_key_is_pattern(r->key), AVAHI_ERR_IS_PATTERN);
181     AVAHI_CHECK_VALIDITY_RETURN_NULL(s, avahi_record_is_valid(r), AVAHI_ERR_INVALID_RECORD);
182     AVAHI_CHECK_VALIDITY_RETURN_NULL(s, r->key->clazz == AVAHI_DNS_CLASS_IN, AVAHI_ERR_INVALID_DNS_CLASS);
183     AVAHI_CHECK_VALIDITY_RETURN_NULL(s,
184                                      (r->key->type != 0) &&
185                                      (r->key->type != AVAHI_DNS_TYPE_ANY) &&
186                                      (r->key->type != AVAHI_DNS_TYPE_OPT) &&
187                                      (r->key->type != AVAHI_DNS_TYPE_TKEY) &&
188                                      (r->key->type != AVAHI_DNS_TYPE_TSIG) &&
189                                      (r->key->type != AVAHI_DNS_TYPE_IXFR) &&
190                                      (r->key->type != AVAHI_DNS_TYPE_AXFR), AVAHI_ERR_INVALID_DNS_TYPE);
191
192     if (flags & AVAHI_PUBLISH_UPDATE) {
193         AvahiRecord *old_record;
194         int is_first = 1;
195         
196         /* Update and existing record */
197
198         /* Find the first matching entry */
199         for (e = avahi_hashmap_lookup(s->entries_by_key, r->key); e; e = e->by_key_next) {
200             if (!e->dead && e->group == g && e->interface == interface && e->protocol == protocol)
201                 break;
202
203             is_first = 0;
204         }
205
206         /* Hmm, nothing found? */
207         if (!e) {
208             avahi_server_set_errno(s, AVAHI_ERR_NOT_FOUND);
209             return NULL;
210         }
211
212         /* Update the entry */
213         old_record = e->record;
214         e->record = avahi_record_ref(r);
215         e->flags = flags;
216
217         /* Announce our changes when needed */
218         if (!avahi_record_equal_no_ttl(old_record, r) && (!g || g->state != AVAHI_ENTRY_GROUP_UNCOMMITED)) {
219
220             /* Remove the old entry from all caches, if needed */
221             if (!(e->flags & AVAHI_PUBLISH_UNIQUE))
222                 avahi_goodbye_entry(s, e, 1, 0);
223
224             /* Reannounce our updated entry */
225             avahi_reannounce_entry(s, e);
226         }
227
228         /* If we were the first entry in the list, we need to update the key */
229         if (is_first)
230             avahi_hashmap_replace(s->entries_by_key, e->record->key, e);
231         
232         avahi_record_unref(old_record);
233
234     } else {
235         AvahiEntry *t;
236
237         /* Add a new record */
238     
239         if (check_record_conflict(s, interface, protocol, r, flags) < 0) {
240             avahi_server_set_errno(s, AVAHI_ERR_COLLISION);
241             return NULL;
242         }
243
244         if (!(e = avahi_new(AvahiEntry, 1))) {
245             avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
246             return NULL;
247         }
248         
249         e->server = s;
250         e->record = avahi_record_ref(r);
251         e->group = g;
252         e->interface = interface;
253         e->protocol = protocol;
254         e->flags = flags;
255         e->dead = 0;
256         
257         AVAHI_LLIST_HEAD_INIT(AvahiAnnouncer, e->announcers);
258         
259         AVAHI_LLIST_PREPEND(AvahiEntry, entries, s->entries, e);
260         
261         /* Insert into hash table indexed by name */
262         t = avahi_hashmap_lookup(s->entries_by_key, e->record->key);
263         AVAHI_LLIST_PREPEND(AvahiEntry, by_key, t, e);
264         avahi_hashmap_replace(s->entries_by_key, e->record->key, t);
265         
266         /* Insert into group list */
267         if (g)
268             AVAHI_LLIST_PREPEND(AvahiEntry, by_group, g->entries, e); 
269         
270         avahi_announce_entry(s, e);
271     }
272
273     return e;
274 }
275
276 int avahi_server_add(
277     AvahiServer *s,
278     AvahiSEntryGroup *g,
279     AvahiIfIndex interface,
280     AvahiProtocol protocol,
281     AvahiPublishFlags flags,
282     AvahiRecord *r) {
283
284     if (!server_add_internal(s, g, interface, protocol, flags, r))
285         return avahi_server_errno(s);
286
287     return AVAHI_OK;
288 }
289
290 const AvahiRecord *avahi_server_iterate(AvahiServer *s, AvahiSEntryGroup *g, void **state) {
291     AvahiEntry **e = (AvahiEntry**) state;
292     assert(s);
293     assert(e);
294
295     if (!*e)
296         *e = g ? g->entries : s->entries;
297     
298     while (*e && (*e)->dead)
299         *e = g ? (*e)->by_group_next : (*e)->entries_next;
300         
301     if (!*e)
302         return NULL;
303
304     return avahi_record_ref((*e)->record);
305 }
306
307 int avahi_server_dump(AvahiServer *s, AvahiDumpCallback callback, void* userdata) {
308     AvahiEntry *e;
309     
310     assert(s);
311     assert(callback);
312
313     callback(";;; ZONE DUMP FOLLOWS ;;;", userdata);
314
315     for (e = s->entries; e; e = e->entries_next) {
316         char *t;
317         char ln[256];
318
319         if (e->dead)
320             continue;
321         
322         if (!(t = avahi_record_to_string(e->record)))
323             return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
324         
325         snprintf(ln, sizeof(ln), "%s ; iface=%i proto=%i", t, e->interface, e->protocol);
326         avahi_free(t);
327
328         callback(ln, userdata);
329     }
330
331     avahi_dump_caches(s->monitor, callback, userdata);
332
333     if (s->wide_area_lookup_engine)
334         avahi_wide_area_cache_dump(s->wide_area_lookup_engine, callback, userdata);
335     return AVAHI_OK;
336 }
337
338 static AvahiEntry *server_add_ptr_internal(
339     AvahiServer *s,
340     AvahiSEntryGroup *g,
341     AvahiIfIndex interface,
342     AvahiProtocol protocol,
343     AvahiPublishFlags flags,
344     uint32_t ttl,
345     const char *name,
346     const char *dest) {
347
348     AvahiRecord *r;
349     AvahiEntry *e;
350     
351     assert(s);
352     assert(dest);
353
354     AVAHI_CHECK_VALIDITY_RETURN_NULL(s, !name || avahi_is_valid_domain_name(name), AVAHI_ERR_INVALID_HOST_NAME);
355     AVAHI_CHECK_VALIDITY_RETURN_NULL(s, avahi_is_valid_domain_name(dest), AVAHI_ERR_INVALID_HOST_NAME);
356
357     if (!name)
358         name = s->host_name_fqdn;
359     
360     if (!(r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR, ttl))) {
361         avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
362         return NULL;
363     }
364         
365     r->data.ptr.name = avahi_normalize_name_strdup(dest);
366     e = server_add_internal(s, g, interface, protocol, flags, r);
367     avahi_record_unref(r);
368     return e;
369 }
370     
371 int avahi_server_add_ptr(
372     AvahiServer *s,
373     AvahiSEntryGroup *g,
374     AvahiIfIndex interface,
375     AvahiProtocol protocol,
376     AvahiPublishFlags flags,
377     uint32_t ttl,
378     const char *name,
379     const char *dest) {
380
381     AvahiEntry *e;
382
383     assert(s);
384
385     if (!(e = server_add_ptr_internal(s, g, interface, protocol, flags, ttl, name, dest)))
386         return avahi_server_errno(s);
387
388     return AVAHI_OK;
389 }
390
391 int avahi_server_add_address(
392     AvahiServer *s,
393     AvahiSEntryGroup *g,
394     AvahiIfIndex interface,
395     AvahiProtocol protocol,
396     AvahiPublishFlags flags,
397     const char *name,
398     AvahiAddress *a) {
399
400     char n[AVAHI_DOMAIN_NAME_MAX];
401     int ret = AVAHI_OK;
402     AvahiEntry *entry = NULL, *reverse = NULL;
403     AvahiRecord  *r;
404     
405     assert(s);
406     assert(a);
407
408     AVAHI_CHECK_VALIDITY(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE);
409     AVAHI_CHECK_VALIDITY(s, AVAHI_PROTO_VALID(protocol) && AVAHI_PROTO_VALID(a->proto), AVAHI_ERR_INVALID_PROTOCOL);
410     AVAHI_CHECK_VALIDITY(s, AVAHI_FLAGS_VALID(flags, AVAHI_PUBLISH_NO_REVERSE|AVAHI_PUBLISH_NO_ANNOUNCE|AVAHI_PUBLISH_NO_PROBE|AVAHI_PUBLISH_UPDATE), AVAHI_ERR_INVALID_FLAGS);
411     AVAHI_CHECK_VALIDITY(s, !name || avahi_is_valid_domain_name(name), AVAHI_ERR_INVALID_HOST_NAME);
412
413     /* Prepare the host naem */
414     
415     if (!name)
416         name = s->host_name_fqdn;
417     else {
418         AVAHI_ASSERT_TRUE(avahi_normalize_name(name, n, sizeof(n)));
419         name = n;
420     }
421
422     /* Create the A/AAAA record */
423     
424     if (a->proto == AVAHI_PROTO_INET) {
425
426         if (!(r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A, AVAHI_DEFAULT_TTL_HOST_NAME))) {
427             ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
428             goto finish;
429         }
430         
431         r->data.a.address = a->data.ipv4;
432         
433     } else {
434         assert(a->proto == AVAHI_PROTO_INET6);
435             
436         if (!(r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA, AVAHI_DEFAULT_TTL_HOST_NAME))) {
437             ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
438             goto finish;
439         }
440         
441         r->data.aaaa.address = a->data.ipv6;
442     }
443         
444     entry = server_add_internal(s, g, interface, protocol, (flags & ~ AVAHI_PUBLISH_NO_REVERSE) | AVAHI_PUBLISH_UNIQUE | AVAHI_PUBLISH_ALLOW_MULTIPLE, r);
445     avahi_record_unref(r);
446
447     if (!entry) {
448         ret = avahi_server_errno(s);
449         goto finish;
450     }
451
452     /* Create the reverse lookup entry */
453     
454     if (!(flags & AVAHI_PUBLISH_NO_REVERSE)) {
455         char reverse_n[AVAHI_DOMAIN_NAME_MAX];
456         avahi_reverse_lookup_name(a, reverse_n, sizeof(reverse_n));
457             
458         if (!(reverse = server_add_ptr_internal(s, g, interface, protocol, flags | AVAHI_PUBLISH_UNIQUE, AVAHI_DEFAULT_TTL_HOST_NAME, reverse_n, name))) {
459             ret = avahi_server_errno(s);
460             goto finish;
461         }
462     }
463     
464 finish:
465
466     if (ret != AVAHI_OK && !(flags & AVAHI_PUBLISH_UPDATE)) {
467         if (entry)
468             avahi_entry_free(s, entry);
469         if (reverse)
470             avahi_entry_free(s, reverse);
471     }
472
473     return ret;
474 }
475
476 static AvahiEntry *server_add_txt_strlst_nocopy(
477     AvahiServer *s,
478     AvahiSEntryGroup *g,
479     AvahiIfIndex interface,
480     AvahiProtocol protocol,
481     AvahiPublishFlags flags,
482     uint32_t ttl,
483     const char *name,
484     AvahiStringList *strlst) {
485
486     AvahiRecord *r;
487     AvahiEntry *e;
488     
489     assert(s);
490
491     if (!(r = avahi_record_new_full(name ? name : s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_TXT, ttl))) {
492         avahi_string_list_free(strlst);
493         avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
494         return NULL;
495     }
496     
497     r->data.txt.string_list = strlst;
498     e = server_add_internal(s, g, interface, protocol, flags, r);
499     avahi_record_unref(r);
500
501     return e;
502 }
503
504 static AvahiStringList *add_magic_cookie(
505     AvahiServer *s,
506     AvahiStringList *strlst) {
507
508     assert(s);
509
510     if (!s->config.add_service_cookie)
511         return strlst;
512
513     if (avahi_string_list_find(strlst, AVAHI_SERVICE_COOKIE))
514         /* This string list already contains a magic cookie */
515         return strlst;
516
517     return avahi_string_list_add_printf(strlst, AVAHI_SERVICE_COOKIE"=%u", s->local_service_cookie);
518 }
519
520 static int server_add_service_strlst_nocopy(
521     AvahiServer *s,
522     AvahiSEntryGroup *g,
523     AvahiIfIndex interface,
524     AvahiProtocol protocol,
525     AvahiPublishFlags flags,
526     const char *name,
527     const char *type,
528     const char *domain,
529     const char *host,
530     uint16_t port,
531     AvahiStringList *strlst) {
532
533     char ptr_name[AVAHI_DOMAIN_NAME_MAX], svc_name[AVAHI_DOMAIN_NAME_MAX], enum_ptr[AVAHI_DOMAIN_NAME_MAX], *h = NULL;
534     AvahiRecord *r = NULL;
535     int ret = AVAHI_OK;
536     AvahiEntry *srv_entry = NULL, *txt_entry = NULL, *ptr_entry = NULL, *enum_entry = NULL;
537     
538     assert(s);
539     assert(type);
540     assert(name);
541
542     AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE);
543     AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL);
544     AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_FLAGS_VALID(flags, AVAHI_PUBLISH_NO_COOKIE|AVAHI_PUBLISH_UPDATE), AVAHI_ERR_INVALID_FLAGS);
545     AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_name(name), AVAHI_ERR_INVALID_SERVICE_NAME);
546     AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_type_strict(type), AVAHI_ERR_INVALID_SERVICE_TYPE);
547     AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME);
548     AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, !host || avahi_is_valid_domain_name(host), AVAHI_ERR_INVALID_HOST_NAME);
549
550     if (!domain)
551         domain = s->domain_name;
552
553     if (!host)
554         host = s->host_name_fqdn;
555
556     if (!(h = avahi_normalize_name_strdup(host))) {
557         ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
558         goto fail;
559     }
560
561     if ((ret = avahi_service_name_join(svc_name, sizeof(svc_name), name, type, domain)) < 0 ||
562         (ret = avahi_service_name_join(ptr_name, sizeof(ptr_name), NULL, type, domain)) < 0 ||
563         (ret = avahi_service_name_join(enum_ptr, sizeof(enum_ptr), NULL, "_services._dns-sd._udp", domain)) < 0) {
564         avahi_server_set_errno(s, ret);
565         goto fail;
566     }
567
568     /* Add service enumeration PTR record */
569     
570     if (!(ptr_entry = server_add_ptr_internal(s, g, interface, protocol, 0, AVAHI_DEFAULT_TTL, ptr_name, svc_name))) {
571         ret = avahi_server_errno(s);
572         goto fail;
573     }
574
575     /* Add SRV record */
576     
577     if (!(r = avahi_record_new_full(svc_name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV, AVAHI_DEFAULT_TTL_HOST_NAME))) {
578         ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
579         goto fail;
580     }
581     
582     r->data.srv.priority = 0;
583     r->data.srv.weight = 0;
584     r->data.srv.port = port;
585     r->data.srv.name = h;
586     h = NULL;
587     srv_entry = server_add_internal(s, g, interface, protocol, AVAHI_PUBLISH_UNIQUE, r);
588     avahi_record_unref(r);
589
590     if (!srv_entry) {
591         ret = avahi_server_errno(s);
592         goto fail;
593     }
594
595     /* Add TXT record */
596
597     if (!(flags & AVAHI_PUBLISH_NO_COOKIE))
598         strlst = add_magic_cookie(s, strlst);
599     
600     txt_entry = server_add_txt_strlst_nocopy(s, g, interface, protocol, AVAHI_PUBLISH_UNIQUE, AVAHI_DEFAULT_TTL, svc_name, strlst);
601     strlst = NULL;
602
603     if (!txt_entry) {
604         ret = avahi_server_errno(s);
605         goto fail;
606     }
607
608     /* Add service type enumeration record */
609     
610     if (!(enum_entry = server_add_ptr_internal(s, g, interface, protocol, 0, AVAHI_DEFAULT_TTL, enum_ptr, ptr_name))) {
611         ret = avahi_server_errno(s);
612         goto fail;
613     }
614
615 fail:
616     if (ret != AVAHI_OK && !(flags & AVAHI_PUBLISH_UPDATE)) {
617         if (srv_entry)
618             avahi_entry_free(s, srv_entry);
619         if (txt_entry)
620             avahi_entry_free(s, txt_entry);
621         if (ptr_entry)
622             avahi_entry_free(s, ptr_entry);
623         if (enum_entry)
624             avahi_entry_free(s, enum_entry);
625     }
626     
627     avahi_string_list_free(strlst);
628     avahi_free(h);
629     
630     return ret;
631 }
632
633 int avahi_server_add_service_strlst(
634     AvahiServer *s,
635     AvahiSEntryGroup *g,
636     AvahiIfIndex interface,
637     AvahiProtocol protocol,
638     AvahiPublishFlags flags,
639     const char *name,
640     const char *type,
641     const char *domain,
642     const char *host,
643     uint16_t port,
644     AvahiStringList *strlst) {
645
646     assert(s);
647     assert(type);
648     assert(name);
649
650     return server_add_service_strlst_nocopy(s, g, interface, protocol, flags, name, type, domain, host, port, avahi_string_list_copy(strlst));
651 }
652
653 int avahi_server_add_service(
654     AvahiServer *s,
655     AvahiSEntryGroup *g,
656     AvahiIfIndex interface,
657     AvahiProtocol protocol,
658     AvahiPublishFlags flags,
659     const char *name,
660     const char *type,
661     const char *domain,
662     const char *host,
663     uint16_t port,
664     ... ){
665
666     va_list va;
667     int ret;
668     
669     va_start(va, port);
670     ret = server_add_service_strlst_nocopy(s, g, interface, protocol, flags, name, type, domain, host, port, avahi_string_list_new_va(va));
671     va_end(va);
672     
673     return ret;
674 }
675
676 static int server_update_service_txt_strlst_nocopy(
677     AvahiServer *s,
678     AvahiSEntryGroup *g,
679     AvahiIfIndex interface,
680     AvahiProtocol protocol,
681     AvahiPublishFlags flags,
682     const char *name,     
683     const char *type,     
684     const char *domain,   
685     AvahiStringList *strlst) {
686
687     char svc_name[AVAHI_DOMAIN_NAME_MAX];
688     int ret = AVAHI_OK;
689     AvahiEntry *e;
690     
691     assert(s);
692     assert(type);
693     assert(name);
694
695     AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE);
696     AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL);
697     AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_FLAGS_VALID(flags, AVAHI_PUBLISH_NO_COOKIE), AVAHI_ERR_INVALID_FLAGS);
698     AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_name(name), AVAHI_ERR_INVALID_SERVICE_NAME);
699     AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_type_strict(type), AVAHI_ERR_INVALID_SERVICE_TYPE);
700     AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME);
701
702     if (!domain)
703         domain = s->domain_name;
704
705     if ((ret = avahi_service_name_join(svc_name, sizeof(svc_name), name, type, domain)) < 0) {
706         avahi_server_set_errno(s, ret);
707         goto fail;
708     }
709
710     /* Add TXT record */
711     if (!(flags & AVAHI_PUBLISH_NO_COOKIE))
712         strlst = add_magic_cookie(s, strlst);
713     
714     e = server_add_txt_strlst_nocopy(s, g, interface, protocol, AVAHI_PUBLISH_UNIQUE | AVAHI_PUBLISH_UPDATE, AVAHI_DEFAULT_TTL, svc_name, strlst);
715     strlst = NULL;
716
717     if (!e)
718         ret = avahi_server_errno(s);
719     
720 fail:
721     
722     avahi_string_list_free(strlst);
723     
724     return ret;
725 }
726
727 int avahi_server_update_service_txt_strlst(
728     AvahiServer *s,
729     AvahiSEntryGroup *g,
730     AvahiIfIndex interface,
731     AvahiProtocol protocol,
732     AvahiPublishFlags flags,
733     const char *name,     
734     const char *type,     
735     const char *domain,   
736     AvahiStringList *strlst) {
737
738     return server_update_service_txt_strlst_nocopy(s, g, interface, protocol, flags, name, type, domain, avahi_string_list_copy(strlst));
739 }
740
741 /** Update the TXT record for a service with the NULL termonate list of strings */
742 int avahi_server_update_service_txt(
743     AvahiServer *s,
744     AvahiSEntryGroup *g,
745     AvahiIfIndex interface,
746     AvahiProtocol protocol,
747     AvahiPublishFlags flags,
748     const char *name,     
749     const char *type,     
750     const char *domain,   
751     ...) {
752
753     va_list va;
754     int ret;
755     
756     va_start(va, domain);
757     ret = server_update_service_txt_strlst_nocopy(s, g, interface, protocol, flags, name, type, domain, avahi_string_list_new_va(va));
758     va_end(va);
759     
760     return ret;
761 }
762
763 int avahi_server_add_service_subtype(
764     AvahiServer *s,
765     AvahiSEntryGroup *g,
766     AvahiIfIndex interface,
767     AvahiProtocol protocol,
768     AvahiPublishFlags flags,
769     const char *name,        
770     const char *type,        
771     const char *domain,      
772     const char *subtype) {
773
774     int ret = AVAHI_OK;
775     char svc_name[AVAHI_DOMAIN_NAME_MAX], ptr_name[AVAHI_DOMAIN_NAME_MAX];
776     
777     assert(name);
778     assert(type);
779     assert(subtype);
780
781     AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE);
782     AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL);
783     AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_FLAGS_VALID(flags, 0), AVAHI_ERR_INVALID_FLAGS);
784     AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_name(name), AVAHI_ERR_INVALID_SERVICE_NAME);
785     AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_type_strict(type), AVAHI_ERR_INVALID_SERVICE_TYPE);
786     AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME);
787     AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_subtype(subtype), AVAHI_ERR_INVALID_SERVICE_SUBTYPE);
788
789     if (!domain)
790         domain = s->domain_name;
791
792     if ((ret = avahi_service_name_join(svc_name, sizeof(svc_name), name, type, domain)) < 0 ||
793         (ret = avahi_service_name_join(ptr_name, sizeof(ptr_name), NULL, subtype, domain)) < 0) {
794         avahi_server_set_errno(s, ret);
795         goto fail;
796     }
797
798     if ((ret = avahi_server_add_ptr(s, g, interface, protocol, 0, AVAHI_DEFAULT_TTL, ptr_name, svc_name)) < 0)
799         goto fail;
800
801 fail:
802     
803     return ret;
804 }
805
806 static void hexstring(char *s, size_t sl, const void *p, size_t pl) {
807     static const char hex[] = "0123456789abcdef";
808     int b = 0;
809     const uint8_t *k = p;
810
811     while (sl > 1 && pl > 0) {
812         *(s++) = hex[(b ? *k : *k >> 4) & 0xF];
813
814         if (b) {
815             k++;
816             pl--;
817         }
818         
819         b = !b;
820
821         sl--;
822     }
823
824     if (sl > 0)
825         *s = 0;
826 }
827
828 static AvahiEntry * server_add_dns_server_name(
829     AvahiServer *s,
830     AvahiSEntryGroup *g,
831     AvahiIfIndex interface,
832     AvahiProtocol protocol,
833     AvahiPublishFlags flags,
834     const char *domain,
835     AvahiDNSServerType type,
836     const char *name,
837     uint16_t port /** should be 53 */) {
838
839     AvahiEntry *e;
840     char t[AVAHI_DOMAIN_NAME_MAX], normalized_d[AVAHI_DOMAIN_NAME_MAX], *n;
841     
842     AvahiRecord *r;
843     
844     assert(s);
845     assert(name);
846
847     AVAHI_CHECK_VALIDITY_RETURN_NULL(s, AVAHI_FLAGS_VALID(flags, 0), AVAHI_ERR_INVALID_FLAGS);
848     AVAHI_CHECK_VALIDITY_RETURN_NULL(s, type == AVAHI_DNS_SERVER_UPDATE || type == AVAHI_DNS_SERVER_RESOLVE, AVAHI_ERR_INVALID_FLAGS);
849     AVAHI_CHECK_VALIDITY_RETURN_NULL(s, port != 0, AVAHI_ERR_INVALID_PORT);
850     AVAHI_CHECK_VALIDITY_RETURN_NULL(s, avahi_is_valid_domain_name(name), AVAHI_ERR_INVALID_HOST_NAME);
851     AVAHI_CHECK_VALIDITY_RETURN_NULL(s, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME);
852     
853     if (!domain)
854         domain = s->domain_name;
855
856     if (!(n = avahi_normalize_name_strdup(name))) {
857         avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
858         return NULL;
859     }
860     
861     AVAHI_ASSERT_TRUE(avahi_normalize_name(domain, normalized_d, sizeof(normalized_d)));
862
863     snprintf(t, sizeof(t), "%s.%s", type == AVAHI_DNS_SERVER_RESOLVE ? "_domain._udp" : "_dns-update._udp", normalized_d);
864     
865     if (!(r = avahi_record_new_full(t, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV, AVAHI_DEFAULT_TTL_HOST_NAME))) {
866         avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
867         avahi_free(n);
868         return NULL;
869     }
870     
871     r->data.srv.priority = 0;
872     r->data.srv.weight = 0;
873     r->data.srv.port = port;
874     r->data.srv.name = n;
875     e = server_add_internal(s, g, interface, protocol, 0, r);
876     avahi_record_unref(r);
877
878     return e;
879 }
880
881 int avahi_server_add_dns_server_address(
882     AvahiServer *s,
883     AvahiSEntryGroup *g,
884     AvahiIfIndex interface,
885     AvahiProtocol protocol,
886     AvahiPublishFlags flags,
887     const char *domain,
888     AvahiDNSServerType type,
889     const AvahiAddress *address,
890     uint16_t port /** should be 53 */) {
891
892     AvahiRecord *r;
893     char n[64], h[64];
894     AvahiEntry *a_entry, *s_entry;
895
896     assert(s);
897     assert(address);
898
899     AVAHI_CHECK_VALIDITY(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE);
900     AVAHI_CHECK_VALIDITY(s, AVAHI_PROTO_VALID(protocol) && AVAHI_PROTO_VALID(address->proto), AVAHI_ERR_INVALID_PROTOCOL);
901     AVAHI_CHECK_VALIDITY(s, AVAHI_FLAGS_VALID(flags, 0), AVAHI_ERR_INVALID_FLAGS);
902     AVAHI_CHECK_VALIDITY(s, type == AVAHI_DNS_SERVER_UPDATE || type == AVAHI_DNS_SERVER_RESOLVE, AVAHI_ERR_INVALID_FLAGS);
903     AVAHI_CHECK_VALIDITY(s, port != 0, AVAHI_ERR_INVALID_PORT);
904     AVAHI_CHECK_VALIDITY(s, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME);
905
906     if (!domain)
907         domain = s->domain_name;
908
909     if (address->proto == AVAHI_PROTO_INET) {
910         hexstring(h, sizeof(h), &address->data, sizeof(AvahiIPv4Address));
911         snprintf(n, sizeof(n), "ip-%s.%s", h, domain);
912         r = avahi_record_new_full(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A, AVAHI_DEFAULT_TTL_HOST_NAME);
913         r->data.a.address = address->data.ipv4;
914     } else {
915         hexstring(h, sizeof(h), &address->data, sizeof(AvahiIPv6Address));
916         snprintf(n, sizeof(n), "ip6-%s.%s", h, domain);
917         r = avahi_record_new_full(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA, AVAHI_DEFAULT_TTL_HOST_NAME);
918         r->data.aaaa.address = address->data.ipv6;
919     }
920
921     if (!r)
922         return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
923     
924     a_entry = server_add_internal(s, g, interface, protocol, AVAHI_PUBLISH_UNIQUE | AVAHI_PUBLISH_ALLOW_MULTIPLE, r);
925     avahi_record_unref(r);
926
927     if (!a_entry)
928         return avahi_server_errno(s);
929     
930     if (!(s_entry = server_add_dns_server_name(s, g, interface, protocol, flags, domain, type, n, port))) {
931         if (!(flags & AVAHI_PUBLISH_UPDATE))
932             avahi_entry_free(s, a_entry);
933         return avahi_server_errno(s);
934     }
935
936     return AVAHI_OK;
937 }
938
939 void avahi_s_entry_group_change_state(AvahiSEntryGroup *g, AvahiEntryGroupState state) {
940     assert(g);
941
942     if (g->state == state)
943         return;
944
945     assert(state <= AVAHI_ENTRY_GROUP_COLLISION);
946
947     if (g->state == AVAHI_ENTRY_GROUP_ESTABLISHED) {
948
949         /* If the entry group was established for a time longer then
950          * 5s, reset the establishment trial counter */
951         
952         if (avahi_age(&g->established_at) > 5000000)
953             g->n_register_try = 0;
954     }
955     
956     if (state == AVAHI_ENTRY_GROUP_ESTABLISHED)
957
958         /* If the entry group is now established, remember the time
959          * this happened */
960         
961         gettimeofday(&g->established_at, NULL);
962     
963     g->state = state;
964     
965     if (g->callback)
966         g->callback(g->server, g, state, g->userdata);
967 }
968
969 AvahiSEntryGroup *avahi_s_entry_group_new(AvahiServer *s, AvahiSEntryGroupCallback callback, void* userdata) {
970     AvahiSEntryGroup *g;
971     
972     assert(s);
973
974     if (!(g = avahi_new(AvahiSEntryGroup, 1))) {
975         avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
976         return NULL;
977     }
978     
979     g->server = s;
980     g->callback = callback;
981     g->userdata = userdata;
982     g->dead = 0;
983     g->state = AVAHI_ENTRY_GROUP_UNCOMMITED;
984     g->n_probing = 0;
985     g->n_register_try = 0;
986     g->register_time_event = NULL;
987     g->register_time.tv_sec = 0;
988     g->register_time.tv_usec = 0;
989     AVAHI_LLIST_HEAD_INIT(AvahiEntry, g->entries);
990
991     AVAHI_LLIST_PREPEND(AvahiSEntryGroup, groups, s->groups, g);
992     return g;
993 }
994
995 void avahi_s_entry_group_free(AvahiSEntryGroup *g) {
996     AvahiEntry *e;
997     
998     assert(g);
999     assert(g->server);
1000
1001     for (e = g->entries; e; e = e->by_group_next) {
1002         if (!e->dead) {
1003             avahi_goodbye_entry(g->server, e, 1, 1);
1004             e->dead = 1;
1005         }
1006     }
1007
1008     if (g->register_time_event) {
1009         avahi_time_event_free(g->register_time_event);
1010         g->register_time_event = NULL;
1011     }
1012
1013     g->dead = 1;
1014     
1015     g->server->need_group_cleanup = 1;
1016     g->server->need_entry_cleanup = 1;
1017 }
1018
1019 static void entry_group_commit_real(AvahiSEntryGroup *g) {
1020     assert(g);
1021
1022     gettimeofday(&g->register_time, NULL);
1023
1024     avahi_s_entry_group_change_state(g, AVAHI_ENTRY_GROUP_REGISTERING);
1025
1026     if (!g->dead) {
1027         avahi_announce_group(g->server, g);
1028         avahi_s_entry_group_check_probed(g, 0);
1029     }
1030 }
1031
1032 static void entry_group_register_time_event_callback(AVAHI_GCC_UNUSED AvahiTimeEvent *e, void* userdata) {
1033     AvahiSEntryGroup *g = userdata;
1034     assert(g);
1035
1036 /*     avahi_log_debug("Holdoff passed, waking up and going on."); */
1037
1038     avahi_time_event_free(g->register_time_event);
1039     g->register_time_event = NULL;
1040     
1041     /* Holdoff time passed, so let's start probing */
1042     entry_group_commit_real(g);
1043 }
1044
1045 int avahi_s_entry_group_commit(AvahiSEntryGroup *g) {
1046     struct timeval now;
1047     
1048     assert(g);
1049     assert(!g->dead);
1050
1051     if (g->state != AVAHI_ENTRY_GROUP_UNCOMMITED && g->state != AVAHI_ENTRY_GROUP_COLLISION)
1052         return avahi_server_set_errno(g->server, AVAHI_ERR_BAD_STATE);
1053
1054     g->n_register_try++;
1055
1056     avahi_timeval_add(&g->register_time,
1057                       1000*(g->n_register_try >= AVAHI_RR_RATE_LIMIT_COUNT ?
1058                             AVAHI_RR_HOLDOFF_MSEC_RATE_LIMIT :
1059                             AVAHI_RR_HOLDOFF_MSEC));
1060
1061     gettimeofday(&now, NULL);
1062
1063     if (avahi_timeval_compare(&g->register_time, &now) <= 0) {
1064         /* Holdoff time passed, so let's start probing */
1065 /*         avahi_log_debug("Holdoff passed, directly going on.");  */
1066
1067         entry_group_commit_real(g);
1068     } else {
1069 /*          avahi_log_debug("Holdoff not passed, sleeping.");  */
1070
1071          /* Holdoff time has not yet passed, so let's wait */
1072         assert(!g->register_time_event);
1073         g->register_time_event = avahi_time_event_new(g->server->time_event_queue, &g->register_time, entry_group_register_time_event_callback, g);
1074         
1075         avahi_s_entry_group_change_state(g, AVAHI_ENTRY_GROUP_REGISTERING);
1076     }
1077
1078     return AVAHI_OK;
1079 }
1080
1081 void avahi_s_entry_group_reset(AvahiSEntryGroup *g) {
1082     AvahiEntry *e;
1083     assert(g);
1084     
1085     for (e = g->entries; e; e = e->by_group_next) {
1086         if (!e->dead) {
1087             avahi_goodbye_entry(g->server, e, 1, 1);
1088             e->dead = 1;
1089         }
1090     }
1091     g->server->need_entry_cleanup = 1;
1092
1093     if (g->register_time_event) {
1094         avahi_time_event_free(g->register_time_event);
1095         g->register_time_event = NULL;
1096     }
1097     
1098     g->n_probing = 0;
1099
1100     gettimeofday(&g->register_time, NULL);
1101
1102     avahi_s_entry_group_change_state(g, AVAHI_ENTRY_GROUP_UNCOMMITED);
1103 }
1104
1105 int avahi_entry_is_commited(AvahiEntry *e) {
1106     assert(e);
1107     assert(!e->dead);
1108
1109     return !e->group ||
1110         e->group->state == AVAHI_ENTRY_GROUP_REGISTERING ||
1111         e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED;
1112 }
1113
1114 AvahiEntryGroupState avahi_s_entry_group_get_state(AvahiSEntryGroup *g) {
1115     assert(g);
1116     assert(!g->dead);
1117
1118     return g->state;
1119 }
1120
1121 void avahi_s_entry_group_set_data(AvahiSEntryGroup *g, void* userdata) {
1122     assert(g);
1123
1124     g->userdata = userdata;
1125 }
1126
1127 void* avahi_s_entry_group_get_data(AvahiSEntryGroup *g) {
1128     assert(g);
1129
1130     return g->userdata;
1131 }
1132
1133 int avahi_s_entry_group_is_empty(AvahiSEntryGroup *g) {
1134     AvahiEntry *e;
1135     assert(g);
1136
1137     /* Look for an entry that is not dead */
1138     for (e = g->entries; e; e = e->by_group_next)
1139         if (!e->dead)
1140             return 0;
1141
1142     return 1;
1143 }