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