1/***
2  This file is part of avahi.
3
4  avahi is free software; you can redistribute it and/or modify it
5  under the terms of the GNU Lesser General Public License as
6  published by the Free Software Foundation; either version 2.1 of the
7  License, or (at your option) any later version.
8
9  avahi is distributed in the hope that it will be useful, but WITHOUT
10  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11  or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
12  Public License for more details.
13
14  You should have received a copy of the GNU Lesser General Public
15  License along with avahi; if not, write to the Free Software
16  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
17  USA.
18***/
19
20#ifdef HAVE_CONFIG_H
21#include <config.h>
22#endif
23
24#include <string.h>
25#include <unistd.h>
26#include <errno.h>
27#include <stdio.h>
28#include <assert.h>
29#include <stdlib.h>
30
31#include <arpa/inet.h>
32
33#include <sys/utsname.h>
34#include <sys/types.h>
35#include <sys/socket.h>
36
37#include <avahi-common/domain.h>
38#include <avahi-common/timeval.h>
39#include <avahi-common/malloc.h>
40#include <avahi-common/error.h>
41#include <avahi-common/domain.h>
42#include <avahi-common/defs.h>
43
44#include "internal.h"
45#include "iface.h"
46#include "socket.h"
47#include "browse.h"
48#include "log.h"
49#include "util.h"
50#include "dns-srv-rr.h"
51#include "rr-util.h"
52#include "domain-util.h"
53
54static void transport_flags_from_domain(AvahiServer *s, AvahiPublishFlags *flags, const char *domain, int check_for_llmnr) {
55    assert(flags);
56    assert(domain);
57    assert(!((*flags & AVAHI_PUBLISH_USE_MULTICAST) && (*flags & AVAHI_PUBLISH_USE_WIDE_AREA)));
58
59    if (check_for_llmnr) {
60        assert(!((*flags & AVAHI_PUBLISH_USE_WIDE_AREA) && (*flags & AVAHI_PUBLISH_USE_LLMNR)));
61        assert(!((*flags & AVAHI_PUBLISH_USE_MULTICAST) && (*flags & AVAHI_PUBLISH_USE_LLMNR)));
62
63        if (*flags & (AVAHI_PUBLISH_USE_MULTICAST | AVAHI_PUBLISH_USE_WIDE_AREA | AVAHI_PUBLISH_USE_LLMNR))
64            return;
65
66    } else /*!check_for_llmnr*/
67        if (*flags & (AVAHI_PUBLISH_USE_MULTICAST | AVAHI_PUBLISH_USE_WIDE_AREA))
68            return;
69
70    if (check_for_llmnr && avahi_is_valid_host_name(domain))
71           *flags |= AVAHI_PUBLISH_USE_LLMNR;
72    else if (!s->wide_area.wide_area_lookup_engine ||
73             !avahi_wide_area_has_servers(s->wide_area.wide_area_lookup_engine) ||
74             avahi_domain_ends_with(domain, AVAHI_MDNS_SUFFIX_LOCAL) ||
75             avahi_domain_ends_with(domain, AVAHI_MDNS_SUFFIX_ADDR_IPV4) ||
76             avahi_domain_ends_with(domain, AVAHI_MDNS_SUFFIX_ADDR_IPV6))
77        *flags |= AVAHI_PUBLISH_USE_MULTICAST;
78    else
79        *flags |= AVAHI_PUBLISH_USE_WIDE_AREA;
80}
81
82void avahi_entry_free(AvahiServer*s, AvahiEntry *e) {
83    AvahiEntry *t;
84    AvahiHashmap *entries_by_key;
85
86    assert(s);
87    assert(e);
88
89    if (e->type == AVAHI_ENTRY_MDNS) {
90        avahi_goodbye_entry(s, e, 1, 1);
91        AVAHI_LLIST_REMOVE(AvahiEntry, entries, s->mdns.entries, e);
92        entries_by_key = s->mdns.entries_by_key;
93    } else {
94        assert (e->type == AVAHI_ENTRY_LLMNR);
95        avahi_remove_verifiers (s, e);
96        AVAHI_LLIST_REMOVE(AvahiEntry, entries, s->llmnr.entries, e);
97        entries_by_key = s->llmnr.entries_by_key;
98    }
99
100    t = avahi_hashmap_lookup(entries_by_key, e->record->key);
101    AVAHI_LLIST_REMOVE(AvahiEntry, by_key, t, e);
102    if (t)
103        avahi_hashmap_replace(entries_by_key, t->record->key, t);
104    else
105        avahi_hashmap_remove(entries_by_key, e->record->key);
106
107    /* Remove from associated group */
108    if (e->group)
109        AVAHI_LLIST_REMOVE(AvahiEntry, by_group, e->group->entries, e);
110
111    avahi_record_unref(e->record);
112    avahi_free(e);
113}
114
115void avahi_entry_group_free(AvahiServer *s, AvahiSEntryGroup *g) {
116    assert(s);
117    assert(g);
118
119    while (g->entries)
120        avahi_entry_free(s, g->entries);
121
122    if (g->type == AVAHI_GROUP_MDNS && g->proto.mdns.register_time_event)
123        avahi_time_event_free(g->proto.mdns.register_time_event);
124
125    if (g->type == AVAHI_GROUP_MDNS)
126        AVAHI_LLIST_REMOVE(AvahiSEntryGroup, groups, s->mdns.groups, g);
127    else
128        AVAHI_LLIST_REMOVE(AvahiSEntryGroup, groups, s->llmnr.groups, g);
129
130    avahi_free(g);
131}
132
133void avahi_cleanup_dead_entries(AvahiServer *s) {
134    assert(s);
135
136    if (s->mdns.need_group_cleanup) {
137        AvahiSEntryGroup *g, *next;
138
139        for (g = s->mdns.groups; g; g = next) {
140            next = g->groups_next;
141
142            if (g->dead)
143                avahi_entry_group_free(s, g);
144        }
145
146        s->mdns.need_group_cleanup = 0;
147    }
148
149    if (s->mdns.need_entry_cleanup) {
150        AvahiEntry *e, *next;
151
152        for (e = s->mdns.entries; e; e = next) {
153            next = e->entries_next;
154
155            if (e->dead)
156                avahi_entry_free(s, e);
157        }
158
159        s->mdns.need_entry_cleanup = 0;
160    }
161
162    if (s->need_browser_cleanup)
163        avahi_browser_cleanup(s);
164
165    if (s->cleanup_time_event) {
166        avahi_time_event_free(s->cleanup_time_event);
167        s->cleanup_time_event = NULL;
168    }
169}
170
171static int check_record_conflict(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiRecord *r, AvahiPublishFlags flags, AvahiPublishProtocol proto) {
172    AvahiEntry *e;
173
174    assert(s);
175    assert(r);
176
177    for (e = avahi_hashmap_lookup(proto == AVAHI_MDNS ? s->mdns.entries_by_key : s->llmnr.entries_by_key, r->key); e; e = e->by_key_next) {
178
179        if (proto == AVAHI_MDNS)
180            assert(e->type == AVAHI_ENTRY_MDNS);
181        else
182            assert(e->type == AVAHI_ENTRY_LLMNR);
183
184        if (e->dead)
185            continue;
186
187        if (!(flags & AVAHI_PUBLISH_UNIQUE) && !(e->flags & AVAHI_PUBLISH_UNIQUE))
188            continue;
189
190        if ((flags & AVAHI_PUBLISH_ALLOW_MULTIPLE) && (e->flags & AVAHI_PUBLISH_ALLOW_MULTIPLE) )
191            continue;
192
193        if (avahi_record_equal_no_ttl(r, e->record)) {
194            /* The records are the same, not a conflict in any case */
195            continue;
196        }
197
198        if ((interface <= 0 ||
199             e->interface <= 0 ||
200             e->interface == interface) &&
201            (protocol == AVAHI_PROTO_UNSPEC ||
202             e->protocol == AVAHI_PROTO_UNSPEC ||
203             e->protocol == protocol))
204
205            return -1;
206    }
207
208    return 0;
209}
210
211static AvahiEntry *server_add_llmnr_internal(
212    AvahiServer *s,
213    AvahiSEntryGroup *g,
214    AvahiIfIndex interface,
215    AvahiProtocol protocol,
216    AvahiPublishFlags flags,
217    AvahiRecord *r) {
218
219    AvahiEntry *e;
220
221    assert(s);
222    assert(r);
223
224    /* Flags should be LLMNR flags only */
225    AVAHI_CHECK_VALIDITY_RETURN_NULL(s, AVAHI_FLAGS_VALID(
226                                    flags,
227                                    AVAHI_PUBLISH_UNIQUE|
228                                    AVAHI_PUBLISH_ALLOW_MULTIPLE|
229                                    AVAHI_PUBLISH_UPDATE|
230                                    AVAHI_PUBLISH_NO_VERIFY|
231                                    AVAHI_PUBLISH_USE_LLMNR), AVAHI_ERR_INVALID_FLAGS);
232
233    /* Publishing of only A/AAAA/PTR records is supported using LLMNR */
234    AVAHI_CHECK_VALIDITY_RETURN_NULL(s,
235                                    (r->key->type == AVAHI_DNS_TYPE_A) ||
236                                    (r->key->type == AVAHI_DNS_TYPE_AAAA) ||
237				    (r->key->type == AVAHI_DNS_TYPE_CNAME) ||
238                                    (r->key->type == AVAHI_DNS_TYPE_PTR), AVAHI_ERR_INVALID_DNS_TYPE);
239
240    AVAHI_CHECK_VALIDITY_RETURN_NULL(s,
241                                     !g ||
242                                     (g->state != AVAHI_ENTRY_GROUP_LLMNR_ESTABLISHED &&
243                                      g->state != AVAHI_ENTRY_GROUP_LLMNR_VERIFYING) ||
244                                     (flags & AVAHI_PUBLISH_UPDATE), AVAHI_ERR_BAD_STATE);
245
246    /* Copy Copy Copy. */
247    if (flags & AVAHI_PUBLISH_UPDATE) {
248        AvahiRecord *old_record;
249        int is_first = 1;
250
251        /* type can't be _UNSET*/
252        AVAHI_CHECK_VALIDITY_RETURN_NULL(s, !g || g->type == AVAHI_GROUP_LLMNR, AVAHI_ERR_INVALID_GROUP);
253
254        /* Find the first matching entry */
255        for (e = avahi_hashmap_lookup(s->llmnr.entries_by_key, r->key); e; e = e->by_key_next) {
256
257            assert(e->type == AVAHI_ENTRY_LLMNR);
258            if (!e->dead && e->group == g && e->interface == interface && e->protocol == protocol)
259                break;
260
261            is_first = 0;
262        }
263
264        if (!e) {
265            avahi_server_set_errno(s, AVAHI_ERR_NOT_FOUND);
266            return NULL;
267        }
268
269        /* Update the entry */
270        old_record = e->record;
271        e->record = avahi_record_ref(r);
272        e->flags = flags;
273
274        /* Reverify changes, if needed */
275        if (!avahi_record_equal_no_ttl(old_record, r) && (!g || g->state != AVAHI_ENTRY_GROUP_LLMNR_UNCOMMITED))
276            /* Reverify our updated entry */
277            avahi_reverify_entry(s, e);
278
279        /* If we were the first entry in the list, we need to update the key */
280        if (is_first)
281            avahi_hashmap_replace(s->llmnr.entries_by_key, e->record->key, e);
282
283        avahi_record_unref(old_record);
284
285    } else {
286        AvahiEntry *t;
287
288        AVAHI_CHECK_VALIDITY_RETURN_NULL(s,
289                                         !g ||
290                                         g->type == AVAHI_GROUP_UNSET ||
291                                         g->type == AVAHI_GROUP_LLMNR, AVAHI_ERR_INVALID_GROUP);
292
293        if (g && g->type == AVAHI_GROUP_UNSET) {
294            g->proto.llmnr.n_verifying = 0;
295            g->proto.llmnr.n_entries = 0;
296            g->type = AVAHI_GROUP_LLMNR;
297
298            AVAHI_LLIST_HEAD_INIT(AvahiEntry, g->entries);
299            AVAHI_LLIST_PREPEND(AvahiSEntryGroup, groups, s->llmnr.groups, g);
300        }
301        /* Check for the conflict */
302
303        if (check_record_conflict(s, interface, protocol, r, flags, AVAHI_LLMNR) < 0) {
304            avahi_server_set_errno(s, AVAHI_ERR_COLLISION);
305            return NULL;
306        }
307
308        if (!(e = avahi_new(AvahiEntry, 1))) {
309            avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
310            return NULL;
311        }
312
313        e->server = s;
314        e->record = avahi_record_ref(r);
315        e->group = g;
316        e->interface = interface;
317        e->protocol = protocol;
318        e->flags = flags;
319        e->dead = 0;
320        e->type = AVAHI_ENTRY_LLMNR;
321        AVAHI_LLIST_HEAD_INIT(AvahiLLMNREntryVerify, e->proto.llmnr.verifiers);
322
323        AVAHI_LLIST_PREPEND(AvahiEntry, entries, s->llmnr.entries, e);
324
325        /* Insert into hash table indexed by name */
326        t = avahi_hashmap_lookup(s->llmnr.entries_by_key, r->key);
327        AVAHI_LLIST_PREPEND(AvahiEntry, by_key, t, e);
328        avahi_hashmap_replace(s->llmnr.entries_by_key, e->record->key, t);
329
330        /* Insert into group list */
331        if (g)
332            AVAHI_LLIST_PREPEND(AvahiEntry, by_group, g->entries, e);
333        else
334            /* Verify now if it doesn't belong to any group otherwise entry will be verified
335            when group will be commited */
336            avahi_verify_entry(s, e);
337      }
338
339    return e;
340}
341static AvahiEntry * server_add_internal(
342    AvahiServer *s,
343    AvahiSEntryGroup *g,
344    AvahiIfIndex interface,
345    AvahiProtocol protocol,
346    AvahiPublishFlags flags,
347    AvahiRecord *r) {
348
349    AvahiEntry *e;
350
351    assert(s);
352    assert(r);
353
354    AVAHI_CHECK_VALIDITY_RETURN_NULL(s, s->state != AVAHI_SERVER_FAILURE && s->state != AVAHI_SERVER_INVALID, AVAHI_ERR_BAD_STATE);
355    AVAHI_CHECK_VALIDITY_RETURN_NULL(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE);
356    AVAHI_CHECK_VALIDITY_RETURN_NULL(s, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL);
357    AVAHI_CHECK_VALIDITY_RETURN_NULL(s, AVAHI_FLAGS_VALID(
358                                         flags,
359                                         AVAHI_PUBLISH_NO_ANNOUNCE|
360                                         AVAHI_PUBLISH_NO_PROBE|
361                                         AVAHI_PUBLISH_NO_VERIFY|
362                                         AVAHI_PUBLISH_UNIQUE|
363                                         AVAHI_PUBLISH_ALLOW_MULTIPLE|
364                                         AVAHI_PUBLISH_UPDATE|
365                                         AVAHI_PUBLISH_USE_WIDE_AREA|
366                                         AVAHI_PUBLISH_USE_MULTICAST|
367                                         AVAHI_PUBLISH_USE_LLMNR), AVAHI_ERR_INVALID_FLAGS);
368
369    AVAHI_CHECK_VALIDITY_RETURN_NULL(s, avahi_is_valid_domain_name(r->key->name), AVAHI_ERR_INVALID_HOST_NAME);
370    AVAHI_CHECK_VALIDITY_RETURN_NULL(s, r->ttl != 0, AVAHI_ERR_INVALID_TTL);
371    AVAHI_CHECK_VALIDITY_RETURN_NULL(s, !avahi_key_is_pattern(r->key), AVAHI_ERR_IS_PATTERN);
372    AVAHI_CHECK_VALIDITY_RETURN_NULL(s, avahi_record_is_valid(r), AVAHI_ERR_INVALID_RECORD);
373    AVAHI_CHECK_VALIDITY_RETURN_NULL(s, r->key->clazz == AVAHI_DNS_CLASS_IN, AVAHI_ERR_INVALID_DNS_CLASS);
374    AVAHI_CHECK_VALIDITY_RETURN_NULL(s,
375                                     (r->key->type != 0) &&
376                                     (r->key->type != AVAHI_DNS_TYPE_ANY) &&
377                                     (r->key->type != AVAHI_DNS_TYPE_OPT) &&
378                                     (r->key->type != AVAHI_DNS_TYPE_TKEY) &&
379                                     (r->key->type != AVAHI_DNS_TYPE_TSIG) &&
380                                     (r->key->type != AVAHI_DNS_TYPE_IXFR) &&
381                                     (r->key->type != AVAHI_DNS_TYPE_AXFR), AVAHI_ERR_INVALID_DNS_TYPE);
382
383    transport_flags_from_domain(s, &flags, r->key->name, 1);
384
385    AVAHI_CHECK_VALIDITY_RETURN_NULL(s, (flags & AVAHI_PUBLISH_USE_MULTICAST) || (flags & AVAHI_PUBLISH_USE_LLMNR), AVAHI_ERR_NOT_SUPPORTED);
386
387    AVAHI_CHECK_VALIDITY_RETURN_NULL(s, !s->config.disable_publishing, AVAHI_ERR_NOT_PERMITTED);
388
389    if (flags & AVAHI_PUBLISH_USE_LLMNR)
390        return server_add_llmnr_internal(s, g, interface, protocol, flags, r);
391
392    AVAHI_CHECK_VALIDITY_RETURN_NULL(s, !(flags & AVAHI_PUBLISH_NO_VERIFY), AVAHI_ERR_INVALID_FLAGS);
393    AVAHI_CHECK_VALIDITY_RETURN_NULL(s,
394                                     !g ||
395                                     (g->state != AVAHI_ENTRY_GROUP_ESTABLISHED && g->state != AVAHI_ENTRY_GROUP_REGISTERING) ||
396                                     (flags & AVAHI_PUBLISH_UPDATE), AVAHI_ERR_BAD_STATE);
397
398    if (flags & AVAHI_PUBLISH_UPDATE) {
399        AvahiRecord *old_record;
400        int is_first = 1;
401
402        AVAHI_CHECK_VALIDITY_RETURN_NULL(s, !g || g->type == AVAHI_GROUP_MDNS, AVAHI_ERR_INVALID_GROUP);
403
404       /* Update an existing record */
405
406       /* Find the first matching entry */
407        for (e = avahi_hashmap_lookup(s->mdns.entries_by_key, r->key); e; e = e->by_key_next) {
408            assert(e->type == AVAHI_ENTRY_MDNS);
409            if (!e->dead && e->group == g && e->interface == interface && e->protocol == protocol)
410                break;
411
412            is_first = 0;
413        }
414
415        /* Hmm, nothing found? */
416        if (!e) {
417            avahi_server_set_errno(s, AVAHI_ERR_NOT_FOUND);
418            return NULL;
419        }
420
421        /* Update the entry */
422        old_record = e->record;
423        e->record = avahi_record_ref(r);
424        e->flags = flags;
425
426        /* Announce our changes when needed */
427        if (!avahi_record_equal_no_ttl(old_record, r) && (!g || g->state != AVAHI_ENTRY_GROUP_UNCOMMITED)) {
428
429            /* Remove the old entry from all caches, if needed */
430            if (!(e->flags & AVAHI_PUBLISH_UNIQUE))
431                avahi_goodbye_entry(s, e, 1, 0);
432
433            /* Reannounce our updated entry */
434            avahi_reannounce_entry(s, e);
435        }
436
437        /* If we were the first entry in the list, we need to update the key */
438        if (is_first)
439            avahi_hashmap_replace(s->mdns.entries_by_key, e->record->key, e);
440
441        avahi_record_unref(old_record);
442
443    } else {
444        AvahiEntry *t;
445
446        AVAHI_CHECK_VALIDITY_RETURN_NULL(s,
447                                         !g ||
448                                         g->type == AVAHI_GROUP_UNSET ||
449                                         g->type == AVAHI_GROUP_MDNS, AVAHI_ERR_INVALID_GROUP);
450
451        if (g && g->type == AVAHI_GROUP_UNSET) {
452            g->proto.mdns.n_probing = 0;
453            g->proto.mdns.n_register_try = 0;
454            g->proto.mdns.register_time_event = NULL;
455            g->proto.mdns.register_time.tv_sec = 0;
456            g->proto.mdns.register_time.tv_usec = 0;
457            g->type = AVAHI_GROUP_MDNS;
458
459            AVAHI_LLIST_HEAD_INIT(AvahiEntry, g->entries);
460            AVAHI_LLIST_PREPEND(AvahiSEntryGroup, groups, s->mdns.groups, g);
461        }
462        /* Add a new record */
463
464        if (check_record_conflict(s, interface, protocol, r, flags, AVAHI_MDNS) < 0) {
465            avahi_server_set_errno(s, AVAHI_ERR_COLLISION);
466            return NULL;
467        }
468
469        if (!(e = avahi_new(AvahiEntry, 1))) {
470            avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
471            return NULL;
472        }
473
474        e->server = s;
475        e->record = avahi_record_ref(r);
476        e->group = g;
477        e->interface = interface;
478        e->protocol = protocol;
479        e->flags = flags;
480        e->dead = 0;
481        e->type = AVAHI_ENTRY_MDNS;
482
483        AVAHI_LLIST_HEAD_INIT(AvahiAnnouncer, e->proto.mdns.announcers);
484
485        AVAHI_LLIST_PREPEND(AvahiEntry, entries, s->mdns.entries, e);
486
487        /* Insert into hash table indexed by name */
488        t = avahi_hashmap_lookup(s->mdns.entries_by_key, e->record->key);
489        AVAHI_LLIST_PREPEND(AvahiEntry, by_key, t, e);
490        avahi_hashmap_replace(s->mdns.entries_by_key, e->record->key, t);
491
492        /* Insert into group list */
493        if (g)
494            AVAHI_LLIST_PREPEND(AvahiEntry, by_group, g->entries, e);
495
496        avahi_announce_entry(s, e);
497    }
498
499    return e;
500}
501
502int avahi_server_add(
503    AvahiServer *s,
504    AvahiSEntryGroup *g,
505    AvahiIfIndex interface,
506    AvahiProtocol protocol,
507    AvahiPublishFlags flags,
508    AvahiRecord *r) {
509
510    if (!server_add_internal(s, g, interface, protocol, flags, r))
511        return avahi_server_errno(s);
512
513    return AVAHI_OK;
514}
515
516const AvahiRecord *avahi_server_iterate(AvahiServer *s, AvahiSEntryGroup *g, void **state, AvahiPublishProtocol proto) {
517    AvahiEntry **e = (AvahiEntry**) state;
518    assert(s);
519    assert(e);
520
521    if (!*e)
522        *e = g ? g->entries : (proto == AVAHI_MDNS ? s->mdns.entries : s->llmnr.entries);
523
524    while (*e && (*e)->dead)
525        *e = g ? (*e)->by_group_next : (*e)->entries_next;
526
527    if (!*e)
528        return NULL;
529
530    return avahi_record_ref((*e)->record);
531}
532
533static void zone_dump(AvahiServer *s, AvahiDumpCallback callback, void* userdata, AvahiPublishProtocol proto) {
534    AvahiEntry *e;
535
536    for (e = ((proto == AVAHI_MDNS) ? (s->mdns.entries) : (s->llmnr.entries)); e; e = e->entries_next) {
537        char *t;
538        char ln[256];
539
540        if (e->dead)
541            continue;
542
543        if (!(t = avahi_record_to_string(e->record))) {
544            avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
545            return;
546        }
547
548        snprintf(ln, sizeof(ln), "%s ; iface=%i proto=%i", t, e->interface, e->protocol);
549        avahi_free(t);
550
551        callback(ln, userdata);
552    }
553}
554
555int avahi_server_dump(AvahiServer *s, AvahiDumpCallback callback, void* userdata) {
556    assert(s);
557    assert(callback);
558
559    callback(";;; mDNS ZONE DUMP FOLLOWS ;;;", userdata);
560    zone_dump(s, callback, userdata, AVAHI_MDNS);
561    avahi_dump_caches(s->monitor, callback, userdata);
562
563    callback(";;; LLMNR ZONE DUMP FOLLOWS ;;;", userdata);
564    zone_dump(s, callback, userdata, AVAHI_LLMNR);
565    avahi_llmnr_cache_dump(s->llmnr.llmnr_lookup_engine, callback, userdata);
566
567    if (s->wide_area.wide_area_lookup_engine)
568        avahi_wide_area_cache_dump(s->wide_area.wide_area_lookup_engine, callback, userdata);
569
570    return AVAHI_OK;
571}
572
573static AvahiEntry *server_add_ptr_internal(
574    AvahiServer *s,
575    AvahiSEntryGroup *g,
576    AvahiIfIndex interface,
577    AvahiProtocol protocol,
578    AvahiPublishFlags flags,
579    uint32_t ttl,
580    const char *name,
581    const char *dest) {
582
583    AvahiRecord *r;
584    AvahiEntry *e;
585
586    assert(s);
587    assert(dest);
588
589    AVAHI_CHECK_VALIDITY_RETURN_NULL(s, !name || avahi_is_valid_domain_name(name), AVAHI_ERR_INVALID_HOST_NAME);
590    AVAHI_CHECK_VALIDITY_RETURN_NULL(s, avahi_is_valid_domain_name(dest), AVAHI_ERR_INVALID_HOST_NAME);
591
592    if (!name)
593        name = s->host_name_fqdn;
594
595    if (!(r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR, ttl))) {
596        avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
597        return NULL;
598    }
599
600    r->data.ptr.name = avahi_normalize_name_strdup(dest);
601    e = server_add_internal(s, g, interface, protocol, flags, r);
602    avahi_record_unref(r);
603    return e;
604}
605
606int avahi_server_add_ptr(
607    AvahiServer *s,
608    AvahiSEntryGroup *g,
609    AvahiIfIndex interface,
610    AvahiProtocol protocol,
611    AvahiPublishFlags flags,
612    uint32_t ttl,
613    const char *name,
614    const char *dest) {
615
616    AvahiEntry *e;
617
618    assert(s);
619
620    if (!(e = server_add_ptr_internal(s, g, interface, protocol, flags, ttl, name, dest)))
621        return avahi_server_errno(s);
622
623    return AVAHI_OK;
624}
625
626int avahi_server_add_cname(
627    AvahiServer *s,
628    AvahiSEntryGroup *g,
629    AvahiIfIndex interface,
630    AvahiProtocol protocol,
631    AvahiPublishFlags flags,
632    uint32_t ttl,
633    const char *name) {
634
635    AvahiRecord *r;
636    AvahiEntry *e;
637    char *fq;
638
639    assert(s);
640    assert(s->domain_name);
641    assert(name);
642
643    AVAHI_CHECK_VALIDITY(s, !name || avahi_is_valid_domain_name(name), AVAHI_ERR_INVALID_HOST_NAME);
644
645    if (!(fq = avahi_strdup_printf("%s.%s", name, s->domain_name)))
646        return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
647
648    if (!(r = avahi_record_new_full(fq, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_CNAME, ttl)))
649        return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
650
651    avahi_free(fq);
652
653    r->data.cname.name = avahi_normalize_name_strdup(s->host_name_fqdn);
654    e= server_add_internal(s, g, interface, protocol, (flags & ~ AVAHI_PUBLISH_NO_REVERSE) | AVAHI_PUBLISH_UNIQUE | AVAHI_PUBLISH_ALLOW_MULTIPLE, r);
655    avahi_record_unref(r);
656
657    if (!e)
658	return avahi_server_errno(s);
659
660    return AVAHI_OK;
661}
662
663int avahi_server_add_llmnr_cname(
664    AvahiServer *s,
665    AvahiSEntryGroup *g,
666    AvahiIfIndex interface,
667    AvahiProtocol protocol,
668    AvahiPublishFlags flags,
669    uint32_t ttl,
670    const char *name) {
671
672    AvahiRecord *r;
673    AvahiEntry *e;
674    char *fq;
675
676    assert(s);
677    assert(s->domain_name);
678    assert(name);
679
680    AVAHI_CHECK_VALIDITY(s, !name || avahi_is_valid_domain_name(name), AVAHI_ERR_INVALID_HOST_NAME);
681
682    if (!(fq = avahi_strdup_printf("%s", name)))
683        return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
684
685    if (!(r = avahi_record_new_full(fq, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_CNAME, ttl)))
686        return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
687
688    avahi_free(fq);
689
690    r->data.cname.name = avahi_normalize_name_strdup(s->host_name_fqdn);
691    e= server_add_internal(s, g, interface, protocol, (flags & ~ AVAHI_PUBLISH_NO_REVERSE) | AVAHI_PUBLISH_UNIQUE | AVAHI_PUBLISH_ALLOW_MULTIPLE, r);
692    avahi_record_unref(r);
693
694    if (!e)
695	return avahi_server_errno(s);
696
697    return AVAHI_OK;
698}
699
700
701static int server_add_address_internal (
702    AvahiServer *s,
703    AvahiSEntryGroup *g,
704    AvahiIfIndex interface,
705    AvahiProtocol protocol,
706    AvahiPublishFlags flags,
707    const char *name,
708    AvahiAddress *a,
709    uint32_t ttl) {
710
711    AvahiRecord *r;
712    int ret = AVAHI_OK;
713    AvahiEntry *reverse = NULL, *entry = NULL;
714
715    assert(s);
716    assert(a);
717    assert(name);
718
719    /* Create the A/AAAA record */
720    if (a->proto == AVAHI_PROTO_INET) {
721
722        if (!(r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A, ttl))) {
723            ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
724            goto finish;
725        }
726
727        r->data.a.address = a->data.ipv4;
728
729    } else {
730        assert(a->proto == AVAHI_PROTO_INET6);
731
732        if (!(r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA, ttl))) {
733            ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
734            goto finish;
735        }
736
737        r->data.aaaa.address = a->data.ipv6;
738    }
739
740    entry = server_add_internal(s, g, interface, protocol, (flags & ~ AVAHI_PUBLISH_NO_REVERSE) | AVAHI_PUBLISH_UNIQUE | AVAHI_PUBLISH_ALLOW_MULTIPLE, r);
741    avahi_record_unref(r);
742
743    if (!entry) {
744        ret = avahi_server_errno(s);
745        goto finish;
746    }
747
748    /* Create the reverse lookup entry */
749
750    if (!(flags & AVAHI_PUBLISH_NO_REVERSE)) {
751        char reverse_n[AVAHI_DOMAIN_NAME_MAX];
752        avahi_reverse_lookup_name(a, reverse_n, sizeof(reverse_n));
753
754        if (!(reverse = server_add_ptr_internal(s, g, interface, protocol, flags | AVAHI_PUBLISH_UNIQUE, ttl, reverse_n, name))) {
755            ret = avahi_server_errno(s);
756            goto finish;
757        }
758    }
759
760finish:
761
762    if (ret != AVAHI_OK && !(flags & AVAHI_PUBLISH_UPDATE)) {
763        if (entry)
764            avahi_entry_free(s, entry);
765        if (reverse)
766            avahi_entry_free(s, reverse);
767    }
768
769    return ret;
770}
771
772
773int avahi_server_add_address(
774    AvahiServer *s,
775    AvahiSEntryGroup *g,
776    AvahiIfIndex interface,
777    AvahiProtocol protocol,
778    AvahiPublishFlags flags,
779    const char *name,
780    AvahiAddress *a) {
781
782    char n[AVAHI_DOMAIN_NAME_MAX];
783    int ret_fqdn, null_name = 0;
784
785    assert(s);
786    assert(a);
787    /*assert(name || (flags & (AVAHI_PUBLISH_USE_MULTICAST | AVAHI_PUBLISH_USE_LLMNR | AVAHI_PUBLISH_USE_WIDE_AREA));*/
788
789    AVAHI_CHECK_VALIDITY(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE);
790    AVAHI_CHECK_VALIDITY(s, AVAHI_PROTO_VALID(protocol) && AVAHI_PROTO_VALID(a->proto), AVAHI_ERR_INVALID_PROTOCOL);
791    AVAHI_CHECK_VALIDITY(s, AVAHI_FLAGS_VALID(flags,
792                                              AVAHI_PUBLISH_NO_REVERSE|
793                                              AVAHI_PUBLISH_NO_ANNOUNCE|
794                                              AVAHI_PUBLISH_NO_PROBE|
795                                              AVAHI_PUBLISH_NO_VERIFY|
796                                              AVAHI_PUBLISH_UPDATE|
797                                              AVAHI_PUBLISH_USE_WIDE_AREA|
798                                              AVAHI_PUBLISH_USE_MULTICAST|
799                                              AVAHI_PUBLISH_USE_LLMNR), AVAHI_ERR_INVALID_FLAGS);
800
801    AVAHI_CHECK_VALIDITY(s,
802                         !name ||
803                         ((flags & AVAHI_PUBLISH_USE_MULTICAST) ? (avahi_is_valid_fqdn(name)) : (avahi_is_valid_domain_name(name))),
804                         AVAHI_ERR_INVALID_HOST_NAME);
805
806    /* Prepare the host name */
807    if (!name) {
808        name = s->host_name_fqdn;
809        null_name = 1;
810    } else {
811        AVAHI_ASSERT_TRUE(avahi_normalize_name(name, n, sizeof(n)));
812        name = n;
813    }
814
815    /* transport flags */
816    transport_flags_from_domain(s, &flags, name, 1);
817    AVAHI_CHECK_VALIDITY(s, (flags & (AVAHI_PUBLISH_USE_MULTICAST | AVAHI_PUBLISH_USE_LLMNR)), AVAHI_ERR_NOT_SUPPORTED);
818
819    /* No matter mDNS or LLMNR */
820    if (flags & AVAHI_PUBLISH_USE_MULTICAST)
821        ret_fqdn = server_add_address_internal(s, g, interface, protocol, flags, name, a, AVAHI_DEFAULT_TTL_HOST_NAME);
822    else
823        ret_fqdn = server_add_address_internal(s, g,
824                                               interface,
825                                               protocol,
826                                               (null_name == 1) ? (flags |= AVAHI_PUBLISH_NO_REVERSE) : (flags),
827                                               name, a,
828                                               AVAHI_DEFAULT_LLMNR_TTL_HOST_NAME);
829
830    /* If previous entries have been added successfully && name parameter was NULL && we are using LLMNR,
831    publish hostname -> A/AAAA entry and PTR entry*/
832    if (ret_fqdn == AVAHI_OK && null_name == 1 && flags & AVAHI_PUBLISH_USE_LLMNR )
833        return server_add_address_internal(s, g, interface, protocol, flags & ~ AVAHI_PUBLISH_NO_REVERSE, s->host_name, a, AVAHI_DEFAULT_LLMNR_TTL_HOST_NAME);
834
835    return ret_fqdn;
836}
837
838static AvahiEntry *server_add_txt_strlst_nocopy(
839    AvahiServer *s,
840    AvahiSEntryGroup *g,
841    AvahiIfIndex interface,
842    AvahiProtocol protocol,
843    AvahiPublishFlags flags,
844    uint32_t ttl,
845    const char *name,
846    AvahiStringList *strlst) {
847
848    AvahiRecord *r;
849    AvahiEntry *e;
850
851    assert(s);
852
853    if (!(r = avahi_record_new_full(name ? name : s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_TXT, ttl))) {
854        avahi_string_list_free(strlst);
855        avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
856        return NULL;
857    }
858
859    r->data.txt.string_list = strlst;
860    e = server_add_internal(s, g, interface, protocol, flags, r);
861    avahi_record_unref(r);
862
863    return e;
864}
865
866static AvahiStringList *add_magic_cookie(
867    AvahiServer *s,
868    AvahiStringList *strlst) {
869
870    assert(s);
871
872    if (!s->config.add_service_cookie)
873        return strlst;
874
875    if (avahi_string_list_find(strlst, AVAHI_SERVICE_COOKIE))
876        /* This string list already contains a magic cookie */
877        return strlst;
878
879    return avahi_string_list_add_printf(strlst, AVAHI_SERVICE_COOKIE"=%u", s->local_service_cookie);
880}
881
882static int server_add_service_strlst_nocopy(
883    AvahiServer *s,
884    AvahiSEntryGroup *g,
885    AvahiIfIndex interface,
886    AvahiProtocol protocol,
887    AvahiPublishFlags flags,
888    const char *name,
889    const char *type,
890    const char *domain,
891    const char *host,
892    uint16_t port,
893    AvahiStringList *strlst) {
894
895    char ptr_name[AVAHI_DOMAIN_NAME_MAX], svc_name[AVAHI_DOMAIN_NAME_MAX], enum_ptr[AVAHI_DOMAIN_NAME_MAX], *h = NULL;
896    AvahiRecord *r = NULL;
897    int ret = AVAHI_OK;
898    AvahiEntry *srv_entry = NULL, *txt_entry = NULL, *ptr_entry = NULL, *enum_entry = NULL;
899
900    assert(s);
901    assert(type);
902    assert(name);
903
904    AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE);
905    AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL);
906    AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_FLAGS_VALID(flags,
907                                                                AVAHI_PUBLISH_NO_COOKIE|
908                                                                AVAHI_PUBLISH_UPDATE|
909                                                                AVAHI_PUBLISH_USE_WIDE_AREA|
910                                                                AVAHI_PUBLISH_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS);
911    AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_name(name), AVAHI_ERR_INVALID_SERVICE_NAME);
912    AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_type_strict(type), AVAHI_ERR_INVALID_SERVICE_TYPE);
913    AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME);
914    AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, !host || avahi_is_valid_fqdn(host), AVAHI_ERR_INVALID_HOST_NAME);
915
916    if (!domain)
917        domain = s->domain_name;
918
919    if (!host)
920        host = s->host_name_fqdn;
921
922    transport_flags_from_domain(s, &flags, domain, 0);
923    AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, flags & AVAHI_PUBLISH_USE_MULTICAST, AVAHI_ERR_NOT_SUPPORTED);
924
925    if (!(h = avahi_normalize_name_strdup(host))) {
926        ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
927        goto fail;
928    }
929
930    if ((ret = avahi_service_name_join(svc_name, sizeof(svc_name), name, type, domain)) < 0 ||
931        (ret = avahi_service_name_join(ptr_name, sizeof(ptr_name), NULL, type, domain)) < 0 ||
932        (ret = avahi_service_name_join(enum_ptr, sizeof(enum_ptr), NULL, "_services._dns-sd._udp", domain)) < 0) {
933        avahi_server_set_errno(s, ret);
934        goto fail;
935    }
936
937    /* Add service enumeration PTR record */
938
939    if (!(ptr_entry = server_add_ptr_internal(s, g, interface, protocol, 0, AVAHI_DEFAULT_TTL, ptr_name, svc_name))) {
940        ret = avahi_server_errno(s);
941        goto fail;
942    }
943
944    /* Add SRV record */
945
946    if (!(r = avahi_record_new_full(svc_name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV, AVAHI_DEFAULT_TTL_HOST_NAME))) {
947        ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
948        goto fail;
949    }
950
951    r->data.srv.priority = 0;
952    r->data.srv.weight = 0;
953    r->data.srv.port = port;
954    r->data.srv.name = h;
955    h = NULL;
956    srv_entry = server_add_internal(s, g, interface, protocol, AVAHI_PUBLISH_UNIQUE, r);
957    avahi_record_unref(r);
958
959    if (!srv_entry) {
960        ret = avahi_server_errno(s);
961        goto fail;
962    }
963
964    /* Add TXT record */
965
966    if (!(flags & AVAHI_PUBLISH_NO_COOKIE))
967        strlst = add_magic_cookie(s, strlst);
968
969    txt_entry = server_add_txt_strlst_nocopy(s, g, interface, protocol, AVAHI_PUBLISH_UNIQUE, AVAHI_DEFAULT_TTL, svc_name, strlst);
970    strlst = NULL;
971
972    if (!txt_entry) {
973        ret = avahi_server_errno(s);
974        goto fail;
975    }
976
977    /* Add service type enumeration record */
978
979    if (!(enum_entry = server_add_ptr_internal(s, g, interface, protocol, 0, AVAHI_DEFAULT_TTL, enum_ptr, ptr_name))) {
980        ret = avahi_server_errno(s);
981        goto fail;
982    }
983
984fail:
985   if (ret != AVAHI_OK && !(flags & AVAHI_PUBLISH_UPDATE)) {
986        if (srv_entry)
987            avahi_entry_free(s, srv_entry);
988        if (txt_entry)
989            avahi_entry_free(s, txt_entry);
990        if (ptr_entry)
991            avahi_entry_free(s, ptr_entry);
992        if (enum_entry)
993            avahi_entry_free(s, enum_entry);
994    }
995
996    avahi_string_list_free(strlst);
997    avahi_free(h);
998
999    return ret;
1000}
1001
1002int avahi_server_add_service_strlst(
1003    AvahiServer *s,
1004    AvahiSEntryGroup *g,
1005    AvahiIfIndex interface,
1006    AvahiProtocol protocol,
1007    AvahiPublishFlags flags,
1008    const char *name,
1009    const char *type,
1010    const char *domain,
1011    const char *host,
1012    uint16_t port,
1013    AvahiStringList *strlst) {
1014
1015    assert(s);
1016    assert(type);
1017    assert(name);
1018
1019    return server_add_service_strlst_nocopy(s, g, interface, protocol, flags, name, type, domain, host, port, avahi_string_list_copy(strlst));
1020}
1021
1022int avahi_server_add_service(
1023    AvahiServer *s,
1024    AvahiSEntryGroup *g,
1025    AvahiIfIndex interface,
1026    AvahiProtocol protocol,
1027    AvahiPublishFlags flags,
1028    const char *name,
1029    const char *type,
1030    const char *domain,
1031    const char *host,
1032    uint16_t port,
1033    ... ){
1034
1035    va_list va;
1036    int ret;
1037
1038    va_start(va, port);
1039    ret = server_add_service_strlst_nocopy(s, g, interface, protocol, flags, name, type, domain, host, port, avahi_string_list_new_va(va));
1040    va_end(va);
1041
1042    return ret;
1043}
1044
1045static int server_update_service_txt_strlst_nocopy(
1046    AvahiServer *s,
1047    AvahiSEntryGroup *g,
1048    AvahiIfIndex interface,
1049    AvahiProtocol protocol,
1050    AvahiPublishFlags flags,
1051    const char *name,
1052    const char *type,
1053    const char *domain,
1054    AvahiStringList *strlst) {
1055
1056    char svc_name[AVAHI_DOMAIN_NAME_MAX];
1057    int ret = AVAHI_OK;
1058    AvahiEntry *e;
1059
1060    assert(s);
1061    assert(type);
1062    assert(name);
1063    assert (flags & AVAHI_PUBLISH_USE_MULTICAST || flags & AVAHI_PUBLISH_USE_WIDE_AREA);
1064
1065    AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE);
1066    AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL);
1067    AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_FLAGS_VALID(flags,
1068                                                                AVAHI_PUBLISH_NO_COOKIE|
1069                                                                AVAHI_PUBLISH_USE_WIDE_AREA|
1070                                                                AVAHI_PUBLISH_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS);
1071    AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_name(name), AVAHI_ERR_INVALID_SERVICE_NAME);
1072    AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_type_strict(type), AVAHI_ERR_INVALID_SERVICE_TYPE);
1073    AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME);
1074
1075    if (!domain)
1076        domain = s->domain_name;
1077
1078    transport_flags_from_domain(s, &flags, domain, 0);
1079    AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, flags & AVAHI_PUBLISH_USE_MULTICAST, AVAHI_ERR_NOT_SUPPORTED);
1080
1081    if ((ret = avahi_service_name_join(svc_name, sizeof(svc_name), name, type, domain)) < 0) {
1082        avahi_server_set_errno(s, ret);
1083        goto fail;
1084    }
1085
1086    /* Add TXT record */
1087    if (!(flags & AVAHI_PUBLISH_NO_COOKIE))
1088        strlst = add_magic_cookie(s, strlst);
1089
1090    e = server_add_txt_strlst_nocopy(s, g, interface, protocol, AVAHI_PUBLISH_UNIQUE | AVAHI_PUBLISH_UPDATE, AVAHI_DEFAULT_TTL, svc_name, strlst);
1091    strlst = NULL;
1092
1093    if (!e)
1094        ret = avahi_server_errno(s);
1095
1096fail:
1097
1098    avahi_string_list_free(strlst);
1099
1100    return ret;
1101}
1102
1103int avahi_server_update_service_txt_strlst(
1104    AvahiServer *s,
1105    AvahiSEntryGroup *g,
1106    AvahiIfIndex interface,
1107    AvahiProtocol protocol,
1108    AvahiPublishFlags flags,
1109    const char *name,
1110    const char *type,
1111    const char *domain,
1112    AvahiStringList *strlst) {
1113
1114    return server_update_service_txt_strlst_nocopy(s, g, interface, protocol, flags, name, type, domain, avahi_string_list_copy(strlst));
1115}
1116
1117/** Update the TXT record for a service with the NULL termonate list of strings */
1118int avahi_server_update_service_txt(
1119    AvahiServer *s,
1120    AvahiSEntryGroup *g,
1121    AvahiIfIndex interface,
1122    AvahiProtocol protocol,
1123    AvahiPublishFlags flags,
1124    const char *name,
1125    const char *type,
1126    const char *domain,
1127    ...) {
1128
1129    va_list va;
1130    int ret;
1131
1132    va_start(va, domain);
1133    ret = server_update_service_txt_strlst_nocopy(s, g, interface, protocol, flags, name, type, domain, avahi_string_list_new_va(va));
1134    va_end(va);
1135
1136    return ret;
1137}
1138
1139int avahi_server_add_service_subtype(
1140    AvahiServer *s,
1141    AvahiSEntryGroup *g,
1142    AvahiIfIndex interface,
1143    AvahiProtocol protocol,
1144    AvahiPublishFlags flags,
1145    const char *name,
1146    const char *type,
1147    const char *domain,
1148    const char *subtype) {
1149
1150    int ret = AVAHI_OK;
1151    char svc_name[AVAHI_DOMAIN_NAME_MAX], ptr_name[AVAHI_DOMAIN_NAME_MAX];
1152
1153    assert(name);
1154    assert(type);
1155    assert(subtype);
1156    assert (flags & AVAHI_PUBLISH_USE_MULTICAST || flags & AVAHI_PUBLISH_USE_WIDE_AREA);
1157
1158    AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE);
1159    AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL);
1160    AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_FLAGS_VALID(flags, AVAHI_PUBLISH_USE_MULTICAST|AVAHI_PUBLISH_USE_WIDE_AREA), AVAHI_ERR_INVALID_FLAGS);
1161    AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_name(name), AVAHI_ERR_INVALID_SERVICE_NAME);
1162    AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_type_strict(type), AVAHI_ERR_INVALID_SERVICE_TYPE);
1163    AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME);
1164    AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_subtype(subtype), AVAHI_ERR_INVALID_SERVICE_SUBTYPE);
1165
1166    if (!domain)
1167        domain = s->domain_name;
1168
1169    transport_flags_from_domain(s, &flags, domain, 0);
1170    AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, flags & AVAHI_PUBLISH_USE_MULTICAST, AVAHI_ERR_NOT_SUPPORTED);
1171
1172    if ((ret = avahi_service_name_join(svc_name, sizeof(svc_name), name, type, domain)) < 0 ||
1173        (ret = avahi_service_name_join(ptr_name, sizeof(ptr_name), NULL, subtype, domain)) < 0) {
1174        avahi_server_set_errno(s, ret);
1175        goto fail;
1176    }
1177
1178    if ((ret = avahi_server_add_ptr(s, g, interface, protocol, 0, AVAHI_DEFAULT_TTL, ptr_name, svc_name)) < 0)
1179        goto fail;
1180
1181fail:
1182
1183    return ret;
1184}
1185
1186static void hexstring(char *s, size_t sl, const void *p, size_t pl) {
1187    static const char hex[] = "0123456789abcdef";
1188    int b = 0;
1189    const uint8_t *k = p;
1190
1191    while (sl > 1 && pl > 0) {
1192        *(s++) = hex[(b ? *k : *k >> 4) & 0xF];
1193
1194        if (b) {
1195            k++;
1196            pl--;
1197        }
1198
1199        b = !b;
1200
1201        sl--;
1202    }
1203
1204    if (sl > 0)
1205        *s = 0;
1206}
1207
1208static AvahiEntry *server_add_dns_server_name(
1209    AvahiServer *s,
1210    AvahiSEntryGroup *g,
1211    AvahiIfIndex interface,
1212    AvahiProtocol protocol,
1213    AvahiPublishFlags flags,
1214    const char *domain,
1215    AvahiDNSServerType type,
1216    const char *name,
1217    uint16_t port /** should be 53 */) {
1218
1219    AvahiEntry *e;
1220    char t[AVAHI_DOMAIN_NAME_MAX], normalized_d[AVAHI_DOMAIN_NAME_MAX], *n;
1221
1222    AvahiRecord *r;
1223
1224    assert(s);
1225    assert(name);
1226    assert (flags & AVAHI_PUBLISH_USE_MULTICAST || flags & AVAHI_PUBLISH_USE_WIDE_AREA);
1227
1228    AVAHI_CHECK_VALIDITY_RETURN_NULL(s, AVAHI_FLAGS_VALID(flags, AVAHI_PUBLISH_USE_WIDE_AREA|AVAHI_PUBLISH_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS);
1229    AVAHI_CHECK_VALIDITY_RETURN_NULL(s, type == AVAHI_DNS_SERVER_UPDATE || type == AVAHI_DNS_SERVER_RESOLVE, AVAHI_ERR_INVALID_FLAGS);
1230    AVAHI_CHECK_VALIDITY_RETURN_NULL(s, port != 0, AVAHI_ERR_INVALID_PORT);
1231    AVAHI_CHECK_VALIDITY_RETURN_NULL(s, avahi_is_valid_fqdn(name), AVAHI_ERR_INVALID_HOST_NAME);
1232    AVAHI_CHECK_VALIDITY_RETURN_NULL(s, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME);
1233
1234    if (!domain)
1235        domain = s->domain_name;
1236
1237    transport_flags_from_domain(s, &flags, domain, 0);
1238    AVAHI_CHECK_VALIDITY_RETURN_NULL(s, flags & AVAHI_PUBLISH_USE_MULTICAST, AVAHI_ERR_NOT_SUPPORTED);
1239
1240    if (!(n = avahi_normalize_name_strdup(name))) {
1241        avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1242        return NULL;
1243    }
1244
1245    AVAHI_ASSERT_TRUE(avahi_normalize_name(domain, normalized_d, sizeof(normalized_d)));
1246
1247    snprintf(t, sizeof(t), "%s.%s", type == AVAHI_DNS_SERVER_RESOLVE ? "_domain._udp" : "_dns-update._udp", normalized_d);
1248
1249    if (!(r = avahi_record_new_full(t, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV, AVAHI_DEFAULT_TTL_HOST_NAME))) {
1250        avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1251        avahi_free(n);
1252        return NULL;
1253    }
1254
1255    r->data.srv.priority = 0;
1256    r->data.srv.weight = 0;
1257    r->data.srv.port = port;
1258    r->data.srv.name = n;
1259    e = server_add_internal(s, g, interface, protocol, 0, r);
1260    avahi_record_unref(r);
1261
1262    return e;
1263}
1264
1265int avahi_server_add_dns_server_address(
1266    AvahiServer *s,
1267    AvahiSEntryGroup *g,
1268    AvahiIfIndex interface,
1269    AvahiProtocol protocol,
1270    AvahiPublishFlags flags,
1271    const char *domain,
1272    AvahiDNSServerType type,
1273    const AvahiAddress *address,
1274    uint16_t port /** should be 53 */) {
1275
1276    AvahiRecord *r;
1277    char n[64], h[64];
1278    AvahiEntry *a_entry, *s_entry;
1279
1280    assert(s);
1281    assert(address);
1282    assert (flags & AVAHI_PUBLISH_USE_MULTICAST || flags & AVAHI_PUBLISH_USE_WIDE_AREA);
1283
1284    AVAHI_CHECK_VALIDITY(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE);
1285    AVAHI_CHECK_VALIDITY(s, AVAHI_PROTO_VALID(protocol) && AVAHI_PROTO_VALID(address->proto), AVAHI_ERR_INVALID_PROTOCOL);
1286    AVAHI_CHECK_VALIDITY(s, AVAHI_FLAGS_VALID(flags, AVAHI_PUBLISH_USE_MULTICAST|AVAHI_PUBLISH_USE_WIDE_AREA), AVAHI_ERR_INVALID_FLAGS);
1287    AVAHI_CHECK_VALIDITY(s, type == AVAHI_DNS_SERVER_UPDATE || type == AVAHI_DNS_SERVER_RESOLVE, AVAHI_ERR_INVALID_FLAGS);
1288    AVAHI_CHECK_VALIDITY(s, port != 0, AVAHI_ERR_INVALID_PORT);
1289    AVAHI_CHECK_VALIDITY(s, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME);
1290
1291    if (!domain)
1292        domain = s->domain_name;
1293
1294    transport_flags_from_domain(s, &flags, domain, 0);
1295    AVAHI_CHECK_VALIDITY(s, flags & AVAHI_PUBLISH_USE_MULTICAST, AVAHI_ERR_NOT_SUPPORTED);
1296
1297    if (address->proto == AVAHI_PROTO_INET) {
1298        hexstring(h, sizeof(h), &address->data, sizeof(AvahiIPv4Address));
1299        snprintf(n, sizeof(n), "ip-%s.%s", h, domain);
1300        r = avahi_record_new_full(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A, AVAHI_DEFAULT_TTL_HOST_NAME);
1301        r->data.a.address = address->data.ipv4;
1302    } else {
1303        hexstring(h, sizeof(h), &address->data, sizeof(AvahiIPv6Address));
1304        snprintf(n, sizeof(n), "ip6-%s.%s", h, domain);
1305        r = avahi_record_new_full(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA, AVAHI_DEFAULT_TTL_HOST_NAME);
1306        r->data.aaaa.address = address->data.ipv6;
1307    }
1308
1309    if (!r)
1310        return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1311
1312    a_entry = server_add_internal(s, g, interface, protocol, AVAHI_PUBLISH_UNIQUE | AVAHI_PUBLISH_ALLOW_MULTIPLE, r);
1313    avahi_record_unref(r);
1314
1315    if (!a_entry)
1316        return avahi_server_errno(s);
1317
1318    if (!(s_entry = server_add_dns_server_name(s, g, interface, protocol, flags, domain, type, n, port))) {
1319        if (!(flags & AVAHI_PUBLISH_UPDATE))
1320            avahi_entry_free(s, a_entry);
1321        return avahi_server_errno(s);
1322    }
1323
1324    return AVAHI_OK;
1325}
1326
1327void avahi_s_entry_group_change_state(AvahiSEntryGroup *g, AvahiEntryGroupState state) {
1328    assert(g);
1329    assert(g->type != AVAHI_GROUP_UNSET);
1330
1331    if (g->state == state)
1332        return;
1333
1334    assert(state <= AVAHI_ENTRY_GROUP_COLLISION);
1335
1336    if (g->state == AVAHI_ENTRY_GROUP_ESTABLISHED && g->type == AVAHI_GROUP_MDNS) {
1337
1338        /* If the entry group was established for a time longer then
1339         * 5s, reset the establishment trial counter */
1340
1341        if (avahi_age(&g->proto.mdns.established_at) > 5000000)
1342            g->proto.mdns.n_register_try = 0;
1343    } else if (g->state == AVAHI_ENTRY_GROUP_REGISTERING && g->type == AVAHI_GROUP_MDNS) {
1344        if (g->proto.mdns.register_time_event) {
1345            avahi_time_event_free(g->proto.mdns.register_time_event);
1346            g->proto.mdns.register_time_event = NULL;
1347        }
1348    }
1349
1350    if (state == AVAHI_ENTRY_GROUP_ESTABLISHED && g->type == AVAHI_GROUP_MDNS)
1351
1352        /* If the entry group is now established, remember the time
1353         * this happened */
1354
1355        gettimeofday(&g->proto.mdns.established_at, NULL);
1356
1357    g->state = state;
1358
1359    if (g->callback)
1360        g->callback(g->server, g, state, g->userdata);
1361}
1362
1363AvahiSEntryGroup *avahi_s_entry_group_new(AvahiServer *s, AvahiSEntryGroupCallback callback, void* userdata) {
1364    AvahiSEntryGroup *g;
1365
1366    assert(s);
1367
1368    if (!(g = avahi_new(AvahiSEntryGroup, 1))) {
1369        avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1370        return NULL;
1371    }
1372
1373    g->server = s;
1374    g->callback = callback;
1375    g->userdata = userdata;
1376    g->dead = 0;
1377    g->state = AVAHI_ENTRY_GROUP_UNCOMMITED;
1378    g->type = AVAHI_GROUP_UNSET;
1379
1380    return g;
1381}
1382
1383static void cleanup_time_event_callback(AVAHI_GCC_UNUSED AvahiTimeEvent *e, void* userdata) {
1384    AvahiServer *s = userdata;
1385
1386    assert(s);
1387
1388    avahi_cleanup_dead_entries(s);
1389}
1390
1391static void schedule_cleanup(AvahiServer *s) {
1392    struct timeval tv;
1393
1394    assert(s);
1395
1396    if (!s->cleanup_time_event)
1397        s->cleanup_time_event = avahi_time_event_new(s->time_event_queue, avahi_elapse_time(&tv, 1000, 0), &cleanup_time_event_callback, s);
1398}
1399
1400void avahi_s_entry_group_free(AvahiSEntryGroup *g) {
1401    AvahiEntry *e;
1402
1403    assert(g);
1404    assert(g->server);
1405
1406    if (g->type == AVAHI_GROUP_UNSET) {
1407        avahi_free(g);
1408
1409    } else if (g->type == AVAHI_GROUP_MDNS) {
1410        for (e = g->entries; e; e = e->by_group_next) {
1411            assert(e->type == AVAHI_ENTRY_MDNS);
1412
1413            if (!e->dead) {
1414                avahi_goodbye_entry(g->server, e, 1, 1);
1415                e->dead = 1;
1416            }
1417        }
1418
1419        if (g->proto.mdns.register_time_event) {
1420            avahi_time_event_free(g->proto.mdns.register_time_event);
1421            g->proto.mdns.register_time_event = NULL;
1422        }
1423
1424        g->server->mdns.need_group_cleanup = 1;
1425        g->server->mdns.need_entry_cleanup = 1;
1426
1427    } else {
1428        assert (g->type == AVAHI_GROUP_LLMNR);
1429        for (e = g->entries; e; e = e->by_group_next) {
1430            assert(e->type == AVAHI_ENTRY_LLMNR);
1431
1432            if (!e->dead) {
1433                avahi_remove_verifiers(g->server, e);
1434                e->dead = 1;
1435            }
1436        }
1437
1438        g->server->llmnr.need_entry_cleanup = 1;
1439        g->server->llmnr.need_group_cleanup = 1;
1440    }
1441
1442    g->dead = 1;
1443
1444    schedule_cleanup(g->server);
1445}
1446
1447static void entry_group_commit_real(AvahiSEntryGroup *g) {
1448    assert(g);
1449    assert(g->type == AVAHI_GROUP_MDNS);
1450
1451    gettimeofday(&g->proto.mdns.register_time, NULL);
1452
1453    avahi_s_entry_group_change_state(g, AVAHI_ENTRY_GROUP_REGISTERING);
1454
1455    if (g->dead)
1456        return;
1457
1458    avahi_announce_group(g->server, g);
1459    avahi_s_entry_group_check_probed(g, 0);
1460}
1461
1462static void entry_group_register_time_event_callback(AVAHI_GCC_UNUSED AvahiTimeEvent *e, void* userdata) {
1463    AvahiSEntryGroup *g = userdata;
1464    assert(g);
1465    assert(g->type == AVAHI_GROUP_MDNS);
1466
1467    avahi_time_event_free(g->proto.mdns.register_time_event);
1468    g->proto.mdns.register_time_event = NULL;
1469
1470    /* Holdoff time passed, so let's start probing */
1471    entry_group_commit_real(g);
1472}
1473
1474int avahi_s_entry_group_commit(AvahiSEntryGroup *g) {
1475    struct timeval now;
1476
1477    assert(g);
1478    assert(!g->dead);
1479
1480    if (g->type == AVAHI_GROUP_UNSET)
1481        /* Group is empty */
1482        return avahi_server_set_errno(g->server, AVAHI_ERR_IS_EMPTY);
1483
1484    if (g->type == AVAHI_GROUP_MDNS) {
1485        if (g->state != AVAHI_ENTRY_GROUP_UNCOMMITED && g->state != AVAHI_ENTRY_GROUP_COLLISION)
1486            return avahi_server_set_errno(g->server, AVAHI_ERR_BAD_STATE);
1487
1488/*        if (avahi_s_entry_group_is_empty(g))
1489            return avahi_server_set_errno(g->server, AVAHI_ERR_IS_EMPTY);*/
1490
1491        g->proto.mdns.n_register_try++;
1492
1493        avahi_timeval_add(&g->proto.mdns.register_time,
1494                          1000*(g->proto.mdns.n_register_try >= AVAHI_RR_RATE_LIMIT_COUNT ?
1495                                AVAHI_RR_HOLDOFF_MSEC_RATE_LIMIT :
1496                                AVAHI_RR_HOLDOFF_MSEC));
1497
1498        gettimeofday(&now, NULL);
1499
1500        if (avahi_timeval_compare(&g->proto.mdns.register_time, &now) <= 0) {
1501
1502            /* Holdoff time passed, so let's start probing */
1503            entry_group_commit_real(g);
1504        } else {
1505
1506             /* Holdoff time has not yet passed, so let's wait */
1507            assert(!g->proto.mdns.register_time_event);
1508            g->proto.mdns.register_time_event = avahi_time_event_new(g->server->time_event_queue, &g->proto.mdns.register_time, entry_group_register_time_event_callback, g);
1509
1510            avahi_s_entry_group_change_state(g, AVAHI_ENTRY_GROUP_REGISTERING);
1511        }
1512
1513    } else {
1514        assert(g->type == AVAHI_GROUP_LLMNR);
1515
1516        if (g->state != AVAHI_ENTRY_GROUP_LLMNR_UNCOMMITED && g->state != AVAHI_ENTRY_GROUP_LLMNR_COLLISION)
1517            return avahi_server_set_errno(g->server, AVAHI_ERR_BAD_STATE);
1518
1519/*        if (avahi_s_entry_group_is_empty(g))
1520            return avahi_server_set_errno(g->server, AVAHI_ERR_IS_EMPTY);*/
1521
1522        avahi_s_entry_group_change_state(g, AVAHI_ENTRY_GROUP_LLMNR_VERIFYING);
1523        avahi_verify_group(g->server, g);
1524    }
1525
1526    return AVAHI_OK;
1527}
1528
1529void avahi_s_entry_group_reset(AvahiSEntryGroup *g) {
1530    AvahiEntry *e;
1531    assert(g);
1532
1533    if (g->type == AVAHI_GROUP_UNSET)
1534        return;
1535
1536    if (g->type == AVAHI_GROUP_MDNS) {
1537
1538        for (e = g->entries; e; e = e->by_group_next) {
1539            assert(e->type == AVAHI_ENTRY_MDNS);
1540            if (!e->dead) {
1541                avahi_goodbye_entry(g->server, e, 1, 1);
1542                e->dead = 1;
1543            }
1544        }
1545        g->server->mdns.need_entry_cleanup = 1;
1546
1547        g->proto.mdns.n_probing = 0;
1548
1549        avahi_s_entry_group_change_state(g, AVAHI_ENTRY_GROUP_UNCOMMITED);
1550
1551    } else {
1552        assert(g->type == AVAHI_GROUP_LLMNR);
1553
1554        for (e = g->entries; e; e = e->by_group_next) {
1555            assert(e->type == AVAHI_ENTRY_LLMNR);
1556            if (!e->dead) {
1557                avahi_remove_verifiers(g->server, e);
1558                e->dead = 1;
1559            }
1560        }
1561        g->server->llmnr.need_entry_cleanup = 1;
1562
1563        g->proto.llmnr.n_verifying = 0;
1564
1565        avahi_s_entry_group_change_state(g, AVAHI_ENTRY_GROUP_LLMNR_UNCOMMITED);
1566    }
1567
1568    schedule_cleanup(g->server);
1569}
1570
1571int avahi_entry_is_commited(AvahiEntry *e) {
1572    assert(e);
1573    assert(!e->dead);
1574
1575    if (e->type == AVAHI_ENTRY_MDNS)
1576        return !e->group ||
1577            e->group->state == AVAHI_ENTRY_GROUP_REGISTERING ||
1578            e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED;
1579
1580    else
1581        return (!e->group ||
1582            e->group->state == AVAHI_ENTRY_GROUP_LLMNR_VERIFYING ||
1583            e->group->state == AVAHI_ENTRY_GROUP_LLMNR_ESTABLISHED );
1584}
1585
1586AvahiEntryGroupState avahi_s_entry_group_get_state(AvahiSEntryGroup *g) {
1587    assert(g);
1588    assert(!g->dead);
1589
1590    return g->state;
1591}
1592
1593void avahi_s_entry_group_set_data(AvahiSEntryGroup *g, void* userdata) {
1594    assert(g);
1595
1596    g->userdata = userdata;
1597}
1598
1599void* avahi_s_entry_group_get_data(AvahiSEntryGroup *g) {
1600    assert(g);
1601
1602    return g->userdata;
1603}
1604
1605int avahi_s_entry_group_is_empty(AvahiSEntryGroup *g) {
1606    AvahiEntry *e;
1607    assert(g);
1608
1609    if (g->type == AVAHI_GROUP_UNSET)
1610        /* There has not been any entry added in this group so far. */
1611        return 1;
1612
1613    /* Look for an entry that is not dead */
1614    for (e = g->entries; e; e = e->by_group_next)
1615        if (!e->dead)
1616            return 0;
1617
1618    return 1;
1619}
1620