Compare commits
11 Commits
cf72f7f116
...
a15e7eb9e0
Author | SHA1 | Date |
---|---|---|
Vishal Chillara | a15e7eb9e0 | |
Winterhuman | 5bed97dd57 | |
Luca Boccassi | c4d7a13c06 | |
Abderrahim Kitouni | 0ae6f4843e | |
Frantisek Sumsal | c922c19d0e | |
Vishal Chillara Srinivas | 49c4cc2b8f | |
Vishal Chillara Srinivas | 28efd46614 | |
Vishal Chillara Srinivas | dfcc54ac15 | |
Yu Watanabe | 1ea1a79aa1 | |
Luca Boccassi | 7a9d0abe4d | |
Yu Watanabe | 6046cc3660 |
|
@ -684,6 +684,15 @@ fi</programlisting>
|
|||
<citerefentry><refentrytitle>file-hierarchy</refentrytitle><manvolnum>7</manvolnum></citerefentry>.</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>Notes</title>
|
||||
|
||||
<para>
|
||||
All example codes in this page are licensed under <literal>MIT No Attribution</literal>
|
||||
(SPDX-License-Identifier: MIT-0).
|
||||
</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>See Also</title>
|
||||
<para><simplelist type="inline">
|
||||
|
|
|
@ -302,7 +302,7 @@
|
|||
and running in an initrd equivalent to true, otherwise false. This implements a restricted subset of
|
||||
the per-unit setting of the same name, see
|
||||
<citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
|
||||
details: currently, the <literal>full</literal> or <literal>struct</literal> values are not
|
||||
details: currently, the <literal>full</literal> or <literal>strict</literal> values are not
|
||||
supported.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v256"/></listitem>
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include "fileio.h"
|
||||
#include "format-util.h"
|
||||
#include "hexdecoct.h"
|
||||
#include "iovec-util.h"
|
||||
#include "macro.h"
|
||||
#include "memory-util.h"
|
||||
#include "parse-util.h"
|
||||
|
@ -31,8 +32,7 @@ int decrypt_pkcs11_key(
|
|||
const char *key_file, /* We either expect key_file and associated parameters to be set (for file keys) … */
|
||||
size_t key_file_size,
|
||||
uint64_t key_file_offset,
|
||||
const void *key_data, /* … or key_data and key_data_size (for literal keys) */
|
||||
size_t key_data_size,
|
||||
const struct iovec *key_data, /* … or literal keys via key_data */
|
||||
usec_t until,
|
||||
AskPasswordFlags askpw_flags,
|
||||
void **ret_decrypted_key,
|
||||
|
@ -47,15 +47,15 @@ int decrypt_pkcs11_key(
|
|||
|
||||
assert(friendly_name);
|
||||
assert(pkcs11_uri);
|
||||
assert(key_file || key_data);
|
||||
assert(key_file || iovec_is_set(key_data));
|
||||
assert(ret_decrypted_key);
|
||||
assert(ret_decrypted_key_size);
|
||||
|
||||
/* The functions called here log about all errors, except for EAGAIN which means "token not found right now" */
|
||||
|
||||
if (key_data) {
|
||||
data.encrypted_key = (void*) key_data;
|
||||
data.encrypted_key_size = key_data_size;
|
||||
if (iovec_is_set(key_data)) {
|
||||
data.encrypted_key = (void*) key_data->iov_base;
|
||||
data.encrypted_key_size = key_data->iov_len;
|
||||
|
||||
data.free_encrypted_key = false;
|
||||
} else {
|
||||
|
|
|
@ -16,8 +16,7 @@ int decrypt_pkcs11_key(
|
|||
const char *key_file,
|
||||
size_t key_file_size,
|
||||
uint64_t key_file_offset,
|
||||
const void *key_data,
|
||||
size_t key_data_size,
|
||||
const struct iovec *key_data,
|
||||
usec_t until,
|
||||
AskPasswordFlags askpw_flags,
|
||||
void **ret_decrypted_key,
|
||||
|
@ -39,8 +38,7 @@ static inline int decrypt_pkcs11_key(
|
|||
const char *key_file,
|
||||
size_t key_file_size,
|
||||
uint64_t key_file_offset,
|
||||
const void *key_data,
|
||||
size_t key_data_size,
|
||||
const struct iovec *key_data,
|
||||
usec_t until,
|
||||
AskPasswordFlags askpw_flags,
|
||||
void **ret_decrypted_key,
|
||||
|
|
|
@ -1471,8 +1471,7 @@ static int attach_luks_or_plain_or_bitlk_by_fido2(
|
|||
struct crypt_device *cd,
|
||||
const char *name,
|
||||
const char *key_file,
|
||||
const void *key_data,
|
||||
size_t key_data_size,
|
||||
const struct iovec *key_data,
|
||||
usec_t until,
|
||||
uint32_t flags,
|
||||
bool pass_volume_key) {
|
||||
|
@ -1489,7 +1488,7 @@ static int attach_luks_or_plain_or_bitlk_by_fido2(
|
|||
assert(name);
|
||||
assert(arg_fido2_device || arg_fido2_device_auto);
|
||||
|
||||
if (arg_fido2_cid && !key_file && !key_data)
|
||||
if (arg_fido2_cid && !key_file && !iovec_is_set(key_data))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"FIDO2 mode with manual parameters selected, but no keyfile specified, refusing.");
|
||||
|
||||
|
@ -1513,7 +1512,7 @@ static int attach_luks_or_plain_or_bitlk_by_fido2(
|
|||
arg_fido2_rp_id,
|
||||
arg_fido2_cid, arg_fido2_cid_size,
|
||||
key_file, arg_keyfile_size, arg_keyfile_offset,
|
||||
key_data, key_data_size,
|
||||
key_data,
|
||||
until,
|
||||
arg_fido2_manual_flags,
|
||||
"cryptsetup.fido2-pin",
|
||||
|
@ -1623,8 +1622,7 @@ static int attach_luks_or_plain_or_bitlk_by_pkcs11(
|
|||
struct crypt_device *cd,
|
||||
const char *name,
|
||||
const char *key_file,
|
||||
const void *key_data,
|
||||
size_t key_data_size,
|
||||
const struct iovec *key_data,
|
||||
usec_t until,
|
||||
uint32_t flags,
|
||||
bool pass_volume_key) {
|
||||
|
@ -1635,6 +1633,7 @@ static int attach_luks_or_plain_or_bitlk_by_pkcs11(
|
|||
_cleanup_(erase_and_freep) void *decrypted_key = NULL;
|
||||
_cleanup_(sd_event_unrefp) sd_event *event = NULL;
|
||||
_cleanup_free_ void *discovered_key = NULL;
|
||||
struct iovec discovered_key_data = {};
|
||||
int keyslot = arg_key_slot, r;
|
||||
const char *uri = NULL;
|
||||
bool use_libcryptsetup_plugin = use_token_plugins();
|
||||
|
@ -1653,13 +1652,13 @@ static int attach_luks_or_plain_or_bitlk_by_pkcs11(
|
|||
return r;
|
||||
|
||||
uri = discovered_uri;
|
||||
key_data = discovered_key;
|
||||
key_data_size = discovered_key_size;
|
||||
discovered_key_data = IOVEC_MAKE(discovered_key, discovered_key_size);
|
||||
key_data = &discovered_key_data;
|
||||
}
|
||||
} else {
|
||||
uri = arg_pkcs11_uri;
|
||||
|
||||
if (!key_file && !key_data)
|
||||
if (!key_file && !iovec_is_set(key_data))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "PKCS#11 mode selected but no key file specified, refusing.");
|
||||
}
|
||||
|
||||
|
@ -1682,7 +1681,7 @@ static int attach_luks_or_plain_or_bitlk_by_pkcs11(
|
|||
friendly,
|
||||
uri,
|
||||
key_file, arg_keyfile_size, arg_keyfile_offset,
|
||||
key_data, key_data_size,
|
||||
key_data,
|
||||
until,
|
||||
arg_ask_password_flags,
|
||||
&decrypted_key, &decrypted_key_size);
|
||||
|
@ -2231,9 +2230,9 @@ static int attach_luks_or_plain_or_bitlk(
|
|||
if (token_type == TOKEN_TPM2)
|
||||
return attach_luks_or_plain_or_bitlk_by_tpm2(cd, name, key_file, key_data, until, flags, pass_volume_key);
|
||||
if (token_type == TOKEN_FIDO2)
|
||||
return attach_luks_or_plain_or_bitlk_by_fido2(cd, name, key_file, key_data->iov_base, key_data->iov_len, until, flags, pass_volume_key);
|
||||
return attach_luks_or_plain_or_bitlk_by_fido2(cd, name, key_file, key_data, until, flags, pass_volume_key);
|
||||
if (token_type == TOKEN_PKCS11)
|
||||
return attach_luks_or_plain_or_bitlk_by_pkcs11(cd, name, key_file, key_data->iov_base, key_data->iov_len, until, flags, pass_volume_key);
|
||||
return attach_luks_or_plain_or_bitlk_by_pkcs11(cd, name, key_file, key_data, until, flags, pass_volume_key);
|
||||
if (key_data)
|
||||
return attach_luks_or_plain_or_bitlk_by_key_data(cd, name, key_data, flags, pass_volume_key);
|
||||
if (key_file)
|
||||
|
|
|
@ -8,6 +8,7 @@ basic_dns_sources = files(
|
|||
'resolved-dns-rr.c',
|
||||
'resolved-dns-answer.c',
|
||||
'resolved-dns-question.c',
|
||||
'resolved-dns-browse-services.c',
|
||||
'resolved-util.c',
|
||||
'dns-type.c',
|
||||
)
|
||||
|
|
|
@ -1858,7 +1858,7 @@ static int bus_method_reset_server_features(sd_bus_message *message, void *userd
|
|||
}
|
||||
|
||||
static int dnssd_service_on_bus_track(sd_bus_track *t, void *userdata) {
|
||||
DnssdService *s = ASSERT_PTR(userdata);
|
||||
DnssdRegisteredService *s = ASSERT_PTR(userdata);
|
||||
|
||||
assert(t);
|
||||
|
||||
|
@ -1870,11 +1870,11 @@ static int dnssd_service_on_bus_track(sd_bus_track *t, void *userdata) {
|
|||
|
||||
static int bus_method_register_service(sd_bus_message *message, void *userdata, sd_bus_error *error) {
|
||||
_cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
|
||||
_cleanup_(dnssd_service_freep) DnssdService *service = NULL;
|
||||
_cleanup_(dnssd_service_freep) DnssdRegisteredService *service = NULL;
|
||||
_cleanup_(sd_bus_track_unrefp) sd_bus_track *bus_track = NULL;
|
||||
const char *id, *name_template, *type;
|
||||
_cleanup_free_ char *path = NULL;
|
||||
DnssdService *s = NULL;
|
||||
DnssdRegisteredService *s = NULL;
|
||||
Manager *m = ASSERT_PTR(userdata);
|
||||
uid_t euid;
|
||||
int r;
|
||||
|
@ -1884,7 +1884,7 @@ static int bus_method_register_service(sd_bus_message *message, void *userdata,
|
|||
if (m->mdns_support != RESOLVE_SUPPORT_YES)
|
||||
return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Support for MulticastDNS is disabled");
|
||||
|
||||
service = new0(DnssdService, 1);
|
||||
service = new0(DnssdRegisteredService, 1);
|
||||
if (!service)
|
||||
return log_oom();
|
||||
|
||||
|
@ -2046,7 +2046,7 @@ static int bus_method_register_service(sd_bus_message *message, void *userdata,
|
|||
|
||||
static int call_dnssd_method(Manager *m, sd_bus_message *message, sd_bus_message_handler_t handler, sd_bus_error *error) {
|
||||
_cleanup_free_ char *name = NULL;
|
||||
DnssdService *s = NULL;
|
||||
DnssdRegisteredService *s = NULL;
|
||||
const char *path;
|
||||
int r;
|
||||
|
||||
|
|
|
@ -0,0 +1,776 @@
|
|||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "resolved-dns-browse-services.h"
|
||||
#include "af-list.h"
|
||||
#include "event-util.h"
|
||||
#include "json-util.h"
|
||||
#include "random-util.h"
|
||||
#include "resolved-dns-cache.h"
|
||||
#include "resolved-varlink.h"
|
||||
#include "string-table.h"
|
||||
|
||||
typedef enum BrowseServiceUpdateFlag {
|
||||
BROWSE_SERVICE_UPDATE_ADDED,
|
||||
BROWSE_SERVICE_UPDATE_REMOVED,
|
||||
_BROWSE_SERVICE_UPDATE_MAX,
|
||||
_BROWSE_SERVICE_UPDATE_INVALID = -EINVAL,
|
||||
} BrowseServiceUpdateFlag;
|
||||
|
||||
static const char * const browse_service_update_flag_table[_BROWSE_SERVICE_UPDATE_MAX] = {
|
||||
[BROWSE_SERVICE_UPDATE_ADDED] = "added",
|
||||
[BROWSE_SERVICE_UPDATE_REMOVED] = "removed",
|
||||
};
|
||||
|
||||
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(browse_service_update_flag, BrowseServiceUpdateFlag);
|
||||
|
||||
/* RFC 6762 section 5.2 - The querier should plan to issue a query at 80% of
|
||||
* the record lifetime, and then if no answer is received, at 85%, 90%, and 95%.
|
||||
*/
|
||||
static usec_t mdns_maintenance_next_time(usec_t until, uint32_t ttl, DnsRecordTTLState ttl_state) {
|
||||
assert(ttl_state >= DNS_RECORD_TTL_STATE_80_PERCENT);
|
||||
assert(ttl_state <= _DNS_RECORD_TTL_STATE_MAX);
|
||||
|
||||
return usec_sub_unsigned(until, (20 - ttl_state * 5) * ttl * USEC_PER_SEC / 100);
|
||||
}
|
||||
|
||||
/* RFC 6762 section 5.2 - A random variation of 2% of the record TTL should
|
||||
* be added to maintenance queries. */
|
||||
static usec_t mdns_maintenance_jitter(uint32_t ttl) {
|
||||
return random_u64_range(2 * ttl * USEC_PER_SEC / 100);
|
||||
}
|
||||
|
||||
static void mdns_maintenance_query_complete(DnsQuery *q) {
|
||||
_cleanup_(dns_service_browser_unrefp) DnsServiceBrowser *sb = NULL;
|
||||
_cleanup_(dns_query_freep) DnsQuery *query = q;
|
||||
DnssdDiscoveredService *service = NULL;
|
||||
int r;
|
||||
|
||||
assert(query);
|
||||
assert(query->manager);
|
||||
|
||||
if (query->state != DNS_TRANSACTION_SUCCESS)
|
||||
return;
|
||||
|
||||
service = dns_service_ref(query->dnsservice_request);
|
||||
|
||||
if (!service)
|
||||
return;
|
||||
|
||||
sb = dns_service_browser_ref(service->service_browser);
|
||||
|
||||
if (!sb)
|
||||
return;
|
||||
|
||||
r = dns_answer_match_key(query->answer, sb->key, NULL);
|
||||
if (r <= 0) {
|
||||
log_error_errno(r, "mDNS answer does not match service browser key: %m");
|
||||
return;
|
||||
}
|
||||
|
||||
r = mdns_browser_revisit_cache(sb, query->answer_family);
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to mDNS revisit cache for family %d: %m", query->answer_family);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static int mdns_maintenance_query(sd_event_source *s, uint64_t usec, void *userdata) {
|
||||
DnssdDiscoveredService *service = NULL;
|
||||
_cleanup_(dns_query_freep) DnsQuery *q = NULL;
|
||||
int r;
|
||||
|
||||
assert(userdata);
|
||||
|
||||
service = userdata;
|
||||
|
||||
/* Check if the TTL state has reached the maximum value, then revisit
|
||||
* cache */
|
||||
if (service->rr_ttl_state++ == _DNS_RECORD_TTL_STATE_MAX)
|
||||
return mdns_browser_revisit_cache(service->service_browser, service->family);
|
||||
|
||||
/* Create a new DNS query */
|
||||
r = dns_query_new(
|
||||
service->service_browser->manager,
|
||||
&q,
|
||||
service->service_browser->question_utf8,
|
||||
service->service_browser->question_idna,
|
||||
/* question_bypass= */ NULL,
|
||||
service->service_browser->ifindex,
|
||||
service->service_browser->flags);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to create mDNS query for maintenance: %m");
|
||||
|
||||
q->complete = mdns_maintenance_query_complete;
|
||||
q->varlink_request = sd_varlink_ref(service->service_browser->link);
|
||||
q->dnsservice_request = dns_service_ref(service);
|
||||
|
||||
/* Schedule the next maintenance query based on the TTL */
|
||||
usec_t next_time = mdns_maintenance_next_time(service->until, service->rr->ttl, service->rr_ttl_state);
|
||||
|
||||
r = event_reset_time(
|
||||
service->service_browser->manager->event,
|
||||
&service->schedule_event,
|
||||
CLOCK_BOOTTIME,
|
||||
next_time,
|
||||
/* accuracy= */ 0,
|
||||
mdns_maintenance_query,
|
||||
service,
|
||||
/* priority= */ 0,
|
||||
"mdns-next-query-schedule",
|
||||
/* force_reset= */ true);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to schedule next mDNS maintenance query: %m");
|
||||
|
||||
/* Perform the query */
|
||||
r = dns_query_go(q);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to send mDNS maintenance query: %m");
|
||||
|
||||
TAKE_PTR(q);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dns_add_new_service(DnsServiceBrowser *sb, DnsResourceRecord *rr, int owner_family) {
|
||||
_cleanup_(dns_service_unrefp) DnssdDiscoveredService *s = NULL;
|
||||
int r;
|
||||
|
||||
assert(sb);
|
||||
assert(rr);
|
||||
|
||||
s = new (DnssdDiscoveredService, 1);
|
||||
if (!s) {
|
||||
log_oom();
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
usec_t usec = now(CLOCK_BOOTTIME);
|
||||
|
||||
*s = (DnssdDiscoveredService) {
|
||||
.n_ref = 1,
|
||||
.service_browser = sb,
|
||||
.rr = dns_resource_record_copy(rr),
|
||||
.family = owner_family,
|
||||
.until = rr->until,
|
||||
.query = NULL,
|
||||
.rr_ttl_state = DNS_RECORD_TTL_STATE_80_PERCENT,
|
||||
};
|
||||
|
||||
LIST_PREPEND(dns_services, sb->dns_services, s);
|
||||
|
||||
/* Schedule the first cache maintenance query at 80% of the record's
|
||||
* TTL. Subsequent queries issued at 5% increments until 100% of the
|
||||
* TTL. RFC 6762 section 5.2. If service is being added after 80% of the
|
||||
* TTL has already elapsed, schedule the next query at the next 5%
|
||||
* increment. */
|
||||
usec_t next_time = 0;
|
||||
while (s->rr_ttl_state >= DNS_RECORD_TTL_STATE_80_PERCENT &&
|
||||
s->rr_ttl_state <= _DNS_RECORD_TTL_STATE_MAX) {
|
||||
next_time = mdns_maintenance_next_time(rr->until, rr->ttl, s->rr_ttl_state);
|
||||
if (next_time >= usec)
|
||||
break;
|
||||
|
||||
s->rr_ttl_state++;
|
||||
}
|
||||
|
||||
if (next_time < usec) {
|
||||
/* If next_time is still in the past, the service is being added
|
||||
* after it has already expired. Just schedule a 100%
|
||||
* maintenance query */
|
||||
next_time = usec + USEC_PER_SEC;
|
||||
s->rr_ttl_state = _DNS_RECORD_TTL_STATE_MAX;
|
||||
}
|
||||
|
||||
usec_t jitter = mdns_maintenance_jitter(rr->ttl);
|
||||
|
||||
r = sd_event_add_time(
|
||||
sb->manager->event,
|
||||
&s->schedule_event,
|
||||
CLOCK_BOOTTIME,
|
||||
usec_add(next_time, jitter),
|
||||
/* accuracy= */ 0,
|
||||
mdns_maintenance_query,
|
||||
s);
|
||||
if (r < 0) {
|
||||
return log_error_errno(
|
||||
r,
|
||||
"Failed to schedule mDNS maintenance query "
|
||||
"for DNS service: %m");
|
||||
}
|
||||
TAKE_PTR(s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dns_remove_service(DnsServiceBrowser *sb, DnssdDiscoveredService *service) {
|
||||
assert(sb);
|
||||
assert(service);
|
||||
|
||||
LIST_REMOVE(dns_services, sb->dns_services, service);
|
||||
dns_service_unref(service);
|
||||
}
|
||||
|
||||
DnssdDiscoveredService *dns_service_free(DnssdDiscoveredService *service) {
|
||||
if (!service)
|
||||
return NULL;
|
||||
|
||||
sd_event_source_disable_unref(service->schedule_event);
|
||||
|
||||
if (service->query && DNS_TRANSACTION_IS_LIVE(service->query->state))
|
||||
dns_query_complete(service->query, DNS_TRANSACTION_ABORTED);
|
||||
|
||||
dns_resource_record_unref(service->rr);
|
||||
|
||||
return mfree(service);
|
||||
}
|
||||
|
||||
DEFINE_TRIVIAL_REF_UNREF_FUNC(DnssdDiscoveredService, dns_service, dns_service_free);
|
||||
|
||||
int mdns_service_update(DnssdDiscoveredService *service, DnsResourceRecord *rr, usec_t t) {
|
||||
service->until = rr->until;
|
||||
service->rr->ttl = rr->ttl;
|
||||
|
||||
/* Update the 80% TTL maintenance event based on new record received
|
||||
* from the network. RFC 6762 section 5.2 */
|
||||
usec_t next_time = mdns_maintenance_next_time(
|
||||
service->until, service->rr->ttl, DNS_RECORD_TTL_STATE_80_PERCENT);
|
||||
usec_t jitter = mdns_maintenance_jitter(service->rr->ttl);
|
||||
|
||||
if (service->schedule_event)
|
||||
return sd_event_source_set_time(service->schedule_event, usec_add(next_time, jitter));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool dns_service_contains(DnssdDiscoveredService *services, DnsResourceRecord *rr, int owner_family) {
|
||||
usec_t t = now(CLOCK_BOOTTIME);
|
||||
|
||||
LIST_FOREACH(dns_services, service, services)
|
||||
if (dns_resource_record_equal(rr, service->rr) > 0 && service->family == owner_family) {
|
||||
if (rr->ttl <= 1)
|
||||
return true;
|
||||
|
||||
if (rr->until > service->until)
|
||||
mdns_service_update(service, rr, t);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void dns_browse_services_purge(Manager *m, int family) {
|
||||
int r = 0;
|
||||
|
||||
/* Called after caches are flushed.
|
||||
* Clear local service records and notify varlink client. */
|
||||
if (!(m && m->dns_service_browsers))
|
||||
return;
|
||||
|
||||
DnsServiceBrowser *sb;
|
||||
HASHMAP_FOREACH(sb, m->dns_service_browsers) {
|
||||
r = sd_event_source_set_enabled(sb->schedule_event, SD_EVENT_OFF);
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to disable event source for service browser: %m");
|
||||
return;
|
||||
}
|
||||
|
||||
if (family == AF_UNSPEC) {
|
||||
r = mdns_browser_revisit_cache(sb, AF_INET);
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to revisit cache for IPv4: %m");
|
||||
return;
|
||||
}
|
||||
r = mdns_browser_revisit_cache(sb, AF_INET6);
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to revisit cache for IPv6: %m");
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
r = mdns_browser_revisit_cache(sb, family);
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to revisit cache for family %d: %m", family);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int mdns_manage_services_answer(DnsServiceBrowser *sb, DnsAnswer *answer, int owner_family) {
|
||||
DnsResourceRecord *i;
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *array = NULL;
|
||||
int r;
|
||||
|
||||
assert(sb);
|
||||
|
||||
/* Check for new service added */
|
||||
DNS_ANSWER_FOREACH(i, answer) {
|
||||
_cleanup_free_ char *name = NULL, *type = NULL, *domain = NULL;
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *entry = NULL;
|
||||
|
||||
if (dns_service_contains(sb->dns_services, i, owner_family))
|
||||
continue;
|
||||
|
||||
r = dns_service_split(i->ptr.name, &name, &type, &domain);
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to split DNS service name: %m");
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if (!name) {
|
||||
type = mfree(type);
|
||||
domain = mfree(domain);
|
||||
r = dns_service_split(dns_resource_key_name(i->key), &name, &type, &domain);
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to split DNS service name (fallback): %m");
|
||||
goto finish;
|
||||
}
|
||||
}
|
||||
|
||||
if (!type)
|
||||
continue;
|
||||
|
||||
r = dns_add_new_service(sb, i, owner_family);
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to add new DNS service: %m");
|
||||
goto finish;
|
||||
}
|
||||
|
||||
log_debug("Add into the list %s, %s, %s, %s, %d",
|
||||
strna(name),
|
||||
strna(type),
|
||||
strna(domain),
|
||||
strna(af_to_ipv4_ipv6(owner_family)),
|
||||
sb->ifindex);
|
||||
|
||||
r = sd_json_buildo(
|
||||
&entry,
|
||||
SD_JSON_BUILD_PAIR(
|
||||
"updateFlag",
|
||||
SD_JSON_BUILD_STRING(browse_service_update_flag_to_string(
|
||||
BROWSE_SERVICE_UPDATE_ADDED))),
|
||||
SD_JSON_BUILD_PAIR("family", SD_JSON_BUILD_INTEGER(owner_family)),
|
||||
SD_JSON_BUILD_PAIR_CONDITION(
|
||||
!isempty(name), "name", SD_JSON_BUILD_STRING(name)),
|
||||
SD_JSON_BUILD_PAIR_CONDITION(
|
||||
!isempty(type), "type", SD_JSON_BUILD_STRING(type)),
|
||||
SD_JSON_BUILD_PAIR_CONDITION(
|
||||
!isempty(domain), "domain", SD_JSON_BUILD_STRING(domain)),
|
||||
SD_JSON_BUILD_PAIR("ifindex", SD_JSON_BUILD_INTEGER(sb->ifindex)));
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to build JSON for new service: %m");
|
||||
goto finish;
|
||||
}
|
||||
|
||||
r = sd_json_variant_append_array(&array, entry);
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to append JSON entry to array: %m");
|
||||
goto finish;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check for services removed */
|
||||
LIST_FOREACH(dns_services, service, sb->dns_services) {
|
||||
_cleanup_free_ char *name = NULL, *type = NULL, *domain = NULL;
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *entry = NULL;
|
||||
|
||||
if (service->family != owner_family)
|
||||
continue;
|
||||
|
||||
if (dns_answer_contains(answer, service->rr))
|
||||
continue;
|
||||
|
||||
r = dns_service_split(service->rr->ptr.name, &name, &type, &domain);
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to split DNS service name from list: %m");
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if (!name) {
|
||||
type = mfree(type);
|
||||
domain = mfree(domain);
|
||||
r = dns_service_split(dns_resource_key_name(service->rr->key), &name, &type, &domain);
|
||||
if (r < 0) {
|
||||
log_error_errno(r,
|
||||
"Failed to split DNS service name (fallback) from list: %m");
|
||||
goto finish;
|
||||
}
|
||||
}
|
||||
|
||||
dns_remove_service(sb, service);
|
||||
|
||||
log_debug("Remove from the list %s, %s, %s, %s, %d",
|
||||
strna(name),
|
||||
strna(type),
|
||||
strna(domain),
|
||||
strna(af_to_ipv4_ipv6(owner_family)),
|
||||
sb->ifindex);
|
||||
|
||||
r = sd_json_buildo(
|
||||
&entry,
|
||||
SD_JSON_BUILD_PAIR(
|
||||
"updateFlag",
|
||||
SD_JSON_BUILD_STRING(browse_service_update_flag_to_string(
|
||||
BROWSE_SERVICE_UPDATE_REMOVED))),
|
||||
SD_JSON_BUILD_PAIR("family", SD_JSON_BUILD_INTEGER(owner_family)),
|
||||
SD_JSON_BUILD_PAIR("name", SD_JSON_BUILD_STRING(name ?: "")),
|
||||
SD_JSON_BUILD_PAIR("type", SD_JSON_BUILD_STRING(type ?: "")),
|
||||
SD_JSON_BUILD_PAIR("domain", SD_JSON_BUILD_STRING(domain ?: "")),
|
||||
SD_JSON_BUILD_PAIR("ifindex", SD_JSON_BUILD_INTEGER(sb->ifindex)));
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to build JSON for removed service: %m");
|
||||
goto finish;
|
||||
}
|
||||
|
||||
r = sd_json_variant_append_array(&array, entry);
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to append JSON entry to array: %m");
|
||||
goto finish;
|
||||
}
|
||||
}
|
||||
|
||||
if (!sd_json_variant_is_blank_array(array)) {
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *vm = NULL;
|
||||
|
||||
r = sd_json_buildo(&vm, SD_JSON_BUILD_PAIR("browserServiceData", SD_JSON_BUILD_VARIANT(array)));
|
||||
if (r < 0) {
|
||||
log_error_errno(r,
|
||||
"Failed to build JSON object for "
|
||||
"browser service data: %m");
|
||||
goto finish;
|
||||
}
|
||||
|
||||
r = sd_varlink_notify(sb->link, vm);
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to notify via varlink: %m");
|
||||
goto finish;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
finish:
|
||||
log_error_errno(r, "Failed to process received services: %m");
|
||||
return sd_varlink_error_errno(sb->link, r);
|
||||
}
|
||||
|
||||
int mdns_browser_revisit_cache(DnsServiceBrowser *sb, int owner_family) {
|
||||
_cleanup_(dns_answer_unrefp) DnsAnswer *lookup_ret_answer = NULL;
|
||||
DnsScope *scope;
|
||||
int r;
|
||||
|
||||
assert(sb);
|
||||
assert(sb->manager);
|
||||
|
||||
scope = manager_find_scope_from_protocol(sb->manager, sb->ifindex, DNS_PROTOCOL_MDNS, owner_family);
|
||||
if (!scope)
|
||||
return 0;
|
||||
|
||||
dns_cache_prune(&scope->cache);
|
||||
|
||||
r = dns_cache_lookup(&scope->cache, sb->key, sb->flags, NULL, &lookup_ret_answer, NULL, NULL, NULL);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to look up DNS cache for service browser key: %m");
|
||||
|
||||
r = mdns_manage_services_answer(sb, lookup_ret_answer, owner_family);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to manage mDNS services after cache lookup: %m");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mdns_notify_browsers_goodbye(DnsScope *scope) {
|
||||
DnsServiceBrowser *sb = NULL;
|
||||
int r;
|
||||
|
||||
if (!scope)
|
||||
return 0;
|
||||
|
||||
HASHMAP_FOREACH(sb, scope->manager->dns_service_browsers) {
|
||||
r = mdns_browser_revisit_cache(sb, scope->family);
|
||||
if (r < 0)
|
||||
return log_error_errno(
|
||||
r,
|
||||
"Failed to revisit cache for service "
|
||||
"browser with family %d: %m",
|
||||
scope->family);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mdns_notify_browsers_unsolicited_updates(Manager *m, DnsAnswer *answer, int owner_family) {
|
||||
DnsServiceBrowser *sb = NULL;
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
|
||||
if (!answer)
|
||||
return 0;
|
||||
|
||||
if (!m->dns_service_browsers)
|
||||
return 0;
|
||||
|
||||
HASHMAP_FOREACH(sb, m->dns_service_browsers) {
|
||||
|
||||
r = dns_answer_match_key(answer, sb->key, NULL);
|
||||
if (r < 0) {
|
||||
return log_error_errno(
|
||||
r,
|
||||
"Failed to match answer key with "
|
||||
"service browser's key: %m");
|
||||
}
|
||||
if (r == 0)
|
||||
continue;
|
||||
|
||||
r = mdns_browser_revisit_cache(sb, owner_family);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to revisit cache for service browser: %m");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mdns_browse_service_query_complete(DnsQuery *q) {
|
||||
_cleanup_(dns_service_browser_unrefp) DnsServiceBrowser *sb = NULL;
|
||||
_cleanup_(dns_query_freep) DnsQuery *query = q;
|
||||
int r;
|
||||
|
||||
assert(query);
|
||||
assert(query->manager);
|
||||
|
||||
if (query->state != DNS_TRANSACTION_SUCCESS)
|
||||
return;
|
||||
|
||||
sb = dns_service_browser_ref(query->service_browser_request);
|
||||
if (!sb)
|
||||
return;
|
||||
|
||||
r = dns_answer_match_key(query->answer, sb->key, NULL);
|
||||
if (r < 0) {
|
||||
log_error_errno(r,
|
||||
"Failed to match answer key with service "
|
||||
"browser's key: %m");
|
||||
return;
|
||||
}
|
||||
|
||||
if (r == 0)
|
||||
return;
|
||||
|
||||
r = mdns_browser_revisit_cache(sb, query->answer_family);
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to revisit cache for service browser: %m");
|
||||
return;
|
||||
}
|
||||
|
||||
/* When the query is answered from cache, we only get answers for one
|
||||
* answer_family i.e. either ipv4 or ipv6. We need to perform another
|
||||
* cache lookup for the other answer_family */
|
||||
if (query->answer_query_flags == SD_RESOLVED_FROM_CACHE) {
|
||||
r = mdns_browser_revisit_cache(sb, query->answer_family == AF_INET ? AF_INET6 : AF_INET);
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to revisit cache for service browser: %m");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int mdns_next_query_schedule(sd_event_source *s, uint64_t usec, void *userdata) {
|
||||
_cleanup_(dns_service_browser_unrefp) DnsServiceBrowser *sb = NULL;
|
||||
_cleanup_(dns_query_freep) DnsQuery *q = NULL;
|
||||
int r;
|
||||
|
||||
assert(userdata);
|
||||
|
||||
sb = dns_service_browser_ref(userdata);
|
||||
if (!sb)
|
||||
return log_error_errno(0, "Failed to reference service browser: %m");
|
||||
|
||||
/* Enable the answer from the cache for the very first query */
|
||||
if (sb->delay == 0) {
|
||||
SET_FLAG(sb->flags, SD_RESOLVED_NO_CACHE, false);
|
||||
sb->delay++;
|
||||
}
|
||||
|
||||
r = dns_query_new(sb->manager, &q, sb->question_utf8, sb->question_idna, NULL, sb->ifindex, sb->flags);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to create new DNS query: %m");
|
||||
|
||||
q->complete = mdns_browse_service_query_complete;
|
||||
q->service_browser_request = dns_service_browser_ref(sb);
|
||||
q->varlink_request = sd_varlink_ref(sb->link);
|
||||
|
||||
if (!q->varlink_request) {
|
||||
log_error_errno(0, "Failed to reference varlink request: %m");
|
||||
dns_query_free(q);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
sd_varlink_set_userdata(sb->link, q);
|
||||
|
||||
r = dns_query_go(q);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to send DNS query: %m");
|
||||
|
||||
/* RFC6762 5.2
|
||||
* The intervals between successive queries MUST increase by at least a
|
||||
* factor of two. When the interval between queries reaches or exceeds
|
||||
* 60 minutes,perform subsequent queries at a steady-state rate of one
|
||||
* query per hour */
|
||||
|
||||
sb->delay = sb->delay < 2048 ? sb->delay * 2 : 3600;
|
||||
|
||||
SET_FLAG(sb->flags, SD_RESOLVED_NO_CACHE, true);
|
||||
|
||||
r = event_reset_time_relative(
|
||||
sb->manager->event,
|
||||
&sb->schedule_event,
|
||||
CLOCK_BOOTTIME,
|
||||
(sb->delay * USEC_PER_SEC),
|
||||
/* accuracy= */ 0,
|
||||
mdns_next_query_schedule,
|
||||
sb,
|
||||
/* priority= */ 0,
|
||||
"mdns-next-query-schedule",
|
||||
/* force_reset= */ true);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to reset event time for next query schedule: %m");
|
||||
|
||||
TAKE_PTR(q);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dns_browse_services_restart(Manager *m) {
|
||||
int r;
|
||||
|
||||
if (!(m && m->dns_service_browsers))
|
||||
return;
|
||||
|
||||
DnsServiceBrowser *sb;
|
||||
|
||||
HASHMAP_FOREACH(sb, m->dns_service_browsers) {
|
||||
sb->delay = 0;
|
||||
|
||||
r = event_reset_time_relative(
|
||||
sb->manager->event,
|
||||
&sb->schedule_event,
|
||||
CLOCK_BOOTTIME,
|
||||
(sb->delay * USEC_PER_SEC),
|
||||
/* accuracy= */ 0,
|
||||
mdns_next_query_schedule,
|
||||
sb,
|
||||
/* priority= */ 0,
|
||||
"mdns-next-query-schedule",
|
||||
/* force_reset= */ true);
|
||||
|
||||
if (r < 0) {
|
||||
log_error_errno(r,
|
||||
"Failed to reset mDNS service subscriber event "
|
||||
"for service browser: %m");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int dns_subscribe_browse_service(
|
||||
Manager *m, sd_varlink *link, const char *domain, const char *type, int ifindex, uint64_t flags) {
|
||||
|
||||
_cleanup_(dns_service_browser_unrefp) DnsServiceBrowser *sb = NULL;
|
||||
_cleanup_(dns_question_unrefp) DnsQuestion *question_idna = NULL, *question_utf8 = NULL;
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
assert(link);
|
||||
|
||||
if (ifindex < 0)
|
||||
return sd_varlink_error_invalid_parameter_name(link, "ifindex");
|
||||
|
||||
if (isempty(type))
|
||||
type = NULL;
|
||||
else if (!dnssd_srv_type_is_valid(type))
|
||||
return sd_varlink_error_invalid_parameter_name(link, "type");
|
||||
|
||||
if (isempty(domain))
|
||||
domain = "local";
|
||||
|
||||
r = dns_name_is_valid(domain);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
return sd_varlink_error_invalid_parameter_name(link, "domain");
|
||||
|
||||
r = dns_question_new_service_type(
|
||||
&question_utf8, /* name= */ NULL, type, domain, /* convert_idna= */ false, DNS_TYPE_PTR);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to create DNS question for UTF8 version: %m");
|
||||
|
||||
r = dns_question_new_service_type(
|
||||
&question_idna, /* name= */ NULL, type, domain, /* convert_idna= */ true, DNS_TYPE_PTR);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to create DNS question for IDNA version: %m");
|
||||
|
||||
sb = new (DnsServiceBrowser, 1);
|
||||
if (!sb)
|
||||
return log_oom();
|
||||
|
||||
*sb = (DnsServiceBrowser) {
|
||||
.n_ref = 1,
|
||||
.manager = m,
|
||||
.link = sd_varlink_ref(link),
|
||||
.question_utf8 = dns_question_ref(question_utf8),
|
||||
.question_idna = dns_question_ref(question_idna),
|
||||
.key = dns_question_first_key(question_utf8),
|
||||
.ifindex = ifindex,
|
||||
.flags = flags,
|
||||
};
|
||||
|
||||
/* Only mDNS continuous querying is currently supported. See RFC 6762 */
|
||||
switch (flags & SD_RESOLVED_PROTOCOLS_ALL) {
|
||||
case SD_RESOLVED_MDNS:
|
||||
r = sd_event_add_time_relative(
|
||||
m->event,
|
||||
&sb->schedule_event,
|
||||
CLOCK_BOOTTIME,
|
||||
(sb->delay * USEC_PER_SEC),
|
||||
/* accuracy= */ 0,
|
||||
mdns_next_query_schedule,
|
||||
sb);
|
||||
if (r < 0)
|
||||
return r;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
r = hashmap_ensure_put(&m->dns_service_browsers, NULL, link, sb);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to add service browser to the hashmap: %m");
|
||||
|
||||
TAKE_PTR(sb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DnsServiceBrowser *dns_service_browser_free(DnsServiceBrowser *sb) {
|
||||
DnsQuery *q;
|
||||
|
||||
if (!sb)
|
||||
return NULL;
|
||||
|
||||
while (sb->dns_services)
|
||||
dns_remove_service(sb, sb->dns_services);
|
||||
|
||||
sd_event_source_disable_unref(sb->schedule_event);
|
||||
|
||||
q = sd_varlink_get_userdata(sb->link);
|
||||
if (q && DNS_TRANSACTION_IS_LIVE(q->state))
|
||||
dns_query_complete(q, DNS_TRANSACTION_ABORTED);
|
||||
|
||||
dns_question_unref(sb->question_idna);
|
||||
dns_question_unref(sb->question_utf8);
|
||||
|
||||
sd_varlink_unref(sb->link);
|
||||
|
||||
return mfree(sb);
|
||||
}
|
||||
|
||||
DEFINE_TRIVIAL_REF_UNREF_FUNC(DnsServiceBrowser, dns_service_browser, dns_service_browser_free);
|
|
@ -0,0 +1,80 @@
|
|||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
typedef struct DnsServiceBrowser DnsServiceBrowser;
|
||||
typedef struct DnssdDiscoveredService DnssdDiscoveredService;
|
||||
|
||||
#include "sd-varlink.h"
|
||||
#include "resolved-dns-query.h"
|
||||
#include "resolved-manager.h"
|
||||
|
||||
|
||||
typedef enum DnsRecordTTLState DnsRecordTTLState;
|
||||
|
||||
enum DnsRecordTTLState {
|
||||
DNS_RECORD_TTL_STATE_80_PERCENT,
|
||||
DNS_RECORD_TTL_STATE_85_PERCENT,
|
||||
DNS_RECORD_TTL_STATE_90_PERCENT,
|
||||
DNS_RECORD_TTL_STATE_95_PERCENT,
|
||||
_DNS_RECORD_TTL_STATE_MAX,
|
||||
_DNS_RECORD_TTL_STATE_MAX_INVALID = -EINVAL
|
||||
};
|
||||
|
||||
struct DnssdDiscoveredService {
|
||||
unsigned n_ref;
|
||||
DnsServiceBrowser *service_browser;
|
||||
sd_event_source *schedule_event;
|
||||
DnsResourceRecord *rr;
|
||||
int family;
|
||||
usec_t until;
|
||||
DnsRecordTTLState rr_ttl_state;
|
||||
DnsQuery *query;
|
||||
LIST_FIELDS(DnssdDiscoveredService, dns_services);
|
||||
};
|
||||
|
||||
struct DnsServiceBrowser {
|
||||
unsigned n_ref;
|
||||
Manager *manager;
|
||||
sd_varlink *link;
|
||||
DnsQuestion *question_idna;
|
||||
DnsQuestion *question_utf8;
|
||||
uint64_t flags;
|
||||
sd_event_source *schedule_event;
|
||||
usec_t delay;
|
||||
DnsResourceKey *key;
|
||||
int ifindex;
|
||||
uint64_t token;
|
||||
LIST_HEAD(DnssdDiscoveredService, dns_services);
|
||||
};
|
||||
|
||||
DnsServiceBrowser *dns_service_browser_free(DnsServiceBrowser *sb);
|
||||
void dns_remove_service(DnsServiceBrowser *sb, DnssdDiscoveredService *service);
|
||||
DnssdDiscoveredService *dns_service_free(DnssdDiscoveredService *service);
|
||||
|
||||
DnsServiceBrowser *dns_service_browser_ref(DnsServiceBrowser *sb);
|
||||
DnsServiceBrowser *dns_service_browser_unref(DnsServiceBrowser *sb);
|
||||
|
||||
DnssdDiscoveredService *dns_service_ref(DnssdDiscoveredService *service);
|
||||
DnssdDiscoveredService *dns_service_unref(DnssdDiscoveredService *service);
|
||||
|
||||
void dns_browse_services_purge(Manager *m, int family);
|
||||
void dns_browse_services_restart(Manager *m);
|
||||
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(DnsServiceBrowser *, dns_service_browser_unref);
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(DnssdDiscoveredService *, dns_service_unref);
|
||||
|
||||
bool dns_service_contains(DnssdDiscoveredService *services, DnsResourceRecord *rr, int owner_family);
|
||||
int mdns_manage_services_answer(DnsServiceBrowser *sb, DnsAnswer *answer, int owner_family);
|
||||
int dns_add_new_service(DnsServiceBrowser *sb, DnsResourceRecord *rr, int owner_family);
|
||||
int mdns_service_update(DnssdDiscoveredService *service, DnsResourceRecord *rr, usec_t t);
|
||||
int mdns_browser_revisit_cache(DnsServiceBrowser *sb, int owner_family);
|
||||
int dns_subscribe_browse_service(
|
||||
Manager *m,
|
||||
sd_varlink *link,
|
||||
const char *domain,
|
||||
const char *type,
|
||||
int ifindex,
|
||||
uint64_t flags);
|
||||
int mdns_notify_browsers_unsolicited_updates(Manager *m, DnsAnswer *answer, int owner_family);
|
||||
int mdns_notify_browsers_goodbye(DnsScope *scope);
|
|
@ -1011,6 +1011,7 @@ static int answer_add_clamp_ttl(
|
|||
}
|
||||
}
|
||||
|
||||
rr->until = until;
|
||||
r = dns_answer_add_extend(answer, rr, ifindex, answer_flags, rrsig);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
|
|
@ -456,6 +456,12 @@ DnsQuery *dns_query_free(DnsQuery *q) {
|
|||
|
||||
free(q->request_address_string);
|
||||
|
||||
if (q->dnsservice_request)
|
||||
dns_service_unref(q->dnsservice_request);
|
||||
|
||||
if (q->service_browser_request)
|
||||
dns_service_browser_unref(q->service_browser_request);
|
||||
|
||||
if (q->manager) {
|
||||
LIST_REMOVE(queries, q->manager->dns_queries, q);
|
||||
q->manager->n_dns_queries--;
|
||||
|
|
|
@ -11,6 +11,7 @@ typedef struct DnsQuery DnsQuery;
|
|||
typedef struct DnsStubListenerExtra DnsStubListenerExtra;
|
||||
|
||||
#include "resolved-dns-answer.h"
|
||||
#include "resolved-dns-browse-services.h"
|
||||
#include "resolved-dns-question.h"
|
||||
#include "resolved-dns-search-domain.h"
|
||||
#include "resolved-dns-transaction.h"
|
||||
|
@ -111,6 +112,10 @@ struct DnsQuery {
|
|||
DnsAnswer *reply_additional;
|
||||
DnsStubListenerExtra *stub_listener_extra;
|
||||
|
||||
/* Browser Service and Dnssd Discovered Service Information */
|
||||
DnssdDiscoveredService *dnsservice_request;
|
||||
DnsServiceBrowser *service_browser_request;
|
||||
|
||||
/* Completion callback */
|
||||
void (*complete)(DnsQuery* q);
|
||||
|
||||
|
|
|
@ -416,6 +416,66 @@ int dns_question_new_reverse(DnsQuestion **ret, int family, const union in_addr_
|
|||
return 0;
|
||||
}
|
||||
|
||||
int dns_question_new_service_type(
|
||||
DnsQuestion **ret,
|
||||
const char *service,
|
||||
const char *type,
|
||||
const char *domain,
|
||||
bool convert_idna,
|
||||
uint16_t record_type) {
|
||||
|
||||
_cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
|
||||
_cleanup_(dns_question_unrefp) DnsQuestion *q = NULL;
|
||||
_cleanup_free_ char *buf = NULL, *joined = NULL;
|
||||
const char *name;
|
||||
int r;
|
||||
|
||||
assert(ret);
|
||||
|
||||
if (record_type == DNS_TYPE_SRV)
|
||||
return -EINVAL;
|
||||
|
||||
if (!domain)
|
||||
return -EINVAL;
|
||||
|
||||
if (type) {
|
||||
if (convert_idna) {
|
||||
r = dns_name_apply_idna(domain, &buf);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r > 0)
|
||||
domain = buf;
|
||||
}
|
||||
|
||||
r = dns_service_join(service, type, domain, &joined);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
name = joined;
|
||||
} else {
|
||||
if (service)
|
||||
return -EINVAL;
|
||||
|
||||
name = domain;
|
||||
}
|
||||
|
||||
q = dns_question_new(1);
|
||||
if (!q)
|
||||
return -ENOMEM;
|
||||
|
||||
key = dns_resource_key_new(DNS_CLASS_IN, record_type, name);
|
||||
if (!key)
|
||||
return -ENOMEM;
|
||||
|
||||
r = dns_question_add(q, key, 0);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*ret = TAKE_PTR(q);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dns_question_new_service(
|
||||
DnsQuestion **ret,
|
||||
const char *service,
|
||||
|
|
|
@ -31,6 +31,7 @@ DnsQuestion *dns_question_unref(DnsQuestion *q);
|
|||
int dns_question_new_address(DnsQuestion **ret, int family, const char *name, bool convert_idna);
|
||||
int dns_question_new_reverse(DnsQuestion **ret, int family, const union in_addr_union *a);
|
||||
int dns_question_new_service(DnsQuestion **ret, const char *service, const char *type, const char *domain, bool with_txt, bool convert_idna);
|
||||
int dns_question_new_service_type(DnsQuestion **ret, const char *service, const char *type, const char *domain, bool convert_idna, uint16_t record_type);
|
||||
|
||||
int dns_question_add_raw(DnsQuestion *q, DnsResourceKey *key, DnsQuestionFlags flags);
|
||||
int dns_question_add(DnsQuestion *q, DnsResourceKey *key, DnsQuestionFlags flags);
|
||||
|
|
|
@ -398,6 +398,7 @@ DnsResourceRecord* dns_resource_record_new(DnsResourceKey *key) {
|
|||
.n_ref = 1,
|
||||
.key = dns_resource_key_ref(key),
|
||||
.expiry = USEC_INFINITY,
|
||||
.until = USEC_INFINITY,
|
||||
.n_skip_labels_signer = UINT8_MAX,
|
||||
.n_skip_labels_source = UINT8_MAX,
|
||||
};
|
||||
|
@ -1704,6 +1705,7 @@ DnsResourceRecord *dns_resource_record_copy(DnsResourceRecord *rr) {
|
|||
|
||||
copy->ttl = rr->ttl;
|
||||
copy->expiry = rr->expiry;
|
||||
copy->until = rr->until;
|
||||
copy->n_skip_labels_signer = rr->n_skip_labels_signer;
|
||||
copy->n_skip_labels_source = rr->n_skip_labels_source;
|
||||
copy->unparsable = rr->unparsable;
|
||||
|
|
|
@ -107,6 +107,8 @@ struct DnsResourceRecord {
|
|||
unsigned n_ref;
|
||||
uint32_t ttl;
|
||||
usec_t expiry; /* RRSIG signature expiry */
|
||||
usec_t until; /* Used to pass until of a record when doing a dns_cache_lookup().
|
||||
* Needed to schedule cache maintenance queries when browsing for services. */
|
||||
|
||||
DnsResourceKey *key;
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "hostname-util.h"
|
||||
#include "missing_network.h"
|
||||
#include "random-util.h"
|
||||
#include "resolved-dns-browse-services.h"
|
||||
#include "resolved-dnssd.h"
|
||||
#include "resolved-dns-scope.h"
|
||||
#include "resolved-dns-synthesize.h"
|
||||
|
@ -121,6 +122,9 @@ DnsScope* dns_scope_free(DnsScope *s) {
|
|||
dns_cache_flush(&s->cache);
|
||||
dns_zone_flush(&s->zone);
|
||||
|
||||
/* Clear records of mDNS service browse subscriber, since cache bas been flushed */
|
||||
dns_browse_services_purge(s->manager, s->family);
|
||||
|
||||
LIST_REMOVE(scopes, s->manager->dns_scopes, s);
|
||||
return mfree(s);
|
||||
}
|
||||
|
@ -1602,7 +1606,7 @@ int dns_scope_announce(DnsScope *scope, bool goodbye) {
|
|||
}
|
||||
|
||||
int dns_scope_add_dnssd_services(DnsScope *scope) {
|
||||
DnssdService *service;
|
||||
DnssdRegisteredService *service;
|
||||
int r;
|
||||
|
||||
assert(scope);
|
||||
|
@ -1641,7 +1645,7 @@ int dns_scope_add_dnssd_services(DnsScope *scope) {
|
|||
|
||||
int dns_scope_remove_dnssd_services(DnsScope *scope) {
|
||||
_cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
|
||||
DnssdService *service;
|
||||
DnssdRegisteredService *service;
|
||||
int r;
|
||||
|
||||
assert(scope);
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
#include "user-util.h"
|
||||
|
||||
int bus_dnssd_method_unregister(sd_bus_message *message, void *userdata, sd_bus_error *error) {
|
||||
DnssdService *s = ASSERT_PTR(userdata);
|
||||
DnssdRegisteredService *s = ASSERT_PTR(userdata);
|
||||
Manager *m;
|
||||
Link *l;
|
||||
int r;
|
||||
|
@ -69,7 +69,7 @@ int bus_dnssd_method_unregister(sd_bus_message *message, void *userdata, sd_bus_
|
|||
static int dnssd_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
|
||||
_cleanup_free_ char *name = NULL;
|
||||
Manager *m = ASSERT_PTR(userdata);
|
||||
DnssdService *service;
|
||||
DnssdRegisteredService *service;
|
||||
int r;
|
||||
|
||||
assert(bus);
|
||||
|
@ -92,7 +92,7 @@ static int dnssd_object_find(sd_bus *bus, const char *path, const char *interfac
|
|||
static int dnssd_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
|
||||
_cleanup_strv_free_ char **l = NULL;
|
||||
Manager *m = ASSERT_PTR(userdata);
|
||||
DnssdService *service;
|
||||
DnssdRegisteredService *service;
|
||||
unsigned c = 0;
|
||||
int r;
|
||||
|
||||
|
|
|
@ -18,8 +18,8 @@ struct ConfigPerfItem;
|
|||
Service.Name, config_parse_dnssd_service_name, 0, 0
|
||||
Service.Type, config_parse_dnssd_service_type, 0, 0
|
||||
Service.SubType, config_parse_dnssd_service_subtype, 0, 0
|
||||
Service.Port, config_parse_ip_port, 0, offsetof(DnssdService, port)
|
||||
Service.Priority, config_parse_uint16, 0, offsetof(DnssdService, priority)
|
||||
Service.Weight, config_parse_uint16, 0, offsetof(DnssdService, weight)
|
||||
Service.Port, config_parse_ip_port, 0, offsetof(DnssdRegisteredService, port)
|
||||
Service.Priority, config_parse_uint16, 0, offsetof(DnssdRegisteredService, priority)
|
||||
Service.Weight, config_parse_uint16, 0, offsetof(DnssdRegisteredService, weight)
|
||||
Service.TxtText, config_parse_dnssd_txt, DNS_TXT_ITEM_TEXT, 0
|
||||
Service.TxtData, config_parse_dnssd_txt, DNS_TXT_ITEM_DATA, 0
|
||||
|
|
|
@ -37,7 +37,7 @@ DnssdTxtData *dnssd_txtdata_free_all(DnssdTxtData *txt_data) {
|
|||
return dnssd_txtdata_free_all(next);
|
||||
}
|
||||
|
||||
DnssdService *dnssd_service_free(DnssdService *service) {
|
||||
DnssdRegisteredService *dnssd_service_free(DnssdRegisteredService *service) {
|
||||
if (!service)
|
||||
return NULL;
|
||||
|
||||
|
@ -60,7 +60,7 @@ DnssdService *dnssd_service_free(DnssdService *service) {
|
|||
}
|
||||
|
||||
void dnssd_service_clear_on_reload(Hashmap *services) {
|
||||
DnssdService *service;
|
||||
DnssdRegisteredService *service;
|
||||
|
||||
HASHMAP_FOREACH(service, services)
|
||||
if (service->config_source == RESOLVE_CONFIG_SOURCE_FILE) {
|
||||
|
@ -91,7 +91,7 @@ static int dnssd_id_from_path(const char *path, char **ret_id) {
|
|||
}
|
||||
|
||||
static int dnssd_service_load(Manager *manager, const char *path) {
|
||||
_cleanup_(dnssd_service_freep) DnssdService *service = NULL;
|
||||
_cleanup_(dnssd_service_freep) DnssdRegisteredService *service = NULL;
|
||||
_cleanup_(dnssd_txtdata_freep) DnssdTxtData *txt_data = NULL;
|
||||
_cleanup_free_ char *dropin_dirname = NULL;
|
||||
int r;
|
||||
|
@ -99,7 +99,7 @@ static int dnssd_service_load(Manager *manager, const char *path) {
|
|||
assert(manager);
|
||||
assert(path);
|
||||
|
||||
service = new0(DnssdService, 1);
|
||||
service = new0(DnssdRegisteredService, 1);
|
||||
if (!service)
|
||||
return log_oom();
|
||||
|
||||
|
@ -172,7 +172,7 @@ static int specifier_dnssd_hostname(char specifier, const void *data, const char
|
|||
return strdup_to(ret, m->llmnr_hostname);
|
||||
}
|
||||
|
||||
int dnssd_render_instance_name(Manager *m, DnssdService *s, char **ret) {
|
||||
int dnssd_render_instance_name(Manager *m, DnssdRegisteredService *s, char **ret) {
|
||||
static const Specifier specifier_table[] = {
|
||||
{ 'a', specifier_architecture, NULL },
|
||||
{ 'b', specifier_boot_id, NULL },
|
||||
|
@ -229,7 +229,7 @@ int dnssd_load(Manager *manager) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int dnssd_update_rrs(DnssdService *s) {
|
||||
int dnssd_update_rrs(DnssdRegisteredService *s) {
|
||||
_cleanup_free_ char *n = NULL, *service_name = NULL, *full_name = NULL, *sub_name = NULL, *selective_name = NULL;
|
||||
int r;
|
||||
|
||||
|
@ -370,7 +370,7 @@ int dnssd_txt_item_new_from_data(const char *key, const void *data, const size_t
|
|||
}
|
||||
|
||||
int dnssd_signal_conflict(Manager *manager, const char *name) {
|
||||
DnssdService *s;
|
||||
DnssdRegisteredService *s;
|
||||
int r;
|
||||
|
||||
if (sd_bus_is_ready(manager->bus) <= 0)
|
||||
|
@ -428,7 +428,7 @@ int config_parse_dnssd_service_name(
|
|||
{ 'W', specifier_os_variant_id, NULL },
|
||||
{}
|
||||
};
|
||||
DnssdService *s = ASSERT_PTR(userdata);
|
||||
DnssdRegisteredService *s = ASSERT_PTR(userdata);
|
||||
_cleanup_free_ char *name = NULL;
|
||||
int r;
|
||||
|
||||
|
@ -470,7 +470,7 @@ int config_parse_dnssd_service_type(
|
|||
void *data,
|
||||
void *userdata) {
|
||||
|
||||
DnssdService *s = ASSERT_PTR(userdata);
|
||||
DnssdRegisteredService *s = ASSERT_PTR(userdata);
|
||||
int r;
|
||||
|
||||
assert(filename);
|
||||
|
@ -506,7 +506,7 @@ int config_parse_dnssd_service_subtype(
|
|||
void *data,
|
||||
void *userdata) {
|
||||
|
||||
DnssdService *s = ASSERT_PTR(userdata);
|
||||
DnssdRegisteredService *s = ASSERT_PTR(userdata);
|
||||
|
||||
assert(filename);
|
||||
assert(lvalue);
|
||||
|
@ -538,7 +538,7 @@ int config_parse_dnssd_txt(
|
|||
void *userdata) {
|
||||
|
||||
_cleanup_(dnssd_txtdata_freep) DnssdTxtData *txt_data = NULL;
|
||||
DnssdService *s = ASSERT_PTR(userdata);
|
||||
DnssdRegisteredService *s = ASSERT_PTR(userdata);
|
||||
DnsTxtItem *last = NULL;
|
||||
|
||||
assert(filename);
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
#include "list.h"
|
||||
#include "resolved-conf.h"
|
||||
|
||||
typedef struct DnssdService DnssdService;
|
||||
typedef struct DnssdRegisteredService DnssdRegisteredService;
|
||||
typedef struct DnssdTxtData DnssdTxtData;
|
||||
|
||||
typedef struct Manager Manager;
|
||||
|
@ -25,7 +25,7 @@ struct DnssdTxtData {
|
|||
LIST_FIELDS(DnssdTxtData, items);
|
||||
};
|
||||
|
||||
struct DnssdService {
|
||||
struct DnssdRegisteredService {
|
||||
char *path;
|
||||
char *id;
|
||||
char *name_template;
|
||||
|
@ -52,19 +52,19 @@ struct DnssdService {
|
|||
uid_t originator;
|
||||
};
|
||||
|
||||
DnssdService *dnssd_service_free(DnssdService *service);
|
||||
DnssdRegisteredService *dnssd_service_free(DnssdRegisteredService *service);
|
||||
DnssdTxtData *dnssd_txtdata_free(DnssdTxtData *txt_data);
|
||||
DnssdTxtData *dnssd_txtdata_free_all(DnssdTxtData *txt_data);
|
||||
void dnssd_service_clear_on_reload(Hashmap *services);
|
||||
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(DnssdService*, dnssd_service_free);
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(DnssdRegisteredService*, dnssd_service_free);
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(DnssdTxtData*, dnssd_txtdata_free);
|
||||
|
||||
int dnssd_render_instance_name(Manager *m, DnssdService *s, char **ret);
|
||||
int dnssd_render_instance_name(Manager *m, DnssdRegisteredService *s, char **ret);
|
||||
int dnssd_load(Manager *manager);
|
||||
int dnssd_txt_item_new_from_string(const char *key, const char *value, DnsTxtItem **ret_item);
|
||||
int dnssd_txt_item_new_from_data(const char *key, const void *value, const size_t size, DnsTxtItem **ret_item);
|
||||
int dnssd_update_rrs(DnssdService *s);
|
||||
int dnssd_update_rrs(DnssdRegisteredService *s);
|
||||
int dnssd_signal_conflict(Manager *manager, const char *name);
|
||||
|
||||
const struct ConfigPerfItem* resolved_dnssd_gperf_lookup(const char *key, GPERF_LEN_TYPE length);
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include "mkdir.h"
|
||||
#include "netif-util.h"
|
||||
#include "parse-util.h"
|
||||
#include "resolved-dns-browse-services.h"
|
||||
#include "resolved-link.h"
|
||||
#include "resolved-llmnr.h"
|
||||
#include "resolved-mdns.h"
|
||||
|
@ -166,6 +167,7 @@ void link_allocate_scopes(Link *l) {
|
|||
r = dns_scope_new(l->manager, &l->mdns_ipv4_scope, l, DNS_PROTOCOL_MDNS, AF_INET);
|
||||
if (r < 0)
|
||||
log_link_warning_errno(l, r, "Failed to allocate mDNS IPv4 scope, ignoring: %m");
|
||||
dns_browse_services_restart(l->manager);
|
||||
}
|
||||
} else
|
||||
l->mdns_ipv4_scope = dns_scope_free(l->mdns_ipv4_scope);
|
||||
|
@ -176,6 +178,7 @@ void link_allocate_scopes(Link *l) {
|
|||
r = dns_scope_new(l->manager, &l->mdns_ipv6_scope, l, DNS_PROTOCOL_MDNS, AF_INET6);
|
||||
if (r < 0)
|
||||
log_link_warning_errno(l, r, "Failed to allocate mDNS IPv6 scope, ignoring: %m");
|
||||
dns_browse_services_restart(l->manager);
|
||||
}
|
||||
} else
|
||||
l->mdns_ipv6_scope = dns_scope_free(l->mdns_ipv6_scope);
|
||||
|
|
|
@ -768,7 +768,8 @@ int manager_start(Manager *m) {
|
|||
|
||||
Manager *manager_free(Manager *m) {
|
||||
Link *l;
|
||||
DnssdService *s;
|
||||
DnssdRegisteredService *s;
|
||||
DnsServiceBrowser *sb;
|
||||
|
||||
if (!m)
|
||||
return NULL;
|
||||
|
@ -840,6 +841,10 @@ Manager *manager_free(Manager *m) {
|
|||
dns_trust_anchor_flush(&m->trust_anchor);
|
||||
manager_etc_hosts_flush(m);
|
||||
|
||||
while ((sb = hashmap_first(m->dns_service_browsers)))
|
||||
dns_service_browser_free(sb);
|
||||
hashmap_free(m->dns_service_browsers);
|
||||
|
||||
return mfree(m);
|
||||
}
|
||||
|
||||
|
@ -1345,7 +1350,7 @@ int manager_find_ifindex(Manager *m, int family, const union in_addr_union *in_a
|
|||
|
||||
void manager_refresh_rrs(Manager *m) {
|
||||
Link *l;
|
||||
DnssdService *s;
|
||||
DnssdRegisteredService *s;
|
||||
|
||||
assert(m);
|
||||
|
||||
|
@ -1474,29 +1479,28 @@ bool manager_packet_from_our_transaction(Manager *m, DnsPacket *p) {
|
|||
return t->sent && dns_packet_equal(t->sent, p);
|
||||
}
|
||||
|
||||
DnsScope* manager_find_scope(Manager *m, DnsPacket *p) {
|
||||
DnsScope* manager_find_scope_from_protocol(Manager *m, int ifindex, DnsProtocol protocol, int family) {
|
||||
Link *l;
|
||||
|
||||
assert(m);
|
||||
assert(p);
|
||||
|
||||
l = hashmap_get(m->links, INT_TO_PTR(p->ifindex));
|
||||
l = hashmap_get(m->links, INT_TO_PTR(ifindex));
|
||||
if (!l)
|
||||
return NULL;
|
||||
|
||||
switch (p->protocol) {
|
||||
switch (protocol) {
|
||||
case DNS_PROTOCOL_LLMNR:
|
||||
if (p->family == AF_INET)
|
||||
if (family == AF_INET)
|
||||
return l->llmnr_ipv4_scope;
|
||||
else if (p->family == AF_INET6)
|
||||
else if (family == AF_INET6)
|
||||
return l->llmnr_ipv6_scope;
|
||||
|
||||
break;
|
||||
|
||||
case DNS_PROTOCOL_MDNS:
|
||||
if (p->family == AF_INET)
|
||||
if (family == AF_INET)
|
||||
return l->mdns_ipv4_scope;
|
||||
else if (p->family == AF_INET6)
|
||||
else if (family == AF_INET6)
|
||||
return l->mdns_ipv6_scope;
|
||||
|
||||
break;
|
||||
|
@ -1706,6 +1710,9 @@ void manager_flush_caches(Manager *m, int log_level) {
|
|||
LIST_FOREACH(scopes, scope, m->dns_scopes)
|
||||
dns_cache_flush(&scope->cache);
|
||||
|
||||
dns_browse_services_purge(m, AF_UNSPEC); /* Clear records of DNS service browse subscriber, since caches are flushed */
|
||||
dns_browse_services_restart(m);
|
||||
|
||||
log_full(log_level, "Flushed all caches.");
|
||||
}
|
||||
|
||||
|
@ -1769,7 +1776,7 @@ void manager_cleanup_saved_user(Manager *m) {
|
|||
}
|
||||
|
||||
bool manager_next_dnssd_names(Manager *m) {
|
||||
DnssdService *s;
|
||||
DnssdRegisteredService *s;
|
||||
bool tried = false;
|
||||
int r;
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
typedef struct Manager Manager;
|
||||
|
||||
#include "resolved-dns-browse-services.h"
|
||||
#include "resolved-dns-query.h"
|
||||
#include "resolved-dns-search-domain.h"
|
||||
#include "resolved-dns-stream.h"
|
||||
|
@ -161,6 +162,9 @@ struct Manager {
|
|||
size_t n_socket_graveyard;
|
||||
|
||||
struct sigrtmin18_info sigrtmin18_info;
|
||||
|
||||
/* Map varlink links to DnsServiceBrowser instances. */
|
||||
Hashmap *dns_service_browsers;
|
||||
};
|
||||
|
||||
/* Manager */
|
||||
|
@ -188,7 +192,13 @@ int manager_next_hostname(Manager *m);
|
|||
bool manager_packet_from_local_address(Manager *m, DnsPacket *p);
|
||||
bool manager_packet_from_our_transaction(Manager *m, DnsPacket *p);
|
||||
|
||||
DnsScope* manager_find_scope(Manager *m, DnsPacket *p);
|
||||
DnsScope* manager_find_scope_from_protocol(Manager *m, int ifindex, DnsProtocol protocol, int family);
|
||||
|
||||
static inline DnsScope* manager_find_scope(Manager *m, DnsPacket *p) {
|
||||
assert(m);
|
||||
assert(p);
|
||||
return manager_find_scope_from_protocol(m, p->ifindex, p->protocol, p->family);
|
||||
}
|
||||
|
||||
void manager_verify_all(Manager *m);
|
||||
|
||||
|
|
|
@ -359,17 +359,21 @@ static int mdns_goodbye_callback(sd_event_source *s, uint64_t usec, void *userda
|
|||
|
||||
dns_cache_prune(&scope->cache);
|
||||
|
||||
r = mdns_notify_browsers_goodbye(scope);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "mDNS: Failed to notify service subscribers of goodbyes, ignoring: %m");
|
||||
|
||||
if (dns_cache_expiry_in_one_second(&scope->cache, usec)) {
|
||||
r = sd_event_add_time_relative(
|
||||
scope->manager->event,
|
||||
&scope->mdns_goodbye_event_source,
|
||||
CLOCK_BOOTTIME,
|
||||
USEC_PER_SEC,
|
||||
0,
|
||||
mdns_goodbye_callback,
|
||||
scope);
|
||||
scope->manager->event,
|
||||
&scope->mdns_goodbye_event_source,
|
||||
CLOCK_BOOTTIME,
|
||||
USEC_PER_SEC,
|
||||
/* accuracy= */ 0,
|
||||
mdns_goodbye_callback,
|
||||
scope);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "mDNS: Failed to re-schedule goodbye callback: %m");
|
||||
return log_warning_errno(r, "mDNS: Failed to re-schedule goodbye callback, ignoring: %m");
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -380,6 +384,7 @@ static int on_mdns_packet(sd_event_source *s, int fd, uint32_t revents, void *us
|
|||
Manager *m = userdata;
|
||||
DnsScope *scope;
|
||||
int r;
|
||||
bool unsolicited_packet = true;
|
||||
|
||||
r = manager_recv(m, fd, DNS_PROTOCOL_MDNS, &p);
|
||||
if (r <= 0)
|
||||
|
@ -446,7 +451,7 @@ static int on_mdns_packet(sd_event_source *s, int fd, uint32_t revents, void *us
|
|||
&scope->mdns_goodbye_event_source,
|
||||
CLOCK_BOOTTIME,
|
||||
USEC_PER_SEC,
|
||||
0,
|
||||
/* accuracy= */ 0,
|
||||
mdns_goodbye_callback,
|
||||
scope);
|
||||
if (r < 0)
|
||||
|
@ -455,6 +460,21 @@ static int on_mdns_packet(sd_event_source *s, int fd, uint32_t revents, void *us
|
|||
}
|
||||
}
|
||||
|
||||
dns_cache_put(
|
||||
&scope->cache,
|
||||
scope->manager->enable_cache,
|
||||
DNS_PROTOCOL_MDNS,
|
||||
/* key= */ NULL,
|
||||
DNS_PACKET_RCODE(p),
|
||||
p->answer,
|
||||
/* full_packet= */ NULL,
|
||||
/* query_flags= */ false,
|
||||
_DNSSEC_RESULT_INVALID,
|
||||
/* nsec_ttl= */ UINT32_MAX,
|
||||
p->family,
|
||||
&p->sender,
|
||||
scope->manager->stale_retention_usec);
|
||||
|
||||
for (bool match = true; match;) {
|
||||
match = false;
|
||||
LIST_FOREACH(transactions_by_scope, t, scope->transactions) {
|
||||
|
@ -468,6 +488,7 @@ static int on_mdns_packet(sd_event_source *s, int fd, uint32_t revents, void *us
|
|||
continue;
|
||||
}
|
||||
|
||||
unsolicited_packet = false;
|
||||
/* This packet matches the transaction, let's pass it on as reply */
|
||||
dns_transaction_process_reply(t, p, false);
|
||||
|
||||
|
@ -479,20 +500,9 @@ static int on_mdns_packet(sd_event_source *s, int fd, uint32_t revents, void *us
|
|||
}
|
||||
}
|
||||
|
||||
dns_cache_put(
|
||||
&scope->cache,
|
||||
scope->manager->enable_cache,
|
||||
DNS_PROTOCOL_MDNS,
|
||||
NULL,
|
||||
DNS_PACKET_RCODE(p),
|
||||
p->answer,
|
||||
NULL,
|
||||
false,
|
||||
_DNSSEC_RESULT_INVALID,
|
||||
UINT32_MAX,
|
||||
p->family,
|
||||
&p->sender,
|
||||
scope->manager->stale_retention_usec);
|
||||
/* Check if incoming packet key matches with active browse clients. If yes, update the same */
|
||||
if (unsolicited_packet)
|
||||
mdns_notify_browsers_unsolicited_updates(m, p->answer, p->family);
|
||||
|
||||
} else if (dns_packet_validate_query(p) > 0) {
|
||||
log_debug("Got mDNS query packet for id %u", DNS_PACKET_ID(p));
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "glyph-util.h"
|
||||
#include "in-addr-util.h"
|
||||
#include "json-util.h"
|
||||
#include "resolved-dns-browse-services.h"
|
||||
#include "resolved-dns-synthesize.h"
|
||||
#include "resolved-varlink.h"
|
||||
#include "socket-netlink.h"
|
||||
|
@ -30,6 +31,13 @@ typedef struct LookupParametersResolveService {
|
|||
uint64_t flags;
|
||||
} LookupParametersResolveService;
|
||||
|
||||
typedef struct LookupParametersBrowse {
|
||||
const char *domain;
|
||||
const char *type;
|
||||
int ifindex;
|
||||
uint64_t flags;
|
||||
} LookupParametersBrowse;
|
||||
|
||||
static void lookup_parameters_destroy(LookupParameters *p) {
|
||||
assert(p);
|
||||
free(p->name);
|
||||
|
@ -108,10 +116,18 @@ static int reply_query_state(DnsQuery *q) {
|
|||
|
||||
static void vl_on_disconnect(sd_varlink_server *s, sd_varlink *link, void *userdata) {
|
||||
DnsQuery *q;
|
||||
Manager *m;
|
||||
|
||||
assert(s);
|
||||
assert(link);
|
||||
|
||||
m = sd_varlink_server_get_userdata(s);
|
||||
if (!m)
|
||||
return;
|
||||
|
||||
DnsServiceBrowser *sb = hashmap_remove(m->dns_service_browsers, link);
|
||||
dns_service_browser_unref(sb);
|
||||
|
||||
q = sd_varlink_get_userdata(link);
|
||||
if (!q)
|
||||
return;
|
||||
|
@ -1209,6 +1225,42 @@ static int verify_polkit(sd_varlink *link, sd_json_variant *parameters, const ch
|
|||
&m->polkit_registry);
|
||||
}
|
||||
|
||||
static int vl_method_browse_services(sd_varlink* link, sd_json_variant* parameters, sd_varlink_method_flags_t flags, void* userdata) {
|
||||
static const sd_json_dispatch_field dispatch_table[] = {
|
||||
{ "domain", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(LookupParametersBrowse, domain), 0 },
|
||||
{ "type", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(LookupParametersBrowse, type), 0 },
|
||||
{ "ifindex", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_int, offsetof(LookupParametersBrowse, ifindex), 0 },
|
||||
{ "flags", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(LookupParametersBrowse, flags), 0 },
|
||||
{}
|
||||
};
|
||||
|
||||
LookupParametersBrowse p = {};
|
||||
Manager *m;
|
||||
int r = 0;
|
||||
|
||||
assert(link);
|
||||
|
||||
/* if the client didn't set the more flag, it is using us incorrectly */
|
||||
if (!FLAGS_SET(flags, SD_VARLINK_METHOD_MORE))
|
||||
return sd_varlink_error(link, SD_VARLINK_ERROR_EXPECTED_MORE, NULL);
|
||||
|
||||
m = sd_varlink_server_get_userdata(sd_varlink_get_server(link));
|
||||
assert(m);
|
||||
|
||||
r = sd_varlink_dispatch(link, parameters, dispatch_table, &p);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed vl_method_browse_services json dispatch: %m");
|
||||
|
||||
if (!validate_and_mangle_flags(NULL, &p.flags, 0))
|
||||
return sd_varlink_error_invalid_parameter_name(link, "flags");
|
||||
|
||||
r = dns_subscribe_browse_service(m, link, p.domain, p.type, p.ifindex, p.flags);
|
||||
if (r < 0)
|
||||
return sd_varlink_error_errno(link, r);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int vl_method_subscribe_query_results(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
|
||||
Manager *m = ASSERT_PTR(sd_varlink_get_userdata(ASSERT_PTR(link)));
|
||||
int r;
|
||||
|
@ -1422,7 +1474,8 @@ static int varlink_main_server_init(Manager *m) {
|
|||
"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);
|
||||
"io.systemd.Resolve.ResolveRecord", vl_method_resolve_record,
|
||||
"io.systemd.Resolve.BrowseServices", vl_method_browse_services);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to register varlink methods: %m");
|
||||
|
||||
|
|
|
@ -24,8 +24,7 @@ int acquire_fido2_key(
|
|||
const char *key_file,
|
||||
size_t key_file_size,
|
||||
uint64_t key_file_offset,
|
||||
const void *key_data,
|
||||
size_t key_data_size,
|
||||
const struct iovec *key_data,
|
||||
usec_t until,
|
||||
Fido2EnrollFlags required,
|
||||
const char *askpw_credential,
|
||||
|
@ -45,10 +44,10 @@ int acquire_fido2_key(
|
|||
"Local verification is required to unlock this volume, but the 'headless' parameter was set.");
|
||||
|
||||
assert(cid);
|
||||
assert(key_file || key_data);
|
||||
assert(key_file || iovec_is_set(key_data));
|
||||
|
||||
if (key_data)
|
||||
salt = IOVEC_MAKE(key_data, key_data_size);
|
||||
if (iovec_is_set(key_data))
|
||||
salt = *key_data;
|
||||
else {
|
||||
if (key_file_size > 0)
|
||||
log_debug("Ignoring 'keyfile-size=' option for a FIDO2 salt file.");
|
||||
|
@ -252,7 +251,7 @@ int acquire_fido2_key_auto(
|
|||
/* key_file= */ NULL, /* salt is read from LUKS header instead of key_file */
|
||||
/* key_file_size= */ 0,
|
||||
/* key_file_offset= */ 0,
|
||||
salt, salt_size,
|
||||
&IOVEC_MAKE(salt, salt_size),
|
||||
until,
|
||||
required,
|
||||
"cryptsetup.fido2-pin",
|
||||
|
|
|
@ -20,8 +20,7 @@ int acquire_fido2_key(
|
|||
const char *key_file,
|
||||
size_t key_file_size,
|
||||
uint64_t key_file_offset,
|
||||
const void *key_data,
|
||||
size_t key_data_size,
|
||||
const struct iovec *key_data,
|
||||
usec_t until,
|
||||
Fido2EnrollFlags required,
|
||||
const char *askpw_credential,
|
||||
|
@ -52,8 +51,7 @@ static inline int acquire_fido2_key(
|
|||
const char *key_file,
|
||||
size_t key_file_size,
|
||||
uint64_t key_file_offset,
|
||||
const void *key_data,
|
||||
size_t key_data_size,
|
||||
const struct iovec *key_data,
|
||||
usec_t until,
|
||||
Fido2EnrollFlags required,
|
||||
const char *askpw_credential,
|
||||
|
|
|
@ -102,6 +102,27 @@ static SD_VARLINK_DEFINE_STRUCT_TYPE(
|
|||
SD_VARLINK_DEFINE_FIELD(ifindex, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_DEFINE_FIELD(name, SD_VARLINK_STRING, 0));
|
||||
|
||||
static SD_VARLINK_DEFINE_ENUM_TYPE(
|
||||
BrowseServiceUpdateFlag,
|
||||
SD_VARLINK_FIELD_COMMENT("Indicates that the service was added."),
|
||||
SD_VARLINK_DEFINE_ENUM_VALUE(added),
|
||||
SD_VARLINK_FIELD_COMMENT("Indicates that the service was removed."),
|
||||
SD_VARLINK_DEFINE_ENUM_VALUE(removed));
|
||||
|
||||
static SD_VARLINK_DEFINE_STRUCT_TYPE(
|
||||
ServiceData,
|
||||
SD_VARLINK_DEFINE_FIELD_BY_TYPE(updateFlag, BrowseServiceUpdateFlag, 0),
|
||||
SD_VARLINK_FIELD_COMMENT("The address family of the service, one of AF_INET or AF_INET6."),
|
||||
SD_VARLINK_DEFINE_FIELD(family, SD_VARLINK_INT, 0),
|
||||
SD_VARLINK_FIELD_COMMENT("The name of the service, e.g., 'My Service'. May be null if not specified."),
|
||||
SD_VARLINK_DEFINE_FIELD(name, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_FIELD_COMMENT("The type of service, e.g., '_http._tcp'."),
|
||||
SD_VARLINK_DEFINE_FIELD(type, SD_VARLINK_STRING, 0),
|
||||
SD_VARLINK_FIELD_COMMENT("The domain in which the service resides, e.g., 'local'."),
|
||||
SD_VARLINK_DEFINE_FIELD(domain, SD_VARLINK_STRING, 0),
|
||||
SD_VARLINK_FIELD_COMMENT("The Linux interface index for the network interface associated with this service."),
|
||||
SD_VARLINK_DEFINE_FIELD(ifindex, SD_VARLINK_INT, 0));
|
||||
|
||||
static SD_VARLINK_DEFINE_METHOD(
|
||||
ResolveAddress,
|
||||
SD_VARLINK_FIELD_COMMENT("The Linux interface index for the network interface to search on. Typically left unspecified, in order to search on all interfaces."),
|
||||
|
@ -159,6 +180,20 @@ static SD_VARLINK_DEFINE_METHOD(
|
|||
SD_VARLINK_DEFINE_OUTPUT_BY_TYPE(rrs, ResolvedRecord, SD_VARLINK_ARRAY),
|
||||
SD_VARLINK_DEFINE_OUTPUT(flags, SD_VARLINK_INT, 0));
|
||||
|
||||
static SD_VARLINK_DEFINE_METHOD_FULL(
|
||||
BrowseServices,
|
||||
SD_VARLINK_SUPPORTS_MORE,
|
||||
SD_VARLINK_FIELD_COMMENT("The domain to browse for services. If null, the default browsing domain local is used."),
|
||||
SD_VARLINK_DEFINE_INPUT(domain, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_FIELD_COMMENT("The service type to browse for (e.g., '_http._tcp')."),
|
||||
SD_VARLINK_DEFINE_INPUT(type, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_FIELD_COMMENT("The Linux interface index for the network interface to search on."),
|
||||
SD_VARLINK_DEFINE_INPUT(ifindex, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_FIELD_COMMENT("Various browsing flags to modify the operation."),
|
||||
SD_VARLINK_DEFINE_INPUT(flags, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_FIELD_COMMENT("An array of service data containing information about discovered services."),
|
||||
SD_VARLINK_DEFINE_OUTPUT_BY_TYPE(browserServiceData, ServiceData, SD_VARLINK_ARRAY));
|
||||
|
||||
static SD_VARLINK_DEFINE_ERROR(NoNameServers);
|
||||
static SD_VARLINK_DEFINE_ERROR(NoSuchResourceRecord);
|
||||
static SD_VARLINK_DEFINE_ERROR(QueryTimedOut);
|
||||
|
@ -198,6 +233,8 @@ SD_VARLINK_DEFINE_INTERFACE(
|
|||
&vl_method_ResolveService,
|
||||
SD_VARLINK_SYMBOL_COMMENT("Resolves a domain name to one or more DNS resource records."),
|
||||
&vl_method_ResolveRecord,
|
||||
SD_VARLINK_SYMBOL_COMMENT("Starts browsing for DNS-SD services of specified type."),
|
||||
&vl_method_BrowseServices,
|
||||
SD_VARLINK_SYMBOL_COMMENT("Encapsulates a resolved address."),
|
||||
&vl_type_ResolvedAddress,
|
||||
SD_VARLINK_SYMBOL_COMMENT("Encapsulates a resolved host name."),
|
||||
|
@ -212,6 +249,10 @@ SD_VARLINK_DEFINE_INTERFACE(
|
|||
&vl_type_ResourceRecord,
|
||||
SD_VARLINK_SYMBOL_COMMENT("Encapsulates information about a resolved DNS resource record "),
|
||||
&vl_type_ResolvedRecord,
|
||||
SD_VARLINK_SYMBOL_COMMENT("Describes the update flag for browsing services, indicating whether a service was added or removed during browsing."),
|
||||
&vl_type_BrowseServiceUpdateFlag,
|
||||
SD_VARLINK_SYMBOL_COMMENT("Encapsulates the service data obtained from browsing."),
|
||||
&vl_type_ServiceData,
|
||||
&vl_error_NoNameServers,
|
||||
&vl_error_NoSuchResourceRecord,
|
||||
&vl_error_QueryTimedOut,
|
||||
|
|
|
@ -1414,7 +1414,7 @@ static int verb_enable(int argc, char **argv, void *userdata) {
|
|||
"SetFeatureEnabled",
|
||||
&error,
|
||||
/* reply= */ NULL,
|
||||
"sbt",
|
||||
"sit",
|
||||
*feature,
|
||||
(int) enable,
|
||||
UINT64_C(0));
|
||||
|
|
|
@ -522,6 +522,7 @@ static int reply_callback(
|
|||
if (!arg_quiet)
|
||||
sd_json_variant_dump(parameters, arg_json_format_flags, stdout, NULL);
|
||||
|
||||
fflush(stdout);
|
||||
return r;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
all setup run clean clean-again:
|
||||
@TEST_BASE_DIR=../ ./test.sh --$@
|
||||
|
||||
.PHONY: all setup run clean clean-again
|
|
@ -0,0 +1,8 @@
|
|||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
integration_tests += [
|
||||
integration_test_template + {
|
||||
'name' : fs.name(meson.current_source_dir()),
|
||||
'vm' : true,
|
||||
},
|
||||
]
|
|
@ -0,0 +1,12 @@
|
|||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
set -e
|
||||
|
||||
TEST_DESCRIPTION="Test for systemd-resolved's mDNS functionality"
|
||||
IMAGE_NAME="resolved-mdns"
|
||||
TEST_NO_NSPAWN=1
|
||||
|
||||
# shellcheck source=test/test-functions
|
||||
. "${TEST_BASE_DIR:?}/test-functions"
|
||||
|
||||
do_test "$@"
|
|
@ -380,6 +380,7 @@ foreach dirname : [
|
|||
'TEST-84-STORAGETM',
|
||||
'TEST-85-NETWORK',
|
||||
'TEST-86-MULTI-PROFILE-UKI',
|
||||
'TEST-87-RESOLVED-MDNS',
|
||||
]
|
||||
subdir(dirname)
|
||||
endforeach
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
[Unit]
|
||||
Description=TEST-87-RESOLVED-MDNS
|
||||
|
||||
[Service]
|
||||
ExecStartPre=rm -f /failed /testok
|
||||
ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
|
||||
Type=oneshot
|
|
@ -0,0 +1,255 @@
|
|||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
set -eux
|
||||
set -o pipefail
|
||||
|
||||
# shellcheck source=test/units/test-control.sh
|
||||
. "$(dirname "$0")"/test-control.sh
|
||||
|
||||
SERVICE_TYPE_COUNT=10
|
||||
SERVICE_COUNT=20
|
||||
CONTAINER_ZONE="test-$RANDOM"
|
||||
CONTAINER_1="test-mdns-1"
|
||||
CONTAINER_2="test-mdns-2"
|
||||
|
||||
# Prepare containers
|
||||
create_container() {
|
||||
local container="${1:?}"
|
||||
local stype sid svc
|
||||
|
||||
# Prepare container's /etc
|
||||
#
|
||||
# Since we also need the various test suite related dropins from the host's /etc,
|
||||
# we'll overlay our customizations on top of that
|
||||
mkdir -p "/var/lib/machines/$container/etc/systemd/dnssd"
|
||||
# Create 20 test services for each service type (_testServiceX._udp) and number them sequentially,
|
||||
# i.e. create services 0-19 for _testService0._udp, services 20-39 for _testService1._udp, and so on
|
||||
for stype in $(seq 0 $((SERVICE_TYPE_COUNT - 1))); do
|
||||
for sid in $(seq 0 $((SERVICE_COUNT - 1))); do
|
||||
svc=$((stype * SERVICE_COUNT + sid))
|
||||
|
||||
cat >"/var/lib/machines/$container/etc/systemd/dnssd/test-service-$container-$svc.dnssd" <<EOF
|
||||
[Service]
|
||||
Name=Test Service $svc on %H
|
||||
Type=_testService$stype._udp
|
||||
Port=24002
|
||||
TxtText=DC=Monitor PN=867313 SN=XZ051Z0051
|
||||
EOF
|
||||
done
|
||||
done
|
||||
|
||||
# To make things fast, spawn the container with a transient version of what's currently the host's
|
||||
# rootfs, sans a couple of tweaks to make the container unique enough
|
||||
mkdir -p "/run/systemd/system/systemd-nspawn@$container.service.d"
|
||||
cat >"/run/systemd/system/systemd-nspawn@$container.service.d/override.conf" <<EOF
|
||||
[Service]
|
||||
ExecStart=
|
||||
ExecStart=systemd-nspawn --quiet --link-journal=try-guest --keep-unit --machine=%i --boot \
|
||||
--volatile=yes --directory=/ \
|
||||
--inaccessible=/etc/machine-id \
|
||||
--inaccessible=/etc/hostname \
|
||||
--resolv-conf=replace-stub \
|
||||
--network-zone=$CONTAINER_ZONE \
|
||||
--overlay=/etc:/var/lib/machines/$container/etc::/etc \
|
||||
--hostname=$container
|
||||
EOF
|
||||
}
|
||||
|
||||
check_both() {
|
||||
local service_id="${1:?}"
|
||||
local result_file="${2:?}"
|
||||
|
||||
# We should get 20 services per container, 40 total
|
||||
if [[ "$(wc -l <"$result_file")" -ge 40 ]]; then
|
||||
# Check if the services we got are the correct ones
|
||||
for i in $(seq 0 $((SERVICE_TYPE_COUNT - 1))); do
|
||||
svc=$((service_id * SERVICE_COUNT + i))
|
||||
if ! grep "Test Service $svc on $CONTAINER_1" "$result_file" || \
|
||||
! grep "Test Service $svc on $CONTAINER_2" "$result_file"; then
|
||||
return 1
|
||||
fi
|
||||
done
|
||||
|
||||
# We got all records and all of them are what we expect
|
||||
return 0
|
||||
fi
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
check_first() {
|
||||
local service_id="${1:?}"
|
||||
local result_file="${2:?}"
|
||||
|
||||
# We should get 20 services per container
|
||||
if [[ "$(wc -l <"$result_file")" -ge 20 ]]; then
|
||||
# Check if the services we got are the correct ones
|
||||
for i in $(seq 0 $((SERVICE_TYPE_COUNT - 1))); do
|
||||
svc=$((service_id * SERVICE_COUNT + i))
|
||||
if ! grep "Test Service $svc on $CONTAINER_1" "$result_file"; then
|
||||
return 1
|
||||
fi
|
||||
# This check assumes the second container is unreachable, so this shouldn't happen
|
||||
if grep "Test Service $svc on $CONTAINER_2" "$result_file"; then
|
||||
echo >&2 "Found a record from an unreachable container"
|
||||
cat "$result_file"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
# We got all records and all of them are what we expect
|
||||
return 0
|
||||
fi
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
run_and_check_services() {
|
||||
local service_id="${1:?}"
|
||||
local check_func="${2:?}"
|
||||
local unit_name="varlinkctl-$service_id-$SRANDOM.service"
|
||||
local i out_file parameters service_type svc tmp_file
|
||||
|
||||
out_file="$(mktemp)"
|
||||
error_file="$(mktemp)"
|
||||
tmp_file="$(mktemp)"
|
||||
service_type="_testService$service_id._udp"
|
||||
parameters="{ \"domain\": \"$service_type.local\", \"type\": \"\", \"ifindex\": ${BRIDGE_INDEX:?}, \"flags\": 16785432 }"
|
||||
|
||||
systemd-run --unit="$unit_name" --service-type=exec -p StandardOutput="file:$out_file" -p StandardError="file:$error_file" \
|
||||
varlinkctl call --more /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.BrowseServices "$parameters"
|
||||
|
||||
# shellcheck disable=SC2064
|
||||
# Note: unregister the trap once it's fired, otherwise it'll get propagated to functions that call this
|
||||
# one, *sigh*
|
||||
|
||||
trap "trap - RETURN; systemctl stop $unit_name" RETURN
|
||||
|
||||
for _ in {0..14}; do
|
||||
# The response format, for reference (it's JSON-SEQ):
|
||||
#
|
||||
# {
|
||||
# "browser_service_data": [
|
||||
# {
|
||||
# "updateFlag": true,
|
||||
# "family": 10,
|
||||
# "name": "Test Service 13 on test-mdns-1",
|
||||
# "type": "_testService0._udp",
|
||||
# "domain": "local",
|
||||
# "interface": 3
|
||||
# },
|
||||
# ...
|
||||
# ]
|
||||
# }
|
||||
if [[ -s "$out_file" ]]; then
|
||||
# Extract the service name from each valid record...
|
||||
# jq --slurp --raw-output \
|
||||
# ".[].browser_service_data[] | select(.updateFlag == true and .type == \"$service_type\" and .family == 10).name" "$out_file" | sort | tee "$tmp_file"
|
||||
grep -o '"name":"[^"]*"' "$out_file" | sed 's/"name":"//;s/"//g' | sort | tee "$tmp_file"
|
||||
# ...and compare them with what we expect
|
||||
if "$check_func" "$service_id" "$tmp_file"; then
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
sleep 2
|
||||
done
|
||||
|
||||
cat "$out_file"
|
||||
cat "$error_file"
|
||||
return 1
|
||||
}
|
||||
|
||||
testcase_all_sequential() {
|
||||
: "Test each service type (sequentially)"
|
||||
resolvectl flush-caches
|
||||
for id in $(seq 0 $((SERVICE_TYPE_COUNT - 1))); do
|
||||
run_and_check_services "$id" check_both
|
||||
done
|
||||
|
||||
echo testcase_end
|
||||
}
|
||||
|
||||
testcase_all_parallel() {
|
||||
: "Test each service type (in parallel)"
|
||||
resolvectl flush-caches
|
||||
for id in $(seq 0 $((SERVICE_TYPE_COUNT - 1))); do
|
||||
run_and_check_services "$id" check_both &
|
||||
done
|
||||
wait
|
||||
}
|
||||
|
||||
testcase_single_service_multiple_times() {
|
||||
: "Test one service type multiple times"
|
||||
resolvectl flush-caches
|
||||
for _ in {0..4}; do
|
||||
run_and_check_services 4 check_both
|
||||
done
|
||||
}
|
||||
|
||||
testcase_second_unreachable() {
|
||||
: "Test each service type while the second container is unreachable"
|
||||
resolvectl flush-caches
|
||||
systemd-run -M "$CONTAINER_2" --wait --pipe -- networkctl down host0
|
||||
for id in $(seq 0 $((SERVICE_TYPE_COUNT - 1))); do
|
||||
run_and_check_services "$id" check_first
|
||||
done
|
||||
|
||||
|
||||
: "Test each service type after bringing the second container back up again"
|
||||
systemd-run -M "$CONTAINER_2" --wait --pipe -- networkctl up host0
|
||||
systemd-run -M "$CONTAINER_2" --wait --pipe -- \
|
||||
/usr/lib/systemd/systemd-networkd-wait-online --ipv4 --ipv6 --interface=host0 --operational-state=degraded --timeout=30
|
||||
for id in $(seq 0 $((SERVICE_TYPE_COUNT - 1))); do
|
||||
run_and_check_services "$id" check_both
|
||||
done
|
||||
}
|
||||
|
||||
: "Setup host & containers"
|
||||
# Note: create the drop-in intentionally under /run/ and copy it manually into the containers
|
||||
mkdir -p /run/systemd/resolved.conf.d/
|
||||
cat >/run/systemd/resolved.conf.d/99-mdns-llmnr.conf <<EOF
|
||||
[Resolve]
|
||||
MulticastDNS=yes
|
||||
LLMNR=yes
|
||||
EOF
|
||||
|
||||
systemctl unmask systemd-resolved.service systemd-networkd.{service,socket} systemd-machined.service
|
||||
systemctl enable --now systemd-resolved.service systemd-networkd.{socket,service} systemd-machined.service
|
||||
ln -svrf /run/systemd/resolved.conf.d/99-mdns-llmnr.conf /etc/resolv.conf
|
||||
systemctl reload systemd-resolved.service systemd-networkd.service
|
||||
|
||||
for container in "$CONTAINER_1" "$CONTAINER_2"; do
|
||||
create_container "$container"
|
||||
mkdir -p "/var/lib/machines/$container/etc/systemd/resolved.conf.d/"
|
||||
cp /run/systemd/resolved.conf.d/99-mdns-llmnr.conf "/var/lib/machines/$container/etc/systemd/resolved.conf.d/"
|
||||
touch "/var/lib/machines/$container/etc/hostname"
|
||||
systemctl daemon-reload
|
||||
machinectl start "$container"
|
||||
# Wait for the system bus to start...
|
||||
timeout 30s bash -xec "while ! systemd-run -M '$container' --wait --pipe true; do sleep 1; done"
|
||||
# ...and from there wait for the machine bootup to finish. We don't really care if the container
|
||||
# boots up in a degraded state, hence the `:`
|
||||
timeout 30s systemd-run -M "$container" --wait --pipe -- systemctl --wait is-system-running || :
|
||||
# Wait until the veth interface is configured and turn on mDNS and LLMNR
|
||||
systemd-run -M "$container" --wait --pipe -- \
|
||||
/usr/lib/systemd/systemd-networkd-wait-online --ipv4 --ipv6 --interface=host0 --operational-state=degraded --timeout=30
|
||||
systemd-run -M "$container" --wait --pipe -- resolvectl mdns host0 yes
|
||||
systemd-run -M "$container" --wait --pipe -- resolvectl llmnr host0 yes
|
||||
systemd-run -M "$container" --wait --pipe -- networkctl status --no-pager
|
||||
systemd-run -M "$container" --wait --pipe -- resolvectl status --no-pager
|
||||
[[ "$(systemd-run -M "$container" --wait --pipe -- resolvectl mdns host0)" =~ :\ yes$ ]]
|
||||
[[ "$(systemd-run -M "$container" --wait --pipe -- resolvectl llmnr host0)" =~ :\ yes$ ]]
|
||||
done
|
||||
|
||||
BRIDGE_INDEX="$(<"/sys/class/net/vz-$CONTAINER_ZONE/ifindex")"
|
||||
machinectl list
|
||||
resolvectl mdns "vz-$CONTAINER_ZONE" on
|
||||
resolvectl llmnr "vz-$CONTAINER_ZONE" on
|
||||
networkctl status
|
||||
resolvectl status
|
||||
|
||||
# Run the actual test cases (functions prefixed by testcase_)
|
||||
run_testcases
|
||||
|
||||
touch /testok
|
Loading…
Reference in New Issue