+/* SPDX-License-Identifier: LGPL-2.1+ */
/***
This file is part of systemd.
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#ifdef HAVE_LIBIDN
-#include <idna.h>
-#include <stringprep.h>
+#if HAVE_LIBIDN2
+# include <idn2.h>
+#elif HAVE_LIBIDN
+# include <idna.h>
+# include <stringprep.h>
#endif
#include <endian.h>
/* Ending NUL */
return -EINVAL;
- else if (*n == '\\' || *n == '.') {
+ else if (IN_SET(*n, '\\', '.')) {
/* Escaped backslash or dot */
if (d)
if (r == 0 && *n)
return -EINVAL;
+ /* More than one trailing dot? */
+ if (*n == '.')
+ return -EINVAL;
+
if (sz >= 1 && d)
*d = 0;
}
terminal = *label_terminal;
- assert(*terminal == '.' || *terminal == 0);
+ assert(IN_SET(*terminal, 0, '.'));
/* Skip current terminal character (and accept domain names ending it ".") */
if (*terminal == 0)
unsigned slashes = 0;
for (y = terminal - 1; y >= name && *y == '\\'; y--)
- slashes ++;
+ slashes++;
if (slashes % 2 == 0) {
/* The '.' was not escaped */
}
}
- terminal --;
+ terminal--;
}
r = dns_label_unescape(&name, dest, sz);
q = dest;
while (l > 0) {
- if (*p == '.' || *p == '\\') {
+ if (IN_SET(*p, '.', '\\')) {
/* Dot or backslash */
sz -= 2;
- } else if (*p == '_' ||
- *p == '-' ||
+ } else if (IN_SET(*p, '_', '-') ||
(*p >= '0' && *p <= '9') ||
(*p >= 'a' && *p <= 'z') ||
(*p >= 'A' && *p <= 'Z')) {
return r;
}
+#if HAVE_LIBIDN
int dns_label_apply_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max) {
-#ifdef HAVE_LIBIDN
_cleanup_free_ uint32_t *input = NULL;
size_t input_size, l;
const char *p;
decoded[l] = 0;
return (int) l;
-#else
- return 0;
-#endif
}
int dns_label_undo_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max) {
-#ifdef HAVE_LIBIDN
size_t input_size, output_size;
_cleanup_free_ uint32_t *input = NULL;
_cleanup_free_ char *result = NULL;
decoded[w] = 0;
return w;
-#else
- return 0;
-#endif
}
+#endif
int dns_name_concat(const char *a, const char *b, char **_ret) {
_cleanup_free_ char *ret = NULL;
if (!name)
return -ENOMEM;
- type = strjoin(b, ".", c, NULL);
+ type = strjoin(b, ".", c);
if (!type)
return -ENOMEM;
name = NULL;
- type = strjoin(a, ".", b, NULL);
+ type = strjoin(a, ".", b);
if (!type)
return -ENOMEM;
assert(a);
assert(ret);
- for (; n_labels > 0; n_labels --) {
+ for (; n_labels > 0; n_labels--) {
r = dns_name_parent(&a);
if (r < 0)
return r;
}
int dns_name_apply_idna(const char *name, char **ret) {
+ /* Return negative on error, 0 if not implemented, positive on success. */
+
+#if HAVE_LIBIDN2
+ int r;
+ _cleanup_free_ char *t = NULL;
+
+ assert(name);
+ assert(ret);
+
+ r = idn2_lookup_u8((uint8_t*) name, (uint8_t**) &t,
+ IDN2_NFC_INPUT | IDN2_NONTRANSITIONAL);
+ log_debug("idn2_lookup_u8: %s → %s", name, t);
+ if (r == IDN2_OK) {
+ if (!startswith(name, "xn--")) {
+ _cleanup_free_ char *s = NULL;
+
+ r = idn2_to_unicode_8z8z(t, &s, 0);
+ if (r != IDN2_OK) {
+ log_debug("idn2_to_unicode_8z8z(\"%s\") failed: %d/%s",
+ t, r, idn2_strerror(r));
+ return 0;
+ }
+
+ if (!streq_ptr(name, s)) {
+ log_debug("idn2 roundtrip failed: \"%s\" → \"%s\" → \"%s\", ignoring.",
+ name, t, s);
+ return 0;
+ }
+ }
+
+ *ret = t;
+ t = NULL;
+ return 1; /* *ret has been written */
+ }
+
+ 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 */
+ return 0;
+ if (IN_SET(r, IDN2_TOO_BIG_DOMAIN, IDN2_TOO_BIG_LABEL))
+ return -ENOSPC;
+ return -EINVAL;
+#elif HAVE_LIBIDN
_cleanup_free_ char *buf = NULL;
size_t n = 0, allocated = 0;
bool first = true;
else
buf[n++] = '.';
- n +=r;
+ n += r;
}
if (n > DNS_HOSTNAME_MAX)
*ret = buf;
buf = NULL;
- return (int) n;
+ return 1;
+#else
+ return 0;
+#endif
+}
+
+int dns_name_is_valid_or_address(const char *name) {
+ /* Returns > 0 if the specified name is either a valid IP address formatted as string or a valid DNS name */
+
+ if (isempty(name))
+ return 0;
+
+ if (in_addr_from_string_auto(name, NULL, NULL) >= 0)
+ return 1;
+
+ return dns_name_is_valid(name);
}