resolved-bus.c revision e40f0647b48b57af5e874501ad9c05dc459108de
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen/***
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen This file is part of systemd.
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen Copyright 2014 Lennart Poettering
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen systemd is free software; you can redistribute it and/or modify it
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen under the terms of the GNU Lesser General Public License as published by
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen the Free Software Foundation; either version 2.1 of the License, or
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen (at your option) any later version.
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen systemd is distributed in the hope that it will be useful, but
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen WITHOUT ANY WARRANTY; without even the implied warranty of
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen Lesser General Public License for more details.
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen You should have received a copy of the GNU Lesser General Public License
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen along with systemd; If not, see <http://www.gnu.org/licenses/>.
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen***/
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen#include "alloc-util.h"
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen#include "bus-common-errors.h"
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen#include "bus-util.h"
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen#include "dns-domain.h"
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen#include "resolved-bus.h"
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen#include "resolved-def.h"
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersenstatic int reply_query_state(DnsQuery *q) {
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen switch (q->state) {
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen case DNS_TRANSACTION_NO_SERVERS:
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen return sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_NAME_SERVERS, "No appropriate name servers or networks for name found");
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen case DNS_TRANSACTION_TIMEOUT:
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen return sd_bus_reply_method_errorf(q->request, SD_BUS_ERROR_TIMEOUT, "Query timed out");
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen case DNS_TRANSACTION_ATTEMPTS_MAX_REACHED:
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen return sd_bus_reply_method_errorf(q->request, SD_BUS_ERROR_TIMEOUT, "All attempts to contact name servers or networks failed");
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen case DNS_TRANSACTION_INVALID_REPLY:
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen return sd_bus_reply_method_errorf(q->request, BUS_ERROR_INVALID_REPLY, "Received invalid reply");
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen case DNS_TRANSACTION_RESOURCES:
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen return sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_RESOURCES, "Not enough resources");
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen case DNS_TRANSACTION_ABORTED:
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen return sd_bus_reply_method_errorf(q->request, BUS_ERROR_ABORTED, "Query aborted");
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen case DNS_TRANSACTION_DNSSEC_FAILED:
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen return sd_bus_reply_method_errorf(q->request, BUS_ERROR_DNSSEC_FAILED, "DNSSEC validation failed: %s",
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen dnssec_result_to_string(q->answer_dnssec_result));
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen case DNS_TRANSACTION_NO_TRUST_ANCHOR:
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen return sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_TRUST_ANCHOR, "No suitable trust anchor known");
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen case DNS_TRANSACTION_RR_TYPE_UNSUPPORTED:
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen return sd_bus_reply_method_errorf(q->request, BUS_ERROR_RR_TYPE_UNSUPPORTED, "Server does not support requested resource record type");
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen case DNS_TRANSACTION_RCODE_FAILURE: {
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (q->answer_rcode == DNS_RCODE_NXDOMAIN)
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen sd_bus_error_setf(&error, _BUS_ERROR_DNS "NXDOMAIN", "'%s' not found", dns_query_string(q));
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen else {
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen const char *rc, *n;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen char p[3]; /* the rcode is 4 bits long */
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen rc = dns_rcode_to_string(q->answer_rcode);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (!rc) {
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen sprintf(p, "%i", q->answer_rcode);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen rc = p;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen }
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen n = strjoina(_BUS_ERROR_DNS, rc);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen sd_bus_error_setf(&error, n, "Could not resolve '%s', server or network returned error %s", dns_query_string(q), rc);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen }
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen return sd_bus_reply_method_error(q->request, &error);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen }
52efd56a6369e19c2400a42981a197cd2eef924aLennart Poettering
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen case DNS_TRANSACTION_NULL:
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen case DNS_TRANSACTION_PENDING:
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen case DNS_TRANSACTION_VALIDATING:
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen case DNS_TRANSACTION_SUCCESS:
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen default:
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen assert_not_reached("Impossible state");
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen }
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen}
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersenstatic int append_address(sd_bus_message *reply, DnsResourceRecord *rr, int ifindex) {
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen int r;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen assert(reply);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen assert(rr);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen r = sd_bus_message_open_container(reply, 'r', "iiay");
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (r < 0)
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen return r;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen r = sd_bus_message_append(reply, "i", ifindex);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (r < 0)
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen return r;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (rr->key->type == DNS_TYPE_A) {
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen r = sd_bus_message_append(reply, "i", AF_INET);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (r < 0)
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen return r;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen r = sd_bus_message_append_array(reply, 'y', &rr->a.in_addr, sizeof(struct in_addr));
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen } else if (rr->key->type == DNS_TYPE_AAAA) {
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen r = sd_bus_message_append(reply, "i", AF_INET6);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (r < 0)
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen return r;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen r = sd_bus_message_append_array(reply, 'y', &rr->aaaa.in6_addr, sizeof(struct in6_addr));
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen } else
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen return -EAFNOSUPPORT;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (r < 0)
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen return r;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen r = sd_bus_message_close_container(reply);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (r < 0)
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen return r;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen return 0;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen}
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersenstatic void bus_method_resolve_hostname_complete(DnsQuery *q) {
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *canonical = NULL;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen unsigned added = 0;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen int r;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen assert(q);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (q->state != DNS_TRANSACTION_SUCCESS) {
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen r = reply_query_state(q);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen goto finish;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen }
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen r = dns_query_process_cname(q);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (r == -ELOOP) {
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_query_string(q));
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen goto finish;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen }
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (r < 0)
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen goto finish;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (r == DNS_QUERY_RESTARTED) /* This was a cname, and the query was restarted. */
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen return;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen r = sd_bus_message_new_method_return(q->request, &reply);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (r < 0)
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen goto finish;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen r = sd_bus_message_open_container(reply, 'a', "(iiay)");
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (r < 0)
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen goto finish;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (q->answer) {
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen DnsResourceRecord *rr;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen int ifindex;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen DNS_ANSWER_FOREACH_IFINDEX(rr, ifindex, q->answer) {
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen DnsQuestion *question;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen question = dns_query_question_for_protocol(q, q->answer_protocol);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen r = dns_question_matches_rr(question, rr, DNS_SEARCH_DOMAIN_NAME(q->answer_search_domain));
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (r < 0)
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen goto finish;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (r == 0)
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen continue;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen r = append_address(reply, rr, ifindex);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (r < 0)
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen goto finish;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (!canonical)
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen canonical = dns_resource_record_ref(rr);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen added ++;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen }
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen }
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (added <= 0) {
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "'%s' does not have any RR of the requested type", dns_query_string(q));
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen goto finish;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen }
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen r = sd_bus_message_close_container(reply);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (r < 0)
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen goto finish;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen /* Return the precise spelling and uppercasing and CNAME target reported by the server */
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen assert(canonical);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen r = sd_bus_message_append(
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen reply, "st",
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen DNS_RESOURCE_KEY_NAME(canonical->key),
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen SD_RESOLVED_FLAGS_MAKE(q->answer_protocol, q->answer_family, q->answer_authenticated));
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (r < 0)
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen goto finish;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen r = sd_bus_send(q->manager->bus, reply, NULL);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersenfinish:
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (r < 0) {
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen log_error_errno(r, "Failed to send hostname reply: %m");
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen sd_bus_reply_method_errno(q->request, r, NULL);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen }
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen dns_query_free(q);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen}
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersenstatic int check_ifindex_flags(int ifindex, uint64_t *flags, uint64_t ok, sd_bus_error *error) {
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen assert(flags);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (ifindex < 0)
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid interface index");
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (*flags & ~(SD_RESOLVED_PROTOCOLS_ALL|SD_RESOLVED_NO_CNAME|ok))
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid flags parameter");
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if ((*flags & SD_RESOLVED_PROTOCOLS_ALL) == 0) /* If no protocol is enabled, enable all */
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen *flags |= SD_RESOLVED_PROTOCOLS_ALL;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen return 0;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen}
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersenstatic int bus_method_resolve_hostname(sd_bus_message *message, void *userdata, sd_bus_error *error) {
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen _cleanup_(dns_question_unrefp) DnsQuestion *question_idna = NULL, *question_utf8 = NULL;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen Manager *m = userdata;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen const char *hostname;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen int family, ifindex;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen uint64_t flags;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen DnsQuery *q;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen int r;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen assert(message);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen assert(m);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen assert_cc(sizeof(int) == sizeof(int32_t));
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen r = sd_bus_message_read(message, "isit", &ifindex, &hostname, &family, &flags);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (r < 0)
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen return r;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (!IN_SET(family, AF_INET, AF_INET6, AF_UNSPEC))
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown address family %i", family);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen r = dns_name_is_valid(hostname);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (r < 0)
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen return r;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (r == 0)
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid hostname '%s'", hostname);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen r = check_ifindex_flags(ifindex, &flags, SD_RESOLVED_NO_SEARCH, error);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (r < 0)
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen return r;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen r = dns_question_new_address(&question_utf8, family, hostname, false);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (r < 0)
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen return r;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen r = dns_question_new_address(&question_idna, family, hostname, true);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (r < 0)
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen return r;
52efd56a6369e19c2400a42981a197cd2eef924aLennart Poettering
52efd56a6369e19c2400a42981a197cd2eef924aLennart Poettering r = dns_query_new(m, &q, question_utf8, question_idna, ifindex, flags);
52efd56a6369e19c2400a42981a197cd2eef924aLennart Poettering if (r < 0)
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen return r;
52efd56a6369e19c2400a42981a197cd2eef924aLennart Poettering
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen q->request = sd_bus_message_ref(message);
52efd56a6369e19c2400a42981a197cd2eef924aLennart Poettering q->request_family = family;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen q->complete = bus_method_resolve_hostname_complete;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen r = dns_query_bus_track(q, message);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (r < 0)
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen goto fail;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen r = dns_query_go(q);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (r < 0)
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen goto fail;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen return 1;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersenfail:
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen dns_query_free(q);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen return r;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen}
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersenstatic void bus_method_resolve_address_complete(DnsQuery *q) {
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen DnsQuestion *question;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen DnsResourceRecord *rr;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen unsigned added = 0;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen int ifindex, r;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen assert(q);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (q->state != DNS_TRANSACTION_SUCCESS) {
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen r = reply_query_state(q);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen goto finish;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen }
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen r = dns_query_process_cname(q);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (r == -ELOOP) {
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_query_string(q));
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen goto finish;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen }
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (r < 0)
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen goto finish;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (r == DNS_QUERY_RESTARTED) /* This was a cname, and the query was restarted. */
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen return;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
7e518afab9fb55b8052f68888210927259275560Thomas Hindoe Paaboel Andersen r = sd_bus_message_new_method_return(q->request, &reply);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (r < 0)
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen goto finish;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen r = sd_bus_message_open_container(reply, 'a', "(is)");
1fa2f38f0f011010bf57522b42fcc168856a7003Zbigniew Jędrzejewski-Szmek if (r < 0)
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen goto finish;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen question = dns_query_question_for_protocol(q, q->answer_protocol);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen DNS_ANSWER_FOREACH_IFINDEX(rr, ifindex, q->answer) {
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen r = dns_question_matches_rr(question, rr, NULL);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (r < 0)
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen goto finish;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (r == 0)
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen continue;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen r = sd_bus_message_append(reply, "(is)", ifindex, rr->ptr.name);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (r < 0)
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen goto finish;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen added ++;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen }
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (added <= 0) {
f0c4b1c3fd827b429ba36aa45fd39e0a023cbf2cTom Gundersen _cleanup_free_ char *ip = NULL;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen in_addr_to_string(q->request_family, &q->request_address, &ip);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "Address '%s' does not have any RR of requested type", strna(ip));
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen goto finish;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen }
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen r = sd_bus_message_close_container(reply);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (r < 0)
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen goto finish;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen r = sd_bus_message_append(reply, "t", SD_RESOLVED_FLAGS_MAKE(q->answer_protocol, q->answer_family, q->answer_authenticated));
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (r < 0)
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen goto finish;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen r = sd_bus_send(q->manager->bus, reply, NULL);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersenfinish:
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (r < 0) {
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen log_error_errno(r, "Failed to send address reply: %m");
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen sd_bus_reply_method_errno(q->request, r, NULL);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen }
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen dns_query_free(q);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen}
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersenstatic int bus_method_resolve_address(sd_bus_message *message, void *userdata, sd_bus_error *error) {
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen Manager *m = userdata;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen int family, ifindex;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen uint64_t flags;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen const void *d;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen DnsQuery *q;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen size_t sz;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen int r;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen assert(message);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen assert(m);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen assert_cc(sizeof(int) == sizeof(int32_t));
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen r = sd_bus_message_read(message, "ii", &ifindex, &family);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (r < 0)
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen return r;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (!IN_SET(family, AF_INET, AF_INET6))
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown address family %i", family);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen r = sd_bus_message_read_array(message, 'y', &d, &sz);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (r < 0)
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen return r;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (sz != FAMILY_ADDRESS_SIZE(family))
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid address size");
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen r = sd_bus_message_read(message, "t", &flags);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (r < 0)
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen return r;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen r = check_ifindex_flags(ifindex, &flags, 0, error);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (r < 0)
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen return r;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen r = dns_question_new_reverse(&question, family, d);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (r < 0)
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen return r;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen r = dns_query_new(m, &q, question, question, ifindex, flags|SD_RESOLVED_NO_SEARCH);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (r < 0)
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen return r;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen q->request = sd_bus_message_ref(message);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen q->request_family = family;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen memcpy(&q->request_address, d, sz);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen q->complete = bus_method_resolve_address_complete;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen r = dns_query_bus_track(q, message);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (r < 0)
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen goto fail;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen r = dns_query_go(q);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (r < 0)
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen goto fail;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen return 1;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersenfail:
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen dns_query_free(q);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen return r;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen}
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersenstatic int bus_message_append_rr(sd_bus_message *m, DnsResourceRecord *rr, int ifindex) {
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen int r;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen assert(m);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen assert(rr);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen r = sd_bus_message_open_container(m, 'r', "iqqay");
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (r < 0)
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen return r;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen r = sd_bus_message_append(m, "iqq",
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen ifindex,
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen rr->key->class,
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen rr->key->type);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (r < 0)
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen return r;
8927b1dad2d4a7330174cb924090b4635a2547fbDavid Herrmann
8927b1dad2d4a7330174cb924090b4635a2547fbDavid Herrmann r = dns_resource_record_to_wire_format(rr, false);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (r < 0)
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen return r;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen r = sd_bus_message_append_array(m, 'y', rr->wire_format, rr->wire_format_size);
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen if (r < 0)
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen return r;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen return sd_bus_message_close_container(m);
8927b1dad2d4a7330174cb924090b4635a2547fbDavid Herrmann}
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersenstatic void bus_method_resolve_record_complete(DnsQuery *q) {
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen DnsResourceRecord *rr;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen DnsQuestion *question;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen unsigned added = 0;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen int ifindex;
23fbe14f503c1e98292efc4ba1238adb7dc38d80Tom Gundersen int r;
assert(q);
if (q->state != DNS_TRANSACTION_SUCCESS) {
r = reply_query_state(q);
goto finish;
}
r = dns_query_process_cname(q);
if (r == -ELOOP) {
r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_query_string(q));
goto finish;
}
if (r < 0)
goto finish;
if (r == DNS_QUERY_RESTARTED) /* This was a cname, and the query was restarted. */
return;
r = sd_bus_message_new_method_return(q->request, &reply);
if (r < 0)
goto finish;
r = sd_bus_message_open_container(reply, 'a', "(iqqay)");
if (r < 0)
goto finish;
question = dns_query_question_for_protocol(q, q->answer_protocol);
DNS_ANSWER_FOREACH_IFINDEX(rr, ifindex, q->answer) {
r = dns_question_matches_rr(question, rr, NULL);
if (r < 0)
goto finish;
if (r == 0)
continue;
r = bus_message_append_rr(reply, rr, ifindex);
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", dns_query_string(q));
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, q->answer_authenticated));
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);
assert_cc(sizeof(int) == sizeof(int32_t));
r = sd_bus_message_read(message, "isqqt", &ifindex, &name, &class, &type, &flags);
if (r < 0)
return r;
r = dns_name_is_valid(name);
if (r < 0)
return r;
if (r == 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid name '%s'", name);
if (!dns_type_is_valid_query(type))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Specified resource record type %" PRIu16 " may not be used in a query.", type);
if (dns_type_is_obsolete(type))
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Specified DNS resource record type %" PRIu16 " is obsolete.", type);
r = check_ifindex_flags(ifindex, &flags, 0, 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, question, ifindex, flags|SD_RESOLVED_NO_SEARCH);
if (r < 0)
return r;
q->request = sd_bus_message_ref(message);
q->complete = bus_method_resolve_record_complete;
r = dns_query_bus_track(q, message);
if (r < 0)
goto fail;
r = dns_query_go(q);
if (r < 0)
goto fail;
return 1;
fail:
dns_query_free(q);
return r;
}
static int append_srv(DnsQuery *q, sd_bus_message *reply, DnsResourceRecord *rr) {
_cleanup_(dns_resource_record_unrefp) DnsResourceRecord *canonical = NULL;
DnsQuery *aux;
int r;
assert(q);
assert(reply);
assert(rr);
assert(rr->key);
if (rr->key->type != DNS_TYPE_SRV)
return 0;
if ((q->flags & SD_RESOLVED_NO_ADDRESS) == 0) {
/* First, let's see if we could find an appropriate A or AAAA
* record for the SRV record */
LIST_FOREACH(auxiliary_queries, aux, q->auxiliary_queries) {
DnsResourceRecord *zz;
DnsQuestion *question;
if (aux->state != DNS_TRANSACTION_SUCCESS)
continue;
if (aux->auxiliary_result != 0)
continue;
question = dns_query_question_for_protocol(aux, aux->answer_protocol);
r = dns_name_equal(dns_question_first_name(question), rr->srv.name);
if (r < 0)
return r;
if (r == 0)
continue;
DNS_ANSWER_FOREACH(zz, aux->answer) {
r = dns_question_matches_rr(question, zz, NULL);
if (r < 0)
return r;
if (r == 0)
continue;
canonical = dns_resource_record_ref(zz);
break;
}
if (canonical)
break;
}
/* Is there are successful A/AAAA lookup for this SRV RR? If not, don't add it */
if (!canonical)
return 0;
}
r = sd_bus_message_open_container(reply, 'r', "qqqsa(iiay)s");
if (r < 0)
return r;
r = sd_bus_message_append(
reply,
"qqqs",
rr->srv.priority, rr->srv.weight, rr->srv.port, rr->srv.name);
if (r < 0)
return r;
r = sd_bus_message_open_container(reply, 'a', "(iiay)");
if (r < 0)
return r;
if ((q->flags & SD_RESOLVED_NO_ADDRESS) == 0) {
LIST_FOREACH(auxiliary_queries, aux, q->auxiliary_queries) {
DnsResourceRecord *zz;
DnsQuestion *question;
int ifindex;
if (aux->state != DNS_TRANSACTION_SUCCESS)
continue;
if (aux->auxiliary_result != 0)
continue;
question = dns_query_question_for_protocol(aux, aux->answer_protocol);
r = dns_name_equal(dns_question_first_name(question), rr->srv.name);
if (r < 0)
return r;
if (r == 0)
continue;
DNS_ANSWER_FOREACH_IFINDEX(zz, ifindex, aux->answer) {
r = dns_question_matches_rr(question, zz, NULL);
if (r < 0)
return r;
if (r == 0)
continue;
r = append_address(reply, zz, ifindex);
if (r < 0)
return r;
}
}
}
r = sd_bus_message_close_container(reply);
if (r < 0)
return r;
/* Note that above we appended the hostname as encoded in the
* SRV, and here the canonical hostname this maps to. */
r = sd_bus_message_append(reply, "s", canonical ? DNS_RESOURCE_KEY_NAME(canonical->key) : rr->srv.name);
if (r < 0)
return r;
r = sd_bus_message_close_container(reply);
if (r < 0)
return r;
return 1;
}
static int append_txt(sd_bus_message *reply, DnsResourceRecord *rr) {
DnsTxtItem *i;
int r;
assert(reply);
assert(rr);
assert(rr->key);
if (rr->key->type != DNS_TYPE_TXT)
return 0;
LIST_FOREACH(items, i, rr->txt.items) {
if (i->length <= 0)
continue;
r = sd_bus_message_append_array(reply, 'y', i->data, i->length);
if (r < 0)
return r;
}
return 1;
}
static void resolve_service_all_complete(DnsQuery *q) {
_cleanup_(dns_resource_record_unrefp) DnsResourceRecord *canonical = NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_free_ char *name = NULL, *type = NULL, *domain = NULL;
DnsQuestion *question;
DnsResourceRecord *rr;
unsigned added = 0;
DnsQuery *aux;
int r;
assert(q);
if (q->block_all_complete > 0)
return;
if ((q->flags & SD_RESOLVED_NO_ADDRESS) == 0) {
DnsQuery *bad = NULL;
bool have_success = false;
LIST_FOREACH(auxiliary_queries, aux, q->auxiliary_queries) {
switch (aux->state) {
case DNS_TRANSACTION_PENDING:
/* If an auxiliary query is still pending, let's wait */
return;
case DNS_TRANSACTION_SUCCESS:
if (aux->auxiliary_result == 0)
have_success = true;
else
bad = aux;
break;
default:
bad = aux;
break;
}
}
if (!have_success) {
/* We can only return one error, hence pick the last error we encountered */
assert(bad);
if (bad->state == DNS_TRANSACTION_SUCCESS) {
assert(bad->auxiliary_result != 0);
if (bad->auxiliary_result == -ELOOP) {
r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_query_string(bad));
goto finish;
}
r = bad->auxiliary_result;
goto finish;
}
r = reply_query_state(bad);
goto finish;
}
}
r = sd_bus_message_new_method_return(q->request, &reply);
if (r < 0)
goto finish;
r = sd_bus_message_open_container(reply, 'a', "(qqqsa(iiay)s)");
if (r < 0)
goto finish;
question = dns_query_question_for_protocol(q, q->answer_protocol);
DNS_ANSWER_FOREACH(rr, q->answer) {
r = dns_question_matches_rr(question, rr, NULL);
if (r < 0)
goto finish;
if (r == 0)
continue;
r = append_srv(q, reply, rr);
if (r < 0)
goto finish;
if (r == 0) /* not an SRV record */
continue;
if (!canonical)
canonical = dns_resource_record_ref(rr);
added++;
}
if (added <= 0) {
r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "'%s' does not have any RR of the requested type", dns_query_string(q));
goto finish;
}
r = sd_bus_message_close_container(reply);
if (r < 0)
goto finish;
r = sd_bus_message_open_container(reply, 'a', "ay");
if (r < 0)
goto finish;
DNS_ANSWER_FOREACH(rr, q->answer) {
r = dns_question_matches_rr(question, rr, NULL);
if (r < 0)
goto finish;
if (r == 0)
continue;
r = append_txt(reply, rr);
if (r < 0)
goto finish;
}
r = sd_bus_message_close_container(reply);
if (r < 0)
goto finish;
assert(canonical);
r = dns_service_split(DNS_RESOURCE_KEY_NAME(canonical->key), &name, &type, &domain);
if (r < 0)
goto finish;
r = sd_bus_message_append(
reply,
"ssst",
name, type, domain,
SD_RESOLVED_FLAGS_MAKE(q->answer_protocol, q->answer_family, q->answer_authenticated));
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 service reply: %m");
sd_bus_reply_method_errno(q->request, r, NULL);
}
dns_query_free(q);
}
static void resolve_service_hostname_complete(DnsQuery *q) {
int r;
assert(q);
assert(q->auxiliary_for);
if (q->state != DNS_TRANSACTION_SUCCESS) {
resolve_service_all_complete(q->auxiliary_for);
return;
}
r = dns_query_process_cname(q);
if (r == DNS_QUERY_RESTARTED) /* This was a cname, and the query was restarted. */
return;
/* This auxiliary lookup is finished or failed, let's see if all are finished now. */
q->auxiliary_result = r;
resolve_service_all_complete(q->auxiliary_for);
}
static int resolve_service_hostname(DnsQuery *q, DnsResourceRecord *rr, int ifindex) {
_cleanup_(dns_question_unrefp) DnsQuestion *question = NULL;
DnsQuery *aux;
int r;
assert(q);
assert(rr);
assert(rr->key);
assert(rr->key->type == DNS_TYPE_SRV);
/* OK, we found an SRV record for the service. Let's resolve
* the hostname included in it */
r = dns_question_new_address(&question, q->request_family, rr->srv.name, false);
if (r < 0)
return r;
r = dns_query_new(q->manager, &aux, question, question, ifindex, q->flags|SD_RESOLVED_NO_SEARCH);
if (r < 0)
return r;
aux->request_family = q->request_family;
aux->complete = resolve_service_hostname_complete;
r = dns_query_make_auxiliary(aux, q);
if (r == -EAGAIN) {
/* Too many auxiliary lookups? If so, don't complain,
* let's just not add this one, we already have more
* than enough */
dns_query_free(aux);
return 0;
}
if (r < 0)
goto fail;
/* Note that auxiliary queries do not track the original bus
* client, only the primary request does that. */
r = dns_query_go(aux);
if (r < 0)
goto fail;
return 1;
fail:
dns_query_free(aux);
return r;
}
static void bus_method_resolve_service_complete(DnsQuery *q) {
bool has_root_domain = false;
DnsResourceRecord *rr;
DnsQuestion *question;
unsigned found = 0;
int ifindex, r;
assert(q);
if (q->state != DNS_TRANSACTION_SUCCESS) {
r = reply_query_state(q);
goto finish;
}
r = dns_query_process_cname(q);
if (r == -ELOOP) {
r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_query_string(q));
goto finish;
}
if (r < 0)
goto finish;
if (r == DNS_QUERY_RESTARTED) /* This was a cname, and the query was restarted. */
return;
question = dns_query_question_for_protocol(q, q->answer_protocol);
DNS_ANSWER_FOREACH_IFINDEX(rr, ifindex, q->answer) {
r = dns_question_matches_rr(question, rr, NULL);
if (r < 0)
goto finish;
if (r == 0)
continue;
if (rr->key->type != DNS_TYPE_SRV)
continue;
if (dns_name_is_root(rr->srv.name)) {
has_root_domain = true;
continue;
}
if ((q->flags & SD_RESOLVED_NO_ADDRESS) == 0) {
q->block_all_complete ++;
r = resolve_service_hostname(q, rr, ifindex);
q->block_all_complete --;
if (r < 0)
goto finish;
}
found++;
}
if (has_root_domain && found <= 0) {
/* If there's exactly one SRV RR and it uses
* the root domain as host name, then the
* service is explicitly not offered on the
* domain. Report this as a recognizable
* error. See RFC 2782, Section "Usage
* Rules". */
r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_SERVICE, "'%s' does not provide the requested service", dns_query_string(q));
goto finish;
}
if (found <= 0) {
r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "'%s' does not have any RR of the requested type", dns_query_string(q));
goto finish;
}
/* Maybe we are already finished? check now... */
resolve_service_all_complete(q);
return;
finish:
if (r < 0) {
log_error_errno(r, "Failed to send service reply: %m");
sd_bus_reply_method_errno(q->request, r, NULL);
}
dns_query_free(q);
}
static int bus_method_resolve_service(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_(dns_question_unrefp) DnsQuestion *question_idna = NULL, *question_utf8 = NULL;
const char *name, *type, *domain;
_cleanup_free_ char *n = NULL;
Manager *m = userdata;
int family, ifindex;
uint64_t flags;
DnsQuery *q;
int r;
assert(message);
assert(m);
assert_cc(sizeof(int) == sizeof(int32_t));
r = sd_bus_message_read(message, "isssit", &ifindex, &name, &type, &domain, &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);
if (isempty(name))
name = NULL;
else if (!dns_service_name_is_valid(name))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid service name '%s'", name);
if (isempty(type))
type = NULL;
else if (!dns_srv_type_is_valid(type))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid SRV service type '%s'", type);
r = dns_name_is_valid(domain);
if (r < 0)
return r;
if (r == 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid domain '%s'", domain);
if (name && !type)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Service name cannot be specified without service type.");
r = check_ifindex_flags(ifindex, &flags, SD_RESOLVED_NO_TXT|SD_RESOLVED_NO_ADDRESS, error);
if (r < 0)
return r;
r = dns_question_new_service(&question_utf8, name, type, domain, !(flags & SD_RESOLVED_NO_TXT), false);
if (r < 0)
return r;
r = dns_question_new_service(&question_idna, name, type, domain, !(flags & SD_RESOLVED_NO_TXT), true);
if (r < 0)
return r;
r = dns_query_new(m, &q, question_utf8, question_idna, ifindex, flags|SD_RESOLVED_NO_SEARCH);
if (r < 0)
return r;
q->request = sd_bus_message_ref(message);
q->request_family = family;
q->complete = bus_method_resolve_service_complete;
r = dns_query_bus_track(q, message);
if (r < 0)
goto fail;
r = dns_query_go(q);
if (r < 0)
goto fail;
return 1;
fail:
dns_query_free(q);
return r;
}
static int append_dns_server(sd_bus_message *reply, DnsServer *s) {
int r;
assert(reply);
assert(s);
r = sd_bus_message_open_container(reply, 'r', "iiay");
if (r < 0)
return r;
r = sd_bus_message_append(reply, "ii", s->link ? s->link->ifindex : 0, s->family);
if (r < 0)
return r;
r = sd_bus_message_append_array(reply, 'y', &s->address, FAMILY_ADDRESS_SIZE(s->family));
if (r < 0)
return r;
return sd_bus_message_close_container(reply);
}
static int bus_property_get_dns_servers(
sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *reply,
void *userdata,
sd_bus_error *error) {
Manager *m = userdata;
unsigned c = 0;
DnsServer *s;
Iterator i;
Link *l;
int r;
assert(reply);
assert(m);
r = sd_bus_message_open_container(reply, 'a', "(iiay)");
if (r < 0)
return r;
LIST_FOREACH(servers, s, m->dns_servers) {
r = append_dns_server(reply, s);
if (r < 0)
return r;
c++;
}
HASHMAP_FOREACH(l, m->links, i) {
LIST_FOREACH(servers, s, l->dns_servers) {
r = append_dns_server(reply, s);
if (r < 0)
return r;
c++;
}
}
if (c == 0) {
LIST_FOREACH(servers, s, m->fallback_dns_servers) {
r = append_dns_server(reply, s);
if (r < 0)
return r;
}
}
return sd_bus_message_close_container(reply);
}
static int bus_property_get_search_domains(
sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *reply,
void *userdata,
sd_bus_error *error) {
Manager *m = userdata;
DnsSearchDomain *d;
Iterator i;
Link *l;
int r;
assert(reply);
assert(m);
r = sd_bus_message_open_container(reply, 'a', "(is)");
if (r < 0)
return r;
LIST_FOREACH(domains, d, m->search_domains) {
r = sd_bus_message_append(reply, "(is)", 0, d->name);
if (r < 0)
return r;
}
HASHMAP_FOREACH(l, m->links, i) {
LIST_FOREACH(domains, d, l->search_domains) {
r = sd_bus_message_append(reply, "is", l->ifindex, d->name);
if (r < 0)
return r;
}
}
return sd_bus_message_close_container(reply);
}
static int bus_property_get_transaction_statistics(
sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *reply,
void *userdata,
sd_bus_error *error) {
Manager *m = userdata;
assert(reply);
assert(m);
return sd_bus_message_append(reply, "(tt)",
(uint64_t) hashmap_size(m->dns_transactions),
(uint64_t) m->n_transactions_total);
}
static int bus_property_get_cache_statistics(
sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *reply,
void *userdata,
sd_bus_error *error) {
uint64_t size = 0, hit = 0, miss = 0;
Manager *m = userdata;
DnsScope *s;
assert(reply);
assert(m);
LIST_FOREACH(scopes, s, m->dns_scopes) {
size += dns_cache_size(&s->cache);
hit += s->cache.n_hit;
miss += s->cache.n_miss;
}
return sd_bus_message_append(reply, "(ttt)", size, hit, miss);
}
static int bus_property_get_dnssec_statistics(
sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *reply,
void *userdata,
sd_bus_error *error) {
Manager *m = userdata;
assert(reply);
assert(m);
return sd_bus_message_append(reply, "(tttt)",
(uint64_t) m->n_dnssec_secure,
(uint64_t) m->n_dnssec_insecure,
(uint64_t) m->n_dnssec_bogus,
(uint64_t) m->n_dnssec_indeterminate);
}
static int bus_property_get_dnssec_supported(
sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *reply,
void *userdata,
sd_bus_error *error) {
Manager *m = userdata;
DnsServer *server;
bool supported = true;
Iterator i;
Link *l;
assert(reply);
assert(m);
server = manager_get_dns_server(m);
if (server)
supported = supported && dns_server_dnssec_supported(server);
HASHMAP_FOREACH(l, m->links, i) {
server = link_get_dns_server(l);
if (server)
supported = supported && dns_server_dnssec_supported(server);
}
return sd_bus_message_append(reply, "b", supported);
}
static int bus_method_reset_statistics(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
DnsScope *s;
assert(message);
assert(m);
LIST_FOREACH(scopes, s, m->dns_scopes)
s->cache.n_hit = s->cache.n_miss = 0;
m->n_transactions_total = 0;
m->n_dnssec_secure = m->n_dnssec_insecure = m->n_dnssec_bogus = m->n_dnssec_indeterminate = 0;
return sd_bus_reply_method_return(message, NULL);
}
static int get_unmanaged_link(Manager *m, int ifindex, Link **ret, sd_bus_error *error) {
Link *l;
assert(m);
if (ifindex <= 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid interface index");
l = hashmap_get(m->links, INT_TO_PTR(ifindex));
if (!l)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_LINK, "Link %i not known", ifindex);
if (l->flags & IFF_LOOPBACK)
return sd_bus_error_setf(error, BUS_ERROR_LINK_BUSY, "Link %s is loopback device.", l->name);
if (l->is_managed)
return sd_bus_error_setf(error, BUS_ERROR_LINK_BUSY, "Link %s is managed.", l->name);
*ret = l;
return 0;
}
static int bus_method_set_link_dns_servers(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_free_ struct in_addr_data *dns = NULL;
size_t allocated = 0, n = 0;
Manager *m = userdata;
int ifindex, r;
unsigned i;
Link *l;
assert(message);
assert(m);
r = sd_bus_message_read(message, "i", &ifindex);
if (r < 0)
return r;
r = get_unmanaged_link(m, ifindex, &l, error);
if (r < 0)
return r;
r = sd_bus_message_enter_container(message, 'a', "(iay)");
if (r < 0)
return r;
for (;;) {
int family;
size_t sz;
const void *d;
assert_cc(sizeof(int) == sizeof(int32_t));
r = sd_bus_message_enter_container(message, 'r', "iay");
if (r < 0)
return r;
if (r == 0)
break;
r = sd_bus_message_read(message, "i", &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_exit_container(message);
if (r < 0)
return r;
if (!GREEDY_REALLOC(dns, allocated, n+1))
return -ENOMEM;
dns[n].family = family;
memcpy(&dns[n].address, d, sz);
n++;
}
r = sd_bus_message_exit_container(message);
if (r < 0)
return r;
dns_server_mark_all(l->dns_servers);
for (i = 0; i < n; i++) {
DnsServer *s;
s = dns_server_find(l->dns_servers, dns[i].family, &dns[i].address);
if (s)
dns_server_move_back_and_unmark(s);
else {
r = dns_server_new(l->manager, NULL, DNS_SERVER_LINK, l, dns[i].family, &dns[i].address);
if (r < 0)
goto clear;
}
}
dns_server_unlink_marked(l->dns_servers);
link_allocate_scopes(l);
return sd_bus_reply_method_return(message, NULL);
clear:
dns_server_unlink_all(l->dns_servers);
return r;
}
static int bus_method_set_link_domains(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_free_ char **domains = NULL;
Manager *m = userdata;
int ifindex, r;
char **i;
Link *l;
assert(message);
assert(m);
r = sd_bus_message_read(message, "i", &ifindex);
if (r < 0)
return r;
r = get_unmanaged_link(m, ifindex, &l, error);
if (r < 0)
return r;
r = sd_bus_message_read_strv(message, &domains);
if (r < 0)
return r;
STRV_FOREACH(i, domains) {
r = dns_name_is_valid(*i);
if (r < 0)
return r;
if (r == 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid search domain %s", *i);
if (dns_name_is_root(*i))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Root domain is not suitable as search domain");
}
dns_search_domain_mark_all(l->search_domains);
STRV_FOREACH(i, domains) {
DnsSearchDomain *d;
r = dns_search_domain_find(l->search_domains, *i, &d);
if (r < 0)
goto clear;
if (r > 0)
dns_search_domain_move_back_and_unmark(d);
else {
r = dns_search_domain_new(l->manager, NULL, DNS_SEARCH_DOMAIN_LINK, l, *i);
if (r < 0)
goto clear;
}
}
dns_search_domain_unlink_marked(l->search_domains);
return sd_bus_reply_method_return(message, NULL);
clear:
dns_search_domain_unlink_all(l->search_domains);
return r;
}
static int bus_method_set_link_llmnr(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
ResolveSupport mode;
const char *llmnr;
int ifindex, r;
Link *l;
assert(message);
assert(m);
assert_cc(sizeof(int) == sizeof(int32_t));
r = sd_bus_message_read(message, "is", &ifindex, &llmnr);
if (r < 0)
return r;
if (isempty(llmnr))
mode = RESOLVE_SUPPORT_YES;
else {
mode = resolve_support_from_string(llmnr);
if (mode < 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid LLMNR setting: %s", llmnr);
}
r = get_unmanaged_link(m, ifindex, &l, error);
if (r < 0)
return r;
l->llmnr_support = mode;
link_allocate_scopes(l);
link_add_rrs(l, false);
return sd_bus_reply_method_return(message, NULL);
}
static int bus_method_set_link_mdns(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
ResolveSupport mode;
const char *mdns;
int ifindex, r;
Link *l;
assert(message);
assert(m);
assert_cc(sizeof(int) == sizeof(int32_t));
r = sd_bus_message_read(message, "is", &ifindex, &mdns);
if (r < 0)
return r;
if (isempty(mdns))
mode = RESOLVE_SUPPORT_NO;
else {
mode = resolve_support_from_string(mdns);
if (mode < 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid MulticastDNS setting: %s", mdns);
}
r = get_unmanaged_link(m, ifindex, &l, error);
if (r < 0)
return r;
l->mdns_support = mode;
link_allocate_scopes(l);
link_add_rrs(l, false);
return sd_bus_reply_method_return(message, NULL);
}
static int bus_method_set_link_dnssec(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
const char *dnssec;
DnssecMode mode;
int ifindex, r;
Link *l;
assert(message);
assert(m);
assert_cc(sizeof(int) == sizeof(int32_t));
r = sd_bus_message_read(message, "is", &ifindex, &dnssec);
if (r < 0)
return r;
if (isempty(dnssec))
mode = _DNSSEC_MODE_INVALID;
else {
mode = dnssec_mode_from_string(dnssec);
if (mode < 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid DNSSEC setting: %s", dnssec);
}
r = get_unmanaged_link(m, ifindex, &l, error);
if (r < 0)
return r;
link_set_dnssec_mode(l, mode);
return sd_bus_reply_method_return(message, NULL);
}
static int bus_method_set_link_dnssec_negative_trust_anchors(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_set_free_free_ Set *ns = NULL;
_cleanup_free_ char **ntas = NULL;
Manager *m = userdata;
int ifindex, r;
char **i;
Link *l;
assert(message);
assert(m);
r = sd_bus_message_read(message, "i", &ifindex);
if (r < 0)
return r;
r = get_unmanaged_link(m, ifindex, &l, error);
if (r < 0)
return r;
r = sd_bus_message_read_strv(message, &ntas);
if (r < 0)
return r;
STRV_FOREACH(i, ntas) {
r = dns_name_is_valid(*i);
if (r < 0)
return r;
if (r == 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid search negative trust anchor domain: %s", *i);
}
ns = set_new(&dns_name_hash_ops);
if (!ns)
return -ENOMEM;
STRV_FOREACH(i, ntas) {
r = set_put_strdup(ns, *i);
if (r < 0)
return r;
}
set_free_free(l->dnssec_negative_trust_anchors);
l->dnssec_negative_trust_anchors = ns;
ns = NULL;
return sd_bus_reply_method_return(message, NULL);
}
static int bus_method_revert_link(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
int ifindex;
Link *l;
int r;
assert(message);
assert(m);
assert_cc(sizeof(int) == sizeof(int32_t));
r = sd_bus_message_read(message, "i", &ifindex);
if (r < 0)
return r;
r = get_unmanaged_link(m, ifindex, &l, error);
if (r < 0)
return r;
link_flush_settings(l);
link_allocate_scopes(l);
link_add_rrs(l, false);
return sd_bus_reply_method_return(message, NULL);
}
static const sd_bus_vtable resolve_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_PROPERTY("LLMNRHostname", "s", NULL, offsetof(Manager, llmnr_hostname), 0),
SD_BUS_PROPERTY("DNS", "a(iiay)", bus_property_get_dns_servers, 0, 0),
SD_BUS_PROPERTY("Domains", "a(is)", bus_property_get_search_domains, 0, 0),
SD_BUS_PROPERTY("TransactionStatistics", "(tt)", bus_property_get_transaction_statistics, 0, 0),
SD_BUS_PROPERTY("CacheStatistics", "(ttt)", bus_property_get_cache_statistics, 0, 0),
SD_BUS_PROPERTY("DNSSECStatistics", "(tttt)", bus_property_get_dnssec_statistics, 0, 0),
SD_BUS_PROPERTY("DNSSECSupported", "b", bus_property_get_dnssec_supported, 0, 0),
SD_BUS_METHOD("ResolveHostname", "isit", "a(iiay)st", bus_method_resolve_hostname, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("ResolveAddress", "iiayt", "a(is)t", bus_method_resolve_address, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("ResolveRecord", "isqqt", "a(iqqay)t", bus_method_resolve_record, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("ResolveService", "isssit", "a(qqqsa(iiay)s)aayssst", bus_method_resolve_service, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("ResetStatistics", NULL, NULL, bus_method_reset_statistics, 0),
SD_BUS_METHOD("SetLinkDNS", "ia(iay)", NULL, bus_method_set_link_dns_servers, 0),
SD_BUS_METHOD("SetLinkDomains", "ias", NULL, bus_method_set_link_domains, 0),
SD_BUS_METHOD("SetLinkLLMNR", "is", NULL, bus_method_set_link_llmnr, 0),
SD_BUS_METHOD("SetLinkMulticastDNS", "is", NULL, bus_method_set_link_mdns, 0),
SD_BUS_METHOD("SetLinkDNSSEC", "is", NULL, bus_method_set_link_dnssec, 0),
SD_BUS_METHOD("SetLinkDNSSECNegativeTrustAnchors", "ias", NULL, bus_method_set_link_dnssec_negative_trust_anchors, 0),
SD_BUS_METHOD("RevertLink", "i", NULL, bus_method_revert_link, 0),
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");
(void) sd_event_source_set_description(m->bus_retry_event_source, "bus-retry");
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;
}