resolved-bus.c revision 966c66e34940001a40806142ecebaae61b478444
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering/***
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering This file is part of systemd.
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering Copyright 2014 Lennart Poettering
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering systemd is free software; you can redistribute it and/or modify it
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering under the terms of the GNU Lesser General Public License as published by
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering the Free Software Foundation; either version 2.1 of the License, or
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering (at your option) any later version.
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering systemd is distributed in the hope that it will be useful, but
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering WITHOUT ANY WARRANTY; without even the implied warranty of
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering Lesser General Public License for more details.
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering You should have received a copy of the GNU Lesser General Public License
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering along with systemd; If not, see <http://www.gnu.org/licenses/>.
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering***/
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering#include "bus-common-errors.h"
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering#include "bus-util.h"
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering#include "resolved-dns-domain.h"
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering#include "resolved-bus.h"
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering#include "resolved-def.h"
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poetteringstatic int reply_query_state(DnsQuery *q) {
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering _cleanup_free_ char *ip = NULL;
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering const char *name;
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering int r;
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering if (q->request_hostname)
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering name = q->request_hostname;
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering else {
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering r = in_addr_to_string(q->request_family, &q->request_address, &ip);
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering if (r < 0)
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering return r;
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering name = ip;
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering }
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering switch (q->state) {
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering case DNS_TRANSACTION_NO_SERVERS:
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering return sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_NAME_SERVERS, "No appropriate name servers or networks for name found");
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering case DNS_TRANSACTION_TIMEOUT:
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering return sd_bus_reply_method_errorf(q->request, SD_BUS_ERROR_TIMEOUT, "Query timed out");
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering case DNS_TRANSACTION_ATTEMPTS_MAX_REACHED:
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering return sd_bus_reply_method_errorf(q->request, SD_BUS_ERROR_TIMEOUT, "All attempts to contact name servers or networks failed");
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering case DNS_TRANSACTION_INVALID_REPLY:
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering return sd_bus_reply_method_errorf(q->request, BUS_ERROR_INVALID_REPLY, "Received invalid reply");
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering case DNS_TRANSACTION_RESOURCES:
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering return sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_RESOURCES, "Not enough resources");
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering case DNS_TRANSACTION_ABORTED:
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering return sd_bus_reply_method_errorf(q->request, BUS_ERROR_ABORTED, "Query aborted");
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering case DNS_TRANSACTION_FAILURE: {
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering if (q->answer_rcode == DNS_RCODE_NXDOMAIN)
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering sd_bus_error_setf(&error, _BUS_ERROR_DNS "NXDOMAIN", "'%s' not found", name);
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering else {
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering const char *rc, *n;
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering char p[3]; /* the rcode is 4 bits long */
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering rc = dns_rcode_to_string(q->answer_rcode);
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering if (!rc) {
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering sprintf(p, "%i", q->answer_rcode);
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering rc = p;
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering }
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering n = strjoina(_BUS_ERROR_DNS, rc);
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering sd_bus_error_setf(&error, n, "Could not resolve '%s', server or network returned error %s", name, rc);
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering }
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering return sd_bus_reply_method_error(q->request, &error);
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering }
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering case DNS_TRANSACTION_NULL:
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering case DNS_TRANSACTION_PENDING:
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering case DNS_TRANSACTION_SUCCESS:
default:
assert_not_reached("Impossible state");
}
}
static int append_address(sd_bus_message *reply, DnsResourceRecord *rr) {
int r;
assert(reply);
assert(rr);
r = sd_bus_message_open_container(reply, 'r', "iay");
if (r < 0)
return r;
if (rr->key->type == DNS_TYPE_A) {
r = sd_bus_message_append(reply, "i", AF_INET);
if (r < 0)
return r;
r = sd_bus_message_append_array(reply, 'y', &rr->a.in_addr, sizeof(struct in_addr));
} else if (rr->key->type == DNS_TYPE_AAAA) {
r = sd_bus_message_append(reply, "i", AF_INET6);
if (r < 0)
return r;
r = sd_bus_message_append_array(reply, 'y', &rr->aaaa.in6_addr, sizeof(struct in6_addr));
} else
return -EAFNOSUPPORT;
if (r < 0)
return r;
r = sd_bus_message_close_container(reply);
if (r < 0)
return r;
return 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++;
}
// what about the cache?
/* 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);
}
static int check_ifindex_flags(int ifindex, uint64_t *flags, sd_bus_error *error) {
assert(flags);
if (ifindex < 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid interface index");
if (*flags & ~SD_RESOLVED_FLAGS_ALL)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid flags parameter");
if (*flags == 0)
*flags = SD_RESOLVED_FLAGS_DEFAULT;
return 0;
}
static int bus_method_resolve_hostname(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(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, 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;
}
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);
}
static int bus_method_resolve_address(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(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, 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;
}
static void bus_method_resolve_record_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', "(qqay)");
if (r < 0)
goto finish;
if (q->answer) {
answer = dns_answer_ref(q->answer);
for (i = 0; i < answer->n_rrs; i++) {
_cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
size_t start;
r = dns_question_matches_rr(q->question, answer->rrs[i]);
if (r < 0)
goto finish;
if (r == 0)
continue;
r = dns_packet_new(&p, DNS_PROTOCOL_DNS, 0);
if (r < 0)
goto finish;
r = dns_packet_append_rr(p, answer->rrs[i], &start);
if (r < 0)
goto finish;
r = sd_bus_message_open_container(reply, 'r', "qqay");
if (r < 0)
goto finish;
r = sd_bus_message_append(reply, "qq", answer->rrs[i]->key->class, answer->rrs[i]->key->type);
if (r < 0)
goto finish;
r = sd_bus_message_append_array(reply, 'y', DNS_PACKET_DATA(p) + start, p->size - start);
if (r < 0)
goto finish;
r = sd_bus_message_close_container(reply);
if (r < 0)
goto finish;
added ++;
}
}
if (added <= 0) {
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);
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 record reply: %m");
sd_bus_reply_method_errno(q->request, -r, NULL);
}
dns_query_free(q);
}
static int bus_method_resolve_record(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;
Manager *m = userdata;
uint16_t class, type;
const char *name;
int r, ifindex;
uint64_t flags;
DnsQuery *q;
assert(message);
assert(m);
r = sd_bus_message_read(message, "isqqt", &ifindex, &name, &class, &type, &flags);
if (r < 0)
return r;
r = dns_name_normalize(name, NULL);
if (r < 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid name '%s'", name);
r = check_ifindex_flags(ifindex, &flags, error);
if (r < 0)
return r;
question = dns_question_new(1);
if (!question)
return -ENOMEM;
key = dns_resource_key_new(class, type, name);
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_hostname = name;
q->complete = bus_method_resolve_record_complete;
r = dns_query_bus_track(q, 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;
}
static const sd_bus_vtable resolve_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_METHOD("ResolveHostname", "isit", "ia(iay)st", bus_method_resolve_hostname, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("ResolveAddress", "iiayt", "iast", bus_method_resolve_address, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("ResolveRecord", "isqqt", "ia(qqay)t", bus_method_resolve_record, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_VTABLE_END,
};
static int on_bus_retry(sd_event_source *s, usec_t usec, void *userdata) {
Manager *m = userdata;
assert(s);
assert(m);
m->bus_retry_event_source = sd_event_source_unref(m->bus_retry_event_source);
manager_connect_bus(m);
return 0;
}
static int match_prepare_for_sleep(sd_bus_message *message, void *userdata, sd_bus_error *ret_error) {
Manager *m = userdata;
int b, r;
assert(message);
assert(m);
r = sd_bus_message_read(message, "b", &b);
if (r < 0) {
log_debug_errno(r, "Failed to parse PrepareForSleep signal: %m");
return 0;
}
if (b)
return 0;
log_debug("Coming back from suspend, verifying all RRs...");
manager_verify_all(m);
return 0;
}
int manager_connect_bus(Manager *m) {
int r;
assert(m);
if (m->bus)
return 0;
r = sd_bus_default_system(&m->bus);
if (r < 0) {
/* We failed to connect? Yuck, we must be in early
* boot. Let's try in 5s again. As soon as we have
* kdbus we can stop doing this... */
log_debug_errno(r, "Failed to connect to bus, trying again in 5s: %m");
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);
if (r < 0)
return log_error_errno(r, "Failed to install bus reconnect time event: %m");
return 0;
}
r = sd_bus_add_object_vtable(m->bus, NULL, "/org/freedesktop/resolve1", "org.freedesktop.resolve1.Manager", resolve_vtable, m);
if (r < 0)
return log_error_errno(r, "Failed to register object: %m");
r = sd_bus_request_name(m->bus, "org.freedesktop.resolve1", 0);
if (r < 0)
return log_error_errno(r, "Failed to register name: %m");
r = sd_bus_attach_event(m->bus, m->event, 0);
if (r < 0)
return log_error_errno(r, "Failed to attach bus to event loop: %m");
r = sd_bus_add_match(m->bus, &m->prepare_for_sleep_slot,
"type='signal',"
"sender='org.freedesktop.login1',"
"interface='org.freedesktop.login1.Manager',"
"member='PrepareForSleep',"
"path='/org/freedesktop/login1'",
match_prepare_for_sleep,
m);
if (r < 0)
log_error_errno(r, "Failed to add match for PrepareForSleep: %m");
return 0;
}