summaryrefslogtreecommitdiff
path: root/src/resolve/resolved-dns-server.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/resolve/resolved-dns-server.c')
-rw-r--r--src/resolve/resolved-dns-server.c114
1 files changed, 75 insertions, 39 deletions
diff --git a/src/resolve/resolved-dns-server.c b/src/resolve/resolved-dns-server.c
index 207ec4c603..371594c710 100644
--- a/src/resolve/resolved-dns-server.c
+++ b/src/resolve/resolved-dns-server.c
@@ -67,6 +67,7 @@ int dns_server_new(
assert_not_reached("Unknown server type");
s->manager = m;
+ s->linked = true;
/* A new DNS server that isn't fallback is added and the one
* we used so far was a fallback one? Then let's try to pick
@@ -87,39 +88,61 @@ DnsServer* dns_server_ref(DnsServer *s) {
return NULL;
assert(s->n_ref > 0);
-
s->n_ref ++;
return s;
}
-static DnsServer* dns_server_free(DnsServer *s) {
+DnsServer* dns_server_unref(DnsServer *s) {
if (!s)
return NULL;
- if (s->link && s->link->current_dns_server == s)
- link_set_dns_server(s->link, NULL);
+ assert(s->n_ref > 0);
+ s->n_ref --;
- if (s->manager && s->manager->current_dns_server == s)
- manager_set_dns_server(s->manager, NULL);
+ if (s->n_ref > 0)
+ return NULL;
free(s);
-
return NULL;
}
-DnsServer* dns_server_unref(DnsServer *s) {
- if (!s)
- return NULL;
+void dns_server_unlink(DnsServer *s) {
+ assert(s);
+ assert(s->manager);
- assert(s->n_ref > 0);
+ /* This removes the specified server from the linked list of
+ * servers, but any server might still stay around if it has
+ * refs, for example from an ongoing transaction. */
- if (s->n_ref == 1)
- dns_server_free(s);
- else
- s->n_ref --;
+ if (!s->linked)
+ return;
- return NULL;
+ switch (s->type) {
+
+ case DNS_SERVER_LINK:
+ assert(s->link);
+ LIST_REMOVE(servers, s->link->dns_servers, s);
+ break;
+
+ case DNS_SERVER_SYSTEM:
+ LIST_REMOVE(servers, s->manager->dns_servers, s);
+ break;
+
+ case DNS_SERVER_FALLBACK:
+ LIST_REMOVE(servers, s->manager->fallback_dns_servers, s);
+ break;
+ }
+
+ s->linked = false;
+
+ if (s->link && s->link->current_dns_server == s)
+ link_set_dns_server(s->link, NULL);
+
+ if (s->manager->current_dns_server == s)
+ manager_set_dns_server(s->manager, NULL);
+
+ dns_server_unref(s);
}
void dns_server_packet_received(DnsServer *s, usec_t rtt) {
@@ -166,34 +189,48 @@ const struct hash_ops dns_server_hash_ops = {
.compare = dns_server_compare_func
};
-void manager_flush_dns_servers(Manager *m, DnsServerType type) {
- DnsServer **first, *s;
+DnsServer *manager_get_first_dns_server(Manager *m, DnsServerType t) {
+ assert(m);
+
+ switch (t) {
+
+ case DNS_SERVER_SYSTEM:
+ return m->dns_servers;
+
+ case DNS_SERVER_FALLBACK:
+ return m->fallback_dns_servers;
+ default:
+ return NULL;
+ }
+}
+
+void manager_flush_dns_servers(Manager *m, DnsServerType type) {
assert(m);
- first = type == DNS_SERVER_FALLBACK ? &m->fallback_dns_servers : &m->dns_servers;
+ for (;;) {
+ DnsServer *first;
- while (*first) {
- s = *first;
+ first = manager_get_first_dns_server(m, type);
+ if (!first)
+ break;
- LIST_REMOVE(servers, *first, s);
- dns_server_unref(s);
+ dns_server_unlink(first);
}
}
void manager_flush_marked_dns_servers(Manager *m, DnsServerType type) {
- DnsServer **first, *s, *next;
+ DnsServer *first, *s, *next;
assert(m);
- first = type == DNS_SERVER_FALLBACK ? &m->fallback_dns_servers : &m->dns_servers;
+ first = manager_get_first_dns_server(m, type);
- LIST_FOREACH_SAFE(servers, s, next, *first) {
+ LIST_FOREACH_SAFE(servers, s, next, first) {
if (!s->marked)
continue;
- LIST_REMOVE(servers, *first, s);
- dns_server_unref(s);
+ dns_server_unlink(s);
}
}
@@ -202,23 +239,20 @@ void manager_mark_dns_servers(Manager *m, DnsServerType type) {
assert(m);
- first = type == DNS_SERVER_FALLBACK ? m->fallback_dns_servers : m->dns_servers;
-
+ first = manager_get_first_dns_server(m, type);
LIST_FOREACH(servers, s, first)
s->marked = true;
}
-DnsServer* manager_find_dns_server(Manager *m, int family, const union in_addr_union *in_addr) {
- DnsServer *s;
+DnsServer* manager_find_dns_server(Manager *m, DnsServerType type, int family, const union in_addr_union *in_addr) {
+ DnsServer *first, *s;
assert(m);
assert(in_addr);
- LIST_FOREACH(servers, s, m->dns_servers)
- if (s->family == family && in_addr_equal(family, &s->address, in_addr) > 0)
- return s;
+ first = manager_get_first_dns_server(m, type);
- LIST_FOREACH(servers, s, m->fallback_dns_servers)
+ LIST_FOREACH(servers, s, first)
if (s->family == family && in_addr_equal(family, &s->address, in_addr) > 0)
return s;
@@ -238,7 +272,8 @@ DnsServer *manager_set_dns_server(Manager *m, DnsServer *s) {
log_info("Switching to system DNS server %s.", strna(ip));
}
- m->current_dns_server = s;
+ dns_server_unref(m->current_dns_server);
+ m->current_dns_server = dns_server_ref(s);
if (m->unicast_scope)
dns_cache_flush(&m->unicast_scope->cache);
@@ -286,8 +321,9 @@ void manager_next_dns_server(Manager *m) {
if (!m->current_dns_server)
return;
- /* Change to the next one */
- if (m->current_dns_server->servers_next) {
+ /* Change to the next one, but make sure to follow the linked
+ * list only if the server is still linked. */
+ if (m->current_dns_server->linked && m->current_dns_server->servers_next) {
manager_set_dns_server(m, m->current_dns_server->servers_next);
return;
}