mirror of
https://github.com/systemd/systemd.git
synced 2024-12-27 07:22:31 +03:00
resolved: implement client-side DNAME resolution
Most servers apparently always implicitly convert DNAME to CNAME, but some servers don't, hence implement this properly, as this is required by edns0.
This commit is contained in:
parent
5ce1946f4d
commit
58db254ade
@ -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) {
|
||||
|
@ -207,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);
|
||||
@ -219,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;
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user