/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
- ***/
#if HAVE_LIBIDN2
# include <idn2.h>
#include "dns-domain.h"
#include "hashmap.h"
#include "hexdecoct.h"
+#include "hostname-util.h"
#include "in-addr-util.h"
#include "macro.h"
#include "parse-util.h"
#include "strv.h"
#include "utf8.h"
-int dns_label_unescape(const char **name, char *dest, size_t sz) {
+int dns_label_unescape(const char **name, char *dest, size_t sz, DNSLabelFlags flags) {
const char *n;
- char *d;
+ char *d, last_char = 0;
int r = 0;
assert(name);
d = dest;
for (;;) {
- if (*n == '.') {
- n++;
- break;
- }
+ if (*n == 0 || *n == '.') {
+ if (FLAGS_SET(flags, DNS_LABEL_LDH) && last_char == '-')
+ /* Trailing dash */
+ return -EINVAL;
- if (*n == 0)
+ if (*n == '.')
+ n++;
break;
+ }
if (r >= DNS_LABEL_MAX)
return -EINVAL;
if (*n == '\\') {
/* Escaped character */
+ if (FLAGS_SET(flags, DNS_LABEL_NO_ESCAPES))
+ return -EINVAL;
n++;
else if (IN_SET(*n, '\\', '.')) {
/* Escaped backslash or dot */
+ if (FLAGS_SET(flags, DNS_LABEL_LDH))
+ return -EINVAL;
+
+ last_char = *n;
if (d)
*(d++) = *n;
sz--;
if (k > 255)
return -EINVAL;
+ if (FLAGS_SET(flags, DNS_LABEL_LDH) &&
+ !valid_ldh_char((char) k))
+ return -EINVAL;
+
+ last_char = (char) k;
if (d)
*(d++) = (char) k;
sz--;
/* Normal character */
+ if (FLAGS_SET(flags, DNS_LABEL_LDH)) {
+ if (!valid_ldh_char(*n))
+ return -EINVAL;
+ if (r == 0 && *n == '-')
+ /* Leading dash */
+ return -EINVAL;
+ }
+
+ last_char = *n;
if (d)
*(d++) = *n;
sz--;
terminal--;
}
- r = dns_label_unescape(&name, dest, sz);
+ r = dns_label_unescape(&name, dest, sz, 0);
if (r < 0)
return r;
}
#endif
-int dns_name_concat(const char *a, const char *b, char **_ret) {
+int dns_name_concat(const char *a, const char *b, DNSLabelFlags flags, char **_ret) {
_cleanup_free_ char *ret = NULL;
size_t n = 0, allocated = 0;
const char *p;
for (;;) {
char label[DNS_LABEL_MAX];
- r = dns_label_unescape(&p, label, sizeof(label));
+ r = dns_label_unescape(&p, label, sizeof label, flags);
if (r < 0)
return r;
if (r == 0) {
return 0;
}
-void dns_name_hash_func(const void *s, struct siphash *state) {
- const char *p = s;
+void dns_name_hash_func(const char *p, struct siphash *state) {
int r;
assert(p);
for (;;) {
char label[DNS_LABEL_MAX+1];
- r = dns_label_unescape(&p, label, sizeof(label));
+ r = dns_label_unescape(&p, label, sizeof label, 0);
if (r < 0)
break;
if (r == 0)
string_hash_func("", state);
}
-int dns_name_compare_func(const void *a, const void *b) {
+int dns_name_compare_func(const char *a, const char *b) {
const char *x, *y;
int r, q;
assert(a);
assert(b);
- x = (const char *) a + strlen(a);
- y = (const char *) b + strlen(b);
+ x = a + strlen(a);
+ y = b + strlen(b);
for (;;) {
char la[DNS_LABEL_MAX], lb[DNS_LABEL_MAX];
r = dns_label_unescape_suffix(a, &x, la, sizeof(la));
q = dns_label_unescape_suffix(b, &y, lb, sizeof(lb));
if (r < 0 || q < 0)
- return r - q;
+ return CMP(r, q);
r = ascii_strcasecmp_nn(la, r, lb, q);
if (r != 0)
}
}
-const struct hash_ops dns_name_hash_ops = {
- .hash = dns_name_hash_func,
- .compare = dns_name_compare_func
-};
+DEFINE_HASH_OPS(dns_name_hash_ops, char, dns_name_hash_func, dns_name_compare_func);
int dns_name_equal(const char *x, const char *y) {
int r, q;
for (;;) {
char la[DNS_LABEL_MAX], lb[DNS_LABEL_MAX];
- r = dns_label_unescape(&x, la, sizeof(la));
+ r = dns_label_unescape(&x, la, sizeof la, 0);
if (r < 0)
return r;
- q = dns_label_unescape(&y, lb, sizeof(lb));
+ q = dns_label_unescape(&y, lb, sizeof lb, 0);
if (q < 0)
return q;
for (;;) {
char ln[DNS_LABEL_MAX], ls[DNS_LABEL_MAX];
- r = dns_label_unescape(&n, ln, sizeof(ln));
+ r = dns_label_unescape(&n, ln, sizeof ln, 0);
if (r < 0)
return r;
if (!saved_n)
saved_n = n;
- q = dns_label_unescape(&s, ls, sizeof(ls));
+ q = dns_label_unescape(&s, ls, sizeof ls, 0);
if (q < 0)
return q;
for (;;) {
char ln[DNS_LABEL_MAX], lp[DNS_LABEL_MAX];
- r = dns_label_unescape(&p, lp, sizeof(lp));
+ r = dns_label_unescape(&p, lp, sizeof lp, 0);
if (r < 0)
return r;
if (r == 0)
return true;
- q = dns_label_unescape(&n, ln, sizeof(ln));
+ q = dns_label_unescape(&n, ln, sizeof ln, 0);
if (q < 0)
return q;
if (!saved_before)
saved_before = n;
- r = dns_label_unescape(&n, ln, sizeof(ln));
+ r = dns_label_unescape(&n, ln, sizeof ln, 0);
if (r < 0)
return r;
if (!saved_after)
saved_after = n;
- q = dns_label_unescape(&s, ls, sizeof(ls));
+ q = dns_label_unescape(&s, ls, sizeof ls, 0);
if (q < 0)
return q;
/* Found it! Now generate the new name */
prefix = strndupa(name, saved_before - name);
- r = dns_name_concat(prefix, new_suffix, ret);
+ r = dns_name_concat(prefix, new_suffix, 0, ret);
if (r < 0)
return r;
for (i = 0; i < ELEMENTSOF(a); i++) {
char label[DNS_LABEL_MAX+1];
- r = dns_label_unescape(&p, label, sizeof(label));
+ r = dns_label_unescape(&p, label, sizeof label, 0);
if (r < 0)
return r;
if (r == 0)
char label[DNS_LABEL_MAX+1];
int x, y;
- r = dns_label_unescape(&p, label, sizeof(label));
+ r = dns_label_unescape(&p, label, sizeof label, 0);
if (r <= 0)
return r;
if (r != 1)
if (x < 0)
return -EINVAL;
- r = dns_label_unescape(&p, label, sizeof(label));
+ r = dns_label_unescape(&p, label, sizeof label, 0);
if (r <= 0)
return r;
if (r != 1)
* dns_label_unescape() returns 0 when it hits the end
* of the domain name, which we rely on here to encode
* the trailing NUL byte. */
- r = dns_label_unescape(&domain, (char *) out, len);
+ r = dns_label_unescape(&domain, (char *) out, len, 0);
if (r < 0)
return r;
/* This more or less implements RFC 6335, Section 5.1 */
- r = dns_label_unescape(&name, label, sizeof(label));
+ r = dns_label_unescape(&name, label, sizeof label, 0);
if (r < 0)
return false;
if (r == 0)
return -EINVAL;
if (!name)
- return dns_name_concat(type, domain, ret);
+ return dns_name_concat(type, domain, 0, ret);
if (!dns_service_name_is_valid(name))
return -EINVAL;
if (r < 0)
return r;
- r = dns_name_concat(type, domain, &n);
+ r = dns_name_concat(type, domain, 0, &n);
if (r < 0)
return r;
- return dns_name_concat(escaped, n, ret);
+ return dns_name_concat(escaped, n, 0, ret);
}
static bool dns_service_name_label_is_valid(const char *label, size_t n) {
assert(joined);
/* Get first label from the full name */
- an = dns_label_unescape(&p, a, sizeof(a));
+ an = dns_label_unescape(&p, a, sizeof(a), 0);
if (an < 0)
return an;
x++;
/* If there was a first label, try to get the second one */
- bn = dns_label_unescape(&p, b, sizeof(b));
+ bn = dns_label_unescape(&p, b, sizeof(b), 0);
if (bn < 0)
return bn;
/* If there was a second label, try to get the third one */
q = p;
- cn = dns_label_unescape(&p, c, sizeof(c));
+ cn = dns_label_unescape(&p, c, sizeof(c), 0);
if (cn < 0)
return cn;
d = joined;
finish:
- r = dns_name_normalize(d, &domain);
+ r = dns_name_normalize(d, 0, &domain);
if (r < 0)
return r;
return 0;
}
-static int dns_name_build_suffix_table(const char *name, const char*table[]) {
+static int dns_name_build_suffix_table(const char *name, const char *table[]) {
const char *p;
unsigned n = 0;
int r;
}
x = a_labels[n - 1 - k];
- r = dns_label_unescape(&x, la, sizeof(la));
+ r = dns_label_unescape(&x, la, sizeof la, 0);
if (r < 0)
return r;
y = b_labels[m - 1 - k];
- q = dns_label_unescape(&y, lb, sizeof(lb));
+ q = dns_label_unescape(&y, lb, sizeof lb, 0);
if (q < 0)
return q;
log_debug("idn2_lookup_u8(\"%s\") failed: %d/%s", name, r, idn2_strerror(r));
if (r == IDN2_2HYPHEN)
- /* The name has two hypens — forbidden by IDNA2008 in some cases */
+ /* The name has two hyphens — forbidden by IDNA2008 in some cases */
return 0;
if (IN_SET(r, IDN2_TOO_BIG_DOMAIN, IDN2_TOO_BIG_LABEL))
return -ENOSPC;
for (;;) {
char label[DNS_LABEL_MAX];
- r = dns_label_unescape(&name, label, sizeof(label));
+ r = dns_label_unescape(&name, label, sizeof label, 0);
if (r < 0)
return r;
if (r == 0)
break;
- q = dns_label_apply_idna(label, r, label, sizeof(label));
+ q = dns_label_apply_idna(label, r, label, sizeof label);
if (q < 0)
return q;
if (q > 0)