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