X-Git-Url: http://git.ipfire.org/?a=blobdiff_plain;f=src%2Fresolve%2Fresolved-dns-server.c;h=2ff5b192df691a5646fa4689200c7d7f7f0e123b;hb=da1d9fc2cc3bf6f89c037e11a584d444d0c3a2a8;hp=734441bccdf545fc17935e2717ceef2dd09c3c72;hpb=0dd25fb9f005d8ab7ac4bc10a609d00569f8c56a;p=thirdparty%2Fsystemd.git diff --git a/src/resolve/resolved-dns-server.c b/src/resolve/resolved-dns-server.c index 734441bccdf..2ff5b192df6 100644 --- a/src/resolve/resolved-dns-server.c +++ b/src/resolve/resolved-dns-server.c @@ -19,12 +19,18 @@ along with systemd; If not, see . ***/ +#include "siphash24.h" + #include "resolved-dns-server.h" +/* After how much time to repeat classic DNS requests */ +#define DNS_TIMEOUT_MIN_USEC (500 * USEC_PER_MSEC) +#define DNS_TIMEOUT_MAX_USEC (5 * USEC_PER_SEC) + int dns_server_new( Manager *m, DnsServer **ret, - DnsServerSource source, + DnsServerType type, Link *l, int family, const union in_addr_union *in_addr) { @@ -32,67 +38,127 @@ int dns_server_new( DnsServer *s, *tail; assert(m); + assert((type == DNS_SERVER_LINK) == !!l); assert(in_addr); - assert(source < _DNS_SERVER_SOURCE_MAX); s = new0(DnsServer, 1); if (!s) return -ENOMEM; - s->source = source; + s->n_ref = 1; + s->type = type; s->family = family; s->address = *in_addr; + s->resend_timeout = DNS_TIMEOUT_MIN_USEC; - if (source == DNS_SERVER_LINK) { - assert(l); - LIST_FIND_TAIL(servers, l->link_dns_servers, tail); - LIST_INSERT_AFTER(servers, l->link_dns_servers, tail, s); - s->link = l; - } else if (source == DNS_SERVER_DHCP) { - assert(l); - LIST_FIND_TAIL(servers, l->dhcp_dns_servers, tail); - LIST_INSERT_AFTER(servers, l->dhcp_dns_servers, tail, s); + if (type == DNS_SERVER_LINK) { + LIST_FIND_TAIL(servers, l->dns_servers, tail); + LIST_INSERT_AFTER(servers, l->dns_servers, tail, s); s->link = l; - } else { - assert(!l); + } else if (type == DNS_SERVER_SYSTEM) { LIST_FIND_TAIL(servers, m->dns_servers, tail); LIST_INSERT_AFTER(servers, m->dns_servers, tail, s); - } + } else if (type == DNS_SERVER_FALLBACK) { + LIST_FIND_TAIL(servers, m->fallback_dns_servers, tail); + LIST_INSERT_AFTER(servers, m->fallback_dns_servers, tail, s); + } else + assert_not_reached("Unknown server type"); s->manager = m; + /* 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 + * the new one */ + if (type != DNS_SERVER_FALLBACK && + m->current_dns_server && + m->current_dns_server->type == DNS_SERVER_FALLBACK) + manager_set_dns_server(m, NULL); + if (ret) *ret = s; return 0; } -DnsServer* dns_server_free(DnsServer *s) { +DnsServer* dns_server_ref(DnsServer *s) { if (!s) return NULL; - if (s->source == DNS_SERVER_LINK) { + assert(s->n_ref > 0); - if (s->link) - LIST_REMOVE(servers, s->link->link_dns_servers, s); - } else if (s->source == DNS_SERVER_DHCP) { + s->n_ref ++; - if (s->link) - LIST_REMOVE(servers, s->link->dhcp_dns_servers, s); - - } else if (s->source == DNS_SERVER_SYSTEM) { + return s; +} - if (s->manager) - LIST_REMOVE(servers, s->manager->dns_servers, s); - } +static DnsServer* dns_server_free(DnsServer *s) { + if (!s) + return NULL; if (s->link && s->link->current_dns_server == s) - s->link->current_dns_server = NULL; + link_set_dns_server(s->link, NULL); if (s->manager && s->manager->current_dns_server == s) - s->manager->current_dns_server = NULL; + manager_set_dns_server(s->manager, NULL); free(s); return NULL; } + +DnsServer* dns_server_unref(DnsServer *s) { + if (!s) + return NULL; + + assert(s->n_ref > 0); + + if (s->n_ref == 1) + dns_server_free(s); + else + s->n_ref --; + + return NULL; +} + +void dns_server_packet_received(DnsServer *s, usec_t rtt) { + assert(s); + + if (rtt > s->max_rtt) { + s->max_rtt = rtt; + s->resend_timeout = MIN(MAX(DNS_TIMEOUT_MIN_USEC, s->max_rtt * 2), + DNS_TIMEOUT_MAX_USEC); + } +} + +void dns_server_packet_lost(DnsServer *s, usec_t usec) { + assert(s); + + if (s->resend_timeout <= usec) + s->resend_timeout = MIN(s->resend_timeout * 2, DNS_TIMEOUT_MAX_USEC); +} + +static unsigned long dns_server_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) { + const DnsServer *s = p; + uint64_t u; + + siphash24((uint8_t*) &u, &s->address, FAMILY_ADDRESS_SIZE(s->family), hash_key); + u = u * hash_key[0] + u + s->family; + + return u; +} + +static int dns_server_compare_func(const void *a, const void *b) { + const DnsServer *x = a, *y = b; + + if (x->family < y->family) + return -1; + if (x->family > y->family) + return 1; + + return memcmp(&x->address, &y->address, FAMILY_ADDRESS_SIZE(x->family)); +} + +const struct hash_ops dns_server_hash_ops = { + .hash = dns_server_hash_func, + .compare = dns_server_compare_func +};