Example #1
0
void avahi_s_service_resolver_free(AvahiSServiceResolver *r) {
    assert(r);

    AVAHI_LLIST_REMOVE(AvahiSServiceResolver, resolver, r->server->service_resolvers, r);

    if (r->time_event)
        avahi_time_event_free(r->time_event);
    
    if (r->record_browser_srv)
        avahi_s_record_browser_free(r->record_browser_srv);
    if (r->record_browser_txt)
        avahi_s_record_browser_free(r->record_browser_txt);
    if (r->record_browser_a)
        avahi_s_record_browser_free(r->record_browser_a);
    if (r->record_browser_aaaa)
        avahi_s_record_browser_free(r->record_browser_aaaa);

    if (r->srv_record)
        avahi_record_unref(r->srv_record);
    if (r->txt_record)
        avahi_record_unref(r->txt_record);
    if (r->address_record)
        avahi_record_unref(r->address_record);
    
    avahi_free(r->service_name);
    avahi_free(r->service_type);
    avahi_free(r->domain_name);
    avahi_free(r);
}
int avahi_server_add_dns_server_address(
    AvahiServer *s,
    AvahiSEntryGroup *g,
    AvahiIfIndex interface,
    AvahiProtocol protocol,
    AvahiPublishFlags flags,
    const char *domain,
    AvahiDNSServerType type,
    const AvahiAddress *address,
    uint16_t port /** should be 53 */) {

    AvahiRecord *r;
    char n[64], h[64];
    AvahiEntry *a_entry, *s_entry;

    assert(s);
    assert(address);

    AVAHI_CHECK_VALIDITY(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE);
    AVAHI_CHECK_VALIDITY(s, AVAHI_PROTO_VALID(protocol) && AVAHI_PROTO_VALID(address->proto), AVAHI_ERR_INVALID_PROTOCOL);
    AVAHI_CHECK_VALIDITY(s, AVAHI_FLAGS_VALID(flags, AVAHI_PUBLISH_USE_MULTICAST|AVAHI_PUBLISH_USE_WIDE_AREA), AVAHI_ERR_INVALID_FLAGS);
    AVAHI_CHECK_VALIDITY(s, type == AVAHI_DNS_SERVER_UPDATE || type == AVAHI_DNS_SERVER_RESOLVE, AVAHI_ERR_INVALID_FLAGS);
    AVAHI_CHECK_VALIDITY(s, port != 0, AVAHI_ERR_INVALID_PORT);
    AVAHI_CHECK_VALIDITY(s, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME);

    if (!domain)
        domain = s->domain_name;

    transport_flags_from_domain(s, &flags, domain);
    AVAHI_CHECK_VALIDITY(s, flags & AVAHI_PUBLISH_USE_MULTICAST, AVAHI_ERR_NOT_SUPPORTED);
    
    if (address->proto == AVAHI_PROTO_INET) {
        hexstring(h, sizeof(h), &address->data, sizeof(AvahiIPv4Address));
        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, 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;
    }

    if (!r)
        return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
    
    a_entry = server_add_internal(s, g, interface, protocol, AVAHI_PUBLISH_UNIQUE | AVAHI_PUBLISH_ALLOW_MULTIPLE, r);
    avahi_record_unref(r);

    if (!a_entry)
        return avahi_server_errno(s);
    
    if (!(s_entry = server_add_dns_server_name(s, g, interface, protocol, flags, domain, type, n, port))) {
        if (!(flags & AVAHI_PUBLISH_UPDATE))
            avahi_entry_free(s, a_entry);
        return avahi_server_errno(s);
    }

    return AVAHI_OK;
}
void avahi_entry_free(AvahiServer*s, AvahiEntry *e) {
    AvahiEntry *t;

    assert(s);
    assert(e);

    avahi_goodbye_entry(s, e, 1, 1);

    /* Remove from linked list */
    AVAHI_LLIST_REMOVE(AvahiEntry, entries, s->entries, e);

    /* Remove from hash table indexed by name */
    t = avahi_hashmap_lookup(s->entries_by_key, e->record->key);
    AVAHI_LLIST_REMOVE(AvahiEntry, by_key, t, e);
    if (t)
        avahi_hashmap_replace(s->entries_by_key, t->record->key, t);
    else
        avahi_hashmap_remove(s->entries_by_key, e->record->key);

    /* Remove from associated group */
    if (e->group)
        AVAHI_LLIST_REMOVE(AvahiEntry, by_group, e->group->entries, e);

    avahi_record_unref(e->record);
    avahi_free(e);
}
static AvahiEntry *server_add_txt_strlst_nocopy(
    AvahiServer *s,
    AvahiSEntryGroup *g,
    AvahiIfIndex interface,
    AvahiProtocol protocol,
    AvahiPublishFlags flags,
    uint32_t ttl,
    const char *name,
    AvahiStringList *strlst) {

    AvahiRecord *r;
    AvahiEntry *e;
    
    assert(s);

    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);
        avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
        return NULL;
    }
    
    r->data.txt.string_list = strlst;
    e = server_add_internal(s, g, interface, protocol, flags, r);
    avahi_record_unref(r);

    return e;
}
static AvahiEntry *server_add_ptr_internal(
    AvahiServer *s,
    AvahiSEntryGroup *g,
    AvahiIfIndex interface,
    AvahiProtocol protocol,
    AvahiPublishFlags flags,
    uint32_t ttl,
    const char *name,
    const char *dest) {

    AvahiRecord *r;
    AvahiEntry *e;
    
    assert(s);
    assert(dest);

    AVAHI_CHECK_VALIDITY_RETURN_NULL(s, !name || avahi_is_valid_domain_name(name), AVAHI_ERR_INVALID_HOST_NAME);
    AVAHI_CHECK_VALIDITY_RETURN_NULL(s, avahi_is_valid_domain_name(dest), 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))) {
        avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
        return NULL;
    }
        
    r->data.ptr.name = avahi_normalize_name_strdup(dest);
    e = server_add_internal(s, g, interface, protocol, flags, r);
    avahi_record_unref(r);
    return e;
}
Example #6
0
static void send_goodbye_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, void* userdata) {
    AvahiEntry *e = userdata;
    AvahiRecord *g;

    assert(m);
    assert(i);
    assert(e);
    assert(!e->dead);

    if (!avahi_interface_match(i, e->interface, e->protocol))
        return;

    if (e->flags & AVAHI_PUBLISH_NO_ANNOUNCE)
        return;

    if (!avahi_entry_is_registered(m->server, e, i))
        return;

    if (is_duplicate_entry(m->server, e))
        return;

    if (!(g = make_goodbye_record(e->record)))
        return; /* OOM */

    avahi_interface_post_response(i, g, e->flags & AVAHI_PUBLISH_UNIQUE, NULL, 1);
    avahi_record_unref(g);
}
Example #7
0
static void remove_entry(AvahiCache *c, AvahiCacheEntry *e) {
    AvahiCacheEntry *t;

    assert(c);
    assert(e);

/*     avahi_log_debug("removing from cache: %p %p", c, e); */

    /* Remove from hash table */
    t = avahi_hashmap_lookup(c->hashmap, e->record->key);
    AVAHI_LLIST_REMOVE(AvahiCacheEntry, by_key, t, e);
    if (t)
        avahi_hashmap_replace(c->hashmap, t->record->key, t);
    else
        avahi_hashmap_remove(c->hashmap, e->record->key);

    /* Remove from linked list */
    AVAHI_LLIST_REMOVE(AvahiCacheEntry, entry, c->entries, e);

    if (e->time_event)
        avahi_time_event_free(e->time_event);

    avahi_multicast_lookup_engine_notify(c->server->mdns.multicast_lookup_engine, c->interface, e->record, AVAHI_BROWSER_REMOVE);

    avahi_record_unref(e->record);

    avahi_free(e);

    assert(c->n_entries-- >= 1);
}
static AvahiEntry *server_add_dns_server_name(
    AvahiServer *s,
    AvahiSEntryGroup *g,
    AvahiIfIndex interface,
    AvahiProtocol protocol,
    AvahiPublishFlags flags,
    const char *domain,
    AvahiDNSServerType type,
    const char *name,
    uint16_t port /** should be 53 */) {

    AvahiEntry *e;
    char t[AVAHI_DOMAIN_NAME_MAX], normalized_d[AVAHI_DOMAIN_NAME_MAX], *n;
    
    AvahiRecord *r;
    
    assert(s);
    assert(name);

    AVAHI_CHECK_VALIDITY_RETURN_NULL(s, AVAHI_FLAGS_VALID(flags, AVAHI_PUBLISH_USE_WIDE_AREA|AVAHI_PUBLISH_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS);
    AVAHI_CHECK_VALIDITY_RETURN_NULL(s, type == AVAHI_DNS_SERVER_UPDATE || type == AVAHI_DNS_SERVER_RESOLVE, AVAHI_ERR_INVALID_FLAGS);
    AVAHI_CHECK_VALIDITY_RETURN_NULL(s, port != 0, AVAHI_ERR_INVALID_PORT);
    AVAHI_CHECK_VALIDITY_RETURN_NULL(s, avahi_is_valid_fqdn(name), AVAHI_ERR_INVALID_HOST_NAME);
    AVAHI_CHECK_VALIDITY_RETURN_NULL(s, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME);
    
    if (!domain)
        domain = s->domain_name;

    transport_flags_from_domain(s, &flags, domain);
    AVAHI_CHECK_VALIDITY_RETURN_NULL(s, flags & AVAHI_PUBLISH_USE_MULTICAST, AVAHI_ERR_NOT_SUPPORTED);

    if (!(n = avahi_normalize_name_strdup(name))) {
        avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
        return NULL;
    }
    
    AVAHI_ASSERT_TRUE(avahi_normalize_name(domain, normalized_d, sizeof(normalized_d)));

    snprintf(t, sizeof(t), "%s.%s", type == AVAHI_DNS_SERVER_RESOLVE ? "_domain._udp" : "_dns-update._udp", normalized_d);
    
    if (!(r = avahi_record_new_full(t, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV, AVAHI_DEFAULT_TTL_HOST_NAME))) {
        avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
        avahi_free(n);
        return NULL;
    }
    
    r->data.srv.priority = 0;
    r->data.srv.weight = 0;
    r->data.srv.port = port;
    r->data.srv.name = n;
    e = server_add_internal(s, g, interface, protocol, 0, r);
    avahi_record_unref(r);

    return e;
}
Example #9
0
static void add_to_cache(AvahiWideAreaLookupEngine *e, AvahiRecord *r) {
    AvahiWideAreaCacheEntry *c;
    int is_new;
    
    assert(e);
    assert(r);

    if ((c = find_record_in_cache(e, r))) {
        is_new = 0;

        /* Update the existing entry */
        avahi_record_unref(c->record);
    } else {
        AvahiWideAreaCacheEntry *t;

        is_new = 1;

        /* Enforce cache size */
        if (e->cache_n_entries >= CACHE_ENTRIES_MAX)
            /* Eventually we should improve the caching algorithm here */
            goto finish;
        
        c = avahi_new(AvahiWideAreaCacheEntry, 1);
        c->engine = e;
        c->time_event = NULL;

        AVAHI_LLIST_PREPEND(AvahiWideAreaCacheEntry, cache, e->cache, c);

        /* Add the new entry to the cache entry hash table */
        t = avahi_hashmap_lookup(e->cache_by_key, r->key);
        AVAHI_LLIST_PREPEND(AvahiWideAreaCacheEntry, by_key, t, c);
        avahi_hashmap_replace(e->cache_by_key, avahi_key_ref(r->key), t);

        e->cache_n_entries ++;
    }

    c->record = avahi_record_ref(r);
    
    gettimeofday(&c->timestamp, NULL);
    c->expiry = c->timestamp;
    avahi_timeval_add(&c->expiry, r->ttl * 1000000);

    if (c->time_event)
        avahi_time_event_update(c->time_event, &c->expiry);
    else
        c->time_event = avahi_time_event_new(e->server->time_event_queue, &c->expiry, expiry_event, c);

finish:
    
    if (is_new)
        run_callbacks(e, r);
}
static void job_free(AvahiProbeScheduler *s, AvahiProbeJob *pj) {
    assert(pj);

    if (pj->time_event)
        avahi_time_event_free(pj->time_event);

    if (pj->done)
        AVAHI_LLIST_REMOVE(AvahiProbeJob, jobs, s->history, pj);
    else
        AVAHI_LLIST_REMOVE(AvahiProbeJob, jobs, s->jobs, pj);

    avahi_record_unref(pj->record);
    avahi_free(pj);
}
Example #11
0
static void job_free(AvahiResponseScheduler *s, AvahiResponseJob *rj) {
    assert(s);
    assert(rj);

    if (rj->time_event)
        avahi_time_event_free(rj->time_event);

    if (rj->state == AVAHI_SCHEDULED)
        AVAHI_LLIST_REMOVE(AvahiResponseJob, jobs, s->jobs, rj);
    else if (rj->state == AVAHI_DONE)
        AVAHI_LLIST_REMOVE(AvahiResponseJob, jobs, s->history, rj);
    else /* rj->state == AVAHI_SUPPRESSED */
        AVAHI_LLIST_REMOVE(AvahiResponseJob, jobs, s->suppressed, rj);

    avahi_record_unref(rj->record);
    avahi_free(rj);
}
Example #12
0
static void append_known_answers_and_send(AvahiQueryScheduler *s, AvahiDnsPacket *p) {
    AvahiKnownAnswer *ka;
    unsigned n;
    assert(s);
    assert(p);

    n = 0;

    while ((ka = s->known_answers)) {
        int too_large = 0;

        while (!avahi_dns_packet_append_record(p, ka->record, 0, 0)) {

            if (avahi_dns_packet_is_empty(p)) {
                /* The record is too large to fit into one packet, so
                   there's no point in sending it. Better is letting
                   the owner of the record send it as a response. This
                   has the advantage of a cache refresh. */

                too_large = 1;
                break;
            }

            avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_FLAGS, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) | AVAHI_DNS_FLAG_TC);
            avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ANCOUNT, n);
            avahi_interface_send_packet(s->interface, p, AVAHI_MDNS);
            avahi_dns_packet_free(p);

            p = avahi_dns_packet_new_query(s->interface->hardware->mtu);
            n = 0;
        }

        AVAHI_LLIST_REMOVE(AvahiKnownAnswer, known_answer, s->known_answers, ka);
        avahi_record_unref(ka->record);
        avahi_free(ka);

        if (!too_large)
            n++;
    }

    avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ANCOUNT, n);
    avahi_interface_send_packet(s->interface, p, AVAHI_MDNS);
    avahi_dns_packet_free(p);
}
void avahi_s_address_resolver_free(AvahiSAddressResolver *r) {
    assert(r);

    AVAHI_LLIST_REMOVE(AvahiSAddressResolver, resolver, r->server->address_resolvers, r);

    if (r->record_browser)
        avahi_s_record_browser_free(r->record_browser);

    if (r->time_event)
        avahi_time_event_free(r->time_event);

    if (r->ptr_record)
        avahi_record_unref(r->ptr_record);

    if (r->key)
        avahi_key_unref(r->key);

    avahi_free(r);
}
Example #14
0
static void cache_entry_free(AvahiWideAreaCacheEntry *c) {
    AvahiWideAreaCacheEntry *t;
    assert(c);

    if (c->time_event)
        avahi_time_event_free(c->time_event);

    AVAHI_LLIST_REMOVE(AvahiWideAreaCacheEntry, cache, c->engine->cache, c);

    t = avahi_hashmap_lookup(c->engine->cache_by_key, c->record->key);
    AVAHI_LLIST_REMOVE(AvahiWideAreaCacheEntry, by_key, t, c);
    if (t)
        avahi_hashmap_replace(c->engine->cache_by_key, avahi_key_ref(c->record->key), t);
    else
        avahi_hashmap_remove(c->engine->cache_by_key, c->record->key);

    c->engine->cache_n_entries --;

    avahi_record_unref(c->record);
    avahi_free(c);
}
Example #15
0
void avahi_response_scheduler_suppress(AvahiResponseScheduler *s, AvahiRecord *record, const AvahiAddress *querier) {
    AvahiResponseJob *rj;
    
    assert(s);
    assert(record);
    assert(querier);

    if ((rj = find_scheduled_job(s, record))) {
        
        if (rj->querier_valid && avahi_address_cmp(querier, &rj->querier) == 0 && /* same originator */
            avahi_record_is_goodbye(record) == avahi_record_is_goodbye(rj->record) && /* both goodbye packets, or both not */
            record->ttl >= rj->record->ttl/2) {                                  /* sensible TTL */

            /* A matching entry was found, so let's drop it */
/*             avahi_log_debug("Known answer suppression active!"); */
            job_free(s, rj);
        }
    }

    if ((rj = find_suppressed_job(s, record, querier))) {

        /* Let's update the old entry */
        avahi_record_unref(rj->record);
        rj->record = avahi_record_ref(record);
        
    } else {

        /* Create a new entry */
        if (!(rj = job_new(s, record, AVAHI_SUPPRESSED)))
            return; /* OOM */
        rj->querier_valid = 1;
        rj->querier = *querier;
    }

    gettimeofday(&rj->delivery, NULL);
    job_set_elapse_time(s, rj, AVAHI_RESPONSE_SUPPRESS_MSEC, 0);
}
Example #16
0
void avahi_response_scheduler_incoming(AvahiResponseScheduler *s, AvahiRecord *record, int flush_cache) {
    AvahiResponseJob *rj;
    assert(s);

    /* This function is called whenever an incoming response was
     * receieved. We drop scheduled responses which match here. The
     * keyword is "DUPLICATE ANSWER SUPPRESION". */
    
    if ((rj = find_scheduled_job(s, record))) {

        if ((!rj->flush_cache || flush_cache) &&    /* flush cache bit was set correctly */
            avahi_record_is_goodbye(record) == avahi_record_is_goodbye(rj->record) &&   /* both goodbye packets, or both not */
            record->ttl >= rj->record->ttl/2) {     /* sensible TTL */

            /* A matching entry was found, so let's mark it done */
/*             avahi_log_debug("Response suppressed by distributed duplicate suppression"); */
            job_mark_done(s, rj);
        }

        return;
    }

    if ((rj = find_history_job(s, record))) {
        /* Found a history job, let's update it */
        avahi_record_unref(rj->record);
        rj->record = avahi_record_ref(record);
    } else
        /* Found no existing history job, so let's create a new one */
        if (!(rj = job_new(s, record, AVAHI_DONE)))
            return; /* OOM */

    rj->flush_cache = flush_cache;
    rj->querier_valid = 0;
    
    gettimeofday(&rj->delivery, NULL);
    job_set_elapse_time(s, rj, AVAHI_RESPONSE_HISTORY_MSEC, 0);
}
Example #17
0
static void handle_packet(AvahiWideAreaLookupEngine *e, AvahiDnsPacket *p) {
    AvahiWideAreaLookup *l = NULL;
    int i, r;

    AvahiBrowserEvent final_event = AVAHI_BROWSER_ALL_FOR_NOW;
    
    assert(e);
    assert(p);

    /* Some superficial validity tests */
    if (avahi_dns_packet_check_valid(p) < 0 || avahi_dns_packet_is_query(p)) {
        avahi_log_warn(__FILE__": Ignoring invalid response for wide area datagram.");
        goto finish;
    }

    /* Look for the lookup that issued this query */
    if (!(l = find_lookup(e, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID))) || l->dead)
        goto finish;

    /* Check whether this a packet indicating a failure */
    if ((r = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & 15) != 0 ||
        avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0) {

        avahi_server_set_errno(e->server, r == 0 ? AVAHI_ERR_NOT_FOUND : map_dns_error(r));
        /* Tell the user about the failure */
        final_event = AVAHI_BROWSER_FAILURE;

        /* We go on here, since some of the records contained in the
           reply might be interesting in some way */
    }

    /* Skip over the question */
    for (i = (int) avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT); i > 0; i--) {
        AvahiKey *k;
        
        if (!(k = avahi_dns_packet_consume_key(p, NULL))) {
            avahi_log_warn(__FILE__": Wide area response packet too short or invalid while reading question key. (Maybe a UTF-8 problem?)");
            avahi_server_set_errno(e->server, AVAHI_ERR_INVALID_PACKET);
            final_event = AVAHI_BROWSER_FAILURE;
            goto finish;
        }

        avahi_key_unref(k);
    }

    /* Process responses */
    for (i = (int) avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) +
             (int) avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) +
             (int) avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT); i > 0; i--) {

        AvahiRecord *rr;

        if (!(rr = avahi_dns_packet_consume_record(p, NULL))) {
            avahi_log_warn(__FILE__": Wide area response packet too short or invalid while reading response record. (Maybe a UTF-8 problem?)");
            avahi_server_set_errno(e->server, AVAHI_ERR_INVALID_PACKET);
            final_event = AVAHI_BROWSER_FAILURE;
            goto finish;
        }

        add_to_cache(e, rr);
        avahi_record_unref(rr);
    }

finish:
    
    if (l && !l->dead) {
        if (l->callback)
            l->callback(e, final_event, AVAHI_LOOKUP_RESULT_WIDE_AREA, NULL, l->userdata);

        lookup_stop(l);
    }
}
Example #18
0
int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char *argv[]) {
    char t[AVAHI_DOMAIN_NAME_MAX], *m;
    const char *a, *b, *c, *d;
    AvahiDnsPacket *p;
    AvahiRecord *r, *r2;
    uint8_t rdata[AVAHI_DNS_RDATA_MAX];
    size_t l;

    p = avahi_dns_packet_new(0);

    assert(avahi_dns_packet_append_name(p, a = "Ahello.hello.hello.de."));
    assert(avahi_dns_packet_append_name(p, b = "Bthis is a test.hello.de."));
    assert(avahi_dns_packet_append_name(p, c = "Cthis\\.is\\.a\\.test\\.with\\.dots.hello.de."));
    assert(avahi_dns_packet_append_name(p, d = "Dthis\\\\is another test.hello.de."));

    avahi_hexdump(AVAHI_DNS_PACKET_DATA(p), p->size);

    assert(avahi_dns_packet_consume_name(p, t, sizeof(t)) == 0);
    avahi_log_debug(">%s<", t);
    assert(avahi_domain_equal(a, t));
    
    assert(avahi_dns_packet_consume_name(p, t, sizeof(t)) == 0);
    avahi_log_debug(">%s<", t);
    assert(avahi_domain_equal(b, t));

    assert(avahi_dns_packet_consume_name(p, t, sizeof(t)) == 0);
    avahi_log_debug(">%s<", t);
    assert(avahi_domain_equal(c, t));

    assert(avahi_dns_packet_consume_name(p, t, sizeof(t)) == 0);
    avahi_log_debug(">%s<", t);
    assert(avahi_domain_equal(d, t));
    
    avahi_dns_packet_free(p);

    /* RDATA PARSING AND SERIALIZATION */
    
    /* Create an AvahiRecord with some usful data */
    r = avahi_record_new_full("foobar.local", AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_HINFO, AVAHI_DEFAULT_TTL);
    assert(r);
    r->data.hinfo.cpu = avahi_strdup("FOO");
    r->data.hinfo.os = avahi_strdup("BAR");

    /* Serialize it into a blob */
    assert((l = avahi_rdata_serialize(r, rdata, sizeof(rdata))) != (size_t) -1);

    /* Print it */
    avahi_hexdump(rdata, l);

    /* Create a new record and fill in the data from the blob */
    r2 = avahi_record_new(r->key, AVAHI_DEFAULT_TTL);
    assert(r2);
    assert(avahi_rdata_parse(r2, rdata, l) >= 0);

    /* Compare both versions */
    assert(avahi_record_equal_no_ttl(r, r2));

    /* Free the records */
    avahi_record_unref(r);
    avahi_record_unref(r2);

    r = avahi_record_new_full("foobar", 77, 77, AVAHI_DEFAULT_TTL);
    assert(r);

    assert(r->data.generic.data = avahi_memdup("HALLO", r->data.generic.size = 5));

    m = avahi_record_to_string(r);
    assert(m);

    avahi_log_debug(">%s<", m);

    avahi_free(m);
    avahi_record_unref(r);
    
    return 0;
}
static AvahiEntry * server_add_internal(
    AvahiServer *s,
    AvahiSEntryGroup *g,
    AvahiIfIndex interface,
    AvahiProtocol protocol,
    AvahiPublishFlags flags,
    AvahiRecord *r) {

    AvahiEntry *e;
    
    assert(s);
    assert(r);

    AVAHI_CHECK_VALIDITY_RETURN_NULL(s, s->state != AVAHI_SERVER_FAILURE && s->state != AVAHI_SERVER_INVALID, AVAHI_ERR_BAD_STATE);
    AVAHI_CHECK_VALIDITY_RETURN_NULL(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE);
    AVAHI_CHECK_VALIDITY_RETURN_NULL(s, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL);
    AVAHI_CHECK_VALIDITY_RETURN_NULL(s, AVAHI_FLAGS_VALID(
                                         flags,
                                         AVAHI_PUBLISH_NO_ANNOUNCE|
                                         AVAHI_PUBLISH_NO_PROBE|
                                         AVAHI_PUBLISH_UNIQUE|
                                         AVAHI_PUBLISH_ALLOW_MULTIPLE|
                                         AVAHI_PUBLISH_UPDATE|
                                         AVAHI_PUBLISH_USE_WIDE_AREA|
                                         AVAHI_PUBLISH_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS);
    AVAHI_CHECK_VALIDITY_RETURN_NULL(s, avahi_is_valid_domain_name(r->key->name), AVAHI_ERR_INVALID_HOST_NAME);
    AVAHI_CHECK_VALIDITY_RETURN_NULL(s, r->ttl != 0, AVAHI_ERR_INVALID_TTL);
    AVAHI_CHECK_VALIDITY_RETURN_NULL(s, !avahi_key_is_pattern(r->key), AVAHI_ERR_IS_PATTERN);
    AVAHI_CHECK_VALIDITY_RETURN_NULL(s, avahi_record_is_valid(r), AVAHI_ERR_INVALID_RECORD);
    AVAHI_CHECK_VALIDITY_RETURN_NULL(s, r->key->clazz == AVAHI_DNS_CLASS_IN, AVAHI_ERR_INVALID_DNS_CLASS);
    AVAHI_CHECK_VALIDITY_RETURN_NULL(s,
                                     (r->key->type != 0) &&
                                     (r->key->type != AVAHI_DNS_TYPE_ANY) &&
                                     (r->key->type != AVAHI_DNS_TYPE_OPT) &&
                                     (r->key->type != AVAHI_DNS_TYPE_TKEY) &&
                                     (r->key->type != AVAHI_DNS_TYPE_TSIG) &&
                                     (r->key->type != AVAHI_DNS_TYPE_IXFR) &&
                                     (r->key->type != AVAHI_DNS_TYPE_AXFR), AVAHI_ERR_INVALID_DNS_TYPE);

    transport_flags_from_domain(s, &flags, r->key->name);
    AVAHI_CHECK_VALIDITY_RETURN_NULL(s, flags & AVAHI_PUBLISH_USE_MULTICAST, AVAHI_ERR_NOT_SUPPORTED);
    AVAHI_CHECK_VALIDITY_RETURN_NULL(s, !s->config.disable_publishing, AVAHI_ERR_NOT_PERMITTED);
    AVAHI_CHECK_VALIDITY_RETURN_NULL(s,
                                     !g ||
                                     (g->state != AVAHI_ENTRY_GROUP_ESTABLISHED && g->state != AVAHI_ENTRY_GROUP_REGISTERING) ||
                                     (flags & AVAHI_PUBLISH_UPDATE), AVAHI_ERR_BAD_STATE);
    
    if (flags & AVAHI_PUBLISH_UPDATE) {
        AvahiRecord *old_record;
        int is_first = 1;
        
        /* Update and existing record */

        /* Find the first matching entry */
        for (e = avahi_hashmap_lookup(s->entries_by_key, r->key); e; e = e->by_key_next) {
            if (!e->dead && e->group == g && e->interface == interface && e->protocol == protocol)
                break;

            is_first = 0;
        }

        /* Hmm, nothing found? */
        if (!e) {
            avahi_server_set_errno(s, AVAHI_ERR_NOT_FOUND);
            return NULL;
        }

        /* Update the entry */
        old_record = e->record;
        e->record = avahi_record_ref(r);
        e->flags = flags;

        /* Announce our changes when needed */
        if (!avahi_record_equal_no_ttl(old_record, r) && (!g || g->state != AVAHI_ENTRY_GROUP_UNCOMMITED)) {

            /* Remove the old entry from all caches, if needed */
            if (!(e->flags & AVAHI_PUBLISH_UNIQUE))
                avahi_goodbye_entry(s, e, 1, 0);

            /* Reannounce our updated entry */
            avahi_reannounce_entry(s, e);
        }

        /* If we were the first entry in the list, we need to update the key */
        if (is_first)
            avahi_hashmap_replace(s->entries_by_key, e->record->key, e);
        
        avahi_record_unref(old_record);

    } else {
        AvahiEntry *t;

        /* Add a new record */
    
        if (check_record_conflict(s, interface, protocol, r, flags) < 0) {
            avahi_server_set_errno(s, AVAHI_ERR_COLLISION);
            return NULL;
        }

        if (!(e = avahi_new(AvahiEntry, 1))) {
            avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
            return NULL;
        }
        
        e->server = s;
        e->record = avahi_record_ref(r);
        e->group = g;
        e->interface = interface;
        e->protocol = protocol;
        e->flags = flags;
        e->dead = 0;
        
        AVAHI_LLIST_HEAD_INIT(AvahiAnnouncer, e->announcers);
        
        AVAHI_LLIST_PREPEND(AvahiEntry, entries, s->entries, e);
        
        /* Insert into hash table indexed by name */
        t = avahi_hashmap_lookup(s->entries_by_key, e->record->key);
        AVAHI_LLIST_PREPEND(AvahiEntry, by_key, t, e);
        avahi_hashmap_replace(s->entries_by_key, e->record->key, t);
        
        /* Insert into group list */
        if (g)
            AVAHI_LLIST_PREPEND(AvahiEntry, by_group, g->entries, e); 
        
        avahi_announce_entry(s, e);
    }

    return e;
}
int avahi_server_add_address(
    AvahiServer *s,
    AvahiSEntryGroup *g,
    AvahiIfIndex interface,
    AvahiProtocol protocol,
    AvahiPublishFlags flags,
    const char *name,
    AvahiAddress *a) {

    char n[AVAHI_DOMAIN_NAME_MAX];
    int ret = AVAHI_OK;
    AvahiEntry *entry = NULL, *reverse = NULL;
    AvahiRecord  *r;
    
    assert(s);
    assert(a);

    AVAHI_CHECK_VALIDITY(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE);
    AVAHI_CHECK_VALIDITY(s, AVAHI_PROTO_VALID(protocol) && AVAHI_PROTO_VALID(a->proto), AVAHI_ERR_INVALID_PROTOCOL);
    AVAHI_CHECK_VALIDITY(s, AVAHI_FLAGS_VALID(flags,
                                              AVAHI_PUBLISH_NO_REVERSE|
                                              AVAHI_PUBLISH_NO_ANNOUNCE|
                                              AVAHI_PUBLISH_NO_PROBE|
                                              AVAHI_PUBLISH_UPDATE|
                                              AVAHI_PUBLISH_USE_WIDE_AREA|
                                              AVAHI_PUBLISH_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS);
    AVAHI_CHECK_VALIDITY(s, !name || avahi_is_valid_fqdn(name), AVAHI_ERR_INVALID_HOST_NAME);

    /* Prepare the host naem */
    
    if (!name)
        name = s->host_name_fqdn;
    else {
        AVAHI_ASSERT_TRUE(avahi_normalize_name(name, n, sizeof(n)));
        name = n;
    }

    transport_flags_from_domain(s, &flags, name);
    AVAHI_CHECK_VALIDITY(s, flags & AVAHI_PUBLISH_USE_MULTICAST, AVAHI_ERR_NOT_SUPPORTED);
    
    /* Create the A/AAAA record */
    
    if (a->proto == AVAHI_PROTO_INET) {

        if (!(r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A, AVAHI_DEFAULT_TTL_HOST_NAME))) {
            ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
            goto finish;
        }
        
        r->data.a.address = a->data.ipv4;
        
    } else {
        assert(a->proto == AVAHI_PROTO_INET6);
            
        if (!(r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA, AVAHI_DEFAULT_TTL_HOST_NAME))) {
            ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
            goto finish;
        }
        
        r->data.aaaa.address = a->data.ipv6;
    }
        
    entry = server_add_internal(s, g, interface, protocol, (flags & ~ AVAHI_PUBLISH_NO_REVERSE) | AVAHI_PUBLISH_UNIQUE | AVAHI_PUBLISH_ALLOW_MULTIPLE, r);
    avahi_record_unref(r);

    if (!entry) {
        ret = avahi_server_errno(s);
        goto finish;
    }

    /* Create the reverse lookup entry */
    
    if (!(flags & AVAHI_PUBLISH_NO_REVERSE)) {
        char reverse_n[AVAHI_DOMAIN_NAME_MAX];
        avahi_reverse_lookup_name(a, reverse_n, sizeof(reverse_n));
            
        if (!(reverse = server_add_ptr_internal(s, g, interface, protocol, flags | AVAHI_PUBLISH_UNIQUE, AVAHI_DEFAULT_TTL_HOST_NAME, reverse_n, name))) {
            ret = avahi_server_errno(s);
            goto finish;
        }
    }
    
finish:

    if (ret != AVAHI_OK && !(flags & AVAHI_PUBLISH_UPDATE)) {
        if (entry)
            avahi_entry_free(s, entry);
        if (reverse)
            avahi_entry_free(s, reverse);
    }

    return ret;
}
Example #21
0
static void record_browser_callback(
    AvahiSRecordBrowser*rr,
    AvahiIfIndex interface,
    AvahiProtocol protocol,
    AvahiBrowserEvent event,
    AvahiRecord *record,
    AvahiLookupResultFlags flags,
    void* userdata) {
    
    AvahiSServiceResolver *r = userdata;

    assert(rr);
    assert(r);

    if (rr == r->record_browser_aaaa || rr == r->record_browser_a)
        r->address_flags = flags;
    else if (rr == r->record_browser_srv)
        r->srv_flags = flags;
    else if (rr == r->record_browser_txt)
        r->txt_flags = flags;

    switch (event) {

        case AVAHI_BROWSER_NEW: {
            int changed = 0;
            assert(record);
            
            if (r->interface > 0 && interface > 0 &&  interface != r->interface)
                return;
            
            if (r->protocol != AVAHI_PROTO_UNSPEC && protocol != AVAHI_PROTO_UNSPEC && protocol != r->protocol)
                return;
            
            if (r->interface <= 0)
                r->interface = interface;
            
            if (r->protocol == AVAHI_PROTO_UNSPEC)
                r->protocol = protocol;
            
            switch (record->key->type) {
                case AVAHI_DNS_TYPE_SRV:
                    if (!r->srv_record) {
                        r->srv_record = avahi_record_ref(record);
                        changed = 1;
                        
                        if (r->record_browser_a) {
                            avahi_s_record_browser_free(r->record_browser_a);
                            r->record_browser_a = NULL;
                        }
                        
                        if (r->record_browser_aaaa) {
                            avahi_s_record_browser_free(r->record_browser_aaaa);
                            r->record_browser_aaaa = NULL;
                        }

                        if (!(r->user_flags & AVAHI_LOOKUP_NO_ADDRESS)) {

                            if (r->address_protocol == AVAHI_PROTO_INET || r->address_protocol == AVAHI_PROTO_UNSPEC) {
                                AvahiKey *k = avahi_key_new(r->srv_record->data.srv.name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A);
                                r->record_browser_a = avahi_s_record_browser_new(r->server, r->interface, r->protocol, k, r->user_flags & ~(AVAHI_LOOKUP_NO_TXT|AVAHI_LOOKUP_NO_ADDRESS), record_browser_callback, r);
                                avahi_key_unref(k);
                            } 
                        
                            if (r->address_protocol == AVAHI_PROTO_INET6 || r->address_protocol == AVAHI_PROTO_UNSPEC) {
                                AvahiKey *k = avahi_key_new(r->srv_record->data.srv.name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA);
                                r->record_browser_aaaa = avahi_s_record_browser_new(r->server, r->interface, r->protocol, k, r->user_flags & ~(AVAHI_LOOKUP_NO_TXT|AVAHI_LOOKUP_NO_ADDRESS), record_browser_callback, r);
                                avahi_key_unref(k);
                            }
                        }
                    }
                    break;
                
                case AVAHI_DNS_TYPE_TXT:

                    assert(!(r->user_flags & AVAHI_LOOKUP_NO_TXT));

                    if (!r->txt_record) {
                        r->txt_record = avahi_record_ref(record);
                        changed = 1;
                    }
                    break;
                
                case AVAHI_DNS_TYPE_A:
                case AVAHI_DNS_TYPE_AAAA:

                    assert(!(r->user_flags & AVAHI_LOOKUP_NO_ADDRESS));

                    if (!r->address_record) {
                        r->address_record = avahi_record_ref(record);
                        changed = 1;
                    }
                    break;
                
                default:
                    abort();
            }


            if (changed &&
                r->srv_record &&
                (r->txt_record || (r->user_flags & AVAHI_LOOKUP_NO_TXT)) &&
                (r->address_record || (r->user_flags & AVAHI_LOOKUP_NO_ADDRESS)))
                finish(r, AVAHI_RESOLVER_FOUND);

            break;

        }
            
        case AVAHI_BROWSER_REMOVE:

            assert(record);

            switch (record->key->type) {
                case AVAHI_DNS_TYPE_SRV:
                    
                    if (r->srv_record && avahi_record_equal_no_ttl(record, r->srv_record)) {
                        avahi_record_unref(r->srv_record);
                        r->srv_record = NULL;

                        if (r->record_browser_a) {
                            avahi_s_record_browser_free(r->record_browser_a);
                            r->record_browser_a = NULL;
                        }
                        
                        if (r->record_browser_aaaa) {
                            avahi_s_record_browser_free(r->record_browser_aaaa);
                            r->record_browser_aaaa = NULL;
                        }
                        
                        /** Look for a replacement */
                        avahi_s_record_browser_restart(r->record_browser_srv);
                        start_timeout(r);
                    }
                    
                    break;
                    
                case AVAHI_DNS_TYPE_TXT:
                    
                    assert(!(r->user_flags & AVAHI_LOOKUP_NO_TXT));
                    
                    if (r->txt_record && avahi_record_equal_no_ttl(record, r->txt_record)) {
                        avahi_record_unref(r->txt_record);
                        r->txt_record = NULL;
                        
                        /** Look for a replacement */
                        avahi_s_record_browser_restart(r->record_browser_txt);
                        start_timeout(r);
                    }
                    break;
                    
                case AVAHI_DNS_TYPE_A:
                case AVAHI_DNS_TYPE_AAAA:
                    
                    assert(!(r->user_flags & AVAHI_LOOKUP_NO_ADDRESS));
                    
                    if (r->address_record && avahi_record_equal_no_ttl(record, r->address_record)) {
                        avahi_record_unref(r->address_record);
                        r->address_record = NULL;
                        
                        /** Look for a replacement */
                        if (r->record_browser_aaaa)
                            avahi_s_record_browser_restart(r->record_browser_aaaa);
                        if (r->record_browser_a)
                            avahi_s_record_browser_restart(r->record_browser_a);
                        start_timeout(r);
                    }
                    break;
                    
                default:
                    abort();
            }

            break;

        case AVAHI_BROWSER_CACHE_EXHAUSTED:
        case AVAHI_BROWSER_ALL_FOR_NOW:
            break;

        case AVAHI_BROWSER_FAILURE:
            
            if (rr == r->record_browser_a && r->record_browser_aaaa) {
                /* We were looking for both AAAA and A, and the other query is still living, so we'll not die */
                avahi_s_record_browser_free(r->record_browser_a);
                r->record_browser_a = NULL;
                break;
            }

            if (rr == r->record_browser_aaaa && r->record_browser_a) {
                /* We were looking for both AAAA and A, and the other query is still living, so we'll not die */
                avahi_s_record_browser_free(r->record_browser_aaaa);
                r->record_browser_aaaa = NULL;
                break;
            }

            /* Hmm, everything's lost, tell the user */
            
            if (r->record_browser_srv)
                avahi_s_record_browser_free(r->record_browser_srv);
            if (r->record_browser_txt)
                avahi_s_record_browser_free(r->record_browser_txt);
            if (r->record_browser_a)
                avahi_s_record_browser_free(r->record_browser_a);
            if (r->record_browser_aaaa)
                avahi_s_record_browser_free(r->record_browser_aaaa);

            r->record_browser_srv = r->record_browser_txt = r->record_browser_a = r->record_browser_aaaa = NULL;

            finish(r, AVAHI_RESOLVER_FAILURE);
            break;
    }
}
Example #22
0
int avahi_response_scheduler_post(AvahiResponseScheduler *s, AvahiRecord *record, int flush_cache, const AvahiAddress *querier, int immediately) {
    AvahiResponseJob *rj;
    struct timeval tv;
/*     char *t; */
    
    assert(s);
    assert(record);

    assert(!avahi_key_is_pattern(record->key));

/*     t = avahi_record_to_string(record); */
/*     avahi_log_debug("post %i %s", immediately, t); */
/*     avahi_free(t); */

    /* Check whether this response is suppressed */
    if (querier &&
        (rj = find_suppressed_job(s, record, querier)) &&
        avahi_record_is_goodbye(record) == avahi_record_is_goodbye(rj->record) &&
        rj->record->ttl >= record->ttl/2) {

/*         avahi_log_debug("Response suppressed by known answer suppression.");  */
        return 0;
    }

    /* Check if we already sent this response recently */
    if ((rj = find_history_job(s, record))) {

        if (avahi_record_is_goodbye(record) == avahi_record_is_goodbye(rj->record) &&
            rj->record->ttl >= record->ttl/2 &&
            (rj->flush_cache || !flush_cache)) {
/*             avahi_log_debug("Response suppressed by local duplicate suppression (history)");  */
            return 0;
        }

        /* Outdated ... */
        job_free(s, rj);
    }

    avahi_elapse_time(&tv, immediately ? 0 : AVAHI_RESPONSE_DEFER_MSEC, immediately ? 0 : AVAHI_RESPONSE_JITTER_MSEC);
         
    if ((rj = find_scheduled_job(s, record))) {
/*          avahi_log_debug("Response suppressed by local duplicate suppression (scheduled)"); */

        /* Update a little ... */

        /* Update the time if the new is prior to the old */
        if (avahi_timeval_compare(&tv, &rj->delivery) < 0) {
            rj->delivery = tv;
            avahi_time_event_update(rj->time_event, &rj->delivery);
        }

        /* Update the flush cache bit */
        if (flush_cache)
            rj->flush_cache = 1;

        /* Update the querier field */
        if (!querier || (rj->querier_valid && avahi_address_cmp(querier, &rj->querier) != 0))
            rj->querier_valid = 0;

        /* Update record data (just for the TTL) */
        avahi_record_unref(rj->record);
        rj->record = avahi_record_ref(record);

        return 1;
    } else {
/*         avahi_log_debug("Accepted new response job.");  */

        /* Create a new job and schedule it */
        if (!(rj = job_new(s, record, AVAHI_SCHEDULED)))
            return 0; /* OOM */
        
        rj->delivery = tv;
        rj->time_event = avahi_time_event_new(s->time_event_queue, &rj->delivery, elapse_callback, rj);
        rj->flush_cache = flush_cache;

        if ((rj->querier_valid = !!querier))
            rj->querier = *querier;

        return 1;
    }
}
static int server_add_service_strlst_nocopy(
    AvahiServer *s,
    AvahiSEntryGroup *g,
    AvahiIfIndex interface,
    AvahiProtocol protocol,
    AvahiPublishFlags flags,
    const char *name,
    const char *type,
    const char *domain,
    const char *host,
    uint16_t port,
    AvahiStringList *strlst) {

    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;
    AvahiEntry *srv_entry = NULL, *txt_entry = NULL, *ptr_entry = NULL, *enum_entry = NULL;
    
    assert(s);
    assert(type);
    assert(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(protocol), AVAHI_ERR_INVALID_PROTOCOL);
    AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_FLAGS_VALID(flags,
                                                                AVAHI_PUBLISH_NO_COOKIE|
                                                                AVAHI_PUBLISH_UPDATE|
                                                                AVAHI_PUBLISH_USE_WIDE_AREA|
                                                                AVAHI_PUBLISH_USE_MULTICAST), 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_strict(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_fqdn(host), AVAHI_ERR_INVALID_HOST_NAME);

    if (!domain)
        domain = s->domain_name;

    if (!host)
        host = s->host_name_fqdn;

    transport_flags_from_domain(s, &flags, domain);
    AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, flags & AVAHI_PUBLISH_USE_MULTICAST, AVAHI_ERR_NOT_SUPPORTED);
    
    if (!(h = avahi_normalize_name_strdup(host))) {
        ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
        goto fail;
    }

    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;
    }

    /* Add service enumeration PTR record */
    
    if (!(ptr_entry = server_add_ptr_internal(s, g, interface, protocol, 0, AVAHI_DEFAULT_TTL, ptr_name, svc_name))) {
        ret = avahi_server_errno(s);
        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;
    }
    
    r->data.srv.priority = 0;
    r->data.srv.weight = 0;
    r->data.srv.port = port;
    r->data.srv.name = h;
    h = NULL;
    srv_entry = server_add_internal(s, g, interface, protocol, AVAHI_PUBLISH_UNIQUE, r);
    avahi_record_unref(r);

    if (!srv_entry) {
        ret = avahi_server_errno(s);
        goto fail;
    }

    /* Add TXT record */

    if (!(flags & AVAHI_PUBLISH_NO_COOKIE))
        strlst = add_magic_cookie(s, strlst);
    
    txt_entry = server_add_txt_strlst_nocopy(s, g, interface, protocol, AVAHI_PUBLISH_UNIQUE, AVAHI_DEFAULT_TTL, svc_name, strlst);
    strlst = NULL;

    if (!txt_entry) {
        ret = avahi_server_errno(s);
        goto fail;
    }

    /* Add service type enumeration record */
    
    if (!(enum_entry = server_add_ptr_internal(s, g, interface, protocol, 0, AVAHI_DEFAULT_TTL, enum_ptr, ptr_name))) {
        ret = avahi_server_errno(s);
        goto fail;
    }

fail:
    if (ret != AVAHI_OK && !(flags & AVAHI_PUBLISH_UPDATE)) {
        if (srv_entry)
            avahi_entry_free(s, srv_entry);
        if (txt_entry)
            avahi_entry_free(s, txt_entry);
        if (ptr_entry)
            avahi_entry_free(s, ptr_entry);
        if (enum_entry)
            avahi_entry_free(s, enum_entry);
    }
    
    avahi_string_list_free(strlst);
    avahi_free(h);
    
    return ret;
}
DBusHandlerResult avahi_dbus_msg_entry_group_impl(DBusConnection *c, DBusMessage *m, void *userdata) {
    DBusError error;
    EntryGroupInfo *i = userdata;

    assert(c);
    assert(m);
    assert(i);

    dbus_error_init(&error);

    avahi_log_debug(__FILE__": interface=%s, path=%s, member=%s",
                    dbus_message_get_interface(m),
                    dbus_message_get_path(m),
                    dbus_message_get_member(m));

    /* Introspection */
    if (dbus_message_is_method_call(m, DBUS_INTERFACE_INTROSPECTABLE, "Introspect"))
        return avahi_dbus_handle_introspect(c, m, "org.freedesktop.Avahi.EntryGroup.xml");

    /* Access control */
    if (strcmp(dbus_message_get_sender(m), i->client->name))
        return avahi_dbus_respond_error(c, m, AVAHI_ERR_ACCESS_DENIED, NULL);

    if (dbus_message_is_method_call(m, AVAHI_DBUS_INTERFACE_ENTRY_GROUP, "Free")) {

        if (!dbus_message_get_args(m, &error, DBUS_TYPE_INVALID)) {
            avahi_log_warn("Error parsing EntryGroup::Free message");
            goto fail;
        }

        avahi_dbus_entry_group_free(i);
        return avahi_dbus_respond_ok(c, m);

    } else if (dbus_message_is_method_call(m, AVAHI_DBUS_INTERFACE_ENTRY_GROUP, "Commit")) {

        if (!dbus_message_get_args(m, &error, DBUS_TYPE_INVALID)) {
            avahi_log_warn("Error parsing EntryGroup::Commit message");
            goto fail;
        }

        if (avahi_s_entry_group_commit(i->entry_group) < 0)
            return avahi_dbus_respond_error(c, m, avahi_server_errno(avahi_server), NULL);

        return avahi_dbus_respond_ok(c, m);


    } else if (dbus_message_is_method_call(m, AVAHI_DBUS_INTERFACE_ENTRY_GROUP, "Reset")) {

        if (!dbus_message_get_args(m, &error, DBUS_TYPE_INVALID)) {
            avahi_log_warn("Error parsing EntryGroup::Reset message");
            goto fail;
        }

        avahi_s_entry_group_reset(i->entry_group);
        i->n_entries = 0;
        return avahi_dbus_respond_ok(c, m);

    } else if (dbus_message_is_method_call(m, AVAHI_DBUS_INTERFACE_ENTRY_GROUP, "IsEmpty")) {

        if (!dbus_message_get_args(m, &error, DBUS_TYPE_INVALID)) {
            avahi_log_warn("Error parsing EntryGroup::IsEmpty message");
            goto fail;
        }

        return avahi_dbus_respond_boolean(c, m, !!avahi_s_entry_group_is_empty(i->entry_group));

    } else if (dbus_message_is_method_call(m, AVAHI_DBUS_INTERFACE_ENTRY_GROUP, "GetState")) {
        AvahiEntryGroupState state;

        if (!dbus_message_get_args(m, &error, DBUS_TYPE_INVALID)) {
            avahi_log_warn("Error parsing EntryGroup::GetState message");
            goto fail;
        }

        state = avahi_s_entry_group_get_state(i->entry_group);
        return avahi_dbus_respond_int32(c, m, (int32_t) state);

    } else if (dbus_message_is_method_call(m, AVAHI_DBUS_INTERFACE_ENTRY_GROUP, "AddService")) {
        int32_t interface, protocol;
        uint32_t flags;
        char *type, *name, *domain, *host;
        uint16_t port;
        AvahiStringList *strlst = NULL;

        if (!dbus_message_get_args(
                m, &error,
                DBUS_TYPE_INT32, &interface,
                DBUS_TYPE_INT32, &protocol,
                DBUS_TYPE_UINT32, &flags,
                DBUS_TYPE_STRING, &name,
                DBUS_TYPE_STRING, &type,
                DBUS_TYPE_STRING, &domain,
                DBUS_TYPE_STRING, &host,
                DBUS_TYPE_UINT16, &port,
                DBUS_TYPE_INVALID) ||
            !type || !name ||
            avahi_dbus_read_strlst(m, 8, &strlst) < 0) {
            avahi_log_warn("Error parsing EntryGroup::AddService message");
            goto fail;
        }

        if (!(flags & AVAHI_PUBLISH_UPDATE) && i->n_entries >= server->n_entries_per_entry_group_max) {
            avahi_string_list_free(strlst);
            return avahi_dbus_respond_error(c, m, AVAHI_ERR_TOO_MANY_ENTRIES, NULL);
        }

        if (domain && !*domain)
            domain = NULL;

        if (host && !*host)
            host = NULL;

        if (avahi_server_add_service_strlst(avahi_server, i->entry_group, (AvahiIfIndex) interface, (AvahiProtocol) protocol, (AvahiPublishFlags) flags, name, type, domain, host, port, strlst) < 0) {
            avahi_string_list_free(strlst);
            return avahi_dbus_respond_error(c, m, avahi_server_errno(avahi_server), NULL);
        }

        if (!(flags & AVAHI_PUBLISH_UPDATE))
            i->n_entries ++;

        avahi_string_list_free(strlst);

        return avahi_dbus_respond_ok(c, m);

    } else if (dbus_message_is_method_call(m, AVAHI_DBUS_INTERFACE_ENTRY_GROUP, "AddServiceSubtype")) {

        int32_t interface, protocol;
        uint32_t flags;
        char *type, *name, *domain, *subtype;

        if (!dbus_message_get_args(
                m, &error,
                DBUS_TYPE_INT32, &interface,
                DBUS_TYPE_INT32, &protocol,
                DBUS_TYPE_UINT32, &flags,
                DBUS_TYPE_STRING, &name,
                DBUS_TYPE_STRING, &type,
                DBUS_TYPE_STRING, &domain,
                DBUS_TYPE_STRING, &subtype,
                DBUS_TYPE_INVALID) || !type || !name || !subtype) {
            avahi_log_warn("Error parsing EntryGroup::AddServiceSubtype message");
            goto fail;
        }

        if (!(flags & AVAHI_PUBLISH_UPDATE) && i->n_entries >= server->n_entries_per_entry_group_max)
            return avahi_dbus_respond_error(c, m, AVAHI_ERR_TOO_MANY_ENTRIES, NULL);

        if (domain && !*domain)
            domain = NULL;

        if (avahi_server_add_service_subtype(avahi_server, i->entry_group, (AvahiIfIndex) interface, (AvahiProtocol) protocol, (AvahiPublishFlags) flags, name, type, domain, subtype) < 0)
            return avahi_dbus_respond_error(c, m, avahi_server_errno(avahi_server), NULL);

        if (!(flags & AVAHI_PUBLISH_UPDATE))
            i->n_entries ++;

        return avahi_dbus_respond_ok(c, m);

    } else if (dbus_message_is_method_call(m, AVAHI_DBUS_INTERFACE_ENTRY_GROUP, "UpdateServiceTxt")) {
        int32_t interface, protocol;
        uint32_t flags;
        char *type, *name, *domain;
        AvahiStringList *strlst;

        if (!dbus_message_get_args(
                m, &error,
                DBUS_TYPE_INT32, &interface,
                DBUS_TYPE_INT32, &protocol,
                DBUS_TYPE_UINT32, &flags,
                DBUS_TYPE_STRING, &name,
                DBUS_TYPE_STRING, &type,
                DBUS_TYPE_STRING, &domain,
                DBUS_TYPE_INVALID) ||
            !type || !name ||
            avahi_dbus_read_strlst(m, 6, &strlst)) {
            avahi_log_warn("Error parsing EntryGroup::UpdateServiceTxt message");
            goto fail;
        }

        if (domain && !*domain)
            domain = NULL;

        if (avahi_server_update_service_txt_strlst(avahi_server, i->entry_group, (AvahiIfIndex) interface, (AvahiProtocol) protocol, (AvahiPublishFlags) flags, name, type, domain, strlst) < 0) {
            avahi_string_list_free(strlst);
            return avahi_dbus_respond_error(c, m, avahi_server_errno(avahi_server), NULL);
        }

        avahi_string_list_free(strlst);

        return avahi_dbus_respond_ok(c, m);

    } else if (dbus_message_is_method_call(m, AVAHI_DBUS_INTERFACE_ENTRY_GROUP, "AddAddress")) {
        int32_t interface, protocol;
        uint32_t flags;
        char *name, *address;
        AvahiAddress a;

        if (!dbus_message_get_args(
                m, &error,
                DBUS_TYPE_INT32, &interface,
                DBUS_TYPE_INT32, &protocol,
                DBUS_TYPE_UINT32, &flags,
                DBUS_TYPE_STRING, &name,
                DBUS_TYPE_STRING, &address,
                DBUS_TYPE_INVALID) || !name || !address) {
            avahi_log_warn("Error parsing EntryGroup::AddAddress message");
            goto fail;
        }

        if (!(flags & AVAHI_PUBLISH_UPDATE) && i->n_entries >= server->n_entries_per_entry_group_max)
            return avahi_dbus_respond_error(c, m, AVAHI_ERR_TOO_MANY_ENTRIES, NULL);

        if (!(avahi_address_parse(address, AVAHI_PROTO_UNSPEC, &a)))
            return avahi_dbus_respond_error(c, m, AVAHI_ERR_INVALID_ADDRESS, NULL);

        if (avahi_server_add_address(avahi_server, i->entry_group, (AvahiIfIndex) interface, (AvahiProtocol) protocol, (AvahiPublishFlags) flags, name, &a) < 0)
            return avahi_dbus_respond_error(c, m, avahi_server_errno(avahi_server), NULL);

        if (!(flags & AVAHI_PUBLISH_UPDATE))
            i->n_entries ++;

        return avahi_dbus_respond_ok(c, m);
    } else if (dbus_message_is_method_call(m, AVAHI_DBUS_INTERFACE_ENTRY_GROUP, "AddRecord")) {
        int32_t interface, protocol;
        uint32_t flags, ttl, size;
        uint16_t clazz, type;
        char *name;
        void *rdata;
        AvahiRecord *r;

        if (!dbus_message_get_args(
                m, &error,
                DBUS_TYPE_INT32, &interface,
                DBUS_TYPE_INT32, &protocol,
                DBUS_TYPE_UINT32, &flags,
                DBUS_TYPE_STRING, &name,
                DBUS_TYPE_UINT16, &clazz,
                DBUS_TYPE_UINT16, &type,
                DBUS_TYPE_UINT32, &ttl,
                DBUS_TYPE_INVALID) || !name ||
            avahi_dbus_read_rdata (m, 7, &rdata, &size)) {
            avahi_log_warn("Error parsing EntryGroup::AddRecord message");
            goto fail;
        }

        if (!(flags & AVAHI_PUBLISH_UPDATE) && i->n_entries >= server->n_entries_per_entry_group_max)
            return avahi_dbus_respond_error(c, m, AVAHI_ERR_TOO_MANY_ENTRIES, NULL);

        if (!avahi_is_valid_domain_name (name))
            return avahi_dbus_respond_error(c, m, AVAHI_ERR_INVALID_DOMAIN_NAME, NULL);

        if (!(r = avahi_record_new_full (name, clazz, type, ttl)))
            return avahi_dbus_respond_error(c, m, AVAHI_ERR_NO_MEMORY, NULL);

        if (avahi_rdata_parse (r, rdata, size) < 0) {
            avahi_record_unref (r);
            return avahi_dbus_respond_error(c, m, AVAHI_ERR_INVALID_RDATA, NULL);
        }

        if (avahi_server_add(avahi_server, i->entry_group, (AvahiIfIndex) interface, (AvahiProtocol) protocol, (AvahiPublishFlags) flags, r) < 0) {
            avahi_record_unref (r);
            return avahi_dbus_respond_error(c, m, avahi_server_errno(avahi_server), NULL);
        }

        if (!(flags & AVAHI_PUBLISH_UPDATE))
            i->n_entries ++;

        avahi_record_unref (r);

        return avahi_dbus_respond_ok(c, m);
    }


    avahi_log_warn("Missed message %s::%s()", dbus_message_get_interface(m), dbus_message_get_member(m));

fail:
    if (dbus_error_is_set(&error))
        dbus_error_free(&error);

    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
static void record_browser_callback(
    AvahiSRecordBrowser*rr,
    AvahiIfIndex interface,
    AvahiProtocol protocol,
    AvahiBrowserEvent event,
    AvahiRecord *record,
    AvahiLookupResultFlags flags,
    void* userdata) {

    AvahiSAddressResolver *r = userdata;

    assert(rr);
    assert(r);

    switch (event) {
        case AVAHI_BROWSER_NEW:
            assert(record);
            assert(record->key->type == AVAHI_DNS_TYPE_PTR);

            if (flags & AVAHI_LOOKUP_RESULT_LLMNR)
                r->llmnr_has_record = 1;

            if (r->interface > 0 && interface != r->interface)
                return;

            if (r->protocol != AVAHI_PROTO_UNSPEC && protocol != r->protocol)
                return;

            if (r->interface <= 0)
                r->interface = interface;

            if (r->protocol == AVAHI_PROTO_UNSPEC)
                r->protocol = protocol;

            if (!r->ptr_record) {
                r->ptr_record = avahi_record_ref(record);
                r->flags = flags;

                finish(r, AVAHI_RESOLVER_FOUND);
            }
            break;

        case AVAHI_BROWSER_REMOVE:
            assert(record);
            assert(record->key->type == AVAHI_DNS_TYPE_PTR);

            if (r->ptr_record && avahi_record_equal_no_ttl(record, r->ptr_record)) {
                avahi_record_unref(r->ptr_record);
                r->ptr_record = NULL;
                r->flags = flags;

                /** Look for a replacement */
                avahi_s_record_browser_restart(r->record_browser);
                start_timeout(r);
            }
            break;

        case AVAHI_BROWSER_CACHE_EXHAUSTED:
            break;

        case AVAHI_BROWSER_ALL_FOR_NOW:
            /* when called by _LLMNR we trigger lookup by _MULTICAST*/
            if ((flags & AVAHI_LOOKUP_RESULT_LLMNR) && (r->retry_with_multicast) && !r->llmnr_has_record) {
                r->retry_with_multicast = 0;
                avahi_s_record_browser_free(r->record_browser);
                r->record_browser = avahi_s_record_browser_new(r->server, r->interface, r->protocol, r->key, AVAHI_LOOKUP_USE_MULTICAST, record_browser_callback, r);

                 if (r->record_browser) {
                    start_timeout(r);
                    break;
                 }
            }
            break;

        case AVAHI_BROWSER_FAILURE:
            if (r->retry_with_llmnr) {
                r->retry_with_llmnr = 0;
                avahi_s_record_browser_free(r->record_browser);
                r->record_browser = avahi_s_record_browser_new(r->server, r->interface, r->protocol, r->key, AVAHI_LOOKUP_USE_LLMNR, record_browser_callback, r);

                if (r->record_browser) {
                    start_timeout(r);
                    break;
                }
            }
            break;
    }
}
Example #26
0
void avahi_cache_update(AvahiCache *c, AvahiRecord *r, int cache_flush, const AvahiAddress *a) {
/*     char *txt; */

    assert(c);
    assert(r && r->ref >= 1);

/*     txt = avahi_record_to_string(r); */

    if (r->ttl == 0) {
        /* This is a goodbye request */

        AvahiCacheEntry *e;

        if ((e = lookup_record(c, r)))
            expire_in_one_second(c, e, AVAHI_CACHE_GOODBYE_FINAL);

    } else {
        AvahiCacheEntry *e = NULL, *first;
        struct timeval now;

        gettimeofday(&now, NULL);

        /* This is an update request */

        if ((first = lookup_key(c, r->key))) {

            if (cache_flush) {

                /* For unique entries drop all entries older than one second */
                for (e = first; e; e = e->by_key_next) {
                    AvahiUsec t;

                    t = avahi_timeval_diff(&now, &e->timestamp);

                    if (t > 1000000)
                        expire_in_one_second(c, e, AVAHI_CACHE_REPLACE_FINAL);
                }
            }

            /* Look for exactly the same entry */
            for (e = first; e; e = e->by_key_next)
                if (avahi_record_equal_no_ttl(e->record, r))
                    break;
        }

        if (e) {

/*             avahi_log_debug("found matching cache entry");  */

            /* We need to update the hash table key if we replace the
             * record */
            if (e->by_key_prev == NULL)
                avahi_hashmap_replace(c->hashmap, r->key, e);

            /* Update the record */
            avahi_record_unref(e->record);
            e->record = avahi_record_ref(r);

/*             avahi_log_debug("cache: updating %s", txt);   */

        } else {
            /* No entry found, therefore we create a new one */

/*             avahi_log_debug("cache: couldn't find matching cache entry for %s", txt);   */

            if (c->n_entries >= AVAHI_CACHE_ENTRIES_MAX)
                return;

            if (!(e = avahi_new(AvahiCacheEntry, 1))) {
                avahi_log_error(__FILE__": Out of memory");
                return;
            }

            e->cache = c;
            e->time_event = NULL;
            e->record = avahi_record_ref(r);

            /* Append to hash table */
            AVAHI_LLIST_PREPEND(AvahiCacheEntry, by_key, first, e);
            avahi_hashmap_replace(c->hashmap, e->record->key, first);

            /* Append to linked list */
            AVAHI_LLIST_PREPEND(AvahiCacheEntry, entry, c->entries, e);

            c->n_entries++;

            /* Notify subscribers */
            avahi_multicast_lookup_engine_notify(c->server->mdns.multicast_lookup_engine, c->interface, e->record, AVAHI_BROWSER_NEW);
        }

        e->origin = *a;
        e->timestamp = now;
        next_expiry(c, e, 80);
        e->state = AVAHI_CACHE_VALID;
        e->cache_flush = cache_flush;
    }

/*     avahi_free(txt);  */
}