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