]> git.meshlink.io Git - catta/blobdiff - avahi-core/server.c
add support for service subtypes: avahi_server_add_service_subtype()
[catta] / avahi-core / server.c
index 27142982f7273b2b7fb13b8b5194c04f312b5530..fdc53d5737b78821186945bb2b2ae8c0c79f240b 100644 (file)
@@ -139,7 +139,7 @@ static void enum_aux_records(AvahiServer *s, AvahiInterface *i, const char *name
 
     for (e = avahi_hashmap_lookup(s->entries_by_key, k); e; e = e->by_key_next)
         if (!e->dead && avahi_entry_is_registered(s, e, i)) 
-            callback(s, e->record, e->flags & AVAHI_ENTRY_UNIQUE, userdata);
+            callback(s, e->record, e->flags & AVAHI_PUBLISH_UNIQUE, userdata);
 
     avahi_key_unref(k);
 }
@@ -166,7 +166,7 @@ void avahi_server_prepare_response(AvahiServer *s, AvahiInterface *i, AvahiEntry
     assert(i);
     assert(e);
 
-    avahi_record_list_push(s->record_list, e->record, e->flags & AVAHI_ENTRY_UNIQUE, unicast_response, auxiliary);
+    avahi_record_list_push(s->record_list, e->record, e->flags & AVAHI_PUBLISH_UNIQUE, unicast_response, auxiliary);
 }
 
 void avahi_server_prepare_matching_responses(AvahiServer *s, AvahiInterface *i, AvahiKey *k, int unicast_response) {
@@ -319,7 +319,7 @@ static int handle_conflict(AvahiServer *s, AvahiInterface *i, AvahiRecord *recor
             continue;
         }
         
-        if (!(e->flags & AVAHI_ENTRY_UNIQUE) && !unique)
+        if (!(e->flags & AVAHI_PUBLISH_UNIQUE) && !unique)
             continue;
 
         /* Either our entry or the other is intended to be unique, so let's check */
@@ -613,8 +613,10 @@ static void handle_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterfac
             goto fail;
         }
 
-        if (!legacy_unicast && !from_local_iface)
+        if (!legacy_unicast && !from_local_iface) {
             reflect_query(s, i, key);
+            avahi_cache_start_poof(i->cache, key, a);
+        }
 
         if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 &&
             !(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_TC))
@@ -641,6 +643,7 @@ static void handle_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterfac
             if (handle_conflict(s, i, record, unique, a)) {
                 avahi_response_scheduler_suppress(i->response_scheduler, record, a);
                 avahi_record_list_drop(s->record_list, record);
+                avahi_cache_stop_poof(i->cache, record, a);
             }
             
             avahi_record_unref(record);
@@ -1192,7 +1195,7 @@ static void register_hinfo(AvahiServer *s) {
         r->data.hinfo.cpu = avahi_strdup(avahi_strup(utsname.machine));
         r->data.hinfo.os = avahi_strdup(avahi_strup(utsname.sysname));
 
-        if (avahi_server_add(s, s->hinfo_entry_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_ENTRY_UNIQUE, r) < 0) {
+        if (avahi_server_add(s, s->hinfo_entry_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_UNIQUE, r) < 0) {
             avahi_log_warn("Failed to add HINFO RR: %s", avahi_strerror(s->error));
             return;
         }
@@ -1211,10 +1214,10 @@ static void register_localhost(AvahiServer *s) {
     
     /* Add localhost entries */
     avahi_address_parse("127.0.0.1", AVAHI_PROTO_INET, &a);
-    avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_ENTRY_NOPROBE|AVAHI_ENTRY_NOANNOUNCE, "localhost", &a);
+    avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_NO_PROBE|AVAHI_PUBLISH_NO_ANNOUNCE, "localhost", &a);
 
     avahi_address_parse("::1", AVAHI_PROTO_INET6, &a);
-    avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_ENTRY_NOPROBE|AVAHI_ENTRY_NOANNOUNCE, "ip6-localhost", &a);
+    avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_NO_PROBE|AVAHI_PUBLISH_NO_ANNOUNCE, "ip6-localhost", &a);
 }
 
 static void register_browse_domain(AvahiServer *s) {
@@ -1282,7 +1285,7 @@ int avahi_server_set_host_name(AvahiServer *s, const char *host_name) {
     withdraw_host_rrs(s);
 
     avahi_free(s->host_name);
-    s->host_name = host_name ? avahi_normalize_name(host_name) : avahi_get_host_name();
+    s->host_name = host_name ? avahi_normalize_name_strdup(host_name) : avahi_get_host_name_strdup();
     s->host_name[strcspn(s->host_name, ".")] = 0;
     update_fqdn(s);
 
@@ -1300,7 +1303,7 @@ int avahi_server_set_domain_name(AvahiServer *s, const char *domain_name) {
     withdraw_host_rrs(s);
 
     avahi_free(s->domain_name);
-    s->domain_name = domain_name ? avahi_normalize_name(domain_name) : avahi_strdup("local");
+    s->domain_name = domain_name ? avahi_normalize_name_strdup(domain_name) : avahi_strdup("local");
     update_fqdn(s);
 
     register_stuff(s);
@@ -1321,8 +1324,8 @@ static int valid_server_config(const AvahiServerConfig *sc) {
 static int setup_sockets(AvahiServer *s) {
     assert(s);
     
-    s->fd_ipv4 = s->config.use_ipv4 ? avahi_open_socket_ipv4() : -1;
-    s->fd_ipv6 = s->config.use_ipv6 ? avahi_open_socket_ipv6() : -1;
+    s->fd_ipv4 = s->config.use_ipv4 ? avahi_open_socket_ipv4(s->config.disallow_other_stacks) : -1;
+    s->fd_ipv6 = s->config.use_ipv6 ? avahi_open_socket_ipv6(s->config.disallow_other_stacks) : -1;
     
     if (s->fd_ipv6 < 0 && s->fd_ipv4 < 0)
         return AVAHI_ERR_NO_NETWORK;
@@ -1415,7 +1418,6 @@ AvahiServer *avahi_server_new(const AvahiPoll *poll_api, const AvahiServerConfig
     s->legacy_unicast_reflect_slots = NULL;
     s->legacy_unicast_reflect_id = 0;
 
-
     if (s->config.enable_wide_area) {
         s->wide_area_lookup_engine = avahi_wide_area_engine_new(s);
         avahi_wide_area_set_servers(s->wide_area_lookup_engine, s->config.wide_area_servers, s->config.n_wide_area_servers);
@@ -1429,9 +1431,9 @@ AvahiServer *avahi_server_new(const AvahiPoll *poll_api, const AvahiServerConfig
     } while (s->local_service_cookie == AVAHI_SERVICE_COOKIE_INVALID);
     
     /* Get host name */
-    s->host_name = s->config.host_name ? avahi_normalize_name(s->config.host_name) : avahi_get_host_name();
+    s->host_name = s->config.host_name ? avahi_normalize_name_strdup(s->config.host_name) : avahi_get_host_name_strdup();
     s->host_name[strcspn(s->host_name, ".")] = 0;
-    s->domain_name = s->config.domain_name ? avahi_normalize_name(s->config.domain_name) : avahi_strdup("local");
+    s->domain_name = s->config.domain_name ? avahi_normalize_name_strdup(s->config.domain_name) : avahi_strdup("local");
     s->host_name_fqdn = NULL;
     update_fqdn(s);
 
@@ -1532,7 +1534,7 @@ void avahi_server_free(AvahiServer* s) {
     avahi_free(s);
 }
 
-static int check_record_conflict(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiRecord *r, AvahiEntryFlags flags) {
+static int check_record_conflict(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiRecord *r, AvahiPublishFlags flags) {
     AvahiEntry *e;
     
     assert(s);
@@ -1542,10 +1544,10 @@ static int check_record_conflict(AvahiServer *s, AvahiIfIndex interface, AvahiPr
         if (e->dead)
             continue;
 
-        if (!(flags & AVAHI_ENTRY_UNIQUE) && !(e->flags & AVAHI_ENTRY_UNIQUE))
+        if (!(flags & AVAHI_PUBLISH_UNIQUE) && !(e->flags & AVAHI_PUBLISH_UNIQUE))
             continue;
         
-        if ((flags & AVAHI_ENTRY_ALLOWMUTIPLE) && (e->flags & AVAHI_ENTRY_ALLOWMUTIPLE) )
+        if ((flags & AVAHI_PUBLISH_ALLOW_MULTIPLE) && (e->flags & AVAHI_PUBLISH_ALLOW_MULTIPLE) )
             continue;
 
         if ((interface <= 0 ||
@@ -1566,7 +1568,7 @@ int avahi_server_add(
     AvahiSEntryGroup *g,
     AvahiIfIndex interface,
     AvahiProtocol protocol,
-    AvahiEntryFlags flags,
+    AvahiPublishFlags flags,
     AvahiRecord *r) {
     
     AvahiEntry *e, *t;
@@ -1574,6 +1576,18 @@ int avahi_server_add(
     assert(s);
     assert(r);
 
+    if (!AVAHI_IF_VALID(interface))
+        return avahi_server_set_errno(s, AVAHI_ERR_INVALID_INTERFACE);
+
+    if (!AVAHI_PROTO_VALID(protocol))
+        return avahi_server_set_errno(s, AVAHI_ERR_INVALID_PROTOCOL);
+    
+    if (!AVAHI_FLAGS_VALID(flags, AVAHI_PUBLISH_NO_ANNOUNCE|AVAHI_PUBLISH_NO_PROBE|AVAHI_PUBLISH_UNIQUE|AVAHI_PUBLISH_ALLOW_MULTIPLE|AVAHI_PUBLISH_IS_PROXY))
+        return avahi_server_set_errno(s, AVAHI_ERR_INVALID_FLAGS);
+    
+    if (!avahi_is_valid_domain_name(r->key->name))
+        return avahi_server_set_errno(s, AVAHI_ERR_INVALID_HOST_NAME);
+    
     if (r->ttl == 0)
         return avahi_server_set_errno(s, AVAHI_ERR_INVALID_TTL);
 
@@ -1587,7 +1601,7 @@ int avahi_server_add(
         return avahi_server_set_errno(s, AVAHI_ERR_LOCAL_COLLISION);
 
     if (!(e = avahi_new(AvahiEntry, 1)))
-        return avahi_server_set_errno(s, AVAHI_ERR_NO_NETWORK);
+        return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
         
     e->server = s;
     e->record = avahi_record_ref(r);
@@ -1668,7 +1682,7 @@ int avahi_server_add_ptr(
     AvahiSEntryGroup *g,
     AvahiIfIndex interface,
     AvahiProtocol protocol,
-    AvahiEntryFlags flags,
+    AvahiPublishFlags flags,
     uint32_t ttl,
     const char *name,
     const char *dest) {
@@ -1679,10 +1693,16 @@ int avahi_server_add_ptr(
     assert(s);
     assert(dest);
 
-    if (!(r = avahi_record_new_full(name ? name : s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR, ttl)))
+    if ((name && !avahi_is_valid_domain_name(name)) || !avahi_is_valid_domain_name(dest))
+        return avahi_server_set_errno(s, AVAHI_ERR_INVALID_HOST_NAME);
+
+    if (!name)
+        name = s->host_name_fqdn;
+    
+    if (!(r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR, ttl)))
         return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
         
-    r->data.ptr.name = avahi_normalize_name(dest);
+    r->data.ptr.name = avahi_normalize_name_strdup(dest);
     ret = avahi_server_add(s, g, interface, protocol, flags, r);
     avahi_record_unref(r);
     return ret;
@@ -1693,30 +1713,38 @@ int avahi_server_add_address(
     AvahiSEntryGroup *g,
     AvahiIfIndex interface,
     AvahiProtocol protocol,
-    AvahiEntryFlags flags,
+    AvahiPublishFlags flags,
     const char *name,
     AvahiAddress *a) {
 
     char *n = NULL;
     int ret = AVAHI_OK;
+    
     assert(s);
     assert(a);
 
-    if (name) {
-        if (!(n = avahi_normalize_name(name)))
-            return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
+    if (!AVAHI_IF_VALID(interface))
+        return avahi_server_set_errno(s, AVAHI_ERR_INVALID_INTERFACE);
 
-        name = n;
-    } else
+    if (!AVAHI_PROTO_VALID(protocol) || !AVAHI_PROTO_VALID(a->proto))
+        return avahi_server_set_errno(s, AVAHI_ERR_INVALID_PROTOCOL);
+    
+    if (!AVAHI_FLAGS_VALID(flags, AVAHI_PUBLISH_NO_REVERSE|AVAHI_PUBLISH_NO_ANNOUNCE|AVAHI_PUBLISH_NO_PROBE))
+        return avahi_server_set_errno(s, AVAHI_ERR_INVALID_FLAGS);
+    
+    if (name && !avahi_is_valid_domain_name(name))
+        return avahi_server_set_errno(s, AVAHI_ERR_INVALID_HOST_NAME);
+
+    if (!name)
         name = s->host_name_fqdn;
+    else {
+        if (!(n = avahi_normalize_name_strdup(name)))
+            return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
 
-    if (!avahi_is_valid_domain_name(name)) {
-        ret = avahi_server_set_errno(s, AVAHI_ERR_INVALID_HOST_NAME);
-        goto fail;
+        name = n;
     }
     
     if (a->proto == AVAHI_PROTO_INET) {
-        char *reverse;
         AvahiRecord  *r;
 
         if (!(r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A, AVAHI_DEFAULT_TTL_HOST_NAME))) {
@@ -1725,22 +1753,25 @@ int avahi_server_add_address(
         }
         
         r->data.a.address = a->data.ipv4;
-        ret = avahi_server_add(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE | AVAHI_ENTRY_ALLOWMUTIPLE, r);
+        ret = avahi_server_add(s, g, interface, protocol, (flags & ~ AVAHI_PUBLISH_NO_REVERSE) | AVAHI_PUBLISH_UNIQUE | AVAHI_PUBLISH_ALLOW_MULTIPLE, r);
         avahi_record_unref(r);
 
         if (ret < 0)
             goto fail;
-        
-        if (!(reverse = avahi_reverse_lookup_name_ipv4(&a->data.ipv4))) {
-            ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
-            goto fail;
-        }
-
-        ret = avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, AVAHI_DEFAULT_TTL_HOST_NAME, reverse, name);
-        avahi_free(reverse);
 
+        if (!(flags & AVAHI_PUBLISH_NO_REVERSE)) {
+            char *reverse;
+            
+            if (!(reverse = avahi_reverse_lookup_name_ipv4(&a->data.ipv4))) {
+                ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
+                goto fail;
+            }
+            
+            ret = avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_PUBLISH_UNIQUE, AVAHI_DEFAULT_TTL_HOST_NAME, reverse, name);
+            avahi_free(reverse);
+        }
+        
     } else {
-        char *reverse;
         AvahiRecord *r;
 
         assert(a->proto == AVAHI_PROTO_INET6);
@@ -1751,30 +1782,34 @@ int avahi_server_add_address(
         }
         
         r->data.aaaa.address = a->data.ipv6;
-        ret = avahi_server_add(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE | AVAHI_ENTRY_ALLOWMUTIPLE, r);
+        ret = avahi_server_add(s, g, interface, protocol, (flags & ~ AVAHI_PUBLISH_NO_REVERSE) | AVAHI_PUBLISH_UNIQUE | AVAHI_PUBLISH_ALLOW_MULTIPLE, r);
         avahi_record_unref(r);
 
         if (ret < 0)
             goto fail;
 
-        if (!(reverse = avahi_reverse_lookup_name_ipv6_arpa(&a->data.ipv6))) {
-            ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
-            goto fail;
-        }
-            
-        ret = avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, AVAHI_DEFAULT_TTL_HOST_NAME, reverse, name);
-        avahi_free(reverse);
+        if (!(flags & AVAHI_PUBLISH_NO_REVERSE)) {
+            char *reverse;
 
-        if (ret < 0)
-            goto fail;
-    
-        if (!(reverse = avahi_reverse_lookup_name_ipv6_int(&a->data.ipv6))) {
-            ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
-            goto fail;
+            if (!(reverse = avahi_reverse_lookup_name_ipv6_arpa(&a->data.ipv6))) {
+                ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
+                goto fail;
+            }
+            
+            ret = avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_PUBLISH_UNIQUE, AVAHI_DEFAULT_TTL_HOST_NAME, reverse, name);
+            avahi_free(reverse);
+            
+            if (ret < 0)
+                goto fail;
+            
+            if (!(reverse = avahi_reverse_lookup_name_ipv6_int(&a->data.ipv6))) {
+                ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
+                goto fail;
+            }
+            
+            ret = avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_PUBLISH_UNIQUE, AVAHI_DEFAULT_TTL_HOST_NAME, reverse, name);
+            avahi_free(reverse);
         }
-
-        ret = avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, AVAHI_DEFAULT_TTL_HOST_NAME, reverse, name);
-        avahi_free(reverse);
     }
 
 fail:
@@ -1789,7 +1824,7 @@ static int server_add_txt_strlst_nocopy(
     AvahiSEntryGroup *g,
     AvahiIfIndex interface,
     AvahiProtocol protocol,
-    AvahiEntryFlags flags,
+    AvahiPublishFlags flags,
     uint32_t ttl,
     const char *name,
     AvahiStringList *strlst) {
@@ -1798,9 +1833,11 @@ static int server_add_txt_strlst_nocopy(
     int ret;
     
     assert(s);
-    
-    if (!(r = avahi_record_new_full(name ? name : s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_TXT, ttl)))
+
+    if (!(r = avahi_record_new_full(name ? name : s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_TXT, ttl))) {
+        avahi_string_list_free(strlst);
         return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
+    }
     
     r->data.txt.string_list = strlst;
     ret = avahi_server_add(s, g, interface, protocol, flags, r);
@@ -1814,7 +1851,7 @@ int avahi_server_add_txt_strlst(
     AvahiSEntryGroup *g,
     AvahiIfIndex interface,
     AvahiProtocol protocol,
-    AvahiEntryFlags flags,
+    AvahiPublishFlags flags,
     uint32_t ttl,
     const char *name,
     AvahiStringList *strlst) {
@@ -1829,7 +1866,7 @@ int avahi_server_add_txt_va(
     AvahiSEntryGroup *g,
     AvahiIfIndex interface,
     AvahiProtocol protocol,
-    AvahiEntryFlags flags,
+    AvahiPublishFlags flags,
     uint32_t ttl,
     const char *name,
     va_list va) {
@@ -1844,7 +1881,7 @@ int avahi_server_add_txt(
     AvahiSEntryGroup *g,
     AvahiIfIndex interface,
     AvahiProtocol protocol,
-    AvahiEntryFlags flags,
+    AvahiPublishFlags flags,
     uint32_t ttl,
     const char *name,
     ...) {
@@ -1861,28 +1898,6 @@ int avahi_server_add_txt(
     return ret;
 }
 
-static void escape_service_name(char *d, size_t size, const char *s) {
-    assert(d);
-    assert(size);
-    assert(s);
-
-    while (*s && size >= 2) {
-        if (*s == '.' || *s == '\\') {
-            if (size < 3)
-                break;
-
-            *(d++) = '\\';
-            size--;
-        }
-            
-        *(d++) = *(s++);
-        size--;
-    }
-
-    assert(size > 0);
-    *(d++) = 0;
-}
-
 static AvahiStringList *add_magic_cookie(
     AvahiServer *s,
     AvahiStringList *strlst) {
@@ -1904,6 +1919,7 @@ static int server_add_service_strlst_nocopy(
     AvahiSEntryGroup *g,
     AvahiIfIndex interface,
     AvahiProtocol protocol,
+    AvahiPublishFlags flags,
     const char *name,
     const char *type,
     const char *domain,
@@ -1911,8 +1927,7 @@ static int server_add_service_strlst_nocopy(
     uint16_t port,
     AvahiStringList *strlst) {
 
-    char ptr_name[256], svc_name[256], ename[64], enum_ptr[256];
-    char *t = NULL, *d = NULL, *h = NULL;
+    char ptr_name[AVAHI_DOMAIN_NAME_MAX], svc_name[AVAHI_DOMAIN_NAME_MAX], enum_ptr[AVAHI_DOMAIN_NAME_MAX], *h = NULL;
     AvahiRecord *r = NULL;
     int ret = AVAHI_OK;
     
@@ -1920,19 +1935,13 @@ static int server_add_service_strlst_nocopy(
     assert(type);
     assert(name);
 
-    if (!avahi_is_valid_service_name(name))
-        return avahi_server_set_errno(s, AVAHI_ERR_INVALID_SERVICE_NAME);
-
-    if (!avahi_is_valid_service_type(type))
-        return avahi_server_set_errno(s, AVAHI_ERR_INVALID_SERVICE_TYPE);
-
-    if (domain && !avahi_is_valid_domain_name(domain))
-        return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
-
-    if (host && !avahi_is_valid_domain_name(host))
-        return avahi_server_set_errno(s, AVAHI_ERR_INVALID_HOST_NAME);
-
-    escape_service_name(ename, sizeof(ename), name);
+    AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE);
+    AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_PROTO_VALID(interface), AVAHI_ERR_INVALID_PROTOCOL);
+    AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_FLAGS_VALID(flags, AVAHI_PUBLISH_NO_COOKIE|AVAHI_PUBLISH_IS_PROXY), AVAHI_ERR_INVALID_FLAGS);
+    AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_name(name), AVAHI_ERR_INVALID_SERVICE_NAME);
+    AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_type(type), AVAHI_ERR_INVALID_SERVICE_TYPE);
+    AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME);
+    AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, !host || avahi_is_valid_domain_name(host), AVAHI_ERR_INVALID_HOST_NAME);
 
     if (!domain)
         domain = s->domain_name;
@@ -1940,19 +1949,25 @@ static int server_add_service_strlst_nocopy(
     if (!host)
         host = s->host_name_fqdn;
 
-    if (!(d = avahi_normalize_name(domain)) ||
-        !(t = avahi_normalize_name(type)) ||
-        !(h = avahi_normalize_name(host))) {
+    if (!(h = avahi_normalize_name_strdup(host))) {
         ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
         goto fail;
     }
 
-    snprintf(ptr_name, sizeof(ptr_name), "%s.%s", t, d);
-    snprintf(svc_name, sizeof(svc_name), "%s.%s.%s", ename, t, d);
+    if ((ret = avahi_service_name_join(svc_name, sizeof(svc_name), name, type, domain)) < 0 ||
+        (ret = avahi_service_name_join(ptr_name, sizeof(ptr_name), NULL, type, domain)) < 0 ||
+        (ret = avahi_service_name_join(enum_ptr, sizeof(enum_ptr), NULL, "_services._dns-sd._udp", domain)) < 0) {
+        avahi_server_set_errno(s, ret);
+        goto fail;
+    }
 
-    if ((ret = avahi_server_add_ptr(s, g, interface, protocol, AVAHI_ENTRY_NULL, AVAHI_DEFAULT_TTL, ptr_name, svc_name)) < 0)
+    /* Add service enumeration PTR record */
+    
+    if ((ret = avahi_server_add_ptr(s, g, interface, protocol, flags & AVAHI_PUBLISH_IS_PROXY, AVAHI_DEFAULT_TTL, ptr_name, svc_name)) < 0)
         goto fail;
 
+    /* Add SRV record */
+    
     if (!(r = avahi_record_new_full(svc_name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV, AVAHI_DEFAULT_TTL_HOST_NAME))) {
         ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
         goto fail;
@@ -1963,39 +1978,43 @@ static int server_add_service_strlst_nocopy(
     r->data.srv.port = port;
     r->data.srv.name = h;
     h = NULL;
-    ret = avahi_server_add(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE, r);
+    ret = avahi_server_add(s, g, interface, protocol, (flags & AVAHI_PUBLISH_IS_PROXY) | AVAHI_PUBLISH_UNIQUE, r);
     avahi_record_unref(r);
 
     if (ret < 0)
         goto fail;
 
-    strlst = add_magic_cookie(s, strlst);
+    /* Add TXT record */
+
+    if (!(flags & AVAHI_PUBLISH_NO_COOKIE))
+        strlst = add_magic_cookie(s, strlst);
     
-    ret = server_add_txt_strlst_nocopy(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE, AVAHI_DEFAULT_TTL, svc_name, strlst);
+    ret = server_add_txt_strlst_nocopy(s, g, interface, protocol, (flags & AVAHI_PUBLISH_IS_PROXY) | AVAHI_PUBLISH_UNIQUE, AVAHI_DEFAULT_TTL, svc_name, strlst);
     strlst = NULL;
 
     if (ret < 0)
         goto fail;
 
-    snprintf(enum_ptr, sizeof(enum_ptr), "_services._dns-sd._udp.%s", d);
-    ret = avahi_server_add_ptr(s, g, interface, protocol, AVAHI_ENTRY_NULL, AVAHI_DEFAULT_TTL, enum_ptr, ptr_name);
+    /* Add service type enumeration record */
+    
+    ret = avahi_server_add_ptr(s, g, interface, protocol, (flags & AVAHI_PUBLISH_IS_PROXY), AVAHI_DEFAULT_TTL, enum_ptr, ptr_name);
 
 fail:
     
-    avahi_free(d);
-    avahi_free(t);
-    avahi_free(h);
-
     avahi_string_list_free(strlst);
+    avahi_free(h);
     
     return ret;
 }
 
+
+
 int avahi_server_add_service_strlst(
     AvahiServer *s,
     AvahiSEntryGroup *g,
     AvahiIfIndex interface,
     AvahiProtocol protocol,
+    AvahiPublishFlags flags,
     const char *name,
     const char *type,
     const char *domain,
@@ -2007,7 +2026,7 @@ int avahi_server_add_service_strlst(
     assert(type);
     assert(name);
 
-    return server_add_service_strlst_nocopy(s, g, interface, protocol, name, type, domain, host, port, avahi_string_list_copy(strlst));
+    return server_add_service_strlst_nocopy(s, g, interface, protocol, flags, name, type, domain, host, port, avahi_string_list_copy(strlst));
 }
 
 int avahi_server_add_service_va(
@@ -2015,18 +2034,19 @@ int avahi_server_add_service_va(
     AvahiSEntryGroup *g,
     AvahiIfIndex interface,
     AvahiProtocol protocol,
+    AvahiPublishFlags flags,
     const char *name,
     const char *type,
     const char *domain,
     const char *host,
     uint16_t port,
-    va_list va){
+    va_list va) {
 
     assert(s);
     assert(type);
     assert(name);
 
-    return server_add_service_strlst_nocopy(s, g, interface, protocol, name, type, domain, host, port, avahi_string_list_new_va(va));
+    return server_add_service_strlst_nocopy(s, g, interface, protocol, flags, name, type, domain, host, port, avahi_string_list_new_va(va));
 }
 
 int avahi_server_add_service(
@@ -2034,6 +2054,7 @@ int avahi_server_add_service(
     AvahiSEntryGroup *g,
     AvahiIfIndex interface,
     AvahiProtocol protocol,
+    AvahiPublishFlags flags,
     const char *name,
     const char *type,
     const char *domain,
@@ -2049,8 +2070,52 @@ int avahi_server_add_service(
     assert(name);
 
     va_start(va, port);
-    ret = avahi_server_add_service_va(s, g, interface, protocol, name, type, domain, host, port, va);
+    ret = avahi_server_add_service_va(s, g, interface, protocol, flags, name, type, domain, host, port, va);
     va_end(va);
+    
+    return ret;
+}
+
+int avahi_server_add_service_subtype(
+    AvahiServer *s,
+    AvahiSEntryGroup *g,
+    AvahiIfIndex interface,
+    AvahiProtocol protocol,
+    AvahiPublishFlags flags,
+    const char *name,        
+    const char *type,        
+    const char *domain,      
+    const char *subtype) {
+
+    int ret = AVAHI_OK;
+    char svc_name[AVAHI_DOMAIN_NAME_MAX], ptr_name[AVAHI_DOMAIN_NAME_MAX];
+    
+    assert(name);
+    assert(type);
+    assert(subtype);
+
+    AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE);
+    AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_PROTO_VALID(interface), AVAHI_ERR_INVALID_PROTOCOL);
+    AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_FLAGS_VALID(flags, AVAHI_PUBLISH_NO_COOKIE|AVAHI_PUBLISH_IS_PROXY), AVAHI_ERR_INVALID_FLAGS);
+    AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_name(name), AVAHI_ERR_INVALID_SERVICE_NAME);
+    AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_type(type), AVAHI_ERR_INVALID_SERVICE_TYPE);
+    AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME);
+    AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_type(subtype), AVAHI_ERR_INVALID_SERVICE_SUBTYPE);
+
+    if (!domain)
+        domain = s->domain_name;
+
+    if ((ret = avahi_service_name_join(svc_name, sizeof(svc_name), name, type, domain)) < 0 ||
+        (ret = avahi_service_name_join(ptr_name, sizeof(ptr_name), NULL, subtype, domain)) < 0) {
+        avahi_server_set_errno(s, ret);
+        goto fail;
+    }
+
+    if ((ret = avahi_server_add_ptr(s, g, interface, protocol, flags & AVAHI_PUBLISH_IS_PROXY, AVAHI_DEFAULT_TTL, ptr_name, svc_name)) < 0)
+        goto fail;
+
+fail:
+    
     return ret;
 }
 
@@ -2081,6 +2146,7 @@ int avahi_server_add_dns_server_address(
     AvahiSEntryGroup *g,
     AvahiIfIndex interface,
     AvahiProtocol protocol,
+    AvahiPublishFlags flags,
     const char *domain,
     AvahiDNSServerType type,
     const AvahiAddress *address,
@@ -2092,23 +2158,33 @@ int avahi_server_add_dns_server_address(
 
     assert(s);
     assert(address);
-    assert(type == AVAHI_DNS_SERVER_UPDATE || type == AVAHI_DNS_SERVER_RESOLVE);
-    assert(address->proto == AVAHI_PROTO_INET || address->proto == AVAHI_PROTO_INET6);
 
+    if (!AVAHI_IF_VALID(interface))
+        return avahi_server_set_errno(s, AVAHI_ERR_INVALID_INTERFACE);
+
+    if (!AVAHI_PROTO_VALID(protocol) || !AVAHI_PROTO_VALID(address->proto))
+        return avahi_server_set_errno(s, AVAHI_ERR_INVALID_PROTOCOL);
+    
+    if (!AVAHI_FLAGS_VALID(flags, 0) || (type != AVAHI_DNS_SERVER_UPDATE && type != AVAHI_DNS_SERVER_RESOLVE))
+        return avahi_server_set_errno(s, AVAHI_ERR_INVALID_FLAGS);
+    
     if (port == 0)
         return avahi_server_set_errno(s, AVAHI_ERR_INVALID_PORT);
     
     if (domain && !avahi_is_valid_domain_name(domain))
         return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
 
+    if (!domain)
+        domain = s->domain_name;
+
     if (address->proto == AVAHI_PROTO_INET) {
         hexstring(h, sizeof(h), &address->data, sizeof(AvahiIPv4Address));
-        snprintf(n, sizeof(n), "ip-%s.%s", h, s->domain_name);
+        snprintf(n, sizeof(n), "ip-%s.%s", h, domain);
         r = avahi_record_new_full(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A, AVAHI_DEFAULT_TTL_HOST_NAME);
         r->data.a.address = address->data.ipv4;
     } else {
         hexstring(h, sizeof(h), &address->data, sizeof(AvahiIPv6Address));
-        snprintf(n, sizeof(n), "ip6-%s.%s", h, s->domain_name);
+        snprintf(n, sizeof(n), "ip6-%s.%s", h, domain);
         r = avahi_record_new_full(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA, AVAHI_DEFAULT_TTL_HOST_NAME);
         r->data.aaaa.address = address->data.ipv6;
     }
@@ -2116,13 +2192,13 @@ int avahi_server_add_dns_server_address(
     if (!r)
         return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
     
-    ret = avahi_server_add(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE | AVAHI_ENTRY_ALLOWMUTIPLE, r);
+    ret = avahi_server_add(s, g, interface, protocol, AVAHI_PUBLISH_UNIQUE | AVAHI_PUBLISH_ALLOW_MULTIPLE, r);
     avahi_record_unref(r);
 
     if (ret < 0)
         return ret;
     
-    return avahi_server_add_dns_server_name(s, g, interface, protocol, domain, type, n, port);
+    return avahi_server_add_dns_server_name(s, g, interface, protocol, flags, domain, type, n, port);
 }
 
 int avahi_server_add_dns_server_name(
@@ -2130,6 +2206,7 @@ int avahi_server_add_dns_server_name(
     AvahiSEntryGroup *g,
     AvahiIfIndex interface,
     AvahiProtocol protocol,
+    AvahiPublishFlags flags,
     const char *domain,
     AvahiDNSServerType type,
     const char *name,
@@ -2141,8 +2218,16 @@ int avahi_server_add_dns_server_name(
     
     assert(s);
     assert(name);
-    assert(type == AVAHI_DNS_SERVER_UPDATE || type == AVAHI_DNS_SERVER_RESOLVE);
 
+    if (!AVAHI_IF_VALID(interface))
+        return avahi_server_set_errno(s, AVAHI_ERR_INVALID_INTERFACE);
+
+    if (!AVAHI_PROTO_VALID(protocol))
+        return avahi_server_set_errno(s, AVAHI_ERR_INVALID_PROTOCOL);
+
+    if (!AVAHI_FLAGS_VALID(flags, 0) || (type != AVAHI_DNS_SERVER_UPDATE && type != AVAHI_DNS_SERVER_RESOLVE))
+        return avahi_server_set_errno(s, AVAHI_ERR_INVALID_FLAGS);
+    
     if (port == 0)
         return avahi_server_set_errno(s, AVAHI_ERR_INVALID_PORT);
 
@@ -2151,13 +2236,12 @@ int avahi_server_add_dns_server_name(
 
     if (domain && !avahi_is_valid_domain_name(domain))
         return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
-
     
     if (!domain)
         domain = s->domain_name;
 
-    if (!(n = avahi_normalize_name(name)) ||
-        !(d = avahi_normalize_name(domain))) {
+    if (!(n = avahi_normalize_name_strdup(name)) ||
+        !(d = avahi_normalize_name_strdup(domain))) {
         avahi_free(n);
         avahi_free(d);
         return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
@@ -2175,7 +2259,7 @@ int avahi_server_add_dns_server_name(
     r->data.srv.weight = 0;
     r->data.srv.port = port;
     r->data.srv.name = n;
-    ret = avahi_server_add(s, g, interface, protocol, AVAHI_ENTRY_NULL, r);
+    ret = avahi_server_add(s, g, interface, protocol, AVAHI_PUBLISH_NULL, r);
     avahi_record_unref(r);
 
     return ret;
@@ -2206,6 +2290,22 @@ void avahi_s_entry_group_change_state(AvahiSEntryGroup *g, AvahiEntryGroupState
 
     assert(state <= AVAHI_ENTRY_GROUP_COLLISION);
 
+    if (g->state == AVAHI_ENTRY_GROUP_ESTABLISHED) {
+
+        /* If the entry group was established for a time longer then
+         * 5s, reset the establishment trial counter */
+        
+        if (avahi_age(&g->established_at) > 5000000)
+            g->n_register_try = 0;
+    }
+    
+    if (state == AVAHI_ENTRY_GROUP_ESTABLISHED)
+
+        /* If the entry group is now established, remember the time
+         * this happened */
+        
+        gettimeofday(&g->established_at, NULL);
+    
     g->state = state;
     
     if (g->callback)
@@ -2328,26 +2428,23 @@ void avahi_s_entry_group_reset(AvahiSEntryGroup *g) {
     AvahiEntry *e;
     assert(g);
     
-    if (g->register_time_event) {
-        avahi_time_event_free(g->register_time_event);
-        g->register_time_event = NULL;
-    }
-    
     for (e = g->entries; e; e = e->by_group_next) {
         if (!e->dead) {
             avahi_goodbye_entry(g->server, e, 1);
             e->dead = 1;
         }
     }
+    g->server->need_entry_cleanup = 1;
 
     if (g->register_time_event) {
         avahi_time_event_free(g->register_time_event);
         g->register_time_event = NULL;
     }
     
-    g->server->need_entry_cleanup = 1;
     g->n_probing = 0;
 
+    gettimeofday(&g->register_time, NULL);
+
     avahi_s_entry_group_change_state(g, AVAHI_ENTRY_GROUP_UNCOMMITED);
 }
 
@@ -2446,6 +2543,7 @@ AvahiServerConfig* avahi_server_config_init(AvahiServerConfig *c) {
     c->add_service_cookie = 1;
     c->enable_wide_area = 0;
     c->n_wide_area_servers = 0;
+    c->disallow_other_stacks = 0;
     
     return c;
 }
@@ -2500,8 +2598,7 @@ uint32_t avahi_server_get_local_service_cookie(AvahiServer *s) {
 
 int avahi_server_is_service_local(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, const char *name, const char *type, const char*domain) {
     AvahiKey *key = NULL;
-    char *d, *t;
-    char ename[64], n[256];
+    char n[256];
     int ret;
     AvahiEntry *e;
     
@@ -2519,26 +2616,19 @@ int avahi_server_is_service_local(AvahiServer *s, AvahiIfIndex interface, AvahiP
     if (domain && !avahi_is_valid_domain_name(domain))
         return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
 
-    escape_service_name(ename, sizeof(ename), name);
-    
-    if (!(d = avahi_normalize_name(domain)) ||
-        !(t = avahi_normalize_name(type))) {
-        ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
-        goto fail;
-    }
-    snprintf(n, sizeof(n), "%s.%s.%s", ename, t, d);
-    
-    if (!(key = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV))) {
-        ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
-        goto fail;
-    }
+    if ((ret = avahi_service_name_join(n, sizeof(n), name, type, domain) < 0))
+        return avahi_server_set_errno(s, ret);
+        
+    if (!(key = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV)))
+        return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
 
     ret = 0;
     
     for (e = avahi_hashmap_lookup(s->entries_by_key, key); e; e = e->by_key_next) {
 
         if ((e->interface == interface || e->interface <= 0 || interface <= 0) &&
-            (e->protocol == protocol || e->protocol == AVAHI_PROTO_UNSPEC || protocol == AVAHI_PROTO_UNSPEC)) {
+            (e->protocol == protocol || e->protocol == AVAHI_PROTO_UNSPEC || protocol == AVAHI_PROTO_UNSPEC) &&
+            !(e->flags & AVAHI_PUBLISH_IS_PROXY)) {
             ret = 1;
             break;
         }
@@ -2546,21 +2636,6 @@ int avahi_server_is_service_local(AvahiServer *s, AvahiIfIndex interface, AvahiP
     
     avahi_key_unref(key);
     
-    avahi_free(d);
-    avahi_free(t);
-
-    return ret;
-    
-fail:
-    if (d)
-        avahi_free(d);
-
-    if (t)
-        avahi_free(t);
-
-    if (key)
-        avahi_key_unref(key);
-
     return ret;
 }