commit
becc96b726
9
TODO
9
TODO
|
@ -33,6 +33,15 @@ Janitorial Clean-ups:
|
|||
|
||||
Features:
|
||||
|
||||
* add systemctl stop --job-mode=triggering that follows TRIGGERED_BY deps and adds them to the same transaction
|
||||
|
||||
* coredump logic should use prlimit() to query RLIMIT_CORE of the dumpee and honour it
|
||||
|
||||
* Add a MaxRuntimeSec= setting for service units (or units in general) to terminate units after they ran for a certain
|
||||
amount of time
|
||||
|
||||
* Maybe add a way how users can "pin" units into memory, so that they are not subject to automatic GC?
|
||||
|
||||
* PID1: find a way how we can reload unit file configuration for
|
||||
specific units only, without reloading the whole of systemd
|
||||
|
||||
|
|
|
@ -348,6 +348,36 @@ char *ascii_strlower_n(char *t, size_t n) {
|
|||
return t;
|
||||
}
|
||||
|
||||
int ascii_strcasecmp_n(const char *a, const char *b, size_t n) {
|
||||
|
||||
for (; n > 0; a++, b++, n--) {
|
||||
int x, y;
|
||||
|
||||
x = (int) (uint8_t) ascii_tolower(*a);
|
||||
y = (int) (uint8_t) ascii_tolower(*b);
|
||||
|
||||
if (x != y)
|
||||
return x - y;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ascii_strcasecmp_nn(const char *a, size_t n, const char *b, size_t m) {
|
||||
int r;
|
||||
|
||||
r = ascii_strcasecmp_n(a, b, MIN(n, m));
|
||||
if (r != 0)
|
||||
return r;
|
||||
|
||||
if (n < m)
|
||||
return -1;
|
||||
else if (n > m)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool chars_intersect(const char *a, const char *b) {
|
||||
const char *p;
|
||||
|
||||
|
|
|
@ -134,6 +134,9 @@ char ascii_tolower(char x);
|
|||
char *ascii_strlower(char *s);
|
||||
char *ascii_strlower_n(char *s, size_t n);
|
||||
|
||||
int ascii_strcasecmp_n(const char *a, const char *b, size_t n);
|
||||
int ascii_strcasecmp_nn(const char *a, size_t n, const char *b, size_t m);
|
||||
|
||||
bool chars_intersect(const char *a, const char *b) _pure_;
|
||||
|
||||
static inline bool _pure_ in_charset(const char *s, const char* charset) {
|
||||
|
|
|
@ -120,6 +120,21 @@ bool dns_type_may_redirect(uint16_t type) {
|
|||
DNS_TYPE_KEY);
|
||||
}
|
||||
|
||||
bool dns_type_may_wildcard(uint16_t type) {
|
||||
|
||||
/* The following records may not be expanded from wildcard RRsets */
|
||||
|
||||
if (dns_type_is_pseudo(type))
|
||||
return false;
|
||||
|
||||
return !IN_SET(type,
|
||||
DNS_TYPE_NSEC3,
|
||||
DNS_TYPE_SOA,
|
||||
|
||||
/* Prohibited by https://tools.ietf.org/html/rfc4592#section-4.4 */
|
||||
DNS_TYPE_DNAME);
|
||||
}
|
||||
|
||||
bool dns_type_is_dnssec(uint16_t type) {
|
||||
return IN_SET(type,
|
||||
DNS_TYPE_DS,
|
||||
|
|
|
@ -131,6 +131,7 @@ bool dns_type_is_valid_rr(uint16_t type);
|
|||
bool dns_type_may_redirect(uint16_t type);
|
||||
bool dns_type_is_dnssec(uint16_t type);
|
||||
bool dns_type_is_obsolete(uint16_t type);
|
||||
bool dns_type_may_wildcard(uint16_t type);
|
||||
|
||||
bool dns_class_is_pseudo(uint16_t class);
|
||||
bool dns_class_is_valid_rr(uint16_t class);
|
||||
|
|
|
@ -320,6 +320,33 @@ int dns_answer_contains_nsec_or_nsec3(DnsAnswer *a) {
|
|||
return false;
|
||||
}
|
||||
|
||||
int dns_answer_contains_zone_nsec3(DnsAnswer *answer, const char *zone) {
|
||||
DnsResourceRecord *rr;
|
||||
int r;
|
||||
|
||||
/* Checks whether the specified answer contains at least one NSEC3 RR in the specified zone */
|
||||
|
||||
DNS_ANSWER_FOREACH(rr, answer) {
|
||||
const char *p;
|
||||
|
||||
if (rr->key->type != DNS_TYPE_NSEC3)
|
||||
continue;
|
||||
|
||||
p = DNS_RESOURCE_KEY_NAME(rr->key);
|
||||
r = dns_name_parent(&p);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
continue;
|
||||
|
||||
r = dns_name_equal(p, zone);
|
||||
if (r != 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int dns_answer_find_soa(DnsAnswer *a, const DnsResourceKey *key, DnsResourceRecord **ret, DnsAnswerFlags *flags) {
|
||||
DnsResourceRecord *rr, *soa = NULL;
|
||||
DnsAnswerFlags rr_flags, soa_flags = 0;
|
||||
|
|
|
@ -64,6 +64,7 @@ int dns_answer_match_key(DnsAnswer *a, const DnsResourceKey *key, DnsAnswerFlags
|
|||
int dns_answer_contains_rr(DnsAnswer *a, DnsResourceRecord *rr, DnsAnswerFlags *combined_flags);
|
||||
int dns_answer_contains_key(DnsAnswer *a, const DnsResourceKey *key, DnsAnswerFlags *combined_flags);
|
||||
int dns_answer_contains_nsec_or_nsec3(DnsAnswer *a);
|
||||
int dns_answer_contains_zone_nsec3(DnsAnswer *answer, const char *zone);
|
||||
|
||||
int dns_answer_find_soa(DnsAnswer *a, const DnsResourceKey *key, DnsResourceRecord **ret, DnsAnswerFlags *flags);
|
||||
int dns_answer_find_cname_or_dname(DnsAnswer *a, const DnsResourceKey *key, DnsResourceRecord **ret, DnsAnswerFlags *flags);
|
||||
|
|
|
@ -442,8 +442,9 @@ static int dnssec_rrsig_expired(DnsResourceRecord *rrsig, usec_t realtime) {
|
|||
expiration = rrsig->rrsig.expiration * USEC_PER_SEC;
|
||||
inception = rrsig->rrsig.inception * USEC_PER_SEC;
|
||||
|
||||
/* Consider inverted validity intervals as expired */
|
||||
if (inception > expiration)
|
||||
return -EKEYREJECTED;
|
||||
return true;
|
||||
|
||||
/* Permit a certain amount of clock skew of 10% of the valid
|
||||
* time range. This takes inspiration from unbound's
|
||||
|
@ -512,8 +513,9 @@ int dnssec_verify_rrset(
|
|||
DnsResourceRecord **list, *rr;
|
||||
gcry_md_hd_t md = NULL;
|
||||
int r, md_algorithm;
|
||||
bool wildcard = false;
|
||||
size_t k, n = 0;
|
||||
bool wildcard;
|
||||
const char *source;
|
||||
|
||||
assert(key);
|
||||
assert(rrsig);
|
||||
|
@ -542,6 +544,28 @@ int dnssec_verify_rrset(
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* Determine the "Source of Synthesis" and whether this is a wildcard RRSIG */
|
||||
r = dns_name_suffix(DNS_RESOURCE_KEY_NAME(key), rrsig->rrsig.labels, &source);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r > 0 && !dns_type_may_wildcard(rrsig->rrsig.type_covered)) {
|
||||
/* We refuse to validate NSEC3 or SOA RRs that are synthesized from wildcards */
|
||||
*result = DNSSEC_INVALID;
|
||||
return 0;
|
||||
}
|
||||
if (r == 1) {
|
||||
/* If we stripped a single label, then let's see if that maybe was "*". If so, we are not really
|
||||
* synthesized from a wildcard, we are the wildcard itself. Treat that like a normal name. */
|
||||
r = dns_name_startswith(DNS_RESOURCE_KEY_NAME(key), "*");
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r > 0)
|
||||
source = DNS_RESOURCE_KEY_NAME(key);
|
||||
|
||||
wildcard = r == 0;
|
||||
} else
|
||||
wildcard = r > 0;
|
||||
|
||||
/* Collect all relevant RRs in a single array, so that we can look at the RRset */
|
||||
list = newa(DnsResourceRecord *, dns_answer_size(a));
|
||||
|
||||
|
@ -592,22 +616,19 @@ int dnssec_verify_rrset(
|
|||
goto finish;
|
||||
gcry_md_write(md, wire_format_name, r);
|
||||
|
||||
/* Convert the source of synthesis into wire format */
|
||||
r = dns_name_to_wire_format(source, wire_format_name, sizeof(wire_format_name), true);
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
|
||||
for (k = 0; k < n; k++) {
|
||||
const char *suffix;
|
||||
size_t l;
|
||||
|
||||
rr = list[k];
|
||||
|
||||
r = dns_name_suffix(DNS_RESOURCE_KEY_NAME(rr->key), rrsig->rrsig.labels, &suffix);
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
if (r > 0) /* This is a wildcard! */ {
|
||||
/* Hash the source of synthesis. If this is a wildcard, then prefix it with the *. label */
|
||||
if (wildcard)
|
||||
gcry_md_write(md, (uint8_t[]) { 1, '*'}, 2);
|
||||
wildcard = true;
|
||||
}
|
||||
|
||||
r = dns_name_to_wire_format(suffix, wire_format_name, sizeof(wire_format_name), true);
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
gcry_md_write(md, wire_format_name, r);
|
||||
|
||||
md_add_uint16(md, rr->key->type);
|
||||
|
@ -1274,8 +1295,8 @@ static int nsec3_hashed_domain_make(DnsResourceRecord *nsec3, const char *domain
|
|||
* name uses an NSEC3 record with the opt-out bit set. Lastly, if we are given insufficient NSEC3 records
|
||||
* to conclude anything we indicate this by returning NO_RR. */
|
||||
static int dnssec_test_nsec3(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result, bool *authenticated, uint32_t *ttl) {
|
||||
_cleanup_free_ char *next_closer_domain = NULL, *wildcard = NULL, *wildcard_domain = NULL;
|
||||
const char *zone, *p, *pp = NULL;
|
||||
_cleanup_free_ char *next_closer_domain = NULL, *wildcard_domain = NULL;
|
||||
const char *zone, *p, *pp = NULL, *wildcard;
|
||||
DnsResourceRecord *rr, *enclosure_rr, *zone_rr, *wildcard_rr = NULL;
|
||||
DnsAnswerFlags flags;
|
||||
int hashed_size, r;
|
||||
|
@ -1401,10 +1422,7 @@ found_closest_encloser:
|
|||
|
||||
/* Prove that there is no next closer and whether or not there is a wildcard domain. */
|
||||
|
||||
wildcard = strappend("*.", p);
|
||||
if (!wildcard)
|
||||
return -ENOMEM;
|
||||
|
||||
wildcard = strjoina("*.", p);
|
||||
r = nsec3_hashed_domain_make(enclosure_rr, wildcard, zone, &wildcard_domain);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
@ -1586,7 +1604,7 @@ int dnssec_nsec_test(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *r
|
|||
return 0;
|
||||
}
|
||||
|
||||
int dnssec_nsec_test_between(DnsAnswer *answer, const char *name, const char *zone, bool *authenticated) {
|
||||
int dnssec_nsec_test_enclosed(DnsAnswer *answer, uint16_t type, const char *name, const char *zone, bool *authenticated) {
|
||||
DnsResourceRecord *rr;
|
||||
DnsAnswerFlags flags;
|
||||
int r;
|
||||
|
@ -1600,6 +1618,9 @@ int dnssec_nsec_test_between(DnsAnswer *answer, const char *name, const char *zo
|
|||
DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) {
|
||||
bool found = false;
|
||||
|
||||
if (rr->key->type != type && type != DNS_TYPE_ANY)
|
||||
continue;
|
||||
|
||||
r = dns_name_endswith(DNS_RESOURCE_KEY_NAME(rr->key), zone);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
@ -1667,6 +1688,145 @@ int dnssec_nsec_test_between(DnsAnswer *answer, const char *name, const char *zo
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int dnssec_test_positive_wildcard_nsec3(
|
||||
DnsAnswer *answer,
|
||||
const char *name,
|
||||
const char *source,
|
||||
const char *zone,
|
||||
bool *authenticated) {
|
||||
|
||||
const char *next_closer = NULL;
|
||||
int r;
|
||||
|
||||
/* Run a positive NSEC3 wildcard proof. Specifically:
|
||||
*
|
||||
* A proof that the the "next closer" of the generating wildcard does not exist.
|
||||
*
|
||||
* Note a key difference between the NSEC3 and NSEC versions of the proof. NSEC RRs don't have to exist for
|
||||
* empty non-transients. NSEC3 RRs however have to. This means it's sufficient to check if the next closer name
|
||||
* exists for the NSEC3 RR and we are done.
|
||||
*
|
||||
* To prove that a.b.c.d.e.f is rightfully synthesized from a wildcard *.d.e.f all we have to check is that
|
||||
* c.d.e.f does not exist. */
|
||||
|
||||
for (;;) {
|
||||
next_closer = name;
|
||||
r = dns_name_parent(&name);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
return 0;
|
||||
|
||||
r = dns_name_equal(name, source);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r > 0)
|
||||
break;
|
||||
}
|
||||
|
||||
return dnssec_nsec_test_enclosed(answer, DNS_TYPE_NSEC3, next_closer, zone, authenticated);
|
||||
}
|
||||
|
||||
static int dnssec_test_positive_wildcard_nsec(
|
||||
DnsAnswer *answer,
|
||||
const char *name,
|
||||
const char *source,
|
||||
const char *zone,
|
||||
bool *_authenticated) {
|
||||
|
||||
bool authenticated = true;
|
||||
int r;
|
||||
|
||||
/* Run a positive NSEC wildcard proof. Specifically:
|
||||
*
|
||||
* A proof that there's neither a wildcard name nor a non-wildcard name that is a suffix of the name "name" and
|
||||
* a prefix of the synthesizing source "source" in the zone "zone".
|
||||
*
|
||||
* See RFC 5155, Section 8.8 and RFC 4035, Section 5.3.4
|
||||
*
|
||||
* Note that if we want to prove that a.b.c.d.e.f is rightfully synthesized from a wildcard *.d.e.f, then we
|
||||
* have to prove that none of the following exist:
|
||||
*
|
||||
* 1) a.b.c.d.e.f
|
||||
* 2) *.b.c.d.e.f
|
||||
* 3) b.c.d.e.f
|
||||
* 4) *.c.d.e.f
|
||||
* 5) c.d.e.f
|
||||
*
|
||||
*/
|
||||
|
||||
for (;;) {
|
||||
_cleanup_free_ char *wc = NULL;
|
||||
bool a = false;
|
||||
|
||||
/* Check if there's an NSEC or NSEC3 RR that proves that the mame we determined is really non-existing,
|
||||
* i.e between the owner name and the next name of an NSEC RR. */
|
||||
r = dnssec_nsec_test_enclosed(answer, DNS_TYPE_NSEC, name, zone, &a);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
authenticated = authenticated && a;
|
||||
|
||||
/* Strip one label off */
|
||||
r = dns_name_parent(&name);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
/* Did we reach the source of synthesis? */
|
||||
r = dns_name_equal(name, source);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r > 0) {
|
||||
/* Successful exit */
|
||||
*_authenticated = authenticated;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Safety check, that the source of synthesis is still our suffix */
|
||||
r = dns_name_endswith(name, source);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
return -EBADMSG;
|
||||
|
||||
/* Replace the label we stripped off with an asterisk */
|
||||
wc = strappend("*.", name);
|
||||
if (!wc)
|
||||
return -ENOMEM;
|
||||
|
||||
/* And check if the proof holds for the asterisk name, too */
|
||||
r = dnssec_nsec_test_enclosed(answer, DNS_TYPE_NSEC, wc, zone, &a);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
authenticated = authenticated && a;
|
||||
/* In the next iteration we'll check the non-asterisk-prefixed version */
|
||||
}
|
||||
}
|
||||
|
||||
int dnssec_test_positive_wildcard(
|
||||
DnsAnswer *answer,
|
||||
const char *name,
|
||||
const char *source,
|
||||
const char *zone,
|
||||
bool *authenticated) {
|
||||
|
||||
int r;
|
||||
|
||||
assert(name);
|
||||
assert(source);
|
||||
assert(zone);
|
||||
assert(authenticated);
|
||||
|
||||
r = dns_answer_contains_zone_nsec3(answer, zone);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r > 0)
|
||||
return dnssec_test_positive_wildcard_nsec3(answer, name, source, zone, authenticated);
|
||||
else
|
||||
return dnssec_test_positive_wildcard_nsec(answer, name, source, zone, authenticated);
|
||||
}
|
||||
|
||||
static const char* const dnssec_result_table[_DNSSEC_RESULT_MAX] = {
|
||||
[DNSSEC_VALIDATED] = "validated",
|
||||
[DNSSEC_VALIDATED_WILDCARD] = "validated-wildcard",
|
||||
|
|
|
@ -83,7 +83,10 @@ typedef enum DnssecNsecResult {
|
|||
} DnssecNsecResult;
|
||||
|
||||
int dnssec_nsec_test(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result, bool *authenticated, uint32_t *ttl);
|
||||
int dnssec_nsec_test_between(DnsAnswer *answer, const char *name, const char *zone, bool *authenticated);
|
||||
|
||||
int dnssec_nsec_test_enclosed(DnsAnswer *answer, uint16_t type, const char *name, const char *zone, bool *authenticated);
|
||||
|
||||
int dnssec_test_positive_wildcard(DnsAnswer *a, const char *name, const char *source, const char *zone, bool *authenticated);
|
||||
|
||||
const char* dnssec_result_to_string(DnssecResult m) _const_;
|
||||
DnssecResult dnssec_result_from_string(const char *s) _pure_;
|
||||
|
|
|
@ -2531,28 +2531,24 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) {
|
|||
|
||||
if (result == DNSSEC_VALIDATED_WILDCARD) {
|
||||
bool authenticated = false;
|
||||
const char *suffix;
|
||||
const char *source;
|
||||
|
||||
/* This RRset validated, but as a wildcard. This means we need to proof via NSEC/NSEC3
|
||||
* that no matching non-wildcard RR exists.
|
||||
*
|
||||
* See RFC 5155, Section 8.8 and RFC 4035, Section 5.3.4*/
|
||||
/* This RRset validated, but as a wildcard. This means we need to prove via NSEC/NSEC3
|
||||
* that no matching non-wildcard RR exists.*/
|
||||
|
||||
r = dns_name_suffix(DNS_RESOURCE_KEY_NAME(rr->key), rrsig->rrsig.labels, &suffix);
|
||||
/* First step, determine the source of synthesis */
|
||||
r = dns_name_suffix(DNS_RESOURCE_KEY_NAME(rr->key), rrsig->rrsig.labels, &source);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
return -EBADMSG;
|
||||
|
||||
r = dns_name_parent(&suffix);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
return -EBADMSG;
|
||||
|
||||
r = dnssec_nsec_test_between(validated, DNS_RESOURCE_KEY_NAME(rr->key), suffix, &authenticated);
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = dnssec_test_positive_wildcard(
|
||||
validated,
|
||||
DNS_RESOURCE_KEY_NAME(rr->key),
|
||||
source,
|
||||
rrsig->rrsig.signer,
|
||||
&authenticated);
|
||||
|
||||
/* Unless the NSEC proof showed that the key really doesn't exist something is off. */
|
||||
if (r == 0)
|
||||
|
|
|
@ -263,7 +263,6 @@ int dns_label_escape(const char *p, size_t l, char *dest, size_t sz) {
|
|||
*(q++) = '0' + (char) ((uint8_t) *p % 10);
|
||||
|
||||
sz -= 4;
|
||||
|
||||
}
|
||||
|
||||
p++;
|
||||
|
@ -522,7 +521,7 @@ int dns_name_compare_func(const void *a, const void *b) {
|
|||
y = (const char *) b + strlen(b);
|
||||
|
||||
for (;;) {
|
||||
char la[DNS_LABEL_MAX+1], lb[DNS_LABEL_MAX+1];
|
||||
char la[DNS_LABEL_MAX], lb[DNS_LABEL_MAX];
|
||||
|
||||
if (x == NULL && y == NULL)
|
||||
return 0;
|
||||
|
@ -532,8 +531,15 @@ int dns_name_compare_func(const void *a, const void *b) {
|
|||
if (r < 0 || q < 0)
|
||||
return r - q;
|
||||
|
||||
k = dns_label_undo_idna(la, r, la, sizeof(la));
|
||||
w = dns_label_undo_idna(lb, q, lb, sizeof(lb));
|
||||
if (r > 0)
|
||||
k = dns_label_undo_idna(la, r, la, sizeof(la));
|
||||
else
|
||||
k = 0;
|
||||
if (q > 0)
|
||||
w = dns_label_undo_idna(lb, q, lb, sizeof(lb));
|
||||
else
|
||||
w = 0;
|
||||
|
||||
if (k < 0 || w < 0)
|
||||
return k - w;
|
||||
if (k > 0)
|
||||
|
@ -541,8 +547,7 @@ int dns_name_compare_func(const void *a, const void *b) {
|
|||
if (w > 0)
|
||||
q = w;
|
||||
|
||||
la[r] = lb[q] = 0;
|
||||
r = strcasecmp(la, lb);
|
||||
r = ascii_strcasecmp_nn(la, r, lb, q);
|
||||
if (r != 0)
|
||||
return r;
|
||||
}
|
||||
|
@ -553,54 +558,54 @@ const struct hash_ops dns_name_hash_ops = {
|
|||
.compare = dns_name_compare_func
|
||||
};
|
||||
|
||||
static int dns_label_unescape_undo_idna(const char **name, char *dest, size_t sz) {
|
||||
int r, k;
|
||||
|
||||
/* Clobbers all arguments on failure... */
|
||||
|
||||
r = dns_label_unescape(name, dest, sz);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
k = dns_label_undo_idna(dest, r, dest, sz);
|
||||
if (k < 0)
|
||||
return k;
|
||||
if (k == 0) /* not an IDNA name */
|
||||
return r;
|
||||
|
||||
return k;
|
||||
}
|
||||
|
||||
int dns_name_equal(const char *x, const char *y) {
|
||||
int r, q, k, w;
|
||||
int r, q;
|
||||
|
||||
assert(x);
|
||||
assert(y);
|
||||
|
||||
for (;;) {
|
||||
char la[DNS_LABEL_MAX+1], lb[DNS_LABEL_MAX+1];
|
||||
char la[DNS_LABEL_MAX], lb[DNS_LABEL_MAX];
|
||||
|
||||
if (*x == 0 && *y == 0)
|
||||
return true;
|
||||
|
||||
r = dns_label_unescape(&x, la, sizeof(la));
|
||||
r = dns_label_unescape_undo_idna(&x, la, sizeof(la));
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r > 0) {
|
||||
k = dns_label_undo_idna(la, r, la, sizeof(la));
|
||||
if (k < 0)
|
||||
return k;
|
||||
if (k > 0)
|
||||
r = k;
|
||||
}
|
||||
|
||||
q = dns_label_unescape(&y, lb, sizeof(lb));
|
||||
q = dns_label_unescape_undo_idna(&y, lb, sizeof(lb));
|
||||
if (q < 0)
|
||||
return q;
|
||||
if (q > 0) {
|
||||
w = dns_label_undo_idna(lb, q, lb, sizeof(lb));
|
||||
if (w < 0)
|
||||
return w;
|
||||
if (w > 0)
|
||||
q = w;
|
||||
}
|
||||
|
||||
/* If one name had fewer labels than the other, this
|
||||
* will show up as empty label here, which the
|
||||
* strcasecmp() below will properly consider different
|
||||
* from a non-empty label. */
|
||||
if (r != q)
|
||||
return false;
|
||||
if (r == 0)
|
||||
return true;
|
||||
|
||||
la[r] = lb[q] = 0;
|
||||
if (strcasecmp(la, lb) != 0)
|
||||
if (ascii_strcasecmp_n(la, lb, r) != 0)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
int dns_name_endswith(const char *name, const char *suffix) {
|
||||
const char *n, *s, *saved_n = NULL;
|
||||
int r, q, k, w;
|
||||
int r, q;
|
||||
|
||||
assert(name);
|
||||
assert(suffix);
|
||||
|
@ -609,41 +614,25 @@ int dns_name_endswith(const char *name, const char *suffix) {
|
|||
s = suffix;
|
||||
|
||||
for (;;) {
|
||||
char ln[DNS_LABEL_MAX+1], ls[DNS_LABEL_MAX+1];
|
||||
char ln[DNS_LABEL_MAX], ls[DNS_LABEL_MAX];
|
||||
|
||||
r = dns_label_unescape(&n, ln, sizeof(ln));
|
||||
r = dns_label_unescape_undo_idna(&n, ln, sizeof(ln));
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r > 0) {
|
||||
k = dns_label_undo_idna(ln, r, ln, sizeof(ln));
|
||||
if (k < 0)
|
||||
return k;
|
||||
if (k > 0)
|
||||
r = k;
|
||||
}
|
||||
|
||||
if (!saved_n)
|
||||
saved_n = n;
|
||||
|
||||
q = dns_label_unescape(&s, ls, sizeof(ls));
|
||||
q = dns_label_unescape_undo_idna(&s, ls, sizeof(ls));
|
||||
if (q < 0)
|
||||
return q;
|
||||
if (q > 0) {
|
||||
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)
|
||||
return true;
|
||||
if (r == 0 && saved_n == n)
|
||||
return false;
|
||||
|
||||
ln[r] = ls[q] = 0;
|
||||
|
||||
if (r != q || strcasecmp(ln, ls)) {
|
||||
if (r != q || ascii_strcasecmp_n(ln, ls, r) != 0) {
|
||||
|
||||
/* Not the same, let's jump back, and try with the next label again */
|
||||
s = suffix;
|
||||
|
@ -653,9 +642,39 @@ int dns_name_endswith(const char *name, const char *suffix) {
|
|||
}
|
||||
}
|
||||
|
||||
int dns_name_startswith(const char *name, const char *prefix) {
|
||||
const char *n, *p;
|
||||
int r, q;
|
||||
|
||||
assert(name);
|
||||
assert(prefix);
|
||||
|
||||
n = name;
|
||||
p = prefix;
|
||||
|
||||
for (;;) {
|
||||
char ln[DNS_LABEL_MAX], lp[DNS_LABEL_MAX];
|
||||
|
||||
r = dns_label_unescape_undo_idna(&p, lp, sizeof(lp));
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
return true;
|
||||
|
||||
q = dns_label_unescape_undo_idna(&n, ln, sizeof(ln));
|
||||
if (q < 0)
|
||||
return q;
|
||||
|
||||
if (r != q)
|
||||
return false;
|
||||
if (ascii_strcasecmp_n(ln, lp, r) != 0)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
int r, q;
|
||||
|
||||
assert(name);
|
||||
assert(old_suffix);
|
||||
|
@ -666,35 +685,21 @@ int dns_name_change_suffix(const char *name, const char *old_suffix, const char
|
|||
s = old_suffix;
|
||||
|
||||
for (;;) {
|
||||
char ln[DNS_LABEL_MAX+1], ls[DNS_LABEL_MAX+1];
|
||||
char ln[DNS_LABEL_MAX], ls[DNS_LABEL_MAX];
|
||||
|
||||
if (!saved_before)
|
||||
saved_before = n;
|
||||
|
||||
r = dns_label_unescape(&n, ln, sizeof(ln));
|
||||
r = dns_label_unescape_undo_idna(&n, ln, sizeof(ln));
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r > 0) {
|
||||
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));
|
||||
q = dns_label_unescape_undo_idna(&s, ls, sizeof(ls));
|
||||
if (q < 0)
|
||||
return q;
|
||||
if (q > 0) {
|
||||
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;
|
||||
|
@ -703,9 +708,7 @@ int dns_name_change_suffix(const char *name, const char *old_suffix, const char
|
|||
return 0;
|
||||
}
|
||||
|
||||
ln[r] = ls[q] = 0;
|
||||
|
||||
if (r != q || strcasecmp(ln, ls)) {
|
||||
if (r != q || ascii_strcasecmp_n(ln, ls, r) != 0) {
|
||||
|
||||
/* Not the same, let's jump back, and try with the next label again */
|
||||
s = old_suffix;
|
||||
|
@ -873,12 +876,11 @@ bool dns_name_is_root(const char *name) {
|
|||
}
|
||||
|
||||
bool dns_name_is_single_label(const char *name) {
|
||||
char label[DNS_LABEL_MAX+1];
|
||||
int r;
|
||||
|
||||
assert(name);
|
||||
|
||||
r = dns_label_unescape(&name, label, sizeof(label));
|
||||
r = dns_name_parent(&name);
|
||||
if (r <= 0)
|
||||
return false;
|
||||
|
||||
|
@ -1099,17 +1101,15 @@ int dns_service_split(const char *joined, char **_name, char **_type, char **_do
|
|||
if (x >= 3 && srv_type_label_is_valid(c, cn)) {
|
||||
|
||||
if (dns_service_name_label_is_valid(a, an)) {
|
||||
|
||||
/* OK, got <name> . <type> . <type2> . <domain> */
|
||||
|
||||
name = strndup(a, an);
|
||||
if (!name)
|
||||
return -ENOMEM;
|
||||
|
||||
type = new(char, bn+1+cn+1);
|
||||
type = strjoin(b, ".", c, NULL);
|
||||
if (!type)
|
||||
return -ENOMEM;
|
||||
strcpy(stpcpy(stpcpy(type, b), "."), c);
|
||||
|
||||
d = p;
|
||||
goto finish;
|
||||
|
@ -1121,10 +1121,9 @@ int dns_service_split(const char *joined, char **_name, char **_type, char **_do
|
|||
|
||||
name = NULL;
|
||||
|
||||
type = new(char, an+1+bn+1);
|
||||
type = strjoin(a, ".", b, NULL);
|
||||
if (!type)
|
||||
return -ENOMEM;
|
||||
strcpy(stpcpy(stpcpy(type, a), "."), b);
|
||||
|
||||
d = q;
|
||||
goto finish;
|
||||
|
|
|
@ -83,6 +83,7 @@ extern const struct hash_ops dns_name_hash_ops;
|
|||
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_startswith(const char *name, const char *prefix);
|
||||
|
||||
int dns_name_change_suffix(const char *name, const char *old_suffix, const char *new_suffix, char **ret);
|
||||
|
||||
|
|
|
@ -1979,7 +1979,7 @@ static void dump_unit_file_changes(const UnitFileChange *changes, unsigned n_cha
|
|||
|
||||
for (i = 0; i < n_changes; i++) {
|
||||
if (changes[i].type == UNIT_FILE_SYMLINK)
|
||||
log_info("Created symlink from %s to %s.", changes[i].path, changes[i].source);
|
||||
log_info("Created symlink %s, pointing to %s.", changes[i].path, changes[i].source);
|
||||
else
|
||||
log_info("Removed symlink %s.", changes[i].path);
|
||||
}
|
||||
|
|
|
@ -276,6 +276,25 @@ static void test_dns_name_endswith(void) {
|
|||
test_dns_name_endswith_one("x.y\001.z", "waldo", -EINVAL);
|
||||
}
|
||||
|
||||
static void test_dns_name_startswith_one(const char *a, const char *b, int ret) {
|
||||
assert_se(dns_name_startswith(a, b) == ret);
|
||||
}
|
||||
|
||||
static void test_dns_name_startswith(void) {
|
||||
test_dns_name_startswith_one("", "", true);
|
||||
test_dns_name_startswith_one("", "xxx", false);
|
||||
test_dns_name_startswith_one("xxx", "", true);
|
||||
test_dns_name_startswith_one("x", "x", true);
|
||||
test_dns_name_startswith_one("x", "y", false);
|
||||
test_dns_name_startswith_one("x.y", "x.y", true);
|
||||
test_dns_name_startswith_one("x.y", "y.x", false);
|
||||
test_dns_name_startswith_one("x.y", "x", true);
|
||||
test_dns_name_startswith_one("x.y", "X", true);
|
||||
test_dns_name_startswith_one("x.y", "y", false);
|
||||
test_dns_name_startswith_one("x.y", "", true);
|
||||
test_dns_name_startswith_one("x.y", "X", true);
|
||||
}
|
||||
|
||||
static void test_dns_name_is_root(void) {
|
||||
assert_se(dns_name_is_root(""));
|
||||
assert_se(dns_name_is_root("."));
|
||||
|
@ -567,6 +586,7 @@ int main(int argc, char *argv[]) {
|
|||
test_dns_name_normalize();
|
||||
test_dns_name_equal();
|
||||
test_dns_name_endswith();
|
||||
test_dns_name_startswith();
|
||||
test_dns_name_between();
|
||||
test_dns_name_is_root();
|
||||
test_dns_name_is_single_label();
|
||||
|
|
|
@ -55,7 +55,53 @@ static void test_string_erase(void) {
|
|||
assert_se(streq(string_erase(x), "xxxxxxxxx"));
|
||||
}
|
||||
|
||||
static void test_ascii_strcasecmp_n(void) {
|
||||
|
||||
assert_se(ascii_strcasecmp_n("", "", 0) == 0);
|
||||
assert_se(ascii_strcasecmp_n("", "", 1) == 0);
|
||||
assert_se(ascii_strcasecmp_n("", "a", 1) < 0);
|
||||
assert_se(ascii_strcasecmp_n("", "a", 2) < 0);
|
||||
assert_se(ascii_strcasecmp_n("a", "", 1) > 0);
|
||||
assert_se(ascii_strcasecmp_n("a", "", 2) > 0);
|
||||
assert_se(ascii_strcasecmp_n("a", "a", 1) == 0);
|
||||
assert_se(ascii_strcasecmp_n("a", "a", 2) == 0);
|
||||
assert_se(ascii_strcasecmp_n("a", "b", 1) < 0);
|
||||
assert_se(ascii_strcasecmp_n("a", "b", 2) < 0);
|
||||
assert_se(ascii_strcasecmp_n("b", "a", 1) > 0);
|
||||
assert_se(ascii_strcasecmp_n("b", "a", 2) > 0);
|
||||
assert_se(ascii_strcasecmp_n("xxxxyxxxx", "xxxxYxxxx", 9) == 0);
|
||||
assert_se(ascii_strcasecmp_n("xxxxxxxxx", "xxxxyxxxx", 9) < 0);
|
||||
assert_se(ascii_strcasecmp_n("xxxxXxxxx", "xxxxyxxxx", 9) < 0);
|
||||
assert_se(ascii_strcasecmp_n("xxxxxxxxx", "xxxxYxxxx", 9) < 0);
|
||||
assert_se(ascii_strcasecmp_n("xxxxXxxxx", "xxxxYxxxx", 9) < 0);
|
||||
|
||||
assert_se(ascii_strcasecmp_n("xxxxYxxxx", "xxxxYxxxx", 9) == 0);
|
||||
assert_se(ascii_strcasecmp_n("xxxxyxxxx", "xxxxxxxxx", 9) > 0);
|
||||
assert_se(ascii_strcasecmp_n("xxxxyxxxx", "xxxxXxxxx", 9) > 0);
|
||||
assert_se(ascii_strcasecmp_n("xxxxYxxxx", "xxxxxxxxx", 9) > 0);
|
||||
assert_se(ascii_strcasecmp_n("xxxxYxxxx", "xxxxXxxxx", 9) > 0);
|
||||
}
|
||||
|
||||
static void test_ascii_strcasecmp_nn(void) {
|
||||
assert_se(ascii_strcasecmp_nn("", 0, "", 0) == 0);
|
||||
assert_se(ascii_strcasecmp_nn("", 0, "", 1) < 0);
|
||||
assert_se(ascii_strcasecmp_nn("", 1, "", 0) > 0);
|
||||
assert_se(ascii_strcasecmp_nn("", 1, "", 1) == 0);
|
||||
|
||||
assert_se(ascii_strcasecmp_nn("aaaa", 4, "aaAa", 4) == 0);
|
||||
assert_se(ascii_strcasecmp_nn("aaa", 3, "aaAa", 4) < 0);
|
||||
assert_se(ascii_strcasecmp_nn("aaa", 4, "aaAa", 4) < 0);
|
||||
assert_se(ascii_strcasecmp_nn("aaaa", 4, "aaA", 3) > 0);
|
||||
assert_se(ascii_strcasecmp_nn("aaaa", 4, "AAA", 4) > 0);
|
||||
|
||||
assert_se(ascii_strcasecmp_nn("aaaa", 4, "bbbb", 4) < 0);
|
||||
assert_se(ascii_strcasecmp_nn("aaAA", 4, "BBbb", 4) < 0);
|
||||
assert_se(ascii_strcasecmp_nn("BBbb", 4, "aaaa", 4) > 0);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
test_string_erase();
|
||||
test_ascii_strcasecmp_n();
|
||||
test_ascii_strcasecmp_nn();
|
||||
return 0;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue