From: Yu Watanabe Date: Sat, 17 Feb 2024 21:50:39 +0000 (+0900) Subject: resolve: several follow-ups for resolving service via varlink X-Git-Tag: v256-rc1~807 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c6c7c461a319ec29c6eeeacebc1ecd02bccb4f92;p=thirdparty%2Fsystemd.git resolve: several follow-ups for resolving service via varlink Follow-ups for e1634bb8321c5534a8e5d16b474c7e9d43ef3baa. - Allow to call the method without "name" and "type". - Allow to specify SD_RESOLVE_NO_TXT and SD_RESOLVE_NO_ADDRESS. - Allow to provide multiple services, and fix memory leak. - Rearrange the return value format. - Encode TXT field with octescape() to make the field matches with the io.systemd.Resolve.Monitor interface. Fixes #31371. --- diff --git a/src/resolve/resolved-varlink.c b/src/resolve/resolved-varlink.c index 2f0971a7f0e..a85a2786762 100644 --- a/src/resolve/resolved-varlink.c +++ b/src/resolve/resolved-varlink.c @@ -23,7 +23,7 @@ typedef struct LookupParametersResolveService { const char *domain; int family; int ifindex; - uint64_t in_flags; + uint64_t flags; } LookupParametersResolveService; static void lookup_parameters_destroy(LookupParameters *p) { @@ -577,7 +577,7 @@ static int append_txt(JsonVariant **txt, DnsResourceRecord *rr) { if (i->length <= 0) continue; - r = json_variant_new_base64(&entry, i->data, i->length); + r = json_variant_new_octescape(&entry, i->data, i->length); if (r < 0) return r; @@ -589,20 +589,20 @@ static int append_txt(JsonVariant **txt, DnsResourceRecord *rr) { return 1; } -static int append_srv(DnsQuery *q, - JsonVariant **ret_srv, - JsonVariant **ret_addr, - char **ret_norm, - DnsResourceRecord *rr) { +static int append_srv( + DnsQuery *q, + DnsResourceRecord *rr, + JsonVariant **array) { + _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *canonical = NULL; + _cleanup_(json_variant_unrefp) JsonVariant *v = NULL; _cleanup_free_ char *normalized = NULL; - _cleanup_(json_variant_unrefp) JsonVariant *srv = NULL, *addr = NULL; - int r; assert(q); assert(rr); assert(rr->key); + assert(array); if (rr->key->type != DNS_TYPE_SRV) return 0; @@ -651,7 +651,7 @@ static int append_srv(DnsQuery *q, if (r < 0) return r; - r = json_build(&srv, + r = json_build(&v, JSON_BUILD_OBJECT( JSON_BUILD_PAIR("priority", JSON_BUILD_UNSIGNED(rr->srv.priority)), JSON_BUILD_PAIR("weight", JSON_BUILD_UNSIGNED(rr->srv.weight)), @@ -660,7 +660,21 @@ static int append_srv(DnsQuery *q, if (r < 0) return r; + if (canonical) { + normalized = mfree(normalized); + + r = dns_name_normalize(dns_resource_key_name(canonical->key), 0, &normalized); + if (r < 0) + return r; + + r = json_variant_set_field_string(&v, "canonicalName", normalized); + if (r < 0) + return r; + } + if ((q->flags & SD_RESOLVED_NO_ADDRESS) == 0) { + _cleanup_(json_variant_unrefp) JsonVariant *addresses = NULL; + LIST_FOREACH(auxiliary_queries, aux, q->auxiliary_queries) { DnsQuestion *question; @@ -677,25 +691,21 @@ static int append_srv(DnsQuery *q, if (r == 0) continue; - r = find_addr_records(&addr, question, aux, NULL, NULL); + r = find_addr_records(&addresses, question, aux, NULL, NULL); if (r < 0) return r; } - } - if (canonical) { - normalized = mfree(normalized); - - r = dns_name_normalize(dns_resource_key_name(canonical->key), 0, &normalized); + r = json_variant_set_field(&v, "addresses", addresses); if (r < 0) return r; } - *ret_srv = TAKE_PTR(srv); - *ret_addr = TAKE_PTR(addr); - *ret_norm = TAKE_PTR(normalized); + r = json_variant_append_array(array, v); + if (r < 0) + return r; - return 1; + return 1; /* added */ } static Varlink *get_vl_link_aux_query(DnsQuery *aux) { @@ -710,12 +720,11 @@ static Varlink *get_vl_link_aux_query(DnsQuery *aux) { static void resolve_service_all_complete(DnsQuery *query) { _cleanup_(dns_query_freep) DnsQuery *q = query; - _cleanup_(json_variant_unrefp) JsonVariant *srv = NULL, *addr = NULL, *txt = NULL; - _cleanup_free_ char *name = NULL, *type = NULL, *domain = NULL, *norm = NULL; + _cleanup_(json_variant_unrefp) JsonVariant *srv = NULL, *txt = NULL; + _cleanup_free_ char *name = NULL, *type = NULL, *domain = NULL; _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *canonical = NULL; DnsQuestion *question; DnsResourceRecord *rr; - unsigned added = 0; int r; assert(q); @@ -782,7 +791,7 @@ static void resolve_service_all_complete(DnsQuery *query) { if (r == 0) continue; - r = append_srv(q, &srv, &addr, &norm, rr); + r = append_srv(q, rr, &srv); if (r < 0) goto finish; if (r == 0) /* not an SRV record */ @@ -790,11 +799,9 @@ static void resolve_service_all_complete(DnsQuery *query) { if (!canonical) canonical = dns_resource_record_ref(rr); - - added++; } - if (added <= 0) { + if (json_variant_is_blank_object(srv)) { r = varlink_error(query->varlink_request, "io.systemd.Resolve.NoSuchResourceRecord", NULL); goto finish; } @@ -820,14 +827,13 @@ static void resolve_service_all_complete(DnsQuery *query) { goto finish; r = varlink_replyb(query->varlink_request, JSON_BUILD_OBJECT( - JSON_BUILD_PAIR("srv", JSON_BUILD_VARIANT(srv)), - JSON_BUILD_PAIR("addr", JSON_BUILD_VARIANT(addr)), - JSON_BUILD_PAIR("txt", JSON_BUILD_VARIANT(txt)), - JSON_BUILD_PAIR("normalized", JSON_BUILD_STRING(norm)), + JSON_BUILD_PAIR("services", JSON_BUILD_VARIANT(srv)), + JSON_BUILD_PAIR_CONDITION(!json_variant_is_blank_object(txt), "txt", JSON_BUILD_VARIANT(txt)), JSON_BUILD_PAIR("canonical", JSON_BUILD_OBJECT( JSON_BUILD_PAIR("name", JSON_BUILD_STRING(name)), JSON_BUILD_PAIR("type", JSON_BUILD_STRING(type)), - JSON_BUILD_PAIR("domain", JSON_BUILD_STRING(domain)))))); + JSON_BUILD_PAIR("domain", JSON_BUILD_STRING(domain)))), + JSON_BUILD_PAIR("flags", JSON_BUILD_UNSIGNED(dns_query_reply_flags_make(query))))); finish: if (r < 0) { @@ -983,12 +989,12 @@ finish: static int vl_method_resolve_service(Varlink* link, JsonVariant* parameters, VarlinkMethodFlags flags, void* userdata) { static const JsonDispatch dispatch_table[] = { - { "name", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParametersResolveService, name), JSON_MANDATORY }, - { "type", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParametersResolveService, type), JSON_MANDATORY }, - { "domain", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParametersResolveService, domain), JSON_MANDATORY }, - { "ifindex", JSON_VARIANT_UNSIGNED, json_dispatch_int, offsetof(LookupParametersResolveService, ifindex), 0 }, - { "family", JSON_VARIANT_INTEGER, json_dispatch_int, offsetof(LookupParametersResolveService, family), 0 }, - { "flags", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(LookupParametersResolveService, in_flags), 0 }, + { "name", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParametersResolveService, name), 0 }, + { "type", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParametersResolveService, type), 0 }, + { "domain", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParametersResolveService, domain), JSON_MANDATORY }, + { "ifindex", _JSON_VARIANT_TYPE_INVALID, json_dispatch_int, offsetof(LookupParametersResolveService, ifindex), 0 }, + { "family", _JSON_VARIANT_TYPE_INVALID, json_dispatch_int, offsetof(LookupParametersResolveService, family), 0 }, + { "flags", _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint64, offsetof(LookupParametersResolveService, flags), 0 }, {} }; @@ -1035,18 +1041,21 @@ static int vl_method_resolve_service(Varlink* link, JsonVariant* parameters, Var if (r == 0) return varlink_error_invalid_parameter(link, JSON_VARIANT_STRING_CONST("domain")); - if (!validate_and_mangle_flags(p.name, &p.in_flags, 0)) + if (p.name && !p.type) /* Service name cannot be specified without service type. */ + return varlink_error_invalid_parameter(link, JSON_VARIANT_STRING_CONST("type")); + + if (!validate_and_mangle_flags(p.name, &p.flags, SD_RESOLVED_NO_TXT|SD_RESOLVED_NO_ADDRESS)) return varlink_error_invalid_parameter(link, JSON_VARIANT_STRING_CONST("flags")); - r = dns_question_new_service(&question_utf8, p.name, p.type, p.domain, !(p.in_flags & SD_RESOLVED_NO_TXT), false); + r = dns_question_new_service(&question_utf8, p.name, p.type, p.domain, !(p.flags & SD_RESOLVED_NO_TXT), false); if (r < 0) return r; - r = dns_question_new_service(&question_idna, p.name, p.type, p.domain, !(p.in_flags & SD_RESOLVED_NO_TXT), true); + r = dns_question_new_service(&question_idna, p.name, p.type, p.domain, !(p.flags & SD_RESOLVED_NO_TXT), true); if (r < 0) return r; - r = dns_query_new(m, &q, question_utf8, question_idna, NULL, p.ifindex, p.in_flags|SD_RESOLVED_NO_SEARCH); + r = dns_query_new(m, &q, question_utf8, question_idna, NULL, p.ifindex, p.flags|SD_RESOLVED_NO_SEARCH); if (r < 0) return r; diff --git a/src/shared/varlink-io.systemd.Resolve.c b/src/shared/varlink-io.systemd.Resolve.c index 88dad32b045..12111b24048 100644 --- a/src/shared/varlink-io.systemd.Resolve.c +++ b/src/shared/varlink-io.systemd.Resolve.c @@ -37,7 +37,9 @@ static VARLINK_DEFINE_STRUCT_TYPE( VARLINK_DEFINE_FIELD(priority, VARLINK_INT, 0), VARLINK_DEFINE_FIELD(weight, VARLINK_INT, 0), VARLINK_DEFINE_FIELD(port, VARLINK_INT, 0), - VARLINK_DEFINE_FIELD(hostname, VARLINK_STRING, 0)); + VARLINK_DEFINE_FIELD(hostname, VARLINK_STRING, 0), + VARLINK_DEFINE_FIELD(canonicalName, VARLINK_STRING, VARLINK_NULLABLE), + VARLINK_DEFINE_FIELD_BY_TYPE(addresses, ResolvedAddress, VARLINK_ARRAY|VARLINK_NULLABLE)); static VARLINK_DEFINE_STRUCT_TYPE( ResolvedCanonical, @@ -47,17 +49,16 @@ static VARLINK_DEFINE_STRUCT_TYPE( static VARLINK_DEFINE_METHOD( ResolveService, - VARLINK_DEFINE_INPUT(name, VARLINK_STRING, 0), - VARLINK_DEFINE_INPUT(type, VARLINK_STRING, 0), + VARLINK_DEFINE_INPUT(name, VARLINK_STRING, VARLINK_NULLABLE), + VARLINK_DEFINE_INPUT(type, VARLINK_STRING, VARLINK_NULLABLE), VARLINK_DEFINE_INPUT(domain, VARLINK_STRING, 0), VARLINK_DEFINE_INPUT(ifindex, VARLINK_INT, VARLINK_NULLABLE), VARLINK_DEFINE_INPUT(family, VARLINK_INT, VARLINK_NULLABLE), VARLINK_DEFINE_INPUT(flags, VARLINK_INT, VARLINK_NULLABLE), - VARLINK_DEFINE_OUTPUT_BY_TYPE(srv, ResolvedService, 0), - VARLINK_DEFINE_OUTPUT_BY_TYPE(addr, ResolvedAddress, VARLINK_ARRAY), - VARLINK_DEFINE_OUTPUT(txt, VARLINK_STRING, VARLINK_ARRAY), - VARLINK_DEFINE_OUTPUT(normalized, VARLINK_STRING, 0), - VARLINK_DEFINE_OUTPUT_BY_TYPE(canonical, ResolvedCanonical, 0)); + VARLINK_DEFINE_OUTPUT_BY_TYPE(services, ResolvedService, VARLINK_ARRAY), + VARLINK_DEFINE_OUTPUT(txt, VARLINK_STRING, VARLINK_ARRAY|VARLINK_NULLABLE), + VARLINK_DEFINE_OUTPUT_BY_TYPE(canonical, ResolvedCanonical, 0), + VARLINK_DEFINE_OUTPUT(flags, VARLINK_INT, 0)); static VARLINK_DEFINE_ERROR(NoNameServers); static VARLINK_DEFINE_ERROR(NoSuchResourceRecord); diff --git a/test/units/testsuite-75.sh b/test/units/testsuite-75.sh index 4b7a8063253..b92ebf6b331 100755 --- a/test/units/testsuite-75.sh +++ b/test/units/testsuite-75.sh @@ -437,14 +437,23 @@ grep -qF "authenticated: yes" "$RUN_OUT" # Test service resolve over Varlink run varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveService '{"name":"","type":"_mysvc._tcp","domain":"signed.test"}' -grep -qF '"srv":{"priority":10,"weight":5,"port":1234,"hostname":"myservice.signed.test"}' "$RUN_OUT" -grep -qF '"addr":[{"ifindex":' "$RUN_OUT" +grep -qF '"services":[{"priority":10,"weight":5,"port":1234,"hostname":"myservice.signed.test","canonicalName":"myservice.signed.test","addresses":[{"ifindex":' "$RUN_OUT" grep -qF '"family":10,"address":[253,0,222,173,190,239,202,254,0,0,0,0,0,0,0,23]' "$RUN_OUT" grep -qF '"family":2,"address":[10,0,0,20]' "$RUN_OUT" -grep -qF '"normalized":"myservice.signed.test"' "$RUN_OUT" -grep -qF '"canonical":{"name":null,"type":"_mysvc._tcp","domain":"signed.test"}' "$RUN_OUT" -TXT_OUT=$(grep -a -o -P '(?<=\"txt\"\:\[\").*(?=\"\])' "$RUN_OUT" | base64 --decode) -assert_in "This is TXT for myservice" "$TXT_OUT" +grep -qF '}]}],"txt":["This is TXT for myservice"],"canonical":{"name":null,"type":"_mysvc._tcp","domain":"signed.test"},"flags":' "$RUN_OUT" + +# without name +run varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveService '{"type":"_mysvc._tcp","domain":"signed.test"}' +# without txt (SD_RESOLVE_NO_TXT) +run varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveService '{"type":"_mysvc._tcp","domain":"signed.test","flags":64}' +(! grep -qF '"txt"' "$RUN_OUT") +# without address (SD_RESOLVE_NO_ADDRESS) +run varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveService '{"type":"_mysvc._tcp","domain":"signed.test","flags":128}' +(! grep -qF '"addresses"' "$RUN_OUT") +# without txt and address +run varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveService '{"type":"_mysvc._tcp","domain":"signed.test","flags":192}' +(! grep -qF '"txt"' "$RUN_OUT") +(! grep -qF '"addresses"' "$RUN_OUT") (! run resolvectl service _invalidsvc._udp signed.test) grep -qE "invalidservice\.signed\.test' not found" "$RUN_OUT"