resolved: add ResolveService() bus call for resolving SRV and DNS-SD services

This also adds client-side support for this to systemd-resolve-host.

Note that the ResolveService() API can deal both with DNS-SD service
(consisting of service name, type and domain), as well as classic SRV
services (consisting just of a type and a domain), all exposed in the
same call.

This patch also reworks CNAME handling in order to reuse it between
hostname, RR and service lookups.

In contrast to Avahi and Bonjour, this new API will actually reolve the
A/AAAA RRs the SRV RRs point to in one go (unless this is explicitly
disabled). This normally comes for free, as these RRs are sent along
the SRV responses anyway, hence let's make use of that. This makes the
API considerably easier to use, as a single ResolveService() invocation
will return all necessary data to pick a server and connect() to it.

Note that this only implements the DNS-SD resolving step, it does not
implement DNS-SD browsing, as that makes sense primarily on mDNS, due to
its continuous nature.
This commit is contained in:
Lennart Poettering 2015-11-23 21:25:40 +01:00
parent a564ca2fd1
commit 45ec7efb6c
8 changed files with 1220 additions and 235 deletions

View file

@ -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;
}
r = parse_boolean(optarg);
if (r < 0)
return log_error_errno(r, "Failed to parse --legend= argument");
arg_legend = !!r;
} else
arg_legend = false;
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;

File diff suppressed because it is too large Load diff

View file

@ -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)

View file

@ -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))

View file

@ -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;

View file

@ -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);

View file

@ -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;
}

View file

@ -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);