mirror of
https://github.com/systemd/systemd.git
synced 2024-12-25 01:34:28 +03:00
Merge pull request #2011 from poettering/resolve-dname
Implement client-side DNAME RR resolving
This commit is contained in:
commit
e35a7876b4
7
TODO
7
TODO
@ -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
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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