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;
}
Exemple #2
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;
}
Exemple #3
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;
}
Exemple #4
0
static void bus_method_resolve_address_complete(DnsQuery *q) {
        _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
        _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
        unsigned added = 0, i;
        int r;

        assert(q);

        if (q->state != DNS_TRANSACTION_SUCCESS) {
                r = reply_query_state(q);
                goto finish;
        }

        r = sd_bus_message_new_method_return(q->request, &reply);
        if (r < 0)
                goto finish;

        r = sd_bus_message_append(reply, "i", q->answer_ifindex);
        if (r < 0)
                goto finish;

        r = sd_bus_message_open_container(reply, 'a', "s");
        if (r < 0)
                goto finish;

        if (q->answer) {
                answer = dns_answer_ref(q->answer);

                for (i = 0; i < answer->n_rrs; i++) {
                        r = dns_question_matches_rr(q->question, answer->rrs[i]);
                        if (r < 0)
                                goto finish;
                        if (r == 0)
                                continue;

                        r = sd_bus_message_append(reply, "s", answer->rrs[i]->ptr.name);
                        if (r < 0)
                                goto finish;

                        added ++;
                }
        }

        if (added <= 0) {
                _cleanup_free_ char *ip = NULL;

                in_addr_to_string(q->request_family, &q->request_address, &ip);

                r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "Address '%s' does not have any RR of requested type", ip);
                goto finish;
        }

        r = sd_bus_message_close_container(reply);
        if (r < 0)
                goto finish;

        r = sd_bus_message_append(reply, "t", SD_RESOLVED_FLAGS_MAKE(q->answer_protocol, q->answer_family));
        if (r < 0)
                goto finish;

        r = sd_bus_send(q->manager->bus, reply, NULL);

finish:
        if (r < 0) {
                log_error_errno(r, "Failed to send address reply: %m");
                sd_bus_reply_method_errno(q->request, -r, NULL);
        }

        dns_query_free(q);
}
Exemple #5
0
static void bus_method_resolve_hostname_complete(DnsQuery *q) {
        _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *cname = NULL, *canonical = NULL;
        _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
        _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
        unsigned added = 0, i;
        int r;

        assert(q);

        if (q->state != DNS_TRANSACTION_SUCCESS) {
                r = reply_query_state(q);
                goto finish;
        }

        r = sd_bus_message_new_method_return(q->request, &reply);
        if (r < 0)
                goto finish;

        r = sd_bus_message_append(reply, "i", q->answer_ifindex);
        if (r < 0)
                goto finish;

        r = sd_bus_message_open_container(reply, 'a', "(iay)");
        if (r < 0)
                goto finish;

        if (q->answer) {
                answer = dns_answer_ref(q->answer);

                for (i = 0; i < answer->n_rrs; i++) {
                        r = dns_question_matches_rr(q->question, answer->rrs[i]);
                        if (r < 0)
                                goto finish;
                        if (r == 0) {
                                /* Hmm, if this is not an address record,
                                   maybe it's a cname? If so, remember this */
                                r = dns_question_matches_cname(q->question, answer->rrs[i]);
                                if (r < 0)
                                        goto finish;
                                if (r > 0)
                                        cname = dns_resource_record_ref(answer->rrs[i]);

                                continue;
                        }

                        r = append_address(reply, answer->rrs[i]);
                        if (r < 0)
                                goto finish;

                        if (!canonical)
                                canonical = dns_resource_record_ref(answer->rrs[i]);

                        added ++;
                }
        }

        if (added <= 0) {
                if (!cname) {
                        r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "'%s' does not have any RR of requested type", q->request_hostname);
                        goto finish;
                }

                /* This has a cname? Then update the query with the
                 * new cname. */
                r = dns_query_cname_redirect(q, cname->cname.name);
                if (r < 0) {
                        if (r == -ELOOP)
                                r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop on '%s'", q->request_hostname);
                        else
                                r = sd_bus_reply_method_errno(q->request, -r, NULL);

                        goto finish;
                }

                /* Before we restart the query, let's see if any of
                 * the RRs we already got already answers our query */
                for (i = 0; i < answer->n_rrs; i++) {
                        r = dns_question_matches_rr(q->question, answer->rrs[i]);
                        if (r < 0)
                                goto finish;
                        if (r == 0)
                                continue;

                        r = append_address(reply, answer->rrs[i]);
                        if (r < 0)
                                goto finish;

                        if (!canonical)
                                canonical = dns_resource_record_ref(answer->rrs[i]);

                        added++;
                }

                /* If we didn't find anything, then let's restart the
                 * query, this time with the cname */
                if (added <= 0) {
                        r = dns_query_go(q);
                        if (r == -ESRCH) {
                                r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_NAME_SERVERS, "No appropriate name servers or networks for name found");
                                goto finish;
                        }
                        if (r < 0) {
                                r = sd_bus_reply_method_errno(q->request, -r, NULL);
                                goto finish;
                        }

                        return;
                }
        }

        r = sd_bus_message_close_container(reply);
        if (r < 0)
                goto finish;

        /* Return the precise spelling and uppercasing reported by the server */
        assert(canonical);
        r = sd_bus_message_append(reply, "st", DNS_RESOURCE_KEY_NAME(canonical->key), SD_RESOLVED_FLAGS_MAKE(q->answer_protocol, q->answer_family));
        if (r < 0)
                goto finish;

        r = sd_bus_send(q->manager->bus, reply, NULL);

finish:
        if (r < 0) {
                log_error_errno(r, "Failed to send hostname reply: %m");
                sd_bus_reply_method_errno(q->request, -r, NULL);
        }

        dns_query_free(q);
}