summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/resolve-host/resolve-host.c342
-rw-r--r--src/resolve/resolved-bus.c840
-rw-r--r--src/resolve/resolved-def.h8
-rw-r--r--src/resolve/resolved-dns-answer.h17
-rw-r--r--src/resolve/resolved-dns-query.c94
-rw-r--r--src/resolve/resolved-dns-query.h18
-rw-r--r--src/resolve/resolved-dns-question.c126
-rw-r--r--src/resolve/resolved-dns-question.h8
8 files changed, 1219 insertions, 234 deletions
diff --git a/src/resolve-host/resolve-host.c b/src/resolve-host/resolve-host.c
index eb4e646846..2c17ad6ede 100644
--- a/src/resolve-host/resolve-host.c
+++ b/src/resolve-host/resolve-host.c
@@ -28,6 +28,7 @@
#include "alloc-util.h"
#include "bus-error.h"
#include "bus-util.h"
+#include "escape.h"
#include "in-addr-util.h"
#include "parse-util.h"
#include "resolved-def.h"
@@ -41,6 +42,7 @@ static int arg_type = 0;
static uint16_t arg_class = 0;
static bool arg_legend = true;
static uint64_t arg_flags = 0;
+static bool arg_resolve_service = false;
static void print_source(uint64_t flags, usec_t rtt) {
char rtt_str[FORMAT_TIMESTAMP_MAX];
@@ -102,10 +104,8 @@ static int resolve_host(sd_bus *bus, const char *name) {
ts = now(CLOCK_MONOTONIC);
r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
- if (r < 0) {
- log_error("%s: resolve call failed: %s", name, bus_error_message(&error, r));
- return r;
- }
+ if (r < 0)
+ return log_error_errno(r, "%s: resolve call failed: %s", name, bus_error_message(&error, r));
ts = now(CLOCK_MONOTONIC) - ts;
@@ -114,10 +114,10 @@ static int resolve_host(sd_bus *bus, const char *name) {
return bus_log_parse_error(r);
while ((r = sd_bus_message_enter_container(reply, 'r', "iiay")) > 0) {
- const void *a;
- size_t sz;
_cleanup_free_ char *pretty = NULL;
int ifindex, family;
+ const void *a;
+ size_t sz;
assert_cc(sizeof(int) == sizeof(int32_t));
@@ -140,7 +140,7 @@ static int resolve_host(sd_bus *bus, const char *name) {
if (sz != FAMILY_ADDRESS_SIZE(family)) {
log_error("%s: systemd-resolved returned address of invalid size %zu for family %s", name, sz, af_to_name(family) ?: "unknown");
- continue;
+ return -EINVAL;
}
ifname[0] = 0;
@@ -437,6 +437,207 @@ static int resolve_record(sd_bus *bus, const char *name) {
return 0;
}
+static int resolve_service(sd_bus *bus, const char *name, const char *type, const char *domain) {
+ const char *canonical_name, *canonical_type, *canonical_domain;
+ _cleanup_bus_message_unref_ sd_bus_message *req = NULL, *reply = NULL;
+ _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+ char ifname[IF_NAMESIZE] = "";
+ size_t indent, sz;
+ uint64_t flags;
+ const char *p;
+ unsigned c;
+ usec_t ts;
+ int r;
+
+ assert(bus);
+ assert(domain);
+
+ if (isempty(name))
+ name = NULL;
+ if (isempty(type))
+ type = NULL;
+
+ if (arg_ifindex > 0 && !if_indextoname(arg_ifindex, ifname))
+ return log_error_errno(errno, "Failed to resolve interface name for index %i: %m", arg_ifindex);
+
+ if (name)
+ log_debug("Resolving service \"%s\" of type %s in %s (family %s, interface %s).", name, type, domain, af_to_name(arg_family) ?: "*", isempty(ifname) ? "*" : ifname);
+ else if (type)
+ log_debug("Resolving service type %s of %s (family %s, interface %s).", type, domain, af_to_name(arg_family) ?: "*", isempty(ifname) ? "*" : ifname);
+ else
+ log_debug("Resolving service type %s (family %s, interface %s).", domain, af_to_name(arg_family) ?: "*", isempty(ifname) ? "*" : ifname);
+
+ r = sd_bus_message_new_method_call(
+ bus,
+ &req,
+ "org.freedesktop.resolve1",
+ "/org/freedesktop/resolve1",
+ "org.freedesktop.resolve1.Manager",
+ "ResolveService");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_append(req, "isssit", arg_ifindex, name, type, domain, arg_family, arg_flags);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ ts = now(CLOCK_MONOTONIC);
+
+ r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply);
+ if (r < 0)
+ return log_error_errno(r, "Resolve call failed: %s", bus_error_message(&error, r));
+
+ ts = now(CLOCK_MONOTONIC) - ts;
+
+ r = sd_bus_message_enter_container(reply, 'a', "(qqqsa(iiay)s)");
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ indent =
+ (name ? strlen(name) + 1 : 0) +
+ (type ? strlen(type) + 1 : 0) +
+ strlen(domain) + 2;
+
+ c = 0;
+ while ((r = sd_bus_message_enter_container(reply, 'r', "qqqsa(iiay)s")) > 0) {
+ uint16_t priority, weight, port;
+ const char *hostname, *canonical;
+
+ r = sd_bus_message_read(reply, "qqqs", &priority, &weight, &port, &hostname);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ if (name)
+ printf("%*s%s", (int) strlen(name), c == 0 ? name : "", c == 0 ? "/" : " ");
+ if (type)
+ printf("%*s%s", (int) strlen(type), c == 0 ? type : "", c == 0 ? "/" : " ");
+
+ printf("%*s%s %s:%u [priority=%u, weight=%u]\n",
+ (int) strlen(domain), c == 0 ? domain : "",
+ c == 0 ? ":" : " ",
+ hostname, port,
+ priority, weight);
+
+ r = sd_bus_message_enter_container(reply, 'a', "(iiay)");
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ while ((r = sd_bus_message_enter_container(reply, 'r', "iiay")) > 0) {
+ _cleanup_free_ char *pretty = NULL;
+ int ifindex, family;
+ const void *a;
+
+ assert_cc(sizeof(int) == sizeof(int32_t));
+
+ r = sd_bus_message_read(reply, "ii", &ifindex, &family);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = sd_bus_message_read_array(reply, 'y', &a, &sz);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = sd_bus_message_exit_container(reply);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ if (!IN_SET(family, AF_INET, AF_INET6)) {
+ log_debug("%s: skipping entry with family %d (%s)", name, family, af_to_name(family) ?: "unknown");
+ continue;
+ }
+
+ if (sz != FAMILY_ADDRESS_SIZE(family)) {
+ log_error("%s: systemd-resolved returned address of invalid size %zu for family %s", name, sz, af_to_name(family) ?: "unknown");
+ return -EINVAL;
+ }
+
+ ifname[0] = 0;
+ if (ifindex > 0 && !if_indextoname(ifindex, ifname))
+ log_warning_errno(errno, "Failed to resolve interface name for index %i: %m", ifindex);
+
+ r = in_addr_to_string(family, a, &pretty);
+ if (r < 0)
+ return log_error_errno(r, "Failed to print address for %s: %m", name);
+
+ printf("%*s%s%s%s\n", (int) indent, "", pretty, isempty(ifname) ? "" : "%s", ifname);
+ }
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = sd_bus_message_exit_container(reply);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = sd_bus_message_read(reply, "s", &canonical);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ if (!streq(hostname, canonical))
+ printf("%*s(%s)\n", (int) indent, "", canonical);
+
+ r = sd_bus_message_exit_container(reply);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ c++;
+ }
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = sd_bus_message_exit_container(reply);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = sd_bus_message_enter_container(reply, 'a', "ay");
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ c = 0;
+ while ((r = sd_bus_message_read_array(reply, 'y', (const void**) &p, &sz)) > 0) {
+ _cleanup_free_ char *escaped = NULL;
+
+ escaped = cescape_length(p, sz);
+ if (!escaped)
+ return log_oom();
+
+ printf("%*s%s\n", (int) indent, "", escaped);
+ c++;
+ }
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = sd_bus_message_exit_container(reply);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ r = sd_bus_message_read(reply, "ssst", &canonical_name, &canonical_type, &canonical_domain, &flags);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ if (isempty(canonical_name))
+ canonical_name = NULL;
+ if (isempty(canonical_type))
+ canonical_type = NULL;
+
+ if (!streq_ptr(name, canonical_name) ||
+ !streq_ptr(type, canonical_type) ||
+ !streq_ptr(domain, canonical_domain)) {
+
+ printf("%*s(", (int) indent, "");
+
+ if (canonical_name)
+ printf("%s/", canonical_name);
+ if (canonical_type)
+ printf("%s/", canonical_type);
+
+ printf("%s)\n", canonical_domain);
+ }
+
+ print_source(flags, ts);
+
+ return 0;
+}
+
static void help_dns_types(void) {
int i;
const char *t;
@@ -464,33 +665,46 @@ static void help_dns_classes(void) {
}
static void help(void) {
- printf("%s [OPTIONS...]\n\n"
- "Resolve IPv4 or IPv6 addresses.\n\n"
- " -h --help Show this help\n"
- " --version Show package version\n"
- " -4 Resolve IPv4 addresses\n"
- " -6 Resolve IPv6 addresses\n"
- " -i INTERFACE Look on interface\n"
- " -p --protocol=PROTOCOL Look via protocol\n"
- " -t --type=TYPE Query RR with DNS type\n"
- " -c --class=CLASS Query RR with DNS class\n"
- " --legend[=BOOL] Do [not] print column headers\n"
- , program_invocation_short_name);
+ printf("%s [OPTIONS...] NAME...\n"
+ "%s [OPTIONS...] --service [[NAME] TYPE] DOMAIN\n\n"
+ "Resolve domain names, IPv4 or IPv6 addresses, resource records, and services.\n\n"
+ " -h --help Show this help\n"
+ " --version Show package version\n"
+ " -4 Resolve IPv4 addresses\n"
+ " -6 Resolve IPv6 addresses\n"
+ " -i INTERFACE Look on interface\n"
+ " -p --protocol=PROTOCOL Look via protocol\n"
+ " -t --type=TYPE Query RR with DNS type\n"
+ " -c --class=CLASS Query RR with DNS class\n"
+ " --service Resolve service (SRV)\n"
+ " --service-address=BOOL Do [not] resolve address for services\n"
+ " --service-txt=BOOL Do [not] resolve TXT records for services\n"
+ " --cname=BOOL Do [not] follow CNAME redirects\n"
+ " --legend=BOOL Do [not] print column headers\n"
+ , program_invocation_short_name, program_invocation_short_name);
}
static int parse_argv(int argc, char *argv[]) {
enum {
ARG_VERSION = 0x100,
ARG_LEGEND,
+ ARG_SERVICE,
+ ARG_CNAME,
+ ARG_SERVICE_ADDRESS,
+ ARG_SERVICE_TXT,
};
static const struct option options[] = {
- { "help", no_argument, NULL, 'h' },
- { "version", no_argument, NULL, ARG_VERSION },
- { "type", required_argument, NULL, 't' },
- { "class", required_argument, NULL, 'c' },
- { "legend", optional_argument, NULL, ARG_LEGEND },
- { "protocol", required_argument, NULL, 'p' },
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, ARG_VERSION },
+ { "type", required_argument, NULL, 't' },
+ { "class", required_argument, NULL, 'c' },
+ { "legend", required_argument, NULL, ARG_LEGEND },
+ { "protocol", required_argument, NULL, 'p' },
+ { "cname", required_argument, NULL, ARG_CNAME },
+ { "service", no_argument, NULL, ARG_SERVICE },
+ { "service-address", required_argument, NULL, ARG_SERVICE_ADDRESS },
+ { "service-txt", required_argument, NULL, ARG_SERVICE_TXT },
{}
};
@@ -563,16 +777,11 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_LEGEND:
- if (optarg) {
- r = parse_boolean(optarg);
- if (r < 0) {
- log_error("Failed to parse --legend= argument");
- return r;
- }
-
- arg_legend = !!r;
- } else
- arg_legend = false;
+ r = parse_boolean(optarg);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse --legend= argument");
+
+ arg_legend = r;
break;
case 'p':
@@ -591,6 +800,40 @@ static int parse_argv(int argc, char *argv[]) {
break;
+ case ARG_SERVICE:
+ arg_resolve_service = true;
+ break;
+
+ case ARG_CNAME:
+ r = parse_boolean(optarg);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse --cname= argument.");
+ if (r == 0)
+ arg_flags |= SD_RESOLVED_NO_CNAME;
+ else
+ arg_flags &= ~SD_RESOLVED_NO_CNAME;
+ break;
+
+ case ARG_SERVICE_ADDRESS:
+ r = parse_boolean(optarg);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse --service-address= argument.");
+ if (r == 0)
+ arg_flags |= SD_RESOLVED_NO_ADDRESS;
+ else
+ arg_flags &= ~SD_RESOLVED_NO_ADDRESS;
+ break;
+
+ case ARG_SERVICE_TXT:
+ r = parse_boolean(optarg);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse --service-txt= argument.");
+ if (r == 0)
+ arg_flags |= SD_RESOLVED_NO_TXT;
+ else
+ arg_flags &= ~SD_RESOLVED_NO_TXT;
+ break;
+
case '?':
return -EINVAL;
@@ -599,7 +842,12 @@ static int parse_argv(int argc, char *argv[]) {
}
if (arg_type == 0 && arg_class != 0) {
- log_error("--class= may only be used in conjunction with --type=");
+ log_error("--class= may only be used in conjunction with --type=.");
+ return -EINVAL;
+ }
+
+ if (arg_type != 0 && arg_resolve_service) {
+ log_error("--service and --type= may not be combined.");
return -EINVAL;
}
@@ -632,6 +880,28 @@ int main(int argc, char **argv) {
goto finish;
}
+ if (arg_resolve_service) {
+
+ if (argc < optind + 1) {
+ log_error("Domain specification required.");
+ r = -EINVAL;
+ goto finish;
+
+ } else if (argc == optind + 1)
+ r = resolve_service(bus, NULL, NULL, argv[optind]);
+ else if (argc == optind + 2)
+ r = resolve_service(bus, NULL, argv[optind], argv[optind+1]);
+ else if (argc == optind + 3)
+ r = resolve_service(bus, argv[optind], argv[optind+1], argv[optind+2]);
+ else {
+ log_error("Too many arguments");
+ r = -EINVAL;
+ goto finish;
+ }
+
+ goto finish;
+ }
+
while (argv[optind]) {
int family, ifindex, k;
union in_addr_union a;
diff --git a/src/resolve/resolved-bus.c b/src/resolve/resolved-bus.c
index f0a3b607d4..1908cae2b7 100644
--- a/src/resolve/resolved-bus.c
+++ b/src/resolve/resolved-bus.c
@@ -31,15 +31,14 @@ static int reply_query_state(DnsQuery *q) {
const char *name;
int r;
- if (q->request_hostname)
- name = q->request_hostname;
- else {
+ if (q->request_address_valid) {
r = in_addr_to_string(q->request_family, &q->request_address, &ip);
if (r < 0)
return r;
name = ip;
- }
+ } else
+ name = dns_question_name(q->question);
switch (q->state) {
@@ -132,10 +131,9 @@ static int append_address(sd_bus_message *reply, DnsResourceRecord *rr, int ifin
}
static void bus_method_resolve_hostname_complete(DnsQuery *q) {
- _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *cname = NULL, *canonical = NULL;
+ _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *canonical = NULL;
_cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
- _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
- unsigned added = 0, i;
+ unsigned added = 0;
int r;
assert(q);
@@ -145,6 +143,16 @@ static void bus_method_resolve_hostname_complete(DnsQuery *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_question_name(q->question));
+ goto finish;
+ }
+ if (r < 0)
+ goto finish;
+ if (r > 0) /* 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;
@@ -154,92 +162,42 @@ static void bus_method_resolve_hostname_complete(DnsQuery *q) {
goto finish;
if (q->answer) {
- answer = dns_answer_ref(q->answer);
+ DnsResourceRecord *rr;
+ int ifindex;
- for (i = 0; i < answer->n_rrs; i++) {
- r = dns_question_matches_rr(q->question, answer->items[i].rr);
+ DNS_ANSWER_FOREACH_IFINDEX(rr, ifindex, q->answer) {
+ r = dns_question_matches_rr(q->question, rr);
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->items[i].rr);
- if (r < 0)
- goto finish;
- if (r > 0)
- cname = dns_resource_record_ref(answer->items[i].rr);
-
+ if (r == 0)
continue;
- }
- r = append_address(reply, answer->items[i].rr, answer->items[i].ifindex);
+ r = append_address(reply, rr, ifindex);
if (r < 0)
goto finish;
if (!canonical)
- canonical = dns_resource_record_ref(answer->items[i].rr);
+ canonical = dns_resource_record_ref(rr);
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);
- 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->items[i].rr);
- if (r < 0)
- goto finish;
- if (r == 0)
- continue;
-
- r = append_address(reply, answer->items[i].rr, answer->items[i].ifindex);
- if (r < 0)
- goto finish;
-
- if (!canonical)
- canonical = dns_resource_record_ref(answer->items[i].rr);
-
- added++;
- }
-
- /* If we didn't find anything, then let's restart the
- * query, this time with the cname */
- if (added <= 0) {
- r = dns_query_go(q);
- if (r < 0) {
- r = sd_bus_reply_method_errno(q->request, -r, NULL);
- goto finish;
- }
-
- return;
- }
+ 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_question_name(q->question));
+ goto finish;
}
r = sd_bus_message_close_container(reply);
if (r < 0)
goto finish;
- /* Return the precise spelling and uppercasing reported by the server */
+ /* Return the precise spelling and uppercasing and CNAME target 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));
+ 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;
@@ -248,23 +206,23 @@ static void bus_method_resolve_hostname_complete(DnsQuery *q) {
finish:
if (r < 0) {
log_error_errno(r, "Failed to send hostname reply: %m");
- sd_bus_reply_method_errno(q->request, -r, NULL);
+ 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) {
+static int check_ifindex_flags(int ifindex, uint64_t *flags, uint64_t ok, 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)
+ if (*flags & ~(SD_RESOLVED_PROTOCOLS_ALL|SD_RESOLVED_NO_CNAME|ok))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid flags parameter");
- if (*flags == 0)
- *flags = SD_RESOLVED_FLAGS_DEFAULT;
+ if ((*flags & SD_RESOLVED_PROTOCOLS_ALL) == 0) /* If no protocol is enabled, enable all */
+ *flags |= SD_RESOLVED_PROTOCOLS_ALL;
return 0;
}
@@ -281,6 +239,8 @@ static int bus_method_resolve_hostname(sd_bus_message *message, void *userdata,
assert(message);
assert(m);
+ assert_cc(sizeof(int) == sizeof(int32_t));
+
r = sd_bus_message_read(message, "isit", &ifindex, &hostname, &family, &flags);
if (r < 0)
return r;
@@ -288,41 +248,19 @@ static int bus_method_resolve_hostname(sd_bus_message *message, void *userdata,
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);
+ r = dns_name_is_valid(hostname);
if (r < 0)
+ return r;
+ 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);
+ r = check_ifindex_flags(ifindex, &flags, 0, 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_question_new_address(&question, family, hostname);
+ if (r < 0)
+ return r;
r = dns_query_new(m, &q, question, ifindex, flags);
if (r < 0)
@@ -330,27 +268,28 @@ static int bus_method_resolve_hostname(sd_bus_message *message, void *userdata,
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;
+ goto fail;
r = dns_query_go(q);
- if (r < 0) {
- dns_query_free(q);
- return r;
- }
+ if (r < 0)
+ goto fail;
return 1;
+
+fail:
+ dns_query_free(q);
+ return r;
}
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;
+ DnsResourceRecord *rr;
+ unsigned added = 0;
+ int ifindex, r;
assert(q);
@@ -359,6 +298,8 @@ static void bus_method_resolve_address_complete(DnsQuery *q) {
goto finish;
}
+ /* We don't process CNAME for PTR lookups. */
+
r = sd_bus_message_new_method_return(q->request, &reply);
if (r < 0)
goto finish;
@@ -368,16 +309,14 @@ static void bus_method_resolve_address_complete(DnsQuery *q) {
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->items[i].rr);
+ DNS_ANSWER_FOREACH_IFINDEX(rr, ifindex, q->answer) {
+ r = dns_question_matches_rr(q->question, rr);
if (r < 0)
goto finish;
if (r == 0)
continue;
- r = sd_bus_message_append(reply, "(is)", answer->items[i].ifindex, answer->items[i].rr->ptr.name);
+ r = sd_bus_message_append(reply, "(is)", ifindex, rr->ptr.name);
if (r < 0)
goto finish;
@@ -385,12 +324,11 @@ static void bus_method_resolve_address_complete(DnsQuery *q) {
}
}
- if (added == 0) {
+ 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);
+ 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));
goto finish;
}
@@ -407,16 +345,14 @@ static void bus_method_resolve_address_complete(DnsQuery *q) {
finish:
if (r < 0) {
log_error_errno(r, "Failed to send address reply: %m");
- sd_bus_reply_method_errno(q->request, -r, NULL);
+ 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;
@@ -428,6 +364,8 @@ static int bus_method_resolve_address(sd_bus_message *message, void *userdata, s
assert(message);
assert(m);
+ assert_cc(sizeof(int) == sizeof(int32_t));
+
r = sd_bus_message_read(message, "ii", &ifindex, &family);
if (r < 0)
return r;
@@ -446,25 +384,11 @@ static int bus_method_resolve_address(sd_bus_message *message, void *userdata, s
if (r < 0)
return r;
- r = check_ifindex_flags(ifindex, &flags, error);
- if (r < 0)
- return r;
-
- r = dns_name_reverse(family, d, &reverse);
+ 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_consume(DNS_CLASS_IN, DNS_TYPE_PTR, reverse);
- if (!key)
- return -ENOMEM;
-
- reverse = NULL;
-
- r = dns_question_add(question, key);
+ r = dns_question_new_reverse(&question, family, d);
if (r < 0)
return r;
@@ -479,21 +403,58 @@ static int bus_method_resolve_address(sd_bus_message *message, void *userdata, s
r = dns_query_bus_track(q, message);
if (r < 0)
- return r;
+ goto fail;
r = dns_query_go(q);
- if (r < 0) {
- dns_query_free(q);
- return r;
- }
+ if (r < 0)
+ goto fail;
return 1;
+
+fail:
+ dns_query_free(q);
+ return r;
+}
+
+static int bus_message_append_rr(sd_bus_message *m, DnsResourceRecord *rr, int ifindex) {
+ _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
+ size_t start;
+ int r;
+
+ assert(m);
+ assert(rr);
+
+ r = sd_bus_message_open_container(m, 'r', "iqqay");
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append(m, "iqq",
+ ifindex,
+ rr->key->class,
+ rr->key->type);
+ if (r < 0)
+ return r;
+
+ r = dns_packet_new(&p, DNS_PROTOCOL_DNS, 0);
+ if (r < 0)
+ return r;
+
+ p->refuse_compression = true;
+
+ r = dns_packet_append_rr(p, rr, &start);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append_array(m, 'y', DNS_PACKET_DATA(p) + start, p->size - start);
+ if (r < 0)
+ return r;
+
+ return sd_bus_message_close_container(m);
}
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;
+ unsigned added = 0;
int r;
assert(q);
@@ -503,6 +464,16 @@ static void bus_method_resolve_record_complete(DnsQuery *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_question_name(q->question));
+ goto finish;
+ }
+ if (r < 0)
+ goto finish;
+ if (r > 0) /* Following a CNAME */
+ return;
+
r = sd_bus_message_new_method_return(q->request, &reply);
if (r < 0)
goto finish;
@@ -512,44 +483,17 @@ static void bus_method_resolve_record_complete(DnsQuery *q) {
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;
+ DnsResourceRecord *rr;
+ int ifindex;
- r = dns_question_matches_rr(q->question, answer->items[i].rr);
+ DNS_ANSWER_FOREACH_IFINDEX(rr, ifindex, q->answer) {
+ r = dns_question_matches_rr(q->question, rr);
if (r < 0)
goto finish;
if (r == 0)
continue;
- r = dns_packet_new(&p, DNS_PROTOCOL_DNS, 0);
- if (r < 0)
- goto finish;
-
- p->refuse_compression = true;
-
- r = dns_packet_append_rr(p, answer->items[i].rr, &start);
- if (r < 0)
- goto finish;
-
- r = sd_bus_message_open_container(reply, 'r', "iqqay");
- if (r < 0)
- goto finish;
-
- r = sd_bus_message_append(reply, "iqq",
- answer->items[i].ifindex,
- answer->items[i].rr->key->class,
- answer->items[i].rr->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);
+ r = bus_message_append_rr(reply, rr, ifindex);
if (r < 0)
goto finish;
@@ -558,7 +502,7 @@ static void bus_method_resolve_record_complete(DnsQuery *q) {
}
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);
+ 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_question_name(q->question));
goto finish;
}
@@ -575,7 +519,7 @@ static void bus_method_resolve_record_complete(DnsQuery *q) {
finish:
if (r < 0) {
log_error_errno(r, "Failed to send record reply: %m");
- sd_bus_reply_method_errno(q->request, -r, NULL);
+ sd_bus_reply_method_errno(q->request, r, NULL);
}
dns_query_free(q);
@@ -594,15 +538,19 @@ static int bus_method_resolve_record(sd_bus_message *message, void *userdata, sd
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_normalize(name, NULL);
+ 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);
- r = check_ifindex_flags(ifindex, &flags, error);
+ r = check_ifindex_flags(ifindex, &flags, 0, error);
if (r < 0)
return r;
@@ -623,20 +571,535 @@ static int bus_method_resolve_record(sd_bus_message *message, void *userdata, sd
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;
+ 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;
+
+ if (aux->state != DNS_TRANSACTION_SUCCESS)
+ continue;
+ if (aux->auxiliary_result != 0)
+ continue;
+
+ r = dns_name_equal(dns_question_name(aux->question), rr->srv.name);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ continue;
+
+ DNS_ANSWER_FOREACH(zz, aux->answer) {
+
+ r = dns_question_matches_rr(aux->question, zz);
+ 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;
+ int ifindex;
+
+ if (aux->state != DNS_TRANSACTION_SUCCESS)
+ continue;
+ if (aux->auxiliary_result != 0)
+ continue;
+
+ r = dns_name_equal(dns_question_name(aux->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(aux->question, zz);
+ 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;
+ DnsQuery *aux;
+ unsigned added = false;
+ 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_question_name(bad->question));
+ 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;
+
+ if (q->answer) {
+ DnsResourceRecord *rr;
+
+ DNS_ANSWER_FOREACH(rr, q->answer) {
+ r = dns_question_matches_rr(q->question, rr);
+ 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_question_name(q->question));
+ 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;
+
+ if (q->answer) {
+ DnsResourceRecord *rr;
+
+ DNS_ANSWER_FOREACH(rr, q->answer) {
+ r = dns_question_matches_rr(q->question, rr);
+ 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));
+ 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 > 0) /* 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);
+ if (r < 0)
+ return r;
+
+ r = dns_query_new(q->manager, &aux, question, ifindex, q->flags);
+ 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) {
+ unsigned found = 0;
+ 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_question_name(q->question));
+ goto finish;
+ }
+ if (r < 0)
+ goto finish;
+ if (r > 0) /* This was a cname, and the query was restarted. */
+ return;
+
+ if (q->answer) {
+ DnsResourceRecord *rr;
+ int ifindex;
+
+ DNS_ANSWER_FOREACH_IFINDEX(rr, ifindex, q->answer) {
+ r = dns_question_matches_rr(q->question, rr);
+ if (r < 0)
+ goto finish;
+ if (r == 0)
+ continue;
+
+ if (rr->key->type != DNS_TYPE_SRV)
+ 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 (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_question_name(q->question));
+ goto finish;
+ }
+
+ /* Maybe we are already finished? check now... */
+ resolve_service_all_complete(q);
+ return;
+
+finish:
if (r < 0) {
- dns_query_free(q);
+ 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_resource_key_unrefp) DnsResourceKey *key = NULL;
+ _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL;
+ const char *name, *type, *domain, *joined;
+ _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 {
+ r = dns_srv_type_verify(type);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ 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;
+
+ if (type) {
+ /* If the type is specified, we generate the full domain name to look up ourselves */
+ r = dns_service_join(name, type, domain, &n);
+ if (r < 0)
+ return r;
+
+ joined = n;
+ } else
+ /* If no type is specified, we assume the domain
+ * contains the full domain name to lookup already */
+ joined = domain;
+
+ r = dns_question_new_service(&question, joined, !(flags & SD_RESOLVED_NO_TXT));
+ 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->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 const sd_bus_vtable resolve_vtable[] = {
@@ -644,6 +1107,7 @@ static const sd_bus_vtable resolve_vtable[] = {
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_VTABLE_END,
};
diff --git a/src/resolve/resolved-def.h b/src/resolve/resolved-def.h
index 086d111205..85af1923ff 100644
--- a/src/resolve/resolved-def.h
+++ b/src/resolve/resolved-def.h
@@ -24,7 +24,9 @@
#define SD_RESOLVED_DNS ((uint64_t) 1)
#define SD_RESOLVED_LLMNR_IPV4 ((uint64_t) 2)
#define SD_RESOLVED_LLMNR_IPV6 ((uint64_t) 4)
-#define SD_RESOLVED_LLMNR (SD_RESOLVED_LLMNR_IPV4|SD_RESOLVED_LLMNR_IPV6)
+#define SD_RESOLVED_NO_CNAME ((uint64_t) 8)
+#define SD_RESOLVED_NO_TXT ((uint64_t) 16)
+#define SD_RESOLVED_NO_ADDRESS ((uint64_t) 32)
-#define SD_RESOLVED_FLAGS_ALL (SD_RESOLVED_DNS|SD_RESOLVED_LLMNR_IPV4|SD_RESOLVED_LLMNR_IPV6)
-#define SD_RESOLVED_FLAGS_DEFAULT SD_RESOLVED_FLAGS_ALL
+#define SD_RESOLVED_LLMNR (SD_RESOLVED_LLMNR_IPV4|SD_RESOLVED_LLMNR_IPV6)
+#define SD_RESOLVED_PROTOCOLS_ALL (SD_RESOLVED_LLMNR|SD_RESOLVED_DNS)
diff --git a/src/resolve/resolved-dns-answer.h b/src/resolve/resolved-dns-answer.h
index 044d73b19c..b5b1ad56ba 100644
--- a/src/resolve/resolved-dns-answer.h
+++ b/src/resolve/resolved-dns-answer.h
@@ -58,3 +58,20 @@ void dns_answer_order_by_scope(DnsAnswer *a, bool prefer_link_local);
int dns_answer_reserve(DnsAnswer **a, unsigned n_free);
DEFINE_TRIVIAL_CLEANUP_FUNC(DnsAnswer*, dns_answer_unref);
+
+#define DNS_ANSWER_FOREACH(kk, a) \
+ for (unsigned _i = ({ \
+ (kk) = ((a) && (a)->n_rrs > 0 ? (a)->items[0].rr : NULL); \
+ 0; \
+ }); \
+ (a) && ((_i) < (a)->n_rrs); \
+ _i++, (kk) = (_i < (a)->n_rrs ? (a)->items[_i].rr : NULL))
+
+#define DNS_ANSWER_FOREACH_IFINDEX(kk, ifindex, a) \
+ for (unsigned _i = ({ \
+ (kk) = ((a) && (a)->n_rrs > 0 ? (a)->items[0].rr : NULL); \
+ (ifindex) = ((a) && (a)->n_rrs > 0 ? (a)->items[0].ifindex : 0); \
+ 0; \
+ }); \
+ (a) && ((_i) < (a)->n_rrs); \
+ _i++, (kk) = (_i < (a)->n_rrs ? (a)->items[_i].rr : NULL), (ifindex) = (_i < (a)->n_rrs ? (a)->items[_i].ifindex : 0))
diff --git a/src/resolve/resolved-dns-query.c b/src/resolve/resolved-dns-query.c
index f7cb84e2a6..c1cd650651 100644
--- a/src/resolve/resolved-dns-query.c
+++ b/src/resolve/resolved-dns-query.c
@@ -30,6 +30,7 @@
#define CNAME_MAX 8
#define QUERIES_MAX 2048
+#define AUXILIARY_QUERIES_MAX 64
static void dns_query_stop(DnsQuery *q) {
DnsTransaction *t;
@@ -48,6 +49,15 @@ DnsQuery *dns_query_free(DnsQuery *q) {
if (!q)
return NULL;
+ while (q->auxiliary_queries)
+ dns_query_free(q->auxiliary_queries);
+
+ if (q->auxiliary_for) {
+ assert(q->auxiliary_for->n_auxiliary_queries > 0);
+ q->auxiliary_for->n_auxiliary_queries--;
+ LIST_REMOVE(auxiliary_queries, q->auxiliary_for->auxiliary_queries, q);
+ }
+
dns_query_stop(q);
set_free(q->transactions);
@@ -111,6 +121,29 @@ int dns_query_new(Manager *m, DnsQuery **ret, DnsQuestion *question, int ifindex
return 0;
}
+int dns_query_make_auxiliary(DnsQuery *q, DnsQuery *auxiliary_for) {
+ assert(q);
+ assert(auxiliary_for);
+
+ /* Ensure that that the query is not auxiliary yet, and
+ * nothing else is auxiliary to it either */
+ assert(!q->auxiliary_for);
+ assert(!q->auxiliary_queries);
+
+ /* Ensure that the unit we shall be made auxiliary for isn't
+ * auxiliary itself */
+ assert(!auxiliary_for->auxiliary_for);
+
+ if (auxiliary_for->n_auxiliary_queries >= AUXILIARY_QUERIES_MAX)
+ return -EAGAIN;
+
+ LIST_PREPEND(auxiliary_queries, auxiliary_for->auxiliary_queries, q);
+ q->auxiliary_for = auxiliary_for;
+
+ auxiliary_for->n_auxiliary_queries++;
+ return 0;
+}
+
static void dns_query_complete(DnsQuery *q, DnsTransactionState state) {
assert(q);
assert(!IN_SET(state, DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING));
@@ -622,7 +655,7 @@ int dns_query_go(DnsQuery *q) {
assert(q->question);
assert(q->question->n_keys > 0);
- name = DNS_RESOURCE_KEY_NAME(q->question->keys[0]);
+ name = dns_question_name(q->question);
LIST_FOREACH(scopes, s, q->manager->dns_scopes) {
DnsScopeMatch match;
@@ -831,12 +864,13 @@ void dns_query_ready(DnsQuery *q) {
dns_query_complete(q, state);
}
-int dns_query_cname_redirect(DnsQuery *q, const DnsResourceRecord *cname) {
+static int dns_query_cname_redirect(DnsQuery *q, const DnsResourceRecord *cname) {
_cleanup_(dns_question_unrefp) DnsQuestion *nq = NULL;
int r;
assert(q);
+ q->n_cname_redirects ++;
if (q->n_cname_redirects > CNAME_MAX)
return -ELOOP;
@@ -848,14 +882,66 @@ int dns_query_cname_redirect(DnsQuery *q, const DnsResourceRecord *cname) {
q->question = nq;
nq = NULL;
- q->n_cname_redirects++;
-
dns_query_stop(q);
q->state = DNS_TRANSACTION_NULL;
return 0;
}
+int dns_query_process_cname(DnsQuery *q) {
+ _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *cname = NULL;
+ DnsResourceRecord *rr;
+ int r;
+
+ assert(q);
+
+ if (q->state != DNS_TRANSACTION_SUCCESS)
+ return 0;
+
+ DNS_ANSWER_FOREACH(rr, q->answer) {
+
+ r = dns_question_matches_rr(q->question, rr);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ return 0; /* The answer matches directly, no need to follow cnames */
+
+ r = dns_question_matches_cname(q->question, rr);
+ if (r < 0)
+ return r;
+ if (r > 0 && !cname)
+ cname = dns_resource_record_ref(rr);
+ }
+
+ if (!cname)
+ return 0; /* No cname to follow */
+
+ if (q->flags & SD_RESOLVED_NO_CNAME)
+ return -ELOOP;
+
+ /* OK, let's actually follow the CNAME */
+ r = dns_query_cname_redirect(q, cname);
+ if (r < 0)
+ return r;
+
+ /* Let's see if the answer can already answer the new
+ * redirected question */
+ DNS_ANSWER_FOREACH(rr, q->answer) {
+ r = dns_question_matches_rr(q->question, rr);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ return 0; /* It can answer it, yay! */
+ }
+
+ /* OK, it cannot, let's begin with the new query */
+ r = dns_query_go(q);
+ if (r < 0)
+ return r;
+
+ return 1; /* We return > 0, if we restarted the query for a new cname */
+}
+
static int on_bus_track(sd_bus_track *t, void *userdata) {
DnsQuery *q = userdata;
diff --git a/src/resolve/resolved-dns-query.h b/src/resolve/resolved-dns-query.h
index 2c28a7e284..256dddc00b 100644
--- a/src/resolve/resolved-dns-query.h
+++ b/src/resolve/resolved-dns-query.h
@@ -34,6 +34,16 @@ typedef struct DnsQuery DnsQuery;
struct DnsQuery {
Manager *manager;
+
+ /* When resolving a service, we first create a TXT+SRV query,
+ * and then for the hostnames we discover auxiliary A+AAAA
+ * queries. This pointer always points from the auxiliary
+ * queries back to the TXT+SRV query. */
+ DnsQuery *auxiliary_for;
+ LIST_HEAD(DnsQuery, auxiliary_queries);
+ unsigned n_auxiliary_queries;
+ int auxiliary_result;
+
DnsQuestion *question;
uint64_t flags;
@@ -53,8 +63,9 @@ struct DnsQuery {
/* Bus client information */
sd_bus_message *request;
int request_family;
- const char *request_hostname;
+ bool request_address_valid;
union in_addr_union request_address;
+ unsigned block_all_complete;
/* Completion callback */
void (*complete)(DnsQuery* q);
@@ -65,15 +76,18 @@ struct DnsQuery {
sd_bus_track *bus_track;
LIST_FIELDS(DnsQuery, queries);
+ LIST_FIELDS(DnsQuery, auxiliary_queries);
};
int dns_query_new(Manager *m, DnsQuery **q, DnsQuestion *question, int family, uint64_t flags);
DnsQuery *dns_query_free(DnsQuery *q);
+int dns_query_make_auxiliary(DnsQuery *q, DnsQuery *auxiliary_for);
+
int dns_query_go(DnsQuery *q);
void dns_query_ready(DnsQuery *q);
-int dns_query_cname_redirect(DnsQuery *q, const DnsResourceRecord *cname);
+int dns_query_process_cname(DnsQuery *q);
int dns_query_bus_track(DnsQuery *q, sd_bus_message *m);
diff --git a/src/resolve/resolved-dns-question.c b/src/resolve/resolved-dns-question.c
index 48951221dc..868658652d 100644
--- a/src/resolve/resolved-dns-question.c
+++ b/src/resolve/resolved-dns-question.c
@@ -301,3 +301,129 @@ int dns_question_cname_redirect(DnsQuestion *q, const DnsResourceRecord *cname,
return 1;
}
+
+const char *dns_question_name(DnsQuestion *q) {
+ assert(q);
+
+ if (q->n_keys < 1)
+ return NULL;
+
+ return DNS_RESOURCE_KEY_NAME(q->keys[0]);
+}
+
+int dns_question_new_address(DnsQuestion **ret, int family, const char *name) {
+ _cleanup_(dns_question_unrefp) DnsQuestion *q = NULL;
+ int r;
+
+ assert(ret);
+ assert(name);
+
+ if (!IN_SET(family, AF_INET, AF_INET6, AF_UNSPEC))
+ return -EAFNOSUPPORT;
+
+ q = dns_question_new(family == AF_UNSPEC ? 2 : 1);
+ if (!q)
+ 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, name);
+ if (!key)
+ return -ENOMEM;
+
+ r = dns_question_add(q, 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, name);
+ if (!key)
+ return -ENOMEM;
+
+ r = dns_question_add(q, key);
+ if (r < 0)
+ return r;
+ }
+
+ *ret = q;
+ q = NULL;
+
+ return 0;
+}
+
+int dns_question_new_reverse(DnsQuestion **ret, int family, const union in_addr_union *a) {
+ _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
+ _cleanup_(dns_question_unrefp) DnsQuestion *q = NULL;
+ _cleanup_free_ char *reverse = NULL;
+ int r;
+
+ assert(ret);
+ assert(a);
+
+ if (!IN_SET(family, AF_INET, AF_INET6, AF_UNSPEC))
+ return -EAFNOSUPPORT;
+
+ r = dns_name_reverse(family, a, &reverse);
+ if (r < 0)
+ return r;
+
+ q = dns_question_new(1);
+ if (!q)
+ 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(q, key);
+ if (r < 0)
+ return r;
+
+ *ret = q;
+ q = NULL;
+
+ return 0;
+}
+
+int dns_question_new_service(DnsQuestion **ret, const char *name, bool with_txt) {
+ _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
+ _cleanup_(dns_question_unrefp) DnsQuestion *q = NULL;
+ int r;
+
+ assert(ret);
+ assert(name);
+
+ q = dns_question_new(1 + with_txt);
+ if (!q)
+ return -ENOMEM;
+
+ key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_SRV, name);
+ if (!key)
+ return -ENOMEM;
+
+ r = dns_question_add(q, key);
+ if (r < 0)
+ return r;
+
+ if (with_txt) {
+ dns_resource_key_unref(key);
+ key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_TXT, name);
+ if (!key)
+ return -ENOMEM;
+
+ r = dns_question_add(q, key);
+ if (r < 0)
+ return r;
+ }
+
+ *ret = q;
+ q = NULL;
+
+ return 0;
+}
diff --git a/src/resolve/resolved-dns-question.h b/src/resolve/resolved-dns-question.h
index 13cd1f20f3..7f65e47158 100644
--- a/src/resolve/resolved-dns-question.h
+++ b/src/resolve/resolved-dns-question.h
@@ -25,7 +25,7 @@ typedef struct DnsQuestion DnsQuestion;
#include "resolved-dns-rr.h"
-/* A simple array of resources keys */
+/* A simple array of resources keys, all sharing the same domain */
struct DnsQuestion {
unsigned n_ref;
@@ -37,6 +37,10 @@ DnsQuestion *dns_question_new(unsigned n);
DnsQuestion *dns_question_ref(DnsQuestion *q);
DnsQuestion *dns_question_unref(DnsQuestion *q);
+int dns_question_new_address(DnsQuestion **ret, int family, const char *name);
+int dns_question_new_reverse(DnsQuestion **ret, int family, const union in_addr_union *a);
+int dns_question_new_service(DnsQuestion **ret, const char *name, bool with_txt);
+
int dns_question_add(DnsQuestion *q, DnsResourceKey *key);
int dns_question_matches_rr(DnsQuestion *q, DnsResourceRecord *rr);
@@ -48,4 +52,6 @@ int dns_question_is_equal(DnsQuestion *a, DnsQuestion *b);
int dns_question_cname_redirect(DnsQuestion *q, const DnsResourceRecord *cname, DnsQuestion **ret);
+const char *dns_question_name(DnsQuestion *q);
+
DEFINE_TRIVIAL_CLEANUP_FUNC(DnsQuestion*, dns_question_unref);