header_t *
dns_header_decode(netif_t *netif, packet_t *packet, raw_packet_t *raw_packet, packet_offset_t offset)
{
    dns_header_t       *dns = dns_header_new();
    packet_offset_t     field_offset;

    if (raw_packet->len < (offset + DNS_HEADER_LEN)) {
        LOG_PRINTLN(LOG_HEADER_DNS, LOG_ERROR, ("decode DNS header: size too small (present=%u, required=%u)", raw_packet->len - offset, DNS_HEADER_LEN));
        DNS_FAILURE_EXIT;
    }
    
    /* fetch header */
    uint8_to_uint16(&(dns->id),         &(raw_packet->data[offset + DNS_HEADER_OFFSET_ID]));
    uint8_to_uint16(&(dns->flags.raw),  &(raw_packet->data[offset + DNS_HEADER_OFFSET_FLAGS]));
    uint8_to_uint16(&(dns->qd_count),   &(raw_packet->data[offset + DNS_HEADER_OFFSET_QD_COUNT]));
    uint8_to_uint16(&(dns->an_count),   &(raw_packet->data[offset + DNS_HEADER_OFFSET_AN_COUNT]));
    uint8_to_uint16(&(dns->ns_count),   &(raw_packet->data[offset + DNS_HEADER_OFFSET_NS_COUNT]));
    uint8_to_uint16(&(dns->ar_count),   &(raw_packet->data[offset + DNS_HEADER_OFFSET_AR_COUNT]));
    
    field_offset = offset + DNS_HEADER_LEN;
    
    /* question section */
    if (dns->qd_count > 0) {
        dns->qd = dns_query_new();
        if (!dns_header_decode_query(raw_packet, offset, &field_offset, dns->qd_count, dns->qd)) {
            dns_query_free(dns->qd);
            DNS_FAILURE_EXIT;
        }
    }
    
    /* answer records section */
    if (dns->an_count > 0) {
        dns->an = dns_rr_new();
        if (!dns_header_decode_rr(raw_packet, offset, &field_offset, dns->an_count, dns->an)) {
            dns_rr_free(dns->an);
            DNS_FAILURE_EXIT;
        }
    }
    
    /* authority records section */
    if (dns->ns_count > 0) {
        dns->ns = dns_rr_new();
        if (!dns_header_decode_rr(raw_packet, offset, &field_offset, dns->ns_count, dns->ns)) {
            dns_rr_free(dns->ns);
            DNS_FAILURE_EXIT;
        }
    }
    
    /* additional records section */
    if (dns->ar_count > 0) {
        dns->ar = dns_rr_new();
        if (!dns_header_decode_rr(raw_packet, offset, &field_offset, dns->ar_count, dns->ar)) {
            dns_rr_free(dns->ar);
            DNS_FAILURE_EXIT;
        }
    }
    
    return (header_t *) dns;
}
static bool
dns_header_decode_query(raw_packet_t *raw_packet, packet_offset_t header_offset, packet_offset_t *field_offset, uint16_t count, dns_query_t *query)
{
    dns_label_t    *label;

    for (; count > 0; count--) {

        if (raw_packet->len < (*field_offset + DNS_QUERY_MIN_LEN)) {
            LOG_PRINTLN(LOG_HEADER_DNS, LOG_ERROR, ("decode DNS query: size too small (present=%" PRIoffset ", required=%" PRIoffset ", offset=%" PRIoffset "/%x)", raw_packet->len - *field_offset, DNS_QUERY_MIN_LEN, *field_offset, *field_offset));
            return false;
        }

        /* qname */
        label = dns_label_new();

        if (!dns_header_decode_label(raw_packet, header_offset, field_offset, label)) {
            dns_label_free(label);
            return false;
        }
        query->qname = label;

        /* qtype + qclass */
        uint8_to_uint16(&(query->qtype),  &(raw_packet->data[*field_offset + DNS_QUERY_OFFSET_QTYPE]));
        uint8_to_uint16(&(query->qclass), &(raw_packet->data[*field_offset + DNS_QUERY_OFFSET_QCLASS]));

        *field_offset += DNS_QUERY_SIZE;

        /* not the last query */
        if (count > 1) {
            query->next = dns_query_new();
            query       = query->next;
        }
    }

    return true;
}
Beispiel #3
0
static int bus_method_resolve_address(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
        _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
        _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL;
        _cleanup_free_ char *reverse = NULL;
        Manager *m = userdata;
        int family, ifindex;
        uint64_t flags;
        const void *d;
        DnsQuery *q;
        size_t sz;
        int r;

        assert(bus);
        assert(message);
        assert(m);

        r = sd_bus_message_read(message, "ii", &ifindex, &family);
        if (r < 0)
                return r;

        if (!IN_SET(family, AF_INET, AF_INET6))
                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown address family %i", family);

        r = sd_bus_message_read_array(message, 'y', &d, &sz);
        if (r < 0)
                return r;

        if (sz != FAMILY_ADDRESS_SIZE(family))
                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid address size");

        r = sd_bus_message_read(message, "t", &flags);
        if (r < 0)
                return r;

        r = check_ifindex_flags(ifindex, &flags, error);
        if (r < 0)
                return r;

        r = dns_name_reverse(family, d, &reverse);
        if (r < 0)
                return r;

        question = dns_question_new(1);
        if (!question)
                return -ENOMEM;

        key = dns_resource_key_new_consume(DNS_CLASS_IN, DNS_TYPE_PTR, reverse);
        if (!key)
                return -ENOMEM;

        reverse = NULL;

        r = dns_question_add(question, key);
        if (r < 0)
                return r;

        r = dns_query_new(m, &q, question, ifindex, flags);
        if (r < 0)
                return r;

        q->request = sd_bus_message_ref(message);
        q->request_family = family;
        memcpy(&q->request_address, d, sz);
        q->complete = bus_method_resolve_address_complete;

        r = dns_query_bus_track(q, bus, message);
        if (r < 0)
                return r;

        r = dns_query_go(q);
        if (r < 0) {
                dns_query_free(q);

                if (r == -ESRCH)
                        sd_bus_error_setf(error, BUS_ERROR_NO_NAME_SERVERS, "No appropriate name servers or networks for name found");

                return r;
        }

        return 1;
}
Beispiel #4
0
static int bus_method_resolve_hostname(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
        _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL;
        Manager *m = userdata;
        const char *hostname;
        int family, ifindex;
        uint64_t flags;
        DnsQuery *q;
        int r;

        assert(bus);
        assert(message);
        assert(m);

        r = sd_bus_message_read(message, "isit", &ifindex, &hostname, &family, &flags);
        if (r < 0)
                return r;

        if (!IN_SET(family, AF_INET, AF_INET6, AF_UNSPEC))
                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown address family %i", family);

        r = dns_name_normalize(hostname, NULL);
        if (r < 0)
                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid hostname '%s'", hostname);

        r = check_ifindex_flags(ifindex, &flags, error);
        if (r < 0)
                return r;

        question = dns_question_new(family == AF_UNSPEC ? 2 : 1);
        if (!question)
                return -ENOMEM;

        if (family != AF_INET6) {
                _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;

                key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, hostname);
                if (!key)
                        return -ENOMEM;

                r = dns_question_add(question, key);
                if (r < 0)
                        return r;
        }

        if (family != AF_INET) {
                _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;

                key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_AAAA, hostname);
                if (!key)
                        return -ENOMEM;

                r = dns_question_add(question, key);
                if (r < 0)
                        return r;
        }

        r = dns_query_new(m, &q, question, ifindex, flags);
        if (r < 0)
                return r;

        q->request = sd_bus_message_ref(message);
        q->request_family = family;
        q->request_hostname = hostname;
        q->complete = bus_method_resolve_hostname_complete;

        r = dns_query_bus_track(q, bus, message);
        if (r < 0)
                return r;

        r = dns_query_go(q);
        if (r < 0) {
                dns_query_free(q);

                if (r == -ESRCH)
                        sd_bus_error_setf(error, BUS_ERROR_NO_NAME_SERVERS, "No appropriate name servers or networks for name found");

                return r;
        }

        return 1;
}