Пример #1
0
static void lookup_destroy(AvahiWideAreaLookup *l) {
    AvahiWideAreaLookup *t;
    assert(l);
    
    lookup_stop(l);

    t = avahi_hashmap_lookup(l->engine->lookups_by_key, l->key);
    AVAHI_LLIST_REMOVE(AvahiWideAreaLookup, by_key, t, l);
    if (t)
        avahi_hashmap_replace(l->engine->lookups_by_key, avahi_key_ref(l->key), t);
    else
        avahi_hashmap_remove(l->engine->lookups_by_key, l->key);

    AVAHI_LLIST_REMOVE(AvahiWideAreaLookup, lookups, l->engine->lookups, l);
    
    avahi_hashmap_remove(l->engine->lookups_by_id, &l->id);
    avahi_dns_packet_free(l->packet);

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

    if (l->cname_key)
        avahi_key_unref(l->cname_key);
    
    avahi_free(l);
}
Пример #2
0
static void send_response_packet(AvahiResponseScheduler *s, AvahiResponseJob *rj) {
    AvahiDnsPacket *p;
    unsigned n;

    assert(s);
    assert(rj);

    if (!(p = avahi_dns_packet_new_response(s->interface->hardware->mtu, 1)))
        return; /* OOM */
    n = 1;

    /* Put it in the packet. */
    if (packet_add_response_job(s, p, rj)) {

        /* Try to fill up packet with more responses, if available */
        while (s->jobs) {
            
            if (!packet_add_response_job(s, p, s->jobs))
                break;
            
            n++;
        }
        
    } else {
        size_t size;
        
        avahi_dns_packet_free(p);

        /* OK, the packet was too small, so create one that fits */
        size = avahi_record_get_estimate_size(rj->record) + AVAHI_DNS_PACKET_HEADER_SIZE;

        if (!(p = avahi_dns_packet_new_response(size + AVAHI_DNS_PACKET_EXTRA_SIZE, 1)))
            return; /* OOM */

        if (!packet_add_response_job(s, p, rj)) {
            avahi_dns_packet_free(p);

            avahi_log_warn("Record too large, cannot send");
            job_mark_done(s, rj);
            return;
        }
    }

    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);
}
Пример #3
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);
}
Пример #4
0
static void socket_event(AVAHI_GCC_UNUSED AvahiWatch *w, int fd, AVAHI_GCC_UNUSED AvahiWatchEvent events, void *userdata) {
    AvahiWideAreaLookupEngine *e = userdata;
    AvahiDnsPacket *p = NULL;
    
    if (fd == e->fd_ipv4)
        p = avahi_recv_dns_packet_ipv4(e->fd_ipv4, NULL, NULL, NULL, NULL, NULL);
    else {
        assert(fd == e->fd_ipv6);
        p = avahi_recv_dns_packet_ipv6(e->fd_ipv6, NULL, NULL, NULL, NULL, NULL);
    }

    if (p) {
        handle_packet(e, p);
        avahi_dns_packet_free(p);
    }
}
Пример #5
0
void avahi_llmnr_query_job_destroy(AvahiLLMNRQueryScheduler *s, AvahiLLMNRQueryJob *qj) {
    assert(s);
    assert(qj);

    /* Free lq */
    avahi_llmnr_query_destroy(qj->lq);

    /* Free dns packet and time_event */
    if (qj->p)
        avahi_dns_packet_free(qj->p);

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

    /* Remove from the lists */
    AVAHI_LLIST_REMOVE(AvahiLLMNRQueryJob, jobs_by_scheduler, s->jobs, qj);
    AVAHI_LLIST_REMOVE(AvahiLLMNRQueryJob, jobs_by_interface, s->i->llmnr.queryjobs, qj);

    avahi_free(qj);
}
Пример #6
0
AvahiWideAreaLookup *avahi_wide_area_lookup_new(
    AvahiWideAreaLookupEngine *e,
    AvahiKey *key,
    AvahiWideAreaLookupCallback callback,
    void *userdata) {
    
    struct timeval tv;
    AvahiWideAreaLookup *l, *t;
    uint8_t *p;

    assert(e);
    assert(key);
    assert(callback);
    assert(userdata);

    l = avahi_new(AvahiWideAreaLookup, 1);
    l->engine = e;
    l->dead = 0;
    l->key = avahi_key_ref(key);
    l->cname_key = avahi_key_new_cname(l->key);
    l->callback = callback;
    l->userdata = userdata;

    /* If more than 65K wide area quries are issued simultaneously,
     * this will break. This should be limited by some higher level */

    for (;; e->next_id++)
        if (!find_lookup(e, e->next_id))
            break; /* This ID is not yet used. */

    l->id = e->next_id++;
    
    /* We keep the packet around in case we need to repeat our query */
    l->packet = avahi_dns_packet_new(0);

    avahi_dns_packet_set_field(l->packet, AVAHI_DNS_FIELD_ID, (uint16_t) l->id);
    avahi_dns_packet_set_field(l->packet, AVAHI_DNS_FIELD_FLAGS, AVAHI_DNS_FLAGS(0, 0, 0, 0, 1, 0, 0, 0, 0, 0));

    p = avahi_dns_packet_append_key(l->packet, key, 0);
    assert(p);
    
    avahi_dns_packet_set_field(l->packet, AVAHI_DNS_FIELD_QDCOUNT, 1);

    if (send_to_dns_server(l, l->packet) < 0) {
        avahi_log_error(__FILE__": Failed to send packet.");
        avahi_dns_packet_free(l->packet);
        avahi_key_unref(l->key);
        if (l->cname_key)
            avahi_key_unref(l->cname_key);
        avahi_free(l);
        return NULL;
    }

    l->n_send = 1;
    
    l->time_event = avahi_time_event_new(e->server->time_event_queue, avahi_elapse_time(&tv, 500, 0), sender_timeout_callback, l);

    avahi_hashmap_insert(e->lookups_by_id, &l->id, l);

    t = avahi_hashmap_lookup(e->lookups_by_key, l->key);
    AVAHI_LLIST_PREPEND(AvahiWideAreaLookup, by_key, t, l);
    avahi_hashmap_replace(e->lookups_by_key, avahi_key_ref(l->key), t);

    AVAHI_LLIST_PREPEND(AvahiWideAreaLookup, lookups, e->lookups, l);
    
    return l;
}
Пример #7
0
static void elapse_callback(AVAHI_GCC_UNUSED AvahiTimeEvent *e, void* data) {
    AvahiProbeJob *pj = data, *next;
    AvahiProbeScheduler *s;
    AvahiDnsPacket *p;
    unsigned n;

    assert(pj);
    s = pj->scheduler;

    if (pj->done) {
        /* Lets remove it  from the history */
        job_free(s, pj);
        return;
    }

    if (!(p = avahi_dns_packet_new_query(s->interface->hardware->mtu)))
        return; /* OOM */
    n = 1;
    
    /* Add the import probe */
    if (!packet_add_probe_query(s, p, pj)) {
        size_t size;
        AvahiKey *k;
        int b;

        avahi_dns_packet_free(p);

        /* The probe didn't fit in the package, so let's allocate a larger one */

        size =
            avahi_key_get_estimate_size(pj->record->key) +
            avahi_record_get_estimate_size(pj->record) +
            AVAHI_DNS_PACKET_HEADER_SIZE;
        
        if (size > AVAHI_DNS_PACKET_SIZE_MAX)
            size = AVAHI_DNS_PACKET_SIZE_MAX;

        if (!(p = avahi_dns_packet_new_query(size)))
            return; /* OOM */

        if (!(k = avahi_key_new(pj->record->key->name, pj->record->key->clazz, AVAHI_DNS_TYPE_ANY))) {
            avahi_dns_packet_free(p);
            return;  /* OOM */
        }
        
        b = avahi_dns_packet_append_key(p, k, 0) && avahi_dns_packet_append_record(p, pj->record, 0, 0);
        avahi_key_unref(k);

        if (b) {
            avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_NSCOUNT, 1);
            avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_QDCOUNT, 1);
            avahi_interface_send_packet(s->interface, p);
        } else
            avahi_log_warn("Probe record too large, cannot send");   
        
        avahi_dns_packet_free(p);
        job_mark_done(s, pj);

        return;
    }

    /* Try to fill up packet with more probes, if available */
    for (pj = s->jobs; pj; pj = pj->jobs_next) {

        if (pj->chosen)
            continue;
        
        if (!packet_add_probe_query(s, p, pj))
            break;
        
        n++;
    }

    avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_QDCOUNT, n);

    n = 0;

    /* Now add the chosen records to the authorative section */
    for (pj = s->jobs; pj; pj = next) {

        next = pj->jobs_next;

        if (!pj->chosen)
            continue;

        if (!avahi_dns_packet_append_record(p, pj->record, 0, 0)) {
/*             avahi_log_warn("Bad probe size estimate!"); */

            /* Unmark all following jobs */
            for (; pj; pj = pj->jobs_next)
                pj->chosen = 0;
            
            break;
        }

        job_mark_done(s, pj);
        
        n ++;
    }
    
    avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_NSCOUNT, n);

    /* Send it now */
    avahi_interface_send_packet(s->interface, p);
    avahi_dns_packet_free(p);
}
Пример #8
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;
}
Пример #9
0
AvahiDnsPacket *avahi_recv_dns_packet_ipv6(
        int fd,
        AvahiIPv6Address *ret_src_address,
        uint16_t *ret_src_port,
        AvahiIPv6Address *ret_dst_address,
        AvahiIfIndex *ret_iface,
        uint8_t *ret_ttl) {

    AvahiDnsPacket *p = NULL;
    struct msghdr msg;
    struct iovec io;
    size_t aux[1024 / sizeof(size_t)];
    ssize_t l;
    int ms;
    struct cmsghdr *cmsg;
    int found_ttl = 0, found_iface = 0;
    struct sockaddr_in6 sa;

    assert(fd >= 0);

    if (ioctl(fd, FIONREAD, &ms) < 0) {
        avahi_log_warn("ioctl(): %s", strerror(errno));
        goto fail;
    }

    if (ms < 0) {
        avahi_log_warn("FIONREAD returned negative value.");
        goto fail;
    }

    p = avahi_dns_packet_new(ms + AVAHI_DNS_PACKET_EXTRA_SIZE);

    io.iov_base = AVAHI_DNS_PACKET_DATA(p);
    io.iov_len = p->max_size;

    memset(&msg, 0, sizeof(msg));
    msg.msg_name = (struct sockaddr*) &sa;
    msg.msg_namelen = sizeof(sa);

    msg.msg_iov = &io;
    msg.msg_iovlen = 1;
    msg.msg_control = aux;
    msg.msg_controllen = sizeof(aux);
    msg.msg_flags = 0;

    if ((l = recvmsg(fd, &msg, 0)) < 0) {
        /* Linux returns EAGAIN when an invalid IP packet has been
        received. We suppress warnings in this case because this might
        create quite a bit of log traffic on machines with unstable
        links. (See #60) */

        if (errno != EAGAIN)
            avahi_log_warn("recvmsg(): %s", strerror(errno));

        goto fail;
    }

    assert(!(msg.msg_flags & MSG_CTRUNC));
    assert(!(msg.msg_flags & MSG_TRUNC));

    p->size = (size_t) l;

    if (ret_src_port)
        *ret_src_port = avahi_port_from_sockaddr((struct sockaddr*) &sa);

    if (ret_src_address) {
        AvahiAddress a;
        avahi_address_from_sockaddr((struct sockaddr*) &sa, &a);
        *ret_src_address = a.data.ipv6;
    }

    for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) {

        if (cmsg->cmsg_level == IPPROTO_IPV6) {

            switch (cmsg->cmsg_type) {

                case IPV6_HOPLIMIT:

                    if (ret_ttl)
                        *ret_ttl = (uint8_t) (*(int *) CMSG_DATA(cmsg));

                    found_ttl = 1;

                    break;

                case IPV6_PKTINFO: {
                    struct in6_pktinfo *i = (struct in6_pktinfo*) CMSG_DATA(cmsg);

                    if (ret_iface)
                        *ret_iface = i->ipi6_ifindex;

                    if (ret_dst_address)
                        memcpy(ret_dst_address->address, i->ipi6_addr.s6_addr, 16);

                    found_iface = 1;
                    break;
                }

                default:
                    avahi_log_warn("Unhandled cmsg_type : %d", cmsg->cmsg_type);
                    break;
            }
        }
    }

    assert(found_iface);
    assert(found_ttl);

    return p;

fail:
    if (p)
        avahi_dns_packet_free(p);

    return NULL;
}
Пример #10
0
AvahiDnsPacket *avahi_recv_dns_packet_ipv4(
        int fd,
        AvahiIPv4Address *ret_src_address,
        uint16_t *ret_src_port,
        AvahiIPv4Address *ret_dst_address,
        AvahiIfIndex *ret_iface,
        uint8_t *ret_ttl) {

    AvahiDnsPacket *p= NULL;
    struct msghdr msg;
    struct iovec io;
    size_t aux[1024 / sizeof(size_t)]; /* for alignment on ia64 ! */
    ssize_t l;
    struct cmsghdr *cmsg;
    int found_addr = 0;
    int ms;
    struct sockaddr_in sa;

    assert(fd >= 0);

    if (ioctl(fd, FIONREAD, &ms) < 0) {
        avahi_log_warn("ioctl(): %s", strerror(errno));
        goto fail;
    }

    if (ms < 0) {
        avahi_log_warn("FIONREAD returned negative value.");
        goto fail;
    }

    p = avahi_dns_packet_new(ms + AVAHI_DNS_PACKET_EXTRA_SIZE);

    io.iov_base = AVAHI_DNS_PACKET_DATA(p);
    io.iov_len = p->max_size;

    memset(&msg, 0, sizeof(msg));
    msg.msg_name = &sa;
    msg.msg_namelen = sizeof(sa);
    msg.msg_iov = &io;
    msg.msg_iovlen = 1;
    msg.msg_control = aux;
    msg.msg_controllen = sizeof(aux);
    msg.msg_flags = 0;

    if ((l = recvmsg(fd, &msg, 0)) < 0) {
        /* Linux returns EAGAIN when an invalid IP packet has been
        received. We suppress warnings in this case because this might
        create quite a bit of log traffic on machines with unstable
        links. (See #60) */

        if (errno != EAGAIN)
            avahi_log_warn("recvmsg(): %s", strerror(errno));

        goto fail;
    }

    if (sa.sin_addr.s_addr == INADDR_ANY) {
        /* Linux 2.4 behaves very strangely sometimes! */
        goto fail;
    }

    assert(!(msg.msg_flags & MSG_CTRUNC));
    assert(!(msg.msg_flags & MSG_TRUNC));

    p->size = (size_t) l;

    if (ret_src_port)
        *ret_src_port = avahi_port_from_sockaddr((struct sockaddr*) &sa);

    if (ret_src_address) {
        AvahiAddress a;
        avahi_address_from_sockaddr((struct sockaddr*) &sa, &a);
        *ret_src_address = a.data.ipv4;
    }

    if (ret_ttl)
        *ret_ttl = 255;

    if (ret_iface)
        *ret_iface = AVAHI_IF_UNSPEC;

    for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) {

        if (cmsg->cmsg_level == IPPROTO_IP) {

            switch (cmsg->cmsg_type) {
#ifdef IP_RECVTTL
                case IP_RECVTTL:
#endif
                case IP_TTL:
                    if (ret_ttl)
                        *ret_ttl = (uint8_t) (*(int *) CMSG_DATA(cmsg));

                    break;

#ifdef IP_PKTINFO
                case IP_PKTINFO: {
                    struct in_pktinfo *i = (struct in_pktinfo*) CMSG_DATA(cmsg);

                    if (ret_iface)
                        *ret_iface = (int) i->ipi_ifindex;

                    if (ret_dst_address)
                        ret_dst_address->address = i->ipi_addr.s_addr;

                    found_addr = 1;

                    break;
                }
#endif

#ifdef IP_RECVIF
                case IP_RECVIF: {
                    struct sockaddr_dl *sdl = (struct sockaddr_dl *) CMSG_DATA (cmsg);

                    if (ret_iface)
#ifdef __sun
                        *ret_iface = *(uint_t*) sdl;
#else
                        *ret_iface = (int) sdl->sdl_index;
#endif

                    break;
                }
#endif

#ifdef IP_RECVDSTADDR
                case IP_RECVDSTADDR:
                    if (ret_dst_address)
                        memcpy(&ret_dst_address->address, CMSG_DATA (cmsg), 4);

                    found_addr = 1;
                    break;
#endif

                default:
                    avahi_log_warn("Unhandled cmsg_type : %d", cmsg->cmsg_type);
                    break;
            }
        }
    }

    assert(found_addr);

    return p;

fail:
    if (p)
        avahi_dns_packet_free(p);

    return NULL;
}