resolved-bus.c revision 2d4c5cbc0ed3ccb09dc086a040088b454c22c644
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen This file is part of systemd.
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen Copyright 2014 Lennart Poettering
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen systemd is free software; you can redistribute it and/or modify it
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen under the terms of the GNU Lesser General Public License as published by
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen the Free Software Foundation; either version 2.1 of the License, or
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen (at your option) any later version.
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen systemd is distributed in the hope that it will be useful, but
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen WITHOUT ANY WARRANTY; without even the implied warranty of
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen Lesser General Public License for more details.
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen You should have received a copy of the GNU Lesser General Public License
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen along with systemd; If not, see <http://www.gnu.org/licenses/>.
07630cea1f3a845c09309f197ac7c4f11edd3b62Lennart Poettering r = in_addr_to_string(q->request_family, &q->request_address, &ip);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen switch (q->state) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_NAME_SERVERS, "No appropriate name servers or networks for name found");
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return sd_bus_reply_method_errorf(q->request, SD_BUS_ERROR_TIMEOUT, "Query timed out");
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return sd_bus_reply_method_errorf(q->request, SD_BUS_ERROR_TIMEOUT, "All attempts to contact name servers or networks failed");
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_RESOURCES, "Not enough resources");
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return sd_bus_reply_method_errorf(q->request, BUS_ERROR_INVALID_REPLY, "Received invalid reply");
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen sd_bus_error_setf(&error, _BUS_ERROR_DNS "NXDOMAIN", "'%s' not found", name);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen const char *rc, *n;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen sd_bus_error_setf(&error, n, "Could not resolve '%s', server or network returned error %s", name, rc);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return sd_bus_reply_method_error(q->request, &error);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic int append_address(sd_bus_message *reply, DnsResourceRecord *rr, int ifindex) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = sd_bus_message_open_container(reply, 'r', "iayi");
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = sd_bus_message_append(reply, "i", AF_INET);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = sd_bus_message_append_array(reply, 'y', &rr->a.in_addr, sizeof(struct in_addr));
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = sd_bus_message_append(reply, "i", AF_INET6);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = sd_bus_message_append_array(reply, 'y', &rr->aaaa.in6_addr, sizeof(struct in6_addr));
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = sd_bus_message_append(reply, "i", ifindex);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic void bus_method_resolve_hostname_complete(DnsQuery *q) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *cname = NULL, *canonical = NULL;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen unsigned added = 0, i;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = sd_bus_message_new_method_return(q->request, &reply);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = sd_bus_message_open_container(reply, 'a', "(iayi)");
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = dns_question_matches_rr(q->question, answer->rrs[i]);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (r == 0) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen /* Hmm, if this is not an address record,
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen maybe it's a cname? If so, remember this */
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = dns_question_matches_cname(q->question, answer->rrs[i]);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen cname = dns_resource_record_ref(answer->rrs[i]);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = append_address(reply, answer->rrs[i], ifindex);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen canonical = dns_resource_record_ref(answer->rrs[i]);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen 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);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen /* This has a cname? Then update the query with the
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * new cname. */
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = dns_query_cname_redirect(q, cname->cname.name);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop on '%s'", q->request_hostname);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = sd_bus_reply_method_errno(q->request, -r, NULL);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen /* Before we restart the query, let's see if any of
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * the RRs we already got already answers our query */
e53fc357a9bb9d0a5362ccc4246d598cb0febd5eLennart Poettering r = dns_question_matches_rr(q->question, answer->rrs[i]);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = append_address(reply, answer->rrs[i], ifindex);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen canonical = dns_resource_record_ref(answer->rrs[i]);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen /* If we didn't find anything, then let's restart the
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * query, this time with the cname */
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_NAME_SERVERS, "No appropriate name servers or networks for name found");
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = sd_bus_reply_method_errno(q->request, -r, NULL);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen /* Return the precise spelling and uppercasing reported by the server */
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = sd_bus_message_append(reply, "s", DNS_RESOURCE_KEY_NAME(canonical->key));
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = sd_bus_send(q->manager->bus, reply, NULL);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_error("Failed to send hostname reply: %s", strerror(-r));
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen sd_bus_reply_method_errno(q->request, -r, NULL);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic int bus_method_resolve_hostname(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = sd_bus_message_read(message, "si", &hostname, &family);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen if (!IN_SET(family, AF_INET, AF_INET6, AF_UNSPEC))
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown address family %i", family);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid hostname '%s'", hostname);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen question = dns_question_new(family == AF_UNSPEC ? 2 : 1);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, hostname);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_AAAA, hostname);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen q->complete = bus_method_resolve_hostname_complete;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen sd_bus_error_setf(error, BUS_ERROR_NO_NAME_SERVERS, "No appropriate name servers or networks for name found");
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic void bus_method_resolve_address_complete(DnsQuery *q) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen unsigned added = 0, i;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = sd_bus_message_new_method_return(q->request, &reply);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = sd_bus_message_open_container(reply, 'a', "s");
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = dns_question_matches_rr(q->question, answer->rrs[i]);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = sd_bus_message_append(reply, "s", answer->rrs[i]->ptr.name);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen in_addr_to_string(q->request_family, &q->request_address, &ip);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "Address '%s' does not have any RR of requested type", ip);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = sd_bus_send(q->manager->bus, reply, NULL);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_error("Failed to send address reply: %s", strerror(-r));
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen sd_bus_reply_method_errno(q->request, -r, NULL);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic int bus_method_resolve_address(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen const void *d;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = sd_bus_message_read(message, "i", &family);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown address family %i", family);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = sd_bus_message_read_array(message, 'y', &d, &sz);
4df4fd1127df4b70f78d952a37a51a8c69e3243fTom Gundersen return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid address size");
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = sd_bus_message_read(message, "i", &ifindex);
de9b34b6d4250056ae2c483cf22844880504bcccTom Gundersen return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid interface index");
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen key = dns_resource_key_new_consume(DNS_CLASS_IN, DNS_TYPE_PTR, reverse);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen q->complete = bus_method_resolve_address_complete;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen sd_bus_error_setf(error, BUS_ERROR_NO_NAME_SERVERS, "No appropriate name servers or networks for name found");
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic void bus_method_resolve_record_complete(DnsQuery *q) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen unsigned added = 0, i;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = sd_bus_message_new_method_return(q->request, &reply);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = sd_bus_message_open_container(reply, 'a', "(qqay)");
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = dns_question_matches_rr(q->question, answer->rrs[i]);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = dns_packet_append_rr(p, answer->rrs[i], &start);
401cb61499f446c7c1579a160eeef435afd525fdTom Gundersen r = sd_bus_message_open_container(reply, 'r', "qqay");
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = sd_bus_message_append(reply, "qq", answer->rrs[i]->key->class, answer->rrs[i]->key->type);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = sd_bus_message_append_array(reply, 'y', DNS_PACKET_DATA(p) + start, p->size - start);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "Name '%s' does not have any RR of the requested type", q->request_hostname);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = sd_bus_send(q->manager->bus, reply, NULL);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_error("Failed to send record reply: %s", strerror(-r));
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen sd_bus_reply_method_errno(q->request, -r, NULL);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic int bus_method_resolve_record(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = sd_bus_message_read(message, "sqq", &name, &class, &type);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen key = dns_resource_key_new(class, type, name);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen q->complete = bus_method_resolve_record_complete;
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen sd_bus_error_setf(error, BUS_ERROR_NO_NAME_SERVERS, "No appropriate name servers or networks for name found");
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic const sd_bus_vtable resolve_vtable[] = {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen SD_BUS_METHOD("ResolveHostname", "si", "a(iayi)s", bus_method_resolve_hostname, SD_BUS_VTABLE_UNPRIVILEGED),
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen SD_BUS_METHOD("ResolveAddress", "iayi", "as", bus_method_resolve_address, SD_BUS_VTABLE_UNPRIVILEGED),
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen SD_BUS_METHOD("ResolveRecord", "sqq", "a(qqay)", bus_method_resolve_record, SD_BUS_VTABLE_UNPRIVILEGED),
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersenstatic int on_bus_retry(sd_event_source *s, usec_t usec, void *userdata) {
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen m->bus_retry_event_source = sd_event_source_unref(m->bus_retry_event_source);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen /* We failed to connect? Yuck, we must be in early
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * boot. Let's try in 5s again. As soon as we have
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen * kdbus we can stop doing this... */
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_debug("Failed to connect to bus, trying again in 5s: %s", strerror(-r));
ccc1002a1c510b7d4631833eaf60225f028f2280Tom Gundersen r = sd_event_add_time(m->event, &m->bus_retry_event_source, CLOCK_MONOTONIC, now(CLOCK_MONOTONIC) + 5*USEC_PER_SEC, 0, on_bus_retry, m);
d854ba50a82f28b776c670d27156f0e9881fde8aMartin Pitt log_error("Failed to install bus reconnect time event: %s", strerror(-r));
aa20f49a1c5816e6e7e97f2e2ba209be47f3c0a3Tom Gundersen r = sd_bus_add_object_vtable(m->bus, NULL, "/org/freedesktop/resolve1", "org.freedesktop.resolve1.Manager", resolve_vtable, m);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_error("Failed to register object: %s", strerror(-r));
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen r = sd_bus_request_name(m->bus, "org.freedesktop.resolve1", 0);
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_error("Failed to register name: %s", strerror(-r));
57fa1d094cd2c5ac68970526ad0a0754c548e75dTom Gundersen log_error("Failed to attach bus to event loop: %s", strerror(-r));