Merge pull request #2011 from poettering/resolve-dname

Implement client-side DNAME RR resolving
This commit is contained in:
Tom Gundersen 2015-11-24 00:46:15 +01:00
commit e35a7876b4
9 changed files with 179 additions and 60 deletions

7
TODO
View file

@ -186,16 +186,17 @@ Features:
- DNS
- search paths
- mDNS/DNS-SD
- mDNS RR resolving
- service registration
- service/domain/types browsing
- avahi compat
- DNS-SD service registration from socket units
- edns0
- dname: Not necessary for plain DNS as synthesized cname is handed out instead if we do not
announce dname support. However, for DNSSEC it is necessary as the synthesized cname
will not be signed.
- cname on PTR (?)
- resolved should optionally register additional per-interface LLMNR
names, so that for the container case we can establish the same name
(maybe "host") for referencing the server, everywhere.
- add API so NM can push DNS server info into resolved
* refcounting in sd-resolve is borked

View file

@ -20,8 +20,10 @@
***/
#include "alloc-util.h"
#include "dns-domain.h"
#include "resolved-dns-cache.h"
#include "resolved-dns-packet.h"
#include "string-util.h"
/* Never cache more than 1K entries */
#define CACHE_MAX 1024
@ -521,25 +523,53 @@ fail:
static DnsCacheItem *dns_cache_get_by_key_follow_cname(DnsCache *c, DnsResourceKey *k) {
_cleanup_(dns_resource_key_unrefp) DnsResourceKey *cname_key = NULL;
DnsCacheItem *i, *j;
DnsCacheItem *i;
const char *n;
int r;
assert(c);
assert(k);
/* If we hit some OOM error, or suchlike, we don't care too
* much, after all this is just a cache */
i = hashmap_get(c->by_key, k);
if (i || k->type == DNS_TYPE_CNAME)
if (i || k->type == DNS_TYPE_CNAME || k->type == DNS_TYPE_DNAME)
return i;
/* check if we have a CNAME record instead */
/* Check if we have a CNAME record instead */
cname_key = dns_resource_key_new_cname(k);
if (!cname_key)
return NULL;
j = hashmap_get(c->by_key, cname_key);
if (j)
return j;
i = hashmap_get(c->by_key, cname_key);
if (i)
return i;
return i;
/* OK, let's look for cached DNAME records. */
n = DNS_RESOURCE_KEY_NAME(k);
for (;;) {
_cleanup_(dns_resource_key_unrefp) DnsResourceKey *dname_key = NULL;
char label[DNS_LABEL_MAX];
if (isempty(n))
return NULL;
dname_key = dns_resource_key_new(k->class, DNS_TYPE_DNAME, n);
if (!dname_key)
return NULL;
i = hashmap_get(c->by_key, dname_key);
if (i)
return i;
/* Jump one label ahead */
r = dns_label_unescape(&n, label, sizeof(label));
if (r <= 0)
return NULL;
}
return NULL;
}
int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **ret) {

View file

@ -155,50 +155,6 @@ int dns_question_is_valid(DnsQuestion *q) {
return 1;
}
int dns_question_is_superset(DnsQuestion *q, DnsQuestion *other) {
unsigned j;
int r;
/* Checks if all keys in "other" are also contained in "q" */
if (!other)
return 1;
for (j = 0; j < other->n_keys; j++) {
DnsResourceKey *b = other->keys[j];
bool found = false;
unsigned i;
if (!q)
return 0;
for (i = 0; i < q->n_keys; i++) {
DnsResourceKey *a = q->keys[i];
r = dns_name_equal(DNS_RESOURCE_KEY_NAME(a), DNS_RESOURCE_KEY_NAME(b));
if (r < 0)
return r;
if (r == 0)
continue;
if (a->class != b->class && a->class != DNS_CLASS_ANY)
continue;
if (a->type != b->type && a->type != DNS_TYPE_ANY)
continue;
found = true;
break;
}
if (!found)
return 0;
}
return 1;
}
int dns_question_contains(DnsQuestion *a, DnsResourceKey *k) {
unsigned j;
int r;
@ -251,6 +207,7 @@ int dns_question_cname_redirect(DnsQuestion *q, const DnsResourceRecord *cname,
assert(cname);
assert(ret);
assert(IN_SET(cname->key->type, DNS_TYPE_CNAME, DNS_TYPE_DNAME));
if (!q) {
n = dns_question_new(0);
@ -263,7 +220,22 @@ int dns_question_cname_redirect(DnsQuestion *q, const DnsResourceRecord *cname,
}
for (i = 0; i < q->n_keys; i++) {
r = dns_name_equal(DNS_RESOURCE_KEY_NAME(q->keys[i]), cname->cname.name);
_cleanup_free_ char *destination = NULL;
const char *d;
if (cname->key->type == DNS_TYPE_CNAME)
d = cname->cname.name;
else {
r = dns_name_change_suffix(DNS_RESOURCE_KEY_NAME(q->keys[i]), DNS_RESOURCE_KEY_NAME(cname->key), cname->dname.name, &destination);
if (r < 0)
return r;
if (r == 0)
continue;
d = destination;
}
r = dns_name_equal(DNS_RESOURCE_KEY_NAME(q->keys[i]), d);
if (r < 0)
return r;

View file

@ -46,7 +46,6 @@ int dns_question_add(DnsQuestion *q, DnsResourceKey *key);
int dns_question_matches_rr(DnsQuestion *q, DnsResourceRecord *rr);
int dns_question_matches_cname(DnsQuestion *q, DnsResourceRecord *rr);
int dns_question_is_valid(DnsQuestion *q);
int dns_question_is_superset(DnsQuestion *q, DnsQuestion *other);
int dns_question_contains(DnsQuestion *a, DnsResourceKey *k);
int dns_question_is_equal(DnsQuestion *a, DnsQuestion *b);

View file

@ -57,10 +57,33 @@ DnsResourceKey* dns_resource_key_new_cname(const DnsResourceKey *key) {
}
DnsResourceKey* dns_resource_key_new_redirect(const DnsResourceKey *key, const DnsResourceRecord *cname) {
int r;
assert(key);
assert(cname);
return dns_resource_key_new(key->class, key->type, cname->cname.name);
assert(IN_SET(cname->key->type, DNS_TYPE_CNAME, DNS_TYPE_DNAME));
if (cname->key->type == DNS_TYPE_CNAME)
return dns_resource_key_new(key->class, key->type, cname->cname.name);
else {
DnsResourceKey *k;
char *destination = NULL;
r = dns_name_change_suffix(DNS_RESOURCE_KEY_NAME(key), DNS_RESOURCE_KEY_NAME(cname->key), cname->dname.name, &destination);
if (r < 0)
return NULL;
if (r == 0)
return dns_resource_key_ref((DnsResourceKey*) key);
k = dns_resource_key_new_consume(key->class, key->type, destination);
if (!k) {
free(destination);
return NULL;
}
return k;
}
}
DnsResourceKey* dns_resource_key_new_consume(uint16_t class, uint16_t type, char *name) {
@ -142,10 +165,12 @@ int dns_resource_key_match_cname(const DnsResourceKey *key, const DnsResourceRec
if (rr->key->class != key->class && key->class != DNS_CLASS_ANY)
return 0;
if (rr->key->type != DNS_TYPE_CNAME)
if (rr->key->type == DNS_TYPE_CNAME)
return dns_name_equal(DNS_RESOURCE_KEY_NAME(key), DNS_RESOURCE_KEY_NAME(rr->key));
else if (rr->key->type == DNS_TYPE_DNAME)
return dns_name_endswith(DNS_RESOURCE_KEY_NAME(key), DNS_RESOURCE_KEY_NAME(rr->key));
else
return 0;
return dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), DNS_RESOURCE_KEY_NAME(key));
}
static void dns_resource_key_hash_func(const void *i, struct siphash *state) {

View file

@ -186,6 +186,7 @@ static inline const char* DNS_RESOURCE_KEY_NAME(const DnsResourceKey *key) {
DnsResourceKey* dns_resource_key_new(uint16_t class, uint16_t type, const char *name);
DnsResourceKey* dns_resource_key_new_cname(const DnsResourceKey *key);
DnsResourceKey* dns_resource_key_new_dname(const DnsResourceKey *key);
DnsResourceKey* dns_resource_key_new_redirect(const DnsResourceKey *key, const DnsResourceRecord *cname);
DnsResourceKey* dns_resource_key_new_consume(uint16_t class, uint16_t type, char *name);
DnsResourceKey* dns_resource_key_ref(DnsResourceKey *key);

View file

@ -547,6 +547,73 @@ int dns_name_endswith(const char *name, const char *suffix) {
}
}
int dns_name_change_suffix(const char *name, const char *old_suffix, const char *new_suffix, char **ret) {
const char *n, *s, *saved_before = NULL, *saved_after = NULL, *prefix;
int r, q, k, w;
assert(name);
assert(old_suffix);
assert(new_suffix);
assert(ret);
n = name;
s = old_suffix;
for (;;) {
char ln[DNS_LABEL_MAX+1], ls[DNS_LABEL_MAX+1];
if (!saved_before)
saved_before = n;
r = dns_label_unescape(&n, ln, sizeof(ln));
if (r < 0)
return r;
k = dns_label_undo_idna(ln, r, ln, sizeof(ln));
if (k < 0)
return k;
if (k > 0)
r = k;
if (!saved_after)
saved_after = n;
q = dns_label_unescape(&s, ls, sizeof(ls));
if (q < 0)
return q;
w = dns_label_undo_idna(ls, q, ls, sizeof(ls));
if (w < 0)
return w;
if (w > 0)
q = w;
if (r == 0 && q == 0)
break;
if (r == 0 && saved_after == n) {
*ret = NULL; /* doesn't match */
return 0;
}
ln[r] = ls[q] = 0;
if (r != q || strcasecmp(ln, ls)) {
/* Not the same, let's jump back, and try with the next label again */
s = old_suffix;
n = saved_after;
saved_after = saved_before = NULL;
}
}
/* Found it! Now generate the new name */
prefix = strndupa(name, saved_before - name);
r = dns_name_concat(prefix, new_suffix, ret);
if (r < 0)
return r;
return 1;
}
int dns_name_between(const char *a, const char *b, const char *c) {
int n;

View file

@ -62,6 +62,8 @@ int dns_name_between(const char *a, const char *b, const char *c);
int dns_name_equal(const char *x, const char *y);
int dns_name_endswith(const char *name, const char *suffix);
int dns_name_change_suffix(const char *name, const char *old_suffix, const char *new_suffix, char **ret);
int dns_name_reverse(int family, const union in_addr_union *a, char **ret);
int dns_name_address(const char *p, int *family, union in_addr_union *a);
@ -76,3 +78,5 @@ bool dns_service_name_is_valid(const char *name);
int dns_service_join(const char *name, const char *type, const char *domain, char **ret);
int dns_service_split(const char *joined, char **name, char **type, char **domain);
int dns_name_replace_suffix(const char *name, const char *old_suffix, const char *new_suffix, char **ret);

View file

@ -408,6 +408,25 @@ static void test_dns_service_split(void) {
test_dns_service_split_one("Wuff\\032Wuff._foo._bar.waldo.com", "Wuff Wuff", "_foo._bar", "waldo.com", 0);
}
static void test_dns_name_change_suffix_one(const char *name, const char *old_suffix, const char *new_suffix, int r, const char *result) {
_cleanup_free_ char *s = NULL;
assert_se(dns_name_change_suffix(name, old_suffix, new_suffix, &s) == r);
assert_se(streq_ptr(s, result));
}
static void test_dns_name_change_suffix(void) {
test_dns_name_change_suffix_one("foo.bar", "bar", "waldo", 1, "foo.waldo");
test_dns_name_change_suffix_one("foo.bar.waldi.quux", "foo.bar.waldi.quux", "piff.paff", 1, "piff.paff");
test_dns_name_change_suffix_one("foo.bar.waldi.quux", "bar.waldi.quux", "piff.paff", 1, "foo.piff.paff");
test_dns_name_change_suffix_one("foo.bar.waldi.quux", "waldi.quux", "piff.paff", 1, "foo.bar.piff.paff");
test_dns_name_change_suffix_one("foo.bar.waldi.quux", "quux", "piff.paff", 1, "foo.bar.waldi.piff.paff");
test_dns_name_change_suffix_one("foo.bar.waldi.quux", "", "piff.paff", 1, "foo.bar.waldi.quux.piff.paff");
test_dns_name_change_suffix_one("", "", "piff.paff", 1, "piff.paff");
test_dns_name_change_suffix_one("", "", "", 1, "");
test_dns_name_change_suffix_one("a", "b", "c", 0, NULL);
}
int main(int argc, char *argv[]) {
test_dns_label_unescape();
@ -427,6 +446,7 @@ int main(int argc, char *argv[]) {
test_dns_srv_type_verify();
test_dns_service_join();
test_dns_service_split();
test_dns_name_change_suffix();
return 0;
}