]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
resolved: expose raw RR resolver via Varlink too 31616/head
authorLennart Poettering <lennart@poettering.net>
Mon, 4 Mar 2024 10:22:41 +0000 (11:22 +0100)
committerLennart Poettering <lennart@poettering.net>
Wed, 6 Mar 2024 18:00:53 +0000 (19:00 +0100)
Now that we have an address, hostname, and service resolve, at the last
kind of resovler we expose over D-Bus also to Varlink.

src/resolve/resolved-varlink.c
src/shared/varlink-io.systemd.Resolve.c
test/units/testsuite-75.sh

index cdd5ca41fde02a5eadc77fa1b42493df71ef699e..6b835904be148483ae8937169ed778974cf024aa 100644 (file)
@@ -15,6 +15,8 @@ typedef struct LookupParameters {
         union in_addr_union address;
         size_t address_size;
         char *name;
+        uint16_t class;
+        uint16_t type;
 } LookupParameters;
 
 typedef struct LookupParametersResolveService {
@@ -1074,6 +1076,158 @@ static int vl_method_resolve_service(Varlink* link, JsonVariant* parameters, Var
         return 1;
 }
 
+static void vl_method_resolve_record_complete(DnsQuery *query) {
+        _cleanup_(json_variant_unrefp) JsonVariant *array = NULL;
+        _cleanup_(dns_query_freep) DnsQuery *q = query;
+        DnsQuestion *question;
+        int r;
+
+        assert(q);
+
+        if (q->state != DNS_TRANSACTION_SUCCESS) {
+                r = reply_query_state(q);
+                goto finish;
+        }
+
+        r = dns_query_process_cname_many(q);
+        if (r == -ELOOP) {
+                r = varlink_error(q->varlink_request, "io.systemd.Resolve.CNAMELoop", NULL);
+                goto finish;
+        }
+        if (r < 0)
+                goto finish;
+        if (r == DNS_QUERY_CNAME) {
+                /* This was a cname, and the query was restarted. */
+                TAKE_PTR(q);
+                return;
+        }
+
+        question = dns_query_question_for_protocol(q, q->answer_protocol);
+
+        unsigned added = 0;
+        int ifindex;
+        DnsResourceRecord *rr;
+        DNS_ANSWER_FOREACH_IFINDEX(rr, ifindex, q->answer) {
+                _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+
+                r = dns_question_matches_rr(question, rr, NULL);
+                if (r < 0)
+                        goto finish;
+                if (r == 0)
+                        continue;
+
+                r = dns_resource_record_to_json(rr, &v);
+                if (r < 0)
+                        goto finish;
+
+                r = dns_resource_record_to_wire_format(rr, /* canonical= */ false); /* don't use DNSSEC canonical format, since it removes casing, but we want that for DNS_SD compat */
+                if (r < 0)
+                        goto finish;
+
+                r = json_variant_append_arrayb(
+                                &array,
+                                JSON_BUILD_OBJECT(JSON_BUILD_PAIR_CONDITION(ifindex > 0, "ifindex", JSON_BUILD_INTEGER(ifindex)),
+                                                  JSON_BUILD_PAIR_CONDITION(v, "rr", JSON_BUILD_VARIANT(v)),
+                                                  JSON_BUILD_PAIR("raw", JSON_BUILD_BASE64(rr->wire_format, rr->wire_format_size))));
+                if (r < 0)
+                        goto finish;
+
+                added++;
+        }
+
+        if (added <= 0) {
+                r = varlink_error(q->varlink_request, "io.systemd.Resolve.NoSuchResourceRecord", NULL);
+                goto finish;
+        }
+
+        r = varlink_replyb(q->varlink_request,
+                           JSON_BUILD_OBJECT(
+                                           JSON_BUILD_PAIR("rrs", JSON_BUILD_VARIANT(array)),
+                                           JSON_BUILD_PAIR("flags", JSON_BUILD_INTEGER(dns_query_reply_flags_make(q)))));
+finish:
+        if (r < 0) {
+                log_full_errno(ERRNO_IS_DISCONNECT(r) ? LOG_DEBUG : LOG_ERR, r, "Failed to send record reply: %m");
+                varlink_error_errno(q->varlink_request, r);
+        }
+}
+
+static int vl_method_resolve_record(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
+        static const JsonDispatch dispatch_table[] = {
+                { "ifindex", _JSON_VARIANT_TYPE_INVALID, json_dispatch_int,    offsetof(LookupParameters, ifindex), 0              },
+                { "name",    JSON_VARIANT_STRING,        json_dispatch_string, offsetof(LookupParameters, name),    JSON_MANDATORY },
+                { "class",   _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint16, offsetof(LookupParameters, class),  0              },
+                { "type",    _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint16, offsetof(LookupParameters, type),   JSON_MANDATORY },
+                { "flags",   _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint64, offsetof(LookupParameters, flags),   0              },
+                {}
+        };
+
+        _cleanup_(lookup_parameters_destroy) LookupParameters p = {
+                .class = DNS_CLASS_IN,
+                .type = _DNS_TYPE_INVALID,
+        };
+        _cleanup_(dns_query_freep) DnsQuery *q = NULL;
+        Manager *m;
+        int r;
+
+        assert(link);
+
+        m = ASSERT_PTR(varlink_server_get_userdata(varlink_get_server(link)));
+
+        if (FLAGS_SET(flags, VARLINK_METHOD_ONEWAY))
+                return -EINVAL;
+
+        r = varlink_dispatch(link, parameters, dispatch_table, &p);
+        if (r != 0)
+                return r;
+
+        if (p.ifindex < 0)
+                return varlink_error_invalid_parameter(link, JSON_VARIANT_STRING_CONST("ifindex"));
+
+        r = dns_name_is_valid(p.name);
+        if (r < 0)
+                return r;
+        if (r == 0)
+                return varlink_error_invalid_parameter(link, JSON_VARIANT_STRING_CONST("name"));
+
+        if (!dns_type_is_valid_query(p.type))
+                return varlink_error(link, "io.systemd.Resolve.ResourceRecordTypeInvalidForQuery", NULL);
+        if (dns_type_is_zone_transfer(p.type))
+                return varlink_error(link, "io.systemd.Resolve.ZoneTransfersNotPermitted", NULL);
+        if (dns_type_is_obsolete(p.type))
+                return varlink_error(link, "io.systemd.Resolve.ResourceRecordTypeObsolete", NULL);
+
+        if (!validate_and_mangle_flags(p.name, &p.flags, SD_RESOLVED_NO_SEARCH))
+                return varlink_error_invalid_parameter(link, JSON_VARIANT_STRING_CONST("flags"));
+
+        _cleanup_(dns_question_unrefp) DnsQuestion *question = dns_question_new(1);
+        if (!question)
+                return -ENOMEM;
+
+        _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
+        key = dns_resource_key_new(p.class, p.type, p.name);
+        if (!key)
+                return -ENOMEM;
+
+        r = dns_question_add(question, key, /* flags= */ 0);
+        if (r < 0)
+                return r;
+
+        r = dns_query_new(m, &q, question, question, NULL, p.ifindex, p.flags|SD_RESOLVED_NO_SEARCH|SD_RESOLVED_CLAMP_TTL);
+        if (r < 0)
+                return r;
+
+        q->varlink_request = varlink_ref(link);
+        varlink_set_userdata(link, q);
+        q->complete = vl_method_resolve_record_complete;
+
+        r = dns_query_go(q);
+        if (r < 0)
+                return r;
+
+        TAKE_PTR(q);
+        return 1;
+}
+
 static int vl_method_subscribe_query_results(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
         Manager *m;
         int r;
@@ -1297,9 +1451,10 @@ static int varlink_main_server_init(Manager *m) {
 
         r = varlink_server_bind_method_many(
                         s,
-                        "io.systemd.Resolve.ResolveHostname",  vl_method_resolve_hostname,
-                        "io.systemd.Resolve.ResolveAddress", vl_method_resolve_address,
-                        "io.systemd.Resolve.ResolveService", vl_method_resolve_service);
+                        "io.systemd.Resolve.ResolveHostname", vl_method_resolve_hostname,
+                        "io.systemd.Resolve.ResolveAddress",  vl_method_resolve_address,
+                        "io.systemd.Resolve.ResolveService",  vl_method_resolve_service,
+                        "io.systemd.Resolve.ResolveRecord",   vl_method_resolve_record);
         if (r < 0)
                 return log_error_errno(r, "Failed to register varlink methods: %m");
 
index c7d934ba77aafd376631f93f1d139856ceb8d7bd..a6c501ab011946a14c15c9451da384a099785cad 100644 (file)
@@ -127,6 +127,22 @@ static VARLINK_DEFINE_METHOD(
                 VARLINK_DEFINE_OUTPUT_BY_TYPE(canonical, ResolvedCanonical, 0),
                 VARLINK_DEFINE_OUTPUT(flags, VARLINK_INT, 0));
 
+static VARLINK_DEFINE_STRUCT_TYPE(
+                ResolvedRecord,
+                VARLINK_DEFINE_FIELD(ifindex, VARLINK_INT, 0),
+                VARLINK_DEFINE_FIELD_BY_TYPE(rr, ResourceRecord, VARLINK_NULLABLE),
+                VARLINK_DEFINE_FIELD(raw, VARLINK_STRING, 0));
+
+static VARLINK_DEFINE_METHOD(
+                ResolveRecord,
+                VARLINK_DEFINE_INPUT(ifindex, VARLINK_INT, VARLINK_NULLABLE),
+                VARLINK_DEFINE_INPUT(name, VARLINK_STRING, 0),
+                VARLINK_DEFINE_INPUT(class, VARLINK_INT, VARLINK_NULLABLE),
+                VARLINK_DEFINE_INPUT(type, VARLINK_INT, 0),
+                VARLINK_DEFINE_INPUT(flags, VARLINK_INT, VARLINK_NULLABLE),
+                VARLINK_DEFINE_OUTPUT_BY_TYPE(rrs, ResolvedRecord, VARLINK_ARRAY),
+                VARLINK_DEFINE_OUTPUT(flags, VARLINK_INT, 0));
+
 static VARLINK_DEFINE_ERROR(NoNameServers);
 static VARLINK_DEFINE_ERROR(NoSuchResourceRecord);
 static VARLINK_DEFINE_ERROR(QueryTimedOut);
@@ -150,6 +166,9 @@ static VARLINK_DEFINE_ERROR(
                 VARLINK_DEFINE_FIELD(extendedDNSErrorMessage, VARLINK_STRING, VARLINK_NULLABLE));
 static VARLINK_DEFINE_ERROR(CNAMELoop);
 static VARLINK_DEFINE_ERROR(BadAddressSize);
+static VARLINK_DEFINE_ERROR(ResourceRecordTypeInvalidForQuery);
+static VARLINK_DEFINE_ERROR(ZoneTransfersNotPermitted);
+static VARLINK_DEFINE_ERROR(ResourceRecordTypeObsolete);
 
 VARLINK_DEFINE_INTERFACE(
                 io_systemd_Resolve,
@@ -157,12 +176,14 @@ VARLINK_DEFINE_INTERFACE(
                 &vl_method_ResolveHostname,
                 &vl_method_ResolveAddress,
                 &vl_method_ResolveService,
+                &vl_method_ResolveRecord,
                 &vl_type_ResolvedAddress,
                 &vl_type_ResolvedName,
                 &vl_type_ResolvedService,
                 &vl_type_ResolvedCanonical,
                 &vl_type_ResourceKey,
                 &vl_type_ResourceRecord,
+                &vl_type_ResolvedRecord,
                 &vl_error_NoNameServers,
                 &vl_error_NoSuchResourceRecord,
                 &vl_error_QueryTimedOut,
@@ -177,4 +198,7 @@ VARLINK_DEFINE_INTERFACE(
                 &vl_error_StubLoop,
                 &vl_error_DNSError,
                 &vl_error_CNAMELoop,
-                &vl_error_BadAddressSize);
+                &vl_error_BadAddressSize,
+                &vl_error_ResourceRecordTypeInvalidForQuery,
+                &vl_error_ZoneTransfersNotPermitted,
+                &vl_error_ResourceRecordTypeObsolete);
index 71f299a406b433386f5780a5b425d3bdfe78b92c..7bfc067e0511c05111d21a98d2744b85cfa0904b 100755 (executable)
@@ -846,6 +846,10 @@ run resolvectl reset-statistics --json=short
 test "$(resolvectl --json=short query -t AAAA localhost)" == '{"key":{"class":1,"type":28,"name":"localhost"},"address":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1]}'
 test "$(resolvectl --json=short query -t A localhost)" == '{"key":{"class":1,"type":1,"name":"localhost"},"address":[127,0,0,1]}'
 
+# Test ResolveRecord RR resolving via Varlink
+test "$(varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveRecord '{"name":"localhost","type":1}' --json=short)" == '{"rrs":[{"ifindex":1,"rr":{"key":{"class":1,"type":1,"name":"localhost"},"address":[127,0,0,1]},"raw":"CWxvY2FsaG9zdAAAAQABAAAAAAAEfwAAAQ=="}],"flags":786945}'
+test "$(varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveRecord '{"name":"localhost","type":28}' --json=short)" == '{"rrs":[{"ifindex":1,"rr":{"key":{"class":1,"type":28,"name":"localhost"},"address":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1]},"raw":"CWxvY2FsaG9zdAAAHAABAAAAAAAQAAAAAAAAAAAAAAAAAAAAAQ=="}],"flags":786945}'
+
 # Check if resolved exits cleanly.
 restart_resolved