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